summaryrefslogtreecommitdiffstats
path: root/chromium/net
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net')
-rw-r--r--chromium/net/BUILD.gn1295
-rw-r--r--chromium/net/DEPS43
-rw-r--r--chromium/net/OWNERS8
-rw-r--r--chromium/net/PRESUBMIT.py19
-rw-r--r--chromium/net/android/OWNERS1
-rw-r--r--chromium/net/android/android_private_key.cc25
-rw-r--r--chromium/net/android/android_private_key.h28
-rw-r--r--chromium/net/android/cert_verify_result_android.cc40
-rw-r--r--chromium/net/android/cert_verify_result_android.h24
-rw-r--r--chromium/net/android/cert_verify_status_android_list.h (renamed from chromium/net/android/cert_verify_result_android_list.h)16
-rw-r--r--chromium/net/android/keystore.cc51
-rw-r--r--chromium/net/android/keystore.h2
-rw-r--r--chromium/net/android/keystore_openssl.cc49
-rw-r--r--chromium/net/android/keystore_unittest.cc6
-rw-r--r--chromium/net/android/network_change_notifier_android.cc52
-rw-r--r--chromium/net/android/network_change_notifier_android.h3
-rw-r--r--chromium/net/android/network_change_notifier_delegate_android.cc1
-rw-r--r--chromium/net/android/network_library.cc36
-rw-r--r--chromium/net/android/network_library.h17
-rw-r--r--chromium/net/base/address_family.h1
-rw-r--r--chromium/net/base/address_tracker_linux.cc101
-rw-r--r--chromium/net/base/address_tracker_linux.h40
-rw-r--r--chromium/net/base/address_tracker_linux_unittest.cc165
-rw-r--r--chromium/net/base/backoff_entry.cc41
-rw-r--r--chromium/net/base/backoff_entry_unittest.cc18
-rw-r--r--chromium/net/base/big_endian.cc98
-rw-r--r--chromium/net/base/big_endian.h103
-rw-r--r--chromium/net/base/big_endian_unittest.cc100
-rw-r--r--chromium/net/base/capturing_net_log.cc10
-rw-r--r--chromium/net/base/capturing_net_log.h2
-rw-r--r--chromium/net/base/data_url.cc8
-rw-r--r--chromium/net/base/data_url_unittest.cc7
-rw-r--r--chromium/net/base/directory_lister_unittest.cc11
-rw-r--r--chromium/net/base/dns_reloader.cc2
-rw-r--r--chromium/net/base/escape.cc231
-rw-r--r--chromium/net/base/escape.h34
-rw-r--r--chromium/net/base/escape_unittest.cc138
-rw-r--r--chromium/net/base/file_stream.cc222
-rw-r--r--chromium/net/base/file_stream.h135
-rw-r--r--chromium/net/base/file_stream_context.cc197
-rw-r--r--chromium/net/base/file_stream_context.h64
-rw-r--r--chromium/net/base/file_stream_context_posix.cc80
-rw-r--r--chromium/net/base/file_stream_context_win.cc99
-rw-r--r--chromium/net/base/file_stream_metrics.cc103
-rw-r--r--chromium/net/base/file_stream_metrics.h44
-rw-r--r--chromium/net/base/file_stream_metrics_posix.cc21
-rw-r--r--chromium/net/base/file_stream_metrics_win.cc146
-rw-r--r--chromium/net/base/file_stream_net_log_parameters.cc25
-rw-r--r--chromium/net/base/file_stream_net_log_parameters.h27
-rw-r--r--chromium/net/base/file_stream_unittest.cc678
-rw-r--r--chromium/net/base/filename_util.cc163
-rw-r--r--chromium/net/base/filename_util.h116
-rw-r--r--chromium/net/base/filename_util_icu.cc90
-rw-r--r--chromium/net/base/filename_util_internal.cc318
-rw-r--r--chromium/net/base/filename_util_internal.h62
-rw-r--r--chromium/net/base/filename_util_unittest.cc1652
-rw-r--r--chromium/net/base/filename_util_unsafe.cc51
-rw-r--r--chromium/net/base/filename_util_unsafe.h30
-rw-r--r--chromium/net/base/host_mapping_rules.cc2
-rw-r--r--chromium/net/base/host_mapping_rules.h4
-rw-r--r--chromium/net/base/host_port_pair.cc10
-rw-r--r--chromium/net/base/host_port_pair_unittest.cc77
-rw-r--r--chromium/net/base/int128.cc4
-rw-r--r--chromium/net/base/int128_unittest.cc18
-rw-r--r--chromium/net/base/io_buffer.h2
-rw-r--r--chromium/net/base/ip_endpoint.h2
-rw-r--r--chromium/net/base/ip_pattern.cc188
-rw-r--r--chromium/net/base/ip_pattern.h70
-rw-r--r--chromium/net/base/ip_pattern_unittest.cc160
-rw-r--r--chromium/net/base/keygen_handler_unittest.cc4
-rw-r--r--chromium/net/base/keygen_handler_win.cc2
-rw-r--r--chromium/net/base/load_timing_info.h4
-rw-r--r--chromium/net/base/mime_util.cc155
-rw-r--r--chromium/net/base/mime_util.h26
-rw-r--r--chromium/net/base/mime_util_unittest.cc194
-rw-r--r--chromium/net/base/mock_file_stream.cc128
-rw-r--r--chromium/net/base/mock_file_stream.h54
-rw-r--r--chromium/net/base/net_error_list.h49
-rw-r--r--chromium/net/base/net_errors.cc11
-rw-r--r--chromium/net/base/net_errors.h10
-rw-r--r--chromium/net/base/net_log.cc74
-rw-r--r--chromium/net/base/net_log.h61
-rw-r--r--chromium/net/base/net_log_event_type_list.h200
-rw-r--r--chromium/net/base/net_log_logger.cc46
-rw-r--r--chromium/net/base/net_log_logger.h10
-rw-r--r--chromium/net/base/net_log_logger_unittest.cc24
-rw-r--r--chromium/net/base/net_log_source_type_list.h2
-rw-r--r--chromium/net/base/net_log_unittest.cc97
-rw-r--r--chromium/net/base/net_string_util.h42
-rw-r--r--chromium/net/base/net_string_util_icu.cc62
-rw-r--r--chromium/net/base/net_string_util_icu_alternatives_android.cc115
-rw-r--r--chromium/net/base/net_string_util_icu_alternatives_android.h18
-rw-r--r--chromium/net/base/net_util.cc1384
-rw-r--r--chromium/net/base/net_util.h144
-rw-r--r--chromium/net/base/net_util_icu.cc830
-rw-r--r--chromium/net/base/net_util_icu_unittest.cc1071
-rw-r--r--chromium/net/base/net_util_posix.cc177
-rw-r--r--chromium/net/base/net_util_unittest.cc2699
-rw-r--r--chromium/net/base/net_util_win.cc118
-rw-r--r--chromium/net/base/network_change_notifier.cc93
-rw-r--r--chromium/net/base/network_change_notifier.h20
-rw-r--r--chromium/net/base/network_change_notifier_linux.cc3
-rw-r--r--chromium/net/base/network_change_notifier_mac.cc3
-rw-r--r--chromium/net/base/network_change_notifier_win.cc8
-rw-r--r--chromium/net/base/network_change_notifier_win.h2
-rw-r--r--chromium/net/base/network_change_notifier_win_unittest.cc4
-rw-r--r--chromium/net/base/network_delegate.cc23
-rw-r--r--chromium/net/base/network_delegate.h44
-rw-r--r--chromium/net/base/network_time_notifier.cc91
-rw-r--r--chromium/net/base/network_time_notifier.h84
-rw-r--r--chromium/net/base/nss_memio.c11
-rw-r--r--chromium/net/base/nss_memio.h11
-rw-r--r--chromium/net/base/platform_mime_util_win.cc5
-rw-r--r--chromium/net/base/privacy_mode.h7
-rw-r--r--chromium/net/base/registry_controlled_domains/BUILD.gn23
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names.cc47055
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names.dat1111
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names.gperf369
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest1.cc218
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest2.cc161
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf13
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf523
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf9
-rw-r--r--chromium/net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf9
-rw-r--r--chromium/net/base/registry_controlled_domains/registry_controlled_domain.cc241
-rw-r--r--chromium/net/base/registry_controlled_domains/registry_controlled_domain.h9
-rw-r--r--chromium/net/base/registry_controlled_domains/registry_controlled_domain_unittest.cc188
-rw-r--r--chromium/net/base/request_priority.h4
-rw-r--r--chromium/net/base/sdch_dictionary_fetcher.cc105
-rw-r--r--chromium/net/base/sdch_dictionary_fetcher.h99
-rw-r--r--chromium/net/base/sdch_manager.cc144
-rw-r--r--chromium/net/base/sdch_manager.h48
-rw-r--r--chromium/net/base/sdch_manager_unittest.cc478
-rw-r--r--chromium/net/base/static_cookie_policy.cc6
-rw-r--r--chromium/net/base/static_cookie_policy.h2
-rw-r--r--chromium/net/base/static_cookie_policy_unittest.cc16
-rw-r--r--chromium/net/base/upload_data.cc37
-rw-r--r--chromium/net/base/upload_data.h83
-rw-r--r--chromium/net/base/upload_data_stream_unittest.cc20
-rw-r--r--chromium/net/base/upload_file_element_reader.cc290
-rw-r--r--chromium/net/base/upload_file_element_reader.h63
-rw-r--r--chromium/net/base/upload_file_element_reader_unittest.cc137
-rw-r--r--chromium/net/base/url_util.cc14
-rw-r--r--chromium/net/base/url_util.h6
-rw-r--r--chromium/net/cert/cert_database.cc4
-rw-r--r--chromium/net/cert/cert_database.h16
-rw-r--r--chromium/net/cert/cert_database_android.cc4
-rw-r--r--chromium/net/cert/cert_database_mac.cc2
-rw-r--r--chromium/net/cert/cert_database_nss.cc19
-rw-r--r--chromium/net/cert/cert_status_flags.cc8
-rw-r--r--chromium/net/cert/cert_status_flags.h27
-rw-r--r--chromium/net/cert/cert_status_flags_list.h31
-rw-r--r--chromium/net/cert/cert_verify_proc.cc59
-rw-r--r--chromium/net/cert/cert_verify_proc_android.cc56
-rw-r--r--chromium/net/cert/cert_verify_proc_mac.cc30
-rw-r--r--chromium/net/cert/cert_verify_proc_nss.cc83
-rw-r--r--chromium/net/cert/cert_verify_proc_nss.h13
-rw-r--r--chromium/net/cert/cert_verify_proc_openssl.cc28
-rw-r--r--chromium/net/cert/cert_verify_proc_unittest.cc132
-rw-r--r--chromium/net/cert/cert_verify_proc_win.cc18
-rw-r--r--chromium/net/cert/crl_set.cc52
-rw-r--r--chromium/net/cert/crl_set.h4
-rw-r--r--chromium/net/cert/ct_log_response_parser.cc140
-rw-r--r--chromium/net/cert/ct_log_response_parser.h27
-rw-r--r--chromium/net/cert/ct_log_response_parser_unittest.cc106
-rw-r--r--chromium/net/cert/ct_log_verifier.cc41
-rw-r--r--chromium/net/cert/ct_log_verifier.h14
-rw-r--r--chromium/net/cert/ct_log_verifier_nss.cc2
-rw-r--r--chromium/net/cert/ct_log_verifier_openssl.cc1
-rw-r--r--chromium/net/cert/ct_log_verifier_unittest.cc14
-rw-r--r--chromium/net/cert/ct_serialization.cc24
-rw-r--r--chromium/net/cert/ct_serialization.h8
-rw-r--r--chromium/net/cert/ct_serialization_unittest.cc24
-rw-r--r--chromium/net/cert/ct_signed_certificate_timestamp_log_param.cc2
-rw-r--r--chromium/net/cert/jwk_serializer_nss.cc2
-rw-r--r--chromium/net/cert/multi_log_ct_verifier.cc44
-rw-r--r--chromium/net/cert/multi_log_ct_verifier_unittest.cc174
-rw-r--r--chromium/net/cert/multi_threaded_cert_verifier.cc61
-rw-r--r--chromium/net/cert/multi_threaded_cert_verifier.h3
-rw-r--r--chromium/net/cert/nss_cert_database.cc177
-rw-r--r--chromium/net/cert/nss_cert_database.h86
-rw-r--r--chromium/net/cert/nss_cert_database_chromeos.cc91
-rw-r--r--chromium/net/cert/nss_cert_database_chromeos.h53
-rw-r--r--chromium/net/cert/nss_cert_database_chromeos_unittest.cc279
-rw-r--r--chromium/net/cert/nss_cert_database_unittest.cc57
-rw-r--r--chromium/net/cert/nss_profile_filter_chromeos.cc141
-rw-r--r--chromium/net/cert/nss_profile_filter_chromeos.h71
-rw-r--r--chromium/net/cert/nss_profile_filter_chromeos_unittest.cc192
-rw-r--r--chromium/net/cert/pem_tokenizer.cc4
-rw-r--r--chromium/net/cert/scoped_nss_types.h10
-rw-r--r--chromium/net/cert/sct_status_flags.h6
-rw-r--r--chromium/net/cert/signed_certificate_timestamp.cc6
-rw-r--r--chromium/net/cert/signed_certificate_timestamp.h9
-rw-r--r--chromium/net/cert/signed_tree_head.h40
-rw-r--r--chromium/net/cert/test_root_certs.h23
-rw-r--r--chromium/net/cert/test_root_certs_nss.cc11
-rw-r--r--chromium/net/cert/test_root_certs_openssl.cc22
-rw-r--r--chromium/net/cert/test_root_certs_unittest.cc33
-rw-r--r--chromium/net/cert/x509_cert_types_mac.cc23
-rw-r--r--chromium/net/cert/x509_cert_types_win.cc4
-rw-r--r--chromium/net/cert/x509_certificate.cc5
-rw-r--r--chromium/net/cert/x509_certificate.h8
-rw-r--r--chromium/net/cert/x509_certificate_mac.cc42
-rw-r--r--chromium/net/cert/x509_certificate_win.cc11
-rw-r--r--chromium/net/cert/x509_util_android.cc13
-rw-r--r--chromium/net/cert/x509_util_nss.cc2
-rw-r--r--chromium/net/cookies/canonical_cookie.cc30
-rw-r--r--chromium/net/cookies/canonical_cookie.h9
-rw-r--r--chromium/net/cookies/cookie_monster.cc120
-rw-r--r--chromium/net/cookies/cookie_monster.h94
-rw-r--r--chromium/net/cookies/cookie_monster_store_test.cc2
-rw-r--r--chromium/net/cookies/cookie_monster_store_test.h8
-rw-r--r--chromium/net/cookies/cookie_monster_unittest.cc83
-rw-r--r--chromium/net/cookies/cookie_store.h24
-rw-r--r--chromium/net/cookies/cookie_store_test_callbacks.cc10
-rw-r--r--chromium/net/cookies/cookie_store_test_callbacks.h32
-rw-r--r--chromium/net/cookies/cookie_store_test_helpers.cc14
-rw-r--r--chromium/net/cookies/cookie_store_test_helpers.h10
-rw-r--r--chromium/net/cookies/cookie_store_unittest.h72
-rw-r--r--chromium/net/cookies/cookie_util.cc5
-rw-r--r--chromium/net/data/ftp/dir-listing-ls-32.expected196
-rw-r--r--chromium/net/data/spdy_tests/examples_07.hpackbin0 -> 5617 bytes
-rw-r--r--chromium/net/data/ssl/certificates/1024-rsa-ee-by-secp256k1-ecdsa-intermediate.pem44
-rw-r--r--chromium/net/data/ssl/certificates/2048-rsa-ee-by-secp256k1-ecdsa-intermediate.pem56
-rw-r--r--chromium/net/data/ssl/certificates/768-rsa-ee-by-secp256k1-ecdsa-intermediate.pem42
-rw-r--r--chromium/net/data/ssl/certificates/README7
-rw-r--r--chromium/net/data/ssl/certificates/multi-root-chain1.pem328
-rw-r--r--chromium/net/data/ssl/certificates/multi-root-chain2.pem328
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-aia-certs.sh17
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-bad-eku-certs.sh39
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-client-certificates.sh23
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-cross-signed-certs.sh6
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-duplicate-cn-certs.sh41
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-multi-root-test-chains.sh161
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-policy-certs.sh13
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-redundant-test-chains.sh79
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-test-certs.sh20
-rwxr-xr-xchromium/net/data/ssl/scripts/generate-weak-test-chains.sh12
-rw-r--r--chromium/net/data/ssl/scripts/redundant-ca.cnf46
-rw-r--r--chromium/net/data/url_request_unittest/308-without-location-header1
-rw-r--r--chromium/net/data/url_request_unittest/308-without-location-header.mock-http-headers1
-rw-r--r--chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable1
-rw-r--r--chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers4
-rw-r--r--chromium/net/data/url_request_unittest/redirect308-to-echo1
-rw-r--r--chromium/net/data/url_request_unittest/redirect308-to-echo.mock-http-headers2
-rw-r--r--chromium/net/data/websocket/close-with-split-packet_wsh.py11
-rw-r--r--chromium/net/data/websocket/split_packet_check.html2
-rw-r--r--chromium/net/disk_cache/backend_unittest.cc59
-rw-r--r--chromium/net/disk_cache/blockfile/addr.cc (renamed from chromium/net/disk_cache/addr.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/addr.h (renamed from chromium/net/disk_cache/addr.h)28
-rw-r--r--chromium/net/disk_cache/blockfile/addr_unittest.cc (renamed from chromium/net/disk_cache/addr_unittest.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/backend_impl.cc (renamed from chromium/net/disk_cache/backend_impl.cc)83
-rw-r--r--chromium/net/disk_cache/blockfile/backend_impl.h (renamed from chromium/net/disk_cache/backend_impl.h)23
-rw-r--r--chromium/net/disk_cache/blockfile/backend_impl_v3.cc (renamed from chromium/net/disk_cache/v3/backend_impl_v3.cc)557
-rw-r--r--chromium/net/disk_cache/blockfile/backend_impl_v3.h (renamed from chromium/net/disk_cache/v3/backend_impl_v3.h)120
-rw-r--r--chromium/net/disk_cache/blockfile/backend_worker_v3.cc (renamed from chromium/net/disk_cache/v3/backend_worker.cc)63
-rw-r--r--chromium/net/disk_cache/blockfile/backend_worker_v3.h (renamed from chromium/net/disk_cache/v3/backend_worker.h)34
-rw-r--r--chromium/net/disk_cache/blockfile/bitmap.cc (renamed from chromium/net/disk_cache/bitmap.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/bitmap.h (renamed from chromium/net/disk_cache/bitmap.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/bitmap_unittest.cc (renamed from chromium/net/disk_cache/bitmap_unittest.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/block_bitmaps_v3.cc (renamed from chromium/net/disk_cache/v3/block_bitmaps.cc)6
-rw-r--r--chromium/net/disk_cache/blockfile/block_bitmaps_v3.h (renamed from chromium/net/disk_cache/v3/block_bitmaps.h)10
-rw-r--r--chromium/net/disk_cache/blockfile/block_bitmaps_v3_unittest.cc (renamed from chromium/net/disk_cache/v3/block_bitmaps_unittest.cc)8
-rw-r--r--chromium/net/disk_cache/blockfile/block_files.cc (renamed from chromium/net/disk_cache/block_files.cc)36
-rw-r--r--chromium/net/disk_cache/blockfile/block_files.h (renamed from chromium/net/disk_cache/block_files.h)12
-rw-r--r--chromium/net/disk_cache/blockfile/block_files_unittest.cc (renamed from chromium/net/disk_cache/block_files_unittest.cc)6
-rw-r--r--chromium/net/disk_cache/blockfile/disk_cache_perftest.cc (renamed from chromium/net/disk_cache/disk_cache_perftest.cc)14
-rw-r--r--chromium/net/disk_cache/blockfile/disk_format.cc (renamed from chromium/net/disk_cache/disk_format.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/disk_format.h (renamed from chromium/net/disk_cache/disk_format.h)8
-rw-r--r--chromium/net/disk_cache/blockfile/disk_format_base.h (renamed from chromium/net/disk_cache/disk_format_base.h)7
-rw-r--r--chromium/net/disk_cache/blockfile/disk_format_v3.h (renamed from chromium/net/disk_cache/v3/disk_format_v3.h)94
-rw-r--r--chromium/net/disk_cache/blockfile/entry_impl.cc (renamed from chromium/net/disk_cache/entry_impl.cc)25
-rw-r--r--chromium/net/disk_cache/blockfile/entry_impl.h (renamed from chromium/net/disk_cache/entry_impl.h)12
-rw-r--r--chromium/net/disk_cache/blockfile/entry_impl_v3.cc (renamed from chromium/net/disk_cache/v3/entry_impl_v3.cc)221
-rw-r--r--chromium/net/disk_cache/blockfile/entry_impl_v3.h (renamed from chromium/net/disk_cache/v3/entry_impl_v3.h)87
-rw-r--r--chromium/net/disk_cache/blockfile/errors.h (renamed from chromium/net/disk_cache/errors.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/eviction.cc (renamed from chromium/net/disk_cache/eviction.cc)19
-rw-r--r--chromium/net/disk_cache/blockfile/eviction.h (renamed from chromium/net/disk_cache/eviction.h)8
-rw-r--r--chromium/net/disk_cache/blockfile/eviction_v3.cc (renamed from chromium/net/disk_cache/v3/eviction_v3.cc)78
-rw-r--r--chromium/net/disk_cache/blockfile/eviction_v3.h (renamed from chromium/net/disk_cache/v3/eviction_v3.h)46
-rw-r--r--chromium/net/disk_cache/blockfile/experiments.h (renamed from chromium/net/disk_cache/experiments.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/file.cc (renamed from chromium/net/disk_cache/file.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/file.h (renamed from chromium/net/disk_cache/file.h)20
-rw-r--r--chromium/net/disk_cache/blockfile/file_block.h (renamed from chromium/net/disk_cache/file_block.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/file_ios.cc (renamed from chromium/net/disk_cache/file_ios.cc)69
-rw-r--r--chromium/net/disk_cache/blockfile/file_lock.cc (renamed from chromium/net/disk_cache/file_lock.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/file_lock.h (renamed from chromium/net/disk_cache/file_lock.h)8
-rw-r--r--chromium/net/disk_cache/blockfile/file_posix.cc (renamed from chromium/net/disk_cache/file_posix.cc)65
-rw-r--r--chromium/net/disk_cache/blockfile/file_win.cc (renamed from chromium/net/disk_cache/file_win.cc)85
-rw-r--r--chromium/net/disk_cache/blockfile/histogram_macros.h107
-rw-r--r--chromium/net/disk_cache/blockfile/histogram_macros_v3.h (renamed from chromium/net/disk_cache/histogram_macros.h)46
-rw-r--r--chromium/net/disk_cache/blockfile/in_flight_backend_io.cc (renamed from chromium/net/disk_cache/in_flight_backend_io.cc)11
-rw-r--r--chromium/net/disk_cache/blockfile/in_flight_backend_io.h (renamed from chromium/net/disk_cache/in_flight_backend_io.h)8
-rw-r--r--chromium/net/disk_cache/blockfile/in_flight_io.cc (renamed from chromium/net/disk_cache/in_flight_io.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/in_flight_io.h (renamed from chromium/net/disk_cache/in_flight_io.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/index_table_v3.cc1149
-rw-r--r--chromium/net/disk_cache/blockfile/index_table_v3.h279
-rw-r--r--chromium/net/disk_cache/blockfile/index_table_v3_unittest.cc706
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file.cc (renamed from chromium/net/disk_cache/mapped_file.cc)13
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file.h (renamed from chromium/net/disk_cache/mapped_file.h)14
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc (renamed from chromium/net/disk_cache/mapped_file_avoid_mmap_posix.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file_posix.cc (renamed from chromium/net/disk_cache/mapped_file_posix.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file_unittest.cc (renamed from chromium/net/disk_cache/mapped_file_unittest.cc)4
-rw-r--r--chromium/net/disk_cache/blockfile/mapped_file_win.cc (renamed from chromium/net/disk_cache/mapped_file_win.cc)2
-rw-r--r--chromium/net/disk_cache/blockfile/rankings.cc (renamed from chromium/net/disk_cache/rankings.cc)17
-rw-r--r--chromium/net/disk_cache/blockfile/rankings.h (renamed from chromium/net/disk_cache/rankings.h)12
-rw-r--r--chromium/net/disk_cache/blockfile/sparse_control.cc (renamed from chromium/net/disk_cache/sparse_control.cc)26
-rw-r--r--chromium/net/disk_cache/blockfile/sparse_control.h (renamed from chromium/net/disk_cache/sparse_control.h)10
-rw-r--r--chromium/net/disk_cache/blockfile/sparse_control_v3.cc (renamed from chromium/net/disk_cache/v3/sparse_control_v3.cc)18
-rw-r--r--chromium/net/disk_cache/blockfile/sparse_control_v3.h (renamed from chromium/net/disk_cache/v3/sparse_control_v3.h)8
-rw-r--r--chromium/net/disk_cache/blockfile/stats.cc (renamed from chromium/net/disk_cache/stats.cc)53
-rw-r--r--chromium/net/disk_cache/blockfile/stats.h (renamed from chromium/net/disk_cache/stats.h)17
-rw-r--r--chromium/net/disk_cache/blockfile/storage_block-inl.h (renamed from chromium/net/disk_cache/storage_block-inl.h)10
-rw-r--r--chromium/net/disk_cache/blockfile/storage_block.h (renamed from chromium/net/disk_cache/storage_block.h)10
-rw-r--r--chromium/net/disk_cache/blockfile/storage_block_unittest.cc (renamed from chromium/net/disk_cache/storage_block_unittest.cc)6
-rw-r--r--chromium/net/disk_cache/blockfile/stress_cache.cc (renamed from chromium/net/disk_cache/stress_cache.cc)12
-rw-r--r--chromium/net/disk_cache/blockfile/stress_support.h (renamed from chromium/net/disk_cache/stress_support.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/trace.cc (renamed from chromium/net/disk_cache/trace.cc)4
-rw-r--r--chromium/net/disk_cache/blockfile/trace.h (renamed from chromium/net/disk_cache/trace.h)6
-rw-r--r--chromium/net/disk_cache/blockfile/webfonts_histogram.cc105
-rw-r--r--chromium/net/disk_cache/blockfile/webfonts_histogram.h26
-rw-r--r--chromium/net/disk_cache/cache_creator.cc23
-rw-r--r--chromium/net/disk_cache/cache_util.cc3
-rw-r--r--chromium/net/disk_cache/disk_cache.h2
-rw-r--r--chromium/net/disk_cache/disk_cache_test_base.cc4
-rw-r--r--chromium/net/disk_cache/disk_cache_test_util.cc17
-rw-r--r--chromium/net/disk_cache/entry_unittest.cc524
-rw-r--r--chromium/net/disk_cache/flash/flash_cache_test_base.cc29
-rw-r--r--chromium/net/disk_cache/flash/flash_cache_test_base.h43
-rw-r--r--chromium/net/disk_cache/flash/flash_entry_impl.cc150
-rw-r--r--chromium/net/disk_cache/flash/flash_entry_impl.h98
-rw-r--r--chromium/net/disk_cache/flash/format.h32
-rw-r--r--chromium/net/disk_cache/flash/internal_entry.cc86
-rw-r--r--chromium/net/disk_cache/flash/internal_entry.h63
-rw-r--r--chromium/net/disk_cache/flash/log_store.cc185
-rw-r--r--chromium/net/disk_cache/flash/log_store.h101
-rw-r--r--chromium/net/disk_cache/flash/log_store_entry.cc171
-rw-r--r--chromium/net/disk_cache/flash/log_store_entry.h65
-rw-r--r--chromium/net/disk_cache/flash/log_store_entry_unittest.cc69
-rw-r--r--chromium/net/disk_cache/flash/log_store_unittest.cc131
-rw-r--r--chromium/net/disk_cache/flash/segment.cc122
-rw-r--r--chromium/net/disk_cache/flash/segment.h118
-rw-r--r--chromium/net/disk_cache/flash/segment_unittest.cc152
-rw-r--r--chromium/net/disk_cache/flash/storage.cc63
-rw-r--r--chromium/net/disk_cache/flash/storage.h35
-rw-r--r--chromium/net/disk_cache/flash/storage_unittest.cc41
-rw-r--r--chromium/net/disk_cache/memory/mem_backend_impl.cc (renamed from chromium/net/disk_cache/mem_backend_impl.cc)4
-rw-r--r--chromium/net/disk_cache/memory/mem_backend_impl.h (renamed from chromium/net/disk_cache/mem_backend_impl.h)8
-rw-r--r--chromium/net/disk_cache/memory/mem_entry_impl.cc (renamed from chromium/net/disk_cache/mem_entry_impl.cc)32
-rw-r--r--chromium/net/disk_cache/memory/mem_entry_impl.h (renamed from chromium/net/disk_cache/mem_entry_impl.h)6
-rw-r--r--chromium/net/disk_cache/memory/mem_rankings.cc (renamed from chromium/net/disk_cache/mem_rankings.cc)4
-rw-r--r--chromium/net/disk_cache/memory/mem_rankings.h (renamed from chromium/net/disk_cache/mem_rankings.h)6
-rw-r--r--chromium/net/disk_cache/simple/simple_backend_impl.cc6
-rw-r--r--chromium/net/disk_cache/simple/simple_entry_impl.cc30
-rw-r--r--chromium/net/disk_cache/simple/simple_histogram_macros.h5
-rw-r--r--chromium/net/disk_cache/simple/simple_index.cc40
-rw-r--r--chromium/net/disk_cache/simple/simple_index.h12
-rw-r--r--chromium/net/disk_cache/simple/simple_index_file.cc7
-rw-r--r--chromium/net/disk_cache/simple/simple_index_file_unittest.cc29
-rw-r--r--chromium/net/disk_cache/simple/simple_index_unittest.cc1
-rw-r--r--chromium/net/disk_cache/simple/simple_synchronous_entry.cc238
-rw-r--r--chromium/net/disk_cache/simple/simple_synchronous_entry.h14
-rw-r--r--chromium/net/disk_cache/simple/simple_test_util.cc18
-rw-r--r--chromium/net/disk_cache/simple/simple_util.cc6
-rw-r--r--chromium/net/disk_cache/simple/simple_util.h2
-rw-r--r--chromium/net/disk_cache/simple/simple_version_upgrade.cc46
-rw-r--r--chromium/net/disk_cache/simple/simple_version_upgrade_unittest.cc10
-rw-r--r--chromium/net/disk_cache/stats_histogram.cc94
-rw-r--r--chromium/net/disk_cache/stats_histogram.h58
-rw-r--r--chromium/net/disk_cache/tracing/tracing_cache_backend.cc (renamed from chromium/net/disk_cache/tracing_cache_backend.cc)2
-rw-r--r--chromium/net/disk_cache/tracing/tracing_cache_backend.h (renamed from chromium/net/disk_cache/tracing_cache_backend.h)11
-rw-r--r--chromium/net/dns/address_sorter_posix_unittest.cc8
-rw-r--r--chromium/net/dns/address_sorter_win.cc4
-rw-r--r--chromium/net/dns/dns_config_service.cc86
-rw-r--r--chromium/net/dns/dns_config_service.h35
-rw-r--r--chromium/net/dns/dns_config_service_posix.cc106
-rw-r--r--chromium/net/dns/dns_config_service_posix.h4
-rw-r--r--chromium/net/dns/dns_config_service_posix_unittest.cc4
-rw-r--r--chromium/net/dns/dns_config_service_unittest.cc94
-rw-r--r--chromium/net/dns/dns_config_service_win.cc20
-rw-r--r--chromium/net/dns/dns_config_service_win.h2
-rw-r--r--chromium/net/dns/dns_config_service_win_unittest.cc6
-rw-r--r--chromium/net/dns/dns_config_watcher_mac.cc2
-rw-r--r--chromium/net/dns/dns_config_watcher_mac.h2
-rw-r--r--chromium/net/dns/dns_query.cc10
-rw-r--r--chromium/net/dns/dns_response.cc10
-rw-r--r--chromium/net/dns/dns_test_util.cc4
-rw-r--r--chromium/net/dns/dns_transaction.cc7
-rw-r--r--chromium/net/dns/dns_transaction_unittest.cc1
-rw-r--r--chromium/net/dns/host_resolver.cc33
-rw-r--r--chromium/net/dns/host_resolver.h18
-rw-r--r--chromium/net/dns/host_resolver_impl.cc77
-rw-r--r--chromium/net/dns/host_resolver_impl.h36
-rw-r--r--chromium/net/dns/host_resolver_impl_unittest.cc110
-rw-r--r--chromium/net/dns/mdns_cache.h1
-rw-r--r--chromium/net/dns/mdns_client.cc2
-rw-r--r--chromium/net/dns/mdns_client.h5
-rw-r--r--chromium/net/dns/mdns_client_impl.cc143
-rw-r--r--chromium/net/dns/mdns_client_impl.h19
-rw-r--r--chromium/net/dns/mdns_client_unittest.cc48
-rw-r--r--chromium/net/dns/mock_mdns_socket_factory.h6
-rw-r--r--chromium/net/dns/record_parsed.cc2
-rw-r--r--chromium/net/dns/record_rdata.cc4
-rw-r--r--chromium/net/dns/record_rdata.h1
-rw-r--r--chromium/net/filter/filter.cc (renamed from chromium/net/base/filter.cc)25
-rw-r--r--chromium/net/filter/filter.h (renamed from chromium/net/base/filter.h)16
-rw-r--r--chromium/net/filter/filter_unittest.cc (renamed from chromium/net/base/filter_unittest.cc)23
-rw-r--r--chromium/net/filter/gzip_filter.cc (renamed from chromium/net/base/gzip_filter.cc)6
-rw-r--r--chromium/net/filter/gzip_filter.h (renamed from chromium/net/base/gzip_filter.h)10
-rw-r--r--chromium/net/filter/gzip_filter_unittest.cc (renamed from chromium/net/base/gzip_filter_unittest.cc)6
-rw-r--r--chromium/net/filter/gzip_header.cc (renamed from chromium/net/base/gzip_header.cc)4
-rw-r--r--chromium/net/filter/gzip_header.h (renamed from chromium/net/base/gzip_header.h)8
-rw-r--r--chromium/net/filter/mock_filter_context.cc (renamed from chromium/net/base/mock_filter_context.cc)20
-rw-r--r--chromium/net/filter/mock_filter_context.h (renamed from chromium/net/base/mock_filter_context.h)28
-rw-r--r--chromium/net/filter/sdch_filter.cc (renamed from chromium/net/base/sdch_filter.cc)40
-rw-r--r--chromium/net/filter/sdch_filter.h (renamed from chromium/net/base/sdch_filter.h)17
-rw-r--r--chromium/net/filter/sdch_filter_unittest.cc (renamed from chromium/net/base/sdch_filter_unittest.cc)491
-rw-r--r--chromium/net/ftp/ftp_auth_cache_unittest.cc1
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser.cc2
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_ls.cc7
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_netware.cc6
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_netware_unittest.cc4
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_os2.cc2
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_unittest.cc2
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_unittest.h4
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_vms.cc12
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc2
-rw-r--r--chromium/net/ftp/ftp_directory_listing_parser_windows.cc2
-rw-r--r--chromium/net/ftp/ftp_network_transaction.cc14
-rw-r--r--chromium/net/ftp/ftp_network_transaction_unittest.cc8
-rw-r--r--chromium/net/ftp/ftp_util.cc3
-rw-r--r--chromium/net/ftp/ftp_util_unittest.cc3
-rw-r--r--chromium/net/http/disk_cache_based_quic_server_info.cc295
-rw-r--r--chromium/net/http/disk_cache_based_quic_server_info.h106
-rw-r--r--chromium/net/http/disk_cache_based_quic_server_info_unittest.cc282
-rw-r--r--chromium/net/http/failing_http_transaction_factory.cc191
-rw-r--r--chromium/net/http/failing_http_transaction_factory.h42
-rw-r--r--chromium/net/http/http_auth.cc53
-rw-r--r--chromium/net/http/http_auth.h42
-rw-r--r--chromium/net/http/http_auth_cache.cc41
-rw-r--r--chromium/net/http/http_auth_cache.h6
-rw-r--r--chromium/net/http/http_auth_cache_unittest.cc6
-rw-r--r--chromium/net/http/http_auth_challenge_tokenizer.cc61
-rw-r--r--chromium/net/http/http_auth_challenge_tokenizer.h61
-rw-r--r--chromium/net/http/http_auth_challenge_tokenizer_unittest.cc177
-rw-r--r--chromium/net/http/http_auth_controller_unittest.cc5
-rw-r--r--chromium/net/http/http_auth_filter_win.h10
-rw-r--r--chromium/net/http/http_auth_gssapi_posix.cc3
-rw-r--r--chromium/net/http/http_auth_gssapi_posix.h4
-rw-r--r--chromium/net/http/http_auth_gssapi_posix_unittest.cc33
-rw-r--r--chromium/net/http/http_auth_handler.cc3
-rw-r--r--chromium/net/http/http_auth_handler.h7
-rw-r--r--chromium/net/http/http_auth_handler_basic.cc22
-rw-r--r--chromium/net/http/http_auth_handler_basic.h8
-rw-r--r--chromium/net/http/http_auth_handler_basic_unittest.cc9
-rw-r--r--chromium/net/http/http_auth_handler_digest.cc21
-rw-r--r--chromium/net/http/http_auth_handler_digest.h8
-rw-r--r--chromium/net/http/http_auth_handler_digest_unittest.cc24
-rw-r--r--chromium/net/http/http_auth_handler_factory.cc7
-rw-r--r--chromium/net/http/http_auth_handler_factory.h5
-rw-r--r--chromium/net/http/http_auth_handler_factory_unittest.cc2
-rw-r--r--chromium/net/http/http_auth_handler_mock.cc7
-rw-r--r--chromium/net/http/http_auth_handler_mock.h6
-rw-r--r--chromium/net/http/http_auth_handler_negotiate.cc6
-rw-r--r--chromium/net/http/http_auth_handler_negotiate.h6
-rw-r--r--chromium/net/http/http_auth_handler_ntlm.cc7
-rw-r--r--chromium/net/http/http_auth_handler_ntlm.h8
-rw-r--r--chromium/net/http/http_auth_handler_ntlm_portable.cc24
-rw-r--r--chromium/net/http/http_auth_handler_ntlm_win.cc2
-rw-r--r--chromium/net/http/http_auth_handler_unittest.cc6
-rw-r--r--chromium/net/http/http_auth_sspi_win.cc3
-rw-r--r--chromium/net/http/http_auth_sspi_win.h4
-rw-r--r--chromium/net/http/http_auth_sspi_win_unittest.cc33
-rw-r--r--chromium/net/http/http_auth_unittest.cc170
-rw-r--r--chromium/net/http/http_basic_stream.cc8
-rw-r--r--chromium/net/http/http_basic_stream.h2
-rw-r--r--chromium/net/http/http_byte_range.h4
-rw-r--r--chromium/net/http/http_cache.cc86
-rw-r--r--chromium/net/http/http_cache.h43
-rw-r--r--chromium/net/http/http_cache_transaction.cc182
-rw-r--r--chromium/net/http/http_cache_transaction.h27
-rw-r--r--chromium/net/http/http_cache_unittest.cc682
-rw-r--r--chromium/net/http/http_chunked_decoder.h3
-rw-r--r--chromium/net/http/http_content_disposition.cc44
-rw-r--r--chromium/net/http/http_content_disposition_unittest.cc4
-rw-r--r--chromium/net/http/http_log_util.cc81
-rw-r--r--chromium/net/http/http_log_util.h24
-rw-r--r--chromium/net/http/http_log_util_unittest.cc76
-rw-r--r--chromium/net/http/http_network_layer.cc3
-rw-r--r--chromium/net/http/http_network_layer.h3
-rw-r--r--chromium/net/http/http_network_layer_unittest.cc427
-rw-r--r--chromium/net/http/http_network_session.cc86
-rw-r--r--chromium/net/http/http_network_session.h54
-rw-r--r--chromium/net/http/http_network_transaction.cc264
-rw-r--r--chromium/net/http/http_network_transaction.h29
-rw-r--r--chromium/net/http/http_network_transaction_unittest.cc1236
-rw-r--r--chromium/net/http/http_pipelined_connection.h93
-rw-r--r--chromium/net/http/http_pipelined_connection_impl.cc845
-rw-r--r--chromium/net/http/http_pipelined_connection_impl.h330
-rw-r--r--chromium/net/http/http_pipelined_connection_impl_unittest.cc1597
-rw-r--r--chromium/net/http/http_pipelined_host.cc17
-rw-r--r--chromium/net/http/http_pipelined_host.h102
-rw-r--r--chromium/net/http/http_pipelined_host_capability.h23
-rw-r--r--chromium/net/http/http_pipelined_host_forced.cc103
-rw-r--r--chromium/net/http/http_pipelined_host_forced.h83
-rw-r--r--chromium/net/http/http_pipelined_host_forced_unittest.cc106
-rw-r--r--chromium/net/http/http_pipelined_host_impl.cc210
-rw-r--r--chromium/net/http/http_pipelined_host_impl.h117
-rw-r--r--chromium/net/http/http_pipelined_host_impl_unittest.cc308
-rw-r--r--chromium/net/http/http_pipelined_host_pool.cc145
-rw-r--r--chromium/net/http/http_pipelined_host_pool.h102
-rw-r--r--chromium/net/http/http_pipelined_host_pool_unittest.cc262
-rw-r--r--chromium/net/http/http_pipelined_host_test_util.cc33
-rw-r--r--chromium/net/http/http_pipelined_host_test_util.h70
-rw-r--r--chromium/net/http/http_pipelined_network_transaction_unittest.cc1035
-rw-r--r--chromium/net/http/http_pipelined_stream.cc153
-rw-r--r--chromium/net/http/http_pipelined_stream.h115
-rw-r--r--chromium/net/http/http_proxy_client_socket.cc6
-rw-r--r--chromium/net/http/http_proxy_client_socket.h4
-rw-r--r--chromium/net/http/http_proxy_client_socket_pool.cc21
-rw-r--r--chromium/net/http/http_proxy_client_socket_pool_unittest.cc15
-rw-r--r--chromium/net/http/http_request_headers.cc21
-rw-r--r--chromium/net/http/http_request_info.cc2
-rw-r--r--chromium/net/http/http_response_body_drainer.cc25
-rw-r--r--chromium/net/http/http_response_body_drainer.h4
-rw-r--r--chromium/net/http/http_response_body_drainer_unittest.cc19
-rw-r--r--chromium/net/http/http_response_headers.cc118
-rw-r--r--chromium/net/http/http_response_headers.h42
-rw-r--r--chromium/net/http/http_response_headers_unittest.cc211
-rw-r--r--chromium/net/http/http_response_info.cc20
-rw-r--r--chromium/net/http/http_response_info.h11
-rw-r--r--chromium/net/http/http_security_headers.cc2
-rw-r--r--chromium/net/http/http_security_headers_unittest.cc261
-rw-r--r--chromium/net/http/http_server_properties.cc35
-rw-r--r--chromium/net/http/http_server_properties.h101
-rw-r--r--chromium/net/http/http_server_properties_impl.cc330
-rw-r--r--chromium/net/http/http_server_properties_impl.h104
-rw-r--r--chromium/net/http/http_server_properties_impl_unittest.cc231
-rw-r--r--chromium/net/http/http_stream.h10
-rw-r--r--chromium/net/http/http_stream_base.h27
-rw-r--r--chromium/net/http/http_stream_factory.cc360
-rw-r--r--chromium/net/http/http_stream_factory.h84
-rw-r--r--chromium/net/http/http_stream_factory_impl.cc76
-rw-r--r--chromium/net/http/http_stream_factory_impl.h24
-rw-r--r--chromium/net/http/http_stream_factory_impl_job.cc402
-rw-r--r--chromium/net/http/http_stream_factory_impl_job.h31
-rw-r--r--chromium/net/http/http_stream_factory_impl_request.cc118
-rw-r--r--chromium/net/http/http_stream_factory_impl_request.h14
-rw-r--r--chromium/net/http/http_stream_factory_impl_request_unittest.cc3
-rw-r--r--chromium/net/http/http_stream_factory_impl_unittest.cc112
-rw-r--r--chromium/net/http/http_stream_parser.cc436
-rw-r--r--chromium/net/http/http_stream_parser.h44
-rw-r--r--chromium/net/http/http_stream_parser_unittest.cc57
-rw-r--r--chromium/net/http/http_transaction.h19
-rw-r--r--chromium/net/http/http_transaction_delegate.h26
-rw-r--r--chromium/net/http/http_transaction_factory.h10
-rw-r--r--chromium/net/http/http_transaction_test_util.cc (renamed from chromium/net/http/http_transaction_unittest.cc)160
-rw-r--r--chromium/net/http/http_transaction_test_util.h (renamed from chromium/net/http/http_transaction_unittest.h)26
-rw-r--r--chromium/net/http/http_util.cc105
-rw-r--r--chromium/net/http/http_util.h4
-rw-r--r--chromium/net/http/mock_http_cache.cc4
-rw-r--r--chromium/net/http/mock_http_cache.h5
-rw-r--r--chromium/net/http/partial_data.cc36
-rw-r--r--chromium/net/http/proxy_connect_redirect_http_stream.cc6
-rw-r--r--chromium/net/http/proxy_connect_redirect_http_stream.h1
-rw-r--r--chromium/net/http/transport_security_persister.cc104
-rw-r--r--chromium/net/http/transport_security_persister_unittest.cc63
-rw-r--r--chromium/net/http/transport_security_state.cc261
-rw-r--r--chromium/net/http/transport_security_state.h165
-rw-r--r--chromium/net/http/transport_security_state_static.h115
-rw-r--r--chromium/net/http/transport_security_state_static.json118
-rw-r--r--chromium/net/http/transport_security_state_unittest.cc624
-rw-r--r--chromium/net/http/url_security_manager_win.cc2
-rw-r--r--chromium/net/net.gyp2020
-rw-r--r--chromium/net/net.gypi1717
-rw-r--r--chromium/net/net.isolate10
-rw-r--r--chromium/net/net_nacl.gyp44
-rw-r--r--chromium/net/net_unittests.isolate14
-rw-r--r--chromium/net/ocsp/nss_ocsp_unittest.cc11
-rw-r--r--chromium/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc8
-rw-r--r--chromium/net/proxy/dhcp_proxy_script_fetcher_win.cc5
-rw-r--r--chromium/net/proxy/mock_proxy_script_fetcher.cc2
-rw-r--r--chromium/net/proxy/multi_threaded_proxy_resolver.cc25
-rw-r--r--chromium/net/proxy/multi_threaded_proxy_resolver.h1
-rw-r--r--chromium/net/proxy/multi_threaded_proxy_resolver_unittest.cc29
-rw-r--r--chromium/net/proxy/network_delegate_error_observer_unittest.cc6
-rw-r--r--chromium/net/proxy/proxy_bypass_rules.cc12
-rw-r--r--chromium/net/proxy/proxy_config.cc2
-rw-r--r--chromium/net/proxy/proxy_config_service_android.cc9
-rw-r--r--chromium/net/proxy/proxy_config_service_linux.cc38
-rw-r--r--chromium/net/proxy/proxy_config_service_linux_unittest.cc8
-rw-r--r--chromium/net/proxy/proxy_config_service_win.cc6
-rw-r--r--chromium/net/proxy/proxy_info.h7
-rw-r--r--chromium/net/proxy/proxy_list.cc57
-rw-r--r--chromium/net/proxy/proxy_list.h24
-rw-r--r--chromium/net/proxy/proxy_list_unittest.cc113
-rw-r--r--chromium/net/proxy/proxy_resolver.h5
-rw-r--r--chromium/net/proxy/proxy_resolver_perftest.cc3
-rw-r--r--chromium/net/proxy/proxy_resolver_script_data.cc2
-rw-r--r--chromium/net/proxy/proxy_resolver_v8.cc110
-rw-r--r--chromium/net/proxy/proxy_resolver_v8.h23
-rw-r--r--chromium/net/proxy/proxy_resolver_v8_tracing.cc16
-rw-r--r--chromium/net/proxy/proxy_resolver_v8_tracing.h1
-rw-r--r--chromium/net/proxy/proxy_resolver_v8_tracing_unittest.cc6
-rw-r--r--chromium/net/proxy/proxy_resolver_v8_unittest.cc5
-rw-r--r--chromium/net/proxy/proxy_resolver_winhttp.cc11
-rw-r--r--chromium/net/proxy/proxy_retry_info.h5
-rw-r--r--chromium/net/proxy/proxy_script_decider.cc12
-rw-r--r--chromium/net/proxy/proxy_script_decider.h9
-rw-r--r--chromium/net/proxy/proxy_script_decider_unittest.cc27
-rw-r--r--chromium/net/proxy/proxy_script_fetcher_impl.cc15
-rw-r--r--chromium/net/proxy/proxy_script_fetcher_impl_unittest.cc28
-rw-r--r--chromium/net/proxy/proxy_server.cc22
-rw-r--r--chromium/net/proxy/proxy_server.h8
-rw-r--r--chromium/net/proxy/proxy_service.cc75
-rw-r--r--chromium/net/proxy/proxy_service.h51
-rw-r--r--chromium/net/proxy/proxy_service_unittest.cc59
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.cc78
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.h64
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator_test.cc109
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.cc110
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.h61
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator_test.cc224
-rw-r--r--chromium/net/quic/congestion_control/cube_root.h3
-rw-r--r--chromium/net/quic/congestion_control/cubic.cc163
-rw-r--r--chromium/net/quic/congestion_control/cubic.h15
-rw-r--r--chromium/net/quic/congestion_control/cubic_test.cc162
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.cc3
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.h3
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.cc86
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.h36
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_test.cc96
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.cc133
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.h61
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start_test.cc59
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc176
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h65
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc404
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc256
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h173
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc1114
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.cc122
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.h61
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe_test.cc84
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.cc48
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.h46
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc55
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.cc529
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.h102
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender_test.cc569
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.cc163
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.h94
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc126
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.cc10
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.h3
-rw-r--r--chromium/net/quic/congestion_control/loss_detection_interface.cc25
-rw-r--r--chromium/net/quic/congestion_control/loss_detection_interface.h42
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.cc57
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.h44
-rw-r--r--chromium/net/quic/congestion_control/paced_sender_test.cc78
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender.cc108
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender.h34
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender_test.cc143
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map.h77
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map_test.cc66
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.cc6
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.h5
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats.cc129
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats.h112
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats_test.cc160
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.cc17
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.h93
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_simulator.cc273
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_simulator.h133
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.cc343
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.h92
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc527
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm.cc79
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm.h46
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc183
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.cc13
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.h5
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver_test.cc6
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm.cc69
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm.h52
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm_test.cc138
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter.h107
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter_nss.cc154
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter_openssl.cc143
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter.h110
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter_nss.cc162
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter_openssl.cc148
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h50
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc185
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc141
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc70
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h50
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc188
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc164
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc60
-rw-r--r--chromium/net/quic/crypto/cert_compressor.h3
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter.h47
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc80
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc31
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc132
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter.h47
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc80
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc31
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc108
-rw-r--r--chromium/net/quic/crypto/channel_id.h66
-rw-r--r--chromium/net/quic/crypto/channel_id_test.cc14
-rw-r--r--chromium/net/quic/crypto/crypto_framer.cc18
-rw-r--r--chromium/net/quic/crypto/crypto_framer.h4
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.cc310
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.h132
-rw-r--r--chromium/net/quic/crypto/crypto_handshake_message.cc324
-rw-r--r--chromium/net/quic/crypto/crypto_handshake_message.h135
-rw-r--r--chromium/net/quic/crypto/crypto_protocol.h131
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.cc78
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.h4
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.cc3
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.h40
-rw-r--r--chromium/net/quic/crypto/crypto_server_test.cc323
-rw-r--r--chromium/net/quic/crypto/crypto_utils.cc4
-rw-r--r--chromium/net/quic/crypto/crypto_utils.h3
-rw-r--r--chromium/net/quic/crypto/curve25519_key_exchange.cc1
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client.cc8
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client.h5
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client_test.cc15
-rw-r--r--chromium/net/quic/crypto/null_decrypter.h2
-rw-r--r--chromium/net/quic/crypto/null_encrypter.h2
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange.h2
-rw-r--r--chromium/net/quic/crypto/proof_test.cc169
-rw-r--r--chromium/net/quic/crypto/proof_verifier.cc15
-rw-r--r--chromium/net/quic/crypto/proof_verifier.h64
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.cc167
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.h71
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config.cc268
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config.h126
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config_test.cc210
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config.cc531
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config.h124
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config_test.cc419
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.cc4
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.h1
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.cc4
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.h1
-rw-r--r--chromium/net/quic/crypto/quic_random.cc5
-rw-r--r--chromium/net/quic/crypto/quic_server_info.cc141
-rw-r--r--chromium/net/quic/crypto/quic_server_info.h118
-rw-r--r--chromium/net/quic/crypto/scoped_evp_aead_ctx.cc23
-rw-r--r--chromium/net/quic/crypto/scoped_evp_aead_ctx.h31
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc22
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.h30
-rw-r--r--chromium/net/quic/crypto/source_address_token.h2
-rw-r--r--chromium/net/quic/crypto/strike_register.cc12
-rw-r--r--chromium/net/quic/crypto/strike_register.h5
-rw-r--r--chromium/net/quic/crypto/strike_register_client.h5
-rw-r--r--chromium/net/quic/iovector.h1
-rw-r--r--chromium/net/quic/quic_ack_notifier.cc62
-rw-r--r--chromium/net/quic/quic_ack_notifier.h58
-rw-r--r--chromium/net/quic/quic_ack_notifier_manager.cc8
-rw-r--r--chromium/net/quic/quic_ack_notifier_manager.h6
-rw-r--r--chromium/net/quic/quic_ack_notifier_test.cc54
-rw-r--r--chromium/net/quic/quic_address_mismatch.cc52
-rw-r--r--chromium/net/quic/quic_address_mismatch.h44
-rw-r--r--chromium/net/quic/quic_address_mismatch_test.cc113
-rw-r--r--chromium/net/quic/quic_bandwidth.h1
-rw-r--r--chromium/net/quic/quic_blocked_writer_interface.h5
-rw-r--r--chromium/net/quic/quic_client_session.cc423
-rw-r--r--chromium/net/quic/quic_client_session.h48
-rw-r--r--chromium/net/quic/quic_client_session_base.cc16
-rw-r--r--chromium/net/quic/quic_client_session_base.h41
-rw-r--r--chromium/net/quic/quic_client_session_test.cc40
-rw-r--r--chromium/net/quic/quic_clock.cc3
-rw-r--r--chromium/net/quic/quic_clock.h3
-rw-r--r--chromium/net/quic/quic_config.cc632
-rw-r--r--chromium/net/quic/quic_config.h311
-rw-r--r--chromium/net/quic/quic_config_test.cc158
-rw-r--r--chromium/net/quic/quic_connection.cc1231
-rw-r--r--chromium/net/quic/quic_connection.h475
-rw-r--r--chromium/net/quic/quic_connection_helper.h2
-rw-r--r--chromium/net/quic/quic_connection_logger.cc448
-rw-r--r--chromium/net/quic/quic_connection_logger.h87
-rw-r--r--chromium/net/quic/quic_connection_stats.cc45
-rw-r--r--chromium/net/quic/quic_connection_stats.h67
-rw-r--r--chromium/net/quic/quic_connection_test.cc2592
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.cc174
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.h32
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_factory.h7
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_test.cc27
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.cc23
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.h14
-rw-r--r--chromium/net/quic/quic_crypto_server_stream_test.cc20
-rw-r--r--chromium/net/quic/quic_crypto_stream.cc8
-rw-r--r--chromium/net/quic/quic_crypto_stream.h5
-rw-r--r--chromium/net/quic/quic_crypto_stream_test.cc6
-rw-r--r--chromium/net/quic/quic_data_reader.h2
-rw-r--r--chromium/net/quic/quic_data_stream.cc226
-rw-r--r--chromium/net/quic/quic_data_stream.h51
-rw-r--r--chromium/net/quic/quic_data_stream_test.cc615
-rw-r--r--chromium/net/quic/quic_data_writer.cc5
-rw-r--r--chromium/net/quic/quic_data_writer.h2
-rw-r--r--chromium/net/quic/quic_data_writer_test.cc10
-rw-r--r--chromium/net/quic/quic_default_packet_writer.cc17
-rw-r--r--chromium/net/quic/quic_default_packet_writer.h22
-rw-r--r--chromium/net/quic/quic_dispatcher.cc426
-rw-r--r--chromium/net/quic/quic_dispatcher.h230
-rw-r--r--chromium/net/quic/quic_end_to_end_unittest.cc29
-rw-r--r--chromium/net/quic/quic_fec_group.cc30
-rw-r--r--chromium/net/quic/quic_fec_group.h37
-rw-r--r--chromium/net/quic/quic_fec_group_test.cc59
-rw-r--r--chromium/net/quic/quic_flags.cc48
-rw-r--r--chromium/net/quic/quic_flags.h20
-rw-r--r--chromium/net/quic/quic_flow_controller.cc203
-rw-r--r--chromium/net/quic/quic_flow_controller.h130
-rw-r--r--chromium/net/quic/quic_flow_controller_test.cc224
-rw-r--r--chromium/net/quic/quic_framer.cc800
-rw-r--r--chromium/net/quic/quic_framer.h166
-rw-r--r--chromium/net/quic/quic_framer_test.cc2172
-rw-r--r--chromium/net/quic/quic_headers_stream.cc266
-rw-r--r--chromium/net/quic/quic_headers_stream.h82
-rw-r--r--chromium/net/quic/quic_headers_stream_test.cc334
-rw-r--r--chromium/net/quic/quic_http_stream.cc58
-rw-r--r--chromium/net/quic/quic_http_stream.h8
-rw-r--r--chromium/net/quic/quic_http_stream_test.cc436
-rw-r--r--chromium/net/quic/quic_http_utils.cc12
-rw-r--r--chromium/net/quic/quic_http_utils.h10
-rw-r--r--chromium/net/quic/quic_in_memory_cache.cc242
-rw-r--r--chromium/net/quic/quic_in_memory_cache.h116
-rw-r--r--chromium/net/quic/quic_network_transaction_unittest.cc691
-rw-r--r--chromium/net/quic/quic_packet_creator.cc362
-rw-r--r--chromium/net/quic/quic_packet_creator.h145
-rw-r--r--chromium/net/quic/quic_packet_creator_test.cc572
-rw-r--r--chromium/net/quic/quic_packet_generator.cc204
-rw-r--r--chromium/net/quic/quic_packet_generator.h93
-rw-r--r--chromium/net/quic/quic_packet_generator_test.cc379
-rw-r--r--chromium/net/quic/quic_packet_writer.h13
-rw-r--r--chromium/net/quic/quic_protocol.cc471
-rw-r--r--chromium/net/quic/quic_protocol.h424
-rw-r--r--chromium/net/quic/quic_protocol_test.cc31
-rw-r--r--chromium/net/quic/quic_received_packet_manager.cc249
-rw-r--r--chromium/net/quic/quic_received_packet_manager.h125
-rw-r--r--chromium/net/quic/quic_received_packet_manager_test.cc238
-rw-r--r--chromium/net/quic/quic_reliable_client_stream.cc10
-rw-r--r--chromium/net/quic/quic_reliable_client_stream_test.cc67
-rw-r--r--chromium/net/quic/quic_sent_entropy_manager.h2
-rw-r--r--chromium/net/quic/quic_sent_packet_manager.cc1044
-rw-r--r--chromium/net/quic/quic_sent_packet_manager.h245
-rw-r--r--chromium/net/quic/quic_sent_packet_manager_test.cc1473
-rw-r--r--chromium/net/quic/quic_server_id.cc58
-rw-r--r--chromium/net/quic/quic_server_id.h60
-rw-r--r--chromium/net/quic/quic_server_id_test.cc319
-rw-r--r--chromium/net/quic/quic_server_packet_writer.cc74
-rw-r--r--chromium/net/quic/quic_server_packet_writer.h53
-rw-r--r--chromium/net/quic/quic_server_session.cc81
-rw-r--r--chromium/net/quic/quic_server_session.h87
-rw-r--r--chromium/net/quic/quic_session.cc553
-rw-r--r--chromium/net/quic/quic_session.h146
-rw-r--r--chromium/net/quic/quic_session_test.cc791
-rw-r--r--chromium/net/quic/quic_socket_address_coder.cc89
-rw-r--r--chromium/net/quic/quic_socket_address_coder.h45
-rw-r--r--chromium/net/quic/quic_socket_address_coder_test.cc117
-rw-r--r--chromium/net/quic/quic_spdy_compressor.cc71
-rw-r--r--chromium/net/quic/quic_spdy_compressor.h49
-rw-r--r--chromium/net/quic/quic_spdy_compressor_test.cc46
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.cc140
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.h65
-rw-r--r--chromium/net/quic/quic_spdy_decompressor_test.cc103
-rw-r--r--chromium/net/quic/quic_spdy_server_stream.cc143
-rw-r--r--chromium/net/quic/quic_spdy_server_stream.h64
-rw-r--r--chromium/net/quic/quic_stream_factory.cc744
-rw-r--r--chromium/net/quic/quic_stream_factory.h148
-rw-r--r--chromium/net/quic/quic_stream_factory_test.cc893
-rw-r--r--chromium/net/quic/quic_stream_sequencer.cc205
-rw-r--r--chromium/net/quic/quic_stream_sequencer.h77
-rw-r--r--chromium/net/quic/quic_stream_sequencer_test.cc486
-rw-r--r--chromium/net/quic/quic_time.cc19
-rw-r--r--chromium/net/quic/quic_time.h9
-rw-r--r--chromium/net/quic/quic_time_test.cc24
-rw-r--r--chromium/net/quic/quic_time_wait_list_manager.cc283
-rw-r--r--chromium/net/quic/quic_time_wait_list_manager.h174
-rw-r--r--chromium/net/quic/quic_types.cc34
-rw-r--r--chromium/net/quic/quic_types.h69
-rw-r--r--chromium/net/quic/quic_unacked_packet_map.cc323
-rw-r--r--chromium/net/quic/quic_unacked_packet_map.h152
-rw-r--r--chromium/net/quic/quic_unacked_packet_map_test.cc166
-rw-r--r--chromium/net/quic/quic_utils.cc31
-rw-r--r--chromium/net/quic/quic_utils.h10
-rw-r--r--chromium/net/quic/quic_utils_chromium.h80
-rw-r--r--chromium/net/quic/quic_utils_chromium_test.cc50
-rw-r--r--chromium/net/quic/quic_write_blocked_list.cc20
-rw-r--r--chromium/net/quic/quic_write_blocked_list.h113
-rw-r--r--chromium/net/quic/quic_write_blocked_list_test.cc117
-rw-r--r--chromium/net/quic/reliable_quic_stream.cc380
-rw-r--r--chromium/net/quic/reliable_quic_stream.h108
-rw-r--r--chromium/net/quic/reliable_quic_stream_test.cc569
-rw-r--r--chromium/net/quic/spdy_utils.cc2
-rw-r--r--chromium/net/quic/spdy_utils.h3
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.cc33
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.h21
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_chromium.cc9
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_nss.cc91
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_openssl.cc83
-rw-r--r--chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc2
-rw-r--r--chromium/net/quic/test_tools/delayed_verify_strike_register_client.h4
-rw-r--r--chromium/net/quic/test_tools/mock_clock.h3
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.cc35
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.h15
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc14
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h15
-rw-r--r--chromium/net/quic/test_tools/mock_quic_dispatcher.cc23
-rw-r--r--chromium/net/quic/test_tools/mock_quic_dispatcher.h37
-rw-r--r--chromium/net/quic/test_tools/mock_random.h4
-rw-r--r--chromium/net/quic/test_tools/quic_config_peer.cc45
-rw-r--r--chromium/net/quic/test_tools/quic_config_peer.h42
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.cc66
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.h28
-rw-r--r--chromium/net/quic/test_tools/quic_data_stream_peer.h1
-rw-r--r--chromium/net/quic/test_tools/quic_flow_controller_peer.cc61
-rw-r--r--chromium/net/quic/test_tools/quic_flow_controller_peer.h42
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.cc26
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.h7
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.cc6
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.h2
-rw-r--r--chromium/net/quic/test_tools/quic_packet_generator_peer.cc20
-rw-r--r--chromium/net/quic/test_tools/quic_packet_generator_peer.h29
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc6
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.h2
-rw-r--r--chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc90
-rw-r--r--chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h31
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.cc20
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.h13
-rw-r--r--chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc28
-rw-r--r--chromium/net/quic/test_tools/quic_stream_sequencer_peer.h31
-rw-r--r--chromium/net/quic/test_tools/quic_test_packet_maker.cc249
-rw-r--r--chromium/net/quic/test_tools/quic_test_packet_maker.h91
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.cc326
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.h346
-rw-r--r--chromium/net/quic/test_tools/quic_test_writer.cc23
-rw-r--r--chromium/net/quic/test_tools/quic_test_writer.h33
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.cc35
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.h8
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.cc95
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.h12
-rw-r--r--chromium/net/server/http_server.cc33
-rw-r--r--chromium/net/server/http_server.h4
-rw-r--r--chromium/net/server/http_server_request_info.cc17
-rw-r--r--chromium/net/server/http_server_request_info.h11
-rw-r--r--chromium/net/server/http_server_unittest.cc223
-rw-r--r--chromium/net/socket/buffered_write_stream_socket.cc161
-rw-r--r--chromium/net/socket/buffered_write_stream_socket.h83
-rw-r--r--chromium/net/socket/buffered_write_stream_socket_unittest.cc124
-rw-r--r--chromium/net/socket/client_socket_handle.cc4
-rw-r--r--chromium/net/socket/client_socket_handle.h16
-rw-r--r--chromium/net/socket/client_socket_pool_base.cc36
-rw-r--r--chromium/net/socket/client_socket_pool_base.h18
-rw-r--r--chromium/net/socket/client_socket_pool_base_unittest.cc70
-rw-r--r--chromium/net/socket/client_socket_pool_manager.cc7
-rw-r--r--chromium/net/socket/next_proto.cc73
-rw-r--r--chromium/net/socket/next_proto.h30
-rw-r--r--chromium/net/socket/nss_ssl_util.cc12
-rw-r--r--chromium/net/socket/openssl_ssl_util.cc156
-rw-r--r--chromium/net/socket/openssl_ssl_util.h32
-rw-r--r--chromium/net/socket/socket.h8
-rw-r--r--chromium/net/socket/socket_test_util.cc73
-rw-r--r--chromium/net/socket/socket_test_util.h23
-rw-r--r--chromium/net/socket/socks5_client_socket.cc50
-rw-r--r--chromium/net/socket/socks5_client_socket.h7
-rw-r--r--chromium/net/socket/socks_client_socket.cc48
-rw-r--r--chromium/net/socket/socks_client_socket.h10
-rw-r--r--chromium/net/socket/ssl_client_socket.cc16
-rw-r--r--chromium/net/socket/ssl_client_socket.h10
-rw-r--r--chromium/net/socket/ssl_client_socket_nss.cc177
-rw-r--r--chromium/net/socket/ssl_client_socket_nss.h15
-rw-r--r--chromium/net/socket/ssl_client_socket_openssl.cc435
-rw-r--r--chromium/net/socket/ssl_client_socket_openssl.h28
-rw-r--r--chromium/net/socket/ssl_client_socket_openssl_unittest.cc89
-rw-r--r--chromium/net/socket/ssl_client_socket_pool.cc2
-rw-r--r--chromium/net/socket/ssl_client_socket_pool_unittest.cc16
-rw-r--r--chromium/net/socket/ssl_client_socket_unittest.cc999
-rw-r--r--chromium/net/socket/ssl_server_socket_nss.cc45
-rw-r--r--chromium/net/socket/ssl_server_socket_nss.h6
-rw-r--r--chromium/net/socket/ssl_server_socket_openssl.cc671
-rw-r--r--chromium/net/socket/ssl_server_socket_openssl.h150
-rw-r--r--chromium/net/socket/ssl_server_socket_unittest.cc21
-rw-r--r--chromium/net/socket/stream_listen_socket.cc28
-rw-r--r--chromium/net/socket/stream_listen_socket.h6
-rw-r--r--chromium/net/socket/stream_socket.h6
-rw-r--r--chromium/net/socket/tcp_client_socket.cc4
-rw-r--r--chromium/net/socket/tcp_client_socket.h4
-rw-r--r--chromium/net/socket/tcp_socket.cc49
-rw-r--r--chromium/net/socket/tcp_socket_libevent.cc51
-rw-r--r--chromium/net/socket/tcp_socket_libevent.h4
-rw-r--r--chromium/net/socket/tcp_socket_unittest.cc55
-rw-r--r--chromium/net/socket/tcp_socket_win.cc46
-rw-r--r--chromium/net/socket/tcp_socket_win.h11
-rw-r--r--chromium/net/socket/transport_client_socket_pool.cc4
-rw-r--r--chromium/net/socket/transport_client_socket_pool_unittest.cc12
-rw-r--r--chromium/net/socket_stream/OWNERS7
-rw-r--r--chromium/net/socket_stream/socket_stream.cc84
-rw-r--r--chromium/net/socket_stream/socket_stream.h17
-rw-r--r--chromium/net/socket_stream/socket_stream_job.cc24
-rw-r--r--chromium/net/socket_stream/socket_stream_job.h16
-rw-r--r--chromium/net/socket_stream/socket_stream_job_manager.cc10
-rw-r--r--chromium/net/socket_stream/socket_stream_job_manager.h3
-rw-r--r--chromium/net/socket_stream/socket_stream_unittest.cc83
-rw-r--r--chromium/net/spdy/buffered_spdy_framer.cc136
-rw-r--r--chromium/net/spdy/buffered_spdy_framer.h32
-rw-r--r--chromium/net/spdy/buffered_spdy_framer_unittest.cc76
-rw-r--r--chromium/net/spdy/fuzzing/hpack_example_generator.cc75
-rw-r--r--chromium/net/spdy/fuzzing/hpack_fuzz_mutator.cc81
-rw-r--r--chromium/net/spdy/fuzzing/hpack_fuzz_util.cc189
-rw-r--r--chromium/net/spdy/fuzzing/hpack_fuzz_util.h93
-rw-r--r--chromium/net/spdy/fuzzing/hpack_fuzz_util_test.cc151
-rw-r--r--chromium/net/spdy/fuzzing/hpack_fuzz_wrapper.cc59
-rw-r--r--chromium/net/spdy/hpack_constants.cc331
-rw-r--r--chromium/net/spdy/hpack_constants.h86
-rw-r--r--chromium/net/spdy/hpack_decoder.cc240
-rw-r--r--chromium/net/spdy/hpack_decoder.h121
-rw-r--r--chromium/net/spdy/hpack_decoder_test.cc670
-rw-r--r--chromium/net/spdy/hpack_encoder.cc291
-rw-r--r--chromium/net/spdy/hpack_encoder.h108
-rw-r--r--chromium/net/spdy/hpack_encoder_test.cc453
-rw-r--r--chromium/net/spdy/hpack_entry.cc59
-rw-r--r--chromium/net/spdy/hpack_entry.h98
-rw-r--r--chromium/net/spdy/hpack_entry_test.cc130
-rw-r--r--chromium/net/spdy/hpack_header_table.cc306
-rw-r--r--chromium/net/spdy/hpack_header_table.h151
-rw-r--r--chromium/net/spdy/hpack_header_table_test.cc517
-rw-r--r--chromium/net/spdy/hpack_huffman_aggregator.cc183
-rw-r--r--chromium/net/spdy/hpack_huffman_aggregator.h74
-rw-r--r--chromium/net/spdy/hpack_huffman_aggregator_test.cc204
-rw-r--r--chromium/net/spdy/hpack_huffman_table.cc323
-rw-r--r--chromium/net/spdy/hpack_huffman_table.h126
-rw-r--r--chromium/net/spdy/hpack_huffman_table_test.cc522
-rw-r--r--chromium/net/spdy/hpack_input_stream.cc172
-rw-r--r--chromium/net/spdy/hpack_input_stream.h80
-rw-r--r--chromium/net/spdy/hpack_input_stream_test.cc629
-rw-r--r--chromium/net/spdy/hpack_output_stream.cc77
-rw-r--r--chromium/net/spdy/hpack_output_stream.h66
-rw-r--r--chromium/net/spdy/hpack_output_stream_test.cc260
-rw-r--r--chromium/net/spdy/hpack_round_trip_test.cc174
-rw-r--r--chromium/net/spdy/hpack_string_util.cc24
-rw-r--r--chromium/net/spdy/hpack_string_util.h24
-rw-r--r--chromium/net/spdy/hpack_string_util_test.cc98
-rw-r--r--chromium/net/spdy/mock_spdy_framer_visitor.cc17
-rw-r--r--chromium/net/spdy/mock_spdy_framer_visitor.h65
-rw-r--r--chromium/net/spdy/spdy_buffer.cc8
-rw-r--r--chromium/net/spdy/spdy_frame_builder.cc94
-rw-r--r--chromium/net/spdy/spdy_frame_builder.h41
-rw-r--r--chromium/net/spdy/spdy_frame_builder_test.cc55
-rw-r--r--chromium/net/spdy/spdy_frame_reader.cc18
-rw-r--r--chromium/net/spdy/spdy_frame_reader.h21
-rw-r--r--chromium/net/spdy/spdy_framer.cc2054
-rw-r--r--chromium/net/spdy/spdy_framer.h333
-rw-r--r--chromium/net/spdy/spdy_framer_test.cc3612
-rw-r--r--chromium/net/spdy/spdy_header_block.cc6
-rw-r--r--chromium/net/spdy/spdy_header_block_unittest.cc2
-rw-r--r--chromium/net/spdy/spdy_headers_block_parser.cc193
-rw-r--r--chromium/net/spdy/spdy_headers_block_parser.h149
-rw-r--r--chromium/net/spdy/spdy_headers_block_parser_test.cc259
-rw-r--r--chromium/net/spdy/spdy_http_stream.cc19
-rw-r--r--chromium/net/spdy/spdy_http_stream.h5
-rw-r--r--chromium/net/spdy/spdy_http_stream_unittest.cc21
-rw-r--r--chromium/net/spdy/spdy_http_utils.cc38
-rw-r--r--chromium/net/spdy/spdy_http_utils.h10
-rw-r--r--chromium/net/spdy/spdy_http_utils_unittest.cc10
-rw-r--r--chromium/net/spdy/spdy_network_transaction_unittest.cc729
-rw-r--r--chromium/net/spdy/spdy_pinnable_buffer_piece.cc34
-rw-r--r--chromium/net/spdy/spdy_pinnable_buffer_piece.h60
-rw-r--r--chromium/net/spdy/spdy_pinnable_buffer_piece_test.cc79
-rw-r--r--chromium/net/spdy/spdy_prefixed_buffer_reader.cc81
-rw-r--r--chromium/net/spdy/spdy_prefixed_buffer_reader.h41
-rw-r--r--chromium/net/spdy/spdy_prefixed_buffer_reader_test.cc129
-rw-r--r--chromium/net/spdy/spdy_priority_forest.h1
-rw-r--r--chromium/net/spdy/spdy_protocol.cc764
-rw-r--r--chromium/net/spdy/spdy_protocol.h413
-rw-r--r--chromium/net/spdy/spdy_protocol_test.cc24
-rw-r--r--chromium/net/spdy/spdy_proxy_client_socket.cc45
-rw-r--r--chromium/net/spdy/spdy_proxy_client_socket.h15
-rw-r--r--chromium/net/spdy/spdy_proxy_client_socket_unittest.cc266
-rw-r--r--chromium/net/spdy/spdy_session.cc1134
-rw-r--r--chromium/net/spdy/spdy_session.h274
-rw-r--r--chromium/net/spdy/spdy_session_key.cc2
-rw-r--r--chromium/net/spdy/spdy_session_pool.cc61
-rw-r--r--chromium/net/spdy/spdy_session_pool.h11
-rw-r--r--chromium/net/spdy/spdy_session_pool_unittest.cc176
-rw-r--r--chromium/net/spdy/spdy_session_unittest.cc957
-rw-r--r--chromium/net/spdy/spdy_stream.cc402
-rw-r--r--chromium/net/spdy/spdy_stream.h113
-rw-r--r--chromium/net/spdy/spdy_stream_unittest.cc35
-rw-r--r--chromium/net/spdy/spdy_test_util_common.cc461
-rw-r--r--chromium/net/spdy/spdy_test_util_common.h57
-rw-r--r--chromium/net/spdy/spdy_test_utils.cc33
-rw-r--r--chromium/net/spdy/spdy_test_utils.h10
-rw-r--r--chromium/net/spdy/spdy_websocket_stream.cc6
-rw-r--r--chromium/net/spdy/spdy_websocket_stream.h3
-rw-r--r--chromium/net/spdy/spdy_websocket_stream_unittest.cc86
-rw-r--r--chromium/net/spdy/spdy_websocket_test_util.cc4
-rw-r--r--chromium/net/spdy/spdy_websocket_test_util.h1
-rw-r--r--chromium/net/spdy/spdy_write_queue.cc52
-rw-r--r--chromium/net/spdy/spdy_write_queue.h2
-rw-r--r--chromium/net/spdy/spdy_write_queue_unittest.cc124
-rw-r--r--chromium/net/ssl/client_cert_store.h3
-rw-r--r--chromium/net/ssl/client_cert_store_chromeos.cc94
-rw-r--r--chromium/net/ssl/client_cert_store_chromeos.h66
-rw-r--r--chromium/net/ssl/client_cert_store_chromeos_unittest.cc189
-rw-r--r--chromium/net/ssl/client_cert_store_mac.cc3
-rw-r--r--chromium/net/ssl/client_cert_store_nss.cc88
-rw-r--r--chromium/net/ssl/client_cert_store_nss.h21
-rw-r--r--chromium/net/ssl/openssl_client_key_store.h2
-rw-r--r--chromium/net/ssl/server_bound_cert_service_unittest.cc8
-rw-r--r--chromium/net/ssl/signed_certificate_timestamp_and_status.cc2
-rw-r--r--chromium/net/ssl/signed_certificate_timestamp_and_status.h12
-rw-r--r--chromium/net/ssl/ssl_cert_request_info.cc2
-rw-r--r--chromium/net/ssl/ssl_cert_request_info.h3
-rw-r--r--chromium/net/ssl/ssl_cipher_suite_names.cc48
-rw-r--r--chromium/net/ssl/ssl_cipher_suite_names.h11
-rw-r--r--chromium/net/ssl/ssl_cipher_suite_names_unittest.cc16
-rw-r--r--chromium/net/ssl/ssl_client_auth_cache.cc6
-rw-r--r--chromium/net/ssl/ssl_client_auth_cache.h9
-rw-r--r--chromium/net/ssl/ssl_client_auth_cache_unittest.cc16
-rw-r--r--chromium/net/ssl/ssl_config.cc69
-rw-r--r--chromium/net/ssl/ssl_config.h156
-rw-r--r--chromium/net/ssl/ssl_config_service.cc95
-rw-r--r--chromium/net/ssl/ssl_config_service.h160
-rw-r--r--chromium/net/ssl/ssl_config_service_defaults.cc1
-rw-r--r--chromium/net/ssl/ssl_config_service_unittest.cc5
-rw-r--r--chromium/net/ssl/ssl_connection_status_flags.h27
-rw-r--r--chromium/net/ssl/ssl_connection_status_flags_unittest.cc37
-rw-r--r--chromium/net/ssl/ssl_info.cc2
-rw-r--r--chromium/net/ssl/ssl_info.h5
-rw-r--r--chromium/net/test/OWNERS3
-rw-r--r--chromium/net/test/android/OWNERS1
-rw-r--r--chromium/net/test/ct_test_util.cc27
-rw-r--r--chromium/net/test/ct_test_util.h7
-rw-r--r--chromium/net/test/embedded_test_server/embedded_test_server.cc1
-rw-r--r--chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc4
-rw-r--r--chromium/net/test/gtest_util.h99
-rw-r--r--chromium/net/test/net_test_suite.cc134
-rw-r--r--chromium/net/test/python_utils.cc4
-rw-r--r--chromium/net/test/python_utils.h5
-rw-r--r--chromium/net/test/python_utils_unittest.cc4
-rw-r--r--chromium/net/test/run_all_unittests.cc17
-rw-r--r--chromium/net/test/scoped_disable_exit_on_dfatal.cc33
-rw-r--r--chromium/net/test/scoped_disable_exit_on_dfatal.h39
-rw-r--r--chromium/net/test/scoped_mock_log.cc58
-rw-r--r--chromium/net/test/scoped_mock_log.h98
-rw-r--r--chromium/net/test/spawned_test_server/base_test_server.cc44
-rw-r--r--chromium/net/test/spawned_test_server/base_test_server.h26
-rw-r--r--chromium/net/test/spawned_test_server/local_test_server.cc9
-rw-r--r--chromium/net/test/spawned_test_server/local_test_server.h8
-rw-r--r--chromium/net/test/spawned_test_server/local_test_server_posix.cc14
-rw-r--r--chromium/net/test/spawned_test_server/local_test_server_win.cc4
-rw-r--r--chromium/net/test/spawned_test_server/remote_test_server.cc7
-rw-r--r--chromium/net/test/spawned_test_server/spawner_communicator.cc2
-rw-r--r--chromium/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp4
-rw-r--r--chromium/net/third_party/nss/README.chromium110
-rw-r--r--chromium/net/third_party/nss/patches/aesgcm.patch1363
-rw-r--r--chromium/net/third_party/nss/patches/aesgcmchromium.patch15
-rw-r--r--chromium/net/third_party/nss/patches/alpn.patch245
-rwxr-xr-xchromium/net/third_party/nss/patches/applypatches.sh49
-rw-r--r--chromium/net/third_party/nss/patches/cachecerts.patch33
-rw-r--r--chromium/net/third_party/nss/patches/cachelocks.patch149
-rw-r--r--chromium/net/third_party/nss/patches/canfalsestart.patch837
-rw-r--r--chromium/net/third_party/nss/patches/cbc.patch81
-rw-r--r--chromium/net/third_party/nss/patches/chacha20poly1305.patch142
-rw-r--r--chromium/net/third_party/nss/patches/channelid.patch239
-rw-r--r--chromium/net/third_party/nss/patches/channelid2.patch155
-rw-r--r--chromium/net/third_party/nss/patches/cipherorder.patch62
-rw-r--r--chromium/net/third_party/nss/patches/ciphersuiteversion.patch169
-rw-r--r--chromium/net/third_party/nss/patches/clientauth.patch100
-rw-r--r--chromium/net/third_party/nss/patches/didhandshakeresume.patch12
-rw-r--r--chromium/net/third_party/nss/patches/disableticketrenewal.patch17
-rw-r--r--chromium/net/third_party/nss/patches/ecpointform.patch19
-rw-r--r--chromium/net/third_party/nss/patches/fallbackscsv.patch162
-rw-r--r--chromium/net/third_party/nss/patches/getrequestedclientcerttypes.patch28
-rw-r--r--chromium/net/third_party/nss/patches/ignorechangecipherspec.patch19
-rw-r--r--chromium/net/third_party/nss/patches/negotiatedextension.patch27
-rw-r--r--chromium/net/third_party/nss/patches/nssrwlock.patch235
-rw-r--r--chromium/net/third_party/nss/patches/nullcipher_934016.patch16
-rw-r--r--chromium/net/third_party/nss/patches/paddingextension.patch142
-rw-r--r--chromium/net/third_party/nss/patches/paddingextensionall.patch26
-rw-r--r--chromium/net/third_party/nss/patches/paddingextvalue.patch16
-rw-r--r--chromium/net/third_party/nss/patches/peercertchain.patch67
-rw-r--r--chromium/net/third_party/nss/patches/peercertchain2.patch107
-rw-r--r--chromium/net/third_party/nss/patches/renegoscsv.patch15
-rw-r--r--chromium/net/third_party/nss/patches/reorderextensions.patch34
-rw-r--r--chromium/net/third_party/nss/patches/restartclientauth.patch30
-rw-r--r--chromium/net/third_party/nss/patches/resumeclienthelloversion.patch31
-rw-r--r--chromium/net/third_party/nss/patches/secitemarray.patch10
-rw-r--r--chromium/net/third_party/nss/patches/secretexporterlocks.patch10
-rw-r--r--chromium/net/third_party/nss/patches/sessioncache.patch53
-rw-r--r--chromium/net/third_party/nss/patches/signedcertificatetimestamps.patch311
-rw-r--r--chromium/net/third_party/nss/patches/sslnoncestatics.patch15
-rw-r--r--chromium/net/third_party/nss/patches/sslsock_903565.patch20
-rw-r--r--chromium/net/third_party/nss/patches/suitebonly.patch8
-rw-r--r--chromium/net/third_party/nss/patches/tls12backuphash.patch220
-rw-r--r--chromium/net/third_party/nss/patches/tls12backuphash2.patch127
-rw-r--r--chromium/net/third_party/nss/patches/tls12chromium.patch20
-rw-r--r--chromium/net/third_party/nss/patches/tlsunique.patch24
-rw-r--r--chromium/net/third_party/nss/patches/versionskew.patch45
-rw-r--r--chromium/net/third_party/nss/ssl.gyp2
-rw-r--r--chromium/net/third_party/nss/ssl/BUILD.gn139
-rw-r--r--chromium/net/third_party/nss/ssl/ssl.h36
-rw-r--r--chromium/net/third_party/nss/ssl/ssl3con.c449
-rw-r--r--chromium/net/third_party/nss/ssl/ssl3ecc.c9
-rw-r--r--chromium/net/third_party/nss/ssl/ssl3ext.c89
-rw-r--r--chromium/net/third_party/nss/ssl/ssl3gthr.c25
-rw-r--r--chromium/net/third_party/nss/ssl/sslauth.c11
-rw-r--r--chromium/net/third_party/nss/ssl/sslcon.c8
-rw-r--r--chromium/net/third_party/nss/ssl/sslenum.c125
-rw-r--r--chromium/net/third_party/nss/ssl/sslimpl.h111
-rw-r--r--chromium/net/third_party/nss/ssl/sslinit.c5
-rw-r--r--chromium/net/third_party/nss/ssl/sslnonce.c124
-rw-r--r--chromium/net/third_party/nss/ssl/sslplatf.c88
-rw-r--r--chromium/net/third_party/nss/ssl/sslsecur.c9
-rw-r--r--chromium/net/third_party/nss/ssl/sslsock.c143
-rw-r--r--chromium/net/third_party/nss/ssl/sslt.h2
-rw-r--r--chromium/net/tools/DEPS4
-rw-r--r--chromium/net/tools/balsa/balsa_enums.h5
-rw-r--r--chromium/net/tools/balsa/balsa_frame.cc15
-rw-r--r--chromium/net/tools/balsa/balsa_frame.h2
-rw-r--r--chromium/net/tools/balsa/balsa_headers.cc25
-rw-r--r--chromium/net/tools/balsa/balsa_headers.h2
-rw-r--r--chromium/net/tools/balsa/string_piece_utils.h29
-rw-r--r--chromium/net/tools/crash_cache/crash_cache.cc6
-rw-r--r--chromium/net/tools/crl_set_dump/crl_set_dump.cc3
-rw-r--r--chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc5
-rw-r--r--chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc2
-rw-r--r--chromium/net/tools/dump_cache/cache_dumper.cc2
-rw-r--r--chromium/net/tools/dump_cache/cache_dumper.h2
-rw-r--r--chromium/net/tools/dump_cache/dump_cache.cc9
-rw-r--r--chromium/net/tools/dump_cache/dump_files.cc25
-rw-r--r--chromium/net/tools/dump_cache/upgrade_win.cc8
-rw-r--r--chromium/net/tools/fetch/fetch_client.cc231
-rw-r--r--chromium/net/tools/fetch/fetch_server.cc57
-rw-r--r--chromium/net/tools/fetch/http_listen_socket.cc249
-rw-r--r--chromium/net/tools/fetch/http_listen_socket.h70
-rw-r--r--chromium/net/tools/fetch/http_server.cc12
-rw-r--r--chromium/net/tools/fetch/http_server.h26
-rw-r--r--chromium/net/tools/fetch/http_server_request_info.cc11
-rw-r--r--chromium/net/tools/fetch/http_server_request_info.h26
-rw-r--r--chromium/net/tools/fetch/http_server_response_info.cc11
-rw-r--r--chromium/net/tools/fetch/http_server_response_info.h39
-rw-r--r--chromium/net/tools/fetch/http_session.cc33
-rw-r--r--chromium/net/tools/fetch/http_session.h27
-rw-r--r--chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc4
-rw-r--r--chromium/net/tools/flip_server/loadtime_measurement.h54
-rw-r--r--chromium/net/tools/flip_server/sm_connection.cc4
-rw-r--r--chromium/net/tools/flip_server/sm_connection.h3
-rw-r--r--chromium/net/tools/flip_server/spdy_interface.cc48
-rw-r--r--chromium/net/tools/flip_server/spdy_interface.h15
-rw-r--r--chromium/net/tools/flip_server/spdy_interface_test.cc93
-rw-r--r--chromium/net/tools/flip_server/spdy_ssl.cc4
-rw-r--r--chromium/net/tools/gdig/file_net_log.cc2
-rw-r--r--chromium/net/tools/gdig/gdig.cc18
-rw-r--r--chromium/net/tools/get_server_time/get_server_time.cc47
-rw-r--r--chromium/net/tools/net_watcher/net_watcher.cc10
-rw-r--r--chromium/net/tools/quic/end_to_end_test.cc696
-rw-r--r--chromium/net/tools/quic/quic_client.cc121
-rw-r--r--chromium/net/tools/quic/quic_client.h92
-rw-r--r--chromium/net/tools/quic/quic_client_bin.cc57
-rw-r--r--chromium/net/tools/quic/quic_client_session.cc25
-rw-r--r--chromium/net/tools/quic/quic_client_session.h14
-rw-r--r--chromium/net/tools/quic/quic_client_session_test.cc24
-rw-r--r--chromium/net/tools/quic/quic_default_packet_writer.cc29
-rw-r--r--chromium/net/tools/quic/quic_default_packet_writer.h23
-rw-r--r--chromium/net/tools/quic/quic_dispatcher.cc381
-rw-r--r--chromium/net/tools/quic/quic_dispatcher.h149
-rw-r--r--chromium/net/tools/quic/quic_dispatcher_test.cc320
-rw-r--r--chromium/net/tools/quic/quic_epoll_clock.h4
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache.cc27
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache.h16
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache_test.cc2
-rw-r--r--chromium/net/tools/quic/quic_packet_writer_wrapper.cc48
-rw-r--r--chromium/net/tools/quic/quic_packet_writer_wrapper.h50
-rw-r--r--chromium/net/tools/quic/quic_server.cc63
-rw-r--r--chromium/net/tools/quic/quic_server.h42
-rw-r--r--chromium/net/tools/quic/quic_server_bin.cc10
-rw-r--r--chromium/net/tools/quic/quic_server_session.cc31
-rw-r--r--chromium/net/tools/quic/quic_server_session.h25
-rw-r--r--chromium/net/tools/quic/quic_server_session_test.cc217
-rw-r--r--chromium/net/tools/quic/quic_server_test.cc32
-rw-r--r--chromium/net/tools/quic/quic_socket_utils.cc68
-rw-r--r--chromium/net/tools/quic/quic_socket_utils.h31
-rw-r--r--chromium/net/tools/quic/quic_spdy_client_stream.cc43
-rw-r--r--chromium/net/tools/quic/quic_spdy_client_stream.h12
-rw-r--r--chromium/net/tools/quic/quic_spdy_client_stream_test.cc36
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream.cc38
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream.h3
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream_test.cc146
-rw-r--r--chromium/net/tools/quic/quic_time_wait_list_manager.cc301
-rw-r--r--chromium/net/tools/quic/quic_time_wait_list_manager.h198
-rw-r--r--chromium/net/tools/quic/quic_time_wait_list_manager_test.cc439
-rw-r--r--chromium/net/tools/quic/spdy_utils.cc8
-rw-r--r--chromium/net/tools/quic/spdy_utils.h3
-rw-r--r--chromium/net/tools/quic/test_tools/http_message.cc (renamed from chromium/net/tools/quic/test_tools/http_message_test_utils.cc)4
-rw-r--r--chromium/net/tools/quic/test_tools/http_message.h (renamed from chromium/net/tools/quic/test_tools/http_message_test_utils.h)8
-rw-r--r--chromium/net/tools/quic/test_tools/mock_epoll_server.h12
-rw-r--r--chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc11
-rw-r--r--chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h9
-rw-r--r--chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc78
-rw-r--r--chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h38
-rw-r--r--chromium/net/tools/quic/test_tools/quic_client_peer.cc14
-rw-r--r--chromium/net/tools/quic/test_tools/quic_client_peer.h12
-rw-r--r--chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc21
-rw-r--r--chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h20
-rw-r--r--chromium/net/tools/quic/test_tools/quic_server_peer.cc5
-rw-r--r--chromium/net/tools/quic/test_tools/quic_server_peer.h6
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_client.cc349
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_client.h162
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_utils.cc60
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_utils.h100
-rw-r--r--chromium/net/tools/quic/test_tools/server_thread.cc45
-rw-r--r--chromium/net/tools/quic/test_tools/server_thread.h21
-rw-r--r--chromium/net/tools/quic/test_tools/simple_client.cc36
-rw-r--r--chromium/net/tools/quic/test_tools/simple_client.h159
-rw-r--r--chromium/net/tools/testserver/run_testserver.cc7
-rwxr-xr-xchromium/net/tools/testserver/testserver.py142
-rw-r--r--chromium/net/tools/testserver/testserver_base.py8
-rw-r--r--chromium/net/tools/tld_cleanup/BUILD.gn14
-rw-r--r--chromium/net/tools/tld_cleanup/PRESUBMIT.py32
-rw-r--r--chromium/net/tools/tld_cleanup/README13
-rwxr-xr-xchromium/net/tools/tld_cleanup/make_dafsa.py469
-rwxr-xr-xchromium/net/tools/tld_cleanup/make_dafsa_unittest.py757
-rw-r--r--chromium/net/tools/tld_cleanup/tld_cleanup.cc2
-rw-r--r--chromium/net/tools/tld_cleanup/tld_cleanup_util.cc4
-rw-r--r--chromium/net/udp/datagram_server_socket.h9
-rw-r--r--chromium/net/udp/udp_client_socket.cc4
-rw-r--r--chromium/net/udp/udp_client_socket.h4
-rw-r--r--chromium/net/udp/udp_server_socket.cc8
-rw-r--r--chromium/net/udp/udp_server_socket.h5
-rw-r--r--chromium/net/udp/udp_socket_libevent.cc45
-rw-r--r--chromium/net/udp/udp_socket_libevent.h7
-rw-r--r--chromium/net/udp/udp_socket_win.cc60
-rw-r--r--chromium/net/udp/udp_socket_win.h9
-rw-r--r--chromium/net/url_request/file_protocol_handler.cc2
-rw-r--r--chromium/net/url_request/http_user_agent_settings.h5
-rw-r--r--chromium/net/url_request/protocol_intercept_job_factory.cc47
-rw-r--r--chromium/net/url_request/protocol_intercept_job_factory.h51
-rw-r--r--chromium/net/url_request/static_http_user_agent_settings.cc2
-rw-r--r--chromium/net/url_request/static_http_user_agent_settings.h2
-rw-r--r--chromium/net/url_request/test_url_fetcher_factory.cc50
-rw-r--r--chromium/net/url_request/test_url_fetcher_factory.h8
-rw-r--r--chromium/net/url_request/url_fetcher.h20
-rw-r--r--chromium/net/url_request/url_fetcher_core.cc15
-rw-r--r--chromium/net/url_request/url_fetcher_core.h5
-rw-r--r--chromium/net/url_request/url_fetcher_impl.cc10
-rw-r--r--chromium/net/url_request/url_fetcher_impl.h6
-rw-r--r--chromium/net/url_request/url_fetcher_impl_unittest.cc2
-rw-r--r--chromium/net/url_request/url_fetcher_response_writer.cc32
-rw-r--r--chromium/net/url_request/url_fetcher_response_writer.h8
-rw-r--r--chromium/net/url_request/url_fetcher_response_writer_unittest.cc180
-rw-r--r--chromium/net/url_request/url_range_request_job.cc31
-rw-r--r--chromium/net/url_request/url_range_request_job.h43
-rw-r--r--chromium/net/url_request/url_request.cc188
-rw-r--r--chromium/net/url_request/url_request.h125
-rw-r--r--chromium/net/url_request/url_request_context.cc18
-rw-r--r--chromium/net/url_request/url_request_context.h27
-rw-r--r--chromium/net/url_request/url_request_context_builder.cc119
-rw-r--r--chromium/net/url_request/url_request_context_builder.h40
-rw-r--r--chromium/net/url_request/url_request_context_builder_unittest.cc45
-rw-r--r--chromium/net/url_request/url_request_context_getter.cc18
-rw-r--r--chromium/net/url_request/url_request_context_getter.h29
-rw-r--r--chromium/net/url_request/url_request_file_dir_job.cc2
-rw-r--r--chromium/net/url_request/url_request_file_job.cc54
-rw-r--r--chromium/net/url_request/url_request_file_job.h13
-rw-r--r--chromium/net/url_request/url_request_file_job_unittest.cc253
-rw-r--r--chromium/net/url_request/url_request_filter.cc166
-rw-r--r--chromium/net/url_request/url_request_filter.h81
-rw-r--r--chromium/net/url_request/url_request_filter_unittest.cc100
-rw-r--r--chromium/net/url_request/url_request_ftp_job.cc9
-rw-r--r--chromium/net/url_request/url_request_ftp_job.h4
-rw-r--r--chromium/net/url_request/url_request_ftp_job_unittest.cc11
-rw-r--r--chromium/net/url_request/url_request_http_job.cc268
-rw-r--r--chromium/net/url_request/url_request_http_job.h22
-rw-r--r--chromium/net/url_request/url_request_http_job_unittest.cc6
-rw-r--r--chromium/net/url_request/url_request_intercepting_job_factory.cc49
-rw-r--r--chromium/net/url_request/url_request_intercepting_job_factory.h58
-rw-r--r--chromium/net/url_request/url_request_interceptor.cc15
-rw-r--r--chromium/net/url_request/url_request_interceptor.h37
-rw-r--r--chromium/net/url_request/url_request_job.cc276
-rw-r--r--chromium/net/url_request/url_request_job.h37
-rw-r--r--chromium/net/url_request/url_request_job_factory_impl.cc22
-rw-r--r--chromium/net/url_request/url_request_job_factory_impl.h15
-rw-r--r--chromium/net/url_request/url_request_job_manager.cc79
-rw-r--r--chromium/net/url_request/url_request_job_manager.h22
-rw-r--r--chromium/net/url_request/url_request_job_unittest.cc68
-rw-r--r--chromium/net/url_request/url_request_redirect_job.cc20
-rw-r--r--chromium/net/url_request/url_request_redirect_job.h10
-rw-r--r--chromium/net/url_request/url_request_simple_job.cc27
-rw-r--r--chromium/net/url_request/url_request_simple_job.h5
-rw-r--r--chromium/net/url_request/url_request_simple_job_unittest.cc141
-rw-r--r--chromium/net/url_request/url_request_test_job.cc16
-rw-r--r--chromium/net/url_request/url_request_test_job.h5
-rw-r--r--chromium/net/url_request/url_request_test_util.cc36
-rw-r--r--chromium/net/url_request/url_request_test_util.h40
-rw-r--r--chromium/net/url_request/url_request_throttler_simulation_unittest.cc2
-rw-r--r--chromium/net/url_request/url_request_throttler_unittest.cc80
-rw-r--r--chromium/net/url_request/url_request_unittest.cc972
-rw-r--r--chromium/net/url_request/view_cache_helper_unittest.cc3
-rw-r--r--chromium/net/websockets/OWNERS9
-rw-r--r--chromium/net/websockets/README4
-rw-r--r--chromium/net/websockets/websocket_basic_handshake_stream.cc480
-rw-r--r--chromium/net/websockets/websocket_basic_handshake_stream.h34
-rw-r--r--chromium/net/websockets/websocket_basic_stream.cc31
-rw-r--r--chromium/net/websockets/websocket_basic_stream_test.cc50
-rw-r--r--chromium/net/websockets/websocket_channel.cc723
-rw-r--r--chromium/net/websockets/websocket_channel.h209
-rw-r--r--chromium/net/websockets/websocket_channel_test.cc1359
-rw-r--r--chromium/net/websockets/websocket_deflate_stream.cc38
-rw-r--r--chromium/net/websockets/websocket_deflate_stream.h2
-rw-r--r--chromium/net/websockets/websocket_deflate_stream_test.cc182
-rw-r--r--chromium/net/websockets/websocket_deflater.cc1
-rw-r--r--chromium/net/websockets/websocket_deflater.h3
-rw-r--r--chromium/net/websockets/websocket_deflater_test.cc6
-rw-r--r--chromium/net/websockets/websocket_event_interface.h61
-rw-r--r--chromium/net/websockets/websocket_frame.cc6
-rw-r--r--chromium/net/websockets/websocket_frame_parser.cc6
-rw-r--r--chromium/net/websockets/websocket_frame_test.cc4
-rw-r--r--chromium/net/websockets/websocket_handshake_handler_spdy_test.cc3
-rw-r--r--chromium/net/websockets/websocket_handshake_request_info.cc19
-rw-r--r--chromium/net/websockets/websocket_handshake_request_info.h33
-rw-r--r--chromium/net/websockets/websocket_handshake_response_info.cc30
-rw-r--r--chromium/net/websockets/websocket_handshake_response_info.h43
-rw-r--r--chromium/net/websockets/websocket_handshake_stream_base.h2
-rw-r--r--chromium/net/websockets/websocket_handshake_stream_create_helper.cc24
-rw-r--r--chromium/net/websockets/websocket_handshake_stream_create_helper.h18
-rw-r--r--chromium/net/websockets/websocket_handshake_stream_create_helper_test.cc67
-rw-r--r--chromium/net/websockets/websocket_job.cc46
-rw-r--r--chromium/net/websockets/websocket_job.h6
-rw-r--r--chromium/net/websockets/websocket_job_test.cc253
-rw-r--r--chromium/net/websockets/websocket_net_log_params_test.cc2
-rw-r--r--chromium/net/websockets/websocket_stream.cc209
-rw-r--r--chromium/net/websockets/websocket_stream.h37
-rw-r--r--chromium/net/websockets/websocket_stream_test.cc791
-rw-r--r--chromium/net/websockets/websocket_test_util.cc62
-rw-r--r--chromium/net/websockets/websocket_test_util.h40
-rw-r--r--chromium/net/websockets/websocket_throttle_test.cc66
1441 files changed, 95964 insertions, 105071 deletions
diff --git a/chromium/net/BUILD.gn b/chromium/net/BUILD.gn
new file mode 100644
index 00000000000..8175b1665fb
--- /dev/null
+++ b/chromium/net/BUILD.gn
@@ -0,0 +1,1295 @@
+# 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.
+
+import("//build/config/crypto.gni")
+import("//build/config/features.gni")
+import("//build/config/ui.gni")
+import("//tools/grit/grit_rule.gni")
+import("//url/config.gni")
+
+if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+} else if (is_mac) {
+ import("//build/config/mac/mac_sdk.gni")
+}
+
+# The list of net files is kept in net.gypi. Read it.
+gypi_values = exec_script(
+ "//build/gypi_to_gn.py",
+ [ rebase_path("net.gypi") ],
+ "scope",
+ [ "net.gypi" ])
+
+# Disable Kerberos on ChromeOS, Android and iOS, at least for now. It needs
+# configuration (krb5.conf and so on).
+use_kerberos = !is_chromeos && !is_android && !is_ios
+
+# The way the cache uses mmap() is inefficient on some Android devices. If
+# this flag is set, we hackily avoid using mmap() in the disk cache. We are
+# pretty confident that mmap-ing the index would not hurt any existing x86
+# android devices, but we cannot be so sure about the variety of ARM devices.
+# So enable it for x86 only for now.
+posix_avoid_mmap = is_android && cpu_arch != "x86"
+
+# WebSockets and socket stream code are used everywhere except iOS.
+enable_websockets = !is_ios
+use_v8_in_net = !is_ios
+enable_built_in_dns = !is_ios
+disable_ftp_support = is_ios
+
+declare_args() {
+ # Disables support for file URLs. File URL support requires use of icu.
+ disable_file_support = false
+}
+
+config("net_config") {
+ defines = []
+ if (posix_avoid_mmap) {
+ defines += [ "POSIX_AVOID_MMAP" ]
+ }
+ if (disable_file_support) {
+ defines += [ "DISABLE_FILE_SUPPORT" ]
+ }
+}
+
+# Disables Windows warning about size to int truncations.
+# TODO(jschuh): crbug.com/167187 fix this and delete this config.
+config("net_win_size_truncation") {
+ if (is_win) {
+ cflags = [ "/wd4267" ]
+ }
+}
+
+component("net") {
+ sources =
+ gypi_values.net_nacl_common_sources +
+ gypi_values.net_non_nacl_sources
+
+ cflags = []
+ defines = [
+ # TODO(GYP) Note that he GYP file supports linux_link_kerberos (defaults to
+ # 0) which implies that we run pkg_config on kerberos and link to that
+ # rather than setting this define which will dynamically open it. That
+ # doesn't seem to be set in the regular builds, so we're skipping this
+ # capability here.
+ "DLOPEN_KERBEROS",
+ "NET_IMPLEMENTATION"
+ ]
+ configs += [ ":net_win_size_truncation" ]
+ direct_dependent_configs = [ ":net_config" ]
+ include_dirs = []
+
+ deps = [
+ ":net_resources",
+ "//base",
+ "//base:i18n",
+ "//base/third_party/dynamic_annotations",
+ "//crypto",
+ "//crypto:platform",
+ "//net/base/registry_controlled_domains",
+ "//sdch",
+ "//third_party/icu",
+ "//third_party/zlib",
+ "//url",
+ ]
+ forward_dependent_configs_from = [
+ "//crypto",
+ "//crypto:platform"
+ ]
+
+ if (use_kerberos) {
+ defines += [ "USE_KERBEROS" ]
+ if (is_android) {
+ include_dirs += [ "/usr/include/kerberosV" ]
+ }
+ } else {
+ sources -= [
+ "http/http_auth_gssapi_posix.cc",
+ "http/http_auth_gssapi_posix.h",
+ "http/http_auth_handler_negotiate.h",
+ "http/http_auth_handler_negotiate.cc",
+ ]
+ }
+
+ if (is_posix) {
+ if (posix_avoid_mmap) {
+ sources -= [ "disk_cache/blockfile/mapped_file_posix.cc" ]
+ } else {
+ sources -= [ "disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc" ]
+ }
+ }
+
+ if (disable_file_support) {
+ sources -= [
+ "base/directory_lister.cc",
+ "base/directory_lister.h",
+ "url_request/url_request_file_dir_job.cc",
+ "url_request/url_request_file_dir_job.h",
+ "url_request/url_request_file_job.cc",
+ "url_request/url_request_file_job.h",
+ "url_request/file_protocol_handler.cc",
+ "url_request/file_protocol_handler.h",
+ ]
+ }
+
+ if (disable_ftp_support) {
+ sources -= [
+ "ftp/ftp_auth_cache.cc",
+ "ftp/ftp_auth_cache.h",
+ "ftp/ftp_ctrl_response_buffer.cc",
+ "ftp/ftp_ctrl_response_buffer.h",
+ "ftp/ftp_directory_listing_parser.cc",
+ "ftp/ftp_directory_listing_parser.h",
+ "ftp/ftp_directory_listing_parser_ls.cc",
+ "ftp/ftp_directory_listing_parser_ls.h",
+ "ftp/ftp_directory_listing_parser_netware.cc",
+ "ftp/ftp_directory_listing_parser_netware.h",
+ "ftp/ftp_directory_listing_parser_os2.cc",
+ "ftp/ftp_directory_listing_parser_os2.h",
+ "ftp/ftp_directory_listing_parser_vms.cc",
+ "ftp/ftp_directory_listing_parser_vms.h",
+ "ftp/ftp_directory_listing_parser_windows.cc",
+ "ftp/ftp_directory_listing_parser_windows.h",
+ "ftp/ftp_network_layer.cc",
+ "ftp/ftp_network_layer.h",
+ "ftp/ftp_network_session.cc",
+ "ftp/ftp_network_session.h",
+ "ftp/ftp_network_transaction.cc",
+ "ftp/ftp_network_transaction.h",
+ "ftp/ftp_request_info.h",
+ "ftp/ftp_response_info.cc",
+ "ftp/ftp_response_info.h",
+ "ftp/ftp_server_type_histograms.cc",
+ "ftp/ftp_server_type_histograms.h",
+ "ftp/ftp_transaction.h",
+ "ftp/ftp_transaction_factory.h",
+ "ftp/ftp_util.cc",
+ "ftp/ftp_util.h",
+ "url_request/ftp_protocol_handler.cc",
+ "url_request/ftp_protocol_handler.h",
+ "url_request/url_request_ftp_job.cc",
+ "url_request/url_request_ftp_job.h",
+ ]
+ }
+
+ if (enable_built_in_dns) {
+ defines += [ "ENABLE_BUILT_IN_DNS" ]
+ } else {
+ sources -= [
+ "dns/address_sorter_posix.cc",
+ "dns/address_sorter_posix.h",
+ "dns/dns_client.cc",
+ ]
+ }
+
+ if (use_openssl) {
+ sources -= [
+ "base/nss_memio.c",
+ "base/nss_memio.h",
+ "cert/ct_log_verifier_nss.cc",
+ "cert/ct_objects_extractor_nss.cc",
+ "cert/jwk_serializer_nss.cc",
+ "cert/scoped_nss_types.h",
+ "cert/x509_util_nss.cc",
+ "cert/x509_util_nss.h",
+ "quic/crypto/aead_base_decrypter_nss.cc",
+ "quic/crypto/aead_base_encrypter_nss.cc",
+ "quic/crypto/aes_128_gcm_12_decrypter_nss.cc",
+ "quic/crypto/aes_128_gcm_12_encrypter_nss.cc",
+ "quic/crypto/chacha20_poly1305_decrypter_nss.cc",
+ "quic/crypto/chacha20_poly1305_encrypter_nss.cc",
+ "quic/crypto/channel_id_nss.cc",
+ "quic/crypto/p256_key_exchange_nss.cc",
+ "socket/nss_ssl_util.cc",
+ "socket/nss_ssl_util.h",
+ "socket/ssl_client_socket_nss.cc",
+ "socket/ssl_client_socket_nss.h",
+ "socket/ssl_server_socket_nss.cc",
+ "socket/ssl_server_socket_nss.h",
+ ]
+ if (is_chromeos) {
+ sources -= [
+ "cert/nss_cert_database_chromeos.cc",
+ "cert/nss_cert_database_chromeos.h",
+ "cert/nss_profile_filter_chromeos.cc",
+ "cert/nss_profile_filter_chromeos.h",
+ ]
+ }
+ if (is_linux) {
+ # These are always removed for non-Linux cases below.
+ sources -= [
+ "base/crypto_module_nss.cc",
+ "base/keygen_handler_nss.cc",
+ "cert/cert_database_nss.cc",
+ "cert/nss_cert_database.cc",
+ "cert/nss_cert_database.h",
+ "cert/x509_certificate_nss.cc",
+ "third_party/mozilla_security_manager/nsKeygenHandler.cpp",
+ "third_party/mozilla_security_manager/nsKeygenHandler.h",
+ "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp",
+ "third_party/mozilla_security_manager/nsNSSCertificateDB.h",
+ "third_party/mozilla_security_manager/nsPKCS12Blob.cpp",
+ "third_party/mozilla_security_manager/nsPKCS12Blob.h",
+ ]
+ }
+ if (is_ios) {
+ # Always removed for !ios below.
+ sources -= [
+ "cert/cert_verify_proc_nss.cc",
+ "cert/cert_verify_proc_nss.h",
+ "cert/test_root_certs_nss.cc",
+ "ocsp/nss_ocsp.cc",
+ "ocsp/nss_ocsp.h",
+ ]
+ }
+ } else {
+ sources -= [
+ "base/crypto_module_openssl.cc",
+ "base/keygen_handler_openssl.cc",
+ "cert/ct_log_verifier_openssl.cc",
+ "cert/ct_objects_extractor_openssl.cc",
+ "cert/jwk_serializer_openssl.cc",
+ "cert/x509_util_openssl.cc",
+ "cert/x509_util_openssl.h",
+ "quic/crypto/aead_base_decrypter_openssl.cc",
+ "quic/crypto/aead_base_encrypter_openssl.cc",
+ "quic/crypto/aes_128_gcm_12_decrypter_openssl.cc",
+ "quic/crypto/aes_128_gcm_12_encrypter_openssl.cc",
+ "quic/crypto/chacha20_poly1305_decrypter_openssl.cc",
+ "quic/crypto/chacha20_poly1305_encrypter_openssl.cc",
+ "quic/crypto/channel_id_openssl.cc",
+ "quic/crypto/p256_key_exchange_openssl.cc",
+ "quic/crypto/scoped_evp_aead_ctx.cc",
+ "quic/crypto/scoped_evp_aead_ctx.h",
+ "socket/openssl_ssl_util.cc",
+ "socket/openssl_ssl_util.h",
+ "socket/ssl_client_socket_openssl.cc",
+ "socket/ssl_client_socket_openssl.h",
+ "socket/ssl_server_socket_openssl.cc",
+ "socket/ssl_server_socket_openssl.h",
+ "socket/ssl_session_cache_openssl.cc",
+ "socket/ssl_session_cache_openssl.h",
+ ]
+ }
+
+ if (!use_openssl_certs) {
+ sources -= [
+ "base/openssl_private_key_store.h",
+ "base/openssl_private_key_store_memory.cc",
+ "cert/cert_database_openssl.cc",
+ "cert/cert_verify_proc_openssl.cc",
+ "cert/cert_verify_proc_openssl.h",
+ "cert/test_root_certs_openssl.cc",
+ "cert/x509_certificate_openssl.cc",
+ "ssl/openssl_client_key_store.cc",
+ "ssl/openssl_client_key_store.h",
+ ]
+ if (is_android) {
+ sources -= [
+ "base/openssl_private_key_store_android.cc",
+ ]
+ }
+ } else if (is_android) {
+ # Android doesn't use these even when using OpenSSL.
+ sources -= [
+ "base/openssl_private_key_store_memory.cc",
+ "cert/cert_database_openssl.cc",
+ "cert/cert_verify_proc_openssl.cc",
+ "cert/test_root_certs_openssl.cc",
+ ]
+ }
+
+ if (use_glib) {
+ configs += [ "//build/config/linux:gconf" ]
+ deps += [ "//build/config/linux:gio" ]
+ }
+
+ if (is_linux) {
+ configs += [ "//build/config/linux:libresolv" ]
+ } else {
+ sources -= [
+ "base/crypto_module_nss.cc",
+ "base/keygen_handler_nss.cc",
+ "cert/cert_database_nss.cc",
+ "cert/nss_cert_database.cc",
+ "cert/nss_cert_database.h",
+ "cert/x509_certificate_nss.cc",
+ "third_party/mozilla_security_manager/nsKeygenHandler.cpp",
+ "third_party/mozilla_security_manager/nsKeygenHandler.h",
+ "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp",
+ "third_party/mozilla_security_manager/nsNSSCertificateDB.h",
+ "third_party/mozilla_security_manager/nsPKCS12Blob.cpp",
+ "third_party/mozilla_security_manager/nsPKCS12Blob.h",
+ ]
+
+ if (!is_ios) {
+ # These files are part of the partial implementation of NSS on iOS so
+ # keep them in that case.
+ sources -= [
+ "cert/test_root_certs_nss.cc",
+ "ocsp/nss_ocsp.cc",
+ "ocsp/nss_ocsp.h",
+ ]
+ }
+ }
+
+ if (!use_nss_certs) {
+ sources -= [
+ "ssl/client_cert_store_nss.cc",
+ "ssl/client_cert_store_nss.h",
+ ]
+ if (!is_ios) {
+ # These files are part of the partial implementation of NSS on iOS so
+ # keep them in that case (even though use_nss_certs is not set).
+ sources -= [
+ "cert/cert_verify_proc_nss.cc",
+ "cert/cert_verify_proc_nss.h",
+ ]
+ }
+ if (is_chromeos) {
+ # These were already removed on non-ChromeOS.
+ sources -= [
+ "ssl/client_cert_store_chromeos.cc",
+ "ssl/client_cert_store_chromeos.h",
+ ]
+ }
+ }
+
+ if (!enable_websockets) {
+ sources -= [
+ "socket_stream/socket_stream.cc",
+ "socket_stream/socket_stream.h",
+ "socket_stream/socket_stream_job.cc",
+ "socket_stream/socket_stream_job.h",
+ "socket_stream/socket_stream_job_manager.cc",
+ "socket_stream/socket_stream_job_manager.h",
+ "socket_stream/socket_stream_metrics.cc",
+ "socket_stream/socket_stream_metrics.h",
+ "spdy/spdy_websocket_stream.cc",
+ "spdy/spdy_websocket_stream.h",
+ "websockets/websocket_basic_handshake_stream.cc",
+ "websockets/websocket_basic_handshake_stream.h",
+ "websockets/websocket_basic_stream.cc",
+ "websockets/websocket_basic_stream.h",
+ "websockets/websocket_channel.cc",
+ "websockets/websocket_channel.h",
+ "websockets/websocket_deflate_predictor.h",
+ "websockets/websocket_deflate_predictor_impl.cc",
+ "websockets/websocket_deflate_predictor_impl.h",
+ "websockets/websocket_deflate_stream.cc",
+ "websockets/websocket_deflate_stream.h",
+ "websockets/websocket_deflater.cc",
+ "websockets/websocket_deflater.h",
+ "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",
+ "websockets/websocket_frame_parser.h",
+ "websockets/websocket_handshake_constants.cc",
+ "websockets/websocket_handshake_constants.h",
+ "websockets/websocket_handshake_handler.cc",
+ "websockets/websocket_handshake_handler.h",
+ "websockets/websocket_handshake_request_info.cc",
+ "websockets/websocket_handshake_request_info.h",
+ "websockets/websocket_handshake_response_info.cc",
+ "websockets/websocket_handshake_response_info.h",
+ "websockets/websocket_handshake_stream_base.h",
+ "websockets/websocket_handshake_stream_create_helper.cc",
+ "websockets/websocket_handshake_stream_create_helper.h",
+ "websockets/websocket_inflater.cc",
+ "websockets/websocket_inflater.h",
+ "websockets/websocket_job.cc",
+ "websockets/websocket_job.h",
+ "websockets/websocket_mux.h",
+ "websockets/websocket_net_log_params.cc",
+ "websockets/websocket_net_log_params.h",
+ "websockets/websocket_stream.cc",
+ "websockets/websocket_stream.h",
+ "websockets/websocket_throttle.cc",
+ "websockets/websocket_throttle.h",
+ ]
+ }
+
+ if (!enable_mdns) {
+ sources -= [
+ "dns/mdns_cache.cc",
+ "dns/mdns_cache.h",
+ "dns/mdns_client.cc",
+ "dns/mdns_client.h",
+ "dns/mdns_client_impl.cc",
+ "dns/mdns_client_impl.h",
+ "dns/record_parsed.cc",
+ "dns/record_parsed.h",
+ "dns/record_rdata.cc",
+ "dns/record_rdata.h",
+ ]
+ }
+
+ if (is_win) {
+ sources -= [
+ "http/http_auth_handler_ntlm_portable.cc",
+ "socket/tcp_socket_libevent.cc",
+ "socket/tcp_socket_libevent.h",
+ "udp/udp_socket_libevent.cc",
+ "udp/udp_socket_libevent.h",
+ ]
+ deps += [
+ "//third_party/nss:nspr",
+ "//third_party/nss",
+ ]
+ } else { # !is_win
+ sources -= [
+ "base/winsock_init.cc",
+ "base/winsock_init.h",
+ "base/winsock_util.cc",
+ "base/winsock_util.h",
+ "proxy/proxy_resolver_winhttp.cc",
+ "proxy/proxy_resolver_winhttp.h",
+ ]
+ }
+
+ if (is_mac) {
+ deps += [
+ "//third_party/nss:nspr",
+ "//third_party/nss",
+ ]
+ libs = [
+ "Foundation.framework",
+ "Security.framework",
+ "SystemConfiguration.framework",
+ "resolv",
+ ]
+ }
+
+ if (is_ios) {
+ # Add back some sources that were otherwise filtered out. iOS additionally
+ # doesn't set USE_NSS but needs some of the files.
+ set_sources_assignment_filter([])
+ sources += [
+ "base/network_change_notifier_mac.cc",
+ "base/network_config_watcher_mac.cc",
+ "base/platform_mime_util_mac.mm",
+ "cert/cert_verify_proc_nss.cc",
+ "cert/cert_verify_proc_nss.h",
+ "cert/test_root_certs_nss.cc",
+ "cert/x509_util_nss.cc",
+ "cert/x509_util_nss.h",
+ "proxy/proxy_resolver_mac.cc",
+ "proxy/proxy_server_mac.cc",
+ "ocsp/nss_ocsp.cc",
+ "ocsp/nss_ocsp.h",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+
+ sources -= [ "disk_cache/blockfile/file_posix.cc" ]
+ deps += [
+ "//third_party/nss",
+ ]
+ libs = [
+ "CFNetwork.framework",
+ "MobileCoreServices.framework",
+ "Security.framework",
+ "SystemConfiguration.framework",
+ "resolv",
+ ]
+ }
+
+ if (is_android) {
+ # Add some Linux sources that were excluded by the filter, but which
+ # are needed.
+ set_sources_assignment_filter([])
+ sources += [
+ "base/platform_mime_util_linux.cc",
+ "base/address_tracker_linux.cc",
+ "base/address_tracker_linux.h",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+
+ if (!is_android_webview_build) {
+ deps += [ ":net_jni_headers" ]
+
+ # The net/android/keystore_openssl.cc source file needs to access an
+ # OpenSSL-internal header.
+ include_dirs = [ "//third_party/openssl" ]
+ }
+ }
+
+ if (use_icu_alternatives_on_android) {
+ deps -= [
+ "//base:i18n",
+ "//third_party/icu",
+ ]
+ sources -= [
+ "base/filename_util_icu.cc",
+ "base/net_string_util_icu.cc",
+ "base/net_util_icu.cc",
+ ]
+ sources += [
+ "base/net_string_util_icu_alternatives_android.cc",
+ "base/net_string_util_icu_alternatives_android.h",
+ ]
+ }
+}
+
+grit("net_resources") {
+ source = "base/net_resources.grd"
+}
+
+static_library("http_server") {
+ sources = [
+ "server/http_connection.cc",
+ "server/http_connection.h",
+ "server/http_server.cc",
+ "server/http_server.h",
+ "server/http_server_request_info.cc",
+ "server/http_server_request_info.h",
+ "server/http_server_response_info.cc",
+ "server/http_server_response_info.h",
+ "server/web_socket.cc",
+ "server/web_socket.h",
+ ]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+}
+
+executable("dump_cache") {
+ sources = [
+ "tools/dump_cache/cache_dumper.cc",
+ "tools/dump_cache/cache_dumper.h",
+ "tools/dump_cache/dump_cache.cc",
+ "tools/dump_cache/dump_files.cc",
+ "tools/dump_cache/dump_files.h",
+ "tools/dump_cache/simple_cache_dumper.cc",
+ "tools/dump_cache/simple_cache_dumper.h",
+ "tools/dump_cache/upgrade_win.cc",
+ "tools/dump_cache/upgrade_win.h",
+ "tools/dump_cache/url_to_filename_encoder.cc",
+ "tools/dump_cache/url_to_filename_encoder.h",
+ "tools/dump_cache/url_utilities.h",
+ "tools/dump_cache/url_utilities.cc",
+ ]
+
+ configs += [ ":net_win_size_truncation" ]
+
+ deps = [
+ "//base",
+ ":net",
+ ":test_support",
+ ]
+}
+
+source_set("test_support") {
+ sources = [
+ "base/capturing_net_log.cc",
+ "base/capturing_net_log.h",
+ "base/load_timing_info_test_util.cc",
+ "base/load_timing_info_test_util.h",
+ "base/mock_file_stream.cc",
+ "base/mock_file_stream.h",
+ "base/test_completion_callback.cc",
+ "base/test_completion_callback.h",
+ "base/test_data_directory.cc",
+ "base/test_data_directory.h",
+ "cert/mock_cert_verifier.cc",
+ "cert/mock_cert_verifier.h",
+ "cookies/cookie_monster_store_test.cc",
+ "cookies/cookie_monster_store_test.h",
+ "cookies/cookie_store_test_callbacks.cc",
+ "cookies/cookie_store_test_callbacks.h",
+ "cookies/cookie_store_test_helpers.cc",
+ "cookies/cookie_store_test_helpers.h",
+ "disk_cache/disk_cache_test_base.cc",
+ "disk_cache/disk_cache_test_base.h",
+ "disk_cache/disk_cache_test_util.cc",
+ "disk_cache/disk_cache_test_util.h",
+ "dns/dns_test_util.cc",
+ "dns/dns_test_util.h",
+ "dns/mock_host_resolver.cc",
+ "dns/mock_host_resolver.h",
+ "dns/mock_mdns_socket_factory.cc",
+ "dns/mock_mdns_socket_factory.h",
+ "http/http_transaction_test_util.cc",
+ "http/http_transaction_test_util.h",
+ "proxy/mock_proxy_resolver.cc",
+ "proxy/mock_proxy_resolver.h",
+ "proxy/mock_proxy_script_fetcher.cc",
+ "proxy/mock_proxy_script_fetcher.h",
+ "proxy/proxy_config_service_common_unittest.cc",
+ "proxy/proxy_config_service_common_unittest.h",
+ "socket/socket_test_util.cc",
+ "socket/socket_test_util.h",
+ "test/cert_test_util.cc",
+ "test/cert_test_util.h",
+ "test/ct_test_util.cc",
+ "test/ct_test_util.h",
+ "test/embedded_test_server/embedded_test_server.cc",
+ "test/embedded_test_server/embedded_test_server.h",
+ "test/embedded_test_server/http_connection.cc",
+ "test/embedded_test_server/http_connection.h",
+ "test/embedded_test_server/http_request.cc",
+ "test/embedded_test_server/http_request.h",
+ "test/embedded_test_server/http_response.cc",
+ "test/embedded_test_server/http_response.h",
+ "test/net_test_suite.cc",
+ "test/net_test_suite.h",
+ "test/python_utils.cc",
+ "test/python_utils.h",
+ "test/spawned_test_server/base_test_server.cc",
+ "test/spawned_test_server/base_test_server.h",
+ "test/spawned_test_server/local_test_server_posix.cc",
+ "test/spawned_test_server/local_test_server_win.cc",
+ "test/spawned_test_server/local_test_server.cc",
+ "test/spawned_test_server/local_test_server.h",
+ "test/spawned_test_server/remote_test_server.cc",
+ "test/spawned_test_server/remote_test_server.h",
+ "test/spawned_test_server/spawned_test_server.h",
+ "test/spawned_test_server/spawner_communicator.cc",
+ "test/spawned_test_server/spawner_communicator.h",
+ "url_request/test_url_fetcher_factory.cc",
+ "url_request/test_url_fetcher_factory.h",
+ "url_request/url_request_test_util.cc",
+ "url_request/url_request_test_util.h",
+ ]
+
+ configs += [ ":net_win_size_truncation" ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//crypto:platform",
+ "//net",
+ "//net/tools/tld_cleanup",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//url",
+ ]
+
+ if (is_ios) {
+ deps += [ "//third_party/nss" ]
+ }
+
+ if (!is_android) {
+ sources -= [
+ "test/spawned_test_server/remote_test_server.cc",
+ "test/spawned_test_server/remote_test_server.h",
+ "test/spawned_test_server/spawner_communicator.cc",
+ "test/spawned_test_server/spawner_communicator.h",
+ ]
+ }
+
+ if (use_v8_in_net) {
+ deps += [ ":net_with_v8" ]
+ }
+
+ if (!enable_mdns) {
+ sources -= [
+ "dns/mock_mdns_socket_factory.cc",
+ "dns/mock_mdns_socket_factory.h",
+ ]
+ }
+
+ forward_dependent_configs_from = deps
+}
+
+if (use_v8_in_net) {
+ component("net_with_v8") {
+ sources = [
+ "proxy/proxy_resolver_v8.cc",
+ "proxy/proxy_resolver_v8.h",
+ "proxy/proxy_resolver_v8_tracing.cc",
+ "proxy/proxy_resolver_v8_tracing.h",
+ "proxy/proxy_service_v8.cc",
+ "proxy/proxy_service_v8.h",
+ ]
+
+ defines = [ "NET_IMPLEMENTATION" ]
+ configs += [
+ ":net_win_size_truncation",
+ "//build/config/compiler:wexit_time_destructors",
+ ]
+
+ deps = [
+ ":net",
+ "//base",
+ "//gin",
+ "//url",
+ "//v8",
+ ]
+ }
+}
+
+if (!is_ios && !is_android) {
+ executable("crash_cache") {
+ sources = [ "tools/crash_cache/crash_cache.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ ":net",
+ ":test_support",
+ "//base",
+ ]
+ }
+
+ executable("crl_set_dump") {
+ sources = [ "tools/crl_set_dump/crl_set_dump.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+
+ executable("dns_fuzz_stub") {
+ sources = [ "tools/dns_fuzz_stub/dns_fuzz_stub.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+
+ executable("gdig") {
+ sources = [
+ "tools/gdig/file_net_log.cc",
+ "tools/gdig/gdig.cc",
+ ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+
+ executable("get_server_time") {
+ sources = [ "tools/get_server_time/get_server_time.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ ":net",
+ "//base",
+ "//base:i18n",
+ "//url",
+ ]
+ }
+
+ if (use_v8_in_net) {
+ executable("net_watcher") {
+ sources = [ "tools/net_watcher/net_watcher.cc" ]
+ deps = [
+ ":net",
+ ":net_with_v8",
+ "//base",
+ ]
+
+ if (is_linux) {
+ configs += [ "//build/config/linux:gconf" ]
+ deps += [ "//build/config/linux:gio" ]
+ }
+ }
+ }
+
+ executable("run_testserver") {
+ sources = [ "tools/testserver/run_testserver.cc" ]
+ deps = [
+ ":net", # TODO(brettw) bug 363749: this shouldn't be necessary. It's not
+ # in the GYP build, and can be removed when the bug is fixed.
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+ }
+
+ executable("stress_cache") {
+ sources = [ "disk_cache/blockfile/stress_cache.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ ":net",
+ ":test_support",
+ "//base",
+ ]
+ }
+
+ executable("tld_cleanup") {
+ sources = [ "tools/tld_cleanup/tld_cleanup.cc" ]
+ configs += [ ":net_win_size_truncation" ]
+ deps = [
+ "//base",
+ "//base:i18n",
+ "//net/tools/tld_cleanup",
+ ]
+ }
+}
+
+if (is_linux) {
+ source_set("balsa") {
+ sources = [
+ "tools/balsa/balsa_enums.h",
+ "tools/balsa/balsa_frame.cc",
+ "tools/balsa/balsa_frame.h",
+ "tools/balsa/balsa_headers.cc",
+ "tools/balsa/balsa_headers.h",
+ "tools/balsa/balsa_headers_token_utils.cc",
+ "tools/balsa/balsa_headers_token_utils.h",
+ "tools/balsa/balsa_visitor_interface.h",
+ "tools/balsa/http_message_constants.cc",
+ "tools/balsa/http_message_constants.h",
+ "tools/balsa/noop_balsa_visitor.h",
+ "tools/balsa/simple_buffer.cc",
+ "tools/balsa/simple_buffer.h",
+ "tools/balsa/split.cc",
+ "tools/balsa/split.h",
+ "tools/balsa/string_piece_utils.h",
+ ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+
+ static_library("epoll_server") {
+ sources = [
+ "tools/epoll_server/epoll_server.cc",
+ "tools/epoll_server/epoll_server.h",
+ ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+
+ static_library("flip_in_mem_edsm_server_base") {
+ sources = [
+ "tools/dump_cache/url_to_filename_encoder.cc",
+ "tools/dump_cache/url_to_filename_encoder.h",
+ "tools/dump_cache/url_utilities.h",
+ "tools/dump_cache/url_utilities.cc",
+ "tools/flip_server/acceptor_thread.h",
+ "tools/flip_server/acceptor_thread.cc",
+ "tools/flip_server/create_listener.cc",
+ "tools/flip_server/create_listener.h",
+ "tools/flip_server/constants.h",
+ "tools/flip_server/flip_config.cc",
+ "tools/flip_server/flip_config.h",
+ "tools/flip_server/http_interface.cc",
+ "tools/flip_server/http_interface.h",
+ "tools/flip_server/loadtime_measurement.h",
+ "tools/flip_server/mem_cache.h",
+ "tools/flip_server/mem_cache.cc",
+ "tools/flip_server/output_ordering.cc",
+ "tools/flip_server/output_ordering.h",
+ "tools/flip_server/ring_buffer.cc",
+ "tools/flip_server/ring_buffer.h",
+ "tools/flip_server/sm_connection.cc",
+ "tools/flip_server/sm_connection.h",
+ "tools/flip_server/sm_interface.h",
+ "tools/flip_server/spdy_ssl.cc",
+ "tools/flip_server/spdy_ssl.h",
+ "tools/flip_server/spdy_interface.cc",
+ "tools/flip_server/spdy_interface.h",
+ "tools/flip_server/spdy_util.cc",
+ "tools/flip_server/spdy_util.h",
+ "tools/flip_server/streamer_interface.cc",
+ "tools/flip_server/streamer_interface.h",
+ ]
+ deps = [
+ ":balsa",
+ ":epoll_server",
+ ":net",
+ "//base",
+ "//third_party/openssl",
+ ]
+ }
+
+ executable("flip_in_mem_edsm_server_unittests") {
+ sources = [
+ "tools/flip_server/flip_test_utils.cc",
+ "tools/flip_server/flip_test_utils.h",
+ "tools/flip_server/http_interface_test.cc",
+ "tools/flip_server/mem_cache_test.cc",
+ "tools/flip_server/run_all_tests.cc",
+ "tools/flip_server/spdy_interface_test.cc",
+ ]
+ deps = [
+ ":flip_in_mem_edsm_server_base",
+ ":net",
+ ":test_support",
+ "//testing/gtest",
+ "//testing/gmock",
+ "//third_party/openssl",
+ ]
+ }
+
+ executable("flip_in_mem_edsm_server") {
+ sources = [ "tools/flip_server/flip_in_mem_edsm_server.cc" ]
+ deps = [
+ ":flip_in_mem_edsm_server_base",
+ ":net",
+ "//base",
+ ]
+ }
+
+ source_set("quic_base") {
+ sources = [
+ "tools/quic/quic_client.cc",
+ "tools/quic/quic_client.h",
+ "tools/quic/quic_client_session.cc",
+ "tools/quic/quic_client_session.h",
+ "tools/quic/quic_default_packet_writer.cc",
+ "tools/quic/quic_default_packet_writer.h",
+ "tools/quic/quic_dispatcher.h",
+ "tools/quic/quic_dispatcher.cc",
+ "tools/quic/quic_epoll_clock.cc",
+ "tools/quic/quic_epoll_clock.h",
+ "tools/quic/quic_epoll_connection_helper.cc",
+ "tools/quic/quic_epoll_connection_helper.h",
+ "tools/quic/quic_in_memory_cache.cc",
+ "tools/quic/quic_in_memory_cache.h",
+ "tools/quic/quic_packet_writer_wrapper.cc",
+ "tools/quic/quic_packet_writer_wrapper.h",
+ "tools/quic/quic_server.cc",
+ "tools/quic/quic_server.h",
+ "tools/quic/quic_server_session.cc",
+ "tools/quic/quic_server_session.h",
+ "tools/quic/quic_socket_utils.cc",
+ "tools/quic/quic_socket_utils.h",
+ "tools/quic/quic_spdy_client_stream.cc",
+ "tools/quic/quic_spdy_client_stream.h",
+ "tools/quic/quic_spdy_server_stream.cc",
+ "tools/quic/quic_spdy_server_stream.h",
+ "tools/quic/quic_time_wait_list_manager.h",
+ "tools/quic/quic_time_wait_list_manager.cc",
+ "tools/quic/spdy_utils.cc",
+ "tools/quic/spdy_utils.h",
+ ]
+ deps = [
+ ":balsa",
+ ":epoll_server",
+ ":net",
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ "//crypto",
+ "//third_party/openssl",
+ "//url",
+ ]
+ }
+
+ executable("quic_client") {
+ sources = [ "tools/quic/quic_client_bin.cc" ]
+ deps = [
+ ":quic_base",
+ ":net",
+ "//base",
+ "//third_party/openssl",
+ ]
+ }
+
+ executable("quic_server") {
+ sources = [ "tools/quic/quic_server_bin.cc" ]
+ deps = [
+ ":quic_base",
+ ":net",
+ "//base",
+ "//third_party/openssl",
+ ]
+ }
+}
+
+if (is_android) {
+ generate_jni("net_jni_headers") {
+ sources = [
+ "android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+ "android/java/src/org/chromium/net/AndroidKeyStore.java",
+ "android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+ "android/java/src/org/chromium/net/AndroidPrivateKey.java",
+ "android/java/src/org/chromium/net/GURLUtils.java",
+ "android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+ "android/java/src/org/chromium/net/ProxyChangeListener.java",
+ "android/java/src/org/chromium/net/X509Util.java",
+ ]
+ jni_package = "net"
+ }
+}
+
+if (is_android || is_linux) {
+ executable("disk_cache_memory_test") {
+ sources = [ "tools/disk_cache_memory_test/disk_cache_memory_test.cc" ]
+ deps = [
+ ":net",
+ "//base",
+ ]
+ }
+}
+
+# TODO(GYP) make this compile on Android, we need some native test deps done.
+if (!is_android) {
+
+test("net_unittests") {
+ sources = gypi_values.net_test_sources
+
+ configs += [ ":net_win_size_truncation" ]
+ defines = []
+
+ deps = [
+ ":http_server",
+ ":net",
+ ":test_support",
+ "//base",
+ "//base:i18n",
+ "//base/allocator",
+ "//base/third_party/dynamic_annotations",
+ "//crypto",
+ "//crypto:platform",
+ "//net/base/registry_controlled_domains",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/zlib",
+ "//url",
+ ]
+
+ if (is_linux) {
+ sources += gypi_values.net_linux_test_sources
+ deps += [
+ ":balsa",
+ ":epoll_server",
+ ":flip_in_mem_edsm_server_base",
+ ":quic_base",
+ ]
+ }
+
+ if (is_chromeos) {
+ sources -= [
+ "base/network_change_notifier_linux_unittest.cc",
+ "proxy/proxy_config_service_linux_unittest.cc",
+ ]
+ }
+
+ if (is_android) {
+ sources -= [
+ # See bug http://crbug.com/344533.
+ "disk_cache/blockfile/index_table_v3_unittest.cc",
+ # No res_ninit() et al on Android, so this doesn't make a lot of
+ # sense.
+ "dns/dns_config_service_posix_unittest.cc",
+ ]
+ deps += [
+ ":net_javatests", # FIXME(brettw)
+ ":net_test_jni_headers",
+ ]
+ }
+
+ if (!use_nss_certs) {
+ sources -= [
+ "ssl/client_cert_store_nss_unittest.cc",
+ ]
+ if (is_chromeos) { # Already removed for all non-ChromeOS builds.
+ sources -= [
+ "ssl/client_cert_store_chromeos_unittest.cc",
+ ]
+ }
+ }
+
+ if (use_openssl) {
+ # When building for OpenSSL, we need to exclude NSS specific tests
+ # or functionality not supported by OpenSSL yet.
+ # TODO(bulach): Add equivalent tests when the underlying
+ # functionality is ported to OpenSSL.
+ sources -= [
+ "cert/ct_objects_extractor_unittest.cc",
+ "cert/multi_log_ct_verifier_unittest.cc",
+ "cert/nss_cert_database_unittest.cc",
+ "cert/nss_cert_database_chromeos_unittest.cc",
+ "cert/nss_profile_filter_chromeos_unittest.cc",
+ "cert/x509_util_nss_unittest.cc",
+ "quic/test_tools/crypto_test_utils_nss.cc",
+ ]
+ } else {
+ sources -= [
+ "cert/x509_util_openssl_unittest.cc",
+ "quic/test_tools/crypto_test_utils_openssl.cc",
+ "socket/ssl_client_socket_openssl_unittest.cc",
+ "socket/ssl_session_cache_openssl_unittest.cc",
+ ]
+ if (!is_desktop_linux && !is_chromeos) {
+ sources -= [ "cert/nss_cert_database_unittest.cc" ]
+ }
+ }
+
+ if (use_kerberos) {
+ defines += [ "USE_KERBEROS" ]
+ } else {
+ sources -= [
+ "http/http_auth_gssapi_posix_unittest.cc",
+ "http/http_auth_handler_negotiate_unittest.cc",
+ "http/mock_gssapi_library_posix.cc",
+ "http/mock_gssapi_library_posix.h",
+ ]
+ }
+
+ if (use_openssl || (!is_desktop_linux && !is_chromeos && !is_ios)) {
+ # Only include this test when on Posix and using NSS for
+ # cert verification or on iOS (which also uses NSS for certs).
+ sources -= [ "ocsp/nss_ocsp_unittest.cc" ]
+ }
+
+ if (!use_openssl_certs) {
+ sources -= [ "ssl/openssl_client_key_store_unittest.cc" ]
+ }
+
+ if (!enable_websockets) {
+ sources -= [
+ "socket_stream/socket_stream_metrics_unittest.cc",
+ "socket_stream/socket_stream_unittest.cc",
+ "spdy/spdy_websocket_stream_unittest.cc",
+ "websockets/websocket_basic_stream_test.cc",
+ "websockets/websocket_channel_test.cc",
+ "websockets/websocket_deflate_predictor_impl_test.cc",
+ "websockets/websocket_deflate_stream_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_spdy_test.cc",
+ "websockets/websocket_handshake_handler_test.cc",
+ "websockets/websocket_handshake_stream_create_helper_test.cc",
+ "websockets/websocket_inflater_test.cc",
+ "websockets/websocket_job_test.cc",
+ "websockets/websocket_net_log_params_test.cc",
+ "websockets/websocket_stream_test.cc",
+ "websockets/websocket_test_util.cc",
+ "websockets/websocket_test_util.h",
+ "websockets/websocket_throttle_test.cc",
+ ]
+ }
+
+ if (disable_file_support) {
+ sources -= [
+ "base/directory_lister_unittest.cc",
+ "url_request/url_request_file_job_unittest.cc",
+ ]
+ }
+
+ if (disable_ftp_support) {
+ sources -= [
+ "ftp/ftp_auth_cache_unittest.cc",
+ "ftp/ftp_ctrl_response_buffer_unittest.cc",
+ "ftp/ftp_directory_listing_parser_ls_unittest.cc",
+ "ftp/ftp_directory_listing_parser_netware_unittest.cc",
+ "ftp/ftp_directory_listing_parser_os2_unittest.cc",
+ "ftp/ftp_directory_listing_parser_unittest.cc",
+ "ftp/ftp_directory_listing_parser_unittest.h",
+ "ftp/ftp_directory_listing_parser_vms_unittest.cc",
+ "ftp/ftp_directory_listing_parser_windows_unittest.cc",
+ "ftp/ftp_network_transaction_unittest.cc",
+ "ftp/ftp_util_unittest.cc",
+ "url_request/url_request_ftp_job_unittest.cc",
+ ]
+ }
+
+ if (!enable_built_in_dns) {
+ sources -= [
+ "dns/address_sorter_posix_unittest.cc",
+ "dns/address_sorter_unittest.cc",
+ ]
+ }
+
+ # Always need use_v8_in_net to be 1 to run on Android, so just remove
+ # net_unittest's dependency on v8 when using icu alternatives instead of
+ # setting use_v8_in_net to 0.
+ if (use_v8_in_net && !use_icu_alternatives_on_android) {
+ deps += [ ":net_with_v8" ]
+ } else {
+ sources -= [
+ "proxy/proxy_resolver_v8_unittest.cc",
+ "proxy/proxy_resolver_v8_tracing_unittest.cc",
+ ]
+ }
+
+ if (!enable_mdns) {
+ sources -= [
+ "dns/mdns_cache_unittest.cc",
+ "dns/mdns_client_unittest.cc",
+ "dns/record_parsed_unittest.cc",
+ "dns/record_rdata_unittest.cc",
+ ]
+ }
+
+ if (is_ios) {
+ # TODO(GYP)
+ # 'actions': [
+ # {
+ # 'action_name': 'copy_test_data',
+ # 'variables': {
+ # 'test_data_files': [
+ # 'data/ssl/certificates/',
+ # 'data/test.html',
+ # 'data/url_request_unittest/',
+ # ],
+ # 'test_data_prefix': 'net',
+ # },
+ # 'includes': [ '../build/copy_test_data_ios.gypi' ],
+ # },
+ # ],
+ sources -= [
+ # TODO(droger): The following tests are disabled because the
+ # implementation is missing or incomplete.
+ # KeygenHandler::GenKeyAndSignChallenge() is not ported to iOS.
+ "base/keygen_handler_unittest.cc",
+ "disk_cache/backend_unittest.cc",
+ "disk_cache/blockfile/block_files_unittest.cc",
+ # Need to read input data files.
+ "filter/gzip_filter_unittest.cc",
+ "socket/ssl_server_socket_unittest.cc",
+ "spdy/fuzzing/hpack_fuzz_util_test.cc",
+ # Need TestServer.
+ "proxy/proxy_script_fetcher_impl_unittest.cc",
+ "socket/ssl_client_socket_unittest.cc",
+ "url_request/url_fetcher_impl_unittest.cc",
+ "url_request/url_request_context_builder_unittest.cc",
+ # Needs GetAppOutput().
+ "test/python_utils_unittest.cc",
+
+ # The following tests are disabled because they don't apply to
+ # iOS.
+ # OS is not "linux" or "freebsd" or "openbsd".
+ "socket/unix_domain_socket_posix_unittest.cc",
+
+ # See bug http://crbug.com/344533.
+ "disk_cache/blockfile/index_table_v3_unittest.cc",
+ ]
+ }
+
+ if (is_android) {
+ sources -= [
+ "dns/dns_config_service_posix_unittest.cc",
+ ]
+ # TODO(GYP)
+ # # TODO(mmenke): This depends on test_support_base, which depends on
+ # # icu. Figure out a way to remove that dependency.
+ # 'dependencies': [
+ # '../testing/android/native_test.gyp:native_test_native_code',
+ # ]
+
+ set_sources_assignment_filter([])
+ sources += [ "base/address_tracker_linux_unittest.cc" ]
+ set_sources_assignment_filter(sources_assignment_filter)
+ }
+
+ if (use_icu_alternatives_on_android) {
+ sources -= [
+ "base/filename_util_unittest.cc",
+ "base/net_util_icu_unittest.cc",
+ ]
+ deps -= [ "//base:i18n" ]
+ }
+}
+
+} # !is_android
diff --git a/chromium/net/DEPS b/chromium/net/DEPS
index edf5377dfbf..e8a28a6db79 100644
--- a/chromium/net/DEPS
+++ b/chromium/net/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+crypto",
+ "+gin/public",
"+jni",
"+third_party/apple_apsl",
"+third_party/libevent",
@@ -7,8 +8,50 @@ include_rules = [
"+third_party/zlib",
"+sdch/open-vcdiff",
"+v8",
+
+ # Most of net should not depend on icu, to keep size down when built as a
+ # library.
+ "-base/i18n",
+ "-third_party/icu",
]
+specific_include_rules = {
+ # Within net, only used by file: requests.
+ "directory_lister(\.cc|_unittest\.cc)": [
+ "+base/i18n",
+ ],
+
+ # Within net, only used by file: requests.
+ "filename_util_icu\.cc": [
+ "+base/i18n/file_util_icu.h",
+ ],
+
+ # Functions largely not used by the rest of net.
+ "net_util_icu\.cc": [
+ "+base/i18n",
+ "+third_party/icu",
+ ],
+
+ # Consolidated string functions that depend on icu.
+ "net_string_util_icu\.cc": [
+ "+base/i18n/i18n_constants.h",
+ "+base/i18n/icu_string_conversions.h",
+ "+third_party/icu/source/common/unicode/ucnv.h"
+ ],
+
+ "websocket_channel\.h": [
+ "+base/i18n",
+ ],
+
+ "ftp_util\.cc": [
+ "+base/i18n",
+ "+third_party/icu",
+ ],
+ "ftp_directory_listing_parser\.cc": [
+ "+base/i18n",
+ ],
+}
+
skip_child_includes = [
"third_party",
"tools/flip_server",
diff --git a/chromium/net/OWNERS b/chromium/net/OWNERS
index 96337499fe8..f137837c158 100644
--- a/chromium/net/OWNERS
+++ b/chromium/net/OWNERS
@@ -1,11 +1,11 @@
agl@chromium.org
-akalin@chromium.org
asanka@chromium.org
cbentzel@chromium.org
davidben@chromium.org
eroman@chromium.org
gavinp@chromium.org
jar@chromium.org
+jgraettinger@chromium.org
mattm@chromium.org
mef@chromium.org
mmenke@chromium.org
@@ -15,10 +15,14 @@ rdsmith@chromium.org
rsleevi@chromium.org
rtenneti@chromium.org
rvargas@chromium.org
-szym@chromium.org
ttuttle@chromium.org
willchan@chromium.org
wtc@chromium.org
per-file *.isolate=csharp@chromium.org
per-file *.isolate=maruel@chromium.org
+
+# Don't use as a reviewer for new CLs, but still here
+# so he can continue to review some already in-progress
+# work.
+szym@chromium.org
diff --git a/chromium/net/PRESUBMIT.py b/chromium/net/PRESUBMIT.py
index 3a2f21323b6..ce5542f1891 100644
--- a/chromium/net/PRESUBMIT.py
+++ b/chromium/net/PRESUBMIT.py
@@ -8,14 +8,17 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details on the presubmit API built into gcl.
"""
-def GetPreferredTrySlaves(project, change):
- slaves = [
- 'linux_rel:sync_integration_tests',
- 'mac_rel:sync_integration_tests',
- 'win_rel:sync_integration_tests',
- ]
+def GetPreferredTryMasters(project, change):
+ masters = {
+ 'tryserver.chromium': {
+ 'linux_chromium_rel': set(['defaulttests']),
+ 'mac_chromium_rel': set(['defaulttests']),
+ 'win_chromium_rel': set(['defaulttests']),
+ }
+ }
# Changes that touch NSS files will likely need a corresponding OpenSSL edit.
# Conveniently, this one glob also matches _openssl.* changes too.
if any('nss' in f.LocalPath() for f in change.AffectedFiles()):
- slaves.append('linux_redux')
- return slaves
+ masters['tryserver.chromium'].setdefault(
+ 'linux_redux', set()).add('defaulttests')
+ return masters
diff --git a/chromium/net/android/OWNERS b/chromium/net/android/OWNERS
index 4bcb60f934c..fa129e131b8 100644
--- a/chromium/net/android/OWNERS
+++ b/chromium/net/android/OWNERS
@@ -1,3 +1,2 @@
digit@chromium.org
-pliard@chromium.org
yfriedman@chromium.org
diff --git a/chromium/net/android/android_private_key.cc b/chromium/net/android/android_private_key.cc
new file mode 100644
index 00000000000..8f56085ad7c
--- /dev/null
+++ b/chromium/net/android/android_private_key.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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/android/android_private_key.h"
+
+#include <vector>
+#include "jni/AndroidPrivateKey_jni.h"
+
+namespace net {
+namespace android {
+
+base::android::ScopedJavaLocalRef<jobject> GetKeyStore(
+ jobject private_key_ref) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return Java_AndroidPrivateKey_getKeyStore(
+ env, private_key_ref);
+}
+
+bool RegisterAndroidPrivateKey(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace net
diff --git a/chromium/net/android/android_private_key.h b/chromium/net/android/android_private_key.h
new file mode 100644
index 00000000000..9782a2d5e03
--- /dev/null
+++ b/chromium/net/android/android_private_key.h
@@ -0,0 +1,28 @@
+// Copyright 2014 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_ANDROID_ANDROID_PRIVATE_KEY_H
+#define NET_ANDROID_ANDROID_PRIVATE_KEY_H
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+
+namespace net {
+namespace android {
+
+// Returns the KeyStore associated with a given AndroidPrivateKey
+NET_EXPORT base::android::ScopedJavaLocalRef<jobject> GetKeyStore(
+ jobject private_key);
+
+// Register JNI methods
+NET_EXPORT bool RegisterAndroidPrivateKey(JNIEnv* env);
+
+} // namespace android
+} // namespace net
+
+#endif // NET_ANDROID_ANDROID_PRIVATE_KEY_H
diff --git a/chromium/net/android/cert_verify_result_android.cc b/chromium/net/android/cert_verify_result_android.cc
new file mode 100644
index 00000000000..151c4ef4eae
--- /dev/null
+++ b/chromium/net/android/cert_verify_result_android.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 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/android/cert_verify_result_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "jni/AndroidCertVerifyResult_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaArrayOfByteArrayToStringVector;
+
+namespace net {
+namespace android {
+
+void ExtractCertVerifyResult(jobject result,
+ CertVerifyStatusAndroid* status,
+ bool* is_issued_by_known_root,
+ std::vector<std::string>* verified_chain) {
+ JNIEnv* env = AttachCurrentThread();
+
+ *status = static_cast<CertVerifyStatusAndroid>(
+ Java_AndroidCertVerifyResult_getStatus(env, result));
+
+ *is_issued_by_known_root =
+ Java_AndroidCertVerifyResult_isIssuedByKnownRoot(env, result);
+
+ ScopedJavaLocalRef<jobjectArray> chain_byte_array =
+ Java_AndroidCertVerifyResult_getCertificateChainEncoded(env, result);
+ JavaArrayOfByteArrayToStringVector(
+ env, chain_byte_array.obj(), verified_chain);
+}
+
+bool RegisterCertVerifyResult(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace net
diff --git a/chromium/net/android/cert_verify_result_android.h b/chromium/net/android/cert_verify_result_android.h
index ac18c218cfd..7027c3d7726 100644
--- a/chromium/net/android/cert_verify_result_android.h
+++ b/chromium/net/android/cert_verify_result_android.h
@@ -5,20 +5,32 @@
#ifndef NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
#define NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
+#include <jni.h>
+
+#include <string>
+#include <vector>
+
#include "base/basictypes.h"
-#include "base/platform_file.h"
-#include "net/base/net_export.h"
namespace net {
namespace android {
-enum CertVerifyResultAndroid {
-#define CERT_VERIFY_RESULT_ANDROID(label, value) VERIFY_ ## label = value,
-#include "net/android/cert_verify_result_android_list.h"
-#undef CERT_VERIFY_RESULT_ANDROID
+enum CertVerifyStatusAndroid {
+#define CERT_VERIFY_STATUS_ANDROID(label, value) VERIFY_ ## label = value,
+#include "net/android/cert_verify_status_android_list.h"
+#undef CERT_VERIFY_STATUS_ANDROID
};
+// Extract parameters out of an AndroidCertVerifyResult object.
+void ExtractCertVerifyResult(jobject result,
+ CertVerifyStatusAndroid* status,
+ bool* is_issued_by_known_root,
+ std::vector<std::string>* verified_chain);
+
+// Register JNI methods.
+bool RegisterCertVerifyResult(JNIEnv* env);
+
} // namespace android
} // namespace net
diff --git a/chromium/net/android/cert_verify_result_android_list.h b/chromium/net/android/cert_verify_status_android_list.h
index a6d882f1e03..be7cec7eb96 100644
--- a/chromium/net/android/cert_verify_result_android_list.h
+++ b/chromium/net/android/cert_verify_status_android_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -9,23 +9,23 @@
// from Java side to the C++ side.
// Certificate is trusted.
-CERT_VERIFY_RESULT_ANDROID(OK, 0)
+CERT_VERIFY_STATUS_ANDROID(OK, 0)
// Certificate verification could not be conducted.
-CERT_VERIFY_RESULT_ANDROID(FAILED, -1)
+CERT_VERIFY_STATUS_ANDROID(FAILED, -1)
// Certificate is not trusted due to non-trusted root of the certificate chain.
-CERT_VERIFY_RESULT_ANDROID(NO_TRUSTED_ROOT, -2)
+CERT_VERIFY_STATUS_ANDROID(NO_TRUSTED_ROOT, -2)
// Certificate is not trusted because it has expired.
-CERT_VERIFY_RESULT_ANDROID(EXPIRED, -3)
+CERT_VERIFY_STATUS_ANDROID(EXPIRED, -3)
// Certificate is not trusted because it is not valid yet.
-CERT_VERIFY_RESULT_ANDROID(NOT_YET_VALID, -4)
+CERT_VERIFY_STATUS_ANDROID(NOT_YET_VALID, -4)
// Certificate is not trusted because it could not be parsed.
-CERT_VERIFY_RESULT_ANDROID(UNABLE_TO_PARSE, -5)
+CERT_VERIFY_STATUS_ANDROID(UNABLE_TO_PARSE, -5)
// Certificate is not trusted because it has an extendedKeyUsage field, but
// its value is not correct for a web server.
-CERT_VERIFY_RESULT_ANDROID(INCORRECT_KEY_USAGE, -6)
+CERT_VERIFY_STATUS_ANDROID(INCORRECT_KEY_USAGE, -6)
diff --git a/chromium/net/android/keystore.cc b/chromium/net/android/keystore.cc
index a3d8cc1771f..de36c95489d 100644
--- a/chromium/net/android/keystore.cc
+++ b/chromium/net/android/keystore.cc
@@ -9,8 +9,8 @@
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
-
#include "jni/AndroidKeyStore_jni.h"
+#include "net/android/android_private_key.h"
using base::android::AttachCurrentThread;
using base::android::HasException;
@@ -28,7 +28,9 @@ bool GetRSAKeyModulus(
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> modulus_ref =
- Java_AndroidKeyStore_getRSAKeyModulus(env, private_key_ref);
+ Java_AndroidKeyStore_getRSAKeyModulus(env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (modulus_ref.is_null())
return false;
@@ -41,7 +43,10 @@ bool GetDSAKeyParamQ(jobject private_key_ref,
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> q_ref =
- Java_AndroidKeyStore_getDSAKeyParamQ(env, private_key_ref);
+ Java_AndroidKeyStore_getDSAKeyParamQ(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (q_ref.is_null())
return false;
@@ -54,7 +59,11 @@ bool GetECKeyOrder(jobject private_key_ref,
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> order_ref =
- Java_AndroidKeyStore_getECKeyOrder(env, private_key_ref);
+ Java_AndroidKeyStore_getECKeyOrder(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
+
if (order_ref.is_null())
return false;
@@ -62,12 +71,15 @@ bool GetECKeyOrder(jobject private_key_ref,
return true;
}
-bool GetPrivateKeyEncodedBytes(jobject private_key,
+bool GetPrivateKeyEncodedBytes(jobject private_key_ref,
std::vector<uint8>* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> encoded_ref =
- Java_AndroidKeyStore_getPrivateKeyEncodedBytes(env, private_key);
+ Java_AndroidKeyStore_getPrivateKeyEncodedBytes(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (encoded_ref.is_null())
return false;
@@ -91,7 +103,10 @@ bool RawSignDigestWithPrivateKey(
// Invoke platform API
ScopedJavaLocalRef<jbyteArray> signature_ref =
Java_AndroidKeyStore_rawSignDigestWithPrivateKey(
- env, private_key_ref, digest_ref.obj());
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref,
+ digest_ref.obj());
if (HasException(env) || signature_ref.is_null())
return false;
@@ -100,14 +115,16 @@ bool RawSignDigestWithPrivateKey(
return true;
}
-PrivateKeyType GetPrivateKeyType(jobject private_key) {
+PrivateKeyType GetPrivateKeyType(jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread();
int type = Java_AndroidKeyStore_getPrivateKeyType(
- env, private_key);
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
return static_cast<PrivateKeyType>(type);
}
-EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key) {
+EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread();
// Note: the pointer is passed as a jint here because that's how it
// is stored in the Java object. Java doesn't have a primitive type
@@ -117,11 +134,21 @@ EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key) {
// Given that this routine shall only be called on Android < 4.2,
// this won't be a problem in the far future (e.g. when Android gets
// ported to 64-bit environments, if ever).
- int pkey =
- Java_AndroidKeyStore_getOpenSSLHandleForPrivateKey(env, private_key);
+ long pkey = Java_AndroidKeyStore_getOpenSSLHandleForPrivateKey(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
return reinterpret_cast<EVP_PKEY*>(pkey);
}
+void ReleaseKey(jobject private_key_ref) {
+ JNIEnv* env = AttachCurrentThread();
+ Java_AndroidKeyStore_releaseKey(env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
+ env->DeleteGlobalRef(private_key_ref);
+}
+
bool RegisterKeyStore(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/chromium/net/android/keystore.h b/chromium/net/android/keystore.h
index f14fa872edd..ac3babe2a2a 100644
--- a/chromium/net/android/keystore.h
+++ b/chromium/net/android/keystore.h
@@ -108,6 +108,8 @@ NET_EXPORT PrivateKeyType GetPrivateKeyType(jobject private_key);
// the returned key's reference count.
EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key);
+NET_EXPORT void ReleaseKey(jobject private_key);
+
// Register JNI methods
NET_EXPORT bool RegisterKeyStore(JNIEnv* env);
diff --git a/chromium/net/android/keystore_openssl.cc b/chromium/net/android/keystore_openssl.cc
index 5ad847344aa..ec08d70236a 100644
--- a/chromium/net/android/keystore_openssl.cc
+++ b/chromium/net/android/keystore_openssl.cc
@@ -208,8 +208,7 @@ int RsaMethodFinish(RSA* rsa) {
jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
if (key != NULL) {
RSA_set_app_data(rsa, NULL);
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(key);
+ ReleaseKey(key);
}
// Actual return value is ignored by OpenSSL. There are no docs
// explaining what this is supposed to be.
@@ -311,6 +310,44 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
return true;
}
+// On Android < 4.2, the libkeystore.so ENGINE uses CRYPTO_EX_DATA and is not
+// added to the global engine list. If all references to it are dropped, OpenSSL
+// will dlclose the module, leaving a dangling function pointer in the RSA
+// CRYPTO_EX_DATA class. To work around this, leak an extra reference to the
+// ENGINE we extract in GetRsaLegacyKey.
+//
+// In 4.2, this change avoids the problem:
+// https://android.googlesource.com/platform/libcore/+/106a8928fb4249f2f3d4dba1dddbe73ca5cb3d61
+//
+// https://crbug.com/381465
+class KeystoreEngineWorkaround {
+ public:
+ KeystoreEngineWorkaround() : leaked_engine_(false) {}
+
+ void LeakRsaEngine(EVP_PKEY* pkey) {
+ if (leaked_engine_)
+ return;
+ ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (!rsa.get() ||
+ !rsa.get()->engine ||
+ strcmp(ENGINE_get_id(rsa.get()->engine), "keystore") ||
+ !ENGINE_init(rsa.get()->engine)) {
+ NOTREACHED();
+ return;
+ }
+ leaked_engine_ = true;
+ }
+
+ private:
+ bool leaked_engine_;
+};
+
+void LeakRsaEngine(EVP_PKEY* pkey) {
+ static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance =
+ LAZY_INSTANCE_INITIALIZER;
+ s_instance.Get().LeakRsaEngine(pkey);
+}
+
// Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object
// for Android 4.0 to 4.1.x. Must only be used on Android < 4.2.
// |private_key| is a JNI reference (local or global) to the object.
@@ -321,6 +358,7 @@ EVP_PKEY* GetRsaLegacyKey(jobject private_key) {
GetOpenSSLSystemHandleForPrivateKey(private_key);
if (sys_pkey != NULL) {
CRYPTO_add(&sys_pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+ LeakRsaEngine(sys_pkey);
} else {
// GetOpenSSLSystemHandleForPrivateKey() will fail on Android
// 4.0.3 and earlier. However, it is possible to get the key
@@ -413,8 +451,7 @@ int DsaMethodFinish(DSA* dsa) {
jobject key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa,0));
if (key != NULL) {
DSA_set_ex_data(dsa, 0, NULL);
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(key);
+ ReleaseKey(key);
}
// Actual return value is ignored by OpenSSL. There are no docs
// explaining what this is supposed to be.
@@ -493,9 +530,7 @@ void ExDataFree(void* parent,
return;
CRYPTO_set_ex_data(ad, idx, NULL);
-
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(private_key);
+ ReleaseKey(private_key);
}
int ExDataDup(CRYPTO_EX_DATA* to,
diff --git a/chromium/net/android/keystore_unittest.cc b/chromium/net/android/keystore_unittest.cc
index 58e3da796a5..ff204357f32 100644
--- a/chromium/net/android/keystore_unittest.cc
+++ b/chromium/net/android/keystore_unittest.cc
@@ -21,7 +21,7 @@
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
-#include "base/memory/scoped_handle.h"
+#include "base/files/scoped_file.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "crypto/openssl_util.h"
@@ -117,7 +117,7 @@ EVP_PKEY* ImportPrivateKeyFile(const char* filename) {
// Load file in memory.
base::FilePath certs_dir = GetTestCertsDirectory();
base::FilePath file_path = certs_dir.AppendASCII(filename);
- ScopedStdioHandle handle(base::OpenFile(file_path, "rb"));
+ base::ScopedFILE handle(base::OpenFile(file_path, "rb"));
if (!handle.get()) {
LOG(ERROR) << "Could not open private key file: " << filename;
return NULL;
@@ -166,7 +166,7 @@ EVP_PKEY* ImportPublicKeyFile(const char* filename) {
// Load file as PEM data.
base::FilePath certs_dir = GetTestCertsDirectory();
base::FilePath file_path = certs_dir.AppendASCII(filename);
- ScopedStdioHandle handle(base::OpenFile(file_path, "rb"));
+ base::ScopedFILE handle(base::OpenFile(file_path, "rb"));
if (!handle.get()) {
LOG(ERROR) << "Could not open public key file: " << filename;
return NULL;
diff --git a/chromium/net/android/network_change_notifier_android.cc b/chromium/net/android/network_change_notifier_android.cc
index d4e1a5c8524..2461d644b64 100644
--- a/chromium/net/android/network_change_notifier_android.cc
+++ b/chromium/net/android/network_change_notifier_android.cc
@@ -59,8 +59,52 @@
#include "net/android/network_change_notifier_android.h"
+#include "base/threading/thread.h"
+#include "net/base/address_tracker_linux.h"
+#include "net/dns/dns_config_service.h"
+
namespace net {
+// Thread on which we can run DnsConfigService, which requires a TYPE_IO
+// message loop to monitor /system/etc/hosts.
+class NetworkChangeNotifierAndroid::DnsConfigServiceThread
+ : public base::Thread {
+ public:
+ DnsConfigServiceThread()
+ : base::Thread("DnsConfigService"),
+ address_tracker_(base::Bind(base::DoNothing),
+ base::Bind(base::DoNothing),
+ // We're only interested in tunnel interface changes.
+ base::Bind(NotifyNetworkChangeNotifierObservers)) {}
+
+ virtual ~DnsConfigServiceThread() {
+ Stop();
+ }
+
+ virtual void Init() OVERRIDE {
+ address_tracker_.Init();
+ dns_config_service_ = DnsConfigService::CreateSystemService();
+ dns_config_service_->WatchConfig(
+ base::Bind(&NetworkChangeNotifier::SetDnsConfig));
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ dns_config_service_.reset();
+ }
+
+ static void NotifyNetworkChangeNotifierObservers() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
+ NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
+ }
+
+ private:
+ scoped_ptr<DnsConfigService> dns_config_service_;
+ // Used to detect tunnel state changes.
+ internal::AddressTrackerLinux address_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
+};
+
NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
delegate_->RemoveObserver(this);
}
@@ -71,8 +115,7 @@ NetworkChangeNotifierAndroid::GetCurrentConnectionType() const {
}
void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() {
- NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
- NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
+ DnsConfigServiceThread::NotifyNetworkChangeNotifierObservers();
}
// static
@@ -83,8 +126,11 @@ bool NetworkChangeNotifierAndroid::Register(JNIEnv* env) {
NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
NetworkChangeNotifierDelegateAndroid* delegate)
: NetworkChangeNotifier(NetworkChangeCalculatorParamsAndroid()),
- delegate_(delegate) {
+ delegate_(delegate),
+ dns_config_service_thread_(new DnsConfigServiceThread()) {
delegate_->AddObserver(this);
+ dns_config_service_thread_->StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
}
// static
diff --git a/chromium/net/android/network_change_notifier_android.h b/chromium/net/android/network_change_notifier_android.h
index f167cfc283b..e82b852f0fb 100644
--- a/chromium/net/android/network_change_notifier_android.h
+++ b/chromium/net/android/network_change_notifier_android.h
@@ -56,12 +56,15 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierAndroid
friend class NetworkChangeNotifierAndroidTest;
friend class NetworkChangeNotifierFactoryAndroid;
+ class DnsConfigServiceThread;
+
explicit NetworkChangeNotifierAndroid(
NetworkChangeNotifierDelegateAndroid* delegate);
static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsAndroid();
NetworkChangeNotifierDelegateAndroid* const delegate_;
+ scoped_ptr<DnsConfigServiceThread> dns_config_service_thread_;
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierAndroid);
};
diff --git a/chromium/net/android/network_change_notifier_delegate_android.cc b/chromium/net/android/network_change_notifier_delegate_android.cc
index 207f01b6c86..fbbaceddd94 100644
--- a/chromium/net/android/network_change_notifier_delegate_android.cc
+++ b/chromium/net/android/network_change_notifier_delegate_android.cc
@@ -23,6 +23,7 @@ NetworkChangeNotifier::ConnectionType ConvertConnectionType(
case NetworkChangeNotifier::CONNECTION_3G:
case NetworkChangeNotifier::CONNECTION_4G:
case NetworkChangeNotifier::CONNECTION_NONE:
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
break;
default:
NOTREACHED() << "Unknown connection type received: " << connection_type;
diff --git a/chromium/net/android/network_library.cc b/chromium/net/android/network_library.cc
index 2407100cdc3..c3a42f70082 100644
--- a/chromium/net/android/network_library.cc
+++ b/chromium/net/android/network_library.cc
@@ -12,7 +12,6 @@
#include "jni/AndroidNetworkLibrary_jni.h"
using base::android::AttachCurrentThread;
-using base::android::ClearException;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::GetApplicationContext;
@@ -23,9 +22,12 @@ using base::android::ToJavaByteArray;
namespace net {
namespace android {
-CertVerifyResultAndroid VerifyX509CertChain(
- const std::vector<std::string>& cert_chain,
- const std::string& auth_type) {
+void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
+ const std::string& auth_type,
+ const std::string& host,
+ CertVerifyStatusAndroid* status,
+ bool* is_issued_by_known_root,
+ std::vector<std::string>* verified_chain) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> chain_byte_array =
@@ -36,10 +38,16 @@ CertVerifyResultAndroid VerifyX509CertChain(
ConvertUTF8ToJavaString(env, auth_type);
DCHECK(!auth_string.is_null());
- jint result = Java_AndroidNetworkLibrary_verifyServerCertificates(
- env, chain_byte_array.obj(), auth_string.obj());
+ ScopedJavaLocalRef<jstring> host_string =
+ ConvertUTF8ToJavaString(env, host);
+ DCHECK(!host_string.is_null());
- return static_cast<CertVerifyResultAndroid>(result);
+ ScopedJavaLocalRef<jobject> result =
+ Java_AndroidNetworkLibrary_verifyServerCertificates(
+ env, chain_byte_array.obj(), auth_string.obj(), host_string.obj());
+
+ ExtractCertVerifyResult(result.obj(),
+ status, is_issued_by_known_root, verified_chain);
}
void AddTestRootCertificate(const uint8* cert, size_t len) {
@@ -114,6 +122,20 @@ bool GetMimeTypeFromExtension(const std::string& extension,
return true;
}
+std::string GetTelephonyNetworkCountryIso() {
+ return base::android::ConvertJavaStringToUTF8(
+ Java_AndroidNetworkLibrary_getNetworkCountryIso(
+ base::android::AttachCurrentThread(),
+ base::android::GetApplicationContext()));
+}
+
+std::string GetTelephonyNetworkOperator() {
+ return base::android::ConvertJavaStringToUTF8(
+ Java_AndroidNetworkLibrary_getNetworkOperator(
+ base::android::AttachCurrentThread(),
+ base::android::GetApplicationContext()));
+}
+
bool RegisterNetworkLibrary(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/chromium/net/android/network_library.h b/chromium/net/android/network_library.h
index 6bdc1ae427c..ba2bfb5eb35 100644
--- a/chromium/net/android/network_library.h
+++ b/chromium/net/android/network_library.h
@@ -21,9 +21,12 @@ namespace android {
// |cert_chain| is DER encoded chain of certificates, with the server's own
// certificate listed first.
// |auth_type| is as per the Java X509Certificate.checkServerTrusted method.
-CertVerifyResultAndroid VerifyX509CertChain(
- const std::vector<std::string>& cert_chain,
- const std::string& auth_type);
+void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
+ const std::string& auth_type,
+ const std::string& host,
+ CertVerifyStatusAndroid* status,
+ bool* is_issued_by_known_root,
+ std::vector<std::string>* verified_chain);
// Adds a certificate as a root trust certificate to the trust manager.
// |cert| is DER encoded certificate, |len| is its length in bytes.
@@ -68,6 +71,14 @@ std::string GetNetworkList();
bool GetMimeTypeFromExtension(const std::string& extension,
std::string* result);
+// Returns the ISO country code equivalent of the current MCC (mobile country
+// code).
+NET_EXPORT std::string GetTelephonyNetworkCountryIso();
+
+// Returns MCC+MNC (mobile country code + mobile network code) as
+// the numeric name of the current registered operator.
+NET_EXPORT std::string GetTelephonyNetworkOperator();
+
// Register JNI methods
NET_EXPORT bool RegisterNetworkLibrary(JNIEnv* env);
diff --git a/chromium/net/base/address_family.h b/chromium/net/base/address_family.h
index 07adfaa2f4d..eb87ae3bf32 100644
--- a/chromium/net/base/address_family.h
+++ b/chromium/net/base/address_family.h
@@ -13,6 +13,7 @@ enum AddressFamily {
ADDRESS_FAMILY_UNSPECIFIED, // AF_UNSPEC
ADDRESS_FAMILY_IPV4, // AF_INET
ADDRESS_FAMILY_IPV6, // AF_INET6
+ ADDRESS_FAMILY_LAST = ADDRESS_FAMILY_IPV6
};
// HostResolverFlags is a bitflag enum used by host resolver procedures to
diff --git a/chromium/net/base/address_tracker_linux.cc b/chromium/net/base/address_tracker_linux.cc
index f863ccd625b..36738136695 100644
--- a/chromium/net/base/address_tracker_linux.cc
+++ b/chromium/net/base/address_tracker_linux.cc
@@ -6,11 +6,11 @@
#include <errno.h>
#include <linux/if.h>
+#include <sys/ioctl.h>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h"
-#include "net/base/network_change_notifier_linux.h"
namespace net {
namespace internal {
@@ -18,7 +18,12 @@ namespace internal {
namespace {
// Retrieves address from NETLINK address message.
-bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
+// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
+bool GetAddress(const struct nlmsghdr* header,
+ IPAddressNumber* out,
+ bool* really_deprecated) {
+ if (really_deprecated)
+ *really_deprecated = false;
const struct ifaddrmsg* msg =
reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
size_t address_length = 0;
@@ -53,6 +58,12 @@ bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
DCHECK_GE(RTA_PAYLOAD(attr), address_length);
local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
break;
+ case IFA_CACHEINFO: {
+ const struct ifa_cacheinfo *cache_info =
+ reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
+ if (really_deprecated)
+ *really_deprecated = (cache_info->ifa_prefered == 0);
+ } break;
default:
break;
}
@@ -65,12 +76,34 @@ bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
return true;
}
+// Returns the name for the interface with interface index |interface_index|.
+// The return value points to a function-scoped static so it may be changed by
+// subsequent calls. This function could be replaced with if_indextoname() but
+// net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively
+// talking to the kernel and not the C library.
+const char* GetInterfaceName(int interface_index) {
+ int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl_socket < 0)
+ return "";
+ static struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = interface_index;
+ int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr);
+ close(ioctl_socket);
+ if (rv != 0)
+ return "";
+ return ifr.ifr_name;
+}
+
} // namespace
AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
- const base::Closure& link_callback)
- : address_callback_(address_callback),
+ const base::Closure& link_callback,
+ const base::Closure& tunnel_callback)
+ : get_interface_name_(GetInterfaceName),
+ address_callback_(address_callback),
link_callback_(link_callback),
+ tunnel_callback_(tunnel_callback),
netlink_fd_(-1),
is_offline_(true),
is_offline_initialized_(false),
@@ -135,7 +168,8 @@ void AddressTrackerLinux::Init() {
// Sending another request without first reading responses results in EBUSY.
bool address_changed;
bool link_changed;
- ReadMessages(&address_changed, &link_changed);
+ bool tunnel_changed;
+ ReadMessages(&address_changed, &link_changed, &tunnel_changed);
// Request dump of link state
request.header.nlmsg_type = RTM_GETLINK;
@@ -150,7 +184,7 @@ void AddressTrackerLinux::Init() {
}
// Consume pending message to populate links_online_, but don't notify.
- ReadMessages(&address_changed, &link_changed);
+ ReadMessages(&address_changed, &link_changed, &tunnel_changed);
{
base::AutoLock lock(is_offline_lock_);
is_offline_initialized_ = true;
@@ -195,9 +229,11 @@ AddressTrackerLinux::GetCurrentConnectionType() {
}
void AddressTrackerLinux::ReadMessages(bool* address_changed,
- bool* link_changed) {
+ bool* link_changed,
+ bool* tunnel_changed) {
*address_changed = false;
*link_changed = false;
+ *tunnel_changed = false;
char buffer[4096];
bool first_loop = true;
for (;;) {
@@ -217,7 +253,7 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed,
PLOG(ERROR) << "Failed to recv from netlink socket";
return;
}
- HandleMessage(buffer, rv, address_changed, link_changed);
+ HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
};
if (*link_changed) {
base::AutoLock lock(is_offline_lock_);
@@ -225,13 +261,13 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed,
}
}
-void AddressTrackerLinux::HandleMessage(const char* buffer,
+void AddressTrackerLinux::HandleMessage(char* buffer,
size_t length,
bool* address_changed,
- bool* link_changed) {
+ bool* link_changed,
+ bool* tunnel_changed) {
DCHECK(buffer);
- for (const struct nlmsghdr* header =
- reinterpret_cast<const struct nlmsghdr*>(buffer);
+ for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
NLMSG_OK(header, length);
header = NLMSG_NEXT(header, length)) {
switch (header->nlmsg_type) {
@@ -244,10 +280,20 @@ void AddressTrackerLinux::HandleMessage(const char* buffer,
} return;
case RTM_NEWADDR: {
IPAddressNumber address;
- if (GetAddress(header, &address)) {
+ bool really_deprecated;
+ if (GetAddress(header, &address, &really_deprecated)) {
base::AutoLock lock(address_map_lock_);
- const struct ifaddrmsg* msg =
+ struct ifaddrmsg* msg =
reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
+ // Routers may frequently (every few seconds) output the IPv6 ULA
+ // prefix which can cause the linux kernel to frequently output two
+ // back-to-back messages, one without the deprecated flag and one with
+ // the deprecated flag but both with preferred lifetimes of 0. Avoid
+ // interpretting this as an actual change by canonicalizing the two
+ // messages by setting the deprecated flag based on the preferred
+ // lifetime also. http://crbug.com/268042
+ if (really_deprecated)
+ msg->ifa_flags |= IFA_F_DEPRECATED;
// Only indicate change if the address is new or ifaddrmsg info has
// changed.
AddressMap::iterator it = address_map_.find(address);
@@ -262,7 +308,7 @@ void AddressTrackerLinux::HandleMessage(const char* buffer,
} break;
case RTM_DELADDR: {
IPAddressNumber address;
- if (GetAddress(header, &address)) {
+ if (GetAddress(header, &address, NULL)) {
base::AutoLock lock(address_map_lock_);
if (address_map_.erase(address))
*address_changed = true;
@@ -273,18 +319,27 @@ void AddressTrackerLinux::HandleMessage(const char* buffer,
reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
(msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
- if (online_links_.insert(msg->ifi_index).second)
+ if (online_links_.insert(msg->ifi_index).second) {
*link_changed = true;
+ if (IsTunnelInterface(msg))
+ *tunnel_changed = true;
+ }
} else {
- if (online_links_.erase(msg->ifi_index))
+ if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
+ if (IsTunnelInterface(msg))
+ *tunnel_changed = true;
+ }
}
} break;
case RTM_DELLINK: {
const struct ifinfomsg* msg =
reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
- if (online_links_.erase(msg->ifi_index))
+ if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
+ if (IsTunnelInterface(msg))
+ *tunnel_changed = true;
+ }
} break;
default:
break;
@@ -296,11 +351,14 @@ void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
DCHECK_EQ(netlink_fd_, fd);
bool address_changed;
bool link_changed;
- ReadMessages(&address_changed, &link_changed);
+ bool tunnel_changed;
+ ReadMessages(&address_changed, &link_changed, &tunnel_changed);
if (address_changed)
address_callback_.Run();
if (link_changed)
link_callback_.Run();
+ if (tunnel_changed)
+ tunnel_callback_.Run();
}
void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
@@ -311,5 +369,10 @@ void AddressTrackerLinux::CloseSocket() {
netlink_fd_ = -1;
}
+bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
+ // Linux kernel drivers/net/tun.c uses "tun" name prefix.
+ return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
+}
+
} // namespace internal
} // namespace net
diff --git a/chromium/net/base/address_tracker_linux.h b/chromium/net/base/address_tracker_linux.h
index e5ab692c233..415e8c89e9d 100644
--- a/chromium/net/base/address_tracker_linux.h
+++ b/chromium/net/base/address_tracker_linux.h
@@ -33,10 +33,12 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
public:
typedef std::map<IPAddressNumber, struct ifaddrmsg> AddressMap;
- // Will run |address_callback| when the AddressMap changes and will run
- // |link_callback| when the list of online links changes.
+ // Will run |address_callback| when the AddressMap changes, |link_callback|
+ // when the list of online links changes, and |tunnel_callback| when the list
+ // of online tunnels changes.
AddressTrackerLinux(const base::Closure& address_callback,
- const base::Closure& link_callback);
+ const base::Closure& link_callback,
+ const base::Closure& tunnel_callback);
virtual ~AddressTrackerLinux();
// Starts watching system configuration for changes. The current thread must
@@ -52,18 +54,27 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
private:
friend class AddressTrackerLinuxTest;
+ // A function that returns the name of an interface given the interface index
+ // in |interface_index|.
+ typedef const char* (*GetInterfaceNameFunction)(int interface_index);
+
// Sets |*address_changed| to indicate whether |address_map_| changed and
- // sets |*link_changed| to indicate if |online_links_| changed while reading
- // messages from |netlink_fd_|.
- void ReadMessages(bool* address_changed, bool* link_changed);
+ // sets |*link_changed| to indicate if |online_links_| changed and sets
+ // |*tunnel_changed| to indicate if |online_links_| changed with regards to a
+ // tunnel interface while reading messages from |netlink_fd_|.
+ void ReadMessages(bool* address_changed,
+ bool* link_changed,
+ bool* tunnel_changed);
// Sets |*address_changed| to true if |address_map_| changed, sets
- // |*link_changed| to true if |online_links_| changed while reading the
- // message from |buffer|.
- void HandleMessage(const char* buffer,
+ // |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed|
+ // to true if |online_links_| changed with regards to a tunnel interface while
+ // reading the message from |buffer|.
+ void HandleMessage(char* buffer,
size_t length,
bool* address_changed,
- bool* link_changed);
+ bool* link_changed,
+ bool* tunnel_changed);
// Call when some part of initialization failed; forces online and unblocks.
void AbortAndForceOnline();
@@ -75,8 +86,17 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
// Close |netlink_fd_|
void CloseSocket();
+ // Does |msg| refer to a tunnel interface?
+ bool IsTunnelInterface(const struct ifinfomsg* msg) const;
+
+ // Gets the name of an interface given the interface index |interface_index|.
+ // May return empty string if it fails but should not return NULL. This is
+ // overridden by tests.
+ GetInterfaceNameFunction get_interface_name_;
+
base::Closure address_callback_;
base::Closure link_callback_;
+ base::Closure tunnel_callback_;
int netlink_fd_;
base::MessageLoopForIO::FileDescriptorWatcher watcher_;
diff --git a/chromium/net/base/address_tracker_linux_unittest.cc b/chromium/net/base/address_tracker_linux_unittest.cc
index 47083801224..1223d04f2ef 100644
--- a/chromium/net/base/address_tracker_linux_unittest.cc
+++ b/chromium/net/base/address_tracker_linux_unittest.cc
@@ -11,35 +11,69 @@
#include "base/bind.h"
#include "testing/gtest/include/gtest/gtest.h"
+#ifndef IFA_F_HOMEADDRESS
+#define IFA_F_HOMEADDRESS 0x10
+#endif
+
namespace net {
namespace internal {
+namespace {
-typedef std::vector<char> Buffer;
+const int kTestInterfaceTun = 123;
+
+const char* TestGetInterfaceName(int interface_index) {
+ if (interface_index == kTestInterfaceTun)
+ return "tun0";
+ return "eth0";
+}
-void Noop() {}
+} // namespace
+
+typedef std::vector<char> Buffer;
class AddressTrackerLinuxTest : public testing::Test {
protected:
- AddressTrackerLinuxTest() : tracker_(base::Bind(&Noop), base::Bind(&Noop)) {}
+ AddressTrackerLinuxTest()
+ : tracker_(base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing)),
+ original_get_interface_name_(tracker_.get_interface_name_) {
+ tracker_.get_interface_name_ = TestGetInterfaceName;
+ }
bool HandleAddressMessage(const Buffer& buf) {
+ Buffer writable_buf = buf;
bool address_changed = false;
bool link_changed = false;
- tracker_.HandleMessage(&buf[0], buf.size(),
- &address_changed, &link_changed);
+ bool tunnel_changed = false;
+ tracker_.HandleMessage(&writable_buf[0], buf.size(),
+ &address_changed, &link_changed, &tunnel_changed);
EXPECT_FALSE(link_changed);
return address_changed;
}
bool HandleLinkMessage(const Buffer& buf) {
+ Buffer writable_buf = buf;
bool address_changed = false;
bool link_changed = false;
- tracker_.HandleMessage(&buf[0], buf.size(),
- &address_changed, &link_changed);
+ bool tunnel_changed = false;
+ tracker_.HandleMessage(&writable_buf[0], buf.size(),
+ &address_changed, &link_changed, &tunnel_changed);
EXPECT_FALSE(address_changed);
return link_changed;
}
+ bool HandleTunnelMessage(const Buffer& buf) {
+ Buffer writable_buf = buf;
+ bool address_changed = false;
+ bool link_changed = false;
+ bool tunnel_changed = false;
+ tracker_.HandleMessage(&writable_buf[0], buf.size(),
+ &address_changed, &link_changed, &tunnel_changed);
+ EXPECT_FALSE(address_changed);
+ return tunnel_changed;
+ }
+
AddressTrackerLinux::AddressMap GetAddressMap() {
return tracker_.GetAddressMap();
}
@@ -49,6 +83,7 @@ class AddressTrackerLinuxTest : public testing::Test {
}
AddressTrackerLinux tracker_;
+ AddressTrackerLinux::GetInterfaceNameFunction original_get_interface_name_;
};
namespace {
@@ -103,12 +138,15 @@ class NetlinkMessage {
Buffer buffer_;
};
-void MakeAddrMessage(uint16 type,
- uint8 flags,
- uint8 family,
- const IPAddressNumber& address,
- const IPAddressNumber& local,
- Buffer* output) {
+#define INFINITY_LIFE_TIME 0xFFFFFFFF
+
+void MakeAddrMessageWithCacheInfo(uint16 type,
+ uint8 flags,
+ uint8 family,
+ const IPAddressNumber& address,
+ const IPAddressNumber& local,
+ uint32 preferred_lifetime,
+ Buffer* output) {
NetlinkMessage nlmsg(type);
struct ifaddrmsg msg = {};
msg.ifa_family = family;
@@ -118,9 +156,23 @@ void MakeAddrMessage(uint16 type,
nlmsg.AddAttribute(IFA_ADDRESS, &address[0], address.size());
if (local.size())
nlmsg.AddAttribute(IFA_LOCAL, &local[0], local.size());
+ struct ifa_cacheinfo cache_info = {};
+ cache_info.ifa_prefered = preferred_lifetime;
+ cache_info.ifa_valid = INFINITY_LIFE_TIME;
+ nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info));
nlmsg.AppendTo(output);
}
+void MakeAddrMessage(uint16 type,
+ uint8 flags,
+ uint8 family,
+ const IPAddressNumber& address,
+ const IPAddressNumber& local,
+ Buffer* output) {
+ MakeAddrMessageWithCacheInfo(type, flags, family, address, local,
+ INFINITY_LIFE_TIME, output);
+}
+
void MakeLinkMessage(uint16 type, uint32 flags, uint32 index, Buffer* output) {
NetlinkMessage nlmsg(type);
struct ifinfomsg msg = {};
@@ -258,6 +310,46 @@ TEST_F(AddressTrackerLinuxTest, DeleteAddress) {
EXPECT_EQ(0u, map.size());
}
+TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) {
+ const IPAddressNumber kEmpty;
+ const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));
+
+ Buffer buffer;
+ MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer);
+ EXPECT_TRUE(HandleAddressMessage(buffer));
+ AddressTrackerLinux::AddressMap map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(1u, map.count(kAddr3));
+ EXPECT_EQ(0, map[kAddr3].ifa_flags);
+
+ // Verify 0 preferred lifetime implies deprecated.
+ buffer.clear();
+ MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0,
+ &buffer);
+ EXPECT_TRUE(HandleAddressMessage(buffer));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);
+
+ // Verify properly flagged message doesn't imply change.
+ buffer.clear();
+ MakeAddrMessageWithCacheInfo(RTM_NEWADDR, IFA_F_DEPRECATED, AF_INET6, kEmpty,
+ kAddr3, 0, &buffer);
+ EXPECT_FALSE(HandleAddressMessage(buffer));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);
+
+ // Verify implied deprecated doesn't imply change.
+ buffer.clear();
+ MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0,
+ &buffer);
+ EXPECT_FALSE(HandleAddressMessage(buffer));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);
+}
+
TEST_F(AddressTrackerLinuxTest, IgnoredMessage) {
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
@@ -358,6 +450,53 @@ TEST_F(AddressTrackerLinuxTest, RemoveInterface) {
EXPECT_TRUE(GetOnlineLinks()->empty());
}
+TEST_F(AddressTrackerLinuxTest, TunnelInterface) {
+ Buffer buffer;
+
+ // Ignores without "tun" prefixed name.
+ MakeLinkMessage(RTM_NEWLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ 0, &buffer);
+ EXPECT_FALSE(HandleTunnelMessage(buffer));
+
+ // Verify success.
+ MakeLinkMessage(RTM_NEWLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ kTestInterfaceTun, &buffer);
+ EXPECT_TRUE(HandleTunnelMessage(buffer));
+
+ // Ignores redundant enables.
+ MakeLinkMessage(RTM_NEWLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ kTestInterfaceTun, &buffer);
+ EXPECT_FALSE(HandleTunnelMessage(buffer));
+
+ // Ignores deleting without "tun" prefixed name.
+ MakeLinkMessage(RTM_DELLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ 0, &buffer);
+ EXPECT_FALSE(HandleTunnelMessage(buffer));
+
+ // Verify successful deletion
+ MakeLinkMessage(RTM_DELLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ kTestInterfaceTun, &buffer);
+ EXPECT_TRUE(HandleTunnelMessage(buffer));
+
+ // Ignores redundant deletions.
+ MakeLinkMessage(RTM_DELLINK,
+ IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
+ kTestInterfaceTun, &buffer);
+ EXPECT_FALSE(HandleTunnelMessage(buffer));
+}
+
+// Check AddressTrackerLinux::get_interface_name_ original implementation
+// doesn't crash or return NULL.
+TEST_F(AddressTrackerLinuxTest, GetInterfaceName) {
+ for (int i = 0; i < 10; i++)
+ EXPECT_NE((const char*)NULL, original_get_interface_name_(i));
+}
+
} // namespace
} // namespace internal
diff --git a/chromium/net/base/backoff_entry.cc b/chromium/net/base/backoff_entry.cc
index b1826b7af46..0b3a06f1d55 100644
--- a/chromium/net/base/backoff_entry.cc
+++ b/chromium/net/base/backoff_entry.cc
@@ -8,7 +8,9 @@
#include <cmath>
#include <limits>
+#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/numerics/safe_math.h"
#include "base/rand_util.h"
namespace net {
@@ -132,22 +134,39 @@ base::TimeTicks BackoffEntry::CalculateReleaseTime() const {
// The delay is calculated with this formula:
// delay = initial_backoff * multiply_factor^(
// effective_failure_count - 1) * Uniform(1 - jitter_factor, 1]
- double delay = policy_->initial_delay_ms;
- delay *= pow(policy_->multiply_factor, effective_failure_count - 1);
- delay -= base::RandDouble() * policy_->jitter_factor * delay;
-
- const int64 kMaxInt64 = std::numeric_limits<int64>::max();
- int64 delay_int = (delay > kMaxInt64) ?
- kMaxInt64 : static_cast<int64>(delay + 0.5);
+ // Note: if the failure count is too high, |delay_ms| will become infinity
+ // after the exponential calculation, and then NaN after the jitter is
+ // accounted for. Both cases are handled by using CheckedNumeric<int64> to
+ // perform the conversion to integers.
+ double delay_ms = policy_->initial_delay_ms;
+ delay_ms *= pow(policy_->multiply_factor, effective_failure_count - 1);
+ delay_ms -= base::RandDouble() * policy_->jitter_factor * delay_ms;
+
+ // Do overflow checking in microseconds, the internal unit of TimeTicks.
+ const int64 kTimeTicksNowUs =
+ (ImplGetTimeNow() - base::TimeTicks()).InMicroseconds();
+ base::internal::CheckedNumeric<int64> calculated_release_time_us =
+ delay_ms + 0.5;
+ calculated_release_time_us *= base::Time::kMicrosecondsPerMillisecond;
+ calculated_release_time_us += kTimeTicksNowUs;
+
+ base::internal::CheckedNumeric<int64> maximum_release_time_us = kint64max;
+ if (policy_->maximum_backoff_ms >= 0) {
+ maximum_release_time_us = policy_->maximum_backoff_ms;
+ maximum_release_time_us *= base::Time::kMicrosecondsPerMillisecond;
+ maximum_release_time_us += kTimeTicksNowUs;
+ }
- // Ensure that we do not exceed maximum delay.
- if (policy_->maximum_backoff_ms >= 0)
- delay_int = std::min(delay_int, policy_->maximum_backoff_ms);
+ // Decide between maximum release time and calculated release time, accounting
+ // for overflow with both.
+ int64 release_time_us = std::min(
+ calculated_release_time_us.ValueOrDefault(kint64max),
+ maximum_release_time_us.ValueOrDefault(kint64max));
// Never reduce previously set release horizon, e.g. due to Retry-After
// header.
return std::max(
- ImplGetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int),
+ base::TimeTicks() + base::TimeDelta::FromMicroseconds(release_time_us),
exponential_backoff_release_time_);
}
diff --git a/chromium/net/base/backoff_entry_unittest.cc b/chromium/net/base/backoff_entry_unittest.cc
index 9a9f4cfca0a..560b2133db1 100644
--- a/chromium/net/base/backoff_entry_unittest.cc
+++ b/chromium/net/base/backoff_entry_unittest.cc
@@ -293,4 +293,22 @@ TEST(BackoffEntryTest, RetainCustomHorizonWhenInitialErrorsIgnored) {
EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
}
+TEST(BackoffEntryTest, OverflowProtection) {
+ BackoffEntry::Policy large_multiply_policy = base_policy;
+ large_multiply_policy.multiply_factor = 256;
+ TestBackoffEntry custom(&large_multiply_policy);
+
+ // Trigger enough failures such that more than 11 bits of exponent are used
+ // to represent the exponential backoff intermediate values. Given a multiply
+ // factor of 256 (2^8), 129 iterations is enough: 2^(8*(129-1)) = 2^1024.
+ for (int i = 0; i < 129; ++i) {
+ custom.set_now(custom.ImplGetTimeNow() + custom.GetTimeUntilRelease());
+ custom.InformOfRequest(false);
+ ASSERT_TRUE(custom.ShouldRejectRequest());
+ }
+
+ // Max delay should still be respected.
+ EXPECT_EQ(20000, custom.GetTimeUntilRelease().InMilliseconds());
+}
+
} // namespace
diff --git a/chromium/net/base/big_endian.cc b/chromium/net/base/big_endian.cc
deleted file mode 100644
index 888cf35c6d6..00000000000
--- a/chromium/net/base/big_endian.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2011 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/base/big_endian.h"
-
-#include "base/strings/string_piece.h"
-
-namespace net {
-
-BigEndianReader::BigEndianReader(const void* buf, size_t len)
- : ptr_(reinterpret_cast<const char*>(buf)), end_(ptr_ + len) {}
-
-bool BigEndianReader::Skip(size_t len) {
- if (ptr_ + len > end_)
- return false;
- ptr_ += len;
- return true;
-}
-
-bool BigEndianReader::ReadBytes(void* out, size_t len) {
- if (ptr_ + len > end_)
- return false;
- memcpy(out, ptr_, len);
- ptr_ += len;
- return true;
-}
-
-bool BigEndianReader::ReadPiece(base::StringPiece* out, size_t len) {
- if (ptr_ + len > end_)
- return false;
- *out = base::StringPiece(ptr_, len);
- ptr_ += len;
- return true;
-}
-
-template<typename T>
-bool BigEndianReader::Read(T* value) {
- if (ptr_ + sizeof(T) > end_)
- return false;
- ReadBigEndian<T>(ptr_, value);
- ptr_ += sizeof(T);
- return true;
-}
-
-bool BigEndianReader::ReadU8(uint8* value) {
- return Read(value);
-}
-
-bool BigEndianReader::ReadU16(uint16* value) {
- return Read(value);
-}
-
-bool BigEndianReader::ReadU32(uint32* value) {
- return Read(value);
-}
-
-BigEndianWriter::BigEndianWriter(void* buf, size_t len)
- : ptr_(reinterpret_cast<char*>(buf)), end_(ptr_ + len) {}
-
-bool BigEndianWriter::Skip(size_t len) {
- if (ptr_ + len > end_)
- return false;
- ptr_ += len;
- return true;
-}
-
-bool BigEndianWriter::WriteBytes(const void* buf, size_t len) {
- if (ptr_ + len > end_)
- return false;
- memcpy(ptr_, buf, len);
- ptr_ += len;
- return true;
-}
-
-template<typename T>
-bool BigEndianWriter::Write(T value) {
- if (ptr_ + sizeof(T) > end_)
- return false;
- WriteBigEndian<T>(ptr_, value);
- ptr_ += sizeof(T);
- return true;
-}
-
-bool BigEndianWriter::WriteU8(uint8 value) {
- return Write(value);
-}
-
-bool BigEndianWriter::WriteU16(uint16 value) {
- return Write(value);
-}
-
-bool BigEndianWriter::WriteU32(uint32 value) {
- return Write(value);
-}
-
-} // namespace net
-
diff --git a/chromium/net/base/big_endian.h b/chromium/net/base/big_endian.h
deleted file mode 100644
index 911f3c5074d..00000000000
--- a/chromium/net/base/big_endian.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2011 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_BASE_BIG_ENDIAN_H_
-#define NET_BASE_BIG_ENDIAN_H_
-
-#include "base/basictypes.h"
-#include "base/strings/string_piece.h"
-#include "net/base/net_export.h"
-
-namespace net {
-
-// Read an integer (signed or unsigned) from |buf| in Big Endian order.
-// Note: this loop is unrolled with -O1 and above.
-// NOTE(szym): glibc dns-canon.c and SpdyFrameBuilder use
-// ntohs(*(uint16_t*)ptr) which is potentially unaligned.
-// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M.
-template<typename T>
-inline void ReadBigEndian(const char buf[], T* out) {
- *out = buf[0];
- for (size_t i = 1; i < sizeof(T); ++i) {
- *out <<= 8;
- // Must cast to uint8 to avoid clobbering by sign extension.
- *out |= static_cast<uint8>(buf[i]);
- }
-}
-
-// Write an integer (signed or unsigned) |val| to |buf| in Big Endian order.
-// Note: this loop is unrolled with -O1 and above.
-template<typename T>
-inline void WriteBigEndian(char buf[], T val) {
- for (size_t i = 0; i < sizeof(T); ++i) {
- buf[sizeof(T)-i-1] = static_cast<char>(val & 0xFF);
- val >>= 8;
- }
-}
-
-// Specializations to make clang happy about the (dead code) shifts above.
-template<>
-inline void ReadBigEndian<uint8>(const char buf[], uint8* out) {
- *out = buf[0];
-}
-
-template<>
-inline void WriteBigEndian<uint8>(char buf[], uint8 val) {
- buf[0] = static_cast<char>(val);
-}
-
-// Allows reading integers in network order (big endian) while iterating over
-// an underlying buffer. All the reading functions advance the internal pointer.
-class NET_EXPORT BigEndianReader {
- public:
- BigEndianReader(const void* buf, size_t len);
-
- const char* ptr() const { return ptr_; }
- int remaining() const { return end_ - ptr_; }
-
- bool Skip(size_t len);
- bool ReadBytes(void* out, size_t len);
- // Creates a StringPiece in |out| that points to the underlying buffer.
- bool ReadPiece(base::StringPiece* out, size_t len);
- bool ReadU8(uint8* value);
- bool ReadU16(uint16* value);
- bool ReadU32(uint32* value);
-
- private:
- // Hidden to promote type safety.
- template<typename T>
- bool Read(T* v);
-
- const char* ptr_;
- const char* end_;
-};
-
-// Allows writing integers in network order (big endian) while iterating over
-// an underlying buffer. All the writing functions advance the internal pointer.
-class NET_EXPORT BigEndianWriter {
- public:
- BigEndianWriter(void* buf, size_t len);
-
- char* ptr() const { return ptr_; }
- int remaining() const { return end_ - ptr_; }
-
- bool Skip(size_t len);
- bool WriteBytes(const void* buf, size_t len);
- bool WriteU8(uint8 value);
- bool WriteU16(uint16 value);
- bool WriteU32(uint32 value);
-
- private:
- // Hidden to promote type safety.
- template<typename T>
- bool Write(T v);
-
- char* ptr_;
- char* end_;
-};
-
-} // namespace net
-
-#endif // NET_BASE_BIG_ENDIAN_H_
-
diff --git a/chromium/net/base/big_endian_unittest.cc b/chromium/net/base/big_endian_unittest.cc
deleted file mode 100644
index e758286efe1..00000000000
--- a/chromium/net/base/big_endian_unittest.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2011 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 "base/strings/string_piece.h"
-#include "net/base/big_endian.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-TEST(BigEndianReaderTest, ReadsValues) {
- char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC };
- char buf[2];
- uint8 u8;
- uint16 u16;
- uint32 u32;
- base::StringPiece piece;
- BigEndianReader reader(data, sizeof(data));
-
- EXPECT_TRUE(reader.Skip(2));
- EXPECT_EQ(data + 2, reader.ptr());
- EXPECT_EQ(reader.remaining(), static_cast<int>(sizeof(data)) - 2);
- EXPECT_TRUE(reader.ReadBytes(buf, sizeof(buf)));
- EXPECT_EQ(0x2, buf[0]);
- EXPECT_EQ(0x3, buf[1]);
- EXPECT_TRUE(reader.ReadU8(&u8));
- EXPECT_EQ(0x4, u8);
- EXPECT_TRUE(reader.ReadU16(&u16));
- EXPECT_EQ(0x0506, u16);
- EXPECT_TRUE(reader.ReadU32(&u32));
- EXPECT_EQ(0x0708090Au, u32);
- base::StringPiece expected(reader.ptr(), 2);
- EXPECT_TRUE(reader.ReadPiece(&piece, 2));
- EXPECT_EQ(2u, piece.size());
- EXPECT_EQ(expected.data(), piece.data());
-}
-
-TEST(BigEndianReaderTest, RespectsLength) {
- char data[4];
- char buf[2];
- uint8 u8;
- uint16 u16;
- uint32 u32;
- base::StringPiece piece;
- BigEndianReader reader(data, sizeof(data));
- // 4 left
- EXPECT_FALSE(reader.Skip(6));
- EXPECT_TRUE(reader.Skip(1));
- // 3 left
- EXPECT_FALSE(reader.ReadU32(&u32));
- EXPECT_FALSE(reader.ReadPiece(&piece, 4));
- EXPECT_TRUE(reader.Skip(2));
- // 1 left
- EXPECT_FALSE(reader.ReadU16(&u16));
- EXPECT_FALSE(reader.ReadBytes(buf, 2));
- EXPECT_TRUE(reader.Skip(1));
- // 0 left
- EXPECT_FALSE(reader.ReadU8(&u8));
- EXPECT_EQ(0, reader.remaining());
-}
-
-TEST(BigEndianWriterTest, WritesValues) {
- char expected[] = { 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 0xA };
- char data[sizeof(expected)];
- char buf[] = { 0x2, 0x3 };
- memset(data, 0, sizeof(data));
- BigEndianWriter writer(data, sizeof(data));
-
- EXPECT_TRUE(writer.Skip(2));
- EXPECT_TRUE(writer.WriteBytes(buf, sizeof(buf)));
- EXPECT_TRUE(writer.WriteU8(0x4));
- EXPECT_TRUE(writer.WriteU16(0x0506));
- EXPECT_TRUE(writer.WriteU32(0x0708090A));
- EXPECT_EQ(0, memcmp(expected, data, sizeof(expected)));
-}
-
-TEST(BigEndianWriterTest, RespectsLength) {
- char data[4];
- char buf[2];
- uint8 u8 = 0;
- uint16 u16 = 0;
- uint32 u32 = 0;
- BigEndianWriter writer(data, sizeof(data));
- // 4 left
- EXPECT_FALSE(writer.Skip(6));
- EXPECT_TRUE(writer.Skip(1));
- // 3 left
- EXPECT_FALSE(writer.WriteU32(u32));
- EXPECT_TRUE(writer.Skip(2));
- // 1 left
- EXPECT_FALSE(writer.WriteU16(u16));
- EXPECT_FALSE(writer.WriteBytes(buf, 2));
- EXPECT_TRUE(writer.Skip(1));
- // 0 left
- EXPECT_FALSE(writer.WriteU8(u8));
- EXPECT_EQ(0, writer.remaining());
-}
-
-} // namespace net
-
diff --git a/chromium/net/base/capturing_net_log.cc b/chromium/net/base/capturing_net_log.cc
index c7c2516e4a8..3837fe69b1d 100644
--- a/chromium/net/base/capturing_net_log.cc
+++ b/chromium/net/base/capturing_net_log.cc
@@ -55,6 +55,14 @@ bool CapturingNetLog::CapturedEntry::GetIntegerValue(
return params->GetInteger(name, value);
}
+bool CapturingNetLog::CapturedEntry::GetListValue(
+ const std::string& name,
+ base::ListValue** value) const {
+ if (!params)
+ return false;
+ return params->GetList(name, value);
+}
+
bool CapturingNetLog::CapturedEntry::GetNetErrorCode(int* value) const {
return GetIntegerValue("net_error", value);
}
@@ -106,7 +114,7 @@ void CapturingNetLog::Observer::OnAddEntry(const net::NetLog::Entry& entry) {
// Using Dictionaries instead of Values makes checking values a little
// simpler.
base::DictionaryValue* param_dict = NULL;
- Value* param_value = entry.ParametersToValue();
+ base::Value* param_value = entry.ParametersToValue();
if (param_value && !param_value->GetAsDictionary(&param_dict))
delete param_value;
diff --git a/chromium/net/base/capturing_net_log.h b/chromium/net/base/capturing_net_log.h
index 156a61d34a4..06bc976eeaa 100644
--- a/chromium/net/base/capturing_net_log.h
+++ b/chromium/net/base/capturing_net_log.h
@@ -19,6 +19,7 @@
namespace base {
class DictionaryValue;
+class ListValue;
}
namespace net {
@@ -50,6 +51,7 @@ class CapturingNetLog : public NetLog {
// modify |value| on failure.
bool GetStringValue(const std::string& name, std::string* value) const;
bool GetIntegerValue(const std::string& name, int* value) const;
+ bool GetListValue(const std::string& name, base::ListValue** value) const;
// Same as GetIntegerValue, but returns the error code associated with a
// log entry.
diff --git a/chromium/net/base/data_url.cc b/chromium/net/base/data_url.cc
index e7b46cd44ce..9699136c85f 100644
--- a/chromium/net/base/data_url.cc
+++ b/chromium/net/base/data_url.cc
@@ -13,6 +13,7 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "net/base/escape.h"
+#include "net/base/mime_util.h"
#include "url/gurl.h"
namespace net {
@@ -59,9 +60,12 @@ bool DataURL::Parse(const GURL& url, std::string* mime_type,
}
}
- // fallback to defaults if nothing specified in the URL:
- if (mime_type->empty())
+ if (mime_type->empty()) {
+ // fallback to defaults if nothing specified in the URL:
mime_type->assign("text/plain");
+ } else if (!ParseMimeTypeWithoutParameter(*mime_type, NULL, NULL)) {
+ return false;
+ }
if (charset->empty())
charset->assign("US-ASCII");
diff --git a/chromium/net/base/data_url_unittest.cc b/chromium/net/base/data_url_unittest.cc
index 2d8e817c980..43f881f7084 100644
--- a/chromium/net/base/data_url_unittest.cc
+++ b/chromium/net/base/data_url_unittest.cc
@@ -88,6 +88,13 @@ TEST(DataURLTest, Parse) {
"US-ASCII",
"<html><body><b>hello world</b></body></html>" },
+ // Bad mime type
+ { "data:f(oo/bar;baz=1;charset=kk,boo",
+ false,
+ "",
+ "",
+ "" },
+
// the comma cannot be url-escaped!
{ "data:%2Cblah",
false,
diff --git a/chromium/net/base/directory_lister_unittest.cc b/chromium/net/base/directory_lister_unittest.cc
index 0be09a5d7b4..005ce0d5128 100644
--- a/chromium/net/base/directory_lister_unittest.cc
+++ b/chromium/net/base/directory_lister_unittest.cc
@@ -10,7 +10,6 @@
#include "base/files/scoped_temp_dir.h"
#include "base/i18n/file_util_icu.h"
#include "base/message_loop/message_loop.h"
-#include "base/platform_file.h"
#include "base/strings/stringprintf.h"
#include "net/base/directory_lister.h"
#include "net/base/net_errors.h"
@@ -110,13 +109,9 @@ class DirectoryListerTest : public PlatformTest {
for (int i = 0; i < kFilesPerDirectory; i++) {
std::string file_name = base::StringPrintf("file_id_%d", i);
base::FilePath file_path = dir_data.first.AppendASCII(file_name);
- base::PlatformFile file = base::CreatePlatformFile(
- file_path,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
- NULL,
- NULL);
- ASSERT_NE(base::kInvalidPlatformFileValue, file);
- ASSERT_TRUE(base::ClosePlatformFile(file));
+ base::File file(file_path,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
}
if (dir_data.second < kMaxDepth - 1) {
for (int i = 0; i < kBranchingFactor; i++) {
diff --git a/chromium/net/base/dns_reloader.cc b/chromium/net/base/dns_reloader.cc
index 1902820177e..04abcb18552 100644
--- a/chromium/net/base/dns_reloader.cc
+++ b/chromium/net/base/dns_reloader.cc
@@ -47,7 +47,7 @@ class DnsReloader : public net::NetworkChangeNotifier::DNSObserver {
// NetworkChangeNotifier::DNSObserver:
virtual void OnDNSChanged() OVERRIDE {
- DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
+ DCHECK(base::MessageLoopForIO::IsCurrent());
base::AutoLock l(lock_);
resolver_generation_++;
}
diff --git a/chromium/net/base/escape.cc b/chromium/net/base/escape.cc
index d1b592c7171..ab70f1db301 100644
--- a/chromium/net/base/escape.cc
+++ b/chromium/net/base/escape.cc
@@ -97,15 +97,41 @@ const char kUrlUnescape[128] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0
};
+// Attempts to unescape the sequence at |index| within |escaped_text|. If
+// successful, sets |value| to the unescaped value. Returns whether
+// unescaping succeeded.
template<typename STR>
-STR UnescapeURLWithOffsetsImpl(const STR& escaped_text,
- UnescapeRule::Type rules,
- std::vector<size_t>* offsets_for_adjustment) {
- if (offsets_for_adjustment) {
- std::for_each(offsets_for_adjustment->begin(),
- offsets_for_adjustment->end(),
- base::LimitOffset<STR>(escaped_text.length()));
+bool UnescapeUnsignedCharAtIndex(const STR& escaped_text,
+ size_t index,
+ unsigned char* value) {
+ if ((index + 2) >= escaped_text.size())
+ return false;
+ if (escaped_text[index] != '%')
+ return false;
+ const typename STR::value_type most_sig_digit(
+ static_cast<typename STR::value_type>(escaped_text[index + 1]));
+ const typename STR::value_type least_sig_digit(
+ static_cast<typename STR::value_type>(escaped_text[index + 2]));
+ if (IsHexDigit(most_sig_digit) && IsHexDigit(least_sig_digit)) {
+ *value = HexDigitToInt(most_sig_digit) * 16 +
+ HexDigitToInt(least_sig_digit);
+ return true;
}
+ return false;
+}
+
+// Unescapes |escaped_text| according to |rules|, returning the resulting
+// string. Fills in an |adjustments| parameter, if non-NULL, so it reflects
+// the alterations done to the string that are not one-character-to-one-
+// character. The resulting |adjustments| will always be sorted by increasing
+// offset.
+template<typename STR>
+STR UnescapeURLWithAdjustmentsImpl(
+ const STR& escaped_text,
+ UnescapeRule::Type rules,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ if (adjustments)
+ adjustments->clear();
// Do not unescape anything, return the |escaped_text| text.
if (rules == UnescapeRule::NONE)
return escaped_text;
@@ -117,7 +143,6 @@ STR UnescapeURLWithOffsetsImpl(const STR& escaped_text,
result.reserve(escaped_text.length());
// Locations of adjusted text.
- net::internal::AdjustEncodingOffset::Adjustments adjustments;
for (size_t i = 0, max = escaped_text.size(); i < max; ++i) {
if (static_cast<unsigned char>(escaped_text[i]) >= 128) {
// Non ASCII character, append as is.
@@ -125,37 +150,73 @@ STR UnescapeURLWithOffsetsImpl(const STR& escaped_text,
continue;
}
- char current_char = static_cast<char>(escaped_text[i]);
- if (current_char == '%' && i + 2 < max) {
- const typename STR::value_type most_sig_digit(
- static_cast<typename STR::value_type>(escaped_text[i + 1]));
- const typename STR::value_type least_sig_digit(
- static_cast<typename STR::value_type>(escaped_text[i + 2]));
- if (IsHexDigit(most_sig_digit) && IsHexDigit(least_sig_digit)) {
- unsigned char value = HexDigitToInt(most_sig_digit) * 16 +
- HexDigitToInt(least_sig_digit);
- if (value >= 0x80 || // Unescape all high-bit characters.
- // For 7-bit characters, the lookup table tells us all valid chars.
- (kUrlUnescape[value] ||
- // ...and we allow some additional unescaping when flags are set.
- (value == ' ' && (rules & UnescapeRule::SPACES)) ||
- // Allow any of the prohibited but non-control characters when
- // we're doing "special" chars.
- (value > ' ' && (rules & UnescapeRule::URL_SPECIAL_CHARS)) ||
- // Additionally allow control characters if requested.
- (value < ' ' && (rules & UnescapeRule::CONTROL_CHARS)))) {
- // Use the unescaped version of the character.
- adjustments.push_back(i);
- result.push_back(value);
- i += 2;
- } else {
- // Keep escaped. Append a percent and we'll get the following two
- // digits on the next loops through.
- result.push_back('%');
+ unsigned char first_byte;
+ if (UnescapeUnsignedCharAtIndex(escaped_text, i, &first_byte)) {
+ // Per http://tools.ietf.org/html/rfc3987#section-4.1, the following BiDi
+ // control characters are not allowed to appear unescaped in URLs:
+ //
+ // U+200E LEFT-TO-RIGHT MARK (%E2%80%8E)
+ // U+200F RIGHT-TO-LEFT MARK (%E2%80%8F)
+ // U+202A LEFT-TO-RIGHT EMBEDDING (%E2%80%AA)
+ // U+202B RIGHT-TO-LEFT EMBEDDING (%E2%80%AB)
+ // U+202C POP DIRECTIONAL FORMATTING (%E2%80%AC)
+ // U+202D LEFT-TO-RIGHT OVERRIDE (%E2%80%AD)
+ // U+202E RIGHT-TO-LEFT OVERRIDE (%E2%80%AE)
+ //
+ // Additionally, the Unicode Technical Report (TR9) as referenced by RFC
+ // 3987 above has since added some new BiDi control characters.
+ // http://www.unicode.org/reports/tr9
+ //
+ // U+061C ARABIC LETTER MARK (%D8%9C)
+ // U+2066 LEFT-TO-RIGHT ISOLATE (%E2%81%A6)
+ // U+2067 RIGHT-TO-LEFT ISOLATE (%E2%81%A7)
+ // U+2068 FIRST STRONG ISOLATE (%E2%81%A8)
+ // U+2069 POP DIRECTIONAL ISOLATE (%E2%81%A9)
+
+ unsigned char second_byte;
+ // Check for ALM.
+ if ((first_byte == 0xD8) &&
+ UnescapeUnsignedCharAtIndex(escaped_text, i + 3, &second_byte) &&
+ (second_byte == 0x9c)) {
+ result.append(escaped_text, i, 6);
+ i += 5;
+ continue;
+ }
+
+ // Check for other BiDi control characters.
+ if ((first_byte == 0xE2) &&
+ UnescapeUnsignedCharAtIndex(escaped_text, i + 3, &second_byte) &&
+ ((second_byte == 0x80) || (second_byte == 0x81))) {
+ unsigned char third_byte;
+ if (UnescapeUnsignedCharAtIndex(escaped_text, i + 6, &third_byte) &&
+ ((second_byte == 0x80) ?
+ ((third_byte == 0x8E) || (third_byte == 0x8F) ||
+ ((third_byte >= 0xAA) && (third_byte <= 0xAE))) :
+ ((third_byte >= 0xA6) && (third_byte <= 0xA9)))) {
+ result.append(escaped_text, i, 9);
+ i += 8;
+ continue;
}
+ }
+
+ if (first_byte >= 0x80 || // Unescape all high-bit characters.
+ // For 7-bit characters, the lookup table tells us all valid chars.
+ (kUrlUnescape[first_byte] ||
+ // ...and we allow some additional unescaping when flags are set.
+ (first_byte == ' ' && (rules & UnescapeRule::SPACES)) ||
+ // Allow any of the prohibited but non-control characters when
+ // we're doing "special" chars.
+ (first_byte > ' ' && (rules & UnescapeRule::URL_SPECIAL_CHARS)) ||
+ // Additionally allow control characters if requested.
+ (first_byte < ' ' && (rules & UnescapeRule::CONTROL_CHARS)))) {
+ // Use the unescaped version of the character.
+ if (adjustments)
+ adjustments->push_back(base::OffsetAdjuster::Adjustment(i, 3, 1));
+ result.push_back(first_byte);
+ i += 2;
} else {
- // Invalid escape sequence, just pass the percent through and continue
- // right after it.
+ // Keep escaped. Append a percent and we'll get the following two
+ // digits on the next loops through.
result.push_back('%');
}
} else if ((rules & UnescapeRule::REPLACE_PLUS_WITH_SPACE) &&
@@ -167,13 +228,6 @@ STR UnescapeURLWithOffsetsImpl(const STR& escaped_text,
}
}
- // Make offset adjustment.
- if (offsets_for_adjustment && !adjustments.empty()) {
- std::for_each(offsets_for_adjustment->begin(),
- offsets_for_adjustment->end(),
- net::internal::AdjustEncodingOffset(adjustments));
- }
-
return result;
}
@@ -281,48 +335,39 @@ base::string16 EscapeForHTML(const base::string16& input) {
std::string UnescapeURLComponent(const std::string& escaped_text,
UnescapeRule::Type rules) {
- return UnescapeURLWithOffsetsImpl(escaped_text, rules, NULL);
+ return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, NULL);
}
base::string16 UnescapeURLComponent(const base::string16& escaped_text,
UnescapeRule::Type rules) {
- return UnescapeURLWithOffsetsImpl(escaped_text, rules, NULL);
+ return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, NULL);
}
-base::string16 UnescapeAndDecodeUTF8URLComponent(
- const std::string& text,
- UnescapeRule::Type rules,
- size_t* offset_for_adjustment) {
- std::vector<size_t> offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
- base::string16 result =
- UnescapeAndDecodeUTF8URLComponentWithOffsets(text, rules, &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
- return result;
+base::string16 UnescapeAndDecodeUTF8URLComponent(const std::string& text,
+ UnescapeRule::Type rules) {
+ return UnescapeAndDecodeUTF8URLComponentWithAdjustments(text, rules, NULL);
}
-base::string16 UnescapeAndDecodeUTF8URLComponentWithOffsets(
+base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments(
const std::string& text,
UnescapeRule::Type rules,
- std::vector<size_t>* offsets_for_adjustment) {
+ base::OffsetAdjuster::Adjustments* adjustments) {
base::string16 result;
- std::vector<size_t> original_offsets;
- if (offsets_for_adjustment)
- original_offsets = *offsets_for_adjustment;
- std::string unescaped_url(
- UnescapeURLWithOffsetsImpl(text, rules, offsets_for_adjustment));
- if (base::UTF8ToUTF16AndAdjustOffsets(unescaped_url.data(),
- unescaped_url.length(),
- &result, offsets_for_adjustment))
- return result; // Character set looks like it's valid.
-
- // Not valid. Return the escaped version. Undo our changes to
- // |offset_for_adjustment| since we haven't changed the string after all.
- if (offsets_for_adjustment)
- *offsets_for_adjustment = original_offsets;
- return base::UTF8ToUTF16AndAdjustOffsets(text, offsets_for_adjustment);
+ base::OffsetAdjuster::Adjustments unescape_adjustments;
+ std::string unescaped_url(UnescapeURLWithAdjustmentsImpl(
+ text, rules, &unescape_adjustments));
+ if (base::UTF8ToUTF16WithAdjustments(unescaped_url.data(),
+ unescaped_url.length(),
+ &result, adjustments)) {
+ // Character set looks like it's valid.
+ if (adjustments) {
+ base::OffsetAdjuster::MergeSequentialAdjustments(unescape_adjustments,
+ adjustments);
+ }
+ return result;
+ }
+ // Character set is not valid. Return the escaped version.
+ return base::UTF8ToUTF16WithAdjustments(text, adjustments);
}
base::string16 UnescapeForHTML(const base::string16& input) {
@@ -337,7 +382,7 @@ base::string16 UnescapeForHTML(const base::string16& input) {
{ "&#39;", '\''},
};
- if (input.find(ASCIIToUTF16("&")) == std::string::npos)
+ if (input.find(base::ASCIIToUTF16("&")) == std::string::npos)
return input;
base::string16 ampersand_chars[ARRAYSIZE_UNSAFE(kEscapeToChars)];
@@ -348,8 +393,10 @@ base::string16 UnescapeForHTML(const base::string16& input) {
// Potential ampersand encode char.
size_t index = iter - text.begin();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kEscapeToChars); i++) {
- if (ampersand_chars[i].empty())
- ampersand_chars[i] = ASCIIToUTF16(kEscapeToChars[i].ampersand_code);
+ if (ampersand_chars[i].empty()) {
+ ampersand_chars[i] =
+ base::ASCIIToUTF16(kEscapeToChars[i].ampersand_code);
+ }
if (text.find(ampersand_chars[i], index) == index) {
text.replace(iter, iter + ampersand_chars[i].length(),
1, kEscapeToChars[i].replacement);
@@ -361,32 +408,4 @@ base::string16 UnescapeForHTML(const base::string16& input) {
return text;
}
-namespace internal {
-
-AdjustEncodingOffset::AdjustEncodingOffset(const Adjustments& adjustments)
- : adjustments(adjustments) {}
-
-void AdjustEncodingOffset::operator()(size_t& offset) {
- // For each encoded character occurring before an offset subtract 2.
- if (offset == base::string16::npos)
- return;
- size_t adjusted_offset = offset;
- for (Adjustments::const_iterator i = adjustments.begin();
- i != adjustments.end(); ++i) {
- size_t location = *i;
- if (offset <= location) {
- offset = adjusted_offset;
- return;
- }
- if (offset <= (location + 2)) {
- offset = base::string16::npos;
- return;
- }
- adjusted_offset -= 2;
- }
- offset = adjusted_offset;
-}
-
-} // namespace internal
-
} // namespace net
diff --git a/chromium/net/base/escape.h b/chromium/net/base/escape.h
index 69eb2a5c656..1915d244188 100644
--- a/chromium/net/base/escape.h
+++ b/chromium/net/base/escape.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/strings/string16.h"
+#include "base/strings/utf_offset_string_conversions.h"
#include "net/base/net_export.h"
namespace net {
@@ -114,42 +115,21 @@ NET_EXPORT base::string16 UnescapeURLComponent(
// Unescapes the given substring as a URL, and then tries to interpret the
// result as being encoded as UTF-8. If the result is convertable into UTF-8, it
// will be returned as converted. If it is not, the original escaped string will
-// be converted into a base::string16 and returned. (|offset[s]_for_adjustment|)
-// specifies one or more offsets into the source strings; each offset will be
-// adjusted to point at the same logical place in the result strings during
-// decoding. If this isn't possible because an offset points past the end of
-// the source strings or into the middle of a multibyte sequence, the offending
-// offset will be set to string16::npos. |offset[s]_for_adjustment| may be NULL.
+// be converted into a base::string16 and returned. |adjustments| provides
+// information on how the original string was adjusted to get the string
+// returned.
NET_EXPORT base::string16 UnescapeAndDecodeUTF8URLComponent(
const std::string& text,
- UnescapeRule::Type rules,
- size_t* offset_for_adjustment);
-NET_EXPORT base::string16 UnescapeAndDecodeUTF8URLComponentWithOffsets(
+ UnescapeRule::Type rules);
+NET_EXPORT base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments(
const std::string& text,
UnescapeRule::Type rules,
- std::vector<size_t>* offsets_for_adjustment);
+ base::OffsetAdjuster::Adjustments* adjustments);
// Unescapes the following ampersand character codes from |text|:
// &lt; &gt; &amp; &quot; &#39;
NET_EXPORT base::string16 UnescapeForHTML(const base::string16& text);
-namespace internal {
-
-// Private Functions (Exposed for Unit Testing) --------------------------------
-
-// A function called by std::for_each that will adjust any offset which occurs
-// after one or more encoded characters.
-struct NET_EXPORT_PRIVATE AdjustEncodingOffset {
- typedef std::vector<size_t> Adjustments;
-
- explicit AdjustEncodingOffset(const Adjustments& adjustments);
- void operator()(size_t& offset);
-
- const Adjustments& adjustments;
-};
-
-} // namespace internal
-
} // namespace net
#endif // NET_BASE_ESCAPE_H_
diff --git a/chromium/net/base/escape_unittest.cc b/chromium/net/base/escape_unittest.cc
index e7e435c08ef..74ae29336d8 100644
--- a/chromium/net/base/escape_unittest.cc
+++ b/chromium/net/base/escape_unittest.cc
@@ -8,7 +8,6 @@
#include "net/base/escape.h"
#include "base/basictypes.h"
-#include "base/i18n/icu_string_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -17,8 +16,6 @@
namespace net {
namespace {
-const size_t kNpos = base::string16::npos;
-
struct EscapeCase {
const char* input;
const char* output;
@@ -122,6 +119,16 @@ TEST(EscapeTest, EscapePath) {
"%7B%7C%7D~%7F%80%FF");
}
+TEST(EscapeTest, DataURLWithAccentedCharacters) {
+ const std::string url =
+ "text/html;charset=utf-8,%3Chtml%3E%3Cbody%3ETonton,%20ton%20th%C3"
+ "%A9%20t'a-t-il%20%C3%B4t%C3%A9%20ta%20toux%20";
+
+ base::OffsetAdjuster::Adjustments adjustments;
+ net::UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+ url, UnescapeRule::SPACES, &adjustments);
+}
+
TEST(EscapeTest, EscapeUrlEncodedData) {
ASSERT_EQ(
// Most of the character space we care about, un-escaped
@@ -218,6 +225,31 @@ TEST(EscapeTest, UnescapeURLComponent) {
L"Some%20random text %25%2dOK"},
{L"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
L"Some%20random text %25-OK"},
+ {L"Some%20random text %25%E2%80", UnescapeRule::NORMAL,
+ L"Some%20random text %25\xE2\x80"},
+ {L"Some%20random text %25%E2%80OK", UnescapeRule::NORMAL,
+ L"Some%20random text %25\xE2\x80OK"},
+ {L"Some%20random text %25%E2%80%84OK", UnescapeRule::NORMAL,
+ L"Some%20random text %25\xE2\x80\x84OK"},
+
+ // BiDi Control characters should not be unescaped.
+ {L"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%D8%9COK"},
+ {L"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%80%8EOK"},
+ {L"Some%20random text %25%E2%80%8FOK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%80%8FOK"},
+ {L"Some%20random text %25%E2%80%AAOK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%80%AAOK"},
+ {L"Some%20random text %25%E2%80%ABOK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%80%ABOK"},
+ {L"Some%20random text %25%E2%80%AEOK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%80%AEOK"},
+ {L"Some%20random text %25%E2%81%A6OK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%81%A6OK"},
+ {L"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL,
+ L"Some%20random text %25%E2%81%A9OK"},
+
{L"Some%20random text %25%2dOK", UnescapeRule::SPACES,
L"Some random text %25-OK"},
{L"Some%20random text %25%2dOK", UnescapeRule::URL_SPECIAL_CHARS,
@@ -250,28 +282,28 @@ TEST(EscapeTest, UnescapeURLComponent) {
};
for (size_t i = 0; i < arraysize(unescape_cases); i++) {
- base::string16 str(WideToUTF16(unescape_cases[i].input));
- EXPECT_EQ(WideToUTF16(unescape_cases[i].output),
+ base::string16 str(base::WideToUTF16(unescape_cases[i].input));
+ EXPECT_EQ(base::WideToUTF16(unescape_cases[i].output),
UnescapeURLComponent(str, unescape_cases[i].rules));
}
// Test the NULL character unescaping (which wouldn't work above since those
// are just char pointers).
- base::string16 input(WideToUTF16(L"Null"));
+ base::string16 input(base::WideToUTF16(L"Null"));
input.push_back(0); // Also have a NULL in the input.
- input.append(WideToUTF16(L"%00%39Test"));
+ input.append(base::WideToUTF16(L"%00%39Test"));
// When we're unescaping NULLs
- base::string16 expected(WideToUTF16(L"Null"));
+ base::string16 expected(base::WideToUTF16(L"Null"));
expected.push_back(0);
expected.push_back(0);
- expected.append(ASCIIToUTF16("9Test"));
+ expected.append(base::ASCIIToUTF16("9Test"));
EXPECT_EQ(expected, UnescapeURLComponent(input, UnescapeRule::CONTROL_CHARS));
// When we're not unescaping NULLs.
- expected = WideToUTF16(L"Null");
+ expected = base::WideToUTF16(L"Null");
expected.push_back(0);
- expected.append(WideToUTF16(L"%009Test"));
+ expected.append(base::WideToUTF16(L"%009Test"));
EXPECT_EQ(expected, UnescapeURLComponent(input, UnescapeRule::NORMAL));
}
@@ -330,33 +362,62 @@ TEST(EscapeTest, UnescapeAndDecodeUTF8URLComponent) {
// TODO: Need to test unescape_spaces and unescape_percent.
base::string16 decoded = UnescapeAndDecodeUTF8URLComponent(
- unescape_cases[i].input, UnescapeRule::NORMAL, NULL);
- EXPECT_EQ(WideToUTF16(unescape_cases[i].decoded), decoded);
+ unescape_cases[i].input, UnescapeRule::NORMAL);
+ EXPECT_EQ(base::WideToUTF16(unescape_cases[i].decoded), decoded);
}
}
TEST(EscapeTest, AdjustOffset) {
const AdjustOffsetCase adjust_cases[] = {
{"", 0, 0},
- {"", 1, std::string::npos},
{"test", 0, 0},
{"test", 2, 2},
{"test", 4, 4},
- {"test", 5, std::string::npos},
{"test", std::string::npos, std::string::npos},
{"%2dtest", 6, 4},
+ {"%2dtest", 3, 1},
{"%2dtest", 2, std::string::npos},
+ {"%2dtest", 1, std::string::npos},
+ {"%2dtest", 0, 0},
{"test%2d", 2, 2},
{"%E4%BD%A0+%E5%A5%BD", 9, 1},
{"%E4%BD%A0+%E5%A5%BD", 6, std::string::npos},
- {"%ED%B0%80+%E5%A5%BD", 6, 6},
+ {"%E4%BD%A0+%E5%A5%BD", 0, 0},
+ {"%E4%BD%A0+%E5%A5%BD", 10, 2},
+ {"%E4%BD%A0+%E5%A5%BD", 19, 3},
+
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 18, 8},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 15, std::string::npos},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 9, 7},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 19, 9},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 28, 10},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 0, 0},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 2, 2},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 3, std::string::npos},
+ {"hi%41test%E4%BD%A0+%E5%A5%BD", 5, 3},
+
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 9, 1},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 6, std::string::npos},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 0, 0},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 10, 2},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 19, 3},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 21, 5},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 22, std::string::npos},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 24, 6},
+ {"%E4%BD%A0+%E5%A5%BDhi%41test", 28, 10},
+
+ {"%ED%B0%80+%E5%A5%BD", 6, 6}, // not convertable to UTF-8
};
for (size_t i = 0; i < arraysize(adjust_cases); i++) {
size_t offset = adjust_cases[i].input_offset;
- UnescapeAndDecodeUTF8URLComponent(adjust_cases[i].input,
- UnescapeRule::NORMAL, &offset);
- EXPECT_EQ(adjust_cases[i].output_offset, offset);
+ base::OffsetAdjuster::Adjustments adjustments;
+ UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+ adjust_cases[i].input, UnescapeRule::NORMAL, &adjustments);
+ base::OffsetAdjuster::AdjustOffset(adjustments, &offset);
+ EXPECT_EQ(adjust_cases[i].output_offset, offset)
+ << "input=" << adjust_cases[i].input
+ << " offset=" << adjust_cases[i].input_offset;
}
}
@@ -387,46 +448,11 @@ TEST(EscapeTest, UnescapeForHTML) {
{ "&amp; &", "& &" },
};
for (size_t i = 0; i < arraysize(tests); ++i) {
- base::string16 result = UnescapeForHTML(ASCIIToUTF16(tests[i].input));
- EXPECT_EQ(ASCIIToUTF16(tests[i].expected_output), result);
+ base::string16 result = UnescapeForHTML(base::ASCIIToUTF16(tests[i].input));
+ EXPECT_EQ(base::ASCIIToUTF16(tests[i].expected_output), result);
}
}
-TEST(EscapeTest, AdjustEncodingOffset) {
- // Imagine we have strings as shown in the following cases where the
- // %XX's represent encoded characters
-
- // 1: abc%ECdef ==> abcXdef
- std::vector<size_t> offsets;
- for (size_t t = 0; t < 9; ++t)
- offsets.push_back(t);
- internal::AdjustEncodingOffset::Adjustments adjustments;
- adjustments.push_back(3);
- std::for_each(offsets.begin(), offsets.end(),
- internal::AdjustEncodingOffset(adjustments));
- size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6};
- EXPECT_EQ(offsets.size(), arraysize(expected_1));
- for (size_t i = 0; i < arraysize(expected_1); ++i)
- EXPECT_EQ(expected_1[i], offsets[i]);
-
-
- // 2: %ECabc%EC%ECdef%EC ==> XabcXXdefX
- offsets.clear();
- for (size_t t = 0; t < 18; ++t)
- offsets.push_back(t);
- adjustments.clear();
- adjustments.push_back(0);
- adjustments.push_back(6);
- adjustments.push_back(9);
- adjustments.push_back(15);
- std::for_each(offsets.begin(), offsets.end(),
- internal::AdjustEncodingOffset(adjustments));
- size_t expected_2[] = {0, kNpos, kNpos, 1, 2, 3, 4, kNpos, kNpos, 5, kNpos,
- kNpos, 6, 7, 8, 9, kNpos, kNpos};
- EXPECT_EQ(offsets.size(), arraysize(expected_2));
- for (size_t i = 0; i < arraysize(expected_2); ++i)
- EXPECT_EQ(expected_2[i], offsets[i]);
-}
} // namespace
} // namespace net
diff --git a/chromium/net/base/file_stream.cc b/chromium/net/base/file_stream.cc
index fd2eb4af3f9..bf56a49e8c0 100644
--- a/chromium/net/base/file_stream.cc
+++ b/chromium/net/base/file_stream.cc
@@ -4,67 +4,22 @@
#include "net/base/file_stream.h"
-#include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/threading/worker_pool.h"
#include "net/base/file_stream_context.h"
-#include "net/base/file_stream_net_log_parameters.h"
#include "net/base/net_errors.h"
namespace net {
-FileStream::FileStream(NetLog* net_log,
- const scoped_refptr<base::TaskRunner>& task_runner)
- /* To allow never opened stream to be destroyed on any thread we set flags
- as if stream was opened asynchronously. */
- : open_flags_(base::PLATFORM_FILE_ASYNC),
- bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
- context_(new Context(bound_net_log_, task_runner)) {
- bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
-}
-
-FileStream::FileStream(NetLog* net_log)
- /* To allow never opened stream to be destroyed on any thread we set flags
- as if stream was opened asynchronously. */
- : open_flags_(base::PLATFORM_FILE_ASYNC),
- bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
- context_(new Context(bound_net_log_,
- base::WorkerPool::GetTaskRunner(true /* slow */))) {
- bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
+FileStream::FileStream(const scoped_refptr<base::TaskRunner>& task_runner)
+ : context_(new Context(task_runner)) {
}
-FileStream::FileStream(base::PlatformFile file,
- int flags,
- NetLog* net_log,
+FileStream::FileStream(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner)
- : open_flags_(flags),
- bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
- context_(new Context(file, bound_net_log_, open_flags_, task_runner)) {
- bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
-}
-
-FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log)
- : open_flags_(flags),
- bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
- context_(new Context(file,
- bound_net_log_,
- open_flags_,
- base::WorkerPool::GetTaskRunner(true /* slow */))) {
- bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
+ : context_(new Context(file.Pass(), task_runner)) {
}
FileStream::~FileStream() {
- if (!is_async()) {
- base::ThreadRestrictions::AssertIOAllowed();
- context_->CloseSync();
- context_.reset();
- } else {
- context_.release()->Orphan();
- }
-
- bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
+ context_.release()->Orphan();
}
int FileStream::Open(const base::FilePath& path, int open_flags,
@@ -74,40 +29,18 @@ int FileStream::Open(const base::FilePath& path, int open_flags,
return ERR_UNEXPECTED;
}
- open_flags_ = open_flags;
- DCHECK(is_async());
+ DCHECK(open_flags & base::File::FLAG_ASYNC);
context_->OpenAsync(path, open_flags, callback);
return ERR_IO_PENDING;
}
-int FileStream::OpenSync(const base::FilePath& path, int open_flags) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (IsOpen()) {
- DLOG(FATAL) << "File is already open!";
- return ERR_UNEXPECTED;
- }
-
- open_flags_ = open_flags;
- DCHECK(!is_async());
- return context_->OpenSync(path, open_flags_);
-}
-
int FileStream::Close(const CompletionCallback& callback) {
- DCHECK(is_async());
context_->CloseAsync(callback);
return ERR_IO_PENDING;
}
-int FileStream::CloseSync() {
- DCHECK(!is_async());
- base::ThreadRestrictions::AssertIOAllowed();
- context_->CloseSync();
- return OK;
-}
-
bool FileStream::IsOpen() const {
- return context_->file() != base::kInvalidPlatformFileValue;
+ return context_->file().IsValid();
}
int FileStream::Seek(Whence whence,
@@ -116,41 +49,10 @@ int FileStream::Seek(Whence whence,
if (!IsOpen())
return ERR_UNEXPECTED;
- // Make sure we're async.
- DCHECK(is_async());
context_->SeekAsync(whence, offset, callback);
return ERR_IO_PENDING;
}
-int64 FileStream::SeekSync(Whence whence, int64 offset) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // If we're in async, make sure we don't have a request in flight.
- DCHECK(!is_async() || !context_->async_in_progress());
- return context_->SeekSync(whence, offset);
-}
-
-int64 FileStream::Available() {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- int64 cur_pos = SeekSync(FROM_CURRENT, 0);
- if (cur_pos < 0)
- return cur_pos;
-
- int64 size = context_->GetFileSize();
- if (size < 0)
- return size;
-
- DCHECK_GE(size, cur_pos);
- return size - cur_pos;
-}
-
int FileStream::Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
@@ -159,139 +61,31 @@ int FileStream::Read(IOBuffer* buf,
// read(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
- DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
- DCHECK(is_async());
return context_->ReadAsync(buf, buf_len, callback);
}
-int FileStream::ReadSync(char* buf, int buf_len) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(!is_async());
- // read(..., 0) will return 0, which indicates end-of-file.
- DCHECK_GT(buf_len, 0);
- DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
-
- return context_->ReadSync(buf, buf_len);
-}
-
-int FileStream::ReadUntilComplete(char *buf, int buf_len) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- int to_read = buf_len;
- int bytes_total = 0;
-
- do {
- int bytes_read = ReadSync(buf, to_read);
- if (bytes_read <= 0) {
- if (bytes_total == 0)
- return bytes_read;
-
- return bytes_total;
- }
-
- bytes_total += bytes_read;
- buf += bytes_read;
- to_read -= bytes_read;
- } while (bytes_total < buf_len);
-
- return bytes_total;
-}
-
int FileStream::Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
- DCHECK(is_async());
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
// write(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
return context_->WriteAsync(buf, buf_len, callback);
}
-int FileStream::WriteSync(const char* buf, int buf_len) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(!is_async());
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
- // write(..., 0) will return 0, which indicates end-of-file.
- DCHECK_GT(buf_len, 0);
-
- return context_->WriteSync(buf, buf_len);
-}
-
-int64 FileStream::Truncate(int64 bytes) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // We'd better be open for writing.
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
-
- // Seek to the position to truncate from.
- int64 seek_position = SeekSync(FROM_BEGIN, bytes);
- if (seek_position != bytes)
- return ERR_UNEXPECTED;
-
- // And truncate the file.
- return context_->Truncate(bytes);
-}
-
int FileStream::Flush(const CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
- // Make sure we're async.
- DCHECK(is_async());
-
context_->FlushAsync(callback);
return ERR_IO_PENDING;
}
-int FileStream::FlushSync() {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
- return context_->FlushSync();
-}
-
-void FileStream::EnableErrorStatistics() {
- context_->set_record_uma(true);
-}
-
-void FileStream::SetBoundNetLogSource(const BoundNetLog& owner_bound_net_log) {
- if ((owner_bound_net_log.source().id == NetLog::Source::kInvalidId) &&
- (bound_net_log_.source().id == NetLog::Source::kInvalidId)) {
- // Both |BoundNetLog|s are invalid.
- return;
- }
-
- // Should never connect to itself.
- DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
-
- bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
- owner_bound_net_log.source().ToEventParametersCallback());
-
- owner_bound_net_log.AddEvent(NetLog::TYPE_FILE_STREAM_SOURCE,
- bound_net_log_.source().ToEventParametersCallback());
-}
-
-base::PlatformFile FileStream::GetPlatformFileForTesting() {
+const base::File& FileStream::GetFileForTesting() const {
return context_->file();
}
diff --git a/chromium/net/base/file_stream.h b/chromium/net/base/file_stream.h
index 9fe274759c7..57011b77dec 100644
--- a/chromium/net/base/file_stream.h
+++ b/chromium/net/base/file_stream.h
@@ -10,15 +10,14 @@
#ifndef NET_BASE_FILE_STREAM_H_
#define NET_BASE_FILE_STREAM_H_
-#include "base/platform_file.h"
-#include "base/task_runner.h"
+#include "base/files/file.h"
#include "net/base/completion_callback.h"
#include "net/base/file_stream_whence.h"
#include "net/base/net_export.h"
-#include "net/base/net_log.h"
namespace base {
class FilePath;
+class TaskRunner;
}
namespace net {
@@ -27,32 +26,15 @@ class IOBuffer;
class NET_EXPORT FileStream {
public:
- // Creates a |FileStream| with a new |BoundNetLog| (based on |net_log|)
- // attached. |net_log| may be NULL if no logging is needed.
+ // Creates a FileStream.
// Uses |task_runner| for asynchronous operations.
- FileStream(net::NetLog* net_log,
- const scoped_refptr<base::TaskRunner>& task_runner);
-
- // Same as above, but runs async tasks in base::WorkerPool.
- explicit FileStream(net::NetLog* net_log);
+ explicit FileStream(const scoped_refptr<base::TaskRunner>& task_runner);
- // Construct a FileStream with an existing file handle and opening flags.
- // |file| is valid file handle.
- // |flags| is a bitfield of base::PlatformFileFlags when the file handle was
- // opened.
- // |net_log| is the net log pointer to use to create a |BoundNetLog|. May be
- // NULL if logging is not needed.
+ // Construct a FileStream with an existing valid |file|.
// Uses |task_runner| for asynchronous operations.
- // Note: the new FileStream object takes ownership of the PlatformFile and
- // will close it on destruction.
- FileStream(base::PlatformFile file,
- int flags,
- net::NetLog* net_log,
+ FileStream(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner);
- // Same as above, but runs async tasks in base::WorkerPool.
- FileStream(base::PlatformFile file, int flags, net::NetLog* net_log);
-
// The underlying file is closed automatically.
virtual ~FileStream();
@@ -63,7 +45,7 @@ class NET_EXPORT FileStream {
//
// Once the operation is done, |callback| will be run on the thread where
// Open() was called, with the result code. open_flags is a bitfield of
- // base::PlatformFileFlags.
+ // base::File::Flags.
//
// If the file stream is not closed manually, the underlying file will be
// automatically closed when FileStream is destructed in an asynchronous
@@ -72,25 +54,12 @@ class NET_EXPORT FileStream {
virtual int Open(const base::FilePath& path, int open_flags,
const CompletionCallback& callback);
- // Call this method to open the FileStream synchronously.
- // The remaining methods cannot be used unless this method returns OK. If
- // the file cannot be opened then an error code is returned. open_flags is
- // a bitfield of base::PlatformFileFlags
- //
- // If the file stream is not closed manually, the underlying file will be
- // automatically closed when FileStream is destructed.
- virtual int OpenSync(const base::FilePath& path, int open_flags);
-
// Returns ERR_IO_PENDING and closes the file asynchronously, calling
// |callback| when done.
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
virtual int Close(const CompletionCallback& callback);
- // Closes the file immediately and returns OK. If the file is open
- // asynchronously, Close(const CompletionCallback&) should be used instead.
- virtual int CloseSync();
-
// Returns true if Open succeeded and Close has not been called.
virtual bool IsOpen() const;
@@ -103,23 +72,13 @@ class NET_EXPORT FileStream {
virtual int Seek(Whence whence, int64 offset,
const Int64CompletionCallback& callback);
- // Adjust the position from where data is read synchronously.
- // Upon success, the stream position relative to the start of the file is
- // returned. Otherwise, an error code is returned. It is not valid to
- // call SeekSync while a Read call has a pending completion.
- virtual int64 SeekSync(Whence whence, int64 offset);
-
- // Returns the number of bytes available to read from the current stream
- // position until the end of the file. Otherwise, an error code is returned.
- virtual int64 Available();
-
// Call this method to read data from the current stream position
// asynchronously. Up to buf_len bytes will be copied into buf. (In
// other words, partial reads are allowed.) Returns the number of bytes
// copied, 0 if at end-of-file, or an error code if the operation could
// not be performed.
//
- // The file must be opened with PLATFORM_FILE_ASYNC, and a non-null
+ // The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the read could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Read() was called, when the
@@ -136,30 +95,13 @@ class NET_EXPORT FileStream {
virtual int Read(IOBuffer* buf, int buf_len,
const CompletionCallback& callback);
- // Call this method to read data from the current stream position
- // synchronously. Up to buf_len bytes will be copied into buf. (In
- // other words, partial reads are allowed.) Returns the number of bytes
- // copied, 0 if at end-of-file, or an error code if the operation could
- // not be performed.
- //
- // The file must not be opened with PLATFORM_FILE_ASYNC.
- // This method must not be called if the stream was opened WRITE_ONLY.
- virtual int ReadSync(char* buf, int buf_len);
-
- // Performs the same as ReadSync, but ensures that exactly buf_len bytes
- // are copied into buf. A partial read may occur, but only as a result of
- // end-of-file or fatal error. Returns the number of bytes copied into buf,
- // 0 if at end-of-file and no bytes have been read into buf yet,
- // or an error code if the operation could not be performed.
- virtual int ReadUntilComplete(char *buf, int buf_len);
-
// Call this method to write data at the current stream position
// asynchronously. Up to buf_len bytes will be written from buf. (In
// other words, partial writes are allowed.) Returns the number of
// bytes written, or an error code if the operation could not be
// performed.
//
- // The file must be opened with PLATFORM_FILE_ASYNC, and a non-null
+ // The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the write could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Write() was called when
@@ -178,31 +120,12 @@ class NET_EXPORT FileStream {
virtual int Write(IOBuffer* buf, int buf_len,
const CompletionCallback& callback);
- // Call this method to write data at the current stream position
- // synchronously. Up to buf_len bytes will be written from buf. (In
- // other words, partial writes are allowed.) Returns the number of
- // bytes written, or an error code if the operation could not be
- // performed.
- //
- // The file must not be opened with PLATFORM_FILE_ASYNC.
- // This method must not be called if the stream was opened READ_ONLY.
- //
- // Zero byte writes are not allowed.
- virtual int WriteSync(const char* buf, int buf_len);
-
- // Truncates the file to be |bytes| length. This is only valid for writable
- // files. After truncation the file stream is positioned at |bytes|. The new
- // position is returned, or a value < 0 on error.
- // WARNING: one may not truncate a file beyond its current length on any
- // platform with this call.
- virtual int64 Truncate(int64 bytes);
-
// Forces out a filesystem sync on this file to make sure that the file was
// written out to disk and is not currently sitting in the buffer. This does
// not have to be called, it just forces one to happen at the time of
// calling.
//
- // The file must be opened with PLATFORM_FILE_ASYNC, and a non-null
+ // The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the write could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Flush() was called when
@@ -218,43 +141,17 @@ class NET_EXPORT FileStream {
// This method should not be called if the stream was opened READ_ONLY.
virtual int Flush(const CompletionCallback& callback);
- // Forces out a filesystem sync on this file to make sure that the file was
- // written out to disk and is not currently sitting in the buffer. This does
- // not have to be called, it just forces one to happen at the time of
- // calling.
- //
- // Returns an error code if the operation could not be performed.
- //
- // This method should not be called if the stream was opened READ_ONLY.
- virtual int FlushSync();
-
- // Turns on UMA error statistics gathering.
- void EnableErrorStatistics();
-
- // Sets the source reference for net-internals logging.
- // Creates source dependency events between |owner_bound_net_log| and
- // |bound_net_log_|. Each gets an event showing the dependency on the other.
- // If only one of those is valid, it gets an event showing that a change
- // of ownership happened, but without details.
- void SetBoundNetLogSource(const net::BoundNetLog& owner_bound_net_log);
-
- // Returns the underlying platform file for testing.
- base::PlatformFile GetPlatformFileForTesting();
+ // Returns the underlying file for testing.
+ const base::File& GetFileForTesting() const;
private:
class Context;
- bool is_async() const { return !!(open_flags_ & base::PLATFORM_FILE_ASYNC); }
-
- int open_flags_;
- net::BoundNetLog bound_net_log_;
-
- // Context performing I/O operations. It was extracted into separate class
+ // Context performing I/O operations. It was extracted into a separate class
// to perform asynchronous operations because FileStream can be destroyed
- // before completion of async operation. Also if async FileStream is destroyed
- // without explicit closing file should be closed asynchronously without
- // delaying FileStream's destructor. To perform all that separate object is
- // necessary.
+ // before completion of an async operation. Also if a FileStream is destroyed
+ // without explicitly calling Close, the file should be closed asynchronously
+ // without delaying FileStream's destructor.
scoped_ptr<Context> context_;
DISALLOW_COPY_AND_ASSIGN(FileStream);
diff --git a/chromium/net/base/file_stream_context.cc b/chromium/net/base/file_stream_context.cc
index e7fe100df27..fbfe5368dd8 100644
--- a/chromium/net/base/file_stream_context.cc
+++ b/chromium/net/base/file_stream_context.cc
@@ -4,26 +4,28 @@
#include "net/base/file_stream_context.h"
+#include "base/files/file_path.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
-#include "net/base/file_stream_net_log_parameters.h"
+#include "base/values.h"
#include "net/base/net_errors.h"
#if defined(OS_ANDROID)
#include "base/android/content_uri_utils.h"
#endif
+namespace net {
+
namespace {
-void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) {
+void CallInt64ToInt(const CompletionCallback& callback, int64 result) {
callback.Run(static_cast<int>(result));
}
-}
-
-namespace net {
+} // namespace
FileStream::Context::IOResult::IOResult()
: result(OK),
@@ -41,27 +43,42 @@ FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
return IOResult(MapSystemError(os_error), os_error);
}
-FileStream::Context::OpenResult::OpenResult()
- : file(base::kInvalidPlatformFileValue) {
+// ---------------------------------------------------------------------
+
+FileStream::Context::OpenResult::OpenResult() {
}
-FileStream::Context::OpenResult::OpenResult(base::PlatformFile file,
+FileStream::Context::OpenResult::OpenResult(base::File file,
IOResult error_code)
- : file(file),
+ : file(file.Pass()),
error_code(error_code) {
}
+FileStream::Context::OpenResult::OpenResult(RValue other)
+ : file(other.object->file.Pass()),
+ error_code(other.object->error_code) {
+}
+
+FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
+ RValue other) {
+ if (this != other.object) {
+ file = other.object->file.Pass();
+ error_code = other.object->error_code;
+ }
+ return *this;
+}
+
+// ---------------------------------------------------------------------
+
void FileStream::Context::Orphan() {
DCHECK(!orphaned_);
orphaned_ = true;
- if (file_ != base::kInvalidPlatformFileValue)
- bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
if (!async_in_progress_) {
CloseAndDelete();
- } else if (file_ != base::kInvalidPlatformFileValue) {
- CancelIo(file_);
+ } else if (file_.IsValid()) {
+ CancelIo(file_.GetPlatformFile());
}
}
@@ -70,9 +87,7 @@ void FileStream::Context::OpenAsync(const base::FilePath& path,
const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
- BeginOpenEvent(path);
-
- const bool posted = base::PostTaskAndReplyWithResult(
+ bool posted = base::PostTaskAndReplyWithResult(
task_runner_.get(),
FROM_HERE,
base::Bind(
@@ -83,42 +98,15 @@ void FileStream::Context::OpenAsync(const base::FilePath& path,
async_in_progress_ = true;
}
-int FileStream::Context::OpenSync(const base::FilePath& path, int open_flags) {
- DCHECK(!async_in_progress_);
-
- BeginOpenEvent(path);
- OpenResult result = OpenFileImpl(path, open_flags);
- file_ = result.file;
- if (file_ == base::kInvalidPlatformFileValue) {
- ProcessOpenError(result.error_code);
- } else {
- // TODO(satorux): Remove this once all async clients are migrated to use
- // Open(). crbug.com/114783
- if (open_flags & base::PLATFORM_FILE_ASYNC)
- OnAsyncFileOpened();
- }
- return result.error_code.result;
-}
-
-void FileStream::Context::CloseSync() {
- DCHECK(!async_in_progress_);
- if (file_ != base::kInvalidPlatformFileValue) {
- base::ClosePlatformFile(file_);
- file_ = base::kInvalidPlatformFileValue;
- bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
- }
-}
-
void FileStream::Context::CloseAsync(const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
- const bool posted = base::PostTaskAndReplyWithResult(
+ bool posted = base::PostTaskAndReplyWithResult(
task_runner_.get(),
FROM_HERE,
base::Bind(&Context::CloseFileImpl, base::Unretained(this)),
- base::Bind(&Context::ProcessAsyncResult,
+ base::Bind(&Context::OnAsyncCompleted,
base::Unretained(this),
- IntToInt64(callback),
- FILE_ERROR_SOURCE_CLOSE));
+ IntToInt64(callback)));
DCHECK(posted);
async_in_progress_ = true;
@@ -129,80 +117,46 @@ void FileStream::Context::SeekAsync(Whence whence,
const Int64CompletionCallback& callback) {
DCHECK(!async_in_progress_);
- const bool posted = base::PostTaskAndReplyWithResult(
+ bool posted = base::PostTaskAndReplyWithResult(
task_runner_.get(),
FROM_HERE,
base::Bind(
&Context::SeekFileImpl, base::Unretained(this), whence, offset),
- base::Bind(&Context::ProcessAsyncResult,
+ base::Bind(&Context::OnAsyncCompleted,
base::Unretained(this),
- callback,
- FILE_ERROR_SOURCE_SEEK));
+ callback));
DCHECK(posted);
async_in_progress_ = true;
}
-int64 FileStream::Context::SeekSync(Whence whence, int64 offset) {
- IOResult result = SeekFileImpl(whence, offset);
- RecordError(result, FILE_ERROR_SOURCE_SEEK);
- return result.result;
-}
-
void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
- const bool posted = base::PostTaskAndReplyWithResult(
+ bool posted = base::PostTaskAndReplyWithResult(
task_runner_.get(),
FROM_HERE,
base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
- base::Bind(&Context::ProcessAsyncResult,
+ base::Bind(&Context::OnAsyncCompleted,
base::Unretained(this),
- IntToInt64(callback),
- FILE_ERROR_SOURCE_FLUSH));
+ IntToInt64(callback)));
DCHECK(posted);
async_in_progress_ = true;
}
-int FileStream::Context::FlushSync() {
- IOResult result = FlushFileImpl();
- RecordError(result, FILE_ERROR_SOURCE_FLUSH);
- return result.result;
-}
-
-void FileStream::Context::RecordError(const IOResult& result,
- FileErrorSource source) const {
- if (result.result >= 0) {
- // |result| is not an error.
- return;
- }
-
- if (!orphaned_) {
- bound_net_log_.AddEvent(
- NetLog::TYPE_FILE_STREAM_ERROR,
- base::Bind(&NetLogFileStreamErrorCallback,
- source, result.os_error,
- static_cast<net::Error>(result.result)));
- }
-
- RecordFileError(result.os_error, source, record_uma_);
-}
-
-void FileStream::Context::BeginOpenEvent(const base::FilePath& path) {
- std::string file_name = path.AsUTF8Unsafe();
- bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN,
- NetLog::StringCallback("file_name", &file_name));
-}
-
FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
const base::FilePath& path, int open_flags) {
- base::PlatformFile file;
+#if defined(OS_POSIX)
+ // Always use blocking IO.
+ open_flags &= ~base::File::FLAG_ASYNC;
+#endif
+ base::File file;
#if defined(OS_ANDROID)
if (path.IsContentUri()) {
// Check that only Read flags are set.
- DCHECK_EQ(open_flags & ~base::PLATFORM_FILE_ASYNC,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
+ DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
file = base::OpenContentUriForRead(path);
} else {
#endif // defined(OS_ANDROID)
@@ -210,67 +164,55 @@ FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
// independently from FileStream's destructor. It can cause problems for
// users wanting to delete the file right after FileStream deletion. Thus
// we are always adding SHARE_DELETE flag to accommodate such use case.
- open_flags |= base::PLATFORM_FILE_SHARE_DELETE;
- file = base::CreatePlatformFile(path, open_flags, NULL, NULL);
+ // TODO(rvargas): This sounds like a bug, as deleting the file would
+ // presumably happen on the wrong thread. There should be an async delete.
+ open_flags |= base::File::FLAG_SHARE_DELETE;
+ file.Initialize(path, open_flags);
#if defined(OS_ANDROID)
}
#endif // defined(OS_ANDROID)
- if (file == base::kInvalidPlatformFileValue)
- return OpenResult(file, IOResult::FromOSError(GetLastErrno()));
+ if (!file.IsValid())
+ return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno()));
- return OpenResult(file, IOResult(OK, 0));
+ return OpenResult(file.Pass(), IOResult(OK, 0));
}
-void FileStream::Context::ProcessOpenError(const IOResult& error_code) {
- bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
- RecordError(error_code, FILE_ERROR_SOURCE_OPEN);
+FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
+ file_.Close();
+ return IOResult(OK, 0);
}
void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
OpenResult open_result) {
- file_ = open_result.file;
- if (file_ == base::kInvalidPlatformFileValue)
- ProcessOpenError(open_result.error_code);
- else if (!orphaned_)
+ file_ = open_result.file.Pass();
+ if (file_.IsValid() && !orphaned_)
OnAsyncFileOpened();
- OnAsyncCompleted(IntToInt64(callback), open_result.error_code.result);
+
+ OnAsyncCompleted(IntToInt64(callback), open_result.error_code);
}
void FileStream::Context::CloseAndDelete() {
DCHECK(!async_in_progress_);
- if (file_ == base::kInvalidPlatformFileValue) {
- delete this;
- } else {
- const bool posted = task_runner_->PostTaskAndReply(
+ if (file_.IsValid()) {
+ bool posted = task_runner_.get()->PostTask(
FROM_HERE,
- base::Bind(base::IgnoreResult(&base::ClosePlatformFile), file_),
- base::Bind(&Context::OnCloseCompleted, base::Unretained(this)));
+ base::Bind(base::IgnoreResult(&Context::CloseFileImpl),
+ base::Owned(this)));
DCHECK(posted);
- file_ = base::kInvalidPlatformFileValue;
+ } else {
+ delete this;
}
}
-void FileStream::Context::OnCloseCompleted() {
- delete this;
-}
-
Int64CompletionCallback FileStream::Context::IntToInt64(
const CompletionCallback& callback) {
return base::Bind(&CallInt64ToInt, callback);
}
-void FileStream::Context::ProcessAsyncResult(
- const Int64CompletionCallback& callback,
- FileErrorSource source,
- const IOResult& result) {
- RecordError(result, source);
- OnAsyncCompleted(callback, result.result);
-}
-
void FileStream::Context::OnAsyncCompleted(
const Int64CompletionCallback& callback,
- int64 result) {
+ const IOResult& result) {
// Reset this before Run() as Run() may issue a new async operation. Also it
// should be reset before CloseAsync() because it shouldn't run if any async
// operation is in progress.
@@ -278,8 +220,7 @@ void FileStream::Context::OnAsyncCompleted(
if (orphaned_)
CloseAndDelete();
else
- callback.Run(result);
+ callback.Run(result.result);
}
} // namespace net
-
diff --git a/chromium/net/base/file_stream_context.h b/chromium/net/base/file_stream_context.h
index 3ce8a5b6cdc..c4a06ee1de6 100644
--- a/chromium/net/base/file_stream_context.h
+++ b/chromium/net/base/file_stream_context.h
@@ -27,14 +27,13 @@
#ifndef NET_BASE_FILE_STREAM_CONTEXT_H_
#define NET_BASE_FILE_STREAM_CONTEXT_H_
+#include "base/files/file.h"
#include "base/message_loop/message_loop.h"
-#include "base/platform_file.h"
+#include "base/move.h"
#include "base/task_runner.h"
#include "net/base/completion_callback.h"
#include "net/base/file_stream.h"
-#include "net/base/file_stream_metrics.h"
#include "net/base/file_stream_whence.h"
-#include "net/base/net_log.h"
#if defined(OS_POSIX)
#include <errno.h>
@@ -59,38 +58,27 @@ class FileStream::Context {
// file_stream_context_{win,posix}.cc.
////////////////////////////////////////////////////////////////////////////
- Context(const BoundNetLog& bound_net_log,
- const scoped_refptr<base::TaskRunner>& task_runner);
- Context(base::PlatformFile file,
- const BoundNetLog& bound_net_log,
- int open_flags,
- const scoped_refptr<base::TaskRunner>& task_runner);
+ explicit Context(const scoped_refptr<base::TaskRunner>& task_runner);
+ Context(base::File file, const scoped_refptr<base::TaskRunner>& task_runner);
#if defined(OS_WIN)
virtual ~Context();
#elif defined(OS_POSIX)
~Context();
#endif
- int64 GetFileSize() const;
-
int ReadAsync(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback);
- int ReadSync(char* buf, int buf_len);
int WriteAsync(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback);
- int WriteSync(const char* buf, int buf_len);
-
- int Truncate(int64 bytes);
////////////////////////////////////////////////////////////////////////////
// Inline methods.
////////////////////////////////////////////////////////////////////////////
- void set_record_uma(bool value) { record_uma_ = value; }
- base::PlatformFile file() const { return file_; }
+ const base::File& file() const { return file_; }
bool async_in_progress() const { return async_in_progress_; }
////////////////////////////////////////////////////////////////////////////
@@ -105,19 +93,14 @@ class FileStream::Context {
void OpenAsync(const base::FilePath& path,
int open_flags,
const CompletionCallback& callback);
- int OpenSync(const base::FilePath& path, int open_flags);
-
- void CloseSync();
void CloseAsync(const CompletionCallback& callback);
void SeekAsync(Whence whence,
int64 offset,
const Int64CompletionCallback& callback);
- int64 SeekSync(Whence whence, int64 offset);
void FlushAsync(const CompletionCallback& callback);
- int FlushSync();
private:
////////////////////////////////////////////////////////////////////////////
@@ -134,37 +117,33 @@ class FileStream::Context {
};
struct OpenResult {
+ MOVE_ONLY_TYPE_FOR_CPP_03(OpenResult, RValue)
+ public:
OpenResult();
- OpenResult(base::PlatformFile file, IOResult error_code);
- base::PlatformFile file;
+ OpenResult(base::File file, IOResult error_code);
+ // C++03 move emulation of this type.
+ OpenResult(RValue other);
+ OpenResult& operator=(RValue other);
+
+ base::File file;
IOResult error_code;
};
- // Log the error from |result| to |bound_net_log_|.
- void RecordError(const IOResult& result, FileErrorSource source) const;
-
- void BeginOpenEvent(const base::FilePath& path);
-
OpenResult OpenFileImpl(const base::FilePath& path, int open_flags);
- void ProcessOpenError(const IOResult& result);
+ IOResult CloseFileImpl();
+
void OnOpenCompleted(const CompletionCallback& callback,
OpenResult open_result);
void CloseAndDelete();
- void OnCloseCompleted();
Int64CompletionCallback IntToInt64(const CompletionCallback& callback);
- // Called when asynchronous Seek() is completed.
- // Reports error if needed and calls callback.
- void ProcessAsyncResult(const Int64CompletionCallback& callback,
- FileErrorSource source,
- const IOResult& result);
-
// Called when asynchronous Open() or Seek()
// is completed. |result| contains the result or a network error code.
- void OnAsyncCompleted(const Int64CompletionCallback& callback, int64 result);
+ void OnAsyncCompleted(const Int64CompletionCallback& callback,
+ const IOResult& result);
////////////////////////////////////////////////////////////////////////////
// Helper stuff which is platform-dependent but is used in the platform-
@@ -193,9 +172,6 @@ class FileStream::Context {
// Flushes all data written to the stream.
IOResult FlushFileImpl();
- // Closes the file.
- IOResult CloseFileImpl();
-
#if defined(OS_WIN)
void IOCompletionIsPending(const CompletionCallback& callback, IOBuffer* buf);
@@ -214,18 +190,15 @@ class FileStream::Context {
IOResult WriteFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
#endif
- base::PlatformFile file_;
- bool record_uma_;
+ base::File file_;
bool async_in_progress_;
bool orphaned_;
- BoundNetLog bound_net_log_;
scoped_refptr<base::TaskRunner> task_runner_;
#if defined(OS_WIN)
base::MessageLoopForIO::IOContext io_context_;
CompletionCallback callback_;
scoped_refptr<IOBuffer> in_flight_buf_;
- FileErrorSource error_source_;
#endif
DISALLOW_COPY_AND_ASSIGN(Context);
@@ -234,4 +207,3 @@ class FileStream::Context {
} // namespace net
#endif // NET_BASE_FILE_STREAM_CONTEXT_H_
-
diff --git a/chromium/net/base/file_stream_context_posix.cc b/chromium/net/base/file_stream_context_posix.cc
index 6e6bc6eaa69..9f3d060822f 100644
--- a/chromium/net/base/file_stream_context_posix.cc
+++ b/chromium/net/base/file_stream_context_posix.cc
@@ -22,6 +22,7 @@
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -44,42 +45,23 @@ COMPILE_ASSERT(FROM_BEGIN == SEEK_SET &&
FROM_CURRENT == SEEK_CUR &&
FROM_END == SEEK_END, whence_matches_system);
-FileStream::Context::Context(const BoundNetLog& bound_net_log,
- const scoped_refptr<base::TaskRunner>& task_runner)
- : file_(base::kInvalidPlatformFileValue),
- record_uma_(false),
- async_in_progress_(false),
+FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner)
+ : async_in_progress_(false),
orphaned_(false),
- bound_net_log_(bound_net_log),
task_runner_(task_runner) {
}
-FileStream::Context::Context(base::PlatformFile file,
- const BoundNetLog& bound_net_log,
- int /* open_flags */,
+FileStream::Context::Context(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner)
- : file_(file),
- record_uma_(false),
+ : file_(file.Pass()),
async_in_progress_(false),
orphaned_(false),
- bound_net_log_(bound_net_log),
task_runner_(task_runner) {
}
FileStream::Context::~Context() {
}
-int64 FileStream::Context::GetFileSize() const {
- struct stat info;
- if (fstat(file_, &info) != 0) {
- IOResult result = IOResult::FromOSError(errno);
- RecordError(result, FILE_ERROR_SOURCE_GET_SIZE);
- return result.result;
- }
-
- return static_cast<int64>(info.st_size);
-}
-
int FileStream::Context::ReadAsync(IOBuffer* in_buf,
int buf_len,
const CompletionCallback& callback) {
@@ -90,23 +72,15 @@ int FileStream::Context::ReadAsync(IOBuffer* in_buf,
task_runner_.get(),
FROM_HERE,
base::Bind(&Context::ReadFileImpl, base::Unretained(this), buf, buf_len),
- base::Bind(&Context::ProcessAsyncResult,
+ base::Bind(&Context::OnAsyncCompleted,
base::Unretained(this),
- IntToInt64(callback),
- FILE_ERROR_SOURCE_READ));
+ IntToInt64(callback)));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
-int FileStream::Context::ReadSync(char* in_buf, int buf_len) {
- scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
- IOResult result = ReadFileImpl(buf, buf_len);
- RecordError(result, FILE_ERROR_SOURCE_READ);
- return result.result;
-}
-
int FileStream::Context::WriteAsync(IOBuffer* in_buf,
int buf_len,
const CompletionCallback& callback) {
@@ -117,36 +91,18 @@ int FileStream::Context::WriteAsync(IOBuffer* in_buf,
task_runner_.get(),
FROM_HERE,
base::Bind(&Context::WriteFileImpl, base::Unretained(this), buf, buf_len),
- base::Bind(&Context::ProcessAsyncResult,
+ base::Bind(&Context::OnAsyncCompleted,
base::Unretained(this),
- IntToInt64(callback),
- FILE_ERROR_SOURCE_WRITE));
+ IntToInt64(callback)));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
-int FileStream::Context::WriteSync(const char* in_buf, int buf_len) {
- scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
- IOResult result = WriteFileImpl(buf, buf_len);
- RecordError(result, FILE_ERROR_SOURCE_WRITE);
- return result.result;
-}
-
-int FileStream::Context::Truncate(int64 bytes) {
- if (ftruncate(file_, bytes) != 0) {
- IOResult result = IOResult::FromOSError(errno);
- RecordError(result, FILE_ERROR_SOURCE_SET_EOF);
- return result.result;
- }
-
- return bytes;
-}
-
FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
int64 offset) {
- off_t res = lseek(file_, static_cast<off_t>(offset),
+ off_t res = lseek(file_.GetPlatformFile(), static_cast<off_t>(offset),
static_cast<int>(whence));
if (res == static_cast<off_t>(-1))
return IOResult::FromOSError(errno);
@@ -155,7 +111,7 @@ FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
}
FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
- ssize_t res = HANDLE_EINTR(fsync(file_));
+ ssize_t res = HANDLE_EINTR(fsync(file_.GetPlatformFile()));
if (res == -1)
return IOResult::FromOSError(errno);
@@ -166,7 +122,7 @@ FileStream::Context::IOResult FileStream::Context::ReadFileImpl(
scoped_refptr<IOBuffer> buf,
int buf_len) {
// Loop in the case of getting interrupted by a signal.
- ssize_t res = HANDLE_EINTR(read(file_, buf->data(),
+ ssize_t res = HANDLE_EINTR(read(file_.GetPlatformFile(), buf->data(),
static_cast<size_t>(buf_len)));
if (res == -1)
return IOResult::FromOSError(errno);
@@ -177,20 +133,12 @@ FileStream::Context::IOResult FileStream::Context::ReadFileImpl(
FileStream::Context::IOResult FileStream::Context::WriteFileImpl(
scoped_refptr<IOBuffer> buf,
int buf_len) {
- ssize_t res = HANDLE_EINTR(write(file_, buf->data(), buf_len));
+ ssize_t res = HANDLE_EINTR(write(file_.GetPlatformFile(), buf->data(),
+ buf_len));
if (res == -1)
return IOResult::FromOSError(errno);
return IOResult(res, 0);
}
-FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
- bool success = base::ClosePlatformFile(file_);
- file_ = base::kInvalidPlatformFileValue;
- if (!success)
- return IOResult::FromOSError(errno);
-
- return IOResult(OK, 0);
-}
-
} // namespace net
diff --git a/chromium/net/base/file_stream_context_win.cc b/chromium/net/base/file_stream_context_win.cc
index 4a666c70a5b..e906b6bb480 100644
--- a/chromium/net/base/file_stream_context_win.cc
+++ b/chromium/net/base/file_stream_context_win.cc
@@ -9,7 +9,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/task_runner_util.h"
+#include "base/task_runner.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -37,36 +37,26 @@ void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
} // namespace
-FileStream::Context::Context(const BoundNetLog& bound_net_log,
- const scoped_refptr<base::TaskRunner>& task_runner)
+FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner)
: io_context_(),
- file_(base::kInvalidPlatformFileValue),
- record_uma_(false),
async_in_progress_(false),
orphaned_(false),
- bound_net_log_(bound_net_log),
- error_source_(FILE_ERROR_SOURCE_COUNT),
task_runner_(task_runner) {
io_context_.handler = this;
memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
}
-FileStream::Context::Context(base::PlatformFile file,
- const BoundNetLog& bound_net_log,
- int open_flags,
+FileStream::Context::Context(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner)
: io_context_(),
- file_(file),
- record_uma_(false),
+ file_(file.Pass()),
async_in_progress_(false),
orphaned_(false),
- bound_net_log_(bound_net_log),
- error_source_(FILE_ERROR_SOURCE_COUNT),
task_runner_(task_runner) {
io_context_.handler = this;
memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
- if (file_ != base::kInvalidPlatformFileValue &&
- (open_flags & base::PLATFORM_FILE_ASYNC)) {
+ if (file_.IsValid()) {
+ // TODO(hashimoto): Check that file_ is async.
OnAsyncFileOpened();
}
}
@@ -74,26 +64,13 @@ FileStream::Context::Context(base::PlatformFile file,
FileStream::Context::~Context() {
}
-int64 FileStream::Context::GetFileSize() const {
- LARGE_INTEGER file_size;
- if (!GetFileSizeEx(file_, &file_size)) {
- IOResult error = IOResult::FromOSError(GetLastError());
- LOG(WARNING) << "GetFileSizeEx failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_GET_SIZE);
- return error.result;
- }
-
- return file_size.QuadPart;
-}
-
int FileStream::Context::ReadAsync(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
- error_source_ = FILE_ERROR_SOURCE_READ;
DWORD bytes_read;
- if (!ReadFile(file_, buf->data(), buf_len,
+ if (!ReadFile(file_.GetPlatformFile(), buf->data(), buf_len,
&bytes_read, &io_context_.overlapped)) {
IOResult error = IOResult::FromOSError(GetLastError());
if (error.os_error == ERROR_IO_PENDING) {
@@ -102,7 +79,6 @@ int FileStream::Context::ReadAsync(IOBuffer* buf,
return 0; // Report EOF by returning 0 bytes read.
} else {
LOG(WARNING) << "ReadFile failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_READ);
}
return error.result;
}
@@ -111,36 +87,17 @@ int FileStream::Context::ReadAsync(IOBuffer* buf,
return ERR_IO_PENDING;
}
-int FileStream::Context::ReadSync(char* buf, int buf_len) {
- DWORD bytes_read;
- if (!ReadFile(file_, buf, buf_len, &bytes_read, NULL)) {
- IOResult error = IOResult::FromOSError(GetLastError());
- if (error.os_error == ERROR_HANDLE_EOF) {
- return 0; // Report EOF by returning 0 bytes read.
- } else {
- LOG(WARNING) << "ReadFile failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_READ);
- }
- return error.result;
- }
-
- return bytes_read;
-}
-
int FileStream::Context::WriteAsync(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
- error_source_ = FILE_ERROR_SOURCE_WRITE;
-
DWORD bytes_written = 0;
- if (!WriteFile(file_, buf->data(), buf_len,
+ if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len,
&bytes_written, &io_context_.overlapped)) {
IOResult error = IOResult::FromOSError(GetLastError());
if (error.os_error == ERROR_IO_PENDING) {
IOCompletionIsPending(callback, buf);
} else {
LOG(WARNING) << "WriteFile failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_WRITE);
}
return error.result;
}
@@ -149,31 +106,9 @@ int FileStream::Context::WriteAsync(IOBuffer* buf,
return ERR_IO_PENDING;
}
-int FileStream::Context::WriteSync(const char* buf, int buf_len) {
- DWORD bytes_written = 0;
- if (!WriteFile(file_, buf, buf_len, &bytes_written, NULL)) {
- IOResult error = IOResult::FromOSError(GetLastError());
- LOG(WARNING) << "WriteFile failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_WRITE);
- return error.result;
- }
-
- return bytes_written;
-}
-
-int FileStream::Context::Truncate(int64 bytes) {
- if (!SetEndOfFile(file_)) {
- IOResult error = IOResult::FromOSError(GetLastError());
- LOG(WARNING) << "SetEndOfFile failed: " << error.os_error;
- RecordError(error, FILE_ERROR_SOURCE_SET_EOF);
- return error.result;
- }
-
- return bytes;
-}
-
void FileStream::Context::OnAsyncFileOpened() {
- base::MessageLoopForIO::current()->RegisterIOHandler(file_, this);
+ base::MessageLoopForIO::current()->RegisterIOHandler(file_.GetPlatformFile(),
+ this);
}
FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
@@ -181,7 +116,7 @@ FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
LARGE_INTEGER distance, res;
distance.QuadPart = offset;
DWORD move_method = static_cast<DWORD>(whence);
- if (SetFilePointerEx(file_, distance, &res, move_method)) {
+ if (SetFilePointerEx(file_.GetPlatformFile(), distance, &res, move_method)) {
SetOffset(&io_context_.overlapped, res);
return IOResult(res.QuadPart, 0);
}
@@ -190,16 +125,7 @@ FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
}
FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
- if (FlushFileBuffers(file_))
- return IOResult(OK, 0);
-
- return IOResult::FromOSError(GetLastError());
-}
-
-FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
- bool success = base::ClosePlatformFile(file_);
- file_ = base::kInvalidPlatformFileValue;
- if (success)
+ if (FlushFileBuffers(file_.GetPlatformFile()))
return IOResult(OK, 0);
return IOResult::FromOSError(GetLastError());
@@ -235,7 +161,6 @@ void FileStream::Context::OnIOCompleted(
result = 0;
} else if (error) {
IOResult error_result = IOResult::FromOSError(error);
- RecordError(error_result, error_source_);
result = error_result.result;
} else {
result = bytes_read;
diff --git a/chromium/net/base/file_stream_metrics.cc b/chromium/net/base/file_stream_metrics.cc
deleted file mode 100644
index bff7174a40b..00000000000
--- a/chromium/net/base/file_stream_metrics.cc
+++ /dev/null
@@ -1,103 +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/base/file_stream_metrics.h"
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-
-namespace net {
-
-namespace {
-
-const char* FileErrorSourceStrings[] = {
- "OPEN",
- "WRITE",
- "READ",
- "SEEK",
- "FLUSH",
- "SET_EOF",
- "GET_SIZE",
- "CLOSE"
-};
-
-COMPILE_ASSERT(ARRAYSIZE_UNSAFE(FileErrorSourceStrings) ==
- FILE_ERROR_SOURCE_COUNT,
- file_error_source_enum_has_changed);
-
-void RecordFileErrorTypeCount(FileErrorSource source) {
- UMA_HISTOGRAM_ENUMERATION(
- "Net.FileErrorType_Counts", source, FILE_ERROR_SOURCE_COUNT);
-}
-
-} // namespace
-
-void RecordFileError(int error, FileErrorSource source, bool record) {
- if (!record)
- return;
-
- RecordFileErrorTypeCount(source);
-
- int bucket = GetFileErrorUmaBucket(error);
-
- // Fixed values per platform.
- static const int max_bucket = MaxFileErrorUmaBucket();
- static const int max_error = MaxFileErrorUmaValue();
-
- switch(source) {
- case FILE_ERROR_SOURCE_OPEN:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Open", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Open", bucket, max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_WRITE:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Write", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Write", bucket, max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_READ:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Read", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Read", bucket, max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_SEEK:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Seek", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Seek", bucket, max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_FLUSH:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Flush", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Flush", bucket, max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_SET_EOF:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_SetEof", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_SetEof", bucket,
- max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_GET_SIZE:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_GetSize", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_GetSize", bucket,
- max_bucket);
- break;
-
- case FILE_ERROR_SOURCE_CLOSE:
- UMA_HISTOGRAM_ENUMERATION("Net.FileError_Close", error, max_error);
- UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Close", bucket,
- max_bucket);
- break;
-
- default:
- break;
- }
-}
-
-const char* GetFileErrorSourceName(FileErrorSource source) {
- DCHECK_NE(FILE_ERROR_SOURCE_COUNT, source);
- return FileErrorSourceStrings[source];
-}
-
-} // namespace net
diff --git a/chromium/net/base/file_stream_metrics.h b/chromium/net/base/file_stream_metrics.h
deleted file mode 100644
index 4dab3ddc64b..00000000000
--- a/chromium/net/base/file_stream_metrics.h
+++ /dev/null
@@ -1,44 +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.
-
-// File error statistics gathering.
-
-#ifndef NET_BASE_FILE_STREAM_METRICS_H_
-#define NET_BASE_FILE_STREAM_METRICS_H_
-
-namespace net {
-
-enum FileErrorSource {
- FILE_ERROR_SOURCE_OPEN = 0,
- FILE_ERROR_SOURCE_WRITE,
- FILE_ERROR_SOURCE_READ,
- FILE_ERROR_SOURCE_SEEK,
- FILE_ERROR_SOURCE_FLUSH,
- FILE_ERROR_SOURCE_SET_EOF,
- FILE_ERROR_SOURCE_GET_SIZE,
- FILE_ERROR_SOURCE_CLOSE,
- FILE_ERROR_SOURCE_COUNT,
-};
-
-// UMA error statistics gathering.
-// Put the error value into a bucket.
-int GetFileErrorUmaBucket(int error);
-
-// The largest bucket number, plus 1.
-int MaxFileErrorUmaBucket();
-
-// The highest error value we want to individually report.
-int MaxFileErrorUmaValue();
-
-// |error| is a platform-specific error (Windows or Posix).
-// |source| indicates the operation that resulted in the error.
-// |record| is a flag indicating that we are interested in this error.
-void RecordFileError(int error, FileErrorSource source, bool record);
-
-// Gets a description for the source of a file error.
-const char* GetFileErrorSourceName(FileErrorSource source);
-
-} // namespace net
-
-#endif // NET_BASE_FILE_STREAM_METRICS_H_
diff --git a/chromium/net/base/file_stream_metrics_posix.cc b/chromium/net/base/file_stream_metrics_posix.cc
deleted file mode 100644
index 7407d50c6e7..00000000000
--- a/chromium/net/base/file_stream_metrics_posix.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2011 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/base/file_stream_metrics.h"
-
-namespace net {
-
-int GetFileErrorUmaBucket(int error) {
- return 1;
-}
-
-int MaxFileErrorUmaBucket() {
- return 2;
-}
-
-int MaxFileErrorUmaValue() {
- return 160;
-}
-
-} // namespace net
diff --git a/chromium/net/base/file_stream_metrics_win.cc b/chromium/net/base/file_stream_metrics_win.cc
deleted file mode 100644
index c397e4b5ee9..00000000000
--- a/chromium/net/base/file_stream_metrics_win.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2011 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/base/file_stream_metrics.h"
-
-#include <windows.h>
-
-#include "base/basictypes.h"
-
-namespace net {
-
-namespace {
-
-struct Range {
- int low;
- int high;
-};
-
-// The error range list is extracted from WinError.h.
-//
-// NOTE: The gaps between the ranges need to be recorded too.
-// They will have odd-numbered buckets.
-const Range kErrorRangeList[] = {
- { 0, 321 }, // 2.
- { 335, 371 }, // 4.
- { 383, 387 }, // 6.
- { 399, 404 }, // etc.
- { 415, 418 },
- { 431, 433 },
- { 447, 868 },
- { 994, 1471 },
- { 1500, 1513 },
- { 1536, 1553 },
- { 1601, 1654 },
- { 1700, 1834 },
- { 1898, 1938 },
- { 2000, 2024 },
- { 2048, 2085 },
- { 2108, 2110 },
- { 2202, 2203 },
- { 2250, 2251 },
- { 2401, 2405 },
- { 3000, 3021 },
- { 3950, 3951 },
- { 4000, 4007 },
- { 4050, 4066 },
- { 4096, 4116 },
- { 4200, 4215 },
- { 4300, 4353 },
- { 4390, 4395 },
- { 4500, 4501 },
- { 4864, 4905 },
- { 5001, 5090 },
- { 5890, 5953 },
- { 6000, 6023 },
- { 6118, 6119 },
- { 6200, 6201 },
- { 6600, 6649 },
- { 6700, 6732 },
- { 6800, 6856 },
- { 7001, 7071 },
- { 8001, 8018 },
- { 8192, 8263 },
- { 8301, 8640 },
- { 8704, 8705 },
- { 8960, 9053 },
- { 9216, 9218 },
- { 9263, 9276 },
- { 9472, 9506 },
- { 9550, 9573 },
- { 9600, 9622 },
- { 9650, 9656 },
- { 9688, 9723 },
- { 9750, 9754 },
- { 9800, 9802 },
- { 9850, 9853 },
- { 9900, 9907 },
- { 10000, 10072 },
- { 10091, 10113 },
- { 11001, 11034 },
- { 12288, 12335 },
- { 12544, 12559 },
- { 12595, 12597 },
- { 12801, 12803 },
- { 13000, 13026 },
- { 13800, 13933 },
- { 14000, 14111 },
- { 15000, 15039 },
- { 15080, 15086 },
- { 15100, 15109 },
- { 15200, 15208 },
- { 15250, 15251 },
- { 15299, 15302 },
- { 16385, 16436 },
- { 18432, 18454 },
- { 20480, 20486 },
- { 24577, 24607 },
- { 28673, 28698 },
- { 32790, 32816 },
- { 33281, 33322 },
- { 35005, 35024 },
- { 36000, 36004 },
- { 40010, 40011 },
- { 40067, 40069 },
- { 53248, 53293 },
- { 53376, 53382 },
- { 57344, 57360 },
- { 57377, 57394 },
- { 65535, 65536 } // 2 * kNumErrorRanges.
-};
-const size_t kNumErrorRanges = ARRAYSIZE_UNSAFE(kErrorRangeList);
-
-} // namespace
-
-// Windows has very many errors. We're not interested in most of them, but we
-// don't know which ones are significant.
-// This function maps error ranges to specific buckets.
-// If we get hits on the buckets, we can add those values to the values we
-// record individually.
-// If we get values *between* the buckets, we record those as buckets too.
-int GetFileErrorUmaBucket(int error) {
- error = HRESULT_CODE(error);
-
- // This is a linear search, but of a short fixed-size array.
- // It also gets called infrequently, on errors.
- for (size_t n = 0; n < kNumErrorRanges; ++n) {
- if (error < kErrorRangeList[n].low)
- return (2 * (n + 1)) - 1; // In gap before the range.
- if (error <= kErrorRangeList[n].high)
- return 2 * (n + 1); // In the range.
- }
-
- // After the last bucket.
- return 2 * kNumErrorRanges + 1;
-}
-
-int MaxFileErrorUmaBucket() {
- return 2 * kNumErrorRanges + 2;
-}
-
-int MaxFileErrorUmaValue() {
- return kErrorRangeList[0].high + 1;
-}
-
-} // namespace net
diff --git a/chromium/net/base/file_stream_net_log_parameters.cc b/chromium/net/base/file_stream_net_log_parameters.cc
deleted file mode 100644
index e85b62592b8..00000000000
--- a/chromium/net/base/file_stream_net_log_parameters.cc
+++ /dev/null
@@ -1,25 +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/base/file_stream_net_log_parameters.h"
-
-#include "base/values.h"
-
-namespace net {
-
-base::Value* NetLogFileStreamErrorCallback(
- FileErrorSource source,
- int os_error,
- net::Error net_error,
- NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue();
-
- dict->SetString("operation", GetFileErrorSourceName(source));
- dict->SetInteger("os_error", os_error);
- dict->SetInteger("net_error", net_error);
-
- return dict;
-}
-
-} // namespace net
diff --git a/chromium/net/base/file_stream_net_log_parameters.h b/chromium/net/base/file_stream_net_log_parameters.h
deleted file mode 100644
index 09f96c8750e..00000000000
--- a/chromium/net/base/file_stream_net_log_parameters.h
+++ /dev/null
@@ -1,27 +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.
-
-// File stream error reporting.
-
-#ifndef NET_BASE_FILE_STREAM_NET_LOG_PARAMETERS_H_
-#define NET_BASE_FILE_STREAM_NET_LOG_PARAMETERS_H_
-
-#include <string>
-
-#include "net/base/file_stream_metrics.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-
-namespace net {
-
-// Creates NetLog parameters when a FileStream has an error.
-base::Value* NetLogFileStreamErrorCallback(
- FileErrorSource source,
- int os_error,
- net::Error net_error,
- NetLog::LogLevel log_level);
-
-} // namespace net
-
-#endif // NET_BASE_FILE_STREAM_NET_LOG_PARAMETERS_H_
diff --git a/chromium/net/base/file_stream_unittest.cc b/chromium/net/base/file_stream_unittest.cc
index b055c78b5d1..292b0d207cf 100644
--- a/chromium/net/base/file_stream_unittest.cc
+++ b/chromium/net/base/file_stream_unittest.cc
@@ -7,13 +7,15 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/path_service.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread_restrictions.h"
#include "net/base/capturing_net_log.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -47,14 +49,14 @@ class FileStreamTest : public PlatformTest {
PlatformTest::SetUp();
base::CreateTemporaryFile(&temp_file_path_);
- file_util::WriteFile(temp_file_path_, kTestData, kTestDataSize);
+ base::WriteFile(temp_file_path_, kTestData, kTestDataSize);
}
virtual void TearDown() {
- EXPECT_TRUE(base::DeleteFile(temp_file_path_, false));
-
// FileStreamContexts must be asynchronously closed on the file task runner
// before they can be deleted. Pump the RunLoop to avoid leaks.
base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(base::DeleteFile(temp_file_path_, false));
+
PlatformTest::TearDown();
}
@@ -66,210 +68,123 @@ class FileStreamTest : public PlatformTest {
namespace {
-TEST_F(FileStreamTest, BasicOpenClose) {
- base::PlatformFile file = base::kInvalidPlatformFileValue;
- {
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int rv = stream.OpenSync(temp_file_path(),
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
- EXPECT_EQ(OK, rv);
- EXPECT_TRUE(stream.IsOpen());
- file = stream.GetPlatformFileForTesting();
- }
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
- base::PlatformFileInfo info;
- // The file should be closed.
- EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
-}
-
-TEST_F(FileStreamTest, BasicOpenExplicitClose) {
- base::PlatformFile file = base::kInvalidPlatformFileValue;
- FileStream stream(NULL);
- int rv = stream.OpenSync(temp_file_path(),
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
- EXPECT_EQ(OK, rv);
- EXPECT_TRUE(stream.IsOpen());
- file = stream.GetPlatformFileForTesting();
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
- EXPECT_EQ(OK, stream.CloseSync());
- EXPECT_FALSE(stream.IsOpen());
- base::PlatformFileInfo info;
- // The file should be closed.
- EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
-}
-
TEST_F(FileStreamTest, AsyncOpenExplicitClose) {
- base::PlatformFile file = base::kInvalidPlatformFileValue;
TestCompletionCallback callback;
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN |
+ base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_TRUE(stream.IsOpen());
- file = stream.GetPlatformFileForTesting();
+ EXPECT_TRUE(stream.GetFileForTesting().IsValid());
EXPECT_EQ(ERR_IO_PENDING, stream.Close(callback.callback()));
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_FALSE(stream.IsOpen());
- base::PlatformFileInfo info;
- // The file should be closed.
- EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
+ EXPECT_FALSE(stream.GetFileForTesting().IsValid());
}
TEST_F(FileStreamTest, AsyncOpenExplicitCloseOrphaned) {
- base::PlatformFile file = base::kInvalidPlatformFileValue;
TestCompletionCallback callback;
- base::PlatformFileInfo info;
scoped_ptr<FileStream> stream(new FileStream(
- NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
int rv = stream->Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_TRUE(stream->IsOpen());
- file = stream->GetPlatformFileForTesting();
+ EXPECT_TRUE(stream->GetFileForTesting().IsValid());
EXPECT_EQ(ERR_IO_PENDING, stream->Close(callback.callback()));
stream.reset();
// File isn't actually closed yet.
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
base::RunLoop runloop;
runloop.RunUntilIdle();
// The file should now be closed, though the callback has not been called.
- EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
-}
-
-TEST_F(FileStreamTest, FileHandleNotLeftOpen) {
- bool created = false;
- ASSERT_EQ(kTestDataSize,
- file_util::WriteFile(temp_file_path(), kTestData, kTestDataSize));
- int flags = base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ;
- base::PlatformFile file = base::CreatePlatformFile(
- temp_file_path(), flags, &created, NULL);
-
- {
- // Seek to the beginning of the file and read.
- FileStream read_stream(file, flags, NULL,
- base::MessageLoopProxy::current());
- EXPECT_TRUE(read_stream.IsOpen());
- }
-
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
- base::PlatformFileInfo info;
- // The file should be closed.
- EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
}
// Test the use of FileStream with a file handle provided at construction.
TEST_F(FileStreamTest, UseFileHandle) {
- bool created = false;
-
+ int rv = 0;
+ TestCompletionCallback callback;
+ TestInt64CompletionCallback callback64;
// 1. Test reading with a file handle.
ASSERT_EQ(kTestDataSize,
- file_util::WriteFile(temp_file_path(), kTestData, kTestDataSize));
- int flags = base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ;
- base::PlatformFile file = base::CreatePlatformFile(
- temp_file_path(), flags, &created, NULL);
+ base::WriteFile(temp_file_path(), kTestData, kTestDataSize));
+ int flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
+ base::File file(temp_file_path(), flags);
// Seek to the beginning of the file and read.
scoped_ptr<FileStream> read_stream(
- new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
- ASSERT_EQ(0, read_stream->SeekSync(FROM_BEGIN, 0));
- ASSERT_EQ(kTestDataSize, read_stream->Available());
+ new FileStream(file.Pass(), base::MessageLoopProxy::current()));
+ ASSERT_EQ(ERR_IO_PENDING,
+ read_stream->Seek(FROM_BEGIN, 0, callback64.callback()));
+ ASSERT_EQ(0, callback64.WaitForResult());
// Read into buffer and compare.
- char buffer[kTestDataSize];
- ASSERT_EQ(kTestDataSize,
- read_stream->ReadSync(buffer, kTestDataSize));
- ASSERT_EQ(0, memcmp(kTestData, buffer, kTestDataSize));
+ scoped_refptr<IOBufferWithSize> read_buffer =
+ new IOBufferWithSize(kTestDataSize);
+ rv = read_stream->Read(read_buffer.get(), kTestDataSize, callback.callback());
+ ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
+ ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
read_stream.reset();
// 2. Test writing with a file handle.
base::DeleteFile(temp_file_path(), false);
- flags = base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE;
- file = base::CreatePlatformFile(temp_file_path(), flags, &created, NULL);
+ flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
+ file.Initialize(temp_file_path(), flags);
scoped_ptr<FileStream> write_stream(
- new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
- ASSERT_EQ(0, write_stream->SeekSync(FROM_BEGIN, 0));
- ASSERT_EQ(kTestDataSize,
- write_stream->WriteSync(kTestData, kTestDataSize));
+ new FileStream(file.Pass(), base::MessageLoopProxy::current()));
+ ASSERT_EQ(ERR_IO_PENDING,
+ write_stream->Seek(FROM_BEGIN, 0, callback64.callback()));
+ ASSERT_EQ(0, callback64.WaitForResult());
+ scoped_refptr<IOBufferWithSize> write_buffer = CreateTestDataBuffer();
+ rv = write_stream->Write(write_buffer.get(), kTestDataSize,
+ callback.callback());
+ ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
write_stream.reset();
// Read into buffer and compare to make sure the handle worked fine.
ASSERT_EQ(kTestDataSize,
- base::ReadFile(temp_file_path(), buffer, kTestDataSize));
- ASSERT_EQ(0, memcmp(kTestData, buffer, kTestDataSize));
+ base::ReadFile(temp_file_path(), read_buffer->data(),
+ kTestDataSize));
+ ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
}
TEST_F(FileStreamTest, UseClosedStream) {
- FileStream stream(NULL, base::MessageLoopProxy::current());
+ int rv = 0;
+ TestCompletionCallback callback;
+ TestInt64CompletionCallback callback64;
+
+ FileStream stream(base::MessageLoopProxy::current());
EXPECT_FALSE(stream.IsOpen());
// Try seeking...
- int64 new_offset = stream.SeekSync(FROM_BEGIN, 5);
- EXPECT_EQ(ERR_UNEXPECTED, new_offset);
-
- // Try available...
- int64 avail = stream.Available();
- EXPECT_EQ(ERR_UNEXPECTED, avail);
+ rv = stream.Seek(FROM_BEGIN, 5, callback64.callback());
+ EXPECT_EQ(ERR_UNEXPECTED, callback64.GetResult(rv));
// Try reading...
- char buf[10];
- int rv = stream.ReadSync(buf, arraysize(buf));
- EXPECT_EQ(ERR_UNEXPECTED, rv);
-}
-
-TEST_F(FileStreamTest, BasicRead) {
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ;
- int rv = stream.OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- int total_bytes_read = 0;
-
- std::string data_read;
- for (;;) {
- char buf[4];
- rv = stream.ReadSync(buf, arraysize(buf));
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf, rv);
- }
- EXPECT_EQ(file_size, total_bytes_read);
- EXPECT_EQ(kTestData, data_read);
+ scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(10);
+ rv = stream.Read(buf, buf->size(), callback.callback());
+ EXPECT_EQ(ERR_UNEXPECTED, callback.GetResult(rv));
}
TEST_F(FileStreamTest, AsyncRead) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
int total_bytes_read = 0;
std::string data_read;
@@ -290,22 +205,17 @@ TEST_F(FileStreamTest, AsyncRead) {
TEST_F(FileStreamTest, AsyncRead_EarlyDelete) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream->Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
rv = stream->Read(buf.get(), buf->size(), callback.callback());
stream.reset(); // Delete instead of closing it.
@@ -319,50 +229,13 @@ TEST_F(FileStreamTest, AsyncRead_EarlyDelete) {
}
}
-TEST_F(FileStreamTest, BasicRead_FromOffset) {
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ;
- int rv = stream.OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- const int64 kOffset = 3;
- int64 new_offset = stream.SeekSync(FROM_BEGIN, kOffset);
- EXPECT_EQ(kOffset, new_offset);
-
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size - kOffset, total_bytes_avail);
-
- int64 total_bytes_read = 0;
-
- std::string data_read;
- for (;;) {
- char buf[4];
- rv = stream.ReadSync(buf, arraysize(buf));
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf, rv);
- }
- EXPECT_EQ(file_size - kOffset, total_bytes_read);
- EXPECT_TRUE(data_read == kTestData + kOffset);
- EXPECT_EQ(kTestData + kOffset, data_read);
-}
-
TEST_F(FileStreamTest, AsyncRead_FromOffset) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -375,9 +248,6 @@ TEST_F(FileStreamTest, AsyncRead_FromOffset) {
int64 new_offset = callback64.WaitForResult();
EXPECT_EQ(kOffset, new_offset);
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size - kOffset, total_bytes_avail);
-
int total_bytes_read = 0;
std::string data_read;
@@ -396,34 +266,10 @@ TEST_F(FileStreamTest, AsyncRead_FromOffset) {
EXPECT_EQ(kTestData + kOffset, data_read);
}
-TEST_F(FileStreamTest, SeekAround) {
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ;
- int rv = stream.OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- const int64 kOffset = 3;
- int64 new_offset = stream.SeekSync(FROM_BEGIN, kOffset);
- EXPECT_EQ(kOffset, new_offset);
-
- new_offset = stream.SeekSync(FROM_CURRENT, kOffset);
- EXPECT_EQ(2 * kOffset, new_offset);
-
- new_offset = stream.SeekSync(FROM_CURRENT, -kOffset);
- EXPECT_EQ(kOffset, new_offset);
-
- const int kTestDataLen = arraysize(kTestData) - 1;
-
- new_offset = stream.SeekSync(FROM_END, -kTestDataLen);
- EXPECT_EQ(0, new_offset);
-}
-
TEST_F(FileStreamTest, AsyncSeekAround) {
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_ASYNC |
- base::PLATFORM_FILE_READ;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_ASYNC |
+ base::File::FLAG_READ;
TestCompletionCallback callback;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -455,41 +301,17 @@ TEST_F(FileStreamTest, AsyncSeekAround) {
EXPECT_EQ(0, new_offset);
}
-TEST_F(FileStreamTest, BasicWrite) {
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
- base::PLATFORM_FILE_WRITE;
- int rv = stream->OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(0, file_size);
-
- rv = stream->WriteSync(kTestData, kTestDataSize);
- EXPECT_EQ(kTestDataSize, rv);
- stream.reset();
-
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(kTestDataSize, file_size);
-}
-
TEST_F(FileStreamTest, AsyncWrite) {
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(0, file_size);
int total_bytes_written = 0;
@@ -498,8 +320,8 @@ TEST_F(FileStreamTest, AsyncWrite) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf.get(), buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream.Write(
- drainable.get(), drainable->BytesRemaining(), callback.callback());
+ rv = stream.Write(drainable.get(), drainable->BytesRemaining(),
+ callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
@@ -508,25 +330,22 @@ TEST_F(FileStreamTest, AsyncWrite) {
drainable->DidConsume(rv);
total_bytes_written += rv;
}
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(file_size, total_bytes_written);
}
TEST_F(FileStreamTest, AsyncWrite_EarlyDelete) {
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream->Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(0, file_size);
scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
@@ -538,47 +357,18 @@ TEST_F(FileStreamTest, AsyncWrite_EarlyDelete) {
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(callback.have_result());
} else {
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(file_size, rv);
}
}
-TEST_F(FileStreamTest, BasicWrite_FromOffset) {
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE;
- int rv = stream->OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(kTestDataSize, file_size);
-
- const int64 kOffset = 0;
- int64 new_offset = stream->SeekSync(FROM_END, kOffset);
- EXPECT_EQ(kTestDataSize, new_offset);
-
- rv = stream->WriteSync(kTestData, kTestDataSize);
- EXPECT_EQ(kTestDataSize, rv);
- stream.reset();
-
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(kTestDataSize * 2, file_size);
-}
-
TEST_F(FileStreamTest, AsyncWrite_FromOffset) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream.Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -597,8 +387,8 @@ TEST_F(FileStreamTest, AsyncWrite_FromOffset) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf.get(), buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream.Write(
- drainable.get(), drainable->BytesRemaining(), callback.callback());
+ rv = stream.Write(drainable.get(), drainable->BytesRemaining(),
+ callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
@@ -607,119 +397,23 @@ TEST_F(FileStreamTest, AsyncWrite_FromOffset) {
drainable->DidConsume(rv);
total_bytes_written += rv;
}
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(file_size, kTestDataSize * 2);
}
-TEST_F(FileStreamTest, BasicReadWrite) {
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE;
- int rv = stream->OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- int total_bytes_read = 0;
-
- std::string data_read;
- for (;;) {
- char buf[4];
- rv = stream->ReadSync(buf, arraysize(buf));
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf, rv);
- }
- EXPECT_EQ(file_size, total_bytes_read);
- EXPECT_TRUE(data_read == kTestData);
-
- rv = stream->WriteSync(kTestData, kTestDataSize);
- EXPECT_EQ(kTestDataSize, rv);
- stream.reset();
-
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(kTestDataSize * 2, file_size);
-}
-
-TEST_F(FileStreamTest, BasicWriteRead) {
- int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE;
- int rv = stream->OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- int64 offset = stream->SeekSync(FROM_END, 0);
- EXPECT_EQ(offset, file_size);
-
- rv = stream->WriteSync(kTestData, kTestDataSize);
- EXPECT_EQ(kTestDataSize, rv);
-
- offset = stream->SeekSync(FROM_BEGIN, 0);
- EXPECT_EQ(0, offset);
-
- int64 total_bytes_read = 0;
-
- std::string data_read;
- for (;;) {
- char buf[4];
- rv = stream->ReadSync(buf, arraysize(buf));
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf, rv);
- }
- stream.reset();
-
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
- EXPECT_EQ(kTestDataSize * 2, file_size);
- EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
-
- const std::string kExpectedFileData =
- std::string(kTestData) + std::string(kTestData);
- EXPECT_EQ(kExpectedFileData, data_read);
-}
-
TEST_F(FileStreamTest, BasicAsyncReadWrite) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream->Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
int64 total_bytes_read = 0;
std::string data_read;
@@ -743,8 +437,8 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf.get(), buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream->Write(
- drainable.get(), drainable->BytesRemaining(), callback.callback());
+ rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
+ callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
@@ -756,30 +450,23 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
stream.reset();
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(kTestDataSize * 2, file_size);
}
TEST_F(FileStreamTest, BasicAsyncWriteRead) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream->Open(temp_file_path(), flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
TestInt64CompletionCallback callback64;
rv = stream->Seek(FROM_END, 0, callback64.callback());
ASSERT_EQ(ERR_IO_PENDING, rv);
@@ -792,8 +479,8 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf.get(), buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream->Write(
- drainable.get(), drainable->BytesRemaining(), callback.callback());
+ rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
+ callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
@@ -826,8 +513,7 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
}
stream.reset();
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(kTestDataSize * 2, file_size);
EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
@@ -889,7 +575,14 @@ class TestWriteReadCompletionCallback {
*total_bytes_read_ += total_bytes_read;
*data_read_ += data_read;
} else { // We're done writing all data. Start reading the data.
- stream_->SeekSync(FROM_BEGIN, 0);
+ TestInt64CompletionCallback callback64;
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->Seek(FROM_BEGIN, 0, callback64.callback()));
+ {
+ base::MessageLoop::ScopedNestableTaskAllower allow(
+ base::MessageLoop::current());
+ EXPECT_LE(0, callback64.WaitForResult());
+ }
TestCompletionCallback callback;
for (;;) {
@@ -930,25 +623,20 @@ class TestWriteReadCompletionCallback {
TEST_F(FileStreamTest, AsyncWriteRead) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
TestCompletionCallback open_callback;
int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, open_callback.WaitForResult());
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- int64 offset = stream->SeekSync(FROM_END, 0);
- EXPECT_EQ(offset, file_size);
+ TestInt64CompletionCallback callback64;
+ EXPECT_EQ(ERR_IO_PENDING, stream->Seek(FROM_END, 0, callback64.callback()));
+ EXPECT_EQ(file_size, callback64.WaitForResult());
int total_bytes_written = 0;
int total_bytes_read = 0;
@@ -965,8 +653,7 @@ TEST_F(FileStreamTest, AsyncWriteRead) {
stream.reset();
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(kTestDataSize * 2, file_size);
EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
@@ -1040,25 +727,20 @@ class TestWriteCloseCompletionCallback {
TEST_F(FileStreamTest, AsyncWriteClose) {
int64 file_size;
- bool ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ new FileStream(base::MessageLoopProxy::current()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
TestCompletionCallback open_callback;
int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, open_callback.WaitForResult());
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- int64 offset = stream->SeekSync(FROM_END, 0);
- EXPECT_EQ(offset, file_size);
+ TestInt64CompletionCallback callback64;
+ EXPECT_EQ(ERR_IO_PENDING, stream->Seek(FROM_END, 0, callback64.callback()));
+ EXPECT_EQ(file_size, callback64.WaitForResult());
int total_bytes_written = 0;
TestWriteCloseCompletionCallback callback(stream.get(), &total_bytes_written);
@@ -1072,45 +754,18 @@ TEST_F(FileStreamTest, AsyncWriteClose) {
stream.reset();
- ok = base::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
EXPECT_EQ(kTestDataSize * 2, file_size);
}
-// Tests truncating a file.
-TEST_F(FileStreamTest, Truncate) {
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
-
- scoped_ptr<FileStream> write_stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- ASSERT_EQ(OK, write_stream->OpenSync(temp_file_path(), flags));
-
- // Write some data to the file.
- const char test_data[] = "0123456789";
- write_stream->WriteSync(test_data, arraysize(test_data));
-
- // Truncate the file.
- ASSERT_EQ(4, write_stream->Truncate(4));
-
- // Write again.
- write_stream->WriteSync(test_data, 4);
-
- // Close the stream.
- write_stream.reset();
-
- // Read in the contents and make sure we get back what we expected.
- std::string read_contents;
- EXPECT_TRUE(base::ReadFileToString(temp_file_path(), &read_contents));
-
- EXPECT_EQ("01230123", read_contents);
-}
-
TEST_F(FileStreamTest, AsyncOpenAndDelete) {
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
+ scoped_refptr<base::SequencedWorkerPool> pool(
+ new base::SequencedWorkerPool(1, "StreamTest"));
+
+ bool prev = base::ThreadRestrictions::SetIOAllowed(false);
+ scoped_ptr<FileStream> stream(new FileStream(pool.get()));
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
TestCompletionCallback open_callback;
int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1118,26 +773,33 @@ TEST_F(FileStreamTest, AsyncOpenAndDelete) {
// Delete the stream without waiting for the open operation to be
// complete. Should be safe.
stream.reset();
+
+ // Force an operation through the pool.
+ scoped_ptr<FileStream> stream2(new FileStream(pool.get()));
+ TestCompletionCallback open_callback2;
+ rv = stream2->Open(temp_file_path(), flags, open_callback2.callback());
+ EXPECT_EQ(OK, open_callback2.GetResult(rv));
+ stream2.reset();
+
+ pool->Shutdown();
+
// open_callback won't be called.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(open_callback.have_result());
+ base::ThreadRestrictions::SetIOAllowed(prev);
}
// Verify that async Write() errors are mapped correctly.
TEST_F(FileStreamTest, AsyncWriteError) {
// 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;
+ uint32 flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
+
+ base::File file(temp_file_path(), flags);
+ ASSERT_TRUE(file.IsValid());
+
scoped_ptr<FileStream> stream(
- new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
+ new FileStream(file.Pass(), base::MessageLoopProxy::current()));
scoped_refptr<IOBuffer> buf = new IOBuffer(1);
buf->data()[0] = 0;
@@ -1148,24 +810,21 @@ TEST_F(FileStreamTest, AsyncWriteError) {
rv = callback.WaitForResult();
EXPECT_LT(rv, 0);
- base::ClosePlatformFile(file);
+ stream.reset();
+ base::RunLoop().RunUntilIdle();
}
// Verify that async Read() errors are mapped correctly.
TEST_F(FileStreamTest, AsyncReadError) {
// 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;
+ uint32 flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
+ base::File::FLAG_ASYNC;
+
+ base::File file(temp_file_path(), flags);
+ ASSERT_TRUE(file.IsValid());
+
scoped_ptr<FileStream> stream(
- new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
+ new FileStream(file.Pass(), base::MessageLoopProxy::current()));
scoped_refptr<IOBuffer> buf = new IOBuffer(1);
TestCompletionCallback callback;
@@ -1174,7 +833,8 @@ TEST_F(FileStreamTest, AsyncReadError) {
rv = callback.WaitForResult();
EXPECT_LT(rv, 0);
- base::ClosePlatformFile(file);
+ stream.reset();
+ base::RunLoop().RunUntilIdle();
}
#if defined(OS_ANDROID)
@@ -1196,18 +856,14 @@ TEST_F(FileStreamTest, ContentUriAsyncRead) {
EXPECT_TRUE(base::GetFileSize(path, &file_size));
EXPECT_LT(0, file_size);
- FileStream stream(NULL, base::MessageLoopProxy::current());
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ FileStream stream(base::MessageLoopProxy::current());
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
TestCompletionCallback callback;
int rv = stream.Open(path, flags, callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
int total_bytes_read = 0;
std::string data_read;
diff --git a/chromium/net/base/filename_util.cc b/chromium/net/base/filename_util.cc
new file mode 100644
index 00000000000..31b5fe5ec70
--- /dev/null
+++ b/chromium/net/base/filename_util.cc
@@ -0,0 +1,163 @@
+// Copyright 2014 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/base/filename_util.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "net/base/escape.h"
+#include "net/base/filename_util_internal.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_string_util.h"
+#include "net/http/http_content_disposition.h"
+#include "url/gurl.h"
+
+namespace net {
+
+// Prefix to prepend to get a file URL.
+static const base::FilePath::CharType kFileURLPrefix[] =
+ FILE_PATH_LITERAL("file:///");
+
+GURL FilePathToFileURL(const base::FilePath& path) {
+ // Produce a URL like "file:///C:/foo" for a regular file, or
+ // "file://///server/path" for UNC. The URL canonicalizer will fix up the
+ // latter case to be the canonical UNC form: "file://server/path"
+ base::FilePath::StringType url_string(kFileURLPrefix);
+ if (!path.IsAbsolute()) {
+ base::FilePath current_dir;
+ PathService::Get(base::DIR_CURRENT, &current_dir);
+ url_string.append(current_dir.value());
+ url_string.push_back(base::FilePath::kSeparators[0]);
+ }
+ url_string.append(path.value());
+
+ // Now do replacement of some characters. Since we assume the input is a
+ // literal filename, anything the URL parser might consider special should
+ // be escaped here.
+
+ // must be the first substitution since others will introduce percents as the
+ // escape character
+ ReplaceSubstringsAfterOffset(
+ &url_string, 0, FILE_PATH_LITERAL("%"), FILE_PATH_LITERAL("%25"));
+
+ // semicolon is supposed to be some kind of separator according to RFC 2396
+ ReplaceSubstringsAfterOffset(
+ &url_string, 0, FILE_PATH_LITERAL(";"), FILE_PATH_LITERAL("%3B"));
+
+ ReplaceSubstringsAfterOffset(
+ &url_string, 0, FILE_PATH_LITERAL("#"), FILE_PATH_LITERAL("%23"));
+
+ ReplaceSubstringsAfterOffset(
+ &url_string, 0, FILE_PATH_LITERAL("?"), FILE_PATH_LITERAL("%3F"));
+
+#if defined(OS_POSIX)
+ ReplaceSubstringsAfterOffset(
+ &url_string, 0, FILE_PATH_LITERAL("\\"), FILE_PATH_LITERAL("%5C"));
+#endif
+
+ return GURL(url_string);
+}
+
+bool FileURLToFilePath(const GURL& url, base::FilePath* file_path) {
+ *file_path = base::FilePath();
+ base::FilePath::StringType& file_path_str =
+ const_cast<base::FilePath::StringType&>(file_path->value());
+ file_path_str.clear();
+
+ if (!url.is_valid())
+ return false;
+
+#if defined(OS_WIN)
+ std::string path;
+ std::string host = url.host();
+ if (host.empty()) {
+ // URL contains no host, the path is the filename. In this case, the path
+ // will probably be preceeded with a slash, as in "/C:/foo.txt", so we
+ // trim out that here.
+ path = url.path();
+ size_t first_non_slash = path.find_first_not_of("/\\");
+ if (first_non_slash != std::string::npos && first_non_slash > 0)
+ path.erase(0, first_non_slash);
+ } else {
+ // URL contains a host: this means it's UNC. We keep the preceeding slash
+ // on the path.
+ path = "\\\\";
+ path.append(host);
+ path.append(url.path());
+ }
+ std::replace(path.begin(), path.end(), '/', '\\');
+#else // defined(OS_WIN)
+ // Firefox seems to ignore the "host" of a file url if there is one. That is,
+ // file://foo/bar.txt maps to /bar.txt.
+ // TODO(dhg): This should probably take into account UNCs which could
+ // include a hostname other than localhost or blank
+ std::string path = url.path();
+#endif // !defined(OS_WIN)
+
+ if (path.empty())
+ return false;
+
+ // GURL stores strings as percent-encoded 8-bit, this will undo if possible.
+ path = UnescapeURLComponent(
+ path, UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+
+#if defined(OS_WIN)
+ if (base::IsStringUTF8(path)) {
+ file_path_str.assign(base::UTF8ToWide(path));
+ // We used to try too hard and see if |path| made up entirely of
+ // the 1st 256 characters in the Unicode was a zero-extended UTF-16.
+ // If so, we converted it to 'Latin-1' and checked if the result was UTF-8.
+ // If the check passed, we converted the result to UTF-8.
+ // Otherwise, we treated the result as the native OS encoding.
+ // However, that led to http://crbug.com/4619 and http://crbug.com/14153
+ } else {
+ // Not UTF-8, assume encoding is native codepage and we're done. We know we
+ // are giving the conversion function a nonempty string, and it may fail if
+ // the given string is not in the current encoding and give us an empty
+ // string back. We detect this and report failure.
+ file_path_str = base::SysNativeMBToWide(path);
+ }
+#else // defined(OS_WIN)
+ // Collapse multiple path slashes into a single path slash.
+ std::string new_path;
+ do {
+ new_path = path;
+ ReplaceSubstringsAfterOffset(&new_path, 0, "//", "/");
+ path.swap(new_path);
+ } while (new_path != path);
+
+ file_path_str.assign(path);
+#endif // !defined(OS_WIN)
+
+ return !file_path_str.empty();
+}
+
+void GenerateSafeFileName(const std::string& mime_type,
+ bool ignore_extension,
+ base::FilePath* file_path) {
+ // Make sure we get the right file extension
+ EnsureSafeExtension(mime_type, ignore_extension, file_path);
+
+#if defined(OS_WIN)
+ // Prepend "_" to the file name if it's a reserved name
+ base::FilePath::StringType leaf_name = file_path->BaseName().value();
+ DCHECK(!leaf_name.empty());
+ if (IsReservedName(leaf_name)) {
+ leaf_name = base::FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
+ *file_path = file_path->DirName();
+ if (file_path->value() == base::FilePath::kCurrentDirectory) {
+ *file_path = base::FilePath(leaf_name);
+ } else {
+ *file_path = file_path->Append(leaf_name);
+ }
+ }
+#endif
+}
+
+} // namespace net
diff --git a/chromium/net/base/filename_util.h b/chromium/net/base/filename_util.h
new file mode 100644
index 00000000000..6b151bad9b0
--- /dev/null
+++ b/chromium/net/base/filename_util.h
@@ -0,0 +1,116 @@
+// Copyright 2014 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_BASE_FILENAME_UTIL_H_
+#define NET_BASE_FILENAME_UTIL_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "net/base/net_export.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+}
+
+namespace net {
+
+// Given the full path to a file name, creates a file: URL. The returned URL
+// may not be valid if the input is malformed.
+NET_EXPORT GURL FilePathToFileURL(const base::FilePath& path);
+
+// Converts a file: URL back to a filename that can be passed to the OS. The
+// file URL must be well-formed (GURL::is_valid() must return true); we don't
+// handle degenerate cases here. Returns true on success, false if it isn't a
+// valid file URL. On failure, *file_path will be empty.
+NET_EXPORT bool FileURLToFilePath(const GURL& url, base::FilePath* file_path);
+
+// Generates a filename using the first successful method from the following (in
+// order):
+//
+// 1) The raw Content-Disposition header in |content_disposition| as read from
+// the network. |referrer_charset| is used to decode non-ASCII strings.
+// 2) |suggested_name| if specified. |suggested_name| is assumed to be in
+// UTF-8.
+// 3) The filename extracted from the |url|. |referrer_charset| will be used to
+// interpret the URL if there are non-ascii characters.
+// 4) |default_name|. If non-empty, |default_name| is assumed to be a filename
+// and shouldn't contain a path. |default_name| is not subject to validation
+// or sanitization, and therefore shouldn't be a user supplied string.
+// 5) The hostname portion from the |url|
+//
+// Then, leading and trailing '.'s will be removed. On Windows, trailing spaces
+// are also removed. The string "download" is the final fallback if no filename
+// is found or the filename is empty.
+//
+// Any illegal characters in the filename will be replaced by '-'. If the
+// filename doesn't contain an extension, and a |mime_type| is specified, the
+// preferred extension for the |mime_type| will be appended to the filename.
+// The resulting filename is then checked against a list of reserved names on
+// Windows. If the name is reserved, an underscore will be prepended to the
+// filename.
+//
+// Note: |mime_type| should only be specified if this function is called from a
+// thread that allows IO.
+NET_EXPORT base::string16 GetSuggestedFilename(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name);
+
+// Similar to GetSuggestedFilename(), but returns a FilePath.
+NET_EXPORT base::FilePath GenerateFileName(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name);
+
+// Valid components:
+// * are not empty
+// * are not Windows reserved names (CON, NUL.zip, etc.)
+// * do not have trailing separators
+// * do not equal kCurrentDirectory
+// * do not reference the parent directory
+// * do not contain illegal characters
+// * do not end with Windows shell-integrated extensions (even on posix)
+// * do not begin with '.' (which would hide them in most file managers)
+// * do not end with ' ' or '.'
+NET_EXPORT bool IsSafePortablePathComponent(const base::FilePath& component);
+
+// Basenames of valid relative paths are IsSafePortableBasename(), and internal
+// path components of valid relative paths are valid path components as
+// described above IsSafePortableBasename(). Valid relative paths are not
+// absolute paths.
+NET_EXPORT bool IsSafePortableRelativePath(const base::FilePath& path);
+
+// Ensures that the filename and extension is safe to use in the filesystem.
+//
+// Assumes that |file_path| already contains a valid path or file name. On
+// Windows if the extension causes the file to have an unsafe interaction with
+// the shell (see net_util::IsShellIntegratedExtension()), then it will be
+// replaced by the string 'download'. If |file_path| doesn't contain an
+// extension or |ignore_extension| is true then the preferred extension, if one
+// exists, for |mime_type| will be used as the extension.
+//
+// On Windows, the filename will be checked against a set of reserved names, and
+// if so, an underscore will be prepended to the name.
+//
+// |file_name| can either be just the file name or it can be a full path to a
+// file.
+//
+// Note: |mime_type| should only be non-empty if this function is called from a
+// thread that allows IO.
+NET_EXPORT void GenerateSafeFileName(const std::string& mime_type,
+ bool ignore_extension,
+ base::FilePath* file_path);
+
+} // namespace net
+
+#endif // NET_BASE_FILENAME_UTIL_H_
diff --git a/chromium/net/base/filename_util_icu.cc b/chromium/net/base/filename_util_icu.cc
new file mode 100644
index 00000000000..72bce6e0c36
--- /dev/null
+++ b/chromium/net/base/filename_util_icu.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 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/base/filename_util.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/i18n/file_util_icu.h"
+#include "base/strings/string16.h"
+#include "net/base/filename_util_internal.h"
+
+class GURL;
+
+namespace net {
+
+bool IsSafePortablePathComponent(const base::FilePath& component) {
+ base::string16 component16;
+ base::FilePath::StringType sanitized = component.value();
+ SanitizeGeneratedFileName(&sanitized, true);
+ base::FilePath::StringType extension = component.Extension();
+ if (!extension.empty())
+ extension.erase(extension.begin()); // Erase preceding '.'.
+ return !component.empty() && (component == component.BaseName()) &&
+ (component == component.StripTrailingSeparators()) &&
+ FilePathToString16(component, &component16) &&
+ file_util::IsFilenameLegal(component16) &&
+ !IsShellIntegratedExtension(extension) &&
+ (sanitized == component.value()) && !IsReservedName(component.value());
+}
+
+bool IsSafePortableRelativePath(const base::FilePath& path) {
+ if (path.empty() || path.IsAbsolute() || path.EndsWithSeparator())
+ return false;
+ std::vector<base::FilePath::StringType> components;
+ path.GetComponents(&components);
+ if (components.empty())
+ return false;
+ for (size_t i = 0; i < components.size() - 1; ++i) {
+ if (!IsSafePortablePathComponent(base::FilePath(components[i])))
+ return false;
+ }
+ return IsSafePortablePathComponent(path.BaseName());
+}
+
+base::string16 GetSuggestedFilename(const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name) {
+ return GetSuggestedFilenameImpl(
+ url,
+ content_disposition,
+ referrer_charset,
+ suggested_name,
+ mime_type,
+ default_name,
+ base::Bind(&file_util::ReplaceIllegalCharactersInPath));
+}
+
+base::FilePath GenerateFileName(const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_file_name) {
+ base::FilePath generated_name(GenerateFileNameImpl(
+ url,
+ content_disposition,
+ referrer_charset,
+ suggested_name,
+ mime_type,
+ default_file_name,
+ base::Bind(&file_util::ReplaceIllegalCharactersInPath)));
+
+#if defined(OS_CHROMEOS)
+ // When doing file manager operations on ChromeOS, the file paths get
+ // normalized in WebKit layer, so let's ensure downloaded files have
+ // normalized names. Otherwise, we won't be able to handle files with NFD
+ // utf8 encoded characters in name.
+ file_util::NormalizeFileNameEncoding(&generated_name);
+#endif
+
+ DCHECK(!generated_name.empty());
+
+ return generated_name;
+}
+
+} // namespace net
diff --git a/chromium/net/base/filename_util_internal.cc b/chromium/net/base/filename_util_internal.cc
new file mode 100644
index 00000000000..a59de0f50c9
--- /dev/null
+++ b/chromium/net/base/filename_util_internal.cc
@@ -0,0 +1,318 @@
+// Copyright 2014 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/base/filename_util.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "net/base/escape.h"
+#include "net/base/filename_util_internal.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_string_util.h"
+#include "net/http/http_content_disposition.h"
+#include "url/gurl.h"
+
+namespace net {
+
+void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
+ bool replace_trailing) {
+ const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("-");
+ if (filename->empty())
+ return;
+ if (replace_trailing) {
+ // Handle CreateFile() stripping trailing dots and spaces on filenames
+ // http://support.microsoft.com/kb/115827
+ size_t length = filename->size();
+ size_t pos = filename->find_last_not_of(FILE_PATH_LITERAL(" ."));
+ filename->resize((pos == std::string::npos) ? 0 : (pos + 1));
+ base::TrimWhitespace(*filename, base::TRIM_TRAILING, filename);
+ if (filename->empty())
+ return;
+ size_t trimmed = length - filename->size();
+ if (trimmed)
+ filename->insert(filename->end(), trimmed, kReplace[0]);
+ }
+ base::TrimString(*filename, FILE_PATH_LITERAL("."), filename);
+ if (filename->empty())
+ return;
+ // Replace any path information by changing path separators.
+ ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("/"), kReplace);
+ ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("\\"), kReplace);
+}
+
+// Returns the filename determined from the last component of the path portion
+// of the URL. Returns an empty string if the URL doesn't have a path or is
+// invalid. If the generated filename is not reliable,
+// |should_overwrite_extension| will be set to true, in which case a better
+// extension should be determined based on the content type.
+std::string GetFileNameFromURL(const GURL& url,
+ const std::string& referrer_charset,
+ bool* should_overwrite_extension) {
+ // about: and data: URLs don't have file names, but esp. data: URLs may
+ // contain parts that look like ones (i.e., contain a slash). Therefore we
+ // don't attempt to divine a file name out of them.
+ if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data"))
+ return std::string();
+
+ const std::string unescaped_url_filename = UnescapeURLComponent(
+ url.ExtractFileName(),
+ UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+
+ // The URL's path should be escaped UTF-8, but may not be.
+ std::string decoded_filename = unescaped_url_filename;
+ if (!base::IsStringUTF8(decoded_filename)) {
+ // TODO(jshin): this is probably not robust enough. To be sure, we need
+ // encoding detection.
+ base::string16 utf16_output;
+ if (!referrer_charset.empty() &&
+ net::ConvertToUTF16(
+ unescaped_url_filename, referrer_charset.c_str(), &utf16_output)) {
+ decoded_filename = base::UTF16ToUTF8(utf16_output);
+ } else {
+ decoded_filename =
+ base::WideToUTF8(base::SysNativeMBToWide(unescaped_url_filename));
+ }
+ }
+ // If the URL contains a (possibly empty) query, assume it is a generator, and
+ // allow the determined extension to be overwritten.
+ *should_overwrite_extension = !decoded_filename.empty() && url.has_query();
+
+ return decoded_filename;
+}
+
+// Returns whether the specified extension is automatically integrated into the
+// windows shell.
+bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
+ base::FilePath::StringType extension_lower = StringToLowerASCII(extension);
+
+ // http://msdn.microsoft.com/en-us/library/ms811694.aspx
+ // Right-clicking on shortcuts can be magical.
+ if ((extension_lower == FILE_PATH_LITERAL("local")) ||
+ (extension_lower == FILE_PATH_LITERAL("lnk")))
+ return true;
+
+ // http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html
+ // Files become magical if they end in a CLSID, so block such extensions.
+ if (!extension_lower.empty() &&
+ (extension_lower[0] == FILE_PATH_LITERAL('{')) &&
+ (extension_lower[extension_lower.length() - 1] == FILE_PATH_LITERAL('}')))
+ return true;
+ return false;
+}
+
+// Returns whether the specified file name is a reserved name on windows.
+// This includes names like "com2.zip" (which correspond to devices) and
+// desktop.ini and thumbs.db which have special meaning to the windows shell.
+bool IsReservedName(const base::FilePath::StringType& filename) {
+ // This list is taken from the MSDN article "Naming a file"
+ // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ // I also added clock$ because GetSaveFileName seems to consider it as a
+ // reserved name too.
+ static const char* const known_devices[] = {
+ "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4",
+ "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
+ "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"};
+#if defined(OS_WIN)
+ std::string filename_lower = StringToLowerASCII(base::WideToUTF8(filename));
+#elif defined(OS_POSIX)
+ std::string filename_lower = StringToLowerASCII(filename);
+#endif
+
+ for (size_t i = 0; i < arraysize(known_devices); ++i) {
+ // Exact match.
+ if (filename_lower == known_devices[i])
+ return true;
+ // Starts with "DEVICE.".
+ if (filename_lower.find(std::string(known_devices[i]) + ".") == 0)
+ return true;
+ }
+
+ static const char* const magic_names[] = {
+ // These file names are used by the "Customize folder" feature of the shell.
+ "desktop.ini",
+ "thumbs.db",
+ };
+
+ for (size_t i = 0; i < arraysize(magic_names); ++i) {
+ if (filename_lower == magic_names[i])
+ return true;
+ }
+
+ return false;
+}
+
+// Examines the current extension in |file_name| and modifies it if necessary in
+// order to ensure the filename is safe. If |file_name| doesn't contain an
+// extension or if |ignore_extension| is true, then a new extension will be
+// constructed based on the |mime_type|.
+//
+// We're addressing two things here:
+//
+// 1) Usability. If there is no reliable file extension, we want to guess a
+// reasonable file extension based on the content type.
+//
+// 2) Shell integration. Some file extensions automatically integrate with the
+// shell. We block these extensions to prevent a malicious web site from
+// integrating with the user's shell.
+void EnsureSafeExtension(const std::string& mime_type,
+ bool ignore_extension,
+ base::FilePath* file_name) {
+ // See if our file name already contains an extension.
+ base::FilePath::StringType extension = file_name->Extension();
+ if (!extension.empty())
+ extension.erase(extension.begin()); // Erase preceding '.'.
+
+ if ((ignore_extension || extension.empty()) && !mime_type.empty()) {
+ base::FilePath::StringType preferred_mime_extension;
+ std::vector<base::FilePath::StringType> all_mime_extensions;
+ net::GetPreferredExtensionForMimeType(mime_type, &preferred_mime_extension);
+ net::GetExtensionsForMimeType(mime_type, &all_mime_extensions);
+ // If the existing extension is in the list of valid extensions for the
+ // given type, use it. This avoids doing things like pointlessly renaming
+ // "foo.jpg" to "foo.jpeg".
+ if (std::find(all_mime_extensions.begin(),
+ all_mime_extensions.end(),
+ extension) != all_mime_extensions.end()) {
+ // leave |extension| alone
+ } else if (!preferred_mime_extension.empty()) {
+ extension = preferred_mime_extension;
+ }
+ }
+
+#if defined(OS_WIN)
+ static const base::FilePath::CharType default_extension[] =
+ FILE_PATH_LITERAL("download");
+
+ // Rename shell-integrated extensions.
+ // TODO(asanka): Consider stripping out the bad extension and replacing it
+ // with the preferred extension for the MIME type if one is available.
+ if (IsShellIntegratedExtension(extension))
+ extension.assign(default_extension);
+#endif
+
+ *file_name = file_name->ReplaceExtension(extension);
+}
+
+bool FilePathToString16(const base::FilePath& path, base::string16* converted) {
+#if defined(OS_WIN)
+ return base::WideToUTF16(
+ path.value().c_str(), path.value().size(), converted);
+#elif defined(OS_POSIX)
+ std::string component8 = path.AsUTF8Unsafe();
+ return !component8.empty() &&
+ base::UTF8ToUTF16(component8.c_str(), component8.size(), converted);
+#endif
+}
+
+base::string16 GetSuggestedFilenameImpl(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name,
+ ReplaceIllegalCharactersCallback replace_illegal_characters_callback) {
+ // TODO: this function to be updated to match the httpbis recommendations.
+ // Talk to abarth for the latest news.
+
+ // We don't translate this fallback string, "download". If localization is
+ // needed, the caller should provide localized fallback in |default_name|.
+ static const base::FilePath::CharType kFinalFallbackName[] =
+ FILE_PATH_LITERAL("download");
+ std::string filename; // In UTF-8
+ bool overwrite_extension = false;
+
+ // Try to extract a filename from content-disposition first.
+ if (!content_disposition.empty()) {
+ HttpContentDisposition header(content_disposition, referrer_charset);
+ filename = header.filename();
+ }
+
+ // Then try to use the suggested name.
+ if (filename.empty() && !suggested_name.empty())
+ filename = suggested_name;
+
+ // Now try extracting the filename from the URL. GetFileNameFromURL() only
+ // looks at the last component of the URL and doesn't return the hostname as a
+ // failover.
+ if (filename.empty())
+ filename = GetFileNameFromURL(url, referrer_charset, &overwrite_extension);
+
+ // Finally try the URL hostname, but only if there's no default specified in
+ // |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a
+ // host name.
+ if (filename.empty() && default_name.empty() && url.is_valid() &&
+ !url.host().empty()) {
+ // TODO(jungshik) : Decode a 'punycoded' IDN hostname. (bug 1264451)
+ filename = url.host();
+ }
+
+ bool replace_trailing = false;
+ base::FilePath::StringType result_str, default_name_str;
+#if defined(OS_WIN)
+ replace_trailing = true;
+ result_str = base::UTF8ToUTF16(filename);
+ default_name_str = base::UTF8ToUTF16(default_name);
+#else
+ result_str = filename;
+ default_name_str = default_name;
+#endif
+ SanitizeGeneratedFileName(&result_str, replace_trailing);
+ if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) ==
+ base::FilePath::StringType::npos) {
+ result_str = !default_name_str.empty()
+ ? default_name_str
+ : base::FilePath::StringType(kFinalFallbackName);
+ overwrite_extension = false;
+ }
+ replace_illegal_characters_callback.Run(&result_str, '-');
+ base::FilePath result(result_str);
+ GenerateSafeFileName(mime_type, overwrite_extension, &result);
+
+ base::string16 result16;
+ if (!FilePathToString16(result, &result16)) {
+ result = base::FilePath(default_name_str);
+ if (!FilePathToString16(result, &result16)) {
+ result = base::FilePath(kFinalFallbackName);
+ FilePathToString16(result, &result16);
+ }
+ }
+ return result16;
+}
+
+base::FilePath GenerateFileNameImpl(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_file_name,
+ ReplaceIllegalCharactersCallback replace_illegal_characters_callback) {
+ base::string16 file_name =
+ GetSuggestedFilenameImpl(url,
+ content_disposition,
+ referrer_charset,
+ suggested_name,
+ mime_type,
+ default_file_name,
+ replace_illegal_characters_callback);
+
+#if defined(OS_WIN)
+ base::FilePath generated_name(file_name);
+#else
+ base::FilePath generated_name(
+ base::SysWideToNativeMB(base::UTF16ToWide(file_name)));
+#endif
+
+ DCHECK(!generated_name.empty());
+
+ return generated_name;
+}
+
+} // namespace net
diff --git a/chromium/net/base/filename_util_internal.h b/chromium/net/base/filename_util_internal.h
new file mode 100644
index 00000000000..0e6033439e0
--- /dev/null
+++ b/chromium/net/base/filename_util_internal.h
@@ -0,0 +1,62 @@
+// Copyright 2014 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.
+
+// Functions used internally by filename_util, filename_util_icu and
+// filename_util_unsafe.
+
+#ifndef NET_BASE_FILENAME_UTIL_INTERNAL_H_
+#define NET_BASE_FILENAME_UTIL_INTERNAL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+
+class GURL;
+
+namespace net {
+
+typedef base::Callback<
+ void(base::FilePath::StringType* file_name, char replace_char)>
+ ReplaceIllegalCharactersCallback;
+
+void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
+ bool replace_trailing);
+
+bool IsShellIntegratedExtension(const base::FilePath::StringType& extension);
+
+bool IsReservedName(const base::FilePath::StringType& filename);
+
+void EnsureSafeExtension(const std::string& mime_type,
+ bool ignore_extension,
+ base::FilePath* file_name);
+
+bool FilePathToString16(const base::FilePath& path, base::string16* converted);
+
+// Similar to GetSuggestedFilename(), but takes callback to replace illegal
+// characters.
+base::string16 GetSuggestedFilenameImpl(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name,
+ ReplaceIllegalCharactersCallback replace_illegal_characters_callback);
+
+// Similar to GenerateFileName(), but takes callback to replace illegal
+// characters.
+base::FilePath GenerateFileNameImpl(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name,
+ ReplaceIllegalCharactersCallback replace_illegal_characters_callback);
+
+} // namespace net
+
+#endif // NET_BASE_FILENAME_UTIL_INTERNAL_H_
diff --git a/chromium/net/base/filename_util_unittest.cc b/chromium/net/base/filename_util_unittest.cc
new file mode 100644
index 00000000000..0bd22500fe9
--- /dev/null
+++ b/chromium/net/base/filename_util_unittest.cc
@@ -0,0 +1,1652 @@
+// Copyright 2014 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/base/filename_util.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+struct FileCase {
+ const wchar_t* file;
+ const char* url;
+};
+
+struct GenerateFilenameCase {
+ int lineno;
+ const char* url;
+ const char* content_disp_header;
+ const char* referrer_charset;
+ const char* suggested_filename;
+ const char* mime_type;
+ const wchar_t* default_filename;
+ const wchar_t* expected_filename;
+};
+
+void RunGenerateFileNameTestCase(const GenerateFilenameCase* test_case) {
+ std::string default_filename(base::WideToUTF8(test_case->default_filename));
+ base::FilePath file_path = GenerateFileName(
+ GURL(test_case->url), test_case->content_disp_header,
+ test_case->referrer_charset, test_case->suggested_filename,
+ test_case->mime_type, default_filename);
+ EXPECT_EQ(test_case->expected_filename,
+ file_util::FilePathAsWString(file_path))
+ << "test case at line number: " << test_case->lineno;
+}
+
+} // namespace
+
+static const base::FilePath::CharType* kSafePortableBasenames[] = {
+ FILE_PATH_LITERAL("a"),
+ FILE_PATH_LITERAL("a.txt"),
+ FILE_PATH_LITERAL("a b.txt"),
+ FILE_PATH_LITERAL("a-b.txt"),
+ FILE_PATH_LITERAL("My Computer"),
+ FILE_PATH_LITERAL(" Computer"),
+};
+
+static const base::FilePath::CharType* kUnsafePortableBasenames[] = {
+ FILE_PATH_LITERAL(""),
+ FILE_PATH_LITERAL("."),
+ FILE_PATH_LITERAL(".."),
+ FILE_PATH_LITERAL("..."),
+ FILE_PATH_LITERAL("con"),
+ FILE_PATH_LITERAL("con.zip"),
+ FILE_PATH_LITERAL("NUL"),
+ FILE_PATH_LITERAL("NUL.zip"),
+ FILE_PATH_LITERAL(".a"),
+ FILE_PATH_LITERAL("a."),
+ FILE_PATH_LITERAL("a\"a"),
+ FILE_PATH_LITERAL("a<a"),
+ FILE_PATH_LITERAL("a>a"),
+ FILE_PATH_LITERAL("a?a"),
+ FILE_PATH_LITERAL("a/"),
+ FILE_PATH_LITERAL("a\\"),
+ FILE_PATH_LITERAL("a "),
+ FILE_PATH_LITERAL("a . ."),
+ FILE_PATH_LITERAL("My Computer.{a}"),
+ FILE_PATH_LITERAL("My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}"),
+#if !defined(OS_WIN)
+ FILE_PATH_LITERAL("a\\a"),
+#endif
+};
+
+static const base::FilePath::CharType* kSafePortableRelativePaths[] = {
+ FILE_PATH_LITERAL("a/a"),
+#if defined(OS_WIN)
+ FILE_PATH_LITERAL("a\\a"),
+#endif
+};
+
+TEST(FilenameUtilTest, IsSafePortablePathComponent) {
+ for (size_t i = 0 ; i < arraysize(kSafePortableBasenames); ++i) {
+ EXPECT_TRUE(IsSafePortablePathComponent(base::FilePath(
+ kSafePortableBasenames[i]))) << kSafePortableBasenames[i];
+ }
+ for (size_t i = 0 ; i < arraysize(kUnsafePortableBasenames); ++i) {
+ EXPECT_FALSE(IsSafePortablePathComponent(base::FilePath(
+ kUnsafePortableBasenames[i]))) << kUnsafePortableBasenames[i];
+ }
+ for (size_t i = 0 ; i < arraysize(kSafePortableRelativePaths); ++i) {
+ EXPECT_FALSE(IsSafePortablePathComponent(base::FilePath(
+ kSafePortableRelativePaths[i]))) << kSafePortableRelativePaths[i];
+ }
+}
+
+TEST(FilenameUtilTest, IsSafePortableRelativePath) {
+ base::FilePath safe_dirname(FILE_PATH_LITERAL("a"));
+ for (size_t i = 0 ; i < arraysize(kSafePortableBasenames); ++i) {
+ EXPECT_TRUE(IsSafePortableRelativePath(base::FilePath(
+ kSafePortableBasenames[i]))) << kSafePortableBasenames[i];
+ EXPECT_TRUE(IsSafePortableRelativePath(safe_dirname.Append(base::FilePath(
+ kSafePortableBasenames[i])))) << kSafePortableBasenames[i];
+ }
+ for (size_t i = 0 ; i < arraysize(kSafePortableRelativePaths); ++i) {
+ EXPECT_TRUE(IsSafePortableRelativePath(base::FilePath(
+ kSafePortableRelativePaths[i]))) << kSafePortableRelativePaths[i];
+ EXPECT_TRUE(IsSafePortableRelativePath(safe_dirname.Append(base::FilePath(
+ kSafePortableRelativePaths[i])))) << kSafePortableRelativePaths[i];
+ }
+ for (size_t i = 0 ; i < arraysize(kUnsafePortableBasenames); ++i) {
+ EXPECT_FALSE(IsSafePortableRelativePath(base::FilePath(
+ kUnsafePortableBasenames[i]))) << kUnsafePortableBasenames[i];
+ if (!base::FilePath::StringType(kUnsafePortableBasenames[i]).empty()) {
+ EXPECT_FALSE(IsSafePortableRelativePath(safe_dirname.Append(
+ base::FilePath(kUnsafePortableBasenames[i]))))
+ << kUnsafePortableBasenames[i];
+ }
+ }
+}
+
+TEST(FilenameUtilTest, FileURLConversion) {
+ // a list of test file names and the corresponding URLs
+ const FileCase round_trip_cases[] = {
+#if defined(OS_WIN)
+ {L"C:\\foo\\bar.txt", "file:///C:/foo/bar.txt"},
+ {L"\\\\some computer\\foo\\bar.txt",
+ "file://some%20computer/foo/bar.txt"}, // UNC
+ {L"D:\\Name;with%some symbols*#",
+ "file:///D:/Name%3Bwith%25some%20symbols*%23"},
+ // issue 14153: To be tested with the OS default codepage other than 1252.
+ {L"D:\\latin1\\caf\x00E9\x00DD.txt",
+ "file:///D:/latin1/caf%C3%A9%C3%9D.txt"},
+ {L"D:\\otherlatin\\caf\x0119.txt",
+ "file:///D:/otherlatin/caf%C4%99.txt"},
+ {L"D:\\greek\\\x03B1\x03B2\x03B3.txt",
+ "file:///D:/greek/%CE%B1%CE%B2%CE%B3.txt"},
+ {L"D:\\Chinese\\\x6240\x6709\x4e2d\x6587\x7f51\x9875.doc",
+ "file:///D:/Chinese/%E6%89%80%E6%9C%89%E4%B8%AD%E6%96%87%E7%BD%91"
+ "%E9%A1%B5.doc"},
+ {L"D:\\plane1\\\xD835\xDC00\xD835\xDC01.txt", // Math alphabet "AB"
+ "file:///D:/plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
+#elif defined(OS_POSIX)
+ {L"/foo/bar.txt", "file:///foo/bar.txt"},
+ {L"/foo/BAR.txt", "file:///foo/BAR.txt"},
+ {L"/C:/foo/bar.txt", "file:///C:/foo/bar.txt"},
+ {L"/foo/bar?.txt", "file:///foo/bar%3F.txt"},
+ {L"/some computer/foo/bar.txt", "file:///some%20computer/foo/bar.txt"},
+ {L"/Name;with%some symbols*#", "file:///Name%3Bwith%25some%20symbols*%23"},
+ {L"/latin1/caf\x00E9\x00DD.txt", "file:///latin1/caf%C3%A9%C3%9D.txt"},
+ {L"/otherlatin/caf\x0119.txt", "file:///otherlatin/caf%C4%99.txt"},
+ {L"/greek/\x03B1\x03B2\x03B3.txt", "file:///greek/%CE%B1%CE%B2%CE%B3.txt"},
+ {L"/Chinese/\x6240\x6709\x4e2d\x6587\x7f51\x9875.doc",
+ "file:///Chinese/%E6%89%80%E6%9C%89%E4%B8%AD%E6%96%87%E7%BD"
+ "%91%E9%A1%B5.doc"},
+ {L"/plane1/\x1D400\x1D401.txt", // Math alphabet "AB"
+ "file:///plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
+#endif
+ };
+
+ // First, we'll test that we can round-trip all of the above cases of URLs
+ base::FilePath output;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(round_trip_cases); i++) {
+ // convert to the file URL
+ GURL file_url(FilePathToFileURL(
+ file_util::WStringAsFilePath(round_trip_cases[i].file)));
+ EXPECT_EQ(round_trip_cases[i].url, file_url.spec());
+
+ // Back to the filename.
+ EXPECT_TRUE(FileURLToFilePath(file_url, &output));
+ EXPECT_EQ(round_trip_cases[i].file, file_util::FilePathAsWString(output));
+ }
+
+ // Test that various file: URLs get decoded into the correct file type
+ FileCase url_cases[] = {
+#if defined(OS_WIN)
+ {L"C:\\foo\\bar.txt", "file:c|/foo\\bar.txt"},
+ {L"C:\\foo\\bar.txt", "file:/c:/foo/bar.txt"},
+ {L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
+ {L"C:\\foo\\bar.txt", "file:///c:/foo/bar.txt"},
+ {L"\\\\foo\\bar.txt", "file:////foo\\bar.txt"},
+ {L"\\\\foo\\bar.txt", "file:/foo/bar.txt"},
+ {L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
+ {L"C:\\foo\\bar.txt", "file:\\\\\\c:/foo/bar.txt"},
+#elif defined(OS_POSIX)
+ {L"/c:/foo/bar.txt", "file:/c:/foo/bar.txt"},
+ {L"/c:/foo/bar.txt", "file:///c:/foo/bar.txt"},
+ {L"/foo/bar.txt", "file:/foo/bar.txt"},
+ {L"/c:/foo/bar.txt", "file:\\\\\\c:/foo/bar.txt"},
+ {L"/foo/bar.txt", "file:foo/bar.txt"},
+ {L"/bar.txt", "file://foo/bar.txt"},
+ {L"/foo/bar.txt", "file:///foo/bar.txt"},
+ {L"/foo/bar.txt", "file:////foo/bar.txt"},
+ {L"/foo/bar.txt", "file:////foo//bar.txt"},
+ {L"/foo/bar.txt", "file:////foo///bar.txt"},
+ {L"/foo/bar.txt", "file:////foo////bar.txt"},
+ {L"/c:/foo/bar.txt", "file:\\\\\\c:/foo/bar.txt"},
+ {L"/c:/foo/bar.txt", "file:c:/foo/bar.txt"},
+ // We get these wrong because GURL turns back slashes into forward
+ // slashes.
+ //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
+ //{L"/c|/foo%5Cbar.txt", "file:c|/foo\\bar.txt"},
+ //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
+ //{L"/foo%5Cbar.txt", "file:////foo\\bar.txt"},
+ //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
+#endif
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(url_cases); i++) {
+ FileURLToFilePath(GURL(url_cases[i].url), &output);
+ EXPECT_EQ(url_cases[i].file, file_util::FilePathAsWString(output));
+ }
+
+ // Unfortunately, UTF8ToWide discards invalid UTF8 input.
+#ifdef BUG_878908_IS_FIXED
+ // Test that no conversion happens if the UTF-8 input is invalid, and that
+ // the input is preserved in UTF-8
+ const char invalid_utf8[] = "file:///d:/Blah/\xff.doc";
+ const wchar_t invalid_wide[] = L"D:\\Blah\\\xff.doc";
+ EXPECT_TRUE(FileURLToFilePath(
+ GURL(std::string(invalid_utf8)), &output));
+ EXPECT_EQ(std::wstring(invalid_wide), output);
+#endif
+
+ // Test that if a file URL is malformed, we get a failure
+ EXPECT_FALSE(FileURLToFilePath(GURL("filefoobar"), &output));
+}
+
+#if defined(OS_WIN)
+#define JPEG_EXT L".jpg"
+#define HTML_EXT L".htm"
+#elif defined(OS_MACOSX)
+#define JPEG_EXT L".jpeg"
+#define HTML_EXT L".html"
+#else
+#define JPEG_EXT L".jpg"
+#define HTML_EXT L".html"
+#endif
+#define TXT_EXT L".txt"
+#define TAR_EXT L".tar"
+
+TEST(FilenameUtilTest, GenerateSafeFileName) {
+ const struct {
+ const char* mime_type;
+ const base::FilePath::CharType* filename;
+ const base::FilePath::CharType* expected_filename;
+ } safe_tests[] = {
+#if defined(OS_WIN)
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\bar.htm"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.htm")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\bar.html"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.html")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\bar"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.htm")
+ },
+ {
+ "image/png",
+ FILE_PATH_LITERAL("C:\\bar.html"),
+ FILE_PATH_LITERAL("C:\\bar.html")
+ },
+ {
+ "image/png",
+ FILE_PATH_LITERAL("C:\\bar"),
+ FILE_PATH_LITERAL("C:\\bar.png")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.exe")
+ },
+ {
+ "image/gif",
+ FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.exe")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\google.com"),
+ FILE_PATH_LITERAL("C:\\foo\\google.com")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\con.htm"),
+ FILE_PATH_LITERAL("C:\\foo\\_con.htm")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\con"),
+ FILE_PATH_LITERAL("C:\\foo\\_con.htm")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\harmless.{not-really-this-may-be-a-guid}"),
+ FILE_PATH_LITERAL("C:\\foo\\harmless.download")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\harmless.local"),
+ FILE_PATH_LITERAL("C:\\foo\\harmless.download")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\harmless.lnk"),
+ FILE_PATH_LITERAL("C:\\foo\\harmless.download")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-"),
+ FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-")
+ },
+ // Allow extension synonyms.
+ {
+ "image/jpeg",
+ FILE_PATH_LITERAL("C:\\foo\\bar.jpg"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.jpg")
+ },
+ {
+ "image/jpeg",
+ FILE_PATH_LITERAL("C:\\foo\\bar.jpeg"),
+ FILE_PATH_LITERAL("C:\\foo\\bar.jpeg")
+ },
+#else // !defined(OS_WIN)
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/bar.htm"),
+ FILE_PATH_LITERAL("/foo/bar.htm")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/bar.html"),
+ FILE_PATH_LITERAL("/foo/bar.html")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/bar"),
+ FILE_PATH_LITERAL("/foo/bar.html")
+ },
+ {
+ "image/png",
+ FILE_PATH_LITERAL("/bar.html"),
+ FILE_PATH_LITERAL("/bar.html")
+ },
+ {
+ "image/png",
+ FILE_PATH_LITERAL("/bar"),
+ FILE_PATH_LITERAL("/bar.png")
+ },
+ {
+ "image/gif",
+ FILE_PATH_LITERAL("/foo/bar.exe"),
+ FILE_PATH_LITERAL("/foo/bar.exe")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/google.com"),
+ FILE_PATH_LITERAL("/foo/google.com")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/con.htm"),
+ FILE_PATH_LITERAL("/foo/con.htm")
+ },
+ {
+ "text/html",
+ FILE_PATH_LITERAL("/foo/con"),
+ FILE_PATH_LITERAL("/foo/con.html")
+ },
+ // Allow extension synonyms.
+ {
+ "image/jpeg",
+ FILE_PATH_LITERAL("/bar.jpg"),
+ FILE_PATH_LITERAL("/bar.jpg")
+ },
+ {
+ "image/jpeg",
+ FILE_PATH_LITERAL("/bar.jpeg"),
+ FILE_PATH_LITERAL("/bar.jpeg")
+ },
+#endif // !defined(OS_WIN)
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(safe_tests); ++i) {
+ base::FilePath file_path(safe_tests[i].filename);
+ GenerateSafeFileName(safe_tests[i].mime_type, false, &file_path);
+ EXPECT_EQ(safe_tests[i].expected_filename, file_path.value())
+ << "Iteration " << i;
+ }
+}
+
+TEST(FilenameUtilTest, GenerateFileName) {
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+ // This test doesn't run when the locale is not UTF-8 because some of the
+ // string conversions fail. This is OK (we have the default value) but they
+ // don't match our expectations.
+ std::string locale = setlocale(LC_CTYPE, NULL);
+ StringToLowerASCII(&locale);
+ EXPECT_TRUE(locale.find("utf-8") != std::string::npos ||
+ locale.find("utf8") != std::string::npos)
+ << "Your locale (" << locale << ") must be set to UTF-8 "
+ << "for this test to pass!";
+#endif
+
+ // Tests whether the correct filename is selected from the the given
+ // parameters and that Content-Disposition headers are properly
+ // handled including failovers when the header is malformed.
+ const GenerateFilenameCase selection_tests[] = {
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=test.html",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename= \"test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename = \"test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ { // filename is whitespace. Should failover to URL host
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename= ",
+ "",
+ "",
+ "",
+ L"",
+ L"www.google.com"
+ },
+ { // No filename.
+ __LINE__,
+ "http://www.google.com/path/test.html",
+ "attachment",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ { // Ditto
+ __LINE__,
+ "http://www.google.com/path/test.html",
+ "attachment;",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ { // No C-D
+ __LINE__,
+ "http://www.google.com/",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"www.google.com"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/test.html",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ { // Now that we use src/url's ExtractFileName, this case falls back to
+ // the hostname. If this behavior is not desirable, we'd better change
+ // ExtractFileName (in url_parse.cc).
+ __LINE__,
+ "http://www.google.com/path/",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"www.google.com"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/path",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"path"
+ },
+ {
+ __LINE__,
+ "file:///",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"download"
+ },
+ {
+ __LINE__,
+ "file:///path/testfile",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"testfile"
+ },
+ {
+ __LINE__,
+ "non-standard-scheme:",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"download"
+ },
+ { // C-D should override default
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename =\"test.html\"",
+ "",
+ "",
+ "",
+ L"download",
+ L"test.html"
+ },
+ { // But the URL shouldn't
+ __LINE__,
+ "http://www.google.com/",
+ "",
+ "",
+ "",
+ "",
+ L"download",
+ L"download"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"../test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"-test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"..\\test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"..\\\\test.html\"",
+ "",
+ "",
+ "",
+ L"",
+ L"-test.html"
+ },
+ { // Filename disappears after leading and trailing periods are removed.
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"..\"",
+ "",
+ "",
+ "",
+ L"default",
+ L"default"
+ },
+ { // C-D specified filename disappears. Failover to final filename.
+ __LINE__,
+ "http://www.google.com/test.html",
+ "attachment; filename=\"..\"",
+ "",
+ "",
+ "",
+ L"default",
+ L"default"
+ },
+ // Below is a small subset of cases taken from HttpContentDisposition tests.
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"%EC%98%88%EC%88%A0%20"
+ "%EC%98%88%EC%88%A0.jpg\"",
+ "",
+ "",
+ "",
+ L"",
+ L"\uc608\uc220 \uc608\uc220.jpg"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg",
+ "",
+ "",
+ "",
+ "",
+ L"download",
+ L"\uc608\uc220 \uc608\uc220.jpg"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment;",
+ "",
+ "",
+ "",
+ L"\uB2E4\uC6B4\uB85C\uB4DC",
+ L"\uB2E4\uC6B4\uB85C\uB4DC"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/",
+ "attachment; filename=\"=?EUC-JP?Q?=B7=DD=BD="
+ "D13=2Epng?=\"",
+ "",
+ "",
+ "",
+ L"download",
+ L"\u82b8\u88533.png"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/images?id=3",
+ "attachment; filename=caf\xc3\xa9.png",
+ "iso-8859-1",
+ "",
+ "",
+ L"",
+ L"caf\u00e9.png"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/images?id=3",
+ "attachment; filename=caf\xe5.png",
+ "windows-1253",
+ "",
+ "",
+ L"",
+ L"caf\u03b5.png"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/file?id=3",
+ "attachment; name=\xcf\xc2\xd4\xd8.zip",
+ "GBK",
+ "",
+ "",
+ L"",
+ L"\u4e0b\u8f7d.zip"
+ },
+ { // Invalid C-D header. Extracts filename from url.
+ __LINE__,
+ "http://www.google.com/test.html",
+ "attachment; filename==?iiso88591?Q?caf=EG?=",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ // about: and data: URLs
+ {
+ __LINE__,
+ "about:chrome",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"download"
+ },
+ {
+ __LINE__,
+ "data:,looks/like/a.path",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"download"
+ },
+ {
+ __LINE__,
+ "data:text/plain;base64,VG8gYmUgb3Igbm90IHRvIGJlLg=",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"download"
+ },
+ {
+ __LINE__,
+ "data:,looks/like/a.path",
+ "",
+ "",
+ "",
+ "",
+ L"default_filename_is_given",
+ L"default_filename_is_given"
+ },
+ {
+ __LINE__,
+ "data:,looks/like/a.path",
+ "",
+ "",
+ "",
+ "",
+ L"\u65e5\u672c\u8a9e", // Japanese Kanji.
+ L"\u65e5\u672c\u8a9e"
+ },
+ { // The filename encoding is specified by the referrer charset.
+ __LINE__,
+ "http://example.com/V%FDvojov%E1%20psychologie.doc",
+ "",
+ "iso-8859-1",
+ "",
+ "",
+ L"",
+ L"V\u00fdvojov\u00e1 psychologie.doc"
+ },
+ { // Suggested filename takes precedence over URL
+ __LINE__,
+ "http://www.google.com/test",
+ "",
+ "",
+ "suggested",
+ "",
+ L"",
+ L"suggested"
+ },
+ { // The content-disposition has higher precedence over the suggested name.
+ __LINE__,
+ "http://www.google.com/test",
+ "attachment; filename=test.html",
+ "",
+ "suggested",
+ "",
+ L"",
+ L"test.html"
+ },
+#if 0
+ { // The filename encoding doesn't match the referrer charset, the system
+ // charset, or UTF-8.
+ // TODO(jshin): we need to handle this case.
+ __LINE__,
+ "http://example.com/V%FDvojov%E1%20psychologie.doc",
+ "",
+ "utf-8",
+ "",
+ "",
+ L"",
+ L"V\u00fdvojov\u00e1 psychologie.doc",
+ },
+#endif
+ // Raw 8bit characters in C-D
+ {
+ __LINE__,
+ "http://www.example.com/images?id=3",
+ "attachment; filename=caf\xc3\xa9.png",
+ "iso-8859-1",
+ "",
+ "image/png",
+ L"",
+ L"caf\u00e9.png"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/images?id=3",
+ "attachment; filename=caf\xe5.png",
+ "windows-1253",
+ "",
+ "image/png",
+ L"",
+ L"caf\u03b5.png"
+ },
+ { // No 'filename' keyword in the disposition, use the URL
+ __LINE__,
+ "http://www.evil.com/my_download.txt",
+ "a_file_name.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"my_download.txt"
+ },
+ { // Spaces in the disposition file name
+ __LINE__,
+ "http://www.frontpagehacker.com/a_download.exe",
+ "filename=My Downloaded File.exe",
+ "",
+ "",
+ "application/octet-stream",
+ L"download",
+ L"My Downloaded File.exe"
+ },
+ { // % encoded
+ __LINE__,
+ "http://www.examples.com/",
+ "attachment; "
+ "filename=\"%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg\"",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"\uc608\uc220 \uc608\uc220.jpg"
+ },
+ { // name= parameter
+ __LINE__,
+ "http://www.examples.com/q.cgi?id=abc",
+ "attachment; name=abc de.pdf",
+ "",
+ "",
+ "application/octet-stream",
+ L"download",
+ L"abc de.pdf"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/path",
+ "filename=\"=?EUC-JP?Q?=B7=DD=BD=D13=2Epng?=\"",
+ "",
+ "",
+ "image/png",
+ L"download",
+ L"\x82b8\x8853" L"3.png"
+ },
+ { // The following two have invalid CD headers and filenames come from the
+ // URL.
+ __LINE__,
+ "http://www.example.com/test%20123",
+ "attachment; filename==?iiso88591?Q?caf=EG?=",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"test 123" JPEG_EXT
+ },
+ {
+ __LINE__,
+ "http://www.google.com/%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg",
+ "malformed_disposition",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"\uc608\uc220 \uc608\uc220.jpg"
+ },
+ { // Invalid C-D. No filename from URL. Falls back to 'download'.
+ __LINE__,
+ "http://www.google.com/path1/path2/",
+ "attachment; filename==?iso88591?Q?caf=E3?",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"download" JPEG_EXT
+ },
+ };
+
+ // Tests filename generation. Once the correct filename is
+ // selected, they should be passed through the validation steps and
+ // a correct extension should be added if necessary.
+ const GenerateFilenameCase generation_tests[] = {
+ // Dotfiles. Ensures preceeding period(s) stripped.
+ {
+ __LINE__,
+ "http://www.google.com/.test.html",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"test.html"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/.test",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"test"
+ },
+ {
+ __LINE__,
+ "http://www.google.com/..test",
+ "",
+ "",
+ "",
+ "",
+ L"",
+ L"test"
+ },
+ { // Disposition has relative paths, remove directory separators
+ __LINE__,
+ "http://www.evil.com/my_download.txt",
+ "filename=../../../../././../a_file_name.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"-..-..-..-.-.-..-a_file_name.txt"
+ },
+ { // Disposition has parent directories, remove directory separators
+ __LINE__,
+ "http://www.evil.com/my_download.txt",
+ "filename=dir1/dir2/a_file_name.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"dir1-dir2-a_file_name.txt"
+ },
+ { // Disposition has relative paths, remove directory separators
+ __LINE__,
+ "http://www.evil.com/my_download.txt",
+ "filename=..\\..\\..\\..\\.\\.\\..\\a_file_name.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"-..-..-..-.-.-..-a_file_name.txt"
+ },
+ { // Disposition has parent directories, remove directory separators
+ __LINE__,
+ "http://www.evil.com/my_download.txt",
+ "filename=dir1\\dir2\\a_file_name.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"dir1-dir2-a_file_name.txt"
+ },
+ { // No useful information in disposition or URL, use default
+ __LINE__,
+ "http://www.truncated.com/path/",
+ "",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"download" TXT_EXT
+ },
+ { // Filename looks like HTML?
+ __LINE__,
+ "http://www.evil.com/get/malware/here",
+ "filename=\"<blink>Hello kitty</blink>\"",
+ "",
+ "",
+ "text/plain",
+ L"default",
+ L"-blink-Hello kitty--blink-" TXT_EXT
+ },
+ { // A normal avi should get .avi and not .avi.avi
+ __LINE__,
+ "https://blah.google.com/misc/2.avi",
+ "",
+ "",
+ "",
+ "video/x-msvideo",
+ L"download",
+ L"2.avi"
+ },
+ { // Extension generation
+ __LINE__,
+ "http://www.example.com/my-cat",
+ "filename=my-cat",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"my-cat" JPEG_EXT
+ },
+ {
+ __LINE__,
+ "http://www.example.com/my-cat",
+ "filename=my-cat",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"my-cat.txt"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/my-cat",
+ "filename=my-cat",
+ "",
+ "",
+ "text/html",
+ L"download",
+ L"my-cat" HTML_EXT
+ },
+ { // Unknown MIME type
+ __LINE__,
+ "http://www.example.com/my-cat",
+ "filename=my-cat",
+ "",
+ "",
+ "dance/party",
+ L"download",
+ L"my-cat"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/my-cat.jpg",
+ "filename=my-cat.jpg",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"my-cat.jpg"
+ },
+ // Windows specific tests
+#if defined(OS_WIN)
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.exe",
+ "filename=evil.exe",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"evil.exe"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/ok.exe",
+ "filename=ok.exe",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"ok.exe"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.dll",
+ "filename=evil.dll",
+ "",
+ "",
+ "dance/party",
+ L"download",
+ L"evil.dll"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.exe",
+ "filename=evil",
+ "",
+ "",
+ "application/rss+xml",
+ L"download",
+ L"evil"
+ },
+ // Test truncation of trailing dots and spaces
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.exe ",
+ "filename=evil.exe ",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"evil.exe"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.exe.",
+ "filename=evil.exe.",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"evil.exe-"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.exe. . .",
+ "filename=evil.exe. . .",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"evil.exe-------"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/evil.",
+ "filename=evil.",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"evil-"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/. . . . .",
+ "filename=. . . . .",
+ "",
+ "",
+ "binary/octet-stream",
+ L"download",
+ L"download"
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/attachment?name=meh.exe%C2%A0",
+ "attachment; filename=\"meh.exe\xC2\xA0\"",
+ "",
+ "",
+ "binary/octet-stream",
+ L"",
+ L"meh.exe-"
+ },
+#endif // OS_WIN
+ {
+ __LINE__,
+ "http://www.goodguy.com/utils.js",
+ "filename=utils.js",
+ "",
+ "",
+ "application/x-javascript",
+ L"download",
+ L"utils.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/contacts.js",
+ "filename=contacts.js",
+ "",
+ "",
+ "application/json",
+ L"download",
+ L"contacts.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/utils.js",
+ "filename=utils.js",
+ "",
+ "",
+ "text/javascript",
+ L"download",
+ L"utils.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/utils.js",
+ "filename=utils.js",
+ "",
+ "",
+ "text/javascript;version=2",
+ L"download",
+ L"utils.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/utils.js",
+ "filename=utils.js",
+ "",
+ "",
+ "application/ecmascript",
+ L"download",
+ L"utils.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/utils.js",
+ "filename=utils.js",
+ "",
+ "",
+ "application/ecmascript;version=4",
+ L"download",
+ L"utils.js"
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/program.exe",
+ "filename=program.exe",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+ L"program.exe"
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/../foo.txt",
+ "filename=../foo.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"-foo.txt"
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/..\\foo.txt",
+ "filename=..\\foo.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"-foo.txt"
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/.hidden",
+ "filename=.hidden",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"hidden" TXT_EXT
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/trailing.",
+ "filename=trailing.",
+ "",
+ "",
+ "dance/party",
+ L"download",
+#if defined(OS_WIN)
+ L"trailing-"
+#else
+ L"trailing"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/trailing.",
+ "filename=trailing.",
+ "",
+ "",
+ "text/plain",
+ L"download",
+#if defined(OS_WIN)
+ L"trailing-" TXT_EXT
+#else
+ L"trailing" TXT_EXT
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/.",
+ "filename=.",
+ "",
+ "",
+ "dance/party",
+ L"download",
+ L"download"
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/..",
+ "filename=..",
+ "",
+ "",
+ "dance/party",
+ L"download",
+ L"download"
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/...",
+ "filename=...",
+ "",
+ "",
+ "dance/party",
+ L"download",
+ L"download"
+ },
+ { // Note that this one doesn't have "filename=" on it.
+ __LINE__,
+ "http://www.evil.com/",
+ "a_file_name.txt",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"download" JPEG_EXT
+ },
+ {
+ __LINE__,
+ "http://www.evil.com/",
+ "filename=",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"download" JPEG_EXT
+ },
+ {
+ __LINE__,
+ "http://www.example.com/simple",
+ "filename=simple",
+ "",
+ "",
+ "application/octet-stream",
+ L"download",
+ L"simple"
+ },
+ // Reserved words on Windows
+ {
+ __LINE__,
+ "http://www.goodguy.com/COM1",
+ "filename=COM1",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"_COM1"
+#else
+ L"COM1"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/COM4.txt",
+ "filename=COM4.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+#if defined(OS_WIN)
+ L"_COM4.txt"
+#else
+ L"COM4.txt"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/lpt1.TXT",
+ "filename=lpt1.TXT",
+ "",
+ "",
+ "text/plain",
+ L"download",
+#if defined(OS_WIN)
+ L"_lpt1.TXT"
+#else
+ L"lpt1.TXT"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/clock$.txt",
+ "filename=clock$.txt",
+ "",
+ "",
+ "text/plain",
+ L"download",
+#if defined(OS_WIN)
+ L"_clock$.txt"
+#else
+ L"clock$.txt"
+#endif
+ },
+ { // Validation should also apply to sugested name
+ __LINE__,
+ "http://www.goodguy.com/blah$.txt",
+ "filename=clock$.txt",
+ "",
+ "clock$.txt",
+ "text/plain",
+ L"download",
+#if defined(OS_WIN)
+ L"_clock$.txt"
+#else
+ L"clock$.txt"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.goodguy.com/mycom1.foo",
+ "filename=mycom1.foo",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"mycom1.foo"
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/Setup.exe.local",
+ "filename=Setup.exe.local",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"Setup.exe.download"
+#else
+ L"Setup.exe.local"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/Setup.exe.local",
+ "filename=Setup.exe.local.local",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"Setup.exe.local.download"
+#else
+ L"Setup.exe.local.local"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/Setup.exe.lnk",
+ "filename=Setup.exe.lnk",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"Setup.exe.download"
+#else
+ L"Setup.exe.lnk"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/Desktop.ini",
+ "filename=Desktop.ini",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"_Desktop.ini"
+#else
+ L"Desktop.ini"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.badguy.com/Thumbs.db",
+ "filename=Thumbs.db",
+ "",
+ "",
+ "application/foo-bar",
+ L"download",
+#if defined(OS_WIN)
+ L"_Thumbs.db"
+#else
+ L"Thumbs.db"
+#endif
+ },
+ {
+ __LINE__,
+ "http://www.hotmail.com",
+ "filename=source.jpg",
+ "",
+ "",
+ "application/x-javascript",
+ L"download",
+ L"source.jpg"
+ },
+ { // http://crbug.com/5772.
+ __LINE__,
+ "http://www.example.com/foo.tar.gz",
+ "",
+ "",
+ "",
+ "application/x-tar",
+ L"download",
+ L"foo.tar.gz"
+ },
+ { // http://crbug.com/52250.
+ __LINE__,
+ "http://www.example.com/foo.tgz",
+ "",
+ "",
+ "",
+ "application/x-tar",
+ L"download",
+ L"foo.tgz"
+ },
+ { // http://crbug.com/7337.
+ __LINE__,
+ "http://maged.lordaeron.org/blank.reg",
+ "",
+ "",
+ "",
+ "text/x-registry",
+ L"download",
+ L"blank.reg"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/bar.tar",
+ "",
+ "",
+ "",
+ "application/x-tar",
+ L"download",
+ L"bar.tar"
+ },
+ {
+ __LINE__,
+ "http://www.example.com/bar.bogus",
+ "",
+ "",
+ "",
+ "application/x-tar",
+ L"download",
+ L"bar.bogus"
+ },
+ { // http://crbug.com/20337
+ __LINE__,
+ "http://www.example.com/.download.txt",
+ "filename=.download.txt",
+ "",
+ "",
+ "text/plain",
+ L"-download",
+ L"download.txt"
+ },
+ { // http://crbug.com/56855.
+ __LINE__,
+ "http://www.example.com/bar.sh",
+ "",
+ "",
+ "",
+ "application/x-sh",
+ L"download",
+ L"bar.sh"
+ },
+ { // http://crbug.com/61571
+ __LINE__,
+ "http://www.example.com/npdf.php?fn=foobar.pdf",
+ "",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"npdf" TXT_EXT
+ },
+ { // Shouldn't overwrite C-D specified extension.
+ __LINE__,
+ "http://www.example.com/npdf.php?fn=foobar.pdf",
+ "filename=foobar.jpg",
+ "",
+ "",
+ "text/plain",
+ L"download",
+ L"foobar.jpg"
+ },
+ { // http://crbug.com/87719
+ __LINE__,
+ "http://www.example.com/image.aspx?id=blargh",
+ "",
+ "",
+ "",
+ "image/jpeg",
+ L"download",
+ L"image" JPEG_EXT
+ },
+#if defined(OS_CHROMEOS)
+ { // http://crosbug.com/26028
+ __LINE__,
+ "http://www.example.com/fooa%cc%88.txt",
+ "",
+ "",
+ "",
+ "image/jpeg",
+ L"foo\xe4",
+ L"foo\xe4.txt"
+ },
+#endif
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(selection_tests); ++i)
+ RunGenerateFileNameTestCase(&selection_tests[i]);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(generation_tests); ++i)
+ RunGenerateFileNameTestCase(&generation_tests[i]);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(generation_tests); ++i) {
+ GenerateFilenameCase test_case = generation_tests[i];
+ test_case.referrer_charset = "GBK";
+ RunGenerateFileNameTestCase(&test_case);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/base/filename_util_unsafe.cc b/chromium/net/base/filename_util_unsafe.cc
new file mode 100644
index 00000000000..12e80dcf32d
--- /dev/null
+++ b/chromium/net/base/filename_util_unsafe.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 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/base/filename_util_unsafe.h"
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "net/base/filename_util_internal.h"
+
+namespace {
+
+// Local ICU-independent implementation of filename sanitizing functions defined
+// in base/i18n/file_util_icu.h. Does not require ICU because on POSIX systems
+// all international characters are considered legal, so only control and
+// special characters have to be replaced.
+const base::FilePath::CharType illegal_characters[] =
+ FILE_PATH_LITERAL("\"*/:<>?\\\\|\001\002\003\004\005\006\007\010\011\012")
+ FILE_PATH_LITERAL("\013\014\015\016\017\020\021\022\023\024\025\025\027");
+
+void ReplaceIllegalCharactersInPath(base::FilePath::StringType* file_name,
+ char replace_char) {
+ base::ReplaceChars(*file_name,
+ illegal_characters,
+ base::FilePath::StringType(1, replace_char),
+ file_name);
+}
+
+} // namespace
+
+namespace net {
+
+base::FilePath::StringType GenerateFileExtensionUnsafe(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_file_name) {
+ base::FilePath filepath =
+ GenerateFileNameImpl(url,
+ content_disposition,
+ referrer_charset,
+ suggested_name,
+ mime_type,
+ default_file_name,
+ base::Bind(&ReplaceIllegalCharactersInPath));
+ return filepath.Extension();
+}
+
+} // namespace net
diff --git a/chromium/net/base/filename_util_unsafe.h b/chromium/net/base/filename_util_unsafe.h
new file mode 100644
index 00000000000..5ca7d7bbf8f
--- /dev/null
+++ b/chromium/net/base/filename_util_unsafe.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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_BASE_FILENAME_UTIL_UNSAFE_H_
+#define NET_BASE_FILENAME_UTIL_UNSAFE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "net/base/net_export.h"
+
+class GURL;
+
+namespace net {
+
+// Extract extension from FilePath generated by GenerateFileName(), but without
+// replacing illegal characters. Does not depend on ICU.
+NET_EXPORT base::FilePath::StringType GenerateFileExtensionUnsafe(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& referrer_charset,
+ const std::string& suggested_name,
+ const std::string& mime_type,
+ const std::string& default_name);
+
+} // namespace net
+
+#endif // NET_BASE_FILENAME_UTIL_UNSAFE_H_
diff --git a/chromium/net/base/host_mapping_rules.cc b/chromium/net/base/host_mapping_rules.cc
index cd82f513286..6ae475458b0 100644
--- a/chromium/net/base/host_mapping_rules.cc
+++ b/chromium/net/base/host_mapping_rules.cc
@@ -67,7 +67,7 @@ bool HostMappingRules::RewriteHost(HostPortPair* host_port) const {
bool HostMappingRules::AddRuleFromString(const std::string& rule_string) {
std::string trimmed;
- TrimWhitespaceASCII(rule_string, TRIM_ALL, &trimmed);
+ base::TrimWhitespaceASCII(rule_string, base::TRIM_ALL, &trimmed);
std::vector<std::string> parts;
base::SplitString(trimmed, ' ', &parts);
diff --git a/chromium/net/base/host_mapping_rules.h b/chromium/net/base/host_mapping_rules.h
index aa0fb77d85e..e1d2cd29d0a 100644
--- a/chromium/net/base/host_mapping_rules.h
+++ b/chromium/net/base/host_mapping_rules.h
@@ -19,8 +19,8 @@ class NET_EXPORT_PRIVATE HostMappingRules {
HostMappingRules();
~HostMappingRules();
- // Modifies |*host_port| based on the current rules. Returns true if the
- // RequestInfo was modified, false otherwise.
+ // Modifies |*host_port| based on the current rules. Returns true if
+ // |*host_port| was modified, false otherwise.
bool RewriteHost(HostPortPair* host_port) const;
// Adds a rule to this mapper. The format of the rule can be one of:
diff --git a/chromium/net/base/host_port_pair.cc b/chromium/net/base/host_port_pair.cc
index bc23a9a8621..6675692123a 100644
--- a/chromium/net/base/host_port_pair.cc
+++ b/chromium/net/base/host_port_pair.cc
@@ -4,6 +4,7 @@
#include "net/base/host_port_pair.h"
+#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -47,6 +48,15 @@ std::string HostPortPair::ToString() const {
}
std::string HostPortPair::HostForURL() const {
+ // TODO(rtenneti): Add support for |host| to have '\0'.
+ if (host_.find('\0') != std::string::npos) {
+ std::string host_for_log(host_);
+ size_t nullpos;
+ while ((nullpos = host_for_log.find('\0')) != std::string::npos) {
+ host_for_log.replace(nullpos, 1, "%00");
+ }
+ LOG(DFATAL) << "Host has a null char: " << host_for_log;
+ }
// Check to see if the host is an IPv6 address. If so, added brackets.
if (host_.find(':') != std::string::npos) {
DCHECK_NE(host_[0], '[');
diff --git a/chromium/net/base/host_port_pair_unittest.cc b/chromium/net/base/host_port_pair_unittest.cc
index d654622dd00..5b15db9477d 100644
--- a/chromium/net/base/host_port_pair_unittest.cc
+++ b/chromium/net/base/host_port_pair_unittest.cc
@@ -4,15 +4,33 @@
#include "net/base/host_port_pair.h"
+#include "base/logging.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
+using std::string;
+
namespace net {
namespace {
+struct TestData {
+ string host;
+ uint16 port;
+ string to_string;
+ string host_for_url;
+} tests[] = {
+ { "www.google.com", 80, "www.google.com:80", "www.google.com" },
+ { "www.google.com", 443, "www.google.com:443", "www.google.com" },
+ { "127.0.0.1", 80, "127.0.0.1:80", "127.0.0.1" },
+ { "192.168.1.1", 80, "192.168.1.1:80", "192.168.1.1" },
+ { "::1", 80, "[::1]:80", "[::1]" },
+ { "2001:db8::42", 80, "[2001:db8::42]:80", "[2001:db8::42]" },
+};
+
TEST(HostPortPairTest, Parsing) {
HostPortPair foo("foo.com", 10);
- std::string foo_str = foo.ToString();
+ string foo_str = foo.ToString();
EXPECT_EQ("foo.com:10", foo_str);
HostPortPair bar = HostPortPair::FromString(foo_str);
EXPECT_TRUE(foo.Equals(bar));
@@ -35,6 +53,63 @@ TEST(HostPortPairTest, Emptiness) {
EXPECT_FALSE(foo.IsEmpty());
}
+TEST(HostPortPairTest, ToString) {
+ for (size_t index = 0; index < arraysize(tests); ++index) {
+ HostPortPair foo(tests[index].host, tests[index].port);
+ EXPECT_EQ(tests[index].to_string, foo.ToString());
+ }
+
+ // Test empty hostname.
+ HostPortPair foo(string(), 10);
+}
+
+TEST(HostPortPairTest, HostForURL) {
+ for (size_t index = 0; index < arraysize(tests); ++index) {
+ HostPortPair foo(tests[index].host, tests[index].port);
+ EXPECT_EQ(tests[index].host_for_url, foo.HostForURL());
+ }
+
+ // Test hostname with null character.
+ string bar_hostname("a\0.\0com", 7);
+ HostPortPair bar(bar_hostname, 80);
+ string expected_error("Host has a null char: a%00.%00com");
+ EXPECT_DFATAL(bar.HostForURL(), expected_error);
+}
+
+TEST(HostPortPairTest, LessThan) {
+ HostPortPair a_10("a.com", 10);
+ HostPortPair a_11("a.com", 11);
+ HostPortPair b_10("b.com", 10);
+ HostPortPair b_11("b.com", 11);
+
+ EXPECT_FALSE(a_10 < a_10);
+ EXPECT_TRUE(a_10 < a_11);
+ EXPECT_TRUE(a_10 < b_10);
+ EXPECT_TRUE(a_10 < b_11);
+
+ EXPECT_FALSE(a_11 < a_10);
+ EXPECT_FALSE(a_11 < b_10);
+
+ EXPECT_FALSE(b_10 < a_10);
+ EXPECT_TRUE(b_10 < a_11);
+
+ EXPECT_FALSE(b_11 < a_10);
+}
+
+TEST(HostPortPairTest, Equals) {
+ HostPortPair a_10("a.com", 10);
+ HostPortPair a_11("a.com", 11);
+ HostPortPair b_10("b.com", 10);
+ HostPortPair b_11("b.com", 11);
+
+ HostPortPair new_a_10("a.com", 10);
+
+ EXPECT_TRUE(new_a_10.Equals(a_10));
+ EXPECT_FALSE(new_a_10.Equals(a_11));
+ EXPECT_FALSE(new_a_10.Equals(b_10));
+ EXPECT_FALSE(new_a_10.Equals(b_11));
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/base/int128.cc b/chromium/net/base/int128.cc
index 745d8ed6e0c..94275eb6ecb 100644
--- a/chromium/net/base/int128.cc
+++ b/chromium/net/base/int128.cc
@@ -7,8 +7,8 @@
#include "net/base/int128.h"
const uint128_pod kuint128max = {
- static_cast<uint64>(GG_LONGLONG(0xFFFFFFFFFFFFFFFF)),
- static_cast<uint64>(GG_LONGLONG(0xFFFFFFFFFFFFFFFF))
+ static_cast<uint64>(0xFFFFFFFFFFFFFFFFULL),
+ static_cast<uint64>(0xFFFFFFFFFFFFFFFFULL)
};
std::ostream& operator<<(std::ostream& o, const uint128& b) {
diff --git a/chromium/net/base/int128_unittest.cc b/chromium/net/base/int128_unittest.cc
index 78790e765fd..957038bffd3 100644
--- a/chromium/net/base/int128_unittest.cc
+++ b/chromium/net/base/int128_unittest.cc
@@ -230,24 +230,18 @@ TEST(Int128, Multiply) {
}
// Verified with dc.
- a = uint128(GG_ULONGLONG(0xffffeeeeddddcccc),
- GG_ULONGLONG(0xbbbbaaaa99998888));
- b = uint128(GG_ULONGLONG(0x7777666655554444),
- GG_ULONGLONG(0x3333222211110000));
+ a = uint128(0xffffeeeeddddccccULL, 0xbbbbaaaa99998888ULL);
+ b = uint128(0x7777666655554444ULL, 0x3333222211110000ULL);
c = a * b;
- EXPECT_EQ(uint128(GG_ULONGLONG(0x530EDA741C71D4C3),
- GG_ULONGLONG(0xBF25975319080000)), c);
+ EXPECT_EQ(uint128(0x530EDA741C71D4C3ULL, 0xBF25975319080000ULL), c);
EXPECT_EQ(0, c - b * a);
EXPECT_EQ(a*a - b*b, (a+b) * (a-b));
// Verified with dc.
- a = uint128(GG_ULONGLONG(0x0123456789abcdef),
- GG_ULONGLONG(0xfedcba9876543210));
- b = uint128(GG_ULONGLONG(0x02468ace13579bdf),
- GG_ULONGLONG(0xfdb97531eca86420));
+ a = uint128(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+ b = uint128(0x02468ace13579bdfULL, 0xfdb97531eca86420ULL);
c = a * b;
- EXPECT_EQ(uint128(GG_ULONGLONG(0x97a87f4f261ba3f2),
- GG_ULONGLONG(0x342d0bbf48948200)), c);
+ EXPECT_EQ(uint128(0x97a87f4f261ba3f2ULL, 0x342d0bbf48948200ULL), c);
EXPECT_EQ(0, c - b * a);
EXPECT_EQ(a*a - b*b, (a+b) * (a-b));
}
diff --git a/chromium/net/base/io_buffer.h b/chromium/net/base/io_buffer.h
index 77712e7e7af..0ce52e8d79c 100644
--- a/chromium/net/base/io_buffer.h
+++ b/chromium/net/base/io_buffer.h
@@ -203,7 +203,7 @@ class NET_EXPORT GrowableIOBuffer : public IOBuffer {
private:
virtual ~GrowableIOBuffer();
- scoped_ptr_malloc<char> real_data_;
+ scoped_ptr<char, base::FreeDeleter> real_data_;
int capacity_;
int offset_;
};
diff --git a/chromium/net/base/ip_endpoint.h b/chromium/net/base/ip_endpoint.h
index 1fc7e0420b8..5e5d9c19684 100644
--- a/chromium/net/base/ip_endpoint.h
+++ b/chromium/net/base/ip_endpoint.h
@@ -23,7 +23,7 @@ namespace net {
class NET_EXPORT IPEndPoint {
public:
IPEndPoint();
- virtual ~IPEndPoint();
+ ~IPEndPoint();
IPEndPoint(const IPAddressNumber& address, int port);
IPEndPoint(const IPEndPoint& endpoint);
diff --git a/chromium/net/base/ip_pattern.cc b/chromium/net/base/ip_pattern.cc
new file mode 100644
index 00000000000..f67d3a0ba64
--- /dev/null
+++ b/chromium/net/base/ip_pattern.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 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/base/ip_pattern.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+
+namespace net {
+
+class IPPattern::ComponentPattern {
+ public:
+ ComponentPattern();
+ void AppendRange(uint32 min, uint32 max);
+ bool Match(uint32 value) const;
+
+ private:
+ struct Range {
+ public:
+ Range(uint32 min, uint32 max) : minimum(min), maximum(max) {}
+ uint32 minimum;
+ uint32 maximum;
+ };
+ typedef std::vector<Range> RangeVector;
+
+ RangeVector ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(ComponentPattern);
+};
+
+IPPattern::ComponentPattern::ComponentPattern() {}
+
+void IPPattern::ComponentPattern::AppendRange(uint32 min, uint32 max) {
+ ranges_.push_back(Range(min, max));
+}
+
+bool IPPattern::ComponentPattern::Match(uint32 value) const {
+ // Simple linear search should be fine, as we usually only have very few
+ // distinct ranges to test.
+ for (RangeVector::const_iterator range_it = ranges_.begin();
+ range_it != ranges_.end(); ++range_it) {
+ if (range_it->maximum >= value && range_it->minimum <= value)
+ return true;
+ }
+ return false;
+}
+
+IPPattern::IPPattern() : is_ipv4_(true) {}
+
+IPPattern::~IPPattern() {
+ STLDeleteElements(&component_patterns_);
+}
+
+bool IPPattern::Match(const IPAddressNumber& address) const {
+ if (ip_mask_.empty())
+ return false;
+ bool address_is_ipv4 = address.size() == kIPv4AddressSize;
+ if (address_is_ipv4 != is_ipv4_)
+ return false;
+
+ ComponentPatternList::const_iterator pattern_it(component_patterns_.begin());
+ int fixed_value_index = 0;
+ // IPv6 |address| vectors have 16 pieces, while our |ip_mask_| has only
+ // 8, so it is easier to count separately.
+ int address_index = 0;
+ for (size_t i = 0; i < ip_mask_.size(); ++i) {
+ uint32 value_to_test = address[address_index++];
+ if (!is_ipv4_) {
+ value_to_test = (value_to_test << 8) + address[address_index++];
+ }
+ if (ip_mask_[i]) {
+ if (component_values_[fixed_value_index++] != value_to_test)
+ return false;
+ continue;
+ }
+ if (!(*pattern_it)->Match(value_to_test))
+ return false;
+ ++pattern_it;
+ }
+ return true;
+}
+
+bool IPPattern::ParsePattern(const std::string& ip_pattern) {
+ DCHECK(ip_mask_.empty());
+ if (ip_pattern.find(':') != std::string::npos) {
+ is_ipv4_ = false;
+ }
+ Strings components;
+ base::SplitString(ip_pattern, is_ipv4_ ? '.' : ':', &components);
+ if (components.size() != (is_ipv4_ ? 4u : 8u)) {
+ DVLOG(1) << "Invalid component count: " << ip_pattern;
+ return false;
+ }
+ for (Strings::iterator component_it = components.begin();
+ component_it != components.end(); ++component_it) {
+ if (component_it->empty()) {
+ DVLOG(1) << "Empty component: " << ip_pattern;
+ return false;
+ }
+ if (*component_it == "*") {
+ // Let standard code handle this below.
+ *component_it = is_ipv4_ ? "[0-255]" : "[0-FFFF]";
+ } else if ((*component_it)[0] != '[') {
+ // This value will just have a specific integer to match.
+ uint32 value;
+ if (!ValueTextToInt(*component_it, &value))
+ return false;
+ ip_mask_.push_back(true);
+ component_values_.push_back(value);
+ continue;
+ }
+ if ((*component_it)[component_it->size() - 1] != ']') {
+ DVLOG(1) << "Missing close bracket: " << ip_pattern;
+ return false;
+ }
+ // Now we know the size() is at least 2.
+ if (component_it->size() == 2) {
+ DVLOG(1) << "Empty bracket: " << ip_pattern;
+ return false;
+ }
+ // We'll need a pattern to match this bracketed component.
+ scoped_ptr<ComponentPattern> component_pattern(new ComponentPattern);
+ // Trim leading and trailing bracket before calling for parsing.
+ if (!ParseComponentPattern(base::StringPiece(component_it->data() + 1,
+ component_it->size() - 2), component_pattern.get())) {
+ return false;
+ }
+ ip_mask_.push_back(false);
+ component_patterns_.push_back(component_pattern.release());
+ }
+ return true;
+}
+
+bool IPPattern::ParseComponentPattern(const base::StringPiece& text,
+ ComponentPattern* pattern) const {
+ // We're given a comma separated set of ranges, some of which may be simple
+ // constants.
+ Strings ranges;
+ base::SplitString(text.as_string(), ',', &ranges);
+ for (Strings::iterator range_it = ranges.begin();
+ range_it != ranges.end(); ++range_it) {
+ base::StringTokenizer range_pair(*range_it, "-");
+ uint32 min = 0;
+ range_pair.GetNext();
+ if (!ValueTextToInt(range_pair.token(), &min))
+ return false;
+ uint32 max = min; // Sometimes we have no distinct max.
+ if (range_pair.GetNext()) {
+ if (!ValueTextToInt(range_pair.token(), &max))
+ return false;
+ }
+ if (range_pair.GetNext()) {
+ // Too many "-" in this range specifier.
+ DVLOG(1) << "Too many hyphens in range: ";
+ return false;
+ }
+ pattern->AppendRange(min, max);
+ }
+ return true;
+}
+
+bool IPPattern::ValueTextToInt(const base::StringPiece& input,
+ uint32* output) const {
+ bool ok = is_ipv4_ ? base::StringToUint(input, output) :
+ base::HexStringToUInt(input, output);
+ if (!ok) {
+ DVLOG(1) << "Could not convert value to number: " << input;
+ return false;
+ }
+ if (is_ipv4_ && *output > 255u) {
+ DVLOG(1) << "IPv4 component greater than 255";
+ return false;
+ }
+ if (!is_ipv4_ && *output > 0xFFFFu) {
+ DVLOG(1) << "IPv6 component greater than 0xFFFF";
+ return false;
+ }
+ return ok;
+}
+
+} // namespace net
diff --git a/chromium/net/base/ip_pattern.h b/chromium/net/base/ip_pattern.h
new file mode 100644
index 00000000000..bf1d45583d3
--- /dev/null
+++ b/chromium/net/base/ip_pattern.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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_BASE_IP_PATTERN_H_
+#define NET_BASE_IP_PATTERN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/base/net_util.h"
+
+namespace net {
+
+// IPPatterns are used to match IP address resolutions for possible augmentation
+// by a MappedIPResolver, which uses IPMappingRules.
+class NET_EXPORT IPPattern {
+ public:
+ IPPattern();
+ ~IPPattern();
+
+ // Parse a textual pattern (IP_PATTERN as defined in ip_mapping_rules.h) into
+ // |this| and return true if the parsing was successful.
+ bool ParsePattern(const std::string& ip_pattern);
+ // Test to see if the current pattern in |this| matches the given |address|
+ // and return true if it matches.
+ bool Match(const IPAddressNumber& address) const;
+
+ bool is_ipv4() const { return is_ipv4_; }
+
+ private:
+ class ComponentPattern;
+ typedef std::vector<std::string> Strings;
+ typedef std::vector<ComponentPattern*> ComponentPatternList;
+
+ // IPv6 addresses have 8 components, while IPv4 addresses have 4 components.
+ // ComponentPattern is used to define patterns to match individual components.
+ bool ParseComponentPattern(const base::StringPiece& text,
+ ComponentPattern* pattern) const;
+ // Convert IP component to an int. Assume hex vs decimal for IPV6 vs V4.
+ bool ValueTextToInt(const base::StringPiece& input, uint32* output) const;
+
+ bool is_ipv4_;
+ // The |ip_mask_| indicates, for each component, if this pattern requires an
+ // exact match (OCTET in IPv4, or 4 hex digits in IPv6).
+ // For each true element there is an entry in |component_values_|, and false
+ // means that an entry from our list of ComponentPattern instances must be
+ // applied.
+ std::vector<bool> ip_mask_;
+ // The vector of fixed values that are requried.
+ // Other values may be restricted by the component_patterns_;
+ // The class invariant is:
+ // ip_mask_.size() == component_patterns_.size()
+ // + size(our ComponentPattern list)
+ std::vector<uint32> component_values_;
+ // If only one component position was specified using a range, then this
+ // list will only have 1 element (i.e., we only have patterns for each element
+ // of ip_mask_ that is false.)
+ // We own these elements, and need to destroy them all when we are destroyed.
+ ComponentPatternList component_patterns_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPPattern);
+};
+
+} // namespace net
+
+#endif // NET_BASE_IP_PATTERN_H_
diff --git a/chromium/net/base/ip_pattern_unittest.cc b/chromium/net/base/ip_pattern_unittest.cc
new file mode 100644
index 00000000000..d1bbdb286ad
--- /dev/null
+++ b/chromium/net/base/ip_pattern_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 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/base/ip_pattern.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+bool IsValidPattern(const std::string& pattern_text) {
+ IPPattern pattern;
+ return pattern.ParsePattern(pattern_text);
+}
+
+bool CheckForMatch(const IPPattern& pattern, std::string address_text) {
+ IPAddressNumber address;
+ EXPECT_TRUE(ParseIPLiteralToNumber(address_text, &address));
+ return pattern.Match(address);
+}
+
+TEST(IPPatternTest, EmptyPattern) {
+ IPPattern pattern;
+ IPAddressNumber ipv4_address1;
+ EXPECT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ipv4_address1));
+ IPAddressNumber ipv6_address1;
+ EXPECT_TRUE(ParseIPLiteralToNumber("1:2:3:4:5:6:7:8", &ipv6_address1));
+
+ EXPECT_FALSE(pattern.Match(ipv4_address1));
+ EXPECT_FALSE(pattern.Match(ipv6_address1));
+}
+
+TEST(IPPatternTest, PerfectMatchPattern) {
+ IPPattern pattern_v4;
+ std::string ipv4_text1("1.2.3.4");
+ EXPECT_TRUE(pattern_v4.ParsePattern(ipv4_text1));
+ EXPECT_TRUE(pattern_v4.is_ipv4());
+ EXPECT_TRUE(CheckForMatch(pattern_v4, ipv4_text1));
+
+ IPPattern pattern_v6;
+ std::string ipv6_text1("1:2:3:4:5:6:7:8");
+ EXPECT_TRUE(pattern_v6.ParsePattern(ipv6_text1));
+ EXPECT_FALSE(pattern_v6.is_ipv4());
+ EXPECT_TRUE(CheckForMatch(pattern_v6, ipv6_text1));
+
+ // Also check that there is no confusion betwene v6 and v4, despite having
+ // similar values in some sense.
+ EXPECT_FALSE(CheckForMatch(pattern_v4, ipv6_text1));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, ipv4_text1));
+}
+
+TEST(IPPatternTest, AlternativeMatchPattern) {
+ IPPattern pattern_v4;
+ EXPECT_TRUE(pattern_v4.ParsePattern("1.2.[3,5].4"));
+ EXPECT_TRUE(pattern_v4.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.2.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.3.4"));
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.4.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.5.4"));
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.6.4"));
+
+ IPPattern pattern_v6;
+ EXPECT_TRUE(pattern_v6.ParsePattern("1:2fab:3:4:5:[6,8]:7:8"));
+ EXPECT_FALSE(pattern_v6.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:5:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:6:7:8"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:7:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:8:7:8"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:9:7:8"));
+}
+
+TEST(IPPatternTest, RangeMatchPattern) {
+ IPPattern pattern_v4;
+ EXPECT_TRUE(pattern_v4.ParsePattern("1.2.[3-5].4"));
+ EXPECT_TRUE(pattern_v4.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.2.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.3.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.4.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.5.4"));
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.6.4"));
+
+ IPPattern pattern_v6;
+ EXPECT_TRUE(pattern_v6.ParsePattern("1:2fab:3:4:5:[6-8]:7:8"));
+ EXPECT_FALSE(pattern_v6.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:5:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:6:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:7:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:8:7:8"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:9:7:8"));
+}
+
+TEST(IPPatternTest, WildCardMatchPattern) {
+ // Use two ranges, and check that only getting both right is a match.
+ IPPattern pattern_v4;
+ EXPECT_TRUE(pattern_v4.ParsePattern("1.2.*.4"));
+ EXPECT_TRUE(pattern_v4.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.2.255"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.255.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.0.4"));
+
+ IPPattern pattern_v6;
+ EXPECT_TRUE(pattern_v6.ParsePattern("1:2fab:3:4:5:*:7:8"));
+ EXPECT_FALSE(pattern_v6.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:5:7:8888"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:FFFF:7:8"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:9999:7:8"));
+}
+
+TEST(IPPatternTest, MultiRangeMatchPattern) {
+ // Use two ranges, and check that only getting both right is a match.
+ // This ensures that the right component range is matched against the desired
+ // component.
+ IPPattern pattern_v4;
+ EXPECT_TRUE(pattern_v4.ParsePattern("1.[2-3].3.[4-5]"));
+ EXPECT_TRUE(pattern_v4.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.4.3.6"));
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.2.3.6"));
+ EXPECT_FALSE(CheckForMatch(pattern_v4, "1.4.3.4"));
+ EXPECT_TRUE(CheckForMatch(pattern_v4, "1.2.3.4"));
+
+ IPPattern pattern_v6;
+ EXPECT_TRUE(pattern_v6.ParsePattern("1:2fab:3:4:[5-7]:6:7:[8-A]"));
+ EXPECT_FALSE(pattern_v6.is_ipv4());
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:4:5:7:F"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:5:7:F"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6, "1:2fab:3:4:4:6:7:A"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6, "1:2fab:3:4:5:6:7:A"));
+}
+
+TEST(IPPatternTest, BytoOrderInIPv6Ranges) {
+ IPPattern pattern_v6_low_byte;
+ EXPECT_TRUE(pattern_v6_low_byte.ParsePattern("1:2:3:4:5:6:7:[0-FF]"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6_low_byte, "1:2:3:4:5:6:7:0088"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6_low_byte, "1:2:3:4:5:6:7:8800"));
+
+ IPPattern pattern_v6_high_byte;
+ EXPECT_TRUE(pattern_v6_high_byte.ParsePattern("1:2:3:4:5:6:7:[0-FF00]"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6_high_byte, "1:2:3:4:5:6:7:0088"));
+ EXPECT_TRUE(CheckForMatch(pattern_v6_high_byte, "1:2:3:4:5:6:7:FF00"));
+ EXPECT_FALSE(CheckForMatch(pattern_v6_high_byte, "1:2:3:4:5:6:7:FF01"));
+}
+
+TEST(IPPatternTest, InvalidPatterns) {
+ EXPECT_FALSE(IsValidPattern("1:2:3:4:5:6:7:8:9")); // Too long.
+ EXPECT_FALSE(IsValidPattern("1:2:3:4:5:6:7")); // Too Short
+ EXPECT_FALSE(IsValidPattern("1:2:3:4:5:6:7:H")); // Non-hex.
+ EXPECT_FALSE(IsValidPattern("1:G:3:4:5:6:7:8")); // Non-hex.
+
+ EXPECT_FALSE(IsValidPattern("1.2.3.4.5")); // Too long
+ EXPECT_FALSE(IsValidPattern("1.2.3")); // Too short
+ EXPECT_FALSE(IsValidPattern("1.2.3.A")); // Non-decimal.
+ EXPECT_FALSE(IsValidPattern("1.A.3.4")); // Non-decimal
+ EXPECT_FALSE(IsValidPattern("1.256.3.4")); // Out of range
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/base/keygen_handler_unittest.cc b/chromium/net/base/keygen_handler_unittest.cc
index 5188d9d745c..85c3844c067 100644
--- a/chromium/net/base/keygen_handler_unittest.cc
+++ b/chromium/net/base/keygen_handler_unittest.cc
@@ -31,8 +31,8 @@ class KeygenHandlerTest : public ::testing::Test {
virtual ~KeygenHandlerTest() {}
virtual void SetUp() {
-#if defined(OS_CHROMEOS)
- crypto::OpenPersistentNSSDB();
+#if defined(OS_CHROMEOS) && defined(USE_NSS)
+ crypto::OpenPersistentNSSDB();
#endif
}
};
diff --git a/chromium/net/base/keygen_handler_win.cc b/chromium/net/base/keygen_handler_win.cc
index 59d90e8b40c..59dc69de54c 100644
--- a/chromium/net/base/keygen_handler_win.cc
+++ b/chromium/net/base/keygen_handler_win.cc
@@ -64,7 +64,7 @@ bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
const std::string& challenge,
std::string* output) {
- std::wstring wide_challenge = ASCIIToWide(challenge);
+ std::wstring wide_challenge = base::ASCIIToWide(challenge);
std::vector<BYTE> spki;
if (!GetSubjectPublicKeyInfo(prov, &spki))
diff --git a/chromium/net/base/load_timing_info.h b/chromium/net/base/load_timing_info.h
index b4acd58b255..00dbabf55dc 100644
--- a/chromium/net/base/load_timing_info.h
+++ b/chromium/net/base/load_timing_info.h
@@ -40,8 +40,8 @@ namespace net {
// Times represent when a request starts/stops blocking on an event, not the
// time the events actually occurred. In particular, in the case of preconnects
// and socket reuse, no time may be spent blocking on establishing a connection.
-// In the case of SPDY and HTTP pipelining, PAC scripts are only run once for
-// each shared session, so no time may be spent blocking on them.
+// In the case of SPDY, PAC scripts are only run once for each shared session,
+// so no time may be spent blocking on them.
//
// DNS and SSL times are both times for the host, not the proxy, so DNS times
// when using proxies are null, and only requests to HTTPS hosts (Not proxies)
diff --git a/chromium/net/base/mime_util.cc b/chromium/net/base/mime_util.cc
index c68965c3133..9b02c4c18d0 100644
--- a/chromium/net/base/mime_util.cc
+++ b/chromium/net/base/mime_util.cc
@@ -16,6 +16,7 @@
#include "base/strings/utf_string_conversions.h"
#include "net/base/mime_util.h"
#include "net/base/platform_mime_util.h"
+#include "net/http/http_util.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
@@ -69,7 +70,11 @@ class MimeUtil : public PlatformMimeUtil {
bool MatchesMimeType(const std::string &mime_type_pattern,
const std::string &mime_type) const;
- bool IsMimeType(const std::string& type_string) const;
+ bool ParseMimeTypeWithoutParameter(const std::string& type_string,
+ std::string* top_level_type,
+ std::string* subtype) const;
+
+ bool IsValidTopLevelMimeType(const std::string& type_string) const;
bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) const;
@@ -158,6 +163,7 @@ static const MimeInfo secondary_mappings[] = {
{ "image/tiff", "tiff,tif" },
{ "image/x-xbitmap", "xbm" },
{ "image/svg+xml", "svg,svgz" },
+ { "image/x-png", "png"},
{ "message/rfc822", "eml" },
{ "text/plain", "txt,text" },
{ "text/html", "ehtml" },
@@ -260,7 +266,8 @@ static const char* const supported_image_types[] = {
"image/bmp",
"image/vnd.microsoft.icon", // ico
"image/x-icon", // ico
- "image/x-xbitmap" // xbm
+ "image/x-xbitmap", // xbm
+ "image/x-png"
};
// A list of media types: http://en.wikipedia.org/wiki/Internet_media_type
@@ -281,6 +288,12 @@ static const char* const common_media_types[] = {
// Wav.
"audio/wav",
"audio/x-wav",
+
+#if defined(OS_ANDROID)
+ // HLS. Supported by Android ICS and above.
+ "application/vnd.apple.mpegurl",
+ "application/x-mpegurl",
+#endif
};
// List of proprietary types only supported by Google Chrome.
@@ -405,6 +418,10 @@ static const char* const supported_javascript_types[] = {
#if defined(OS_ANDROID)
static bool IsCodecSupportedOnAndroid(const std::string& codec) {
+ // Theora is not supported in Android
+ if (!codec.compare("theora"))
+ return false;
+
// VP9 is supported only in KitKat+ (API Level 19).
if ((!codec.compare("vp9") || !codec.compare("vp9.0")) &&
base::android::BuildInfo::GetInstance()->sdk_int() < 19) {
@@ -418,6 +435,16 @@ static bool IsCodecSupportedOnAndroid(const std::string& codec) {
}
return true;
}
+
+static bool IsMimeTypeSupportedOnAndroid(const std::string& mimeType) {
+ // HLS codecs are supported in ICS and above (API level 14)
+ if ((!mimeType.compare("application/vnd.apple.mpegurl") ||
+ !mimeType.compare("application/x-mpegurl")) &&
+ base::android::BuildInfo::GetInstance()->sdk_int() < 14) {
+ return false;
+ }
+ return true;
+}
#endif
struct MediaFormatStrict {
@@ -428,7 +455,19 @@ struct MediaFormatStrict {
static const MediaFormatStrict format_codec_mappings[] = {
{ "video/webm", "opus,vorbis,vp8,vp8.0,vp9,vp9.0" },
{ "audio/webm", "opus,vorbis" },
- { "audio/wav", "1" }
+ { "audio/wav", "1" },
+ { "audio/x-wav", "1" },
+ { "video/ogg", "opus,theora,vorbis" },
+ { "audio/ogg", "opus,vorbis" },
+ { "application/ogg", "opus,theora,vorbis" },
+ { "audio/mpeg", ",mp3" }, // Note: The comma before the 'mp3'results in an
+ // empty string codec ID and indicates
+ // a missing codecs= parameter is also valid.
+ // The presense of 'mp3' is not RFC compliant,
+ // but is common in the wild so it is a defacto
+ // standard.
+ { "audio/mp3", "" },
+ { "audio/x-mp3", "" }
};
MimeUtil::MimeUtil() {
@@ -438,11 +477,22 @@ MimeUtil::MimeUtil() {
// static
bool MimeUtil::AreSupportedCodecs(const MimeMappings& supported_codecs,
const std::vector<std::string>& codecs) {
+ if (supported_codecs.empty())
+ return codecs.empty();
+
+ // If no codecs are specified in the mimetype, check to see if a missing
+ // codecs parameter is allowed.
+ if (codecs.empty())
+ return supported_codecs.find(std::string()) != supported_codecs.end();
+
for (size_t i = 0; i < codecs.size(); ++i) {
- if (supported_codecs.find(codecs[i]) == supported_codecs.end())
+ if (codecs[i].empty() ||
+ supported_codecs.find(codecs[i]) == supported_codecs.end()) {
return false;
+ }
}
- return !codecs.empty();
+
+ return true;
}
void MimeUtil::InitializeMimeTypeMaps() {
@@ -458,16 +508,26 @@ void MimeUtil::InitializeMimeTypeMaps() {
unsupported_text_map_.insert(unsupported_text_types[i]);
for (size_t i = 0; i < arraysize(supported_javascript_types); ++i)
non_image_map_.insert(supported_javascript_types[i]);
- for (size_t i = 0; i < arraysize(common_media_types); ++i)
+ for (size_t i = 0; i < arraysize(common_media_types); ++i) {
+#if defined(OS_ANDROID)
+ if (!IsMimeTypeSupportedOnAndroid(common_media_types[i]))
+ continue;
+#endif
non_image_map_.insert(common_media_types[i]);
+ }
#if defined(USE_PROPRIETARY_CODECS)
for (size_t i = 0; i < arraysize(proprietary_media_types); ++i)
non_image_map_.insert(proprietary_media_types[i]);
#endif
// Initialize the supported media types.
- for (size_t i = 0; i < arraysize(common_media_types); ++i)
+ for (size_t i = 0; i < arraysize(common_media_types); ++i) {
+#if defined(OS_ANDROID)
+ if (!IsMimeTypeSupportedOnAndroid(common_media_types[i]))
+ continue;
+#endif
media_map_.insert(common_media_types[i]);
+ }
#if defined(USE_PROPRIETARY_CODECS)
for (size_t i = 0; i < arraysize(proprietary_media_types); ++i)
media_map_.insert(proprietary_media_types[i]);
@@ -518,7 +578,9 @@ bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
bool MimeUtil::IsSupportedNonImageMimeType(const std::string& mime_type) const {
return non_image_map_.find(mime_type) != non_image_map_.end() ||
(mime_type.compare(0, 5, "text/") == 0 &&
- !IsUnsupportedTextMimeType(mime_type));
+ !IsUnsupportedTextMimeType(mime_type)) ||
+ (mime_type.compare(0, 12, "application/") == 0 &&
+ MatchesMimeType("application/*+json", mime_type));
}
bool MimeUtil::IsUnsupportedTextMimeType(const std::string& mime_type) const {
@@ -616,47 +678,45 @@ bool MimeUtil::MatchesMimeType(const std::string& mime_type_pattern,
return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
}
-// See http://www.iana.org/assignments/media-types/index.html
+// See http://www.iana.org/assignments/media-types/media-types.xhtml
static const char* legal_top_level_types[] = {
- "application/",
- "audio/",
- "example/",
- "image/",
- "message/",
- "model/",
- "multipart/",
- "text/",
- "video/",
+ "application",
+ "audio",
+ "example",
+ "image",
+ "message",
+ "model",
+ "multipart",
+ "text",
+ "video",
};
-bool MimeUtil::IsMimeType(const std::string& type_string) const {
- // MIME types are always ASCII and case-insensitive (at least, the top-level
- // and secondary types we care about).
- if (!IsStringASCII(type_string))
+bool MimeUtil::ParseMimeTypeWithoutParameter(
+ const std::string& type_string,
+ std::string* top_level_type,
+ std::string* subtype) const {
+ std::vector<std::string> components;
+ base::SplitString(type_string, '/', &components);
+ if (components.size() != 2 ||
+ !HttpUtil::IsToken(components[0]) ||
+ !HttpUtil::IsToken(components[1]))
return false;
- if (type_string == "*/*" || type_string == "*")
- return true;
+ if (top_level_type)
+ *top_level_type = components[0];
+ if (subtype)
+ *subtype = components[1];
+ return true;
+}
+bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const {
+ std::string lower_type = StringToLowerASCII(type_string);
for (size_t i = 0; i < arraysize(legal_top_level_types); ++i) {
- if (StartsWithASCII(type_string, legal_top_level_types[i], false) &&
- type_string.length() > strlen(legal_top_level_types[i])) {
+ if (lower_type.compare(legal_top_level_types[i]) == 0)
return true;
- }
}
- // If there's a "/" separator character, and the token before it is
- // "x-" + (ascii characters), it is also a MIME type.
- size_t slash = type_string.find('/');
- if (slash < 3 ||
- slash == std::string::npos || slash == type_string.length() - 1) {
- return false;
- }
-
- if (StartsWithASCII(type_string, "x-", false))
- return true;
-
- return false;
+ return type_string.size() > 2 && StartsWithASCII(type_string, "x-", false);
}
bool MimeUtil::AreSupportedMediaCodecs(
@@ -761,8 +821,15 @@ bool MatchesMimeType(const std::string& mime_type_pattern,
return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type);
}
-bool IsMimeType(const std::string& type_string) {
- return g_mime_util.Get().IsMimeType(type_string);
+bool ParseMimeTypeWithoutParameter(const std::string& type_string,
+ std::string* top_level_type,
+ std::string* subtype) {
+ return g_mime_util.Get().ParseMimeTypeWithoutParameter(
+ type_string, top_level_type, subtype);
+}
+
+bool IsValidTopLevelMimeType(const std::string& type_string) {
+ return g_mime_util.Get().IsValidTopLevelMimeType(type_string);
}
bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) {
@@ -870,11 +937,11 @@ void GetExtensionsFromHardCodedMappings(
for (size_t i = 0; i < mappings_len; ++i) {
if (StartsWithASCII(mappings[i].mime_type, leading_mime_type, false)) {
std::vector<string> this_extensions;
- base::SplitStringUsingSubstr(mappings[i].extensions, ",",
- &this_extensions);
+ base::SplitString(mappings[i].extensions, ',', &this_extensions);
for (size_t j = 0; j < this_extensions.size(); ++j) {
#if defined(OS_WIN)
- base::FilePath::StringType extension(UTF8ToWide(this_extensions[j]));
+ base::FilePath::StringType extension(
+ base::UTF8ToWide(this_extensions[j]));
#else
base::FilePath::StringType extension(this_extensions[j]);
#endif
diff --git a/chromium/net/base/mime_util.h b/chromium/net/base/mime_util.h
index 9662e9656e3..6c943a7f7da 100644
--- a/chromium/net/base/mime_util.h
+++ b/chromium/net/base/mime_util.h
@@ -55,10 +55,28 @@ NET_EXPORT bool IsSupportedMimeType(const std::string& mime_type);
NET_EXPORT bool MatchesMimeType(const std::string& mime_type_pattern,
const std::string& mime_type);
-// Returns true if the |type_string| is a correctly-formed mime type specifier.
-// Allows strings of the form x/y[;params], where "x" is a legal mime type name.
-// Also allows wildcard types -- "x/*", "*/*", and "*".
-NET_EXPORT bool IsMimeType(const std::string& type_string);
+// Returns true if the |type_string| is a correctly-formed mime type specifier
+// with no parameter, i.e. string that matches the following ABNF (see the
+// definition of content ABNF in RFC2045 and media-type ABNF httpbis p2
+// semantics).
+//
+// token "/" token
+//
+// If |top_level_type| is non-NULL, sets it to parsed top-level type string.
+// If |subtype| is non-NULL, sets it to parsed subtype string.
+NET_EXPORT bool ParseMimeTypeWithoutParameter(const std::string& type_string,
+ std::string* top_level_type,
+ std::string* subtype);
+
+// Returns true if the |type_string| is a top-level type of any media type
+// registered with IANA media types registry at
+// http://www.iana.org/assignments/media-types/media-types.xhtml or an
+// experimental type (type with x- prefix).
+//
+// This method doesn't check that the input conforms to token ABNF, so if input
+// is experimental type strings, you need to check check that before using
+// this method.
+NET_EXPORT bool IsValidTopLevelMimeType(const std::string& type_string);
// Returns true if and only if all codecs are supported, false otherwise.
NET_EXPORT bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs);
diff --git a/chromium/net/base/mime_util_unittest.cc b/chromium/net/base/mime_util_unittest.cc
index cc6f4aade7d..bb783af90f3 100644
--- a/chromium/net/base/mime_util_unittest.cc
+++ b/chromium/net/base/mime_util_unittest.cc
@@ -8,6 +8,10 @@
#include "net/base/mime_util.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
namespace net {
TEST(MimeUtilTest, ExtensionTest) {
@@ -73,9 +77,15 @@ TEST(MimeUtilTest, LookupTypes) {
EXPECT_FALSE(IsSupportedNonImageMimeType("text/vcard"));
EXPECT_FALSE(IsSupportedNonImageMimeType("application/virus"));
EXPECT_TRUE(IsSupportedNonImageMimeType("application/x-x509-user-cert"));
+ EXPECT_TRUE(IsSupportedNonImageMimeType("application/json"));
+ EXPECT_TRUE(IsSupportedNonImageMimeType("application/+json"));
+ EXPECT_TRUE(IsSupportedNonImageMimeType("application/x-suggestions+json"));
+ EXPECT_TRUE(IsSupportedNonImageMimeType("application/x-s+json;x=2"));
#if defined(OS_ANDROID)
EXPECT_TRUE(IsSupportedNonImageMimeType("application/x-x509-ca-cert"));
EXPECT_TRUE(IsSupportedNonImageMimeType("application/x-pkcs12"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("application/vnd.apple.mpegurl"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("application/x-mpegurl"));
#endif
EXPECT_TRUE(IsSupportedMimeType("image/jpeg"));
@@ -84,6 +94,39 @@ TEST(MimeUtilTest, LookupTypes) {
EXPECT_TRUE(IsSupportedMimeType("text/banana"));
EXPECT_FALSE(IsSupportedMimeType("text/vcard"));
EXPECT_FALSE(IsSupportedMimeType("application/virus"));
+ EXPECT_FALSE(IsSupportedMimeType("application/x-json"));
+ EXPECT_FALSE(IsSupportedNonImageMimeType("application/vnd.doc;x=y+json"));
+}
+
+TEST(MimeUtilTest, StrictMediaMimeType) {
+ EXPECT_TRUE(IsStrictMediaMimeType("video/webm"));
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/webm"));
+
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/wav"));
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/x-wav"));
+
+ EXPECT_TRUE(IsStrictMediaMimeType("video/ogg"));
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/ogg"));
+ EXPECT_TRUE(IsStrictMediaMimeType("application/ogg"));
+
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/mpeg"));
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/mp3"));
+ EXPECT_TRUE(IsStrictMediaMimeType("audio/x-mp3"));
+
+ // TODO(amogh.bihani): These will be fixed http://crbug.com/53193
+ EXPECT_FALSE(IsStrictMediaMimeType("video/mp4"));
+ EXPECT_FALSE(IsStrictMediaMimeType("video/x-m4v"));
+ EXPECT_FALSE(IsStrictMediaMimeType("audio/mp4"));
+ EXPECT_FALSE(IsStrictMediaMimeType("audio/x-m4a"));
+
+ EXPECT_FALSE(IsStrictMediaMimeType("application/x-mpegurl"));
+ EXPECT_FALSE(IsStrictMediaMimeType("application/vnd.apple.mpegurl"));
+ // ---------------------------------------------------------------------------
+
+ EXPECT_FALSE(IsStrictMediaMimeType("video/unknown"));
+ EXPECT_FALSE(IsStrictMediaMimeType("audio/unknown"));
+ EXPECT_FALSE(IsStrictMediaMimeType("application/unknown"));
+ EXPECT_FALSE(IsStrictMediaMimeType("unknown/unknown"));
}
TEST(MimeUtilTest, MatchesMimeType) {
@@ -94,6 +137,8 @@ TEST(MimeUtilTest, MatchesMimeType) {
EXPECT_TRUE(MatchesMimeType("application/*+xml",
"application/html+xml"));
EXPECT_TRUE(MatchesMimeType("application/*+xml", "application/+xml"));
+ EXPECT_TRUE(MatchesMimeType("application/*+json",
+ "application/x-myformat+json"));
EXPECT_TRUE(MatchesMimeType("aaa*aaa", "aaaaaa"));
EXPECT_TRUE(MatchesMimeType("*", std::string()));
EXPECT_FALSE(MatchesMimeType("video/", "video/x-mpeg"));
@@ -148,6 +193,60 @@ TEST(MimeUtilTest, MatchesMimeType) {
EXPECT_TRUE(MatchesMimeType("ab/*cd", "ab/xxxcd"));
}
+TEST(MimeUtilTest, CommonMediaMimeType) {
+#if defined(OS_ANDROID)
+ bool HLSSupported;
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < 14)
+ HLSSupported = false;
+ else
+ HLSSupported = true;
+#endif
+
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/webm"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("video/webm"));
+
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/wav"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-wav"));
+
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/ogg"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("application/ogg"));
+#if defined(OS_ANDROID)
+ EXPECT_FALSE(IsSupportedMediaMimeType("video/ogg"));
+ EXPECT_EQ(HLSSupported, IsSupportedMediaMimeType("application/x-mpegurl"));
+ EXPECT_EQ(HLSSupported,
+ IsSupportedMediaMimeType("application/vnd.apple.mpegurl"));
+#else
+ EXPECT_TRUE(IsSupportedMediaMimeType("video/ogg"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("application/x-mpegurl"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("application/vnd.apple.mpegurl"));
+#endif // OS_ANDROID
+
+#if defined(USE_PROPRIETARY_CODECS)
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/mp4"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-m4a"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("video/mp4"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("video/x-m4v"));
+
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/mp3"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-mp3"));
+ EXPECT_TRUE(IsSupportedMediaMimeType("audio/mpeg"));
+#else
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/mp4"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/x-m4a"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("video/mp4"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("video/x-m4v"));
+
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/mp3"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/x-mp3"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/mpeg"));
+#endif // USE_PROPRIETARY_CODECS
+ EXPECT_FALSE(IsSupportedMediaMimeType("video/mp3"));
+
+ EXPECT_FALSE(IsSupportedMediaMimeType("video/unknown"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("audio/unknown"));
+ EXPECT_FALSE(IsSupportedMediaMimeType("unknown/unknown"));
+}
+
// Note: codecs should only be a list of 2 or fewer; hence the restriction of
// results' length to 2.
TEST(MimeUtilTest, ParseCodecString) {
@@ -184,38 +283,77 @@ TEST(MimeUtilTest, ParseCodecString) {
EXPECT_EQ("mp4a.40.2", codecs_out[1]);
}
-TEST(MimeUtilTest, TestIsMimeType) {
+TEST(MimeUtilTest, TestParseMimeTypeWithoutParameter) {
std::string nonAscii("application/nonutf8");
- EXPECT_TRUE(IsMimeType(nonAscii));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter(nonAscii, NULL, NULL));
#if defined(OS_WIN)
- nonAscii.append(WideToUTF8(std::wstring(L"\u2603")));
+ nonAscii.append(base::WideToUTF8(std::wstring(L"\u2603")));
#else
nonAscii.append("\u2603"); // unicode snowman
#endif
- EXPECT_FALSE(IsMimeType(nonAscii));
-
- EXPECT_TRUE(IsMimeType("application/mime"));
- EXPECT_TRUE(IsMimeType("audio/mime"));
- EXPECT_TRUE(IsMimeType("example/mime"));
- EXPECT_TRUE(IsMimeType("image/mime"));
- EXPECT_TRUE(IsMimeType("message/mime"));
- EXPECT_TRUE(IsMimeType("model/mime"));
- EXPECT_TRUE(IsMimeType("multipart/mime"));
- EXPECT_TRUE(IsMimeType("text/mime"));
- EXPECT_TRUE(IsMimeType("TEXT/mime"));
- EXPECT_TRUE(IsMimeType("Text/mime"));
- EXPECT_TRUE(IsMimeType("TeXt/mime"));
- EXPECT_TRUE(IsMimeType("video/mime"));
- EXPECT_TRUE(IsMimeType("video/mime;parameter"));
- EXPECT_TRUE(IsMimeType("*/*"));
- EXPECT_TRUE(IsMimeType("*"));
-
- EXPECT_TRUE(IsMimeType("x-video/mime"));
- EXPECT_TRUE(IsMimeType("X-Video/mime"));
- EXPECT_FALSE(IsMimeType("x-video/"));
- EXPECT_FALSE(IsMimeType("x-/mime"));
- EXPECT_FALSE(IsMimeType("mime/looking"));
- EXPECT_FALSE(IsMimeType("text/"));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter(nonAscii, NULL, NULL));
+
+ std::string top_level_type;
+ std::string subtype;
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter(
+ "application/mime", &top_level_type, &subtype));
+ EXPECT_EQ("application", top_level_type);
+ EXPECT_EQ("mime", subtype);
+
+ // Various allowed subtype forms.
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("application/json", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter(
+ "application/x-suggestions+json", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("application/+json", NULL, NULL));
+
+ // Upper case letters are allowed.
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/mime", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("TEXT/mime", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("Text/mime", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("TeXt/mime", NULL, NULL));
+
+ // Experimental types are also considered to be valid.
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("x-video/mime", NULL, NULL));
+ EXPECT_TRUE(ParseMimeTypeWithoutParameter("X-Video/mime", NULL, NULL));
+
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("text", NULL, NULL));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/", NULL, NULL));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/ ", NULL, NULL));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("te(xt/ ", NULL, NULL));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/()plain", NULL, NULL));
+
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("x-video", NULL, NULL));
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("x-video/", NULL, NULL));
+
+ EXPECT_FALSE(ParseMimeTypeWithoutParameter("application/a/b/c", NULL, NULL));
+
+ //EXPECT_TRUE(ParseMimeTypeWithoutParameter("video/mime;parameter"));
+}
+
+TEST(MimeUtilTest, TestIsValidTopLevelMimeType) {
+ EXPECT_TRUE(IsValidTopLevelMimeType("application"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("audio"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("example"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("image"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("message"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("model"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("multipart"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("text"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("video"));
+
+ EXPECT_TRUE(IsValidTopLevelMimeType("TEXT"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("Text"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("TeXt"));
+
+ EXPECT_FALSE(IsValidTopLevelMimeType("mime"));
+ EXPECT_FALSE(IsValidTopLevelMimeType(""));
+ EXPECT_FALSE(IsValidTopLevelMimeType("/"));
+ EXPECT_FALSE(IsValidTopLevelMimeType(" "));
+
+ EXPECT_TRUE(IsValidTopLevelMimeType("x-video"));
+ EXPECT_TRUE(IsValidTopLevelMimeType("X-video"));
+
+ EXPECT_FALSE(IsValidTopLevelMimeType("x-"));
}
TEST(MimeUtilTest, TestToIANAMediaType) {
@@ -267,7 +405,7 @@ TEST(MimeUtilTest, TestGetExtensionsForMimeType) {
bool found = false;
for (size_t j = 0; !found && j < extensions.size(); ++j) {
#if defined(OS_WIN)
- if (extensions[j] == UTF8ToWide(tests[i].contained_result))
+ if (extensions[j] == base::UTF8ToWide(tests[i].contained_result))
found = true;
#else
if (extensions[j] == tests[i].contained_result)
diff --git a/chromium/net/base/mock_file_stream.cc b/chromium/net/base/mock_file_stream.cc
index d160f0c9385..be2dc821527 100644
--- a/chromium/net/base/mock_file_stream.cc
+++ b/chromium/net/base/mock_file_stream.cc
@@ -4,62 +4,136 @@
#include "net/base/mock_file_stream.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+
namespace net {
namespace testing {
-int MockFileStream::OpenSync(const base::FilePath& path, int open_flags) {
- path_ = path;
- return ReturnError(FileStream::OpenSync(path, open_flags));
+MockFileStream::MockFileStream(
+ const scoped_refptr<base::TaskRunner>& task_runner)
+ : net::FileStream(task_runner),
+ forced_error_(net::OK),
+ async_error_(false),
+ throttled_(false),
+ weak_factory_(this) {
}
-int MockFileStream::Seek(Whence whence, int64 offset,
- const Int64CompletionCallback& callback) {
- return ReturnError(FileStream::Seek(whence, offset, callback));
+MockFileStream::MockFileStream(
+ base::File file,
+ const scoped_refptr<base::TaskRunner>& task_runner)
+ : net::FileStream(file.Pass(), task_runner),
+ forced_error_(net::OK),
+ async_error_(false),
+ throttled_(false),
+ weak_factory_(this) {
}
-int64 MockFileStream::SeekSync(Whence whence, int64 offset) {
- return ReturnError64(FileStream::SeekSync(whence, offset));
+MockFileStream::~MockFileStream() {
}
-int64 MockFileStream::Available() {
- return ReturnError64(FileStream::Available());
+int MockFileStream::Seek(Whence whence, int64 offset,
+ const Int64CompletionCallback& callback) {
+ Int64CompletionCallback wrapped_callback =
+ base::Bind(&MockFileStream::DoCallback64,
+ weak_factory_.GetWeakPtr(), callback);
+ if (forced_error_ == net::OK)
+ return FileStream::Seek(whence, offset, wrapped_callback);
+ return ErrorCallback64(wrapped_callback);
}
int MockFileStream::Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
- return ReturnError(FileStream::Read(buf, buf_len, callback));
+ CompletionCallback wrapped_callback = base::Bind(&MockFileStream::DoCallback,
+ weak_factory_.GetWeakPtr(),
+ callback);
+ if (forced_error_ == net::OK)
+ return FileStream::Read(buf, buf_len, wrapped_callback);
+ return ErrorCallback(wrapped_callback);
+}
+
+int MockFileStream::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ CompletionCallback wrapped_callback = base::Bind(&MockFileStream::DoCallback,
+ weak_factory_.GetWeakPtr(),
+ callback);
+ if (forced_error_ == net::OK)
+ return FileStream::Write(buf, buf_len, wrapped_callback);
+ return ErrorCallback(wrapped_callback);
}
-int MockFileStream::ReadSync(char* buf, int buf_len) {
- return ReturnError(FileStream::ReadSync(buf, buf_len));
+int MockFileStream::Flush(const CompletionCallback& callback) {
+ CompletionCallback wrapped_callback = base::Bind(&MockFileStream::DoCallback,
+ weak_factory_.GetWeakPtr(),
+ callback);
+ if (forced_error_ == net::OK)
+ return FileStream::Flush(wrapped_callback);
+ return ErrorCallback(wrapped_callback);
}
-int MockFileStream::ReadUntilComplete(char *buf, int buf_len) {
- return ReturnError(FileStream::ReadUntilComplete(buf, buf_len));
+void MockFileStream::ThrottleCallbacks() {
+ CHECK(!throttled_);
+ throttled_ = true;
}
-int MockFileStream::Write(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- return ReturnError(FileStream::Write(buf, buf_len, callback));
+void MockFileStream::ReleaseCallbacks() {
+ CHECK(throttled_);
+ throttled_ = false;
+
+ if (!throttled_task_.is_null()) {
+ base::Closure throttled_task = throttled_task_;
+ throttled_task_.Reset();
+ base::MessageLoop::current()->PostTask(FROM_HERE, throttled_task);
+ }
}
-int MockFileStream::WriteSync(const char* buf, int buf_len) {
- return ReturnError(FileStream::WriteSync(buf, buf_len));
+void MockFileStream::DoCallback(const CompletionCallback& callback,
+ int result) {
+ if (!throttled_) {
+ callback.Run(result);
+ return;
+ }
+ CHECK(throttled_task_.is_null());
+ throttled_task_ = base::Bind(callback, result);
}
-int64 MockFileStream::Truncate(int64 bytes) {
- return ReturnError64(FileStream::Truncate(bytes));
+void MockFileStream::DoCallback64(const Int64CompletionCallback& callback,
+ int64 result) {
+ if (!throttled_) {
+ callback.Run(result);
+ return;
+ }
+ CHECK(throttled_task_.is_null());
+ throttled_task_ = base::Bind(callback, result);
}
-int MockFileStream::Flush(const CompletionCallback& callback) {
- return ReturnError(FileStream::Flush(callback));
+int MockFileStream::ErrorCallback(const CompletionCallback& callback) {
+ CHECK_NE(net::OK, forced_error_);
+ if (async_error_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, forced_error_));
+ clear_forced_error();
+ return net::ERR_IO_PENDING;
+ }
+ int ret = forced_error_;
+ clear_forced_error();
+ return ret;
}
-int MockFileStream::FlushSync() {
- return ReturnError(FileStream::FlushSync());
+int64 MockFileStream::ErrorCallback64(const Int64CompletionCallback& callback) {
+ CHECK_NE(net::OK, forced_error_);
+ if (async_error_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, forced_error_));
+ clear_forced_error();
+ return net::ERR_IO_PENDING;
+ }
+ int64 ret = forced_error_;
+ clear_forced_error();
+ return ret;
}
} // namespace testing
diff --git a/chromium/net/base/mock_file_stream.h b/chromium/net/base/mock_file_stream.h
index a37b49546d4..c9f07f02487 100644
--- a/chromium/net/base/mock_file_stream.h
+++ b/chromium/net/base/mock_file_stream.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"
@@ -21,36 +22,44 @@ namespace testing {
class MockFileStream : public net::FileStream {
public:
- MockFileStream(net::NetLog* net_log)
- : net::FileStream(net_log), forced_error_(net::OK) {}
-
- MockFileStream(base::PlatformFile file, int flags, net::NetLog* net_log)
- : net::FileStream(file, flags, net_log), forced_error_(net::OK) {}
+ explicit MockFileStream(const scoped_refptr<base::TaskRunner>& task_runner);
+ MockFileStream(base::File file,
+ const scoped_refptr<base::TaskRunner>& task_runner);
+ virtual ~MockFileStream();
// FileStream methods.
- virtual int OpenSync(const base::FilePath& path, int open_flags) OVERRIDE;
virtual int Seek(net::Whence whence, int64 offset,
const Int64CompletionCallback& callback) OVERRIDE;
- virtual int64 SeekSync(net::Whence whence, int64 offset) OVERRIDE;
- virtual int64 Available() OVERRIDE;
virtual int Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
- virtual int ReadSync(char* buf, int buf_len) OVERRIDE;
- virtual int ReadUntilComplete(char *buf, int buf_len) OVERRIDE;
virtual int Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
- virtual int WriteSync(const char* buf, int buf_len) OVERRIDE;
- virtual int64 Truncate(int64 bytes) OVERRIDE;
virtual int Flush(const CompletionCallback& callback) OVERRIDE;
- virtual int FlushSync() OVERRIDE;
- void set_forced_error(int error) { forced_error_ = error; }
- void clear_forced_error() { forced_error_ = net::OK; }
+ void set_forced_error_async(int error) {
+ forced_error_ = error;
+ async_error_ = true;
+ }
+ void set_forced_error(int error) {
+ forced_error_ = error;
+ async_error_ = false;
+ }
+ void clear_forced_error() {
+ forced_error_ = net::OK;
+ async_error_ = false;
+ }
int forced_error() const { return forced_error_; }
const base::FilePath& get_path() const { return path_; }
+ // Throttles all asynchronous callbacks, including forced errors, until a
+ // matching ReleaseCallbacks call.
+ void ThrottleCallbacks();
+
+ // Resumes running asynchronous callbacks and runs any throttled callbacks.
+ void ReleaseCallbacks();
+
private:
int ReturnError(int function_error) {
if (forced_error_ != net::OK) {
@@ -72,8 +81,23 @@ class MockFileStream : public net::FileStream {
return function_error;
}
+ // Wrappers for callbacks to make them honor ThrottleCallbacks and
+ // ReleaseCallbacks.
+ void DoCallback(const CompletionCallback& callback, int result);
+ void DoCallback64(const Int64CompletionCallback& callback, int64 result);
+
+ // Depending on |async_error_|, either synchronously returns |forced_error_|
+ // asynchronously calls |callback| with |async_error_|.
+ int ErrorCallback(const CompletionCallback& callback);
+ int64 ErrorCallback64(const Int64CompletionCallback& callback);
+
int forced_error_;
+ bool async_error_;
+ bool throttled_;
+ base::Closure throttled_task_;
base::FilePath path_;
+
+ base::WeakPtrFactory<MockFileStream> weak_factory_;
};
} // namespace testing
diff --git a/chromium/net/base/net_error_list.h b/chromium/net/base/net_error_list.h
index 3416cd07c15..65d3a45d198 100644
--- a/chromium/net/base/net_error_list.h
+++ b/chromium/net/base/net_error_list.h
@@ -94,6 +94,11 @@ NET_ERROR(BLOCKED_BY_ADMINISTRATOR, -22)
// The socket is already connected.
NET_ERROR(SOCKET_IS_CONNECTED, -23)
+// The request was blocked because the forced reenrollment check is still
+// pending. This error can only occur on ChromeOS.
+// The error can be emitted by code in c/b/policy/policy_helpers.cc.
+NET_ERROR(BLOCKED_ENROLLMENT_CHECK_PENDING, -24)
+
// A connection was closed (corresponding to a TCP FIN).
NET_ERROR(CONNECTION_CLOSED, -100)
@@ -307,6 +312,26 @@ NET_ERROR(SSL_SERVER_CERT_CHANGED, -156)
// performed.
NET_ERROR(SSL_INAPPROPRIATE_FALLBACK, -157)
+// Certificate Transparency: All Signed Certificate Timestamps failed to verify.
+NET_ERROR(CT_NO_SCTS_VERIFIED_OK, -158)
+
+// The SSL server sent us a fatal unrecognized_name alert.
+NET_ERROR(SSL_UNRECOGNIZED_NAME_ALERT, -159)
+
+// Failed to set the socket's receive buffer size as requested.
+NET_ERROR(SOCKET_SET_RECEIVE_BUFFER_SIZE_ERROR, -160)
+
+// Failed to set the socket's send buffer size as requested.
+NET_ERROR(SOCKET_SET_SEND_BUFFER_SIZE_ERROR, -161)
+
+// Failed to set the socket's receive buffer size as requested, despite success
+// return code from setsockopt.
+NET_ERROR(SOCKET_RECEIVE_BUFFER_SIZE_UNCHANGEABLE, -162)
+
+// Failed to set the socket's send buffer size as requested, despite success
+// return code from setsockopt.
+NET_ERROR(SOCKET_SEND_BUFFER_SIZE_UNCHANGEABLE, -163)
+
// Certificate error codes
//
// The values of certificate error codes must be consecutive.
@@ -415,10 +440,6 @@ NET_ERROR(CERT_NAME_CONSTRAINT_VIOLATION, -212)
// The value immediately past the last certificate error code.
NET_ERROR(CERT_END, -213)
-// Certificate Transparency: All Signed Certificate Timestamps failed to verify.
-// XXX(eranm): Move this error to a more appropriate category.
-NET_ERROR(CT_NO_SCTS_VERIFIED_OK, -299)
-
// The URL is invalid.
NET_ERROR(INVALID_URL, -300)
@@ -549,8 +570,9 @@ NET_ERROR(SPDY_SERVER_REFUSED_STREAM, -351)
// SPDY server didn't respond to the PING message.
NET_ERROR(SPDY_PING_FAILED, -352)
-// The request couldn't be completed on an HTTP pipeline. Client should retry.
-NET_ERROR(PIPELINE_EVICTION, -353)
+// Obsolete. Kept here to avoid reuse, as the old error can still appear on
+// histograms.
+// NET_ERROR(PIPELINE_EVICTION, -353)
// The HTTP response body transferred fewer bytes than were advertised by the
// Content-Length header when the connection is closed.
@@ -573,6 +595,21 @@ NET_ERROR(QUIC_HANDSHAKE_FAILED, -358)
// An https resource was requested over an insecure QUIC connection.
NET_ERROR(REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC, -359)
+// Transport security is inadequate for the SPDY version.
+NET_ERROR(SPDY_INADEQUATE_TRANSPORT_SECURITY, -360)
+
+// The peer violated SPDY flow control.
+NET_ERROR(SPDY_FLOW_CONTROL_ERROR, -361)
+
+// The peer sent an improperly sized SPDY frame.
+NET_ERROR(SPDY_FRAME_SIZE_ERROR, -362)
+
+// Decoding or encoding of compressed SPDY headers failed.
+NET_ERROR(SPDY_COMPRESSION_ERROR, -363)
+
+// Proxy Auth Requested without a valid Client Socket Handle.
+NET_ERROR(PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION, -364)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/chromium/net/base/net_errors.cc b/chromium/net/base/net_errors.cc
index 01fda3908ca..a9d1443c913 100644
--- a/chromium/net/base/net_errors.cc
+++ b/chromium/net/base/net_errors.cc
@@ -44,16 +44,15 @@ std::vector<int> GetAllErrorCodesForUma() {
kAllErrorCodes, arraysize(kAllErrorCodes));
}
-Error PlatformFileErrorToNetError(
- base::PlatformFileError file_error) {
+Error FileErrorToNetError(base::File::Error file_error) {
switch (file_error) {
- case base::PLATFORM_FILE_OK:
+ case base::File::FILE_OK:
return net::OK;
- case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
+ case base::File::FILE_ERROR_ACCESS_DENIED:
return net::ERR_ACCESS_DENIED;
- case base::PLATFORM_FILE_ERROR_INVALID_URL:
+ case base::File::FILE_ERROR_INVALID_URL:
return net::ERR_INVALID_URL;
- case base::PLATFORM_FILE_ERROR_NOT_FOUND:
+ case base::File::FILE_ERROR_NOT_FOUND:
return net::ERR_FILE_NOT_FOUND;
default:
return net::ERR_FAILED;
diff --git a/chromium/net/base/net_errors.h b/chromium/net/base/net_errors.h
index 34e355e1104..2c513612209 100644
--- a/chromium/net/base/net_errors.h
+++ b/chromium/net/base/net_errors.h
@@ -8,7 +8,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/platform_file.h"
+#include "base/files/file.h"
#include "net/base/net_export.h"
namespace net {
@@ -36,8 +36,9 @@ NET_EXPORT const char* ErrorToString(int error);
inline bool IsCertificateError(int error) {
// Certificate errors are negative integers from net::ERR_CERT_BEGIN
// (inclusive) to net::ERR_CERT_END (exclusive) in *decreasing* order.
+ // ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN is currently an exception to this
+ // rule.
return (error <= ERR_CERT_BEGIN && error > ERR_CERT_END) ||
- (error == ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY) ||
(error == ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN);
}
@@ -53,9 +54,8 @@ NET_EXPORT Error MapSystemError(int os_error);
// error code that is not followed immediately by a valid error code.
NET_EXPORT std::vector<int> GetAllErrorCodesForUma();
-// A convenient function to translate platform file error to net error code.
-NET_EXPORT Error PlatformFileErrorToNetError(
- base::PlatformFileError file_error);
+// A convenient function to translate file error to net error code.
+NET_EXPORT Error FileErrorToNetError(base::File::Error file_error);
} // namespace net
diff --git a/chromium/net/base/net_log.cc b/chromium/net/base/net_log.cc
index d1e9c98de31..6016d0de026 100644
--- a/chromium/net/base/net_log.cc
+++ b/chromium/net/base/net_log.cc
@@ -123,21 +123,21 @@ bool NetLog::Source::FromEventParameters(base::Value* event_params,
base::Value* NetLog::Entry::ToValue() const {
base::DictionaryValue* entry_dict(new base::DictionaryValue());
- entry_dict->SetString("time", TickCountToString(time_));
+ entry_dict->SetString("time", TickCountToString(data_->time));
// Set the entry source.
base::DictionaryValue* source_dict = new base::DictionaryValue();
- source_dict->SetInteger("id", source_.id);
- source_dict->SetInteger("type", static_cast<int>(source_.type));
+ source_dict->SetInteger("id", data_->source.id);
+ source_dict->SetInteger("type", static_cast<int>(data_->source.type));
entry_dict->Set("source", source_dict);
// Set the event info.
- entry_dict->SetInteger("type", static_cast<int>(type_));
- entry_dict->SetInteger("phase", static_cast<int>(phase_));
+ entry_dict->SetInteger("type", static_cast<int>(data_->type));
+ entry_dict->SetInteger("phase", static_cast<int>(data_->phase));
// Set the event-specific parameters.
- if (parameters_callback_) {
- base::Value* value = parameters_callback_->Run(log_level_);
+ if (data_->parameters_callback) {
+ base::Value* value = data_->parameters_callback->Run(log_level_);
if (value)
entry_dict->Set("params", value);
}
@@ -146,30 +146,35 @@ base::Value* NetLog::Entry::ToValue() const {
}
base::Value* NetLog::Entry::ParametersToValue() const {
- if (parameters_callback_)
- return parameters_callback_->Run(log_level_);
+ if (data_->parameters_callback)
+ return data_->parameters_callback->Run(log_level_);
return NULL;
}
-NetLog::Entry::Entry(
+NetLog::EntryData::EntryData(
EventType type,
Source source,
EventPhase phase,
base::TimeTicks time,
- const ParametersCallback* parameters_callback,
- LogLevel log_level)
- : type_(type),
- source_(source),
- phase_(phase),
- time_(time),
- parameters_callback_(parameters_callback),
- log_level_(log_level) {
-};
+ const ParametersCallback* parameters_callback)
+ : type(type),
+ source(source),
+ phase(phase),
+ time(time),
+ parameters_callback(parameters_callback) {
+}
+
+NetLog::EntryData::~EntryData() {
+}
+
+NetLog::Entry::Entry(const EntryData* data, LogLevel log_level)
+ : data_(data), log_level_(log_level) {
+}
NetLog::Entry::~Entry() {
}
-NetLog::ThreadSafeObserver::ThreadSafeObserver() : log_level_(LOG_BASIC),
+NetLog::ThreadSafeObserver::ThreadSafeObserver() : log_level_(LOG_NONE),
net_log_(NULL) {
}
@@ -189,6 +194,10 @@ NetLog* NetLog::ThreadSafeObserver::net_log() const {
return net_log_;
}
+void NetLog::ThreadSafeObserver::OnAddEntryData(const EntryData& entry_data) {
+ OnAddEntry(Entry(&entry_data, log_level()));
+}
+
NetLog::NetLog()
: last_id_(0),
base_log_level_(LOG_NONE),
@@ -234,9 +243,11 @@ NetLog::LogLevel NetLog::GetLogLevel() const {
void NetLog::AddThreadSafeObserver(
net::NetLog::ThreadSafeObserver* observer,
LogLevel log_level) {
+ DCHECK_NE(LOG_NONE, log_level);
base::AutoLock lock(lock_);
DCHECK(!observer->net_log_);
+ DCHECK_EQ(LOG_NONE, observer->log_level_);
observers_.AddObserver(observer);
observer->net_log_ = this;
observer->log_level_ = log_level;
@@ -246,10 +257,12 @@ void NetLog::AddThreadSafeObserver(
void NetLog::SetObserverLogLevel(
net::NetLog::ThreadSafeObserver* observer,
LogLevel log_level) {
+ DCHECK_NE(LOG_NONE, log_level);
base::AutoLock lock(lock_);
DCHECK(observers_.HasObserver(observer));
DCHECK_EQ(this, observer->net_log_);
+ DCHECK_NE(LOG_NONE, observer->log_level_);
observer->log_level_ = log_level;
UpdateLogLevel();
}
@@ -260,8 +273,10 @@ void NetLog::RemoveThreadSafeObserver(
DCHECK(observers_.HasObserver(observer));
DCHECK_EQ(this, observer->net_log_);
+ DCHECK_NE(LOG_NONE, observer->log_level_);
observers_.RemoveObserver(observer);
observer->net_log_ = NULL;
+ observer->log_level_ = LOG_NONE;
UpdateLogLevel();
}
@@ -349,8 +364,8 @@ bool NetLog::IsLoggingBytes(LogLevel log_level) {
}
// static
-bool NetLog::IsLoggingAllEvents(LogLevel log_level) {
- return log_level <= NetLog::LOG_ALL_BUT_BYTES;
+bool NetLog::IsLogging(LogLevel log_level) {
+ return log_level < NetLog::LOG_NONE;
}
// static
@@ -383,15 +398,14 @@ void NetLog::AddEntry(EventType type,
const Source& source,
EventPhase phase,
const NetLog::ParametersCallback* parameters_callback) {
- LogLevel log_level = GetLogLevel();
- if (log_level == LOG_NONE)
+ if (GetLogLevel() == LOG_NONE)
return;
- Entry entry(type, source, phase, base::TimeTicks::Now(),
- parameters_callback, log_level);
+ EntryData entry_data(type, source, phase, base::TimeTicks::Now(),
+ parameters_callback);
// Notify all of the log observers.
base::AutoLock lock(lock_);
- FOR_EACH_OBSERVER(ThreadSafeObserver, observers_, OnAddEntry(entry));
+ FOR_EACH_OBSERVER(ThreadSafeObserver, observers_, OnAddEntryData(entry_data));
}
void BoundNetLog::AddEntry(NetLog::EventType type,
@@ -469,15 +483,15 @@ void BoundNetLog::AddByteTransferEvent(NetLog::EventType event_type,
NetLog::LogLevel BoundNetLog::GetLogLevel() const {
if (net_log_)
return net_log_->GetLogLevel();
- return NetLog::LOG_BASIC;
+ return NetLog::LOG_NONE;
}
bool BoundNetLog::IsLoggingBytes() const {
return NetLog::IsLoggingBytes(GetLogLevel());
}
-bool BoundNetLog::IsLoggingAllEvents() const {
- return NetLog::IsLoggingAllEvents(GetLogLevel());
+bool BoundNetLog::IsLogging() const {
+ return NetLog::IsLogging(GetLogLevel());
}
// static
diff --git a/chromium/net/base/net_log.h b/chromium/net/base/net_log.h
index 875d785fbc6..b037a336bc5 100644
--- a/chromium/net/base/net_log.h
+++ b/chromium/net/base/net_log.h
@@ -78,9 +78,9 @@ class NET_EXPORT NetLog {
// parameters for bytes sent/received events.
LOG_ALL_BUT_BYTES,
- // Only log events which are cheap, and don't consume much memory. This is
- // the default value for observers.
- LOG_BASIC,
+ // Log all events, but do not include the actual transferred bytes and
+ // remove cookies and HTTP credentials.
+ LOG_STRIP_PRIVATE_DATA,
// Don't log any events.
LOG_NONE,
@@ -120,19 +120,32 @@ class NET_EXPORT NetLog {
uint32 id;
};
+ struct NET_EXPORT EntryData {
+ EntryData(EventType type,
+ Source source,
+ EventPhase phase,
+ base::TimeTicks time,
+ const ParametersCallback* parameters_callback);
+ ~EntryData();
+
+ const EventType type;
+ const Source source;
+ const EventPhase phase;
+ const base::TimeTicks time;
+ const ParametersCallback* const parameters_callback;
+ };
+
+ // An Entry pre-binds EntryData to a LogLevel, so observers will observe the
+ // output of ToValue() and ParametersToValue() at their log level rather than
+ // current maximum.
class NET_EXPORT Entry {
public:
- Entry(EventType type,
- Source source,
- EventPhase phase,
- base::TimeTicks time,
- const ParametersCallback* parameters_callback,
- LogLevel log_level);
+ Entry(const EntryData* data, LogLevel log_level);
~Entry();
- EventType type() const { return type_; }
- Source source() const { return source_; }
- EventPhase phase() const { return phase_; }
+ EventType type() const { return data_->type; }
+ Source source() const { return data_->source; }
+ EventPhase phase() const { return data_->phase; }
// Serializes the specified event to a Value. The Value also includes the
// current time. Caller takes ownership of returned Value. Takes in a time
@@ -144,11 +157,7 @@ class NET_EXPORT NetLog {
base::Value* ParametersToValue() const;
private:
- const EventType type_;
- const Source source_;
- const EventPhase phase_;
- const base::TimeTicks time_;
- const ParametersCallback* parameters_callback_;
+ const EntryData* const data_;
// Log level when the event occurred.
const LogLevel log_level_;
@@ -196,6 +205,8 @@ class NET_EXPORT NetLog {
private:
friend class NetLog;
+ void OnAddEntryData(const EntryData& entry_data);
+
// Both of these values are only modified by the NetLog.
LogLevel log_level_;
NetLog* net_log_;
@@ -222,11 +233,6 @@ class NET_EXPORT NetLog {
// Adds an observer and sets its log level. The observer must not be
// watching any NetLog, including this one, when this is called.
//
- // Typical observers should specify LOG_BASIC.
- //
- // Observers that need to see the full granularity of events can specify
- // LOG_ALL_BUT_BYTES. However, doing so will have performance consequences.
- //
// NetLog implementations must call NetLog::OnAddObserver to update the
// observer's internal state.
void AddThreadSafeObserver(ThreadSafeObserver* observer, LogLevel log_level);
@@ -268,10 +274,9 @@ class NET_EXPORT NetLog {
// be logged. This is only the case when |log_level| is LOG_ALL.
static bool IsLoggingBytes(LogLevel log_level);
- // Returns true if |log_level| indicates that all events should be logged,
- // including frequently occuring ones that may impact performances.
- // This is the case when |log_level| is LOG_ALL or LOG_ALL_BUT_BYTES.
- static bool IsLoggingAllEvents(LogLevel log_level);
+ // Returns true if |log_level| indicates that events should be logged. This is
+ // the case when |log_level| is anything other than LOG_NONE.
+ static bool IsLogging(LogLevel log_level);
// Creates a ParametersCallback that encapsulates a single integer.
// Warning: |name| must remain valid for the life of the callback.
@@ -380,8 +385,8 @@ class NET_EXPORT BoundNetLog {
// Shortcut for NetLog::IsLoggingBytes(this->GetLogLevel()).
bool IsLoggingBytes() const;
- // Shortcut for NetLog::IsLoggingAllEvents(this->GetLogLevel()).
- bool IsLoggingAllEvents() const;
+ // Shortcut for NetLog::IsLogging(this->GetLogLevel()).
+ bool IsLogging() const;
// Helper to create a BoundNetLog given a NetLog and a SourceType. Takes care
// of creating a unique source ID, and handles the case of NULL net_log.
diff --git a/chromium/net/base/net_log_event_type_list.h b/chromium/net/base/net_log_event_type_list.h
index ca43b1bf12c..c6ed7c5d937 100644
--- a/chromium/net/base/net_log_event_type_list.h
+++ b/chromium/net/base/net_log_event_type_list.h
@@ -549,6 +549,10 @@ EVENT_TYPE(SSL_SOCKET_BYTES_RECEIVED)
EVENT_TYPE(SOCKET_READ_ERROR)
EVENT_TYPE(SOCKET_WRITE_ERROR)
+// The socket was closed locally (The socket may or may not have been closed
+// by the remote side already)
+EVENT_TYPE(SOCKET_CLOSED)
+
// Certificates were received from the SSL server (during a handshake or
// renegotiation). This event is only present when logging at LOG_ALL.
// The following parameters are attached to the event:
@@ -781,13 +785,19 @@ EVENT_TYPE(URL_REQUEST_JOB_BYTES_READ)
EVENT_TYPE(URL_REQUEST_JOB_FILTERED_BYTES_READ)
// This event is sent when the priority of a net::URLRequest is
-// changed after it has started. The parameters attached to this event
-// are:
+// changed after it has started. The following parameters are attached:
// {
// "priority": <Numerical value of the priority (higher is more important)>,
// }
EVENT_TYPE(URL_REQUEST_SET_PRIORITY)
+EVENT_TYPE(URL_REQUEST_REDIRECT_JOB)
+// This event is logged when a URLRequestRedirectJob is started for a request.
+// The following parameters are attached:
+// {
+// "reason": <Reason for the redirect, as a string>,
+// }
+
// ------------------------------------------------------------------------
// HttpCache
// ------------------------------------------------------------------------
@@ -989,6 +999,15 @@ EVENT_TYPE(HTTP_TRANSACTION_SEND_REQUEST_BODY)
// }
EVENT_TYPE(HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS)
+// This event is sent for a HTTP request over a SPDY stream.
+// The following parameters are attached:
+// {
+// "headers": <The list of header:value pairs>,
+// "quic_priority": <Integer representing the priority of this request>,
+// "quic_stream_id": <Id of the QUIC stream sending this request>,
+// }
+EVENT_TYPE(HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS)
+
// Measures the time to read HTTP response headers from the server.
EVENT_TYPE(HTTP_TRANSACTION_READ_HEADERS)
@@ -1009,7 +1028,9 @@ EVENT_TYPE(HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART)
// This event is sent when we try to restart a transaction after an error.
// The following parameters are attached:
// {
-// "net_error": <The net error code integer for the failure>,
+// "net_error": <The net error code integer for the failure, if applicable>,
+// "http_status_code": <HTTP status code indicating an error, if
+// applicable>,
// }
EVENT_TYPE(HTTP_TRANSACTION_RESTART_AFTER_ERROR)
@@ -1189,6 +1210,15 @@ EVENT_TYPE(SPDY_SESSION_SEND_DATA)
// }
EVENT_TYPE(SPDY_SESSION_RECV_DATA)
+// This event is sent for a receiving SPDY PUSH_PROMISE frame.
+// The following parameters are attached:
+// {
+// "headers": <The list of header:value pairs>,
+// "id": <The stream id>,
+// "promised_stream_id": <The stream id>,
+// }
+EVENT_TYPE(SPDY_SESSION_RECV_PUSH_PROMISE)
+
// A stream is stalled by the session send window being closed.
EVENT_TYPE(SPDY_SESSION_STREAM_STALLED_BY_SESSION_SEND_WINDOW)
@@ -1330,12 +1360,13 @@ EVENT_TYPE(QUIC_SESSION_PACKET_RECEIVED)
// Session sent a QUIC packet.
// {
-// "encryption_level": <The EncryptionLevel of the packet>
+// "encryption_level": <The EncryptionLevel of the packet>,
+// "transmission_type": <The TransmissionType of the packet>,
// "packet_sequence_number": <The packet's full 64-bit sequence number,
// as a base-10 string.>,
// "size": <The size of the packet in bytes>
// }
-EVENT_TYPE(QUIC_SESSION_PACKET_RETRANSMITTED)
+EVENT_TYPE(QUIC_SESSION_PACKET_SENT)
// Session retransmitted a QUIC packet.
// {
@@ -1344,11 +1375,12 @@ EVENT_TYPE(QUIC_SESSION_PACKET_RETRANSMITTED)
// "new_packet_sequence_number": <The new packet's full 64-bit sequence
// number, as a base-10 string.>,
// }
-EVENT_TYPE(QUIC_SESSION_PACKET_SENT)
+EVENT_TYPE(QUIC_SESSION_PACKET_RETRANSMITTED)
// Session received a QUIC packet header for a valid packet.
// {
-// "guid": <The 64-bit GUID for this connection, as a base-10 string>,
+// "connection_id": <The 64-bit CONNECTION_ID for this connection, as a
+// base-10 string>,
// "public_flags": <The public flags set for this packet>,
// "packet_sequence_number": <The packet's full 64-bit sequence number,
// as a base-10 string.>,
@@ -1411,6 +1443,74 @@ EVENT_TYPE(QUIC_SESSION_ACK_FRAME_RECEIVED)
// }
EVENT_TYPE(QUIC_SESSION_ACK_FRAME_SENT)
+// Session received a WINDOW_UPDATE frame.
+// {
+// "stream_id": <The id of the stream which this data is for>,
+// "byte_offset": <Byte offset in the stream>,
+// }
+EVENT_TYPE(QUIC_SESSION_WINDOW_UPDATE_FRAME_RECEIVED)
+
+// Session sent a WINDOW_UPDATE frame.
+// {
+// "stream_id": <The id of the stream which this data is for>,
+// "byte_offset": <Byte offset in the stream>,
+// }
+EVENT_TYPE(QUIC_SESSION_WINDOW_UPDATE_FRAME_SENT)
+
+// Session received a BLOCKED frame.
+// {
+// "stream_id": <The id of the stream which this data is for>,
+// }
+EVENT_TYPE(QUIC_SESSION_BLOCKED_FRAME_RECEIVED)
+
+// Session sent a BLOCKED frame.
+// {
+// "stream_id": <The id of the stream which this data is for>,
+// }
+EVENT_TYPE(QUIC_SESSION_BLOCKED_FRAME_SENT)
+
+// Session received a GOAWAY frame.
+// {
+// "quic_error": <QuicErrorCode in the frame>,
+// "last_good_stream_id": <Last correctly received stream id by the server>,
+// "reason_phrase": <Prose justifying go-away request>,
+// }
+EVENT_TYPE(QUIC_SESSION_GOAWAY_FRAME_RECEIVED)
+
+// Session sent a GOAWAY frame.
+// {
+// "quic_error": <QuicErrorCode in the frame>,
+// "last_good_stream_id": <Last correctly received stream id by the server>,
+// "reason_phrase": <Prose justifying go-away request>,
+// }
+EVENT_TYPE(QUIC_SESSION_GOAWAY_FRAME_SENT)
+
+// Session received a PING frame.
+EVENT_TYPE(QUIC_SESSION_PING_FRAME_RECEIVED)
+
+// Session sent a PING frame.
+EVENT_TYPE(QUIC_SESSION_PING_FRAME_SENT)
+
+// Session received a STOP_WAITING frame.
+// {
+// "sent_info": <Details of packet sent by the peer>
+// {
+// "least_unacked": <Lowest sequence number of a packet sent by the peer
+// for which it has not received an ACK>,
+// }
+// }
+EVENT_TYPE(QUIC_SESSION_STOP_WAITING_FRAME_RECEIVED)
+
+// Session sent an STOP_WAITING frame.
+// {
+// "sent_info": <Details of packet sent by the peer>
+// {
+// "least_unacked": <Lowest sequence number of a packet sent by the peer
+// for which it has not received an ACK>,
+// }
+// }
+EVENT_TYPE(QUIC_SESSION_STOP_WAITING_FRAME_SENT)
+
// Session recevied a RST_STREAM frame.
// {
// "offset": <Offset in the byte stream which triggered the reset>,
@@ -1502,7 +1602,8 @@ 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>,
+// "connection_id": <The 64-bit CONNECTION_ID for this connection, as a
+// base-10 string>,
// "public_flags": <The public flags set for this packet>,
// "packet_sequence_number": <The packet's full 64-bit sequence number,
// as a base-10 string.>,
@@ -1830,12 +1931,51 @@ EVENT_TYPE(CHROME_POLICY_ABORTED_REQUEST)
EVENT_TYPE(CERT_VERIFIER_REQUEST)
// This event is created when we start a CertVerifier job.
+// The BEGIN phase event parameters are:
+// {
+// "certificates": <A list of PEM encoded certificates, the first one
+// being the certificate to verify and the remaining
+// being intermediate certificates to assist path
+// building. Only present when byte logging is enabled.>
+// }
+//
// The END phase event parameters are:
// {
-// "certificates": <A list of PEM encoded certificates, the first one
-// being the certificate to verify and the remaining
-// being intermediate certificates to assist path
-// building. Only present when byte logging is enabled.>
+// "cert_status": <Bitmask of CERT_STATUS_*
+// from net/base/cert_status_flags.h>
+// "common_name_fallback_used": <True if a fallback to the common name
+// was used when matching the host
+// name, rather than using the
+// subjectAltName.>
+// "has_md2": <True if a certificate in the certificate chain is signed with
+// a MD2 signature.>
+// "has_md4": <True if a certificate in the certificate chain is signed with
+// a MD4 signature.>
+// "has_md5": <True if a certificate in the certificate chain is signed with
+// a MD5 signature.>
+// "is_issued_by_additional_trust_anchor": <True if the root CA used for
+// this verification came from the
+// list of additional trust
+// anchors.>
+// "is_issued_by_known_root": <True if we recognise the root CA as a
+// standard root. If it isn't then it's
+// probably the case that this certificate
+// was generated by a MITM proxy whose root
+// has been installed locally. This is
+// meaningless if the certificate was not
+// trusted.>
+// "public_key_hashes": <If the certificate was successfully verified then
+// this contains the hashes, in several hash
+// algorithms, of the SubjectPublicKeyInfos of the
+// chain.>
+// "verified_cert": <The certificate chain that was constructed
+// during verification. Note that though the verified
+// certificate will match the originally supplied
+// certificate, the intermediate certificates stored
+// within may be substantially different. In the event
+// of a verification failure, this will contain the
+// chain as supplied by the server. This may be NULL
+// if running within the sandbox.>
// }
EVENT_TYPE(CERT_VERIFIER_JOB)
@@ -1848,38 +1988,6 @@ EVENT_TYPE(CERT_VERIFIER_JOB)
EVENT_TYPE(CERT_VERIFIER_REQUEST_BOUND_TO_JOB)
// ------------------------------------------------------------------------
-// HttpPipelinedConnection
-// ------------------------------------------------------------------------
-
-// The start/end of a HttpPipelinedConnection.
-// {
-// "host_and_port": <The host-port string>,
-// }
-EVENT_TYPE(HTTP_PIPELINED_CONNECTION)
-
-// This event is created when a pipelined connection finishes sending a request.
-// {
-// "source_dependency": <Source id of the requesting stream>,
-// }
-EVENT_TYPE(HTTP_PIPELINED_CONNECTION_SENT_REQUEST)
-
-// This event is created when a pipelined connection finishes receiving the
-// response headers.
-// {
-// "source_dependency": <Source id of the requesting stream>,
-// "feedback": <The value of HttpPipelinedConnection::Feedback indicating
-// pipeline capability>,
-// }
-EVENT_TYPE(HTTP_PIPELINED_CONNECTION_RECEIVED_HEADERS)
-
-// This event is created when a pipelined stream closes.
-// {
-// "source_dependency": <Source id of the requesting stream>,
-// "must_close": <True if the pipeline must shut down>,
-// }
-EVENT_TYPE(HTTP_PIPELINED_CONNECTION_STREAM_CLOSED)
-
-// ------------------------------------------------------------------------
// Download start events.
// ------------------------------------------------------------------------
@@ -2267,3 +2375,7 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_CLOSE_BEGIN)
// This event is created when the Simple Cache finishes a CloseEntry call. It
// contains no parameters.
EVENT_TYPE(SIMPLE_CACHE_ENTRY_CLOSE_END)
+
+// This event is created (in a source of the same name) when the internal DNS
+// resolver creates a UDP socket to check for global IPv6 connectivity.
+EVENT_TYPE(IPV6_REACHABILITY_CHECK)
diff --git a/chromium/net/base/net_log_logger.cc b/chromium/net/base/net_log_logger.cc
index bd68bd55a3f..aa5ec4021f8 100644
--- a/chromium/net/base/net_log_logger.cc
+++ b/chromium/net/base/net_log_logger.cc
@@ -23,7 +23,7 @@ namespace net {
static const int kLogFormatVersion = 1;
NetLogLogger::NetLogLogger(FILE* file, const base::Value& constants)
- : file_(file), added_events_(false) {
+ : file_(file), log_level_(NetLog::LOG_ALL_BUT_BYTES), added_events_(false) {
DCHECK(file);
// Write constants to the output file. This allows loading files that have
@@ -40,8 +40,13 @@ NetLogLogger::~NetLogLogger() {
fprintf(file_.get(), "]}");
}
+void NetLogLogger::set_log_level(net::NetLog::LogLevel log_level) {
+ DCHECK(!net_log());
+ log_level_ = log_level;
+}
+
void NetLogLogger::StartObserving(net::NetLog* net_log) {
- net_log->AddThreadSafeObserver(this, net::NetLog::LOG_ALL_BUT_BYTES);
+ net_log->AddThreadSafeObserver(this, log_level_);
}
void NetLogLogger::StopObserving() {
@@ -52,7 +57,7 @@ void NetLogLogger::OnAddEntry(const net::NetLog::Entry& entry) {
// Add a comma and newline for every event but the first. Newlines are needed
// so can load partial log files by just ignoring the last line. For this to
// work, lines cannot be pretty printed.
- scoped_ptr<Value> value(entry.ToValue());
+ scoped_ptr<base::Value> value(entry.ToValue());
std::string json;
base::JSONWriter::Write(value.get(), &json);
fprintf(file_.get(), "%s%s",
@@ -62,7 +67,7 @@ void NetLogLogger::OnAddEntry(const net::NetLog::Entry& entry) {
}
base::DictionaryValue* NetLogLogger::GetConstants() {
- DictionaryValue* constants_dict = new DictionaryValue();
+ base::DictionaryValue* constants_dict = new base::DictionaryValue();
// Version of the file format.
constants_dict->SetInteger("logFormatVersion", kLogFormatVersion);
@@ -71,10 +76,22 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// enums and their symbolic names.
constants_dict->Set("logEventTypes", net::NetLog::GetEventTypesAsValue());
+ // Add a dictionary with information about the relationship between CertStatus
+ // flags and their symbolic names.
+ {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+
+#define CERT_STATUS_FLAG(label, value) dict->SetInteger(#label, value);
+#include "net/cert/cert_status_flags_list.h"
+#undef CERT_STATUS_FLAG
+
+ constants_dict->Set("certStatusFlag", dict);
+ }
+
// Add a dictionary with information about the relationship between load flag
// enums and their symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
#define LOAD_FLAG(label, value) \
dict->SetInteger(# label, static_cast<int>(value));
@@ -87,7 +104,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Add a dictionary with information about the relationship between load state
// enums and their symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
#define LOAD_STATE(label) \
dict->SetInteger(# label, net::LOAD_STATE_ ## label);
@@ -100,7 +117,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Add information on the relationship between net error codes and their
// symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
#define NET_ERROR(label, value) \
dict->SetInteger(# label, static_cast<int>(value));
@@ -113,7 +130,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Add information on the relationship between QUIC error codes and their
// symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
for (net::QuicErrorCode error = net::QUIC_NO_ERROR;
error < net::QUIC_LAST_ERROR;
@@ -128,7 +145,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Add information on the relationship between QUIC RST_STREAM error codes
// and their symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
for (net::QuicRstStreamErrorCode error = net::QUIC_STREAM_NO_ERROR;
error < net::QUIC_STREAM_LAST_ERROR;
@@ -143,7 +160,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Information about the relationship between event phase enums and their
// symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN);
dict->SetInteger("PHASE_END", net::NetLog::PHASE_END);
@@ -159,11 +176,12 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Information about the relationship between LogLevel enums and their
// symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL);
dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES);
- dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC);
+ dict->SetInteger("LOG_STRIP_PRIVATE_DATA",
+ net::NetLog::LOG_STRIP_PRIVATE_DATA);
constants_dict->Set("logLevelType", dict);
}
@@ -171,7 +189,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// Information about the relationship between address family enums and
// their symbolic names.
{
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED",
net::ADDRESS_FAMILY_UNSPECIFIED);
@@ -209,7 +227,7 @@ base::DictionaryValue* NetLogLogger::GetConstants() {
// "clientInfo" key is required for some NetLogLogger log readers.
// Provide a default empty value for compatibility.
- constants_dict->Set("clientInfo", new DictionaryValue());
+ constants_dict->Set("clientInfo", new base::DictionaryValue());
return constants_dict;
}
diff --git a/chromium/net/base/net_log_logger.h b/chromium/net/base/net_log_logger.h
index 1d0bc5b2355..3d53f3910d9 100644
--- a/chromium/net/base/net_log_logger.h
+++ b/chromium/net/base/net_log_logger.h
@@ -7,7 +7,7 @@
#include <stdio.h>
-#include "base/memory/scoped_handle.h"
+#include "base/files/scoped_file.h"
#include "net/base/net_log.h"
namespace base {
@@ -29,6 +29,9 @@ class NET_EXPORT NetLogLogger : public NetLog::ThreadSafeObserver {
NetLogLogger(FILE* file, const base::Value& constants);
virtual ~NetLogLogger();
+ // Sets the log level to log at. Must be called before StartObserving.
+ void set_log_level(NetLog::LogLevel log_level);
+
// Starts observing specified NetLog. Must not already be watching a NetLog.
// Separate from constructor to enforce thread safety.
void StartObserving(NetLog* net_log);
@@ -44,7 +47,10 @@ class NET_EXPORT NetLogLogger : public NetLog::ThreadSafeObserver {
static base::DictionaryValue* GetConstants();
private:
- ScopedStdioHandle file_;
+ base::ScopedFILE file_;
+
+ // The LogLevel to log at.
+ NetLog::LogLevel log_level_;
// True if OnAddEntry() has been called at least once.
bool added_events_;
diff --git a/chromium/net/base/net_log_logger_unittest.cc b/chromium/net/base/net_log_logger_unittest.cc
index 05c16c72fed..5d587dabd5f 100644
--- a/chromium/net/base/net_log_logger_unittest.cc
+++ b/chromium/net/base/net_log_logger_unittest.cc
@@ -57,12 +57,12 @@ TEST_F(NetLogLoggerTest, GeneratesValidJSONWithOneEvent) {
const int kDummyId = 1;
NetLog::Source source(NetLog::SOURCE_SPDY_SESSION, kDummyId);
- NetLog::Entry entry(NetLog::TYPE_PROXY_SERVICE,
- source,
- NetLog::PHASE_BEGIN,
- base::TimeTicks::Now(),
- NULL,
- NetLog::LOG_BASIC);
+ NetLog::EntryData entry_data(NetLog::TYPE_PROXY_SERVICE,
+ source,
+ NetLog::PHASE_BEGIN,
+ base::TimeTicks::Now(),
+ NULL);
+ NetLog::Entry entry(&entry_data, NetLog::LOG_ALL);
logger.OnAddEntry(entry);
}
@@ -89,12 +89,12 @@ TEST_F(NetLogLoggerTest, GeneratesValidJSONWithMultipleEvents) {
const int kDummyId = 1;
NetLog::Source source(NetLog::SOURCE_SPDY_SESSION, kDummyId);
- NetLog::Entry entry(NetLog::TYPE_PROXY_SERVICE,
- source,
- NetLog::PHASE_BEGIN,
- base::TimeTicks::Now(),
- NULL,
- NetLog::LOG_BASIC);
+ NetLog::EntryData entry_data(NetLog::TYPE_PROXY_SERVICE,
+ source,
+ NetLog::PHASE_BEGIN,
+ base::TimeTicks::Now(),
+ NULL);
+ NetLog::Entry entry(&entry_data, NetLog::LOG_ALL);
// Add the entry multiple times.
logger.OnAddEntry(entry);
diff --git a/chromium/net/base/net_log_source_type_list.h b/chromium/net/base/net_log_source_type_list.h
index 2219f7e5f17..265ba747459 100644
--- a/chromium/net/base/net_log_source_type_list.h
+++ b/chromium/net/base/net_log_source_type_list.h
@@ -23,8 +23,8 @@ SOURCE_TYPE(HTTP_STREAM_JOB)
SOURCE_TYPE(EXPONENTIAL_BACKOFF_THROTTLING)
SOURCE_TYPE(UDP_SOCKET)
SOURCE_TYPE(CERT_VERIFIER_JOB)
-SOURCE_TYPE(HTTP_PIPELINED_CONNECTION)
SOURCE_TYPE(DOWNLOAD)
SOURCE_TYPE(FILESTREAM)
SOURCE_TYPE(DNS_PROBER)
SOURCE_TYPE(PROXY_CLIENT_SOCKET)
+SOURCE_TYPE(IPV6_REACHABILITY_CHECK)
diff --git a/chromium/net/base/net_log_unittest.cc b/chromium/net/base/net_log_unittest.cc
index 7a98f797a4c..8f15e9cdf7a 100644
--- a/chromium/net/base/net_log_unittest.cc
+++ b/chromium/net/base/net_log_unittest.cc
@@ -5,6 +5,7 @@
#include "net/base/net_log_unittest.h"
#include "base/bind.h"
+#include "base/memory/scoped_vector.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/simple_thread.h"
#include "base/values.h"
@@ -41,11 +42,10 @@ TEST(NetLogTest, Basic) {
EXPECT_FALSE(entries[0].params);
}
-// Check that the correct LogLevel is sent to NetLog Value callbacks, and that
-// LOG_NONE logs no events.
+// Check that the correct LogLevel is sent to NetLog Value callbacks.
TEST(NetLogTest, LogLevels) {
CapturingNetLog net_log;
- for (int log_level = NetLog::LOG_ALL; log_level <= NetLog::LOG_NONE;
+ for (int log_level = NetLog::LOG_ALL; log_level < NetLog::LOG_NONE;
++log_level) {
net_log.SetLogLevel(static_cast<NetLog::LogLevel>(log_level));
EXPECT_EQ(log_level, net_log.GetLogLevel());
@@ -56,20 +56,16 @@ TEST(NetLogTest, LogLevels) {
CapturingNetLog::CapturedEntryList entries;
net_log.GetEntries(&entries);
- if (log_level == NetLog::LOG_NONE) {
- EXPECT_EQ(0u, entries.size());
- } else {
- ASSERT_EQ(1u, entries.size());
- EXPECT_EQ(NetLog::TYPE_SOCKET_ALIVE, entries[0].type);
- EXPECT_EQ(NetLog::SOURCE_NONE, entries[0].source.type);
- EXPECT_NE(NetLog::Source::kInvalidId, entries[0].source.id);
- EXPECT_EQ(NetLog::PHASE_NONE, entries[0].phase);
- EXPECT_GE(base::TimeTicks::Now(), entries[0].time);
-
- int logged_log_level;
- ASSERT_TRUE(entries[0].GetIntegerValue("log_level", &logged_log_level));
- EXPECT_EQ(log_level, logged_log_level);
- }
+ ASSERT_EQ(1u, entries.size());
+ EXPECT_EQ(NetLog::TYPE_SOCKET_ALIVE, entries[0].type);
+ EXPECT_EQ(NetLog::SOURCE_NONE, entries[0].source.type);
+ EXPECT_NE(NetLog::Source::kInvalidId, entries[0].source.id);
+ EXPECT_EQ(NetLog::PHASE_NONE, entries[0].phase);
+ EXPECT_GE(base::TimeTicks::Now(), entries[0].time);
+
+ int logged_log_level;
+ ASSERT_TRUE(entries[0].GetIntegerValue("log_level", &logged_log_level));
+ EXPECT_EQ(log_level, logged_log_level);
net_log.Clear();
}
@@ -94,8 +90,35 @@ class CountingObserver : public NetLog::ThreadSafeObserver {
int count_;
};
+class LoggingObserver : public NetLog::ThreadSafeObserver {
+ public:
+ LoggingObserver() {}
+
+ virtual ~LoggingObserver() {
+ if (net_log())
+ net_log()->RemoveThreadSafeObserver(this);
+ }
+
+ virtual void OnAddEntry(const NetLog::Entry& entry) OVERRIDE {
+ base::Value* value = entry.ToValue();
+ base::DictionaryValue* dict = NULL;
+ ASSERT_TRUE(value->GetAsDictionary(&dict));
+ values_.push_back(dict);
+ }
+
+ size_t GetNumValues() const { return values_.size(); }
+ base::DictionaryValue* GetValue(size_t index) const { return values_[index]; }
+
+ private:
+ ScopedVector<base::DictionaryValue> values_;
+};
+
+base::Value* LogLevelToValue(NetLog::LogLevel log_level) {
+ return new base::FundamentalValue(log_level);
+}
+
void AddEvent(NetLog* net_log) {
- net_log->AddGlobalEntry(NetLog::TYPE_CANCELLED);
+ net_log->AddGlobalEntry(NetLog::TYPE_CANCELLED, base::Bind(LogLevelToValue));
}
// A thread that waits until an event has been signalled before calling
@@ -164,11 +187,7 @@ class AddRemoveObserverTestThread : public NetLogTestThread {
for (int i = 0; i < kEvents; ++i) {
ASSERT_FALSE(observer_.net_log());
- net_log_->AddThreadSafeObserver(&observer_, NetLog::LOG_BASIC);
- ASSERT_EQ(net_log_, observer_.net_log());
- ASSERT_EQ(NetLog::LOG_BASIC, observer_.log_level());
-
- net_log_->SetObserverLogLevel(&observer_, NetLog::LOG_ALL_BUT_BYTES);
+ net_log_->AddThreadSafeObserver(&observer_, NetLog::LOG_ALL_BUT_BYTES);
ASSERT_EQ(net_log_, observer_.net_log());
ASSERT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer_.log_level());
ASSERT_LE(net_log_->GetLogLevel(), NetLog::LOG_ALL_BUT_BYTES);
@@ -214,7 +233,7 @@ TEST(NetLogTest, NetLogEventThreads) {
// safely detach themselves on destruction.
CountingObserver observers[3];
for (size_t i = 0; i < arraysize(observers); ++i)
- net_log.AddThreadSafeObserver(&observers[i], NetLog::LOG_BASIC);
+ net_log.AddThreadSafeObserver(&observers[i], NetLog::LOG_ALL);
// Run a bunch of threads to completion, each of which will emit events to
// |net_log|.
@@ -237,10 +256,10 @@ TEST(NetLogTest, NetLogAddRemoveObserver) {
EXPECT_EQ(NetLog::LOG_NONE, net_log.GetLogLevel());
// Add the observer and add an event.
- net_log.AddThreadSafeObserver(&observer, NetLog::LOG_BASIC);
+ net_log.AddThreadSafeObserver(&observer, NetLog::LOG_ALL_BUT_BYTES);
EXPECT_EQ(&net_log, observer.net_log());
- EXPECT_EQ(NetLog::LOG_BASIC, observer.log_level());
- EXPECT_EQ(NetLog::LOG_BASIC, net_log.GetLogLevel());
+ EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer.log_level());
+ EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, net_log.GetLogLevel());
AddEvent(&net_log);
EXPECT_EQ(1, observer.count());
@@ -272,10 +291,10 @@ TEST(NetLogTest, NetLogAddRemoveObserver) {
EXPECT_EQ(3, observer.count());
}
-// Test adding and removing two observers.
+// Test adding and removing two observers at different log levels.
TEST(NetLogTest, NetLogTwoObservers) {
NetLog net_log;
- CountingObserver observer[2];
+ LoggingObserver observer[2];
// Add first observer.
net_log.AddThreadSafeObserver(&observer[0], NetLog::LOG_ALL_BUT_BYTES);
@@ -292,10 +311,16 @@ TEST(NetLogTest, NetLogTwoObservers) {
EXPECT_EQ(NetLog::LOG_ALL, observer[1].log_level());
EXPECT_EQ(NetLog::LOG_ALL, net_log.GetLogLevel());
- // Add event and make sure both observers receive it.
+ // Add event and make sure both observers receive it at their respective log
+ // levels.
+ int param;
AddEvent(&net_log);
- EXPECT_EQ(1, observer[0].count());
- EXPECT_EQ(1, observer[1].count());
+ ASSERT_EQ(1U, observer[0].GetNumValues());
+ ASSERT_TRUE(observer[0].GetValue(0)->GetInteger("params", &param));
+ EXPECT_EQ(observer[0].log_level(), param);
+ ASSERT_EQ(1U, observer[1].GetNumValues());
+ ASSERT_TRUE(observer[1].GetValue(0)->GetInteger("params", &param));
+ EXPECT_EQ(observer[1].log_level(), param);
// Remove second observer.
net_log.RemoveThreadSafeObserver(&observer[1]);
@@ -306,8 +331,8 @@ TEST(NetLogTest, NetLogTwoObservers) {
// Add event and make sure only second observer gets it.
AddEvent(&net_log);
- EXPECT_EQ(2, observer[0].count());
- EXPECT_EQ(1, observer[1].count());
+ EXPECT_EQ(2U, observer[0].GetNumValues());
+ EXPECT_EQ(1U, observer[1].GetNumValues());
// Remove first observer.
net_log.RemoveThreadSafeObserver(&observer[0]);
@@ -317,8 +342,8 @@ TEST(NetLogTest, NetLogTwoObservers) {
// Add event and make sure neither observer gets it.
AddEvent(&net_log);
- EXPECT_EQ(2, observer[0].count());
- EXPECT_EQ(1, observer[1].count());
+ EXPECT_EQ(2U, observer[0].GetNumValues());
+ EXPECT_EQ(1U, observer[1].GetNumValues());
}
// Makes sure that adding and removing observers simultaneously on different
diff --git a/chromium/net/base/net_string_util.h b/chromium/net/base/net_string_util.h
new file mode 100644
index 00000000000..8e959a02f5e
--- /dev/null
+++ b/chromium/net/base/net_string_util.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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_BASE_NET_STRING_UTIL_H__
+#define NET_BASE_NET_STRING_UTIL_H__
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+// String conversion functions. By default, they're implemented with ICU, but
+// when building with USE_ICU_ALTERNATIVES, they use platform functions instead.
+
+namespace net {
+
+extern const char* const kCharsetLatin1;
+
+// Converts |text| using |charset| to UTF-8, and writes it to |output|.
+// On failure, returns false and |output| is cleared.
+bool ConvertToUtf8(const std::string& text, const char* charset,
+ std::string* output);
+
+// Converts |text| using |charset| to UTF-8, normalizes the result, and writes
+// it to |output|. On failure, returns false and |output| is cleared.
+bool ConvertToUtf8AndNormalize(const std::string& text, const char* charset,
+ std::string* output);
+
+// Converts |text| using |charset| to UTF-16, and writes it to |output|.
+// On failure, returns false and |output| is cleared.
+bool ConvertToUTF16(const std::string& text, const char* charset,
+ base::string16* output);
+
+// Converts |text| using |charset| to UTF-16, and writes it to |output|.
+// Any characters that can not be converted are replaced with U+FFFD.
+bool ConvertToUTF16WithSubstitutions(const std::string& text,
+ const char* charset,
+ base::string16* output);
+
+} // namespace net
+
+#endif // NET_BASE_NET_STRING_UTIL_H__
diff --git a/chromium/net/base/net_string_util_icu.cc b/chromium/net/base/net_string_util_icu.cc
new file mode 100644
index 00000000000..435f9de2488
--- /dev/null
+++ b/chromium/net/base/net_string_util_icu.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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/base/net_string_util.h"
+
+#include "base/i18n/i18n_constants.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/ucnv.h"
+
+namespace net {
+
+const char* const kCharsetLatin1 = base::kCodepageLatin1;
+
+bool ConvertToUtf8(const std::string& text, const char* charset,
+ std::string* output) {
+ output->clear();
+
+ UErrorCode err = U_ZERO_ERROR;
+ UConverter* converter(ucnv_open(charset, &err));
+ if (U_FAILURE(err))
+ return false;
+
+ // A single byte in a legacy encoding can be expanded to 3 bytes in UTF-8.
+ // A 'two-byte character' in a legacy encoding can be expanded to 4 bytes
+ // in UTF-8. Therefore, the expansion ratio is 3 at most. Add one for a
+ // trailing '\0'.
+ size_t output_length = text.length() * 3 + 1;
+ char* buf = WriteInto(output, output_length);
+ output_length = ucnv_toAlgorithmic(UCNV_UTF8, converter, buf, output_length,
+ text.data(), text.length(), &err);
+ ucnv_close(converter);
+ if (U_FAILURE(err)) {
+ output->clear();
+ return false;
+ }
+
+ output->resize(output_length);
+ return true;
+}
+
+bool ConvertToUtf8AndNormalize(const std::string& text, const char* charset,
+ std::string* output) {
+ return base::ConvertToUtf8AndNormalize(text, charset, output);
+}
+
+bool ConvertToUTF16(const std::string& text, const char* charset,
+ base::string16* output) {
+ return base::CodepageToUTF16(text, charset,
+ base::OnStringConversionError::FAIL, output);
+}
+
+bool ConvertToUTF16WithSubstitutions(const std::string& text,
+ const char* charset,
+ base::string16* output) {
+ return base::CodepageToUTF16(text, charset,
+ base::OnStringConversionError::SUBSTITUTE,
+ output);
+}
+
+} // namespace net
diff --git a/chromium/net/base/net_string_util_icu_alternatives_android.cc b/chromium/net/base/net_string_util_icu_alternatives_android.cc
new file mode 100644
index 00000000000..ca0630328f5
--- /dev/null
+++ b/chromium/net/base/net_string_util_icu_alternatives_android.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 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/base/net_string_util_icu_alternatives_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "jni/NetStringUtil_jni.h"
+#include "net/base/net_string_util.h"
+
+namespace net {
+
+namespace {
+
+// Attempts to convert |text| encoded in |charset| to a jstring (Java unicode
+// string). Returns the result jstring, or NULL on failure.
+ScopedJavaLocalRef<jstring> ConvertToJstring(const std::string& text,
+ const char* charset) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ jobject java_byte_buffer =
+ env->NewDirectByteBuffer(const_cast<char*>(text.data()), text.length());
+ base::android::ScopedJavaLocalRef<jstring> java_charset =
+ base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset));
+ ScopedJavaLocalRef<jstring> java_result =
+ android::Java_NetStringUtil_convertToUnicode(env, java_byte_buffer,
+ java_charset.obj());
+ return java_result;
+}
+
+// Attempts to convert |text| encoded in |charset| to a jstring (Java unicode
+// string) and then normalizes the string. Returns the result jstring, or NULL
+// on failure.
+ScopedJavaLocalRef<jstring> ConvertToNormalizedJstring(
+ const std::string& text, const char* charset) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ jobject java_byte_buffer =
+ env->NewDirectByteBuffer(const_cast<char*>(text.data()), text.length());
+ base::android::ScopedJavaLocalRef<jstring> java_charset =
+ base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset));
+ ScopedJavaLocalRef<jstring> java_result =
+ android::Java_NetStringUtil_convertToUnicodeAndNormalize(
+ env, java_byte_buffer, java_charset.obj());
+ return java_result;
+}
+
+// Converts |text| encoded in |charset| to a jstring (Java unicode string).
+// Any characters that can not be converted are replaced with U+FFFD.
+ScopedJavaLocalRef<jstring> ConvertToJstringWithSubstitutions(
+ const std::string& text, const char* charset) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ jobject java_byte_buffer =
+ env->NewDirectByteBuffer(const_cast<char*>(text.data()), text.length());
+ base::android::ScopedJavaLocalRef<jstring> java_charset =
+ base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset));
+ ScopedJavaLocalRef<jstring> java_result =
+ android::Java_NetStringUtil_convertToUnicodeWithSubstitutions(
+ env, java_byte_buffer, java_charset.obj());
+ return java_result;
+}
+
+} // namespace
+
+const char* const kCharsetLatin1 = "ISO-8859-1";
+
+bool ConvertToUtf8(const std::string& text, const char* charset,
+ std::string* output) {
+ output->clear();
+ ScopedJavaLocalRef<jstring> java_result = ConvertToJstring(text, charset);
+ if (java_result.is_null())
+ return false;
+ *output = base::android::ConvertJavaStringToUTF8(java_result);
+ return true;
+}
+
+bool ConvertToUtf8AndNormalize(const std::string& text, const char* charset,
+ std::string* output) {
+ output->clear();
+ ScopedJavaLocalRef<jstring> java_result = ConvertToNormalizedJstring(
+ text, charset);
+ if (java_result.is_null())
+ return false;
+ *output = base::android::ConvertJavaStringToUTF8(java_result);
+ return true;
+}
+
+bool ConvertToUTF16(const std::string& text, const char* charset,
+ base::string16* output) {
+ output->clear();
+ ScopedJavaLocalRef<jstring> java_result = ConvertToJstring(text, charset);
+ if (java_result.is_null())
+ return false;
+ *output = base::android::ConvertJavaStringToUTF16(java_result);
+ return true;
+}
+
+bool ConvertToUTF16WithSubstitutions(const std::string& text,
+ const char* charset,
+ base::string16* output) {
+ output->clear();
+ ScopedJavaLocalRef<jstring> java_result =
+ ConvertToJstringWithSubstitutions(text, charset);
+ if (java_result.is_null())
+ return false;
+ *output = base::android::ConvertJavaStringToUTF16(java_result);
+ return true;
+}
+
+bool RegisterNetStringUtils(JNIEnv* env) {
+ return android::RegisterNativesImpl(env);
+}
+
+} // namespace net
diff --git a/chromium/net/base/net_string_util_icu_alternatives_android.h b/chromium/net/base/net_string_util_icu_alternatives_android.h
new file mode 100644
index 00000000000..c3a6cf507e6
--- /dev/null
+++ b/chromium/net/base/net_string_util_icu_alternatives_android.h
@@ -0,0 +1,18 @@
+// Copyright 2014 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_BASE_NET_STRING_UTIL_ICU_ALTERNATIVES_ANDROID_H_
+#define NET_BASE_NET_STRING_UTIL_ICU_ALTERNATIVES_ANDROID_H_
+
+#include <jni.h>
+
+namespace net {
+
+// Explicitly register static JNI functions needed when not using ICU.
+bool RegisterNetStringUtils(JNIEnv* env);
+
+} // namespace net
+
+#endif // NET_BASE_NET_STRING_UTIL_ICU_ALTERNATIVES_ANDROID_H_
+
diff --git a/chromium/net/base/net_util.cc b/chromium/net/base/net_util.cc
index 1f69ee71800..5b3d65e515b 100644
--- a/chromium/net/base/net_util.cc
+++ b/chromium/net/base/net_util.cc
@@ -4,9 +4,11 @@
#include "net/base/net_util.h"
+#include <errno.h>
+
#include <algorithm>
#include <iterator>
-#include <map>
+#include <set>
#include "build/build_config.h"
@@ -14,81 +16,54 @@
#include <windows.h>
#include <iphlpapi.h>
#include <winsock2.h>
+#include <ws2bth.h>
#pragma comment(lib, "iphlpapi.lib")
#elif defined(OS_POSIX)
#include <fcntl.h>
-#if !defined(OS_ANDROID)
-#include <ifaddrs.h>
-#endif
-#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
-#endif
+#include <unistd.h>
+#if !defined(OS_NACL)
+#include <net/if.h>
+#if !defined(OS_ANDROID)
+#include <ifaddrs.h>
+#endif // !defined(OS_NACL)
+#endif // !defined(OS_ANDROID)
+#endif // defined(OS_POSIX)
#include "base/basictypes.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/i18n/file_util_icu.h"
-#include "base/i18n/icu_string_conversions.h"
-#include "base/i18n/time_formatting.h"
#include "base/json/string_escape.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram.h"
-#include "base/path_service.h"
-#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
-#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_offset_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/lock.h"
#include "base/sys_byteorder.h"
-#include "base/time/time.h"
#include "base/values.h"
#include "grit/net_resources.h"
#include "url/gurl.h"
#include "url/url_canon.h"
#include "url/url_canon_ip.h"
#include "url/url_parse.h"
-#if defined(OS_ANDROID)
-#include "net/android/network_library.h"
-#endif
#include "net/base/dns_util.h"
-#include "net/base/escape.h"
-#include "net/base/mime_util.h"
#include "net/base/net_module.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/http/http_content_disposition.h"
+
+#if defined(OS_ANDROID)
+#include "net/android/network_library.h"
+#endif
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
-#include "net/http/http_content_disposition.h"
-#include "third_party/icu/source/common/unicode/uidna.h"
-#include "third_party/icu/source/common/unicode/uniset.h"
-#include "third_party/icu/source/common/unicode/uscript.h"
-#include "third_party/icu/source/common/unicode/uset.h"
-#include "third_party/icu/source/i18n/unicode/datefmt.h"
-#include "third_party/icu/source/i18n/unicode/regex.h"
-#include "third_party/icu/source/i18n/unicode/ulocdata.h"
-
-using base::Time;
namespace net {
namespace {
-typedef std::vector<size_t> Offsets;
-
-// what we prepend to get a file URL
-static const base::FilePath::CharType kFileURLPrefix[] =
- FILE_PATH_LITERAL("file:///");
-
// The general list of blocked ports. Will be blocked unless a specific
// protocol overrides it. (Ex: ftp can use ports 20 and 21)
static const int kRestrictedPorts[] = {
@@ -157,7 +132,8 @@ static const int kRestrictedPorts[] = {
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]
0xFFFF, // Used to block all invalid port numbers (see
- // third_party/WebKit/Source/WebCore/platform/KURLGoogle.cpp, port())
+ // third_party/WebKit/Source/platform/weborigin/KURL.cpp,
+ // KURL::port())
};
// FTP overrides the following restricted ports.
@@ -166,793 +142,6 @@ static const int kAllowedFtpPorts[] = {
22, // ssh
};
-// Does some simple normalization of scripts so we can allow certain scripts
-// to exist together.
-// TODO(brettw) bug 880223: we should allow some other languages to be
-// oombined such as Chinese and Latin. We will probably need a more
-// complicated system of language pairs to have more fine-grained control.
-UScriptCode NormalizeScript(UScriptCode code) {
- switch (code) {
- case USCRIPT_KATAKANA:
- case USCRIPT_HIRAGANA:
- case USCRIPT_KATAKANA_OR_HIRAGANA:
- case USCRIPT_HANGUL: // This one is arguable.
- return USCRIPT_HAN;
- default:
- return code;
- }
-}
-
-bool IsIDNComponentInSingleScript(const base::char16* str, int str_len) {
- UScriptCode first_script = USCRIPT_INVALID_CODE;
- bool is_first = true;
-
- int i = 0;
- while (i < str_len) {
- unsigned code_point;
- U16_NEXT(str, i, str_len, code_point);
-
- UErrorCode err = U_ZERO_ERROR;
- UScriptCode cur_script = uscript_getScript(code_point, &err);
- if (err != U_ZERO_ERROR)
- return false; // Report mixed on error.
- cur_script = NormalizeScript(cur_script);
-
- // TODO(brettw) We may have to check for USCRIPT_INHERENT as well.
- if (is_first && cur_script != USCRIPT_COMMON) {
- first_script = cur_script;
- is_first = false;
- } else {
- if (cur_script != USCRIPT_COMMON && cur_script != first_script)
- return false;
- }
- }
- return true;
-}
-
-// Check if the script of a language can be 'safely' mixed with
-// Latin letters in the ASCII range.
-bool IsCompatibleWithASCIILetters(const std::string& lang) {
- // For now, just list Chinese, Japanese and Korean (positive list).
- // An alternative is negative-listing (languages using Greek and
- // Cyrillic letters), but it can be more dangerous.
- return !lang.substr(0, 2).compare("zh") ||
- !lang.substr(0, 2).compare("ja") ||
- !lang.substr(0, 2).compare("ko");
-}
-
-typedef std::map<std::string, icu::UnicodeSet*> LangToExemplarSetMap;
-
-class LangToExemplarSet {
- public:
- static LangToExemplarSet* GetInstance() {
- return Singleton<LangToExemplarSet>::get();
- }
-
- private:
- LangToExemplarSetMap map;
- LangToExemplarSet() { }
- ~LangToExemplarSet() {
- STLDeleteContainerPairSecondPointers(map.begin(), map.end());
- }
-
- friend class Singleton<LangToExemplarSet>;
- friend struct DefaultSingletonTraits<LangToExemplarSet>;
- friend bool GetExemplarSetForLang(const std::string&, icu::UnicodeSet**);
- friend void SetExemplarSetForLang(const std::string&, icu::UnicodeSet*);
-
- DISALLOW_COPY_AND_ASSIGN(LangToExemplarSet);
-};
-
-bool GetExemplarSetForLang(const std::string& lang,
- icu::UnicodeSet** lang_set) {
- const LangToExemplarSetMap& map = LangToExemplarSet::GetInstance()->map;
- LangToExemplarSetMap::const_iterator pos = map.find(lang);
- if (pos != map.end()) {
- *lang_set = pos->second;
- return true;
- }
- return false;
-}
-
-void SetExemplarSetForLang(const std::string& lang,
- icu::UnicodeSet* lang_set) {
- LangToExemplarSetMap& map = LangToExemplarSet::GetInstance()->map;
- map.insert(std::make_pair(lang, lang_set));
-}
-
-static base::LazyInstance<base::Lock>::Leaky
- g_lang_set_lock = LAZY_INSTANCE_INITIALIZER;
-
-// Returns true if all the characters in component_characters are used by
-// the language |lang|.
-bool IsComponentCoveredByLang(const icu::UnicodeSet& component_characters,
- const std::string& lang) {
- CR_DEFINE_STATIC_LOCAL(
- const icu::UnicodeSet, kASCIILetters, ('a', 'z'));
- icu::UnicodeSet* lang_set = NULL;
- // We're called from both the UI thread and the history thread.
- {
- base::AutoLock lock(g_lang_set_lock.Get());
- if (!GetExemplarSetForLang(lang, &lang_set)) {
- UErrorCode status = U_ZERO_ERROR;
- ULocaleData* uld = ulocdata_open(lang.c_str(), &status);
- // TODO(jungshik) Turn this check on when the ICU data file is
- // rebuilt with the minimal subset of locale data for languages
- // to which Chrome is not localized but which we offer in the list
- // of languages selectable for Accept-Languages. With the rebuilt ICU
- // data, ulocdata_open never should fall back to the default locale.
- // (issue 2078)
- // DCHECK(U_SUCCESS(status) && status != U_USING_DEFAULT_WARNING);
- if (U_SUCCESS(status) && status != U_USING_DEFAULT_WARNING) {
- lang_set = reinterpret_cast<icu::UnicodeSet *>(
- ulocdata_getExemplarSet(uld, NULL, 0,
- ULOCDATA_ES_STANDARD, &status));
- // If |lang| is compatible with ASCII Latin letters, add them.
- if (IsCompatibleWithASCIILetters(lang))
- lang_set->addAll(kASCIILetters);
- } else {
- lang_set = new icu::UnicodeSet(1, 0);
- }
- lang_set->freeze();
- SetExemplarSetForLang(lang, lang_set);
- ulocdata_close(uld);
- }
- }
- return !lang_set->isEmpty() && lang_set->containsAll(component_characters);
-}
-
-// Returns true if the given Unicode host component is safe to display to the
-// user.
-bool IsIDNComponentSafe(const base::char16* str,
- int str_len,
- const std::string& languages) {
- // Most common cases (non-IDN) do not reach here so that we don't
- // need a fast return path.
- // TODO(jungshik) : Check if there's any character inappropriate
- // (although allowed) for domain names.
- // See http://www.unicode.org/reports/tr39/#IDN_Security_Profiles and
- // http://www.unicode.org/reports/tr39/data/xidmodifications.txt
- // For now, we borrow the list from Mozilla and tweaked it slightly.
- // (e.g. Characters like U+00A0, U+3000, U+3002 are omitted because
- // they're gonna be canonicalized to U+0020 and full stop before
- // reaching here.)
- // The original list is available at
- // http://kb.mozillazine.org/Network.IDN.blacklist_chars and
- // at http://mxr.mozilla.org/seamonkey/source/modules/libpref/src/init/all.js#703
-
- UErrorCode status = U_ZERO_ERROR;
-#ifdef U_WCHAR_IS_UTF16
- icu::UnicodeSet dangerous_characters(icu::UnicodeString(
- L"[[\\ \u00ad\u00bc\u00bd\u01c3\u0337\u0338"
- L"\u05c3\u05f4\u06d4\u0702\u115f\u1160][\u2000-\u200b]"
- L"[\u2024\u2027\u2028\u2029\u2039\u203a\u2044\u205f]"
- L"[\u2154-\u2156][\u2159-\u215b][\u215f\u2215\u23ae"
- L"\u29f6\u29f8\u2afb\u2afd][\u2ff0-\u2ffb][\u3014"
- L"\u3015\u3033\u3164\u321d\u321e\u33ae\u33af\u33c6\u33df\ufe14"
- L"\ufe15\ufe3f\ufe5d\ufe5e\ufeff\uff0e\uff06\uff61\uffa0\ufff9]"
- L"[\ufffa-\ufffd]]"), status);
- DCHECK(U_SUCCESS(status));
- icu::RegexMatcher dangerous_patterns(icu::UnicodeString(
- // Lone katakana no, so, or n
- L"[^\\p{Katakana}][\u30ce\u30f3\u30bd][^\\p{Katakana}]"
- // Repeating Japanese accent characters
- L"|[\u3099\u309a\u309b\u309c][\u3099\u309a\u309b\u309c]"),
- 0, status);
-#else
- icu::UnicodeSet dangerous_characters(icu::UnicodeString(
- "[[\\u0020\\u00ad\\u00bc\\u00bd\\u01c3\\u0337\\u0338"
- "\\u05c3\\u05f4\\u06d4\\u0702\\u115f\\u1160][\\u2000-\\u200b]"
- "[\\u2024\\u2027\\u2028\\u2029\\u2039\\u203a\\u2044\\u205f]"
- "[\\u2154-\\u2156][\\u2159-\\u215b][\\u215f\\u2215\\u23ae"
- "\\u29f6\\u29f8\\u2afb\\u2afd][\\u2ff0-\\u2ffb][\\u3014"
- "\\u3015\\u3033\\u3164\\u321d\\u321e\\u33ae\\u33af\\u33c6\\u33df\\ufe14"
- "\\ufe15\\ufe3f\\ufe5d\\ufe5e\\ufeff\\uff0e\\uff06\\uff61\\uffa0\\ufff9]"
- "[\\ufffa-\\ufffd]]", -1, US_INV), status);
- DCHECK(U_SUCCESS(status));
- icu::RegexMatcher dangerous_patterns(icu::UnicodeString(
- // Lone katakana no, so, or n
- "[^\\p{Katakana}][\\u30ce\\u30f3\u30bd][^\\p{Katakana}]"
- // Repeating Japanese accent characters
- "|[\\u3099\\u309a\\u309b\\u309c][\\u3099\\u309a\\u309b\\u309c]"),
- 0, status);
-#endif
- DCHECK(U_SUCCESS(status));
- icu::UnicodeSet component_characters;
- icu::UnicodeString component_string(str, str_len);
- component_characters.addAll(component_string);
- if (dangerous_characters.containsSome(component_characters))
- return false;
-
- DCHECK(U_SUCCESS(status));
- dangerous_patterns.reset(component_string);
- if (dangerous_patterns.find())
- return false;
-
- // If the language list is empty, the result is completely determined
- // by whether a component is a single script or not. This will block
- // even "safe" script mixing cases like <Chinese, Latin-ASCII> that are
- // allowed with |languages| (while it blocks Chinese + Latin letters with
- // an accent as should be the case), but we want to err on the safe side
- // when |languages| is empty.
- if (languages.empty())
- return IsIDNComponentInSingleScript(str, str_len);
-
- // |common_characters| is made up of ASCII numbers, hyphen, plus and
- // underscore that are used across scripts and allowed in domain names.
- // (sync'd with characters allowed in url_canon_host with square
- // brackets excluded.) See kHostCharLookup[] array in url_canon_host.cc.
- icu::UnicodeSet common_characters(UNICODE_STRING_SIMPLE("[[0-9]\\-_+\\ ]"),
- status);
- DCHECK(U_SUCCESS(status));
- // Subtract common characters because they're always allowed so that
- // we just have to check if a language-specific set contains
- // the remainder.
- component_characters.removeAll(common_characters);
-
- base::StringTokenizer t(languages, ",");
- while (t.GetNext()) {
- if (IsComponentCoveredByLang(component_characters, t.token()))
- return true;
- }
- return false;
-}
-
-// A wrapper to use LazyInstance<>::Leaky with ICU's UIDNA, a C pointer to
-// a UTS46/IDNA 2008 handling object opened with uidna_openUTS46().
-//
-// We use UTS46 with BiDiCheck to migrate from IDNA 2003 to IDNA 2008 with
-// the backward compatibility in mind. What it does:
-//
-// 1. Use the up-to-date Unicode data.
-// 2. Define a case folding/mapping with the up-to-date Unicode data as
-// in IDNA 2003.
-// 3. Use transitional mechanism for 4 deviation characters (sharp-s,
-// final sigma, ZWJ and ZWNJ) for now.
-// 4. Continue to allow symbols and punctuations.
-// 5. Apply new BiDi check rules more permissive than the IDNA 2003 BiDI rules.
-// 6. Do not apply STD3 rules
-// 7. Do not allow unassigned code points.
-//
-// It also closely matches what IE 10 does except for the BiDi check (
-// http://goo.gl/3XBhqw ).
-// See http://http://unicode.org/reports/tr46/ and references therein
-// for more details.
-struct UIDNAWrapper {
- UIDNAWrapper() {
- UErrorCode err = U_ZERO_ERROR;
- // TODO(jungshik): Change options as different parties (browsers,
- // registrars, search engines) converge toward a consensus.
- value = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
- if (U_FAILURE(err))
- value = NULL;
- }
-
- UIDNA* value;
-};
-
-static base::LazyInstance<UIDNAWrapper>::Leaky
- g_uidna = LAZY_INSTANCE_INITIALIZER;
-
-// Converts one component of a host (between dots) to IDN if safe. The result
-// will be APPENDED to the given output string and will be the same as the input
-// if it is not IDN or the IDN is unsafe to display. Returns whether any
-// conversion was performed.
-bool IDNToUnicodeOneComponent(const base::char16* comp,
- size_t comp_len,
- const std::string& languages,
- base::string16* out) {
- DCHECK(out);
- if (comp_len == 0)
- return false;
-
- // Only transform if the input can be an IDN component.
- static const base::char16 kIdnPrefix[] = {'x', 'n', '-', '-'};
- if ((comp_len > arraysize(kIdnPrefix)) &&
- !memcmp(comp, kIdnPrefix, arraysize(kIdnPrefix) * sizeof(base::char16))) {
- UIDNA* uidna = g_uidna.Get().value;
- DCHECK(uidna != NULL);
- size_t original_length = out->length();
- int output_length = 64;
- UIDNAInfo info = UIDNA_INFO_INITIALIZER;
- UErrorCode status;
- do {
- out->resize(original_length + output_length);
- status = U_ZERO_ERROR;
- // This returns the actual length required. If this is more than 64
- // code units, |status| will be U_BUFFER_OVERFLOW_ERROR and we'll try
- // the conversion again, but with a sufficiently large buffer.
- output_length = uidna_labelToUnicode(
- uidna, comp, static_cast<int32_t>(comp_len), &(*out)[original_length],
- output_length, &info, &status);
- } while ((status == U_BUFFER_OVERFLOW_ERROR && info.errors == 0));
-
- if (U_SUCCESS(status) && info.errors == 0) {
- // Converted successfully. Ensure that the converted component
- // can be safely displayed to the user.
- out->resize(original_length + output_length);
- if (IsIDNComponentSafe(out->data() + original_length, output_length,
- languages))
- return true;
- }
-
- // Something went wrong. Revert to original string.
- out->resize(original_length);
- }
-
- // We get here with no IDN or on error, in which case we just append the
- // literal input.
- out->append(comp, comp_len);
- return false;
-}
-
-// Clamps the offsets in |offsets_for_adjustment| to the length of |str|.
-void LimitOffsets(const base::string16& str, Offsets* offsets_for_adjustment) {
- if (offsets_for_adjustment) {
- std::for_each(offsets_for_adjustment->begin(),
- offsets_for_adjustment->end(),
- base::LimitOffset<base::string16>(str.length()));
- }
-}
-
-// TODO(brettw) bug 734373: check the scripts for each host component and
-// don't un-IDN-ize if there is more than one. Alternatively, only IDN for
-// scripts that the user has installed. For now, just put the entire
-// path through IDN. Maybe this feature can be implemented in ICU itself?
-//
-// We may want to skip this step in the case of file URLs to allow unicode
-// UNC hostnames regardless of encodings.
-base::string16 IDNToUnicodeWithOffsets(const std::string& host,
- const std::string& languages,
- Offsets* offsets_for_adjustment) {
- // Convert the ASCII input to a base::string16 for ICU.
- base::string16 input16;
- input16.reserve(host.length());
- input16.insert(input16.end(), host.begin(), host.end());
-
- // Do each component of the host separately, since we enforce script matching
- // on a per-component basis.
- base::string16 out16;
- {
- base::OffsetAdjuster offset_adjuster(offsets_for_adjustment);
- for (size_t component_start = 0, component_end;
- component_start < input16.length();
- component_start = component_end + 1) {
- // Find the end of the component.
- component_end = input16.find('.', component_start);
- if (component_end == base::string16::npos)
- component_end = input16.length(); // For getting the last component.
- size_t component_length = component_end - component_start;
- size_t new_component_start = out16.length();
- bool converted_idn = false;
- if (component_end > component_start) {
- // Add the substring that we just found.
- converted_idn = IDNToUnicodeOneComponent(
- input16.data() + component_start, component_length, languages,
- &out16);
- }
- size_t new_component_length = out16.length() - new_component_start;
-
- if (converted_idn && offsets_for_adjustment) {
- offset_adjuster.Add(base::OffsetAdjuster::Adjustment(component_start,
- component_length, new_component_length));
- }
-
- // Need to add the dot we just found (if we found one).
- if (component_end < input16.length())
- out16.push_back('.');
- }
- }
-
- LimitOffsets(out16, offsets_for_adjustment);
- return out16;
-}
-
-// Called after transforming a component to set all affected elements in
-// |offsets_for_adjustment| to the correct new values. |original_offsets|
-// represents the offsets before the transform; |original_component_begin| and
-// |original_component_end| represent the pre-transform boundaries of the
-// affected component. |transformed_offsets| should be a vector created by
-// adjusting |original_offsets| to be relative to the beginning of the component
-// in question (via an OffsetAdjuster) and then transformed along with the
-// component. Note that any elements in this vector which didn't originally
-// point into the component may contain arbitrary values and should be ignored.
-// |transformed_component_begin| and |transformed_component_end| are the
-// endpoints of the transformed component and are used in combination with the
-// two offset vectors to calculate the resulting absolute offsets, which are
-// stored in |offsets_for_adjustment|.
-void AdjustForComponentTransform(const Offsets& original_offsets,
- size_t original_component_begin,
- size_t original_component_end,
- const Offsets& transformed_offsets,
- size_t transformed_component_begin,
- size_t transformed_component_end,
- Offsets* offsets_for_adjustment) {
- if (!offsets_for_adjustment)
- return; // Nothing to do.
-
- for (size_t i = 0; i < original_offsets.size(); ++i) {
- size_t original_offset = original_offsets[i];
- if ((original_offset >= original_component_begin) &&
- (original_offset < original_component_end)) {
- // This offset originally pointed into the transformed component.
- // Adjust the transformed relative offset by the new beginning point of
- // the transformed component.
- size_t transformed_offset = transformed_offsets[i];
- (*offsets_for_adjustment)[i] =
- (transformed_offset == base::string16::npos) ?
- base::string16::npos :
- (transformed_offset + transformed_component_begin);
- } else if ((original_offset >= original_component_end) &&
- (original_offset != std::string::npos)) {
- // This offset pointed after the transformed component. Adjust the
- // original absolute offset by the difference between the new and old
- // component lengths.
- (*offsets_for_adjustment)[i] =
- original_offset - original_component_end + transformed_component_end;
- }
- }
-}
-
-// If |component| is valid, its begin is incremented by |delta|.
-void AdjustComponent(int delta, url_parse::Component* component) {
- if (!component->is_valid())
- return;
-
- DCHECK(delta >= 0 || component->begin >= -delta);
- component->begin += delta;
-}
-
-// Adjusts all the components of |parsed| by |delta|, except for the scheme.
-void AdjustAllComponentsButScheme(int delta, url_parse::Parsed* parsed) {
- AdjustComponent(delta, &(parsed->username));
- AdjustComponent(delta, &(parsed->password));
- AdjustComponent(delta, &(parsed->host));
- AdjustComponent(delta, &(parsed->port));
- AdjustComponent(delta, &(parsed->path));
- AdjustComponent(delta, &(parsed->query));
- AdjustComponent(delta, &(parsed->ref));
-}
-
-// Helper for FormatUrlWithOffsets().
-base::string16 FormatViewSourceUrl(const GURL& url,
- const Offsets& original_offsets,
- const std::string& languages,
- FormatUrlTypes format_types,
- UnescapeRule::Type unescape_rules,
- url_parse::Parsed* new_parsed,
- size_t* prefix_end,
- Offsets* offsets_for_adjustment) {
- DCHECK(new_parsed);
- const char kViewSource[] = "view-source:";
- const size_t kViewSourceLength = arraysize(kViewSource) - 1;
-
- // Format the underlying URL and adjust offsets.
- const std::string& url_str(url.possibly_invalid_spec());
- Offsets offsets_into_underlying_url(original_offsets);
- {
- base::OffsetAdjuster adjuster(&offsets_into_underlying_url);
- adjuster.Add(base::OffsetAdjuster::Adjustment(0, kViewSourceLength, 0));
- }
- base::string16 result(ASCIIToUTF16(kViewSource) +
- FormatUrlWithOffsets(GURL(url_str.substr(kViewSourceLength)), languages,
- format_types, unescape_rules, new_parsed, prefix_end,
- &offsets_into_underlying_url));
- AdjustForComponentTransform(original_offsets, kViewSourceLength,
- url_str.length(), offsets_into_underlying_url,
- kViewSourceLength, result.length(),
- offsets_for_adjustment);
- LimitOffsets(result, offsets_for_adjustment);
-
- // Adjust positions of the parsed components.
- if (new_parsed->scheme.is_nonempty()) {
- // Assume "view-source:real-scheme" as a scheme.
- new_parsed->scheme.len += kViewSourceLength;
- } else {
- new_parsed->scheme.begin = 0;
- new_parsed->scheme.len = kViewSourceLength - 1;
- }
- AdjustAllComponentsButScheme(kViewSourceLength, new_parsed);
-
- if (prefix_end)
- *prefix_end += kViewSourceLength;
-
- return result;
-}
-
-class AppendComponentTransform {
- public:
- AppendComponentTransform() {}
- virtual ~AppendComponentTransform() {}
-
- virtual base::string16 Execute(const std::string& component_text,
- Offsets* offsets_into_component) const = 0;
-
- // NOTE: No DISALLOW_COPY_AND_ASSIGN here, since gcc < 4.3.0 requires an
- // accessible copy constructor in order to call AppendFormattedComponent()
- // with an inline temporary (see http://gcc.gnu.org/bugs/#cxx%5Frvalbind ).
-};
-
-class HostComponentTransform : public AppendComponentTransform {
- public:
- explicit HostComponentTransform(const std::string& languages)
- : languages_(languages) {
- }
-
- private:
- virtual base::string16 Execute(
- const std::string& component_text,
- Offsets* offsets_into_component) const OVERRIDE {
- return IDNToUnicodeWithOffsets(component_text, languages_,
- offsets_into_component);
- }
-
- const std::string& languages_;
-};
-
-class NonHostComponentTransform : public AppendComponentTransform {
- public:
- explicit NonHostComponentTransform(UnescapeRule::Type unescape_rules)
- : unescape_rules_(unescape_rules) {
- }
-
- private:
- virtual base::string16 Execute(
- const std::string& component_text,
- Offsets* offsets_into_component) const OVERRIDE {
- return (unescape_rules_ == UnescapeRule::NONE) ?
- base::UTF8ToUTF16AndAdjustOffsets(component_text,
- offsets_into_component) :
- UnescapeAndDecodeUTF8URLComponentWithOffsets(component_text,
- unescape_rules_, offsets_into_component);
- }
-
- const UnescapeRule::Type unescape_rules_;
-};
-
-// Transforms the portion of |spec| covered by |original_component| according to
-// |transform|. Appends the result to |output|. If |output_component| is
-// non-NULL, its start and length are set to the transformed component's new
-// start and length. For each element in |original_offsets| which is at least
-// as large as original_component.begin, the corresponding element of
-// |offsets_for_adjustment| is transformed appropriately.
-void AppendFormattedComponent(const std::string& spec,
- const url_parse::Component& original_component,
- const Offsets& original_offsets,
- const AppendComponentTransform& transform,
- base::string16* output,
- url_parse::Component* output_component,
- Offsets* offsets_for_adjustment) {
- DCHECK(output);
- if (original_component.is_nonempty()) {
- size_t original_component_begin =
- static_cast<size_t>(original_component.begin);
- size_t output_component_begin = output->length();
- std::string component_str(spec, original_component_begin,
- static_cast<size_t>(original_component.len));
-
- // Transform |component_str| and adjust the offsets accordingly.
- Offsets offsets_into_component(original_offsets);
- {
- base::OffsetAdjuster adjuster(&offsets_into_component);
- adjuster.Add(base::OffsetAdjuster::Adjustment(0, original_component_begin,
- 0));
- }
- output->append(transform.Execute(component_str, &offsets_into_component));
- AdjustForComponentTransform(original_offsets, original_component_begin,
- static_cast<size_t>(original_component.end()),
- offsets_into_component, output_component_begin,
- output->length(), offsets_for_adjustment);
-
- // Set positions of the parsed component.
- if (output_component) {
- output_component->begin = static_cast<int>(output_component_begin);
- output_component->len =
- static_cast<int>(output->length() - output_component_begin);
- }
- } else if (output_component) {
- output_component->reset();
- }
-}
-
-void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
- bool replace_trailing) {
- const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("-");
- if (filename->empty())
- return;
- if (replace_trailing) {
- // Handle CreateFile() stripping trailing dots and spaces on filenames
- // http://support.microsoft.com/kb/115827
- size_t length = filename->size();
- size_t pos = filename->find_last_not_of(FILE_PATH_LITERAL(" ."));
- filename->resize((pos == std::string::npos) ? 0 : (pos + 1));
- TrimWhitespace(*filename, TRIM_TRAILING, filename);
- if (filename->empty())
- return;
- size_t trimmed = length - filename->size();
- if (trimmed)
- filename->insert(filename->end(), trimmed, kReplace[0]);
- }
- base::TrimString(*filename, FILE_PATH_LITERAL("."), filename);
- if (filename->empty())
- return;
- // Replace any path information by changing path separators.
- ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("/"), kReplace);
- ReplaceSubstringsAfterOffset(filename, 0, FILE_PATH_LITERAL("\\"), kReplace);
-}
-
-// Returns the filename determined from the last component of the path portion
-// of the URL. Returns an empty string if the URL doesn't have a path or is
-// invalid. If the generated filename is not reliable,
-// |should_overwrite_extension| will be set to true, in which case a better
-// extension should be determined based on the content type.
-std::string GetFileNameFromURL(const GURL& url,
- const std::string& referrer_charset,
- bool* should_overwrite_extension) {
- // about: and data: URLs don't have file names, but esp. data: URLs may
- // contain parts that look like ones (i.e., contain a slash). Therefore we
- // don't attempt to divine a file name out of them.
- if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data"))
- return std::string();
-
- const std::string unescaped_url_filename = UnescapeURLComponent(
- url.ExtractFileName(),
- UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
-
- // The URL's path should be escaped UTF-8, but may not be.
- std::string decoded_filename = unescaped_url_filename;
- if (!IsStringUTF8(decoded_filename)) {
- // TODO(jshin): this is probably not robust enough. To be sure, we need
- // encoding detection.
- base::string16 utf16_output;
- if (!referrer_charset.empty() &&
- base::CodepageToUTF16(unescaped_url_filename,
- referrer_charset.c_str(),
- base::OnStringConversionError::FAIL,
- &utf16_output)) {
- decoded_filename = UTF16ToUTF8(utf16_output);
- } else {
- decoded_filename = WideToUTF8(
- base::SysNativeMBToWide(unescaped_url_filename));
- }
- }
- // If the URL contains a (possibly empty) query, assume it is a generator, and
- // allow the determined extension to be overwritten.
- *should_overwrite_extension = !decoded_filename.empty() && url.has_query();
-
- return decoded_filename;
-}
-
-// Returns whether the specified extension is automatically integrated into the
-// windows shell.
-bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
- base::FilePath::StringType extension_lower = StringToLowerASCII(extension);
-
- // http://msdn.microsoft.com/en-us/library/ms811694.aspx
- // Right-clicking on shortcuts can be magical.
- if ((extension_lower == FILE_PATH_LITERAL("local")) ||
- (extension_lower == FILE_PATH_LITERAL("lnk")))
- return true;
-
- // http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html
- // Files become magical if they end in a CLSID, so block such extensions.
- if (!extension_lower.empty() &&
- (extension_lower[0] == FILE_PATH_LITERAL('{')) &&
- (extension_lower[extension_lower.length() - 1] == FILE_PATH_LITERAL('}')))
- return true;
- return false;
-}
-
-// Returns whether the specified file name is a reserved name on windows.
-// This includes names like "com2.zip" (which correspond to devices) and
-// desktop.ini and thumbs.db which have special meaning to the windows shell.
-bool IsReservedName(const base::FilePath::StringType& filename) {
- // This list is taken from the MSDN article "Naming a file"
- // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
- // I also added clock$ because GetSaveFileName seems to consider it as a
- // reserved name too.
- static const char* const known_devices[] = {
- "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5",
- "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4",
- "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"
- };
-#if defined(OS_WIN)
- std::string filename_lower = StringToLowerASCII(WideToUTF8(filename));
-#elif defined(OS_POSIX)
- std::string filename_lower = StringToLowerASCII(filename);
-#endif
-
- for (size_t i = 0; i < arraysize(known_devices); ++i) {
- // Exact match.
- if (filename_lower == known_devices[i])
- return true;
- // Starts with "DEVICE.".
- if (filename_lower.find(std::string(known_devices[i]) + ".") == 0)
- return true;
- }
-
- static const char* const magic_names[] = {
- // These file names are used by the "Customize folder" feature of the shell.
- "desktop.ini",
- "thumbs.db",
- };
-
- for (size_t i = 0; i < arraysize(magic_names); ++i) {
- if (filename_lower == magic_names[i])
- return true;
- }
-
- return false;
-}
-
-// Examines the current extension in |file_name| and modifies it if necessary in
-// order to ensure the filename is safe. If |file_name| doesn't contain an
-// extension or if |ignore_extension| is true, then a new extension will be
-// constructed based on the |mime_type|.
-//
-// We're addressing two things here:
-//
-// 1) Usability. If there is no reliable file extension, we want to guess a
-// reasonable file extension based on the content type.
-//
-// 2) Shell integration. Some file extensions automatically integrate with the
-// shell. We block these extensions to prevent a malicious web site from
-// integrating with the user's shell.
-void EnsureSafeExtension(const std::string& mime_type,
- bool ignore_extension,
- base::FilePath* file_name) {
- // See if our file name already contains an extension.
- base::FilePath::StringType extension = file_name->Extension();
- if (!extension.empty())
- extension.erase(extension.begin()); // Erase preceding '.'.
-
- if ((ignore_extension || extension.empty()) && !mime_type.empty()) {
- base::FilePath::StringType preferred_mime_extension;
- std::vector<base::FilePath::StringType> all_mime_extensions;
- // The GetPreferredExtensionForMimeType call will end up going to disk. Do
- // this on another thread to avoid slowing the IO thread.
- // http://crbug.com/61827
- // TODO(asanka): Remove this ScopedAllowIO once all callers have switched
- // over to IO safe threads.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- net::GetPreferredExtensionForMimeType(mime_type, &preferred_mime_extension);
- net::GetExtensionsForMimeType(mime_type, &all_mime_extensions);
- // If the existing extension is in the list of valid extensions for the
- // given type, use it. This avoids doing things like pointlessly renaming
- // "foo.jpg" to "foo.jpeg".
- if (std::find(all_mime_extensions.begin(),
- all_mime_extensions.end(),
- extension) != all_mime_extensions.end()) {
- // leave |extension| alone
- } else if (!preferred_mime_extension.empty()) {
- extension = preferred_mime_extension;
- }
- }
-
-#if defined(OS_WIN)
- static const base::FilePath::CharType default_extension[] =
- FILE_PATH_LITERAL("download");
-
- // Rename shell-integrated extensions.
- // TODO(asanka): Consider stripping out the bad extension and replacing it
- // with the preferred extension for the MIME type if one is available.
- if (IsShellIntegratedExtension(extension))
- extension.assign(default_extension);
-#endif
-
- *file_name = file_name->ReplaceExtension(extension);
-}
-
-bool FilePathToString16(const base::FilePath& path, base::string16* converted) {
-#if defined(OS_WIN)
- return WideToUTF16(path.value().c_str(), path.value().size(), converted);
-#elif defined(OS_POSIX)
- std::string component8 = path.AsUTF8Unsafe();
- return !component8.empty() &&
- UTF8ToUTF16(component8.c_str(), component8.size(), converted);
-#endif
-}
-
bool IPNumberPrefixCheck(const IPAddressNumber& ip_number,
const unsigned char* ip_prefix,
size_t prefix_length_in_bits) {
@@ -977,13 +166,6 @@ bool IPNumberPrefixCheck(const IPAddressNumber& ip_number,
} // namespace
-const FormatUrlType kFormatUrlOmitNothing = 0;
-const FormatUrlType kFormatUrlOmitUsernamePassword = 1 << 0;
-const FormatUrlType kFormatUrlOmitHTTP = 1 << 1;
-const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname = 1 << 2;
-const FormatUrlType kFormatUrlOmitAll = kFormatUrlOmitUsernamePassword |
- kFormatUrlOmitHTTP | kFormatUrlOmitTrailingSlashOnBareHostname;
-
static base::LazyInstance<std::multiset<int> >::Leaky
g_explicitly_allowed_ports = LAZY_INSTANCE_INITIALIZER;
@@ -991,40 +173,6 @@ size_t GetCountOfExplicitlyAllowedPorts() {
return g_explicitly_allowed_ports.Get().size();
}
-GURL FilePathToFileURL(const base::FilePath& path) {
- // Produce a URL like "file:///C:/foo" for a regular file, or
- // "file://///server/path" for UNC. The URL canonicalizer will fix up the
- // latter case to be the canonical UNC form: "file://server/path"
- base::FilePath::StringType url_string(kFileURLPrefix);
- url_string.append(path.value());
-
- // Now do replacement of some characters. Since we assume the input is a
- // literal filename, anything the URL parser might consider special should
- // be escaped here.
-
- // must be the first substitution since others will introduce percents as the
- // escape character
- ReplaceSubstringsAfterOffset(&url_string, 0,
- FILE_PATH_LITERAL("%"), FILE_PATH_LITERAL("%25"));
-
- // semicolon is supposed to be some kind of separator according to RFC 2396
- ReplaceSubstringsAfterOffset(&url_string, 0,
- FILE_PATH_LITERAL(";"), FILE_PATH_LITERAL("%3B"));
-
- ReplaceSubstringsAfterOffset(&url_string, 0,
- FILE_PATH_LITERAL("#"), FILE_PATH_LITERAL("%23"));
-
- ReplaceSubstringsAfterOffset(&url_string, 0,
- FILE_PATH_LITERAL("?"), FILE_PATH_LITERAL("%3F"));
-
-#if defined(OS_POSIX)
- ReplaceSubstringsAfterOffset(&url_string, 0,
- FILE_PATH_LITERAL("\\"), FILE_PATH_LITERAL("%5C"));
-#endif
-
- return GURL(url_string);
-}
-
std::string GetSpecificHeader(const std::string& headers,
const std::string& name) {
// We want to grab the Value from the "Key: Value" pairs in the headers,
@@ -1050,28 +198,23 @@ std::string GetSpecificHeader(const std::string& headers,
begin += match.length();
std::string ret;
- TrimWhitespace(std::string(begin, std::find(begin, headers.end(), '\n')),
- TRIM_ALL, &ret);
+ base::TrimWhitespace(std::string(begin,
+ std::find(begin, headers.end(), '\n')),
+ base::TRIM_ALL, &ret);
return ret;
}
-base::string16 IDNToUnicode(const std::string& host,
- const std::string& languages) {
- return IDNToUnicodeWithOffsets(host, languages, NULL);
-}
-
std::string CanonicalizeHost(const std::string& host,
- url_canon::CanonHostInfo* host_info) {
+ url::CanonHostInfo* host_info) {
// Try to canonicalize the host.
- const url_parse::Component raw_host_component(
- 0, static_cast<int>(host.length()));
+ const url::Component raw_host_component(0, static_cast<int>(host.length()));
std::string canon_host;
- url_canon::StdStringCanonOutput canon_host_output(&canon_host);
- url_canon::CanonicalizeHostVerbose(host.c_str(), raw_host_component,
- &canon_host_output, host_info);
+ url::StdStringCanonOutput canon_host_output(&canon_host);
+ url::CanonicalizeHostVerbose(host.c_str(), raw_host_component,
+ &canon_host_output, host_info);
if (host_info->out_host.is_nonempty() &&
- host_info->family != url_canon::CanonHostInfo::BROKEN) {
+ host_info->family != url::CanonHostInfo::BROKEN) {
// Success! Assert that there's no extra garbage.
canon_host_output.Complete();
DCHECK_EQ(host_info->out_host.len, static_cast<int>(canon_host.length()));
@@ -1142,215 +285,14 @@ bool IsCanonicalizedHostCompliant(const std::string& host,
(!desired_tld.empty() && IsHostCharAlphanumeric(desired_tld[0]));
}
-std::string GetDirectoryListingEntry(const base::string16& name,
- const std::string& raw_bytes,
- bool is_dir,
- int64 size,
- Time modified) {
- std::string result;
- result.append("<script>addRow(");
- base::EscapeJSONString(name, true, &result);
- result.append(",");
- if (raw_bytes.empty()) {
- base::EscapeJSONString(EscapePath(UTF16ToUTF8(name)), true, &result);
- } else {
- base::EscapeJSONString(EscapePath(raw_bytes), true, &result);
- }
- if (is_dir) {
- result.append(",1,");
- } else {
- result.append(",0,");
- }
-
- // Negative size means unknown or not applicable (e.g. directory).
- base::string16 size_string;
- if (size >= 0)
- size_string = FormatBytesUnlocalized(size);
- base::EscapeJSONString(size_string, true, &result);
-
- result.append(",");
-
- base::string16 modified_str;
- // |modified| can be NULL in FTP listings.
- if (!modified.is_null()) {
- modified_str = base::TimeFormatShortDateAndTime(modified);
- }
- base::EscapeJSONString(modified_str, true, &result);
-
- result.append(");</script>\n");
-
- return result;
-}
-
base::string16 StripWWW(const base::string16& text) {
- const base::string16 www(ASCIIToUTF16("www."));
+ const base::string16 www(base::ASCIIToUTF16("www."));
return StartsWith(text, www, true) ? text.substr(www.length()) : text;
}
base::string16 StripWWWFromHost(const GURL& url) {
DCHECK(url.is_valid());
- return StripWWW(ASCIIToUTF16(url.host()));
-}
-
-bool IsSafePortablePathComponent(const base::FilePath& component) {
- base::string16 component16;
- base::FilePath::StringType sanitized = component.value();
- SanitizeGeneratedFileName(&sanitized, true);
- base::FilePath::StringType extension = component.Extension();
- if (!extension.empty())
- extension.erase(extension.begin()); // Erase preceding '.'.
- return !component.empty() &&
- (component == component.BaseName()) &&
- (component == component.StripTrailingSeparators()) &&
- FilePathToString16(component, &component16) &&
- file_util::IsFilenameLegal(component16) &&
- !IsShellIntegratedExtension(extension) &&
- (sanitized == component.value()) &&
- !IsReservedName(component.value());
-}
-
-bool IsSafePortableRelativePath(const base::FilePath& path) {
- if (path.empty() || path.IsAbsolute() || path.EndsWithSeparator())
- return false;
- std::vector<base::FilePath::StringType> components;
- path.GetComponents(&components);
- if (components.empty())
- return false;
- for (size_t i = 0; i < components.size() - 1; ++i) {
- if (!IsSafePortablePathComponent(base::FilePath(components[i])))
- return false;
- }
- return IsSafePortablePathComponent(path.BaseName());
-}
-
-void GenerateSafeFileName(const std::string& mime_type,
- bool ignore_extension,
- base::FilePath* file_path) {
- // Make sure we get the right file extension
- EnsureSafeExtension(mime_type, ignore_extension, file_path);
-
-#if defined(OS_WIN)
- // Prepend "_" to the file name if it's a reserved name
- base::FilePath::StringType leaf_name = file_path->BaseName().value();
- DCHECK(!leaf_name.empty());
- if (IsReservedName(leaf_name)) {
- leaf_name = base::FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
- *file_path = file_path->DirName();
- if (file_path->value() == base::FilePath::kCurrentDirectory) {
- *file_path = base::FilePath(leaf_name);
- } else {
- *file_path = file_path->Append(leaf_name);
- }
- }
-#endif
-}
-
-base::string16 GetSuggestedFilename(const GURL& url,
- const std::string& content_disposition,
- const std::string& referrer_charset,
- const std::string& suggested_name,
- const std::string& mime_type,
- const std::string& default_name) {
- // TODO: this function to be updated to match the httpbis recommendations.
- // Talk to abarth for the latest news.
-
- // We don't translate this fallback string, "download". If localization is
- // needed, the caller should provide localized fallback in |default_name|.
- static const base::FilePath::CharType kFinalFallbackName[] =
- FILE_PATH_LITERAL("download");
- std::string filename; // In UTF-8
- bool overwrite_extension = false;
-
- // Try to extract a filename from content-disposition first.
- if (!content_disposition.empty()) {
- HttpContentDisposition header(content_disposition, referrer_charset);
- filename = header.filename();
- }
-
- // Then try to use the suggested name.
- if (filename.empty() && !suggested_name.empty())
- filename = suggested_name;
-
- // Now try extracting the filename from the URL. GetFileNameFromURL() only
- // looks at the last component of the URL and doesn't return the hostname as a
- // failover.
- if (filename.empty())
- filename = GetFileNameFromURL(url, referrer_charset, &overwrite_extension);
-
- // Finally try the URL hostname, but only if there's no default specified in
- // |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a
- // host name.
- if (filename.empty() &&
- default_name.empty() &&
- url.is_valid() &&
- !url.host().empty()) {
- // TODO(jungshik) : Decode a 'punycoded' IDN hostname. (bug 1264451)
- filename = url.host();
- }
-
- bool replace_trailing = false;
- base::FilePath::StringType result_str, default_name_str;
-#if defined(OS_WIN)
- replace_trailing = true;
- result_str = UTF8ToUTF16(filename);
- default_name_str = UTF8ToUTF16(default_name);
-#else
- result_str = filename;
- default_name_str = default_name;
-#endif
- SanitizeGeneratedFileName(&result_str, replace_trailing);
- if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) ==
- base::FilePath::StringType::npos) {
- result_str = !default_name_str.empty() ? default_name_str :
- base::FilePath::StringType(kFinalFallbackName);
- overwrite_extension = false;
- }
- file_util::ReplaceIllegalCharactersInPath(&result_str, '-');
- base::FilePath result(result_str);
- GenerateSafeFileName(mime_type, overwrite_extension, &result);
-
- base::string16 result16;
- if (!FilePathToString16(result, &result16)) {
- result = base::FilePath(default_name_str);
- if (!FilePathToString16(result, &result16)) {
- result = base::FilePath(kFinalFallbackName);
- FilePathToString16(result, &result16);
- }
- }
- return result16;
-}
-
-base::FilePath GenerateFileName(const GURL& url,
- const std::string& content_disposition,
- const std::string& referrer_charset,
- const std::string& suggested_name,
- const std::string& mime_type,
- const std::string& default_file_name) {
- base::string16 file_name = GetSuggestedFilename(url,
- content_disposition,
- referrer_charset,
- suggested_name,
- mime_type,
- default_file_name);
-
-#if defined(OS_WIN)
- base::FilePath generated_name(file_name);
-#else
- base::FilePath generated_name(
- base::SysWideToNativeMB(UTF16ToWide(file_name)));
-#endif
-
-#if defined(OS_CHROMEOS)
- // When doing file manager operations on ChromeOS, the file paths get
- // normalized in WebKit layer, so let's ensure downloaded files have
- // normalized names. Otherwise, we won't be able to handle files with NFD
- // utf8 encoded characters in name.
- file_util::NormalizeFileNameEncoding(&generated_name);
-#endif
-
- DCHECK(!generated_name.empty());
-
- return generated_name;
+ return StripWWW(base::ASCIIToUTF16(url.host()));
}
bool IsPortAllowedByDefault(int port) {
@@ -1400,17 +342,17 @@ bool ParseHostAndPort(std::string::const_iterator host_and_port_begin,
if (host_and_port_begin >= host_and_port_end)
return false;
- // When using url_parse, we use char*.
+ // When using url, we use char*.
const char* auth_begin = &(*host_and_port_begin);
int auth_len = host_and_port_end - host_and_port_begin;
- url_parse::Component auth_component(0, auth_len);
- url_parse::Component username_component;
- url_parse::Component password_component;
- url_parse::Component hostname_component;
- url_parse::Component port_component;
+ url::Component auth_component(0, auth_len);
+ url::Component username_component;
+ url::Component password_component;
+ url::Component hostname_component;
+ url::Component port_component;
- url_parse::ParseAuthority(auth_begin, auth_component, &username_component,
+ url::ParseAuthority(auth_begin, auth_component, &username_component,
&password_component, &hostname_component, &port_component);
// There shouldn't be a username/password.
@@ -1422,7 +364,7 @@ bool ParseHostAndPort(std::string::const_iterator host_and_port_begin,
int parsed_port_number = -1;
if (port_component.is_nonempty()) {
- parsed_port_number = url_parse::ParsePort(auth_begin, port_component);
+ parsed_port_number = url::ParsePort(auth_begin, port_component);
// If parsing failed, port_number will be either PORT_INVALID or
// PORT_UNSPECIFIED, both of which are negative.
@@ -1466,7 +408,7 @@ bool IsHostnameNonUnique(const std::string& hostname) {
// CanonicalizeHost requires surrounding brackets to parse an IPv6 address.
const std::string host_or_ip = hostname.find(':') != std::string::npos ?
"[" + hostname + "]" : hostname;
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
std::string canonical_name = CanonicalizeHost(host_or_ip, &host_info);
// If canonicalization fails, then the input is truly malformed. However,
@@ -1484,11 +426,11 @@ bool IsHostnameNonUnique(const std::string& hostname) {
return false;
}
switch (host_info.family) {
- case url_canon::CanonHostInfo::IPV4:
- case url_canon::CanonHostInfo::IPV6:
+ case url::CanonHostInfo::IPV4:
+ case url::CanonHostInfo::IPV6:
return IsIPAddressReserved(host_addr);
- case url_canon::CanonHostInfo::NEUTRAL:
- case url_canon::CanonHostInfo::BROKEN:
+ case url::CanonHostInfo::NEUTRAL:
+ case url::CanonHostInfo::BROKEN:
return false;
}
}
@@ -1582,25 +524,39 @@ bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr,
return false;
const struct sockaddr_in6* addr =
reinterpret_cast<const struct sockaddr_in6*>(sock_addr);
- *address = reinterpret_cast<const unsigned char*>(&addr->sin6_addr);
+ *address = reinterpret_cast<const uint8*>(&addr->sin6_addr);
*address_len = kIPv6AddressSize;
if (port)
*port = base::NetToHost16(addr->sin6_port);
return true;
}
+#if defined(OS_WIN)
+ if (sock_addr->sa_family == AF_BTH) {
+ if (sock_addr_len < static_cast<socklen_t>(sizeof(SOCKADDR_BTH)))
+ return false;
+ const SOCKADDR_BTH* addr =
+ reinterpret_cast<const SOCKADDR_BTH*>(sock_addr);
+ *address = reinterpret_cast<const uint8*>(&addr->btAddr);
+ *address_len = kBluetoothAddressSize;
+ if (port)
+ *port = addr->port;
+ return true;
+ }
+#endif
+
return false; // Unrecognized |sa_family|.
}
std::string IPAddressToString(const uint8* address,
size_t address_len) {
std::string str;
- url_canon::StdStringCanonOutput output(&str);
+ url::StdStringCanonOutput output(&str);
if (address_len == kIPv4AddressSize) {
- url_canon::AppendIPv4Address(address, &output);
+ url::AppendIPv4Address(address, &output);
} else if (address_len == kIPv6AddressSize) {
- url_canon::AppendIPv6Address(address, &output);
+ url::AppendIPv6Address(address, &output);
} else {
CHECK(false) << "Invalid IP address with length: " << address_len;
}
@@ -1661,6 +617,10 @@ std::string IPAddressToPackedString(const IPAddressNumber& addr) {
}
std::string GetHostName() {
+#if defined(OS_NACL)
+ NOTIMPLEMENTED();
+ return std::string();
+#else // defined(OS_NACL)
#if defined(OS_WIN)
EnsureWinsockInit();
#endif
@@ -1673,6 +633,7 @@ std::string GetHostName() {
buffer[0] = '\0';
}
return std::string(buffer);
+#endif // !defined(OS_NACL)
}
void GetIdentityFromURL(const GURL& url,
@@ -1680,199 +641,14 @@ void GetIdentityFromURL(const GURL& url,
base::string16* password) {
UnescapeRule::Type flags =
UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS;
- *username = UnescapeAndDecodeUTF8URLComponent(url.username(), flags, NULL);
- *password = UnescapeAndDecodeUTF8URLComponent(url.password(), flags, NULL);
+ *username = UnescapeAndDecodeUTF8URLComponent(url.username(), flags);
+ *password = UnescapeAndDecodeUTF8URLComponent(url.password(), flags);
}
std::string GetHostOrSpecFromURL(const GURL& url) {
return url.has_host() ? TrimEndingDot(url.host()) : url.spec();
}
-void AppendFormattedHost(const GURL& url,
- const std::string& languages,
- base::string16* output) {
- Offsets offsets;
- AppendFormattedComponent(url.possibly_invalid_spec(),
- url.parsed_for_possibly_invalid_spec().host, offsets,
- HostComponentTransform(languages), output, NULL, NULL);
-}
-
-base::string16 FormatUrlWithOffsets(
- const GURL& url,
- const std::string& languages,
- FormatUrlTypes format_types,
- UnescapeRule::Type unescape_rules,
- url_parse::Parsed* new_parsed,
- size_t* prefix_end,
- Offsets* offsets_for_adjustment) {
- url_parse::Parsed parsed_temp;
- if (!new_parsed)
- new_parsed = &parsed_temp;
- else
- *new_parsed = url_parse::Parsed();
- Offsets original_offsets;
- if (offsets_for_adjustment)
- original_offsets = *offsets_for_adjustment;
-
- // Special handling for view-source:. Don't use content::kViewSourceScheme
- // because this library shouldn't depend on chrome.
- const char* const kViewSource = "view-source";
- // Reject "view-source:view-source:..." to avoid deep recursion.
- const char* const kViewSourceTwice = "view-source:view-source:";
- if (url.SchemeIs(kViewSource) &&
- !StartsWithASCII(url.possibly_invalid_spec(), kViewSourceTwice, false)) {
- return FormatViewSourceUrl(url, original_offsets, languages, format_types,
- unescape_rules, new_parsed, prefix_end,
- offsets_for_adjustment);
- }
-
- // We handle both valid and invalid URLs (this will give us the spec
- // regardless of validity).
- const std::string& spec = url.possibly_invalid_spec();
- const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
-
- // Scheme & separators. These are ASCII.
- base::string16 url_string;
- url_string.insert(url_string.end(), spec.begin(),
- spec.begin() + parsed.CountCharactersBefore(url_parse::Parsed::USERNAME,
- true));
- const char kHTTP[] = "http://";
- const char kFTP[] = "ftp.";
- // URLFixerUpper::FixupURL() treats "ftp.foo.com" as ftp://ftp.foo.com. This
- // means that if we trim "http://" off a URL whose host starts with "ftp." and
- // the user inputs this into any field subject to fixup (which is basically
- // all input fields), the meaning would be changed. (In fact, often the
- // formatted URL is directly pre-filled into an input field.) For this reason
- // we avoid stripping "http://" in this case.
- bool omit_http = (format_types & kFormatUrlOmitHTTP) &&
- EqualsASCII(url_string, kHTTP) &&
- !StartsWithASCII(url.host(), kFTP, true);
- new_parsed->scheme = parsed.scheme;
-
- // Username & password.
- if ((format_types & kFormatUrlOmitUsernamePassword) != 0) {
- // Remove the username and password fields. We don't want to display those
- // to the user since they can be used for attacks,
- // e.g. "http://google.com:search@evil.ru/"
- new_parsed->username.reset();
- new_parsed->password.reset();
- // Update the offsets based on removed username and/or password.
- if (offsets_for_adjustment && !offsets_for_adjustment->empty() &&
- (parsed.username.is_nonempty() || parsed.password.is_nonempty())) {
- base::OffsetAdjuster offset_adjuster(offsets_for_adjustment);
- if (parsed.username.is_nonempty() && parsed.password.is_nonempty()) {
- // The seeming off-by-one and off-by-two in these first two lines are to
- // account for the ':' after the username and '@' after the password.
- offset_adjuster.Add(base::OffsetAdjuster::Adjustment(
- static_cast<size_t>(parsed.username.begin),
- static_cast<size_t>(parsed.username.len + parsed.password.len + 2),
- 0));
- } else {
- const url_parse::Component* nonempty_component =
- parsed.username.is_nonempty() ? &parsed.username : &parsed.password;
- // The seeming off-by-one in below is to account for the '@' after the
- // username/password.
- offset_adjuster.Add(base::OffsetAdjuster::Adjustment(
- static_cast<size_t>(nonempty_component->begin),
- static_cast<size_t>(nonempty_component->len + 1), 0));
- }
- }
- } else {
- AppendFormattedComponent(spec, parsed.username, original_offsets,
- NonHostComponentTransform(unescape_rules), &url_string,
- &new_parsed->username, offsets_for_adjustment);
- if (parsed.password.is_valid())
- url_string.push_back(':');
- AppendFormattedComponent(spec, parsed.password, original_offsets,
- NonHostComponentTransform(unescape_rules), &url_string,
- &new_parsed->password, offsets_for_adjustment);
- if (parsed.username.is_valid() || parsed.password.is_valid())
- url_string.push_back('@');
- }
- if (prefix_end)
- *prefix_end = static_cast<size_t>(url_string.length());
-
- // Host.
- AppendFormattedComponent(spec, parsed.host, original_offsets,
- HostComponentTransform(languages), &url_string, &new_parsed->host,
- offsets_for_adjustment);
-
- // Port.
- if (parsed.port.is_nonempty()) {
- url_string.push_back(':');
- new_parsed->port.begin = url_string.length();
- url_string.insert(url_string.end(),
- spec.begin() + parsed.port.begin,
- spec.begin() + parsed.port.end());
- new_parsed->port.len = url_string.length() - new_parsed->port.begin;
- } else {
- new_parsed->port.reset();
- }
-
- // Path & query. Both get the same general unescape & convert treatment.
- if (!(format_types & kFormatUrlOmitTrailingSlashOnBareHostname) ||
- !CanStripTrailingSlash(url)) {
- AppendFormattedComponent(spec, parsed.path, original_offsets,
- NonHostComponentTransform(unescape_rules), &url_string,
- &new_parsed->path, offsets_for_adjustment);
- } else {
- base::OffsetAdjuster offset_adjuster(offsets_for_adjustment);
- offset_adjuster.Add(base::OffsetAdjuster::Adjustment(
- url_string.length(), parsed.path.len, 0));
- }
- if (parsed.query.is_valid())
- url_string.push_back('?');
- AppendFormattedComponent(spec, parsed.query, original_offsets,
- NonHostComponentTransform(unescape_rules), &url_string,
- &new_parsed->query, offsets_for_adjustment);
-
- // Ref. This is valid, unescaped UTF-8, so we can just convert.
- if (parsed.ref.is_valid())
- url_string.push_back('#');
- AppendFormattedComponent(spec, parsed.ref, original_offsets,
- NonHostComponentTransform(UnescapeRule::NONE), &url_string,
- &new_parsed->ref, offsets_for_adjustment);
-
- // If we need to strip out http do it after the fact. This way we don't need
- // to worry about how offset_for_adjustment is interpreted.
- if (omit_http && StartsWith(url_string, ASCIIToUTF16(kHTTP), true)) {
- const size_t kHTTPSize = arraysize(kHTTP) - 1;
- url_string = url_string.substr(kHTTPSize);
- if (offsets_for_adjustment && !offsets_for_adjustment->empty()) {
- base::OffsetAdjuster offset_adjuster(offsets_for_adjustment);
- offset_adjuster.Add(base::OffsetAdjuster::Adjustment(0, kHTTPSize, 0));
- }
- if (prefix_end)
- *prefix_end -= kHTTPSize;
-
- // Adjust new_parsed.
- DCHECK(new_parsed->scheme.is_valid());
- int delta = -(new_parsed->scheme.len + 3); // +3 for ://.
- new_parsed->scheme.reset();
- AdjustAllComponentsButScheme(delta, new_parsed);
- }
-
- LimitOffsets(url_string, offsets_for_adjustment);
- return url_string;
-}
-
-base::string16 FormatUrl(const GURL& url,
- const std::string& languages,
- FormatUrlTypes format_types,
- UnescapeRule::Type unescape_rules,
- url_parse::Parsed* new_parsed,
- size_t* prefix_end,
- size_t* offset_for_adjustment) {
- Offsets offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
- base::string16 result = FormatUrlWithOffsets(url, languages, format_types,
- unescape_rules, new_parsed, prefix_end, &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
- return result;
-}
-
bool CanStripTrailingSlash(const GURL& url) {
// Omit the path only for standard, non-file URLs with nothing but "/" after
// the hostname.
@@ -1938,6 +714,9 @@ ScopedPortException::~ScopedPortException() {
bool HaveOnlyLoopbackAddresses() {
#if defined(OS_ANDROID)
return android::HaveOnlyLoopbackAddresses();
+#elif defined(OS_NACL)
+ NOTIMPLEMENTED();
+ return false;
#elif defined(OS_POSIX)
struct ifaddrs* interface_addr = NULL;
int rv = getifaddrs(&interface_addr);
@@ -2014,22 +793,21 @@ bool ParseIPLiteralToNumber(const std::string& ip_literal,
if (ip_literal.find(':') != std::string::npos) {
// GURL expects IPv6 hostnames to be surrounded with brackets.
std::string host_brackets = "[" + ip_literal + "]";
- url_parse::Component host_comp(0, host_brackets.size());
+ url::Component host_comp(0, host_brackets.size());
// Try parsing the hostname as an IPv6 literal.
ip_number->resize(16); // 128 bits.
- return url_canon::IPv6AddressToNumber(host_brackets.data(),
- host_comp,
- &(*ip_number)[0]);
+ return url::IPv6AddressToNumber(host_brackets.data(), host_comp,
+ &(*ip_number)[0]);
}
// Otherwise the string is an IPv4 address.
ip_number->resize(4); // 32 bits.
- url_parse::Component host_comp(0, ip_literal.size());
+ url::Component host_comp(0, ip_literal.size());
int num_components;
- url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber(
+ url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
ip_literal.data(), host_comp, &(*ip_number)[0], &num_components);
- return family == url_canon::CanonHostInfo::IPV4;
+ return family == url::CanonHostInfo::IPV4;
}
namespace {
@@ -2181,15 +959,21 @@ bool IsLocalhost(const std::string& host) {
return false;
}
-NetworkInterface::NetworkInterface() : network_prefix(0) {
+NetworkInterface::NetworkInterface()
+ : type(NetworkChangeNotifier::CONNECTION_UNKNOWN),
+ network_prefix(0) {
}
NetworkInterface::NetworkInterface(const std::string& name,
+ const std::string& friendly_name,
uint32 interface_index,
+ NetworkChangeNotifier::ConnectionType type,
const IPAddressNumber& address,
size_t network_prefix)
: name(name),
+ friendly_name(friendly_name),
interface_index(interface_index),
+ type(type),
address(address),
network_prefix(network_prefix) {
}
diff --git a/chromium/net/base/net_util.h b/chromium/net/base/net_util.h
index baa68e3a097..d83744fb596 100644
--- a/chromium/net/base/net_util.h
+++ b/chromium/net/base/net_util.h
@@ -20,23 +20,20 @@
#include "base/basictypes.h"
#include "base/strings/string16.h"
+#include "base/strings/utf_offset_string_conversions.h"
#include "net/base/address_family.h"
#include "net/base/escape.h"
#include "net/base/net_export.h"
-#include "net/base/net_log.h"
+#include "net/base/network_change_notifier.h"
class GURL;
namespace base {
-class FilePath;
class Time;
}
-namespace url_canon {
+namespace url {
struct CanonHostInfo;
-}
-
-namespace url_parse {
struct Parsed;
}
@@ -56,6 +53,10 @@ typedef std::vector<IPAddressNumber> IPAddressList;
static const size_t kIPv4AddressSize = 4;
static const size_t kIPv6AddressSize = 16;
+#if defined(OS_WIN)
+// Bluetooth address size. Windows Bluetooth is supported via winsock.
+static const size_t kBluetoothAddressSize = 6;
+#endif
// Nothing is ommitted.
NET_EXPORT extern const FormatUrlType kFormatUrlOmitNothing;
@@ -76,16 +77,6 @@ NET_EXPORT extern const FormatUrlType kFormatUrlOmitAll;
// Returns the number of explicitly allowed ports; for testing.
NET_EXPORT_PRIVATE extern size_t GetCountOfExplicitlyAllowedPorts();
-// Given the full path to a file name, creates a file: URL. The returned URL
-// may not be valid if the input is malformed.
-NET_EXPORT GURL FilePathToFileURL(const base::FilePath& path);
-
-// Converts a file: URL back to a filename that can be passed to the OS. The
-// file URL must be well-formed (GURL::is_valid() must return true); we don't
-// handle degenerate cases here. Returns true on success, false if it isn't a
-// valid file URL. On failure, *file_path will be empty.
-NET_EXPORT bool FileURLToFilePath(const GURL& url, base::FilePath* file_path);
-
// Splits an input of the form <host>[":"<port>] into its consitituent parts.
// Saves the result into |*host| and |*port|. If the input did not have
// the optional port, sets |*port| to -1.
@@ -206,7 +197,7 @@ NET_EXPORT base::string16 IDNToUnicode(const std::string& host,
// Canonicalizes |host| and returns it. Also fills |host_info| with
// IP address information. |host_info| must not be NULL.
NET_EXPORT std::string CanonicalizeHost(const std::string& host,
- url_canon::CanonHostInfo* host_info);
+ url::CanonHostInfo* host_info);
// Returns true if |host| is not an IP address and is compliant with a set of
// rules based on RFC 1738 and tweaked to be compatible with the real world.
@@ -255,89 +246,6 @@ NET_EXPORT base::string16 StripWWW(const base::string16& text);
// Runs |url|'s host through StripWWW(). |url| must be valid.
NET_EXPORT base::string16 StripWWWFromHost(const GURL& url);
-// Generates a filename using the first successful method from the following (in
-// order):
-//
-// 1) The raw Content-Disposition header in |content_disposition| as read from
-// the network. |referrer_charset| is used to decode non-ASCII strings.
-// 2) |suggested_name| if specified. |suggested_name| is assumed to be in
-// UTF-8.
-// 3) The filename extracted from the |url|. |referrer_charset| will be used to
-// interpret the URL if there are non-ascii characters.
-// 4) |default_name|. If non-empty, |default_name| is assumed to be a filename
-// and shouldn't contain a path. |default_name| is not subject to validation
-// or sanitization, and therefore shouldn't be a user supplied string.
-// 5) The hostname portion from the |url|
-//
-// Then, leading and trailing '.'s will be removed. On Windows, trailing spaces
-// are also removed. The string "download" is the final fallback if no filename
-// is found or the filename is empty.
-//
-// Any illegal characters in the filename will be replaced by '-'. If the
-// filename doesn't contain an extension, and a |mime_type| is specified, the
-// preferred extension for the |mime_type| will be appended to the filename.
-// The resulting filename is then checked against a list of reserved names on
-// Windows. If the name is reserved, an underscore will be prepended to the
-// filename.
-//
-// Note: |mime_type| should only be specified if this function is called from a
-// thread that allows IO.
-NET_EXPORT base::string16 GetSuggestedFilename(
- const GURL& url,
- const std::string& content_disposition,
- const std::string& referrer_charset,
- const std::string& suggested_name,
- const std::string& mime_type,
- const std::string& default_name);
-
-// Similar to GetSuggestedFilename(), but returns a FilePath.
-NET_EXPORT base::FilePath GenerateFileName(
- const GURL& url,
- const std::string& content_disposition,
- const std::string& referrer_charset,
- const std::string& suggested_name,
- const std::string& mime_type,
- const std::string& default_name);
-
-// Valid components:
-// * are not empty
-// * are not Windows reserved names (CON, NUL.zip, etc.)
-// * do not have trailing separators
-// * do not equal kCurrentDirectory
-// * do not reference the parent directory
-// * do not contain illegal characters
-// * do not end with Windows shell-integrated extensions (even on posix)
-// * do not begin with '.' (which would hide them in most file managers)
-// * do not end with ' ' or '.'
-NET_EXPORT bool IsSafePortablePathComponent(const base::FilePath& component);
-
-// Basenames of valid relative paths are IsSafePortableBasename(), and internal
-// path components of valid relative paths are valid path components as
-// described above IsSafePortableBasename(). Valid relative paths are not
-// absolute paths.
-NET_EXPORT bool IsSafePortableRelativePath(const base::FilePath& path);
-
-// Ensures that the filename and extension is safe to use in the filesystem.
-//
-// Assumes that |file_path| already contains a valid path or file name. On
-// Windows if the extension causes the file to have an unsafe interaction with
-// the shell (see net_util::IsShellIntegratedExtension()), then it will be
-// replaced by the string 'download'. If |file_path| doesn't contain an
-// extension or |ignore_extension| is true then the preferred extension, if one
-// exists, for |mime_type| will be used as the extension.
-//
-// On Windows, the filename will be checked against a set of reserved names, and
-// if so, an underscore will be prepended to the name.
-//
-// |file_name| can either be just the file name or it can be a full path to a
-// file.
-//
-// Note: |mime_type| should only be non-empty if this function is called from a
-// thread that allows IO.
-NET_EXPORT void GenerateSafeFileName(const std::string& mime_type,
- bool ignore_extension,
- base::FilePath* file_path);
-
// Checks |port| against a list of ports which are restricted by default.
// Returns true if |port| is allowed, false if it is restricted.
NET_EXPORT bool IsPortAllowedByDefault(int port);
@@ -394,7 +302,7 @@ NET_EXPORT base::string16 FormatUrl(const GURL& url,
const std::string& languages,
FormatUrlTypes format_types,
UnescapeRule::Type unescape_rules,
- url_parse::Parsed* new_parsed,
+ url::Parsed* new_parsed,
size_t* prefix_end,
size_t* offset_for_adjustment);
NET_EXPORT base::string16 FormatUrlWithOffsets(
@@ -402,9 +310,21 @@ NET_EXPORT base::string16 FormatUrlWithOffsets(
const std::string& languages,
FormatUrlTypes format_types,
UnescapeRule::Type unescape_rules,
- url_parse::Parsed* new_parsed,
+ url::Parsed* new_parsed,
size_t* prefix_end,
std::vector<size_t>* offsets_for_adjustment);
+// This function is like those above except it takes |adjustments| rather
+// than |offset[s]_for_adjustment|. |adjustments| will be set to reflect all
+// the transformations that happened to |url| to convert it into the returned
+// value.
+NET_EXPORT base::string16 FormatUrlWithAdjustments(
+ const GURL& url,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ url::Parsed* new_parsed,
+ size_t* prefix_end,
+ base::OffsetAdjuster::Adjustments* adjustments);
// This is a convenience function for FormatUrl() with
// format_types = kFormatUrlOmitAll and unescape = SPACES. This is the typical
@@ -515,30 +435,44 @@ NET_EXPORT_PRIVATE bool IsLocalhost(const std::string& host);
struct NET_EXPORT NetworkInterface {
NetworkInterface();
NetworkInterface(const std::string& name,
+ const std::string& friendly_name,
uint32 interface_index,
+ NetworkChangeNotifier::ConnectionType type,
const IPAddressNumber& address,
size_t network_prefix);
~NetworkInterface();
std::string name;
+ std::string friendly_name; // Same as |name| on non-Windows.
uint32 interface_index; // Always 0 on Android.
+ NetworkChangeNotifier::ConnectionType type;
IPAddressNumber address;
size_t network_prefix;
};
typedef std::vector<NetworkInterface> NetworkInterfaceList;
+// Policy settings to include/exclude network interfaces.
+enum HostAddressSelectionPolicy {
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES = 0x0,
+ EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES = 0x1,
+ // Include temp address only when interface has both permanent and
+ // temp addresses.
+ INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE = 0x2,
+};
+
// Returns list of network interfaces except loopback interface. If an
// interface has more than one address, a separate entry is added to
// the list for each address.
// Can be called only on a thread that allows IO.
-NET_EXPORT bool GetNetworkList(NetworkInterfaceList* networks);
+NET_EXPORT bool GetNetworkList(NetworkInterfaceList* networks,
+ int policy);
// General category of the IEEE 802.11 (wifi) physical layer operating mode.
enum WifiPHYLayerProtocol {
// No wifi support or no associated AP.
WIFI_PHY_LAYER_PROTOCOL_NONE,
- // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS,
+ // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS.
WIFI_PHY_LAYER_PROTOCOL_ANCIENT,
// 802.11a, OFDM-based rates.
WIFI_PHY_LAYER_PROTOCOL_A,
@@ -567,6 +501,7 @@ unsigned MaskPrefixLength(const IPAddressNumber& mask);
// See http://tools.ietf.org/html/rfc2474 for details.
enum DiffServCodePoint {
DSCP_NO_CHANGE = -1,
+ DSCP_FIRST = DSCP_NO_CHANGE,
DSCP_DEFAULT = 0, // Same as DSCP_CS0
DSCP_CS0 = 0, // The default
DSCP_CS1 = 8, // Bulk/background traffic
@@ -589,6 +524,7 @@ enum DiffServCodePoint {
DSCP_EF = 46, // Voice
DSCP_CS6 = 48, // Voice
DSCP_CS7 = 56, // Control messages
+ DSCP_LAST = DSCP_CS7
};
} // namespace net
diff --git a/chromium/net/base/net_util_icu.cc b/chromium/net/base/net_util_icu.cc
new file mode 100644
index 00000000000..4094feeff74
--- /dev/null
+++ b/chromium/net/base/net_util_icu.cc
@@ -0,0 +1,830 @@
+// 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/base/net_util.h"
+
+#include <map>
+#include <vector>
+
+#include "base/i18n/time_formatting.h"
+#include "base/json/string_escape.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_offset_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+#include "third_party/icu/source/common/unicode/uidna.h"
+#include "third_party/icu/source/common/unicode/uniset.h"
+#include "third_party/icu/source/common/unicode/uscript.h"
+#include "third_party/icu/source/common/unicode/uset.h"
+#include "third_party/icu/source/i18n/unicode/datefmt.h"
+#include "third_party/icu/source/i18n/unicode/regex.h"
+#include "third_party/icu/source/i18n/unicode/ulocdata.h"
+
+using base::Time;
+
+namespace net {
+
+namespace {
+
+typedef std::vector<size_t> Offsets;
+
+// Does some simple normalization of scripts so we can allow certain scripts
+// to exist together.
+// TODO(brettw) bug 880223: we should allow some other languages to be
+// oombined such as Chinese and Latin. We will probably need a more
+// complicated system of language pairs to have more fine-grained control.
+UScriptCode NormalizeScript(UScriptCode code) {
+ switch (code) {
+ case USCRIPT_KATAKANA:
+ case USCRIPT_HIRAGANA:
+ case USCRIPT_KATAKANA_OR_HIRAGANA:
+ case USCRIPT_HANGUL: // This one is arguable.
+ return USCRIPT_HAN;
+ default:
+ return code;
+ }
+}
+
+bool IsIDNComponentInSingleScript(const base::char16* str, int str_len) {
+ UScriptCode first_script = USCRIPT_INVALID_CODE;
+ bool is_first = true;
+
+ int i = 0;
+ while (i < str_len) {
+ unsigned code_point;
+ U16_NEXT(str, i, str_len, code_point);
+
+ UErrorCode err = U_ZERO_ERROR;
+ UScriptCode cur_script = uscript_getScript(code_point, &err);
+ if (err != U_ZERO_ERROR)
+ return false; // Report mixed on error.
+ cur_script = NormalizeScript(cur_script);
+
+ // TODO(brettw) We may have to check for USCRIPT_INHERENT as well.
+ if (is_first && cur_script != USCRIPT_COMMON) {
+ first_script = cur_script;
+ is_first = false;
+ } else {
+ if (cur_script != USCRIPT_COMMON && cur_script != first_script)
+ return false;
+ }
+ }
+ return true;
+}
+
+// Check if the script of a language can be 'safely' mixed with
+// Latin letters in the ASCII range.
+bool IsCompatibleWithASCIILetters(const std::string& lang) {
+ // For now, just list Chinese, Japanese and Korean (positive list).
+ // An alternative is negative-listing (languages using Greek and
+ // Cyrillic letters), but it can be more dangerous.
+ return !lang.substr(0, 2).compare("zh") ||
+ !lang.substr(0, 2).compare("ja") ||
+ !lang.substr(0, 2).compare("ko");
+}
+
+typedef std::map<std::string, icu::UnicodeSet*> LangToExemplarSetMap;
+
+class LangToExemplarSet {
+ public:
+ static LangToExemplarSet* GetInstance() {
+ return Singleton<LangToExemplarSet>::get();
+ }
+
+ private:
+ LangToExemplarSetMap map;
+ LangToExemplarSet() { }
+ ~LangToExemplarSet() {
+ STLDeleteContainerPairSecondPointers(map.begin(), map.end());
+ }
+
+ friend class Singleton<LangToExemplarSet>;
+ friend struct DefaultSingletonTraits<LangToExemplarSet>;
+ friend bool GetExemplarSetForLang(const std::string&, icu::UnicodeSet**);
+ friend void SetExemplarSetForLang(const std::string&, icu::UnicodeSet*);
+
+ DISALLOW_COPY_AND_ASSIGN(LangToExemplarSet);
+};
+
+bool GetExemplarSetForLang(const std::string& lang,
+ icu::UnicodeSet** lang_set) {
+ const LangToExemplarSetMap& map = LangToExemplarSet::GetInstance()->map;
+ LangToExemplarSetMap::const_iterator pos = map.find(lang);
+ if (pos != map.end()) {
+ *lang_set = pos->second;
+ return true;
+ }
+ return false;
+}
+
+void SetExemplarSetForLang(const std::string& lang,
+ icu::UnicodeSet* lang_set) {
+ LangToExemplarSetMap& map = LangToExemplarSet::GetInstance()->map;
+ map.insert(std::make_pair(lang, lang_set));
+}
+
+static base::LazyInstance<base::Lock>::Leaky
+ g_lang_set_lock = LAZY_INSTANCE_INITIALIZER;
+
+// Returns true if all the characters in component_characters are used by
+// the language |lang|.
+bool IsComponentCoveredByLang(const icu::UnicodeSet& component_characters,
+ const std::string& lang) {
+ CR_DEFINE_STATIC_LOCAL(
+ const icu::UnicodeSet, kASCIILetters, ('a', 'z'));
+ icu::UnicodeSet* lang_set = NULL;
+ // We're called from both the UI thread and the history thread.
+ {
+ base::AutoLock lock(g_lang_set_lock.Get());
+ if (!GetExemplarSetForLang(lang, &lang_set)) {
+ UErrorCode status = U_ZERO_ERROR;
+ ULocaleData* uld = ulocdata_open(lang.c_str(), &status);
+ // TODO(jungshik) Turn this check on when the ICU data file is
+ // rebuilt with the minimal subset of locale data for languages
+ // to which Chrome is not localized but which we offer in the list
+ // of languages selectable for Accept-Languages. With the rebuilt ICU
+ // data, ulocdata_open never should fall back to the default locale.
+ // (issue 2078)
+ // DCHECK(U_SUCCESS(status) && status != U_USING_DEFAULT_WARNING);
+ if (U_SUCCESS(status) && status != U_USING_DEFAULT_WARNING) {
+ lang_set = reinterpret_cast<icu::UnicodeSet *>(
+ ulocdata_getExemplarSet(uld, NULL, 0,
+ ULOCDATA_ES_STANDARD, &status));
+ // If |lang| is compatible with ASCII Latin letters, add them.
+ if (IsCompatibleWithASCIILetters(lang))
+ lang_set->addAll(kASCIILetters);
+ } else {
+ lang_set = new icu::UnicodeSet(1, 0);
+ }
+ lang_set->freeze();
+ SetExemplarSetForLang(lang, lang_set);
+ ulocdata_close(uld);
+ }
+ }
+ return !lang_set->isEmpty() && lang_set->containsAll(component_characters);
+}
+
+// Returns true if the given Unicode host component is safe to display to the
+// user.
+bool IsIDNComponentSafe(const base::char16* str,
+ int str_len,
+ const std::string& languages) {
+ // Most common cases (non-IDN) do not reach here so that we don't
+ // need a fast return path.
+ // TODO(jungshik) : Check if there's any character inappropriate
+ // (although allowed) for domain names.
+ // See http://www.unicode.org/reports/tr39/#IDN_Security_Profiles and
+ // http://www.unicode.org/reports/tr39/data/xidmodifications.txt
+ // For now, we borrow the list from Mozilla and tweaked it slightly.
+ // (e.g. Characters like U+00A0, U+3000, U+3002 are omitted because
+ // they're gonna be canonicalized to U+0020 and full stop before
+ // reaching here.)
+ // The original list is available at
+ // http://kb.mozillazine.org/Network.IDN.blacklist_chars and
+ // at http://mxr.mozilla.org/seamonkey/source/modules/libpref/src/init/all.js#703
+
+ UErrorCode status = U_ZERO_ERROR;
+#ifdef U_WCHAR_IS_UTF16
+ icu::UnicodeSet dangerous_characters(icu::UnicodeString(
+ L"[[\\ \u00ad\u00bc\u00bd\u01c3\u0337\u0338"
+ L"\u05c3\u05f4\u06d4\u0702\u115f\u1160][\u2000-\u200b]"
+ L"[\u2024\u2027\u2028\u2029\u2039\u203a\u2044\u205f]"
+ L"[\u2154-\u2156][\u2159-\u215b][\u215f\u2215\u23ae"
+ L"\u29f6\u29f8\u2afb\u2afd][\u2ff0-\u2ffb][\u3014"
+ L"\u3015\u3033\u3164\u321d\u321e\u33ae\u33af\u33c6\u33df\ufe14"
+ L"\ufe15\ufe3f\ufe5d\ufe5e\ufeff\uff0e\uff06\uff61\uffa0\ufff9]"
+ L"[\ufffa-\ufffd]]"), status);
+ DCHECK(U_SUCCESS(status));
+ icu::RegexMatcher dangerous_patterns(icu::UnicodeString(
+ // Lone katakana no, so, or n
+ L"[^\\p{Katakana}][\u30ce\u30f3\u30bd][^\\p{Katakana}]"
+ // Repeating Japanese accent characters
+ L"|[\u3099\u309a\u309b\u309c][\u3099\u309a\u309b\u309c]"),
+ 0, status);
+#else
+ icu::UnicodeSet dangerous_characters(icu::UnicodeString(
+ "[[\\u0020\\u00ad\\u00bc\\u00bd\\u01c3\\u0337\\u0338"
+ "\\u05c3\\u05f4\\u06d4\\u0702\\u115f\\u1160][\\u2000-\\u200b]"
+ "[\\u2024\\u2027\\u2028\\u2029\\u2039\\u203a\\u2044\\u205f]"
+ "[\\u2154-\\u2156][\\u2159-\\u215b][\\u215f\\u2215\\u23ae"
+ "\\u29f6\\u29f8\\u2afb\\u2afd][\\u2ff0-\\u2ffb][\\u3014"
+ "\\u3015\\u3033\\u3164\\u321d\\u321e\\u33ae\\u33af\\u33c6\\u33df\\ufe14"
+ "\\ufe15\\ufe3f\\ufe5d\\ufe5e\\ufeff\\uff0e\\uff06\\uff61\\uffa0\\ufff9]"
+ "[\\ufffa-\\ufffd]]", -1, US_INV), status);
+ DCHECK(U_SUCCESS(status));
+ icu::RegexMatcher dangerous_patterns(icu::UnicodeString(
+ // Lone katakana no, so, or n
+ "[^\\p{Katakana}][\\u30ce\\u30f3\u30bd][^\\p{Katakana}]"
+ // Repeating Japanese accent characters
+ "|[\\u3099\\u309a\\u309b\\u309c][\\u3099\\u309a\\u309b\\u309c]"),
+ 0, status);
+#endif
+ DCHECK(U_SUCCESS(status));
+ icu::UnicodeSet component_characters;
+ icu::UnicodeString component_string(str, str_len);
+ component_characters.addAll(component_string);
+ if (dangerous_characters.containsSome(component_characters))
+ return false;
+
+ DCHECK(U_SUCCESS(status));
+ dangerous_patterns.reset(component_string);
+ if (dangerous_patterns.find())
+ return false;
+
+ // If the language list is empty, the result is completely determined
+ // by whether a component is a single script or not. This will block
+ // even "safe" script mixing cases like <Chinese, Latin-ASCII> that are
+ // allowed with |languages| (while it blocks Chinese + Latin letters with
+ // an accent as should be the case), but we want to err on the safe side
+ // when |languages| is empty.
+ if (languages.empty())
+ return IsIDNComponentInSingleScript(str, str_len);
+
+ // |common_characters| is made up of ASCII numbers, hyphen, plus and
+ // underscore that are used across scripts and allowed in domain names.
+ // (sync'd with characters allowed in url_canon_host with square
+ // brackets excluded.) See kHostCharLookup[] array in url_canon_host.cc.
+ icu::UnicodeSet common_characters(UNICODE_STRING_SIMPLE("[[0-9]\\-_+\\ ]"),
+ status);
+ DCHECK(U_SUCCESS(status));
+ // Subtract common characters because they're always allowed so that
+ // we just have to check if a language-specific set contains
+ // the remainder.
+ component_characters.removeAll(common_characters);
+
+ base::StringTokenizer t(languages, ",");
+ while (t.GetNext()) {
+ if (IsComponentCoveredByLang(component_characters, t.token()))
+ return true;
+ }
+ return false;
+}
+
+// A wrapper to use LazyInstance<>::Leaky with ICU's UIDNA, a C pointer to
+// a UTS46/IDNA 2008 handling object opened with uidna_openUTS46().
+//
+// We use UTS46 with BiDiCheck to migrate from IDNA 2003 to IDNA 2008 with
+// the backward compatibility in mind. What it does:
+//
+// 1. Use the up-to-date Unicode data.
+// 2. Define a case folding/mapping with the up-to-date Unicode data as
+// in IDNA 2003.
+// 3. Use transitional mechanism for 4 deviation characters (sharp-s,
+// final sigma, ZWJ and ZWNJ) for now.
+// 4. Continue to allow symbols and punctuations.
+// 5. Apply new BiDi check rules more permissive than the IDNA 2003 BiDI rules.
+// 6. Do not apply STD3 rules
+// 7. Do not allow unassigned code points.
+//
+// It also closely matches what IE 10 does except for the BiDi check (
+// http://goo.gl/3XBhqw ).
+// See http://http://unicode.org/reports/tr46/ and references therein
+// for more details.
+struct UIDNAWrapper {
+ UIDNAWrapper() {
+ UErrorCode err = U_ZERO_ERROR;
+ // TODO(jungshik): Change options as different parties (browsers,
+ // registrars, search engines) converge toward a consensus.
+ value = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
+ if (U_FAILURE(err))
+ value = NULL;
+ }
+
+ UIDNA* value;
+};
+
+static base::LazyInstance<UIDNAWrapper>::Leaky
+ g_uidna = LAZY_INSTANCE_INITIALIZER;
+
+// Converts one component of a host (between dots) to IDN if safe. The result
+// will be APPENDED to the given output string and will be the same as the input
+// if it is not IDN or the IDN is unsafe to display. Returns whether any
+// conversion was performed.
+bool IDNToUnicodeOneComponent(const base::char16* comp,
+ size_t comp_len,
+ const std::string& languages,
+ base::string16* out) {
+ DCHECK(out);
+ if (comp_len == 0)
+ return false;
+
+ // Only transform if the input can be an IDN component.
+ static const base::char16 kIdnPrefix[] = {'x', 'n', '-', '-'};
+ if ((comp_len > arraysize(kIdnPrefix)) &&
+ !memcmp(comp, kIdnPrefix, arraysize(kIdnPrefix) * sizeof(base::char16))) {
+ UIDNA* uidna = g_uidna.Get().value;
+ DCHECK(uidna != NULL);
+ size_t original_length = out->length();
+ int output_length = 64;
+ UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+ UErrorCode status;
+ do {
+ out->resize(original_length + output_length);
+ status = U_ZERO_ERROR;
+ // This returns the actual length required. If this is more than 64
+ // code units, |status| will be U_BUFFER_OVERFLOW_ERROR and we'll try
+ // the conversion again, but with a sufficiently large buffer.
+ output_length = uidna_labelToUnicode(
+ uidna, comp, static_cast<int32_t>(comp_len), &(*out)[original_length],
+ output_length, &info, &status);
+ } while ((status == U_BUFFER_OVERFLOW_ERROR && info.errors == 0));
+
+ if (U_SUCCESS(status) && info.errors == 0) {
+ // Converted successfully. Ensure that the converted component
+ // can be safely displayed to the user.
+ out->resize(original_length + output_length);
+ if (IsIDNComponentSafe(out->data() + original_length, output_length,
+ languages))
+ return true;
+ }
+
+ // Something went wrong. Revert to original string.
+ out->resize(original_length);
+ }
+
+ // We get here with no IDN or on error, in which case we just append the
+ // literal input.
+ out->append(comp, comp_len);
+ return false;
+}
+
+// TODO(brettw) bug 734373: check the scripts for each host component and
+// don't un-IDN-ize if there is more than one. Alternatively, only IDN for
+// scripts that the user has installed. For now, just put the entire
+// path through IDN. Maybe this feature can be implemented in ICU itself?
+//
+// We may want to skip this step in the case of file URLs to allow unicode
+// UNC hostnames regardless of encodings.
+base::string16 IDNToUnicodeWithAdjustments(
+ const std::string& host,
+ const std::string& languages,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ if (adjustments)
+ adjustments->clear();
+ // Convert the ASCII input to a base::string16 for ICU.
+ base::string16 input16;
+ input16.reserve(host.length());
+ input16.insert(input16.end(), host.begin(), host.end());
+
+ // Do each component of the host separately, since we enforce script matching
+ // on a per-component basis.
+ base::string16 out16;
+ {
+ for (size_t component_start = 0, component_end;
+ component_start < input16.length();
+ component_start = component_end + 1) {
+ // Find the end of the component.
+ component_end = input16.find('.', component_start);
+ if (component_end == base::string16::npos)
+ component_end = input16.length(); // For getting the last component.
+ size_t component_length = component_end - component_start;
+ size_t new_component_start = out16.length();
+ bool converted_idn = false;
+ if (component_end > component_start) {
+ // Add the substring that we just found.
+ converted_idn = IDNToUnicodeOneComponent(
+ input16.data() + component_start, component_length, languages,
+ &out16);
+ }
+ size_t new_component_length = out16.length() - new_component_start;
+
+ if (converted_idn && adjustments) {
+ adjustments->push_back(base::OffsetAdjuster::Adjustment(
+ component_start, component_length, new_component_length));
+ }
+
+ // Need to add the dot we just found (if we found one).
+ if (component_end < input16.length())
+ out16.push_back('.');
+ }
+ }
+ return out16;
+}
+
+// If |component| is valid, its begin is incremented by |delta|.
+void AdjustComponent(int delta, url::Component* component) {
+ if (!component->is_valid())
+ return;
+
+ DCHECK(delta >= 0 || component->begin >= -delta);
+ component->begin += delta;
+}
+
+// Adjusts all the components of |parsed| by |delta|, except for the scheme.
+void AdjustAllComponentsButScheme(int delta, url::Parsed* parsed) {
+ AdjustComponent(delta, &(parsed->username));
+ AdjustComponent(delta, &(parsed->password));
+ AdjustComponent(delta, &(parsed->host));
+ AdjustComponent(delta, &(parsed->port));
+ AdjustComponent(delta, &(parsed->path));
+ AdjustComponent(delta, &(parsed->query));
+ AdjustComponent(delta, &(parsed->ref));
+}
+
+// Helper for FormatUrlWithOffsets().
+base::string16 FormatViewSourceUrl(
+ const GURL& url,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ url::Parsed* new_parsed,
+ size_t* prefix_end,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ DCHECK(new_parsed);
+ const char kViewSource[] = "view-source:";
+ const size_t kViewSourceLength = arraysize(kViewSource) - 1;
+
+ // Format the underlying URL and record adjustments.
+ const std::string& url_str(url.possibly_invalid_spec());
+ adjustments->clear();
+ base::string16 result(base::ASCIIToUTF16(kViewSource) +
+ FormatUrlWithAdjustments(GURL(url_str.substr(kViewSourceLength)),
+ languages, format_types, unescape_rules,
+ new_parsed, prefix_end, adjustments));
+ // Revise |adjustments| by shifting to the offsets to prefix that the above
+ // call to FormatUrl didn't get to see.
+ for (base::OffsetAdjuster::Adjustments::iterator it = adjustments->begin();
+ it != adjustments->end(); ++it)
+ it->original_offset += kViewSourceLength;
+
+ // Adjust positions of the parsed components.
+ if (new_parsed->scheme.is_nonempty()) {
+ // Assume "view-source:real-scheme" as a scheme.
+ new_parsed->scheme.len += kViewSourceLength;
+ } else {
+ new_parsed->scheme.begin = 0;
+ new_parsed->scheme.len = kViewSourceLength - 1;
+ }
+ AdjustAllComponentsButScheme(kViewSourceLength, new_parsed);
+
+ if (prefix_end)
+ *prefix_end += kViewSourceLength;
+
+ return result;
+}
+
+class AppendComponentTransform {
+ public:
+ AppendComponentTransform() {}
+ virtual ~AppendComponentTransform() {}
+
+ virtual base::string16 Execute(
+ const std::string& component_text,
+ base::OffsetAdjuster::Adjustments* adjustments) const = 0;
+
+ // NOTE: No DISALLOW_COPY_AND_ASSIGN here, since gcc < 4.3.0 requires an
+ // accessible copy constructor in order to call AppendFormattedComponent()
+ // with an inline temporary (see http://gcc.gnu.org/bugs/#cxx%5Frvalbind ).
+};
+
+class HostComponentTransform : public AppendComponentTransform {
+ public:
+ explicit HostComponentTransform(const std::string& languages)
+ : languages_(languages) {
+ }
+
+ private:
+ virtual base::string16 Execute(
+ const std::string& component_text,
+ base::OffsetAdjuster::Adjustments* adjustments) const OVERRIDE {
+ return IDNToUnicodeWithAdjustments(component_text, languages_,
+ adjustments);
+ }
+
+ const std::string& languages_;
+};
+
+class NonHostComponentTransform : public AppendComponentTransform {
+ public:
+ explicit NonHostComponentTransform(UnescapeRule::Type unescape_rules)
+ : unescape_rules_(unescape_rules) {
+ }
+
+ private:
+ virtual base::string16 Execute(
+ const std::string& component_text,
+ base::OffsetAdjuster::Adjustments* adjustments) const OVERRIDE {
+ return (unescape_rules_ == UnescapeRule::NONE) ?
+ base::UTF8ToUTF16WithAdjustments(component_text, adjustments) :
+ UnescapeAndDecodeUTF8URLComponentWithAdjustments(component_text,
+ unescape_rules_, adjustments);
+ }
+
+ const UnescapeRule::Type unescape_rules_;
+};
+
+// Transforms the portion of |spec| covered by |original_component| according to
+// |transform|. Appends the result to |output|. If |output_component| is
+// non-NULL, its start and length are set to the transformed component's new
+// start and length. If |adjustments| is non-NULL, appends adjustments (if
+// any) that reflect the transformation the original component underwent to
+// become the transformed value appended to |output|.
+void AppendFormattedComponent(const std::string& spec,
+ const url::Component& original_component,
+ const AppendComponentTransform& transform,
+ base::string16* output,
+ url::Component* output_component,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ DCHECK(output);
+ if (original_component.is_nonempty()) {
+ size_t original_component_begin =
+ static_cast<size_t>(original_component.begin);
+ size_t output_component_begin = output->length();
+ std::string component_str(spec, original_component_begin,
+ static_cast<size_t>(original_component.len));
+
+ // Transform |component_str| and modify |adjustments| appropriately.
+ base::OffsetAdjuster::Adjustments component_transform_adjustments;
+ output->append(
+ transform.Execute(component_str, &component_transform_adjustments));
+
+ // Shift all the adjustments made for this component so the offsets are
+ // valid for the original string and add them to |adjustments|.
+ for (base::OffsetAdjuster::Adjustments::iterator comp_iter =
+ component_transform_adjustments.begin();
+ comp_iter != component_transform_adjustments.end(); ++comp_iter)
+ comp_iter->original_offset += original_component_begin;
+ if (adjustments) {
+ adjustments->insert(adjustments->end(),
+ component_transform_adjustments.begin(),
+ component_transform_adjustments.end());
+ }
+
+ // Set positions of the parsed component.
+ if (output_component) {
+ output_component->begin = static_cast<int>(output_component_begin);
+ output_component->len =
+ static_cast<int>(output->length() - output_component_begin);
+ }
+ } else if (output_component) {
+ output_component->reset();
+ }
+}
+
+} // namespace
+
+const FormatUrlType kFormatUrlOmitNothing = 0;
+const FormatUrlType kFormatUrlOmitUsernamePassword = 1 << 0;
+const FormatUrlType kFormatUrlOmitHTTP = 1 << 1;
+const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname = 1 << 2;
+const FormatUrlType kFormatUrlOmitAll = kFormatUrlOmitUsernamePassword |
+ kFormatUrlOmitHTTP | kFormatUrlOmitTrailingSlashOnBareHostname;
+
+base::string16 IDNToUnicode(const std::string& host,
+ const std::string& languages) {
+ return IDNToUnicodeWithAdjustments(host, languages, NULL);
+}
+
+std::string GetDirectoryListingEntry(const base::string16& name,
+ const std::string& raw_bytes,
+ bool is_dir,
+ int64 size,
+ Time modified) {
+ std::string result;
+ result.append("<script>addRow(");
+ base::EscapeJSONString(name, true, &result);
+ result.append(",");
+ if (raw_bytes.empty()) {
+ base::EscapeJSONString(EscapePath(base::UTF16ToUTF8(name)), true, &result);
+ } else {
+ base::EscapeJSONString(EscapePath(raw_bytes), true, &result);
+ }
+ if (is_dir) {
+ result.append(",1,");
+ } else {
+ result.append(",0,");
+ }
+
+ // Negative size means unknown or not applicable (e.g. directory).
+ base::string16 size_string;
+ if (size >= 0)
+ size_string = FormatBytesUnlocalized(size);
+ base::EscapeJSONString(size_string, true, &result);
+
+ result.append(",");
+
+ base::string16 modified_str;
+ // |modified| can be NULL in FTP listings.
+ if (!modified.is_null()) {
+ modified_str = base::TimeFormatShortDateAndTime(modified);
+ }
+ base::EscapeJSONString(modified_str, true, &result);
+
+ result.append(");</script>\n");
+
+ return result;
+}
+
+void AppendFormattedHost(const GURL& url,
+ const std::string& languages,
+ base::string16* output) {
+ AppendFormattedComponent(url.possibly_invalid_spec(),
+ url.parsed_for_possibly_invalid_spec().host,
+ HostComponentTransform(languages), output, NULL, NULL);
+}
+
+base::string16 FormatUrlWithOffsets(
+ const GURL& url,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ url::Parsed* new_parsed,
+ size_t* prefix_end,
+ std::vector<size_t>* offsets_for_adjustment) {
+ base::OffsetAdjuster::Adjustments adjustments;
+ const base::string16& format_url_return_value =
+ FormatUrlWithAdjustments(url, languages, format_types, unescape_rules,
+ new_parsed, prefix_end, &adjustments);
+ base::OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+ if (offsets_for_adjustment) {
+ std::for_each(
+ offsets_for_adjustment->begin(),
+ offsets_for_adjustment->end(),
+ base::LimitOffset<std::string>(format_url_return_value.length()));
+ }
+ return format_url_return_value;
+}
+
+base::string16 FormatUrlWithAdjustments(
+ const GURL& url,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ url::Parsed* new_parsed,
+ size_t* prefix_end,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ DCHECK(adjustments != NULL);
+ adjustments->clear();
+ url::Parsed parsed_temp;
+ if (!new_parsed)
+ new_parsed = &parsed_temp;
+ else
+ *new_parsed = url::Parsed();
+
+ // Special handling for view-source:. Don't use content::kViewSourceScheme
+ // because this library shouldn't depend on chrome.
+ const char* const kViewSource = "view-source";
+ // Reject "view-source:view-source:..." to avoid deep recursion.
+ const char* const kViewSourceTwice = "view-source:view-source:";
+ if (url.SchemeIs(kViewSource) &&
+ !StartsWithASCII(url.possibly_invalid_spec(), kViewSourceTwice, false)) {
+ return FormatViewSourceUrl(url, languages, format_types,
+ unescape_rules, new_parsed, prefix_end,
+ adjustments);
+ }
+
+ // We handle both valid and invalid URLs (this will give us the spec
+ // regardless of validity).
+ const std::string& spec = url.possibly_invalid_spec();
+ const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+
+ // Scheme & separators. These are ASCII.
+ base::string16 url_string;
+ url_string.insert(
+ url_string.end(), spec.begin(),
+ spec.begin() + parsed.CountCharactersBefore(url::Parsed::USERNAME, true));
+ const char kHTTP[] = "http://";
+ const char kFTP[] = "ftp.";
+ // url_fixer::FixupURL() treats "ftp.foo.com" as ftp://ftp.foo.com. This
+ // means that if we trim "http://" off a URL whose host starts with "ftp." and
+ // the user inputs this into any field subject to fixup (which is basically
+ // all input fields), the meaning would be changed. (In fact, often the
+ // formatted URL is directly pre-filled into an input field.) For this reason
+ // we avoid stripping "http://" in this case.
+ bool omit_http = (format_types & kFormatUrlOmitHTTP) &&
+ EqualsASCII(url_string, kHTTP) &&
+ !StartsWithASCII(url.host(), kFTP, true);
+ new_parsed->scheme = parsed.scheme;
+
+ // Username & password.
+ if ((format_types & kFormatUrlOmitUsernamePassword) != 0) {
+ // Remove the username and password fields. We don't want to display those
+ // to the user since they can be used for attacks,
+ // e.g. "http://google.com:search@evil.ru/"
+ new_parsed->username.reset();
+ new_parsed->password.reset();
+ // Update the adjustments based on removed username and/or password.
+ if (parsed.username.is_nonempty() || parsed.password.is_nonempty()) {
+ if (parsed.username.is_nonempty() && parsed.password.is_nonempty()) {
+ // The seeming off-by-two is to account for the ':' after the username
+ // and '@' after the password.
+ adjustments->push_back(base::OffsetAdjuster::Adjustment(
+ static_cast<size_t>(parsed.username.begin),
+ static_cast<size_t>(parsed.username.len + parsed.password.len + 2),
+ 0));
+ } else {
+ const url::Component* nonempty_component =
+ parsed.username.is_nonempty() ? &parsed.username : &parsed.password;
+ // The seeming off-by-one is to account for the '@' after the
+ // username/password.
+ adjustments->push_back(base::OffsetAdjuster::Adjustment(
+ static_cast<size_t>(nonempty_component->begin),
+ static_cast<size_t>(nonempty_component->len + 1),
+ 0));
+ }
+ }
+ } else {
+ AppendFormattedComponent(spec, parsed.username,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->username, adjustments);
+ if (parsed.password.is_valid())
+ url_string.push_back(':');
+ AppendFormattedComponent(spec, parsed.password,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->password, adjustments);
+ if (parsed.username.is_valid() || parsed.password.is_valid())
+ url_string.push_back('@');
+ }
+ if (prefix_end)
+ *prefix_end = static_cast<size_t>(url_string.length());
+
+ // Host.
+ AppendFormattedComponent(spec, parsed.host, HostComponentTransform(languages),
+ &url_string, &new_parsed->host, adjustments);
+
+ // Port.
+ if (parsed.port.is_nonempty()) {
+ url_string.push_back(':');
+ new_parsed->port.begin = url_string.length();
+ url_string.insert(url_string.end(),
+ spec.begin() + parsed.port.begin,
+ spec.begin() + parsed.port.end());
+ new_parsed->port.len = url_string.length() - new_parsed->port.begin;
+ } else {
+ new_parsed->port.reset();
+ }
+
+ // Path & query. Both get the same general unescape & convert treatment.
+ if (!(format_types & kFormatUrlOmitTrailingSlashOnBareHostname) ||
+ !CanStripTrailingSlash(url)) {
+ AppendFormattedComponent(spec, parsed.path,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->path, adjustments);
+ } else {
+ if (parsed.path.len > 0) {
+ adjustments->push_back(base::OffsetAdjuster::Adjustment(
+ parsed.path.begin, parsed.path.len, 0));
+ }
+ }
+ if (parsed.query.is_valid())
+ url_string.push_back('?');
+ AppendFormattedComponent(spec, parsed.query,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->query, adjustments);
+
+ // Ref. This is valid, unescaped UTF-8, so we can just convert.
+ if (parsed.ref.is_valid())
+ url_string.push_back('#');
+ AppendFormattedComponent(spec, parsed.ref,
+ NonHostComponentTransform(UnescapeRule::NONE),
+ &url_string, &new_parsed->ref, adjustments);
+
+ // If we need to strip out http do it after the fact.
+ if (omit_http && StartsWith(url_string, base::ASCIIToUTF16(kHTTP), true)) {
+ const size_t kHTTPSize = arraysize(kHTTP) - 1;
+ url_string = url_string.substr(kHTTPSize);
+ // Because offsets in the |adjustments| are already calculated with respect
+ // to the string with the http:// prefix in it, those offsets remain correct
+ // after stripping the prefix. The only thing necessary is to add an
+ // adjustment to reflect the stripped prefix.
+ adjustments->insert(adjustments->begin(),
+ base::OffsetAdjuster::Adjustment(0, kHTTPSize, 0));
+
+ if (prefix_end)
+ *prefix_end -= kHTTPSize;
+
+ // Adjust new_parsed.
+ DCHECK(new_parsed->scheme.is_valid());
+ int delta = -(new_parsed->scheme.len + 3); // +3 for ://.
+ new_parsed->scheme.reset();
+ AdjustAllComponentsButScheme(delta, new_parsed);
+ }
+
+ return url_string;
+}
+
+base::string16 FormatUrl(const GURL& url,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ url::Parsed* new_parsed,
+ size_t* prefix_end,
+ size_t* offset_for_adjustment) {
+ Offsets offsets;
+ if (offset_for_adjustment)
+ offsets.push_back(*offset_for_adjustment);
+ base::string16 result = FormatUrlWithOffsets(url, languages, format_types,
+ unescape_rules, new_parsed, prefix_end, &offsets);
+ if (offset_for_adjustment)
+ *offset_for_adjustment = offsets[0];
+ return result;
+}
+
+} // namespace net
diff --git a/chromium/net/base/net_util_icu_unittest.cc b/chromium/net/base/net_util_icu_unittest.cc
new file mode 100644
index 00000000000..9beb4349b57
--- /dev/null
+++ b/chromium/net/base/net_util_icu_unittest.cc
@@ -0,0 +1,1071 @@
+// Copyright 2014 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/base/net_util.h"
+
+#include <string.h>
+
+#include <vector>
+
+#include "base/format_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using base::ASCIIToUTF16;
+using base::WideToUTF16;
+
+namespace net {
+
+namespace {
+
+static const size_t kNpos = base::string16::npos;
+
+const char* kLanguages[] = {
+ "", "en", "zh-CN", "ja", "ko",
+ "he", "ar", "ru", "el", "fr",
+ "de", "pt", "sv", "th", "hi",
+ "de,en", "el,en", "zh-TW,en", "ko,ja", "he,ru,en",
+ "zh,ru,en"
+};
+
+struct IDNTestCase {
+ const char* input;
+ const wchar_t* unicode_output;
+ const bool unicode_allowed[arraysize(kLanguages)];
+};
+
+// TODO(jungshik) This is just a random sample of languages and is far
+// from exhaustive. We may have to generate all the combinations
+// of languages (powerset of a set of all the languages).
+const IDNTestCase idn_cases[] = {
+ // No IDN
+ {"www.google.com", L"www.google.com",
+ {true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true}},
+ {"www.google.com.", L"www.google.com.",
+ {true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true}},
+ {".", L".",
+ {true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true}},
+ {"", L"",
+ {true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true}},
+ // IDN
+ // Hanzi (Traditional Chinese)
+ {"xn--1lq90ic7f1rc.cn", L"\x5317\x4eac\x5927\x5b78.cn",
+ {true, false, true, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, true, true, false,
+ true}},
+ // Hanzi ('video' in Simplified Chinese : will pass only in zh-CN,zh)
+ {"xn--cy2a840a.com", L"\x89c6\x9891.com",
+ {true, false, true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ true}},
+ // Hanzi + '123'
+ {"www.xn--123-p18d.com", L"www.\x4e00" L"123.com",
+ {true, false, true, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, true, true, false,
+ true}},
+ // Hanzi + Latin : U+56FD is simplified and is regarded
+ // as not supported in zh-TW.
+ {"www.xn--hello-9n1hm04c.com", L"www.hello\x4e2d\x56fd.com",
+ {false, false, true, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ true}},
+ // Kanji + Kana (Japanese)
+ {"xn--l8jvb1ey91xtjb.jp", L"\x671d\x65e5\x3042\x3055\x3072.jp",
+ {true, false, false, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ false}},
+ // Katakana including U+30FC
+ {"xn--tckm4i2e.jp", L"\x30b3\x30de\x30fc\x30b9.jp",
+ {true, false, false, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ }},
+ {"xn--3ck7a7g.jp", L"\u30ce\u30f3\u30bd.jp",
+ {true, false, false, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ }},
+ // Katakana + Latin (Japanese)
+ // TODO(jungshik): Change 'false' in the first element to 'true'
+ // after upgrading to ICU 4.2.1 to use new uspoof_* APIs instead
+ // of our IsIDNComponentInSingleScript().
+ {"xn--e-efusa1mzf.jp", L"e\x30b3\x30de\x30fc\x30b9.jp",
+ {false, false, false, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ }},
+ {"xn--3bkxe.jp", L"\x30c8\x309a.jp",
+ {false, false, false, true, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ }},
+ // Hangul (Korean)
+ {"www.xn--or3b17p6jjc.kr", L"www.\xc804\xc790\xc815\xbd80.kr",
+ {true, false, false, false, true,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ false}},
+ // b<u-umlaut>cher (German)
+ {"xn--bcher-kva.de", L"b\x00fc" L"cher.de",
+ {true, false, false, false, false,
+ false, false, false, false, true,
+ true, false, false, false, false,
+ true, false, false, false, false,
+ false}},
+ // a with diaeresis
+ {"www.xn--frgbolaget-q5a.se", L"www.f\x00e4rgbolaget.se",
+ {true, false, false, false, false,
+ false, false, false, false, false,
+ true, false, true, false, false,
+ true, false, false, false, false,
+ false}},
+ // c-cedilla (French)
+ {"www.xn--alliancefranaise-npb.fr", L"www.alliancefran\x00e7" L"aise.fr",
+ {true, false, false, false, false,
+ false, false, false, false, true,
+ false, true, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // caf'e with acute accent' (French)
+ {"xn--caf-dma.fr", L"caf\x00e9.fr",
+ {true, false, false, false, false,
+ false, false, false, false, true,
+ false, true, true, false, false,
+ false, false, false, false, false,
+ false}},
+ // c-cedillla and a with tilde (Portuguese)
+ {"xn--poema-9qae5a.com.br", L"p\x00e3oema\x00e7\x00e3.com.br",
+ {true, false, false, false, false,
+ false, false, false, false, false,
+ false, true, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // s with caron
+ {"xn--achy-f6a.com", L"\x0161" L"achy.com",
+ {true, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // TODO(jungshik) : Add examples with Cyrillic letters
+ // only used in some languages written in Cyrillic.
+ // Eutopia (Greek)
+ {"xn--kxae4bafwg.gr", L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1.gr",
+ {true, false, false, false, false,
+ false, false, false, true, false,
+ false, false, false, false, false,
+ false, true, false, false, false,
+ false}},
+ // Eutopia + 123 (Greek)
+ {"xn---123-pldm0haj2bk.gr",
+ L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1-123.gr",
+ {true, false, false, false, false,
+ false, false, false, true, false,
+ false, false, false, false, false,
+ false, true, false, false, false,
+ false}},
+ // Cyrillic (Russian)
+ {"xn--n1aeec9b.ru", L"\x0442\x043e\x0440\x0442\x044b.ru",
+ {true, false, false, false, false,
+ false, false, true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, true,
+ true}},
+ // Cyrillic + 123 (Russian)
+ {"xn---123-45dmmc5f.ru", L"\x0442\x043e\x0440\x0442\x044b-123.ru",
+ {true, false, false, false, false,
+ false, false, true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, true,
+ true}},
+ // Arabic
+ {"xn--mgba1fmg.ar", L"\x0627\x0641\x0644\x0627\x0645.ar",
+ {true, false, false, false, false,
+ false, true, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // Hebrew
+ {"xn--4dbib.he", L"\x05d5\x05d0\x05d4.he",
+ {true, false, false, false, false,
+ true, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, true,
+ false}},
+ // Thai
+ {"xn--12c2cc4ag3b4ccu.th",
+ L"\x0e2a\x0e32\x0e22\x0e01\x0e32\x0e23\x0e1a\x0e34\x0e19.th",
+ {true, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, true, false,
+ false, false, false, false, false,
+ false}},
+ // Devangari (Hindi)
+ {"www.xn--l1b6a9e1b7c.in", L"www.\x0905\x0915\x094b\x0932\x093e.in",
+ {true, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, true,
+ false, false, false, false, false,
+ false}},
+ // Invalid IDN
+ {"xn--hello?world.com", NULL,
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // Unsafe IDNs
+ // "payp<alpha>l.com"
+ {"www.xn--paypl-g9d.com", L"payp\x03b1l.com",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // google.gr with Greek omicron and epsilon
+ {"xn--ggl-6xc1ca.gr", L"g\x03bf\x03bfgl\x03b5.gr",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // google.ru with Cyrillic o
+ {"xn--ggl-tdd6ba.ru", L"g\x043e\x043egl\x0435.ru",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // h<e with acute>llo<China in Han>.cn
+ {"xn--hllo-bpa7979ih5m.cn", L"h\x00e9llo\x4e2d\x56fd.cn",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // <Greek rho><Cyrillic a><Cyrillic u>.ru
+ {"xn--2xa6t2b.ru", L"\x03c1\x0430\x0443.ru",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ // One that's really long that will force a buffer realloc
+ {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaa",
+ L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ L"aaaaaaaa",
+ {true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true, true, true, true, true,
+ true}},
+ // Test cases for characters we blacklisted although allowed in IDN.
+ // Embedded spaces will be turned to %20 in the display.
+ // TODO(jungshik): We need to have more cases. This is a typical
+ // data-driven trap. The following test cases need to be separated
+ // and tested only for a couple of languages.
+ {"xn--osd3820f24c.kr", L"\xac00\xb098\x115f.kr",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false}},
+ {"www.xn--google-ho0coa.com", L"www.\x2039google\x203a.com",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ }},
+ {"google.xn--comabc-k8d", L"google.com\x0338" L"abc",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ }},
+ {"google.xn--com-oh4ba.evil.jp", L"google.com\x309a\x309a.evil.jp",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ }},
+ {"google.xn--comevil-v04f.jp", L"google.com\x30ce" L"evil.jp",
+ {false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ }},
+#if 0
+ // These two cases are special. We need a separate test.
+ // U+3000 and U+3002 are normalized to ASCII space and dot.
+ {"xn-- -kq6ay5z.cn", L"\x4e2d\x56fd\x3000.cn",
+ {false, false, true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, true, false, false,
+ true}},
+ {"xn--fiqs8s.cn", L"\x4e2d\x56fd\x3002" L"cn",
+ {false, false, true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, true, false, false,
+ true}},
+#endif
+};
+
+struct AdjustOffsetCase {
+ size_t input_offset;
+ size_t output_offset;
+};
+
+struct UrlTestData {
+ const char* description;
+ const char* input;
+ const char* languages;
+ FormatUrlTypes format_types;
+ UnescapeRule::Type escape_rules;
+ const wchar_t* output; // Use |wchar_t| to handle Unicode constants easily.
+ size_t prefix_len;
+};
+
+// A helper for IDN*{Fast,Slow}.
+// Append "::<language list>" to |expected| and |actual| to make it
+// easy to tell which sub-case fails without debugging.
+void AppendLanguagesToOutputs(const char* languages,
+ base::string16* expected,
+ base::string16* actual) {
+ base::string16 to_append = ASCIIToUTF16("::") + ASCIIToUTF16(languages);
+ expected->append(to_append);
+ actual->append(to_append);
+}
+
+// A pair of helpers for the FormatUrlWithOffsets() test.
+void VerboseExpect(size_t expected,
+ size_t actual,
+ const std::string& original_url,
+ size_t position,
+ const base::string16& formatted_url) {
+ EXPECT_EQ(expected, actual) << "Original URL: " << original_url
+ << " (at char " << position << ")\nFormatted URL: " << formatted_url;
+}
+
+void CheckAdjustedOffsets(const std::string& url_string,
+ const std::string& languages,
+ FormatUrlTypes format_types,
+ UnescapeRule::Type unescape_rules,
+ const size_t* output_offsets) {
+ GURL url(url_string);
+ size_t url_length = url_string.length();
+ std::vector<size_t> offsets;
+ for (size_t i = 0; i <= url_length + 1; ++i)
+ offsets.push_back(i);
+ offsets.push_back(500000); // Something larger than any input length.
+ offsets.push_back(std::string::npos);
+ base::string16 formatted_url = FormatUrlWithOffsets(url, languages,
+ format_types, unescape_rules, NULL, NULL, &offsets);
+ for (size_t i = 0; i < url_length; ++i)
+ VerboseExpect(output_offsets[i], offsets[i], url_string, i, formatted_url);
+ VerboseExpect(formatted_url.length(), offsets[url_length], url_string,
+ url_length, formatted_url);
+ VerboseExpect(base::string16::npos, offsets[url_length + 1], url_string,
+ 500000, formatted_url);
+ VerboseExpect(base::string16::npos, offsets[url_length + 2], url_string,
+ std::string::npos, formatted_url);
+}
+
+} // anonymous namespace
+
+TEST(NetUtilTest, IDNToUnicodeFast) {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(idn_cases); i++) {
+ for (size_t j = 0; j < arraysize(kLanguages); j++) {
+ // ja || zh-TW,en || ko,ja -> IDNToUnicodeSlow
+ if (j == 3 || j == 17 || j == 18)
+ continue;
+ base::string16 output(IDNToUnicode(idn_cases[i].input, kLanguages[j]));
+ base::string16 expected(idn_cases[i].unicode_allowed[j] ?
+ WideToUTF16(idn_cases[i].unicode_output) :
+ ASCIIToUTF16(idn_cases[i].input));
+ AppendLanguagesToOutputs(kLanguages[j], &expected, &output);
+ EXPECT_EQ(expected, output);
+ }
+ }
+}
+
+TEST(NetUtilTest, IDNToUnicodeSlow) {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(idn_cases); i++) {
+ for (size_t j = 0; j < arraysize(kLanguages); j++) {
+ // !(ja || zh-TW,en || ko,ja) -> IDNToUnicodeFast
+ if (!(j == 3 || j == 17 || j == 18))
+ continue;
+ base::string16 output(IDNToUnicode(idn_cases[i].input, kLanguages[j]));
+ base::string16 expected(idn_cases[i].unicode_allowed[j] ?
+ WideToUTF16(idn_cases[i].unicode_output) :
+ ASCIIToUTF16(idn_cases[i].input));
+ AppendLanguagesToOutputs(kLanguages[j], &expected, &output);
+ EXPECT_EQ(expected, output);
+ }
+ }
+}
+
+TEST(NetUtilTest, StripWWW) {
+ EXPECT_EQ(base::string16(), StripWWW(base::string16()));
+ EXPECT_EQ(base::string16(), StripWWW(ASCIIToUTF16("www.")));
+ EXPECT_EQ(ASCIIToUTF16("blah"), StripWWW(ASCIIToUTF16("www.blah")));
+ EXPECT_EQ(ASCIIToUTF16("blah"), StripWWW(ASCIIToUTF16("blah")));
+}
+
+// This is currently a windows specific function.
+#if defined(OS_WIN)
+namespace {
+
+struct GetDirectoryListingEntryCase {
+ const wchar_t* name;
+ const char* raw_bytes;
+ bool is_dir;
+ int64 filesize;
+ base::Time time;
+ const char* expected;
+};
+
+} // namespace
+
+TEST(NetUtilTest, GetDirectoryListingEntry) {
+ const GetDirectoryListingEntryCase test_cases[] = {
+ {L"Foo",
+ "",
+ false,
+ 10000,
+ base::Time(),
+ "<script>addRow(\"Foo\",\"Foo\",0,\"9.8 kB\",\"\");</script>\n"},
+ {L"quo\"tes",
+ "",
+ false,
+ 10000,
+ base::Time(),
+ "<script>addRow(\"quo\\\"tes\",\"quo%22tes\",0,\"9.8 kB\",\"\");</script>"
+ "\n"},
+ {L"quo\"tes",
+ "quo\"tes",
+ false,
+ 10000,
+ base::Time(),
+ "<script>addRow(\"quo\\\"tes\",\"quo%22tes\",0,\"9.8 kB\",\"\");</script>"
+ "\n"},
+ // U+D55C0 U+AE00. raw_bytes is empty (either a local file with
+ // UTF-8/UTF-16 encoding or a remote file on an ftp server using UTF-8
+ {L"\xD55C\xAE00.txt",
+ "",
+ false,
+ 10000,
+ base::Time(),
+ "<script>addRow(\"\xED\x95\x9C\xEA\xB8\x80.txt\","
+ "\"%ED%95%9C%EA%B8%80.txt\",0,\"9.8 kB\",\"\");</script>\n"},
+ // U+D55C0 U+AE00. raw_bytes is the corresponding EUC-KR sequence:
+ // a local or remote file in EUC-KR.
+ {L"\xD55C\xAE00.txt",
+ "\xC7\xD1\xB1\xDB.txt",
+ false,
+ 10000,
+ base::Time(),
+ "<script>addRow(\"\xED\x95\x9C\xEA\xB8\x80.txt\",\"%C7%D1%B1%DB.txt\""
+ ",0,\"9.8 kB\",\"\");</script>\n"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ const std::string results = GetDirectoryListingEntry(
+ WideToUTF16(test_cases[i].name),
+ test_cases[i].raw_bytes,
+ test_cases[i].is_dir,
+ test_cases[i].filesize,
+ test_cases[i].time);
+ EXPECT_EQ(test_cases[i].expected, results);
+ }
+}
+
+#endif
+
+TEST(NetUtilTest, FormatUrl) {
+ FormatUrlTypes default_format_type = kFormatUrlOmitUsernamePassword;
+ const UrlTestData tests[] = {
+ {"Empty URL", "", "", default_format_type, UnescapeRule::NORMAL, L"", 0},
+
+ {"Simple URL",
+ "http://www.google.com/", "", default_format_type, UnescapeRule::NORMAL,
+ L"http://www.google.com/", 7},
+
+ {"With a port number and a reference",
+ "http://www.google.com:8080/#\xE3\x82\xB0", "", default_format_type,
+ UnescapeRule::NORMAL,
+ L"http://www.google.com:8080/#\x30B0", 7},
+
+ // -------- IDN tests --------
+ {"Japanese IDN with ja",
+ "http://xn--l8jvb1ey91xtjb.jp", "ja", default_format_type,
+ UnescapeRule::NORMAL, L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
+
+ {"Japanese IDN with en",
+ "http://xn--l8jvb1ey91xtjb.jp", "en", default_format_type,
+ UnescapeRule::NORMAL, L"http://xn--l8jvb1ey91xtjb.jp/", 7},
+
+ {"Japanese IDN without any languages",
+ "http://xn--l8jvb1ey91xtjb.jp", "", default_format_type,
+ UnescapeRule::NORMAL,
+ // Single script is safe for empty languages.
+ L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
+
+ {"mailto: with Japanese IDN",
+ "mailto:foo@xn--l8jvb1ey91xtjb.jp", "ja", default_format_type,
+ UnescapeRule::NORMAL,
+ // GURL doesn't assume an email address's domain part as a host name.
+ L"mailto:foo@xn--l8jvb1ey91xtjb.jp", 7},
+
+ {"file: with Japanese IDN",
+ "file://xn--l8jvb1ey91xtjb.jp/config.sys", "ja", default_format_type,
+ UnescapeRule::NORMAL,
+ L"file://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 7},
+
+ {"ftp: with Japanese IDN",
+ "ftp://xn--l8jvb1ey91xtjb.jp/config.sys", "ja", default_format_type,
+ UnescapeRule::NORMAL,
+ L"ftp://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 6},
+
+ // -------- omit_username_password flag tests --------
+ {"With username and password, omit_username_password=false",
+ "http://user:passwd@example.com/foo", "",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ L"http://user:passwd@example.com/foo", 19},
+
+ {"With username and password, omit_username_password=true",
+ "http://user:passwd@example.com/foo", "", default_format_type,
+ UnescapeRule::NORMAL, L"http://example.com/foo", 7},
+
+ {"With username and no password",
+ "http://user@example.com/foo", "", default_format_type,
+ UnescapeRule::NORMAL, L"http://example.com/foo", 7},
+
+ {"Just '@' without username and password",
+ "http://@example.com/foo", "", default_format_type, UnescapeRule::NORMAL,
+ L"http://example.com/foo", 7},
+
+ // GURL doesn't think local-part of an email address is username for URL.
+ {"mailto:, omit_username_password=true",
+ "mailto:foo@example.com", "", default_format_type, UnescapeRule::NORMAL,
+ L"mailto:foo@example.com", 7},
+
+ // -------- unescape flag tests --------
+ {"Do not unescape",
+ "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
+ "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
+ "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", "en", default_format_type,
+ UnescapeRule::NONE,
+ // GURL parses %-encoded hostnames into Punycode.
+ L"http://xn--qcka1pmc.jp/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
+ L"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", 7},
+
+ {"Unescape normally",
+ "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
+ "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
+ "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", "en", default_format_type,
+ UnescapeRule::NORMAL,
+ L"http://xn--qcka1pmc.jp/\x30B0\x30FC\x30B0\x30EB"
+ L"?q=\x30B0\x30FC\x30B0\x30EB", 7},
+
+ {"Unescape normally with BiDi control character",
+ "http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", "en", default_format_type,
+ UnescapeRule::NORMAL, L"http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", 7},
+
+ {"Unescape normally including unescape spaces",
+ "http://www.google.com/search?q=Hello%20World", "en", default_format_type,
+ UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World", 7},
+
+ /*
+ {"unescape=true with some special characters",
+ "http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", "",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ L"http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", 25},
+ */
+ // Disabled: the resultant URL becomes "...user%253A:%2540passwd...".
+
+ // -------- omit http: --------
+ {"omit http with user name",
+ "http://user@example.com/foo", "", kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, L"example.com/foo", 0},
+
+ {"omit http",
+ "http://www.google.com/", "en", kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL, L"www.google.com/",
+ 0},
+
+ {"omit http with https",
+ "https://www.google.com/", "en", kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL, L"https://www.google.com/",
+ 8},
+
+ {"omit http starts with ftp.",
+ "http://ftp.google.com/", "en", kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL, L"http://ftp.google.com/",
+ 7},
+
+ // -------- omit trailing slash on bare hostname --------
+ {"omit slash when it's the entire path",
+ "http://www.google.com/", "en",
+ kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+ L"http://www.google.com", 7},
+ {"omit slash when there's a ref",
+ "http://www.google.com/#ref", "en",
+ kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+ L"http://www.google.com/#ref", 7},
+ {"omit slash when there's a query",
+ "http://www.google.com/?", "en",
+ kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+ L"http://www.google.com/?", 7},
+ {"omit slash when it's not the entire path",
+ "http://www.google.com/foo", "en",
+ kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+ L"http://www.google.com/foo", 7},
+ {"omit slash for nonstandard URLs",
+ "data:/", "en", kFormatUrlOmitTrailingSlashOnBareHostname,
+ UnescapeRule::NORMAL, L"data:/", 5},
+ {"omit slash for file URLs",
+ "file:///", "en", kFormatUrlOmitTrailingSlashOnBareHostname,
+ UnescapeRule::NORMAL, L"file:///", 7},
+
+ // -------- view-source: --------
+ {"view-source",
+ "view-source:http://xn--qcka1pmc.jp/", "ja", default_format_type,
+ UnescapeRule::NORMAL, L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/",
+ 19},
+
+ {"view-source of view-source",
+ "view-source:view-source:http://xn--qcka1pmc.jp/", "ja",
+ default_format_type, UnescapeRule::NORMAL,
+ L"view-source:view-source:http://xn--qcka1pmc.jp/", 12},
+
+ // view-source should omit http and trailing slash where non-view-source
+ // would.
+ {"view-source omit http",
+ "view-source:http://a.b/c", "en", kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, L"view-source:a.b/c",
+ 12},
+ {"view-source omit http starts with ftp.",
+ "view-source:http://ftp.b/c", "en", kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, L"view-source:http://ftp.b/c",
+ 19},
+ {"view-source omit slash when it's the entire path",
+ "view-source:http://a.b/", "en", kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, L"view-source:a.b",
+ 12},
+ };
+
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ size_t prefix_len;
+ base::string16 formatted = FormatUrl(
+ GURL(tests[i].input), tests[i].languages, tests[i].format_types,
+ tests[i].escape_rules, NULL, &prefix_len, NULL);
+ EXPECT_EQ(WideToUTF16(tests[i].output), formatted) << tests[i].description;
+ EXPECT_EQ(tests[i].prefix_len, prefix_len) << tests[i].description;
+ }
+}
+
+TEST(NetUtilTest, FormatUrlParsed) {
+ // No unescape case.
+ url::Parsed parsed;
+ base::string16 formatted = FormatUrl(
+ GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
+ "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
+ "ja", kFormatUrlOmitNothing, UnescapeRule::NONE, &parsed, NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(
+ L"http://%E3%82%B0:%E3%83%BC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
+ L"/%E3%82%B0/?q=%E3%82%B0#\x30B0"), formatted);
+ EXPECT_EQ(WideToUTF16(L"%E3%82%B0"),
+ formatted.substr(parsed.username.begin, parsed.username.len));
+ EXPECT_EQ(WideToUTF16(L"%E3%83%BC"),
+ formatted.substr(parsed.password.begin, parsed.password.len));
+ EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"8080"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/%E3%82%B0/"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"q=%E3%82%B0"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"\x30B0"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // Unescape case.
+ formatted = FormatUrl(
+ GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
+ "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
+ "ja", kFormatUrlOmitNothing, UnescapeRule::NORMAL, &parsed, NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(L"http://\x30B0:\x30FC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
+ L"/\x30B0/?q=\x30B0#\x30B0"), formatted);
+ EXPECT_EQ(WideToUTF16(L"\x30B0"),
+ formatted.substr(parsed.username.begin, parsed.username.len));
+ EXPECT_EQ(WideToUTF16(L"\x30FC"),
+ formatted.substr(parsed.password.begin, parsed.password.len));
+ EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"8080"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/\x30B0/"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"q=\x30B0"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"\x30B0"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // Omit_username_password + unescape case.
+ formatted = FormatUrl(
+ GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
+ "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
+ "ja", kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL, &parsed,
+ NULL, NULL);
+ EXPECT_EQ(WideToUTF16(L"http://\x30B0\x30FC\x30B0\x30EB.jp:8080"
+ L"/\x30B0/?q=\x30B0#\x30B0"), formatted);
+ EXPECT_FALSE(parsed.username.is_valid());
+ EXPECT_FALSE(parsed.password.is_valid());
+ EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"8080"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/\x30B0/"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"q=\x30B0"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"\x30B0"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // View-source case.
+ formatted =
+ FormatUrl(GURL("view-source:http://user:passwd@host:81/path?query#ref"),
+ std::string(),
+ kFormatUrlOmitUsernamePassword,
+ UnescapeRule::NORMAL,
+ &parsed,
+ NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(L"view-source:http://host:81/path?query#ref"),
+ formatted);
+ EXPECT_EQ(WideToUTF16(L"view-source:http"),
+ formatted.substr(parsed.scheme.begin, parsed.scheme.len));
+ EXPECT_FALSE(parsed.username.is_valid());
+ EXPECT_FALSE(parsed.password.is_valid());
+ EXPECT_EQ(WideToUTF16(L"host"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"81"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/path"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"query"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"ref"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // omit http case.
+ formatted = FormatUrl(GURL("http://host:8000/a?b=c#d"),
+ std::string(),
+ kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL,
+ &parsed,
+ NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(L"host:8000/a?b=c#d"), formatted);
+ EXPECT_FALSE(parsed.scheme.is_valid());
+ EXPECT_FALSE(parsed.username.is_valid());
+ EXPECT_FALSE(parsed.password.is_valid());
+ EXPECT_EQ(WideToUTF16(L"host"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"8000"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/a"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"b=c"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"d"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // omit http starts with ftp case.
+ formatted = FormatUrl(GURL("http://ftp.host:8000/a?b=c#d"),
+ std::string(),
+ kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL,
+ &parsed,
+ NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(L"http://ftp.host:8000/a?b=c#d"), formatted);
+ EXPECT_TRUE(parsed.scheme.is_valid());
+ EXPECT_FALSE(parsed.username.is_valid());
+ EXPECT_FALSE(parsed.password.is_valid());
+ EXPECT_EQ(WideToUTF16(L"http"),
+ formatted.substr(parsed.scheme.begin, parsed.scheme.len));
+ EXPECT_EQ(WideToUTF16(L"ftp.host"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"8000"),
+ formatted.substr(parsed.port.begin, parsed.port.len));
+ EXPECT_EQ(WideToUTF16(L"/a"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+ EXPECT_EQ(WideToUTF16(L"b=c"),
+ formatted.substr(parsed.query.begin, parsed.query.len));
+ EXPECT_EQ(WideToUTF16(L"d"),
+ formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+ // omit http starts with 'f' case.
+ formatted = FormatUrl(GURL("http://f/"),
+ std::string(),
+ kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL,
+ &parsed,
+ NULL,
+ NULL);
+ EXPECT_EQ(WideToUTF16(L"f/"), formatted);
+ EXPECT_FALSE(parsed.scheme.is_valid());
+ EXPECT_FALSE(parsed.username.is_valid());
+ EXPECT_FALSE(parsed.password.is_valid());
+ EXPECT_FALSE(parsed.port.is_valid());
+ EXPECT_TRUE(parsed.path.is_valid());
+ EXPECT_FALSE(parsed.query.is_valid());
+ EXPECT_FALSE(parsed.ref.is_valid());
+ EXPECT_EQ(WideToUTF16(L"f"),
+ formatted.substr(parsed.host.begin, parsed.host.len));
+ EXPECT_EQ(WideToUTF16(L"/"),
+ formatted.substr(parsed.path.begin, parsed.path.len));
+}
+
+// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
+// results in the original GURL, for each ASCII character in the path.
+TEST(NetUtilTest, FormatUrlRoundTripPathASCII) {
+ for (unsigned char test_char = 32; test_char < 128; ++test_char) {
+ GURL url(std::string("http://www.google.com/") +
+ static_cast<char>(test_char));
+ size_t prefix_len;
+ base::string16 formatted = FormatUrl(url,
+ std::string(),
+ kFormatUrlOmitUsernamePassword,
+ UnescapeRule::NORMAL,
+ NULL,
+ &prefix_len,
+ NULL);
+ EXPECT_EQ(url.spec(), GURL(formatted).spec());
+ }
+}
+
+// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
+// results in the original GURL, for each escaped ASCII character in the path.
+TEST(NetUtilTest, FormatUrlRoundTripPathEscaped) {
+ for (unsigned char test_char = 32; test_char < 128; ++test_char) {
+ std::string original_url("http://www.google.com/");
+ original_url.push_back('%');
+ original_url.append(base::HexEncode(&test_char, 1));
+
+ GURL url(original_url);
+ size_t prefix_len;
+ base::string16 formatted = FormatUrl(url,
+ std::string(),
+ kFormatUrlOmitUsernamePassword,
+ UnescapeRule::NORMAL,
+ NULL,
+ &prefix_len,
+ NULL);
+ EXPECT_EQ(url.spec(), GURL(formatted).spec());
+ }
+}
+
+// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
+// results in the original GURL, for each ASCII character in the query.
+TEST(NetUtilTest, FormatUrlRoundTripQueryASCII) {
+ for (unsigned char test_char = 32; test_char < 128; ++test_char) {
+ GURL url(std::string("http://www.google.com/?") +
+ static_cast<char>(test_char));
+ size_t prefix_len;
+ base::string16 formatted = FormatUrl(url,
+ std::string(),
+ kFormatUrlOmitUsernamePassword,
+ UnescapeRule::NORMAL,
+ NULL,
+ &prefix_len,
+ NULL);
+ EXPECT_EQ(url.spec(), GURL(formatted).spec());
+ }
+}
+
+// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
+// only results in a different GURL for certain characters.
+TEST(NetUtilTest, FormatUrlRoundTripQueryEscaped) {
+ // A full list of characters which FormatURL should unescape and GURL should
+ // not escape again, when they appear in a query string.
+ const char* kUnescapedCharacters =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~";
+ for (unsigned char test_char = 0; test_char < 128; ++test_char) {
+ std::string original_url("http://www.google.com/?");
+ original_url.push_back('%');
+ original_url.append(base::HexEncode(&test_char, 1));
+
+ GURL url(original_url);
+ size_t prefix_len;
+ base::string16 formatted = FormatUrl(url,
+ std::string(),
+ kFormatUrlOmitUsernamePassword,
+ UnescapeRule::NORMAL,
+ NULL,
+ &prefix_len,
+ NULL);
+
+ if (test_char &&
+ strchr(kUnescapedCharacters, static_cast<char>(test_char))) {
+ EXPECT_NE(url.spec(), GURL(formatted).spec());
+ } else {
+ EXPECT_EQ(url.spec(), GURL(formatted).spec());
+ }
+ }
+}
+
+TEST(NetUtilTest, FormatUrlWithOffsets) {
+ CheckAdjustedOffsets(std::string(), "en", kFormatUrlOmitNothing,
+ UnescapeRule::NORMAL, NULL);
+
+ const size_t basic_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25
+ };
+ CheckAdjustedOffsets("http://www.google.com/foo/", "en",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ basic_offsets);
+
+ const size_t omit_auth_offsets_1[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
+ };
+ CheckAdjustedOffsets("http://foo:bar@www.google.com/", "en",
+ kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+ omit_auth_offsets_1);
+
+ const size_t omit_auth_offsets_2[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21
+ };
+ CheckAdjustedOffsets("http://foo@www.google.com/", "en",
+ kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+ omit_auth_offsets_2);
+
+ const size_t dont_omit_auth_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, kNpos, 11, 12, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31
+ };
+ // Unescape to "http://foo\x30B0:\x30B0bar@www.google.com".
+ CheckAdjustedOffsets("http://foo%E3%82%B0:%E3%82%B0bar@www.google.com/", "en",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ dont_omit_auth_offsets);
+
+ const size_t view_source_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, kNpos,
+ kNpos, kNpos, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33
+ };
+ CheckAdjustedOffsets("view-source:http://foo@www.google.com/", "en",
+ kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+ view_source_offsets);
+
+ const size_t idn_hostname_offsets_1[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 12,
+ 13, 14, 15, 16, 17, 18, 19
+ };
+ // Convert punycode to "http://\x671d\x65e5\x3042\x3055\x3072.jp/foo/".
+ CheckAdjustedOffsets("http://xn--l8jvb1ey91xtjb.jp/foo/", "ja",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ idn_hostname_offsets_1);
+
+ const size_t idn_hostname_offsets_2[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 14, 15, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, 19, 20, 21, 22, 23, 24
+ };
+ // Convert punycode to
+ // "http://test.\x89c6\x9891.\x5317\x4eac\x5927\x5b78.test/".
+ CheckAdjustedOffsets("http://test.xn--cy2a840a.xn--1lq90ic7f1rc.test/",
+ "zh-CN", kFormatUrlOmitNothing, UnescapeRule::NORMAL,
+ idn_hostname_offsets_2);
+
+ const size_t unescape_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, kNpos, kNpos, 26, 27, 28, 29, 30, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, 31, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, 32, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, 33, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos
+ };
+ // Unescape to "http://www.google.com/foo bar/\x30B0\x30FC\x30B0\x30EB".
+ CheckAdjustedOffsets(
+ "http://www.google.com/foo%20bar/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
+ "en", kFormatUrlOmitNothing, UnescapeRule::SPACES, unescape_offsets);
+
+ const size_t ref_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, kNpos, kNpos, 32, kNpos, kNpos,
+ 33
+ };
+ // Unescape to "http://www.google.com/foo.html#\x30B0\x30B0z".
+ CheckAdjustedOffsets(
+ "http://www.google.com/foo.html#\xE3\x82\xB0\xE3\x82\xB0z", "en",
+ kFormatUrlOmitNothing, UnescapeRule::NORMAL, ref_offsets);
+
+ const size_t omit_http_offsets[] = {
+ 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14
+ };
+ CheckAdjustedOffsets("http://www.google.com/", "en", kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL, omit_http_offsets);
+
+ const size_t omit_http_start_with_ftp_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
+ };
+ CheckAdjustedOffsets("http://ftp.google.com/", "en", kFormatUrlOmitHTTP,
+ UnescapeRule::NORMAL, omit_http_start_with_ftp_offsets);
+
+ const size_t omit_all_offsets[] = {
+ 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, kNpos, kNpos, kNpos, kNpos,
+ 0, 1, 2, 3, 4, 5, 6, 7
+ };
+ CheckAdjustedOffsets("http://user@foo.com/", "en", kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, omit_all_offsets);
+}
+
+} // namespace net
diff --git a/chromium/net/base/net_util_posix.cc b/chromium/net/base/net_util_posix.cc
index 5e1042b4304..315e9ed1a23 100644
--- a/chromium/net/base/net_util_posix.cc
+++ b/chromium/net/base/net_util_posix.cc
@@ -4,6 +4,7 @@
#include "net/base/net_util.h"
+#include <set>
#include <sys/types.h>
#include "base/files/file_path.h"
@@ -17,11 +18,17 @@
#include "net/base/net_errors.h"
#include "url/gurl.h"
-#if !defined(OS_ANDROID)
+#if !defined(OS_ANDROID) && !defined(OS_NACL)
#include <ifaddrs.h>
-#endif
#include <net/if.h>
#include <netinet/in.h>
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <net/if_media.h>
+#include <netinet/in_var.h>
+#include <sys/ioctl.h>
+#endif
#if defined(OS_ANDROID)
#include "net/android/network_library.h"
@@ -29,42 +36,89 @@
namespace net {
-bool FileURLToFilePath(const GURL& url, base::FilePath* path) {
- *path = base::FilePath();
- std::string& file_path_str = const_cast<std::string&>(path->value());
- file_path_str.clear();
+namespace {
- if (!url.is_valid())
- return false;
+#if !defined(OS_ANDROID)
- // Firefox seems to ignore the "host" of a file url if there is one. That is,
- // file://foo/bar.txt maps to /bar.txt.
- // TODO(dhg): This should probably take into account UNCs which could
- // include a hostname other than localhost or blank
- std::string old_path = url.path();
+struct NetworkInterfaceInfo {
+ NetworkInterfaceInfo() : permanent(true) { }
- if (old_path.empty())
- return false;
+ bool permanent; // IPv6 has notion of temporary address. If the address is
+ // IPv6 and it's temporary this field will be false.
+ NetworkInterface interface;
+};
- // GURL stores strings as percent-encoded 8-bit, this will undo if possible.
- old_path = UnescapeURLComponent(old_path,
- UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+// This method will remove permanent IPv6 addresses if a temporary address
+// is available for same network interface.
+void RemovePermanentIPv6AddressesWhereTemporaryExists(
+ std::vector<NetworkInterfaceInfo>* infos) {
+ if (!infos || infos->empty())
+ return;
- // Collapse multiple path slashes into a single path slash.
- std::string new_path;
- do {
- new_path = old_path;
- ReplaceSubstringsAfterOffset(&new_path, 0, "//", "/");
- old_path.swap(new_path);
- } while (new_path != old_path);
+ // Build a set containing the names of interfaces with a temp IPv6 address
+ std::set<std::string> ifaces_with_temp_addrs;
+ std::vector<NetworkInterfaceInfo>::iterator i;
+ for (i = infos->begin(); i != infos->end(); ++i) {
+ if (!i->permanent && i->interface.address.size() == kIPv6AddressSize) {
+ ifaces_with_temp_addrs.insert(i->interface.name);
+ }
+ }
- file_path_str.assign(old_path);
+ // If there are no such interfaces then there's no further work.
+ if (ifaces_with_temp_addrs.empty())
+ return;
- return !file_path_str.empty();
+ // Search for permenent addresses belonging to same network interface.
+ for (i = infos->begin(); i != infos->end(); ) {
+ // If the address is IPv6 and it's permanent and there is temporary
+ // address for it, then we can remove this address.
+ if ((i->interface.address.size() == kIPv6AddressSize) && i->permanent &&
+ (ifaces_with_temp_addrs.find(i->interface.name) !=
+ ifaces_with_temp_addrs.end())) {
+ i = infos->erase(i);
+ } else {
+ ++i;
+ }
+ }
}
-bool GetNetworkList(NetworkInterfaceList* networks) {
-#if defined(OS_ANDROID)
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+
+NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(
+ int addr_family, const std::string& interface_name) {
+ NetworkChangeNotifier::ConnectionType type =
+ NetworkChangeNotifier::CONNECTION_UNKNOWN;
+
+ struct ifmediareq ifmr = {};
+ strncpy(ifmr.ifm_name, interface_name.c_str(), sizeof(ifmr.ifm_name) - 1);
+
+ int s = socket(addr_family, SOCK_DGRAM, 0);
+ if (s == -1) {
+ return type;
+ }
+
+ if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1) {
+ if (ifmr.ifm_current & IFM_IEEE80211) {
+ type = NetworkChangeNotifier::CONNECTION_WIFI;
+ } else if (ifmr.ifm_current & IFM_ETHER) {
+ type = NetworkChangeNotifier::CONNECTION_ETHERNET;
+ }
+ }
+ close(s);
+ return type;
+}
+
+#endif
+
+} // namespace
+
+bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
+#if defined(OS_NACL)
+ NOTIMPLEMENTED();
+ return false;
+#elif defined(OS_ANDROID)
std::string network_list = android::GetNetworkList();
base::StringTokenizer network_interfaces(network_list, "\n");
while (network_interfaces.GetNext()) {
@@ -86,19 +140,30 @@ bool GetNetworkList(NetworkInterfaceList* networks) {
CHECK(base::StringToUint(network_tokenizer.token(), &index));
networks->push_back(
- NetworkInterface(name, index, address, network_prefix));
+ NetworkInterface(name, name, index,
+ NetworkChangeNotifier::CONNECTION_UNKNOWN,
+ address, network_prefix));
}
return true;
#else
// getifaddrs() may require IO operations.
base::ThreadRestrictions::AssertIOAllowed();
+ int ioctl_socket = -1;
+ if (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) {
+ // we need a socket to query information about temporary address.
+ ioctl_socket = socket(AF_INET6, SOCK_DGRAM, 0);
+ DCHECK_GT(ioctl_socket, 0);
+ }
+
ifaddrs *interfaces;
if (getifaddrs(&interfaces) < 0) {
PLOG(ERROR) << "getifaddrs";
return false;
}
+ std::vector<NetworkInterfaceInfo> network_infos;
+
// Enumerate the addresses assigned to network interfaces which are up.
for (ifaddrs *interface = interfaces;
interface != NULL;
@@ -138,25 +203,67 @@ bool GetNetworkList(NetworkInterfaceList* networks) {
continue;
}
+ const std::string& name = interface->ifa_name;
+ // Filter out VMware interfaces, typically named vmnet1 and vmnet8.
+ if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) &&
+ ((name.find("vmnet") != std::string::npos) ||
+ (name.find("vnic") != std::string::npos))) {
+ continue;
+ }
+
+ NetworkInterfaceInfo network_info;
+ NetworkChangeNotifier::ConnectionType connection_type =
+ NetworkChangeNotifier::CONNECTION_UNKNOWN;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // Check if this is a temporary address. Currently this is only supported
+ // on Mac.
+ if ((policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) &&
+ ioctl_socket >= 0 && addr->sa_family == AF_INET6) {
+ struct in6_ifreq ifr = {};
+ strncpy(ifr.ifr_name, interface->ifa_name, sizeof(ifr.ifr_name) - 1);
+ memcpy(&ifr.ifr_ifru.ifru_addr, interface->ifa_addr,
+ interface->ifa_addr->sa_len);
+ int rv = ioctl(ioctl_socket, SIOCGIFAFLAG_IN6, &ifr);
+ if (rv >= 0) {
+ network_info.permanent = !(ifr.ifr_ifru.ifru_flags & IN6_IFF_TEMPORARY);
+ }
+ }
+
+ connection_type = GetNetworkInterfaceType(addr->sa_family, name);
+#endif
+
IPEndPoint address;
- std::string name = interface->ifa_name;
if (address.FromSockAddr(addr, addr_size)) {
uint8 net_mask = 0;
if (interface->ifa_netmask) {
+ // If not otherwise set, assume the same sa_family as ifa_addr.
+ if (interface->ifa_netmask->sa_family == 0) {
+ interface->ifa_netmask->sa_family = addr->sa_family;
+ }
IPEndPoint netmask;
if (netmask.FromSockAddr(interface->ifa_netmask, addr_size)) {
net_mask = MaskPrefixLength(netmask.address());
}
}
+ network_info.interface = NetworkInterface(
+ name, name, if_nametoindex(name.c_str()),
+ connection_type, address.address(), net_mask);
- networks->push_back(
- NetworkInterface(name, if_nametoindex(name.c_str()),
- address.address(), net_mask));
+ network_infos.push_back(NetworkInterfaceInfo(network_info));
}
}
-
freeifaddrs(interfaces);
+ if (ioctl_socket >= 0) {
+ close(ioctl_socket);
+ }
+ if (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) {
+ RemovePermanentIPv6AddressesWhereTemporaryExists(&network_infos);
+ }
+
+ for (size_t i = 0; i < network_infos.size(); ++i) {
+ networks->push_back(network_infos[i].interface);
+ }
return true;
#endif
}
diff --git a/chromium/net/base/net_util_unittest.cc b/chromium/net/base/net_util_unittest.cc
index 2259629209f..674a7259121 100644
--- a/chromium/net/base/net_util_unittest.cc
+++ b/chromium/net/base/net_util_unittest.cc
@@ -6,7 +6,7 @@
#include <string.h>
-#include <algorithm>
+#include <ostream>
#include "base/files/file_path.h"
#include "base/format_macros.h"
@@ -14,10 +14,8 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
-#include "base/test/test_file_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -30,390 +28,24 @@
#include <net/if.h>
#endif // OS_WIN
+using base::ASCIIToUTF16;
+using base::WideToUTF16;
+
namespace net {
namespace {
-static const size_t kNpos = base::string16::npos;
-
-struct FileCase {
- const wchar_t* file;
- const char* url;
-};
-
struct HeaderCase {
const char* header_name;
const char* expected;
};
-struct HeaderParamCase {
- const char* header_name;
- const char* param_name;
- const char* expected;
-};
-
-struct FileNameCDCase {
- const char* header_field;
- const char* referrer_charset;
- const wchar_t* expected;
-};
-
-const char* kLanguages[] = {
- "", "en", "zh-CN", "ja", "ko",
- "he", "ar", "ru", "el", "fr",
- "de", "pt", "sv", "th", "hi",
- "de,en", "el,en", "zh-TW,en", "ko,ja", "he,ru,en",
- "zh,ru,en"
-};
-
-struct IDNTestCase {
- const char* input;
- const wchar_t* unicode_output;
- const bool unicode_allowed[arraysize(kLanguages)];
-};
-
-// TODO(jungshik) This is just a random sample of languages and is far
-// from exhaustive. We may have to generate all the combinations
-// of languages (powerset of a set of all the languages).
-const IDNTestCase idn_cases[] = {
- // No IDN
- {"www.google.com", L"www.google.com",
- {true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true}},
- {"www.google.com.", L"www.google.com.",
- {true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true}},
- {".", L".",
- {true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true}},
- {"", L"",
- {true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true}},
- // IDN
- // Hanzi (Traditional Chinese)
- {"xn--1lq90ic7f1rc.cn", L"\x5317\x4eac\x5927\x5b78.cn",
- {true, false, true, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, true, true, false,
- true}},
- // Hanzi ('video' in Simplified Chinese : will pass only in zh-CN,zh)
- {"xn--cy2a840a.com", L"\x89c6\x9891.com",
- {true, false, true, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- true}},
- // Hanzi + '123'
- {"www.xn--123-p18d.com", L"www.\x4e00" L"123.com",
- {true, false, true, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, true, true, false,
- true}},
- // Hanzi + Latin : U+56FD is simplified and is regarded
- // as not supported in zh-TW.
- {"www.xn--hello-9n1hm04c.com", L"www.hello\x4e2d\x56fd.com",
- {false, false, true, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- true}},
- // Kanji + Kana (Japanese)
- {"xn--l8jvb1ey91xtjb.jp", L"\x671d\x65e5\x3042\x3055\x3072.jp",
- {true, false, false, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- false}},
- // Katakana including U+30FC
- {"xn--tckm4i2e.jp", L"\x30b3\x30de\x30fc\x30b9.jp",
- {true, false, false, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- }},
- {"xn--3ck7a7g.jp", L"\u30ce\u30f3\u30bd.jp",
- {true, false, false, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- }},
- // Katakana + Latin (Japanese)
- // TODO(jungshik): Change 'false' in the first element to 'true'
- // after upgrading to ICU 4.2.1 to use new uspoof_* APIs instead
- // of our IsIDNComponentInSingleScript().
- {"xn--e-efusa1mzf.jp", L"e\x30b3\x30de\x30fc\x30b9.jp",
- {false, false, false, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- }},
- {"xn--3bkxe.jp", L"\x30c8\x309a.jp",
- {false, false, false, true, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- }},
- // Hangul (Korean)
- {"www.xn--or3b17p6jjc.kr", L"www.\xc804\xc790\xc815\xbd80.kr",
- {true, false, false, false, true,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- false}},
- // b<u-umlaut>cher (German)
- {"xn--bcher-kva.de", L"b\x00fc" L"cher.de",
- {true, false, false, false, false,
- false, false, false, false, true,
- true, false, false, false, false,
- true, false, false, false, false,
- false}},
- // a with diaeresis
- {"www.xn--frgbolaget-q5a.se", L"www.f\x00e4rgbolaget.se",
- {true, false, false, false, false,
- false, false, false, false, false,
- true, false, true, false, false,
- true, false, false, false, false,
- false}},
- // c-cedilla (French)
- {"www.xn--alliancefranaise-npb.fr", L"www.alliancefran\x00e7" L"aise.fr",
- {true, false, false, false, false,
- false, false, false, false, true,
- false, true, false, false, false,
- false, false, false, false, false,
- false}},
- // caf'e with acute accent' (French)
- {"xn--caf-dma.fr", L"caf\x00e9.fr",
- {true, false, false, false, false,
- false, false, false, false, true,
- false, true, true, false, false,
- false, false, false, false, false,
- false}},
- // c-cedillla and a with tilde (Portuguese)
- {"xn--poema-9qae5a.com.br", L"p\x00e3oema\x00e7\x00e3.com.br",
- {true, false, false, false, false,
- false, false, false, false, false,
- false, true, false, false, false,
- false, false, false, false, false,
- false}},
- // s with caron
- {"xn--achy-f6a.com", L"\x0161" L"achy.com",
- {true, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // TODO(jungshik) : Add examples with Cyrillic letters
- // only used in some languages written in Cyrillic.
- // Eutopia (Greek)
- {"xn--kxae4bafwg.gr", L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1.gr",
- {true, false, false, false, false,
- false, false, false, true, false,
- false, false, false, false, false,
- false, true, false, false, false,
- false}},
- // Eutopia + 123 (Greek)
- {"xn---123-pldm0haj2bk.gr",
- L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1-123.gr",
- {true, false, false, false, false,
- false, false, false, true, false,
- false, false, false, false, false,
- false, true, false, false, false,
- false}},
- // Cyrillic (Russian)
- {"xn--n1aeec9b.ru", L"\x0442\x043e\x0440\x0442\x044b.ru",
- {true, false, false, false, false,
- false, false, true, false, false,
- false, false, false, false, false,
- false, false, false, false, true,
- true}},
- // Cyrillic + 123 (Russian)
- {"xn---123-45dmmc5f.ru", L"\x0442\x043e\x0440\x0442\x044b-123.ru",
- {true, false, false, false, false,
- false, false, true, false, false,
- false, false, false, false, false,
- false, false, false, false, true,
- true}},
- // Arabic
- {"xn--mgba1fmg.ar", L"\x0627\x0641\x0644\x0627\x0645.ar",
- {true, false, false, false, false,
- false, true, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // Hebrew
- {"xn--4dbib.he", L"\x05d5\x05d0\x05d4.he",
- {true, false, false, false, false,
- true, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, true,
- false}},
- // Thai
- {"xn--12c2cc4ag3b4ccu.th",
- L"\x0e2a\x0e32\x0e22\x0e01\x0e32\x0e23\x0e1a\x0e34\x0e19.th",
- {true, false, false, false, false,
- false, false, false, false, false,
- false, false, false, true, false,
- false, false, false, false, false,
- false}},
- // Devangari (Hindi)
- {"www.xn--l1b6a9e1b7c.in", L"www.\x0905\x0915\x094b\x0932\x093e.in",
- {true, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, true,
- false, false, false, false, false,
- false}},
- // Invalid IDN
- {"xn--hello?world.com", NULL,
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // Unsafe IDNs
- // "payp<alpha>l.com"
- {"www.xn--paypl-g9d.com", L"payp\x03b1l.com",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // google.gr with Greek omicron and epsilon
- {"xn--ggl-6xc1ca.gr", L"g\x03bf\x03bfgl\x03b5.gr",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // google.ru with Cyrillic o
- {"xn--ggl-tdd6ba.ru", L"g\x043e\x043egl\x0435.ru",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // h<e with acute>llo<China in Han>.cn
- {"xn--hllo-bpa7979ih5m.cn", L"h\x00e9llo\x4e2d\x56fd.cn",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // <Greek rho><Cyrillic a><Cyrillic u>.ru
- {"xn--2xa6t2b.ru", L"\x03c1\x0430\x0443.ru",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- // One that's really long that will force a buffer realloc
- {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- "aaaaaaa",
- L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- L"aaaaaaaa",
- {true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true, true, true, true, true,
- true}},
- // Test cases for characters we blacklisted although allowed in IDN.
- // Embedded spaces will be turned to %20 in the display.
- // TODO(jungshik): We need to have more cases. This is a typical
- // data-driven trap. The following test cases need to be separated
- // and tested only for a couple of languages.
- {"xn--osd3820f24c.kr", L"\xac00\xb098\x115f.kr",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false}},
- {"www.xn--google-ho0coa.com", L"www.\x2039google\x203a.com",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- }},
- {"google.xn--comabc-k8d", L"google.com\x0338" L"abc",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- }},
- {"google.xn--com-oh4ba.evil.jp", L"google.com\x309a\x309a.evil.jp",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- }},
- {"google.xn--comevil-v04f.jp", L"google.com\x30ce" L"evil.jp",
- {false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- }},
-#if 0
- // These two cases are special. We need a separate test.
- // U+3000 and U+3002 are normalized to ASCII space and dot.
- {"xn-- -kq6ay5z.cn", L"\x4e2d\x56fd\x3000.cn",
- {false, false, true, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, true, false, false,
- true}},
- {"xn--fiqs8s.cn", L"\x4e2d\x56fd\x3002" L"cn",
- {false, false, true, false, false,
- false, false, false, false, false,
- false, false, false, false, false,
- false, false, true, false, false,
- true}},
-#endif
-};
-
-struct AdjustOffsetCase {
- size_t input_offset;
- size_t output_offset;
-};
-
struct CompliantHostCase {
const char* host;
const char* desired_tld;
bool expected_output;
};
-struct GenerateFilenameCase {
- int lineno;
- const char* url;
- const char* content_disp_header;
- const char* referrer_charset;
- const char* suggested_filename;
- const char* mime_type;
- const wchar_t* default_filename;
- const wchar_t* expected_filename;
-};
-
-struct UrlTestData {
- const char* description;
- const char* input;
- const char* languages;
- FormatUrlTypes format_types;
- UnescapeRule::Type escape_rules;
- const wchar_t* output; // Use |wchar_t| to handle Unicode constants easily.
- size_t prefix_len;
-};
-
// Fills in sockaddr for the given 32-bit address (IPv4.)
// |bytes| should be an array of length 4.
void MakeIPv4Address(const uint8* bytes, int port, SockaddrStorage* storage) {
@@ -436,51 +68,6 @@ void MakeIPv6Address(const uint8* bytes, int port, SockaddrStorage* storage) {
memcpy(&addr6->sin6_addr, bytes, 16);
}
-// A helper for IDN*{Fast,Slow}.
-// Append "::<language list>" to |expected| and |actual| to make it
-// easy to tell which sub-case fails without debugging.
-void AppendLanguagesToOutputs(const char* languages,
- base::string16* expected,
- base::string16* actual) {
- base::string16 to_append = ASCIIToUTF16("::") + ASCIIToUTF16(languages);
- expected->append(to_append);
- actual->append(to_append);
-}
-
-// A pair of helpers for the FormatUrlWithOffsets() test.
-void VerboseExpect(size_t expected,
- size_t actual,
- const std::string& original_url,
- size_t position,
- const base::string16& formatted_url) {
- EXPECT_EQ(expected, actual) << "Original URL: " << original_url
- << " (at char " << position << ")\nFormatted URL: " << formatted_url;
-}
-
-void CheckAdjustedOffsets(const std::string& url_string,
- const std::string& languages,
- FormatUrlTypes format_types,
- UnescapeRule::Type unescape_rules,
- const size_t* output_offsets) {
- GURL url(url_string);
- size_t url_length = url_string.length();
- std::vector<size_t> offsets;
- for (size_t i = 0; i <= url_length + 1; ++i)
- offsets.push_back(i);
- offsets.push_back(500000); // Something larger than any input length.
- offsets.push_back(std::string::npos);
- base::string16 formatted_url = FormatUrlWithOffsets(url, languages,
- format_types, unescape_rules, NULL, NULL, &offsets);
- for (size_t i = 0; i < url_length; ++i)
- VerboseExpect(output_offsets[i], offsets[i], url_string, i, formatted_url);
- VerboseExpect(formatted_url.length(), offsets[url_length], url_string,
- url_length, formatted_url);
- VerboseExpect(base::string16::npos, offsets[url_length + 1], url_string,
- 500000, formatted_url);
- VerboseExpect(base::string16::npos, offsets[url_length + 2], url_string,
- std::string::npos, formatted_url);
-}
-
// Helper to strignize an IP number (used to define expectations).
std::string DumpIPNumber(const IPAddressNumber& v) {
std::string out;
@@ -492,125 +79,8 @@ std::string DumpIPNumber(const IPAddressNumber& v) {
return out;
}
-void RunGenerateFileNameTestCase(const GenerateFilenameCase* test_case) {
- std::string default_filename(WideToUTF8(test_case->default_filename));
- base::FilePath file_path = GenerateFileName(
- GURL(test_case->url), test_case->content_disp_header,
- test_case->referrer_charset, test_case->suggested_filename,
- test_case->mime_type, default_filename);
- EXPECT_EQ(test_case->expected_filename,
- file_util::FilePathAsWString(file_path))
- << "test case at line number: " << test_case->lineno;
-}
-
} // anonymous namespace
-TEST(NetUtilTest, FileURLConversion) {
- // a list of test file names and the corresponding URLs
- const FileCase round_trip_cases[] = {
-#if defined(OS_WIN)
- {L"C:\\foo\\bar.txt", "file:///C:/foo/bar.txt"},
- {L"\\\\some computer\\foo\\bar.txt",
- "file://some%20computer/foo/bar.txt"}, // UNC
- {L"D:\\Name;with%some symbols*#",
- "file:///D:/Name%3Bwith%25some%20symbols*%23"},
- // issue 14153: To be tested with the OS default codepage other than 1252.
- {L"D:\\latin1\\caf\x00E9\x00DD.txt",
- "file:///D:/latin1/caf%C3%A9%C3%9D.txt"},
- {L"D:\\otherlatin\\caf\x0119.txt",
- "file:///D:/otherlatin/caf%C4%99.txt"},
- {L"D:\\greek\\\x03B1\x03B2\x03B3.txt",
- "file:///D:/greek/%CE%B1%CE%B2%CE%B3.txt"},
- {L"D:\\Chinese\\\x6240\x6709\x4e2d\x6587\x7f51\x9875.doc",
- "file:///D:/Chinese/%E6%89%80%E6%9C%89%E4%B8%AD%E6%96%87%E7%BD%91"
- "%E9%A1%B5.doc"},
- {L"D:\\plane1\\\xD835\xDC00\xD835\xDC01.txt", // Math alphabet "AB"
- "file:///D:/plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
-#elif defined(OS_POSIX)
- {L"/foo/bar.txt", "file:///foo/bar.txt"},
- {L"/foo/BAR.txt", "file:///foo/BAR.txt"},
- {L"/C:/foo/bar.txt", "file:///C:/foo/bar.txt"},
- {L"/foo/bar?.txt", "file:///foo/bar%3F.txt"},
- {L"/some computer/foo/bar.txt", "file:///some%20computer/foo/bar.txt"},
- {L"/Name;with%some symbols*#", "file:///Name%3Bwith%25some%20symbols*%23"},
- {L"/latin1/caf\x00E9\x00DD.txt", "file:///latin1/caf%C3%A9%C3%9D.txt"},
- {L"/otherlatin/caf\x0119.txt", "file:///otherlatin/caf%C4%99.txt"},
- {L"/greek/\x03B1\x03B2\x03B3.txt", "file:///greek/%CE%B1%CE%B2%CE%B3.txt"},
- {L"/Chinese/\x6240\x6709\x4e2d\x6587\x7f51\x9875.doc",
- "file:///Chinese/%E6%89%80%E6%9C%89%E4%B8%AD%E6%96%87%E7%BD"
- "%91%E9%A1%B5.doc"},
- {L"/plane1/\x1D400\x1D401.txt", // Math alphabet "AB"
- "file:///plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
-#endif
- };
-
- // First, we'll test that we can round-trip all of the above cases of URLs
- base::FilePath output;
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(round_trip_cases); i++) {
- // convert to the file URL
- GURL file_url(FilePathToFileURL(
- file_util::WStringAsFilePath(round_trip_cases[i].file)));
- EXPECT_EQ(round_trip_cases[i].url, file_url.spec());
-
- // Back to the filename.
- EXPECT_TRUE(FileURLToFilePath(file_url, &output));
- EXPECT_EQ(round_trip_cases[i].file, file_util::FilePathAsWString(output));
- }
-
- // Test that various file: URLs get decoded into the correct file type
- FileCase url_cases[] = {
-#if defined(OS_WIN)
- {L"C:\\foo\\bar.txt", "file:c|/foo\\bar.txt"},
- {L"C:\\foo\\bar.txt", "file:/c:/foo/bar.txt"},
- {L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
- {L"C:\\foo\\bar.txt", "file:///c:/foo/bar.txt"},
- {L"\\\\foo\\bar.txt", "file:////foo\\bar.txt"},
- {L"\\\\foo\\bar.txt", "file:/foo/bar.txt"},
- {L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
- {L"C:\\foo\\bar.txt", "file:\\\\\\c:/foo/bar.txt"},
-#elif defined(OS_POSIX)
- {L"/c:/foo/bar.txt", "file:/c:/foo/bar.txt"},
- {L"/c:/foo/bar.txt", "file:///c:/foo/bar.txt"},
- {L"/foo/bar.txt", "file:/foo/bar.txt"},
- {L"/c:/foo/bar.txt", "file:\\\\\\c:/foo/bar.txt"},
- {L"/foo/bar.txt", "file:foo/bar.txt"},
- {L"/bar.txt", "file://foo/bar.txt"},
- {L"/foo/bar.txt", "file:///foo/bar.txt"},
- {L"/foo/bar.txt", "file:////foo/bar.txt"},
- {L"/foo/bar.txt", "file:////foo//bar.txt"},
- {L"/foo/bar.txt", "file:////foo///bar.txt"},
- {L"/foo/bar.txt", "file:////foo////bar.txt"},
- {L"/c:/foo/bar.txt", "file:\\\\\\c:/foo/bar.txt"},
- {L"/c:/foo/bar.txt", "file:c:/foo/bar.txt"},
- // We get these wrong because GURL turns back slashes into forward
- // slashes.
- //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
- //{L"/c|/foo%5Cbar.txt", "file:c|/foo\\bar.txt"},
- //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
- //{L"/foo%5Cbar.txt", "file:////foo\\bar.txt"},
- //{L"/foo%5Cbar.txt", "file://foo\\bar.txt"},
-#endif
- };
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(url_cases); i++) {
- FileURLToFilePath(GURL(url_cases[i].url), &output);
- EXPECT_EQ(url_cases[i].file, file_util::FilePathAsWString(output));
- }
-
- // Unfortunately, UTF8ToWide discards invalid UTF8 input.
-#ifdef BUG_878908_IS_FIXED
- // Test that no conversion happens if the UTF-8 input is invalid, and that
- // the input is preserved in UTF-8
- const char invalid_utf8[] = "file:///d:/Blah/\xff.doc";
- const wchar_t invalid_wide[] = L"D:\\Blah\\\xff.doc";
- EXPECT_TRUE(FileURLToFilePath(
- GURL(std::string(invalid_utf8)), &output));
- EXPECT_EQ(std::wstring(invalid_wide), output);
-#endif
-
- // Test that if a file URL is malformed, we get a failure
- EXPECT_FALSE(FileURLToFilePath(GURL("filefoobar"), &output));
-}
-
TEST(NetUtilTest, GetIdentityFromURL) {
struct {
const char* input_url;
@@ -728,38 +198,6 @@ TEST(NetUtilTest, GetSpecificHeader) {
}
}
-TEST(NetUtilTest, IDNToUnicodeFast) {
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(idn_cases); i++) {
- for (size_t j = 0; j < arraysize(kLanguages); j++) {
- // ja || zh-TW,en || ko,ja -> IDNToUnicodeSlow
- if (j == 3 || j == 17 || j == 18)
- continue;
- base::string16 output(IDNToUnicode(idn_cases[i].input, kLanguages[j]));
- base::string16 expected(idn_cases[i].unicode_allowed[j] ?
- WideToUTF16(idn_cases[i].unicode_output) :
- ASCIIToUTF16(idn_cases[i].input));
- AppendLanguagesToOutputs(kLanguages[j], &expected, &output);
- EXPECT_EQ(expected, output);
- }
- }
-}
-
-TEST(NetUtilTest, IDNToUnicodeSlow) {
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(idn_cases); i++) {
- for (size_t j = 0; j < arraysize(kLanguages); j++) {
- // !(ja || zh-TW,en || ko,ja) -> IDNToUnicodeFast
- if (!(j == 3 || j == 17 || j == 18))
- continue;
- base::string16 output(IDNToUnicode(idn_cases[i].input, kLanguages[j]));
- base::string16 expected(idn_cases[i].unicode_allowed[j] ?
- WideToUTF16(idn_cases[i].unicode_output) :
- ASCIIToUTF16(idn_cases[i].input));
- AppendLanguagesToOutputs(kLanguages[j], &expected, &output);
- EXPECT_EQ(expected, output);
- }
- }
-}
-
TEST(NetUtilTest, CompliantHost) {
const CompliantHostCase compliant_host_cases[] = {
{"", "", false},
@@ -794,1498 +232,6 @@ TEST(NetUtilTest, CompliantHost) {
}
}
-TEST(NetUtilTest, StripWWW) {
- EXPECT_EQ(base::string16(), StripWWW(base::string16()));
- EXPECT_EQ(base::string16(), StripWWW(ASCIIToUTF16("www.")));
- EXPECT_EQ(ASCIIToUTF16("blah"), StripWWW(ASCIIToUTF16("www.blah")));
- EXPECT_EQ(ASCIIToUTF16("blah"), StripWWW(ASCIIToUTF16("blah")));
-}
-
-#if defined(OS_WIN)
-#define JPEG_EXT L".jpg"
-#define HTML_EXT L".htm"
-#elif defined(OS_MACOSX)
-#define JPEG_EXT L".jpeg"
-#define HTML_EXT L".html"
-#else
-#define JPEG_EXT L".jpg"
-#define HTML_EXT L".html"
-#endif
-#define TXT_EXT L".txt"
-#define TAR_EXT L".tar"
-
-TEST(NetUtilTest, GenerateSafeFileName) {
- const struct {
- const char* mime_type;
- const base::FilePath::CharType* filename;
- const base::FilePath::CharType* expected_filename;
- } safe_tests[] = {
-#if defined(OS_WIN)
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\bar.htm"),
- FILE_PATH_LITERAL("C:\\foo\\bar.htm")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\bar.html"),
- FILE_PATH_LITERAL("C:\\foo\\bar.html")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\bar"),
- FILE_PATH_LITERAL("C:\\foo\\bar.htm")
- },
- {
- "image/png",
- FILE_PATH_LITERAL("C:\\bar.html"),
- FILE_PATH_LITERAL("C:\\bar.html")
- },
- {
- "image/png",
- FILE_PATH_LITERAL("C:\\bar"),
- FILE_PATH_LITERAL("C:\\bar.png")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
- FILE_PATH_LITERAL("C:\\foo\\bar.exe")
- },
- {
- "image/gif",
- FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
- FILE_PATH_LITERAL("C:\\foo\\bar.exe")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\google.com"),
- FILE_PATH_LITERAL("C:\\foo\\google.com")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\con.htm"),
- FILE_PATH_LITERAL("C:\\foo\\_con.htm")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\con"),
- FILE_PATH_LITERAL("C:\\foo\\_con.htm")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\harmless.{not-really-this-may-be-a-guid}"),
- FILE_PATH_LITERAL("C:\\foo\\harmless.download")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\harmless.local"),
- FILE_PATH_LITERAL("C:\\foo\\harmless.download")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\harmless.lnk"),
- FILE_PATH_LITERAL("C:\\foo\\harmless.download")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-"),
- FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-")
- },
- // Allow extension synonyms.
- {
- "image/jpeg",
- FILE_PATH_LITERAL("C:\\foo\\bar.jpg"),
- FILE_PATH_LITERAL("C:\\foo\\bar.jpg")
- },
- {
- "image/jpeg",
- FILE_PATH_LITERAL("C:\\foo\\bar.jpeg"),
- FILE_PATH_LITERAL("C:\\foo\\bar.jpeg")
- },
-#else // !defined(OS_WIN)
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/bar.htm"),
- FILE_PATH_LITERAL("/foo/bar.htm")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/bar.html"),
- FILE_PATH_LITERAL("/foo/bar.html")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/bar"),
- FILE_PATH_LITERAL("/foo/bar.html")
- },
- {
- "image/png",
- FILE_PATH_LITERAL("/bar.html"),
- FILE_PATH_LITERAL("/bar.html")
- },
- {
- "image/png",
- FILE_PATH_LITERAL("/bar"),
- FILE_PATH_LITERAL("/bar.png")
- },
- {
- "image/gif",
- FILE_PATH_LITERAL("/foo/bar.exe"),
- FILE_PATH_LITERAL("/foo/bar.exe")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/google.com"),
- FILE_PATH_LITERAL("/foo/google.com")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/con.htm"),
- FILE_PATH_LITERAL("/foo/con.htm")
- },
- {
- "text/html",
- FILE_PATH_LITERAL("/foo/con"),
- FILE_PATH_LITERAL("/foo/con.html")
- },
- // Allow extension synonyms.
- {
- "image/jpeg",
- FILE_PATH_LITERAL("/bar.jpg"),
- FILE_PATH_LITERAL("/bar.jpg")
- },
- {
- "image/jpeg",
- FILE_PATH_LITERAL("/bar.jpeg"),
- FILE_PATH_LITERAL("/bar.jpeg")
- },
-#endif // !defined(OS_WIN)
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(safe_tests); ++i) {
- base::FilePath file_path(safe_tests[i].filename);
- GenerateSafeFileName(safe_tests[i].mime_type, false, &file_path);
- EXPECT_EQ(safe_tests[i].expected_filename, file_path.value())
- << "Iteration " << i;
- }
-}
-
-TEST(NetUtilTest, GenerateFileName) {
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- // This test doesn't run when the locale is not UTF-8 because some of the
- // string conversions fail. This is OK (we have the default value) but they
- // don't match our expectations.
- std::string locale = setlocale(LC_CTYPE, NULL);
- StringToLowerASCII(&locale);
- EXPECT_TRUE(locale.find("utf-8") != std::string::npos ||
- locale.find("utf8") != std::string::npos)
- << "Your locale (" << locale << ") must be set to UTF-8 "
- << "for this test to pass!";
-#endif
-
- // Tests whether the correct filename is selected from the the given
- // parameters and that Content-Disposition headers are properly
- // handled including failovers when the header is malformed.
- const GenerateFilenameCase selection_tests[] = {
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=test.html",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"test.html\"",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename= \"test.html\"",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename = \"test.html\"",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- { // filename is whitespace. Should failover to URL host
- __LINE__,
- "http://www.google.com/",
- "attachment; filename= ",
- "",
- "",
- "",
- L"",
- L"www.google.com"
- },
- { // No filename.
- __LINE__,
- "http://www.google.com/path/test.html",
- "attachment",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- { // Ditto
- __LINE__,
- "http://www.google.com/path/test.html",
- "attachment;",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- { // No C-D
- __LINE__,
- "http://www.google.com/",
- "",
- "",
- "",
- "",
- L"",
- L"www.google.com"
- },
- {
- __LINE__,
- "http://www.google.com/test.html",
- "",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- { // Now that we use src/url's ExtractFileName, this case falls back to
- // the hostname. If this behavior is not desirable, we'd better change
- // ExtractFileName (in url_parse).
- __LINE__,
- "http://www.google.com/path/",
- "",
- "",
- "",
- "",
- L"",
- L"www.google.com"
- },
- {
- __LINE__,
- "http://www.google.com/path",
- "",
- "",
- "",
- "",
- L"",
- L"path"
- },
- {
- __LINE__,
- "file:///",
- "",
- "",
- "",
- "",
- L"",
- L"download"
- },
- {
- __LINE__,
- "file:///path/testfile",
- "",
- "",
- "",
- "",
- L"",
- L"testfile"
- },
- {
- __LINE__,
- "non-standard-scheme:",
- "",
- "",
- "",
- "",
- L"",
- L"download"
- },
- { // C-D should override default
- __LINE__,
- "http://www.google.com/",
- "attachment; filename =\"test.html\"",
- "",
- "",
- "",
- L"download",
- L"test.html"
- },
- { // But the URL shouldn't
- __LINE__,
- "http://www.google.com/",
- "",
- "",
- "",
- "",
- L"download",
- L"download"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"../test.html\"",
- "",
- "",
- "",
- L"",
- L"-test.html"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"..\\test.html\"",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"..\\\\test.html\"",
- "",
- "",
- "",
- L"",
- L"-test.html"
- },
- { // Filename disappears after leading and trailing periods are removed.
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"..\"",
- "",
- "",
- "",
- L"default",
- L"default"
- },
- { // C-D specified filename disappears. Failover to final filename.
- __LINE__,
- "http://www.google.com/test.html",
- "attachment; filename=\"..\"",
- "",
- "",
- "",
- L"default",
- L"default"
- },
- // Below is a small subset of cases taken from HttpContentDisposition tests.
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"%EC%98%88%EC%88%A0%20"
- "%EC%98%88%EC%88%A0.jpg\"",
- "",
- "",
- "",
- L"",
- L"\uc608\uc220 \uc608\uc220.jpg"
- },
- {
- __LINE__,
- "http://www.google.com/%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg",
- "",
- "",
- "",
- "",
- L"download",
- L"\uc608\uc220 \uc608\uc220.jpg"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment;",
- "",
- "",
- "",
- L"\uB2E4\uC6B4\uB85C\uB4DC",
- L"\uB2E4\uC6B4\uB85C\uB4DC"
- },
- {
- __LINE__,
- "http://www.google.com/",
- "attachment; filename=\"=?EUC-JP?Q?=B7=DD=BD="
- "D13=2Epng?=\"",
- "",
- "",
- "",
- L"download",
- L"\u82b8\u88533.png"
- },
- {
- __LINE__,
- "http://www.example.com/images?id=3",
- "attachment; filename=caf\xc3\xa9.png",
- "iso-8859-1",
- "",
- "",
- L"",
- L"caf\u00e9.png"
- },
- {
- __LINE__,
- "http://www.example.com/images?id=3",
- "attachment; filename=caf\xe5.png",
- "windows-1253",
- "",
- "",
- L"",
- L"caf\u03b5.png"
- },
- {
- __LINE__,
- "http://www.example.com/file?id=3",
- "attachment; name=\xcf\xc2\xd4\xd8.zip",
- "GBK",
- "",
- "",
- L"",
- L"\u4e0b\u8f7d.zip"
- },
- { // Invalid C-D header. Extracts filename from url.
- __LINE__,
- "http://www.google.com/test.html",
- "attachment; filename==?iiso88591?Q?caf=EG?=",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- // about: and data: URLs
- {
- __LINE__,
- "about:chrome",
- "",
- "",
- "",
- "",
- L"",
- L"download"
- },
- {
- __LINE__,
- "data:,looks/like/a.path",
- "",
- "",
- "",
- "",
- L"",
- L"download"
- },
- {
- __LINE__,
- "data:text/plain;base64,VG8gYmUgb3Igbm90IHRvIGJlLg=",
- "",
- "",
- "",
- "",
- L"",
- L"download"
- },
- {
- __LINE__,
- "data:,looks/like/a.path",
- "",
- "",
- "",
- "",
- L"default_filename_is_given",
- L"default_filename_is_given"
- },
- {
- __LINE__,
- "data:,looks/like/a.path",
- "",
- "",
- "",
- "",
- L"\u65e5\u672c\u8a9e", // Japanese Kanji.
- L"\u65e5\u672c\u8a9e"
- },
- { // The filename encoding is specified by the referrer charset.
- __LINE__,
- "http://example.com/V%FDvojov%E1%20psychologie.doc",
- "",
- "iso-8859-1",
- "",
- "",
- L"",
- L"V\u00fdvojov\u00e1 psychologie.doc"
- },
- { // Suggested filename takes precedence over URL
- __LINE__,
- "http://www.google.com/test",
- "",
- "",
- "suggested",
- "",
- L"",
- L"suggested"
- },
- { // The content-disposition has higher precedence over the suggested name.
- __LINE__,
- "http://www.google.com/test",
- "attachment; filename=test.html",
- "",
- "suggested",
- "",
- L"",
- L"test.html"
- },
-#if 0
- { // The filename encoding doesn't match the referrer charset, the system
- // charset, or UTF-8.
- // TODO(jshin): we need to handle this case.
- __LINE__,
- "http://example.com/V%FDvojov%E1%20psychologie.doc",
- "",
- "utf-8",
- "",
- "",
- L"",
- L"V\u00fdvojov\u00e1 psychologie.doc",
- },
-#endif
- // Raw 8bit characters in C-D
- {
- __LINE__,
- "http://www.example.com/images?id=3",
- "attachment; filename=caf\xc3\xa9.png",
- "iso-8859-1",
- "",
- "image/png",
- L"",
- L"caf\u00e9.png"
- },
- {
- __LINE__,
- "http://www.example.com/images?id=3",
- "attachment; filename=caf\xe5.png",
- "windows-1253",
- "",
- "image/png",
- L"",
- L"caf\u03b5.png"
- },
- { // No 'filename' keyword in the disposition, use the URL
- __LINE__,
- "http://www.evil.com/my_download.txt",
- "a_file_name.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"my_download.txt"
- },
- { // Spaces in the disposition file name
- __LINE__,
- "http://www.frontpagehacker.com/a_download.exe",
- "filename=My Downloaded File.exe",
- "",
- "",
- "application/octet-stream",
- L"download",
- L"My Downloaded File.exe"
- },
- { // % encoded
- __LINE__,
- "http://www.examples.com/",
- "attachment; "
- "filename=\"%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg\"",
- "",
- "",
- "image/jpeg",
- L"download",
- L"\uc608\uc220 \uc608\uc220.jpg"
- },
- { // name= parameter
- __LINE__,
- "http://www.examples.com/q.cgi?id=abc",
- "attachment; name=abc de.pdf",
- "",
- "",
- "application/octet-stream",
- L"download",
- L"abc de.pdf"
- },
- {
- __LINE__,
- "http://www.example.com/path",
- "filename=\"=?EUC-JP?Q?=B7=DD=BD=D13=2Epng?=\"",
- "",
- "",
- "image/png",
- L"download",
- L"\x82b8\x8853" L"3.png"
- },
- { // The following two have invalid CD headers and filenames come from the
- // URL.
- __LINE__,
- "http://www.example.com/test%20123",
- "attachment; filename==?iiso88591?Q?caf=EG?=",
- "",
- "",
- "image/jpeg",
- L"download",
- L"test 123" JPEG_EXT
- },
- {
- __LINE__,
- "http://www.google.com/%EC%98%88%EC%88%A0%20%EC%98%88%EC%88%A0.jpg",
- "malformed_disposition",
- "",
- "",
- "image/jpeg",
- L"download",
- L"\uc608\uc220 \uc608\uc220.jpg"
- },
- { // Invalid C-D. No filename from URL. Falls back to 'download'.
- __LINE__,
- "http://www.google.com/path1/path2/",
- "attachment; filename==?iso88591?Q?caf=E3?",
- "",
- "",
- "image/jpeg",
- L"download",
- L"download" JPEG_EXT
- },
- };
-
- // Tests filename generation. Once the correct filename is
- // selected, they should be passed through the validation steps and
- // a correct extension should be added if necessary.
- const GenerateFilenameCase generation_tests[] = {
- // Dotfiles. Ensures preceeding period(s) stripped.
- {
- __LINE__,
- "http://www.google.com/.test.html",
- "",
- "",
- "",
- "",
- L"",
- L"test.html"
- },
- {
- __LINE__,
- "http://www.google.com/.test",
- "",
- "",
- "",
- "",
- L"",
- L"test"
- },
- {
- __LINE__,
- "http://www.google.com/..test",
- "",
- "",
- "",
- "",
- L"",
- L"test"
- },
- { // Disposition has relative paths, remove directory separators
- __LINE__,
- "http://www.evil.com/my_download.txt",
- "filename=../../../../././../a_file_name.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"-..-..-..-.-.-..-a_file_name.txt"
- },
- { // Disposition has parent directories, remove directory separators
- __LINE__,
- "http://www.evil.com/my_download.txt",
- "filename=dir1/dir2/a_file_name.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"dir1-dir2-a_file_name.txt"
- },
- { // Disposition has relative paths, remove directory separators
- __LINE__,
- "http://www.evil.com/my_download.txt",
- "filename=..\\..\\..\\..\\.\\.\\..\\a_file_name.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"-..-..-..-.-.-..-a_file_name.txt"
- },
- { // Disposition has parent directories, remove directory separators
- __LINE__,
- "http://www.evil.com/my_download.txt",
- "filename=dir1\\dir2\\a_file_name.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"dir1-dir2-a_file_name.txt"
- },
- { // No useful information in disposition or URL, use default
- __LINE__,
- "http://www.truncated.com/path/",
- "",
- "",
- "",
- "text/plain",
- L"download",
- L"download" TXT_EXT
- },
- { // Filename looks like HTML?
- __LINE__,
- "http://www.evil.com/get/malware/here",
- "filename=\"<blink>Hello kitty</blink>\"",
- "",
- "",
- "text/plain",
- L"default",
- L"-blink-Hello kitty--blink-" TXT_EXT
- },
- { // A normal avi should get .avi and not .avi.avi
- __LINE__,
- "https://blah.google.com/misc/2.avi",
- "",
- "",
- "",
- "video/x-msvideo",
- L"download",
- L"2.avi"
- },
- { // Extension generation
- __LINE__,
- "http://www.example.com/my-cat",
- "filename=my-cat",
- "",
- "",
- "image/jpeg",
- L"download",
- L"my-cat" JPEG_EXT
- },
- {
- __LINE__,
- "http://www.example.com/my-cat",
- "filename=my-cat",
- "",
- "",
- "text/plain",
- L"download",
- L"my-cat.txt"
- },
- {
- __LINE__,
- "http://www.example.com/my-cat",
- "filename=my-cat",
- "",
- "",
- "text/html",
- L"download",
- L"my-cat" HTML_EXT
- },
- { // Unknown MIME type
- __LINE__,
- "http://www.example.com/my-cat",
- "filename=my-cat",
- "",
- "",
- "dance/party",
- L"download",
- L"my-cat"
- },
- {
- __LINE__,
- "http://www.example.com/my-cat.jpg",
- "filename=my-cat.jpg",
- "",
- "",
- "text/plain",
- L"download",
- L"my-cat.jpg"
- },
- // Windows specific tests
-#if defined(OS_WIN)
- {
- __LINE__,
- "http://www.goodguy.com/evil.exe",
- "filename=evil.exe",
- "",
- "",
- "image/jpeg",
- L"download",
- L"evil.exe"
- },
- {
- __LINE__,
- "http://www.goodguy.com/ok.exe",
- "filename=ok.exe",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"ok.exe"
- },
- {
- __LINE__,
- "http://www.goodguy.com/evil.dll",
- "filename=evil.dll",
- "",
- "",
- "dance/party",
- L"download",
- L"evil.dll"
- },
- {
- __LINE__,
- "http://www.goodguy.com/evil.exe",
- "filename=evil",
- "",
- "",
- "application/rss+xml",
- L"download",
- L"evil"
- },
- // Test truncation of trailing dots and spaces
- {
- __LINE__,
- "http://www.goodguy.com/evil.exe ",
- "filename=evil.exe ",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"evil.exe"
- },
- {
- __LINE__,
- "http://www.goodguy.com/evil.exe.",
- "filename=evil.exe.",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"evil.exe-"
- },
- {
- __LINE__,
- "http://www.goodguy.com/evil.exe. . .",
- "filename=evil.exe. . .",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"evil.exe-------"
- },
- {
- __LINE__,
- "http://www.goodguy.com/evil.",
- "filename=evil.",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"evil-"
- },
- {
- __LINE__,
- "http://www.goodguy.com/. . . . .",
- "filename=. . . . .",
- "",
- "",
- "binary/octet-stream",
- L"download",
- L"download"
- },
- {
- __LINE__,
- "http://www.badguy.com/attachment?name=meh.exe%C2%A0",
- "attachment; filename=\"meh.exe\xC2\xA0\"",
- "",
- "",
- "binary/octet-stream",
- L"",
- L"meh.exe-"
- },
-#endif // OS_WIN
- {
- __LINE__,
- "http://www.goodguy.com/utils.js",
- "filename=utils.js",
- "",
- "",
- "application/x-javascript",
- L"download",
- L"utils.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/contacts.js",
- "filename=contacts.js",
- "",
- "",
- "application/json",
- L"download",
- L"contacts.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/utils.js",
- "filename=utils.js",
- "",
- "",
- "text/javascript",
- L"download",
- L"utils.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/utils.js",
- "filename=utils.js",
- "",
- "",
- "text/javascript;version=2",
- L"download",
- L"utils.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/utils.js",
- "filename=utils.js",
- "",
- "",
- "application/ecmascript",
- L"download",
- L"utils.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/utils.js",
- "filename=utils.js",
- "",
- "",
- "application/ecmascript;version=4",
- L"download",
- L"utils.js"
- },
- {
- __LINE__,
- "http://www.goodguy.com/program.exe",
- "filename=program.exe",
- "",
- "",
- "application/foo-bar",
- L"download",
- L"program.exe"
- },
- {
- __LINE__,
- "http://www.evil.com/../foo.txt",
- "filename=../foo.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"-foo.txt"
- },
- {
- __LINE__,
- "http://www.evil.com/..\\foo.txt",
- "filename=..\\foo.txt",
- "",
- "",
- "text/plain",
- L"download",
- L"-foo.txt"
- },
- {
- __LINE__,
- "http://www.evil.com/.hidden",
- "filename=.hidden",
- "",
- "",
- "text/plain",
- L"download",
- L"hidden" TXT_EXT
- },
- {
- __LINE__,
- "http://www.evil.com/trailing.",
- "filename=trailing.",
- "",
- "",
- "dance/party",
- L"download",
-#if defined(OS_WIN)
- L"trailing-"
-#else
- L"trailing"
-#endif
- },
- {
- __LINE__,
- "http://www.evil.com/trailing.",
- "filename=trailing.",
- "",
- "",
- "text/plain",
- L"download",
-#if defined(OS_WIN)
- L"trailing-" TXT_EXT
-#else
- L"trailing" TXT_EXT
-#endif
- },
- {
- __LINE__,
- "http://www.evil.com/.",
- "filename=.",
- "",
- "",
- "dance/party",
- L"download",
- L"download"
- },
- {
- __LINE__,
- "http://www.evil.com/..",
- "filename=..",
- "",
- "",
- "dance/party",
- L"download",
- L"download"
- },
- {
- __LINE__,
- "http://www.evil.com/...",
- "filename=...",
- "",
- "",
- "dance/party",
- L"download",
- L"download"
- },
- { // Note that this one doesn't have "filename=" on it.
- __LINE__,
- "http://www.evil.com/",
- "a_file_name.txt",
- "",
- "",
- "image/jpeg",
- L"download",
- L"download" JPEG_EXT
- },
- {
- __LINE__,
- "http://www.evil.com/",
- "filename=",
- "",
- "",
- "image/jpeg",
- L"download",
- L"download" JPEG_EXT
- },
- {
- __LINE__,
- "http://www.example.com/simple",
- "filename=simple",
- "",
- "",
- "application/octet-stream",
- L"download",
- L"simple"
- },
- // Reserved words on Windows
- {
- __LINE__,
- "http://www.goodguy.com/COM1",
- "filename=COM1",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"_COM1"
-#else
- L"COM1"
-#endif
- },
- {
- __LINE__,
- "http://www.goodguy.com/COM4.txt",
- "filename=COM4.txt",
- "",
- "",
- "text/plain",
- L"download",
-#if defined(OS_WIN)
- L"_COM4.txt"
-#else
- L"COM4.txt"
-#endif
- },
- {
- __LINE__,
- "http://www.goodguy.com/lpt1.TXT",
- "filename=lpt1.TXT",
- "",
- "",
- "text/plain",
- L"download",
-#if defined(OS_WIN)
- L"_lpt1.TXT"
-#else
- L"lpt1.TXT"
-#endif
- },
- {
- __LINE__,
- "http://www.goodguy.com/clock$.txt",
- "filename=clock$.txt",
- "",
- "",
- "text/plain",
- L"download",
-#if defined(OS_WIN)
- L"_clock$.txt"
-#else
- L"clock$.txt"
-#endif
- },
- { // Validation should also apply to sugested name
- __LINE__,
- "http://www.goodguy.com/blah$.txt",
- "filename=clock$.txt",
- "",
- "clock$.txt",
- "text/plain",
- L"download",
-#if defined(OS_WIN)
- L"_clock$.txt"
-#else
- L"clock$.txt"
-#endif
- },
- {
- __LINE__,
- "http://www.goodguy.com/mycom1.foo",
- "filename=mycom1.foo",
- "",
- "",
- "text/plain",
- L"download",
- L"mycom1.foo"
- },
- {
- __LINE__,
- "http://www.badguy.com/Setup.exe.local",
- "filename=Setup.exe.local",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"Setup.exe.download"
-#else
- L"Setup.exe.local"
-#endif
- },
- {
- __LINE__,
- "http://www.badguy.com/Setup.exe.local",
- "filename=Setup.exe.local.local",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"Setup.exe.local.download"
-#else
- L"Setup.exe.local.local"
-#endif
- },
- {
- __LINE__,
- "http://www.badguy.com/Setup.exe.lnk",
- "filename=Setup.exe.lnk",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"Setup.exe.download"
-#else
- L"Setup.exe.lnk"
-#endif
- },
- {
- __LINE__,
- "http://www.badguy.com/Desktop.ini",
- "filename=Desktop.ini",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"_Desktop.ini"
-#else
- L"Desktop.ini"
-#endif
- },
- {
- __LINE__,
- "http://www.badguy.com/Thumbs.db",
- "filename=Thumbs.db",
- "",
- "",
- "application/foo-bar",
- L"download",
-#if defined(OS_WIN)
- L"_Thumbs.db"
-#else
- L"Thumbs.db"
-#endif
- },
- {
- __LINE__,
- "http://www.hotmail.com",
- "filename=source.jpg",
- "",
- "",
- "application/x-javascript",
- L"download",
- L"source.jpg"
- },
- { // http://crbug.com/5772.
- __LINE__,
- "http://www.example.com/foo.tar.gz",
- "",
- "",
- "",
- "application/x-tar",
- L"download",
- L"foo.tar.gz"
- },
- { // http://crbug.com/52250.
- __LINE__,
- "http://www.example.com/foo.tgz",
- "",
- "",
- "",
- "application/x-tar",
- L"download",
- L"foo.tgz"
- },
- { // http://crbug.com/7337.
- __LINE__,
- "http://maged.lordaeron.org/blank.reg",
- "",
- "",
- "",
- "text/x-registry",
- L"download",
- L"blank.reg"
- },
- {
- __LINE__,
- "http://www.example.com/bar.tar",
- "",
- "",
- "",
- "application/x-tar",
- L"download",
- L"bar.tar"
- },
- {
- __LINE__,
- "http://www.example.com/bar.bogus",
- "",
- "",
- "",
- "application/x-tar",
- L"download",
- L"bar.bogus"
- },
- { // http://crbug.com/20337
- __LINE__,
- "http://www.example.com/.download.txt",
- "filename=.download.txt",
- "",
- "",
- "text/plain",
- L"-download",
- L"download.txt"
- },
- { // http://crbug.com/56855.
- __LINE__,
- "http://www.example.com/bar.sh",
- "",
- "",
- "",
- "application/x-sh",
- L"download",
- L"bar.sh"
- },
- { // http://crbug.com/61571
- __LINE__,
- "http://www.example.com/npdf.php?fn=foobar.pdf",
- "",
- "",
- "",
- "text/plain",
- L"download",
- L"npdf" TXT_EXT
- },
- { // Shouldn't overwrite C-D specified extension.
- __LINE__,
- "http://www.example.com/npdf.php?fn=foobar.pdf",
- "filename=foobar.jpg",
- "",
- "",
- "text/plain",
- L"download",
- L"foobar.jpg"
- },
- { // http://crbug.com/87719
- __LINE__,
- "http://www.example.com/image.aspx?id=blargh",
- "",
- "",
- "",
- "image/jpeg",
- L"download",
- L"image" JPEG_EXT
- },
-#if defined(OS_CHROMEOS)
- { // http://crosbug.com/26028
- __LINE__,
- "http://www.example.com/fooa%cc%88.txt",
- "",
- "",
- "",
- "image/jpeg",
- L"foo\xe4",
- L"foo\xe4.txt"
- },
-#endif
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(selection_tests); ++i)
- RunGenerateFileNameTestCase(&selection_tests[i]);
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(generation_tests); ++i)
- RunGenerateFileNameTestCase(&generation_tests[i]);
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(generation_tests); ++i) {
- GenerateFilenameCase test_case = generation_tests[i];
- test_case.referrer_charset = "GBK";
- RunGenerateFileNameTestCase(&test_case);
- }
-}
-
-// This is currently a windows specific function.
-#if defined(OS_WIN)
-namespace {
-
-struct GetDirectoryListingEntryCase {
- const wchar_t* name;
- const char* raw_bytes;
- bool is_dir;
- int64 filesize;
- base::Time time;
- const char* expected;
-};
-
-} // namespace
-TEST(NetUtilTest, GetDirectoryListingEntry) {
- const GetDirectoryListingEntryCase test_cases[] = {
- {L"Foo",
- "",
- false,
- 10000,
- base::Time(),
- "<script>addRow(\"Foo\",\"Foo\",0,\"9.8 kB\",\"\");</script>\n"},
- {L"quo\"tes",
- "",
- false,
- 10000,
- base::Time(),
- "<script>addRow(\"quo\\\"tes\",\"quo%22tes\",0,\"9.8 kB\",\"\");</script>"
- "\n"},
- {L"quo\"tes",
- "quo\"tes",
- false,
- 10000,
- base::Time(),
- "<script>addRow(\"quo\\\"tes\",\"quo%22tes\",0,\"9.8 kB\",\"\");</script>"
- "\n"},
- // U+D55C0 U+AE00. raw_bytes is empty (either a local file with
- // UTF-8/UTF-16 encoding or a remote file on an ftp server using UTF-8
- {L"\xD55C\xAE00.txt",
- "",
- false,
- 10000,
- base::Time(),
- "<script>addRow(\"\xED\x95\x9C\xEA\xB8\x80.txt\","
- "\"%ED%95%9C%EA%B8%80.txt\",0,\"9.8 kB\",\"\");</script>\n"},
- // U+D55C0 U+AE00. raw_bytes is the corresponding EUC-KR sequence:
- // a local or remote file in EUC-KR.
- {L"\xD55C\xAE00.txt",
- "\xC7\xD1\xB1\xDB.txt",
- false,
- 10000,
- base::Time(),
- "<script>addRow(\"\xED\x95\x9C\xEA\xB8\x80.txt\",\"%C7%D1%B1%DB.txt\""
- ",0,\"9.8 kB\",\"\");</script>\n"},
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
- const std::string results = GetDirectoryListingEntry(
- WideToUTF16(test_cases[i].name),
- test_cases[i].raw_bytes,
- test_cases[i].is_dir,
- test_cases[i].filesize,
- test_cases[i].time);
- EXPECT_EQ(test_cases[i].expected, results);
- }
-}
-
-#endif
-
TEST(NetUtilTest, ParseHostAndPort) {
const struct {
const char* input;
@@ -2459,551 +405,6 @@ TEST(NetUtilTest, GetHostName) {
EXPECT_FALSE(hostname.empty());
}
-TEST(NetUtilTest, FormatUrl) {
- FormatUrlTypes default_format_type = kFormatUrlOmitUsernamePassword;
- const UrlTestData tests[] = {
- {"Empty URL", "", "", default_format_type, UnescapeRule::NORMAL, L"", 0},
-
- {"Simple URL",
- "http://www.google.com/", "", default_format_type, UnescapeRule::NORMAL,
- L"http://www.google.com/", 7},
-
- {"With a port number and a reference",
- "http://www.google.com:8080/#\xE3\x82\xB0", "", default_format_type,
- UnescapeRule::NORMAL,
- L"http://www.google.com:8080/#\x30B0", 7},
-
- // -------- IDN tests --------
- {"Japanese IDN with ja",
- "http://xn--l8jvb1ey91xtjb.jp", "ja", default_format_type,
- UnescapeRule::NORMAL, L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
-
- {"Japanese IDN with en",
- "http://xn--l8jvb1ey91xtjb.jp", "en", default_format_type,
- UnescapeRule::NORMAL, L"http://xn--l8jvb1ey91xtjb.jp/", 7},
-
- {"Japanese IDN without any languages",
- "http://xn--l8jvb1ey91xtjb.jp", "", default_format_type,
- UnescapeRule::NORMAL,
- // Single script is safe for empty languages.
- L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
-
- {"mailto: with Japanese IDN",
- "mailto:foo@xn--l8jvb1ey91xtjb.jp", "ja", default_format_type,
- UnescapeRule::NORMAL,
- // GURL doesn't assume an email address's domain part as a host name.
- L"mailto:foo@xn--l8jvb1ey91xtjb.jp", 7},
-
- {"file: with Japanese IDN",
- "file://xn--l8jvb1ey91xtjb.jp/config.sys", "ja", default_format_type,
- UnescapeRule::NORMAL,
- L"file://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 7},
-
- {"ftp: with Japanese IDN",
- "ftp://xn--l8jvb1ey91xtjb.jp/config.sys", "ja", default_format_type,
- UnescapeRule::NORMAL,
- L"ftp://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 6},
-
- // -------- omit_username_password flag tests --------
- {"With username and password, omit_username_password=false",
- "http://user:passwd@example.com/foo", "",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- L"http://user:passwd@example.com/foo", 19},
-
- {"With username and password, omit_username_password=true",
- "http://user:passwd@example.com/foo", "", default_format_type,
- UnescapeRule::NORMAL, L"http://example.com/foo", 7},
-
- {"With username and no password",
- "http://user@example.com/foo", "", default_format_type,
- UnescapeRule::NORMAL, L"http://example.com/foo", 7},
-
- {"Just '@' without username and password",
- "http://@example.com/foo", "", default_format_type, UnescapeRule::NORMAL,
- L"http://example.com/foo", 7},
-
- // GURL doesn't think local-part of an email address is username for URL.
- {"mailto:, omit_username_password=true",
- "mailto:foo@example.com", "", default_format_type, UnescapeRule::NORMAL,
- L"mailto:foo@example.com", 7},
-
- // -------- unescape flag tests --------
- {"Do not unescape",
- "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
- "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
- "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", "en", default_format_type,
- UnescapeRule::NONE,
- // GURL parses %-encoded hostnames into Punycode.
- L"http://xn--qcka1pmc.jp/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
- L"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", 7},
-
- {"Unescape normally",
- "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
- "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
- "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", "en", default_format_type,
- UnescapeRule::NORMAL,
- L"http://xn--qcka1pmc.jp/\x30B0\x30FC\x30B0\x30EB"
- L"?q=\x30B0\x30FC\x30B0\x30EB", 7},
-
- {"Unescape normally including unescape spaces",
- "http://www.google.com/search?q=Hello%20World", "en", default_format_type,
- UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World", 7},
-
- /*
- {"unescape=true with some special characters",
- "http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", "",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- L"http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", 25},
- */
- // Disabled: the resultant URL becomes "...user%253A:%2540passwd...".
-
- // -------- omit http: --------
- {"omit http with user name",
- "http://user@example.com/foo", "", kFormatUrlOmitAll,
- UnescapeRule::NORMAL, L"example.com/foo", 0},
-
- {"omit http",
- "http://www.google.com/", "en", kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL, L"www.google.com/",
- 0},
-
- {"omit http with https",
- "https://www.google.com/", "en", kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL, L"https://www.google.com/",
- 8},
-
- {"omit http starts with ftp.",
- "http://ftp.google.com/", "en", kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL, L"http://ftp.google.com/",
- 7},
-
- // -------- omit trailing slash on bare hostname --------
- {"omit slash when it's the entire path",
- "http://www.google.com/", "en",
- kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
- L"http://www.google.com", 7},
- {"omit slash when there's a ref",
- "http://www.google.com/#ref", "en",
- kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
- L"http://www.google.com/#ref", 7},
- {"omit slash when there's a query",
- "http://www.google.com/?", "en",
- kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
- L"http://www.google.com/?", 7},
- {"omit slash when it's not the entire path",
- "http://www.google.com/foo", "en",
- kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
- L"http://www.google.com/foo", 7},
- {"omit slash for nonstandard URLs",
- "data:/", "en", kFormatUrlOmitTrailingSlashOnBareHostname,
- UnescapeRule::NORMAL, L"data:/", 5},
- {"omit slash for file URLs",
- "file:///", "en", kFormatUrlOmitTrailingSlashOnBareHostname,
- UnescapeRule::NORMAL, L"file:///", 7},
-
- // -------- view-source: --------
- {"view-source",
- "view-source:http://xn--qcka1pmc.jp/", "ja", default_format_type,
- UnescapeRule::NORMAL, L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/",
- 19},
-
- {"view-source of view-source",
- "view-source:view-source:http://xn--qcka1pmc.jp/", "ja",
- default_format_type, UnescapeRule::NORMAL,
- L"view-source:view-source:http://xn--qcka1pmc.jp/", 12},
-
- // view-source should omit http and trailing slash where non-view-source
- // would.
- {"view-source omit http",
- "view-source:http://a.b/c", "en", kFormatUrlOmitAll,
- UnescapeRule::NORMAL, L"view-source:a.b/c",
- 12},
- {"view-source omit http starts with ftp.",
- "view-source:http://ftp.b/c", "en", kFormatUrlOmitAll,
- UnescapeRule::NORMAL, L"view-source:http://ftp.b/c",
- 19},
- {"view-source omit slash when it's the entire path",
- "view-source:http://a.b/", "en", kFormatUrlOmitAll,
- UnescapeRule::NORMAL, L"view-source:a.b",
- 12},
- };
-
- for (size_t i = 0; i < arraysize(tests); ++i) {
- size_t prefix_len;
- base::string16 formatted = FormatUrl(
- GURL(tests[i].input), tests[i].languages, tests[i].format_types,
- tests[i].escape_rules, NULL, &prefix_len, NULL);
- EXPECT_EQ(WideToUTF16(tests[i].output), formatted) << tests[i].description;
- EXPECT_EQ(tests[i].prefix_len, prefix_len) << tests[i].description;
- }
-}
-
-TEST(NetUtilTest, FormatUrlParsed) {
- // No unescape case.
- url_parse::Parsed parsed;
- base::string16 formatted = FormatUrl(
- GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
- "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- "ja", kFormatUrlOmitNothing, UnescapeRule::NONE, &parsed, NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(
- L"http://%E3%82%B0:%E3%83%BC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
- L"/%E3%82%B0/?q=%E3%82%B0#\x30B0"), formatted);
- EXPECT_EQ(WideToUTF16(L"%E3%82%B0"),
- formatted.substr(parsed.username.begin, parsed.username.len));
- EXPECT_EQ(WideToUTF16(L"%E3%83%BC"),
- formatted.substr(parsed.password.begin, parsed.password.len));
- EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"8080"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/%E3%82%B0/"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"q=%E3%82%B0"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"\x30B0"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // Unescape case.
- formatted = FormatUrl(
- GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
- "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- "ja", kFormatUrlOmitNothing, UnescapeRule::NORMAL, &parsed, NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(L"http://\x30B0:\x30FC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
- L"/\x30B0/?q=\x30B0#\x30B0"), formatted);
- EXPECT_EQ(WideToUTF16(L"\x30B0"),
- formatted.substr(parsed.username.begin, parsed.username.len));
- EXPECT_EQ(WideToUTF16(L"\x30FC"),
- formatted.substr(parsed.password.begin, parsed.password.len));
- EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"8080"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/\x30B0/"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"q=\x30B0"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"\x30B0"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // Omit_username_password + unescape case.
- formatted = FormatUrl(
- GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
- "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- "ja", kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL, &parsed,
- NULL, NULL);
- EXPECT_EQ(WideToUTF16(L"http://\x30B0\x30FC\x30B0\x30EB.jp:8080"
- L"/\x30B0/?q=\x30B0#\x30B0"), formatted);
- EXPECT_FALSE(parsed.username.is_valid());
- EXPECT_FALSE(parsed.password.is_valid());
- EXPECT_EQ(WideToUTF16(L"\x30B0\x30FC\x30B0\x30EB.jp"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"8080"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/\x30B0/"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"q=\x30B0"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"\x30B0"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // View-source case.
- formatted =
- FormatUrl(GURL("view-source:http://user:passwd@host:81/path?query#ref"),
- std::string(),
- kFormatUrlOmitUsernamePassword,
- UnescapeRule::NORMAL,
- &parsed,
- NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(L"view-source:http://host:81/path?query#ref"),
- formatted);
- EXPECT_EQ(WideToUTF16(L"view-source:http"),
- formatted.substr(parsed.scheme.begin, parsed.scheme.len));
- EXPECT_FALSE(parsed.username.is_valid());
- EXPECT_FALSE(parsed.password.is_valid());
- EXPECT_EQ(WideToUTF16(L"host"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"81"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/path"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"query"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"ref"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // omit http case.
- formatted = FormatUrl(GURL("http://host:8000/a?b=c#d"),
- std::string(),
- kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL,
- &parsed,
- NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(L"host:8000/a?b=c#d"), formatted);
- EXPECT_FALSE(parsed.scheme.is_valid());
- EXPECT_FALSE(parsed.username.is_valid());
- EXPECT_FALSE(parsed.password.is_valid());
- EXPECT_EQ(WideToUTF16(L"host"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"8000"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/a"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"b=c"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"d"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // omit http starts with ftp case.
- formatted = FormatUrl(GURL("http://ftp.host:8000/a?b=c#d"),
- std::string(),
- kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL,
- &parsed,
- NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(L"http://ftp.host:8000/a?b=c#d"), formatted);
- EXPECT_TRUE(parsed.scheme.is_valid());
- EXPECT_FALSE(parsed.username.is_valid());
- EXPECT_FALSE(parsed.password.is_valid());
- EXPECT_EQ(WideToUTF16(L"http"),
- formatted.substr(parsed.scheme.begin, parsed.scheme.len));
- EXPECT_EQ(WideToUTF16(L"ftp.host"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"8000"),
- formatted.substr(parsed.port.begin, parsed.port.len));
- EXPECT_EQ(WideToUTF16(L"/a"),
- formatted.substr(parsed.path.begin, parsed.path.len));
- EXPECT_EQ(WideToUTF16(L"b=c"),
- formatted.substr(parsed.query.begin, parsed.query.len));
- EXPECT_EQ(WideToUTF16(L"d"),
- formatted.substr(parsed.ref.begin, parsed.ref.len));
-
- // omit http starts with 'f' case.
- formatted = FormatUrl(GURL("http://f/"),
- std::string(),
- kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL,
- &parsed,
- NULL,
- NULL);
- EXPECT_EQ(WideToUTF16(L"f/"), formatted);
- EXPECT_FALSE(parsed.scheme.is_valid());
- EXPECT_FALSE(parsed.username.is_valid());
- EXPECT_FALSE(parsed.password.is_valid());
- EXPECT_FALSE(parsed.port.is_valid());
- EXPECT_TRUE(parsed.path.is_valid());
- EXPECT_FALSE(parsed.query.is_valid());
- EXPECT_FALSE(parsed.ref.is_valid());
- EXPECT_EQ(WideToUTF16(L"f"),
- formatted.substr(parsed.host.begin, parsed.host.len));
- EXPECT_EQ(WideToUTF16(L"/"),
- formatted.substr(parsed.path.begin, parsed.path.len));
-}
-
-// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
-// results in the original GURL, for each ASCII character in the path.
-TEST(NetUtilTest, FormatUrlRoundTripPathASCII) {
- for (unsigned char test_char = 32; test_char < 128; ++test_char) {
- GURL url(std::string("http://www.google.com/") +
- static_cast<char>(test_char));
- size_t prefix_len;
- base::string16 formatted = FormatUrl(url,
- std::string(),
- kFormatUrlOmitUsernamePassword,
- UnescapeRule::NORMAL,
- NULL,
- &prefix_len,
- NULL);
- EXPECT_EQ(url.spec(), GURL(formatted).spec());
- }
-}
-
-// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
-// results in the original GURL, for each escaped ASCII character in the path.
-TEST(NetUtilTest, FormatUrlRoundTripPathEscaped) {
- for (unsigned char test_char = 32; test_char < 128; ++test_char) {
- std::string original_url("http://www.google.com/");
- original_url.push_back('%');
- original_url.append(base::HexEncode(&test_char, 1));
-
- GURL url(original_url);
- size_t prefix_len;
- base::string16 formatted = FormatUrl(url,
- std::string(),
- kFormatUrlOmitUsernamePassword,
- UnescapeRule::NORMAL,
- NULL,
- &prefix_len,
- NULL);
- EXPECT_EQ(url.spec(), GURL(formatted).spec());
- }
-}
-
-// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
-// results in the original GURL, for each ASCII character in the query.
-TEST(NetUtilTest, FormatUrlRoundTripQueryASCII) {
- for (unsigned char test_char = 32; test_char < 128; ++test_char) {
- GURL url(std::string("http://www.google.com/?") +
- static_cast<char>(test_char));
- size_t prefix_len;
- base::string16 formatted = FormatUrl(url,
- std::string(),
- kFormatUrlOmitUsernamePassword,
- UnescapeRule::NORMAL,
- NULL,
- &prefix_len,
- NULL);
- EXPECT_EQ(url.spec(), GURL(formatted).spec());
- }
-}
-
-// Make sure that calling FormatUrl on a GURL and then converting back to a GURL
-// only results in a different GURL for certain characters.
-TEST(NetUtilTest, FormatUrlRoundTripQueryEscaped) {
- // A full list of characters which FormatURL should unescape and GURL should
- // not escape again, when they appear in a query string.
- const char* kUnescapedCharacters =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~";
- for (unsigned char test_char = 0; test_char < 128; ++test_char) {
- std::string original_url("http://www.google.com/?");
- original_url.push_back('%');
- original_url.append(base::HexEncode(&test_char, 1));
-
- GURL url(original_url);
- size_t prefix_len;
- base::string16 formatted = FormatUrl(url,
- std::string(),
- kFormatUrlOmitUsernamePassword,
- UnescapeRule::NORMAL,
- NULL,
- &prefix_len,
- NULL);
-
- if (test_char &&
- strchr(kUnescapedCharacters, static_cast<char>(test_char))) {
- EXPECT_NE(url.spec(), GURL(formatted).spec());
- } else {
- EXPECT_EQ(url.spec(), GURL(formatted).spec());
- }
- }
-}
-
-TEST(NetUtilTest, FormatUrlWithOffsets) {
- CheckAdjustedOffsets(std::string(), "en", kFormatUrlOmitNothing,
- UnescapeRule::NORMAL, NULL);
-
- const size_t basic_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25
- };
- CheckAdjustedOffsets("http://www.google.com/foo/", "en",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- basic_offsets);
-
- const size_t omit_auth_offsets_1[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 7,
- 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
- };
- CheckAdjustedOffsets("http://foo:bar@www.google.com/", "en",
- kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
- omit_auth_offsets_1);
-
- const size_t omit_auth_offsets_2[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21
- };
- CheckAdjustedOffsets("http://foo@www.google.com/", "en",
- kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
- omit_auth_offsets_2);
-
- const size_t dont_omit_auth_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, kNpos, 11, 12, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
- 30, 31
- };
- // Unescape to "http://foo\x30B0:\x30B0bar@www.google.com".
- CheckAdjustedOffsets("http://foo%E3%82%B0:%E3%82%B0bar@www.google.com/", "en",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- dont_omit_auth_offsets);
-
- const size_t view_source_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, kNpos,
- kNpos, kNpos, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33
- };
- CheckAdjustedOffsets("view-source:http://foo@www.google.com/", "en",
- kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
- view_source_offsets);
-
- const size_t idn_hostname_offsets_1[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 12,
- 13, 14, 15, 16, 17, 18, 19
- };
- // Convert punycode to "http://\x671d\x65e5\x3042\x3055\x3072.jp/foo/".
- CheckAdjustedOffsets("http://xn--l8jvb1ey91xtjb.jp/foo/", "ja",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- idn_hostname_offsets_1);
-
- const size_t idn_hostname_offsets_2[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 14, 15, kNpos, kNpos, kNpos,
- kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, 19, 20, 21, 22, 23, 24
- };
- // Convert punycode to
- // "http://test.\x89c6\x9891.\x5317\x4eac\x5927\x5b78.test/".
- CheckAdjustedOffsets("http://test.xn--cy2a840a.xn--1lq90ic7f1rc.test/",
- "zh-CN", kFormatUrlOmitNothing, UnescapeRule::NORMAL,
- idn_hostname_offsets_2);
-
- const size_t unescape_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, kNpos, kNpos, 26, 27, 28, 29, 30, kNpos, kNpos, kNpos,
- kNpos, kNpos, kNpos, kNpos, kNpos, 31, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, kNpos, kNpos, 32, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
- kNpos, 33, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos
- };
- // Unescape to "http://www.google.com/foo bar/\x30B0\x30FC\x30B0\x30EB".
- CheckAdjustedOffsets(
- "http://www.google.com/foo%20bar/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
- "en", kFormatUrlOmitNothing, UnescapeRule::SPACES, unescape_offsets);
-
- const size_t ref_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, kNpos, kNpos, 32, kNpos, kNpos,
- 33
- };
- // Unescape to "http://www.google.com/foo.html#\x30B0\x30B0z".
- CheckAdjustedOffsets(
- "http://www.google.com/foo.html#\xE3\x82\xB0\xE3\x82\xB0z", "en",
- kFormatUrlOmitNothing, UnescapeRule::NORMAL, ref_offsets);
-
- const size_t omit_http_offsets[] = {
- 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14
- };
- CheckAdjustedOffsets("http://www.google.com/", "en", kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL, omit_http_offsets);
-
- const size_t omit_http_start_with_ftp_offsets[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
- };
- CheckAdjustedOffsets("http://ftp.google.com/", "en", kFormatUrlOmitHTTP,
- UnescapeRule::NORMAL, omit_http_start_with_ftp_offsets);
-
- const size_t omit_all_offsets[] = {
- 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, kNpos, kNpos, kNpos, kNpos,
- 0, 1, 2, 3, 4, 5, 6, 7
- };
- CheckAdjustedOffsets("http://user@foo.com/", "en", kFormatUrlOmitAll,
- UnescapeRule::NORMAL, omit_all_offsets);
-}
-
TEST(NetUtilTest, SimplifyUrlForRequest) {
struct {
const char* input_url;
@@ -3299,11 +700,12 @@ TEST(NetUtilTest, IsLocalhost) {
// Verify GetNetworkList().
TEST(NetUtilTest, GetNetworkList) {
NetworkInterfaceList list;
- ASSERT_TRUE(GetNetworkList(&list));
+ ASSERT_TRUE(GetNetworkList(&list, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES));
for (NetworkInterfaceList::iterator it = list.begin();
it != list.end(); ++it) {
- // Verify that the name is not empty.
+ // Verify that the names are not empty.
EXPECT_FALSE(it->name.empty());
+ EXPECT_FALSE(it->friendly_name.empty());
// Verify that the address is correct.
EXPECT_TRUE(it->address.size() == kIPv4AddressSize ||
@@ -3344,7 +746,7 @@ TEST(NetUtilTest, GetNetworkList) {
EXPECT_EQ(luid_to_guid(&luid, &guid), NO_ERROR);
LPOLESTR name;
StringFromCLSID(guid, &name);
- EXPECT_STREQ(UTF8ToWide(it->name).c_str(), name);
+ EXPECT_STREQ(base::UTF8ToWide(it->name).c_str(), name);
CoTaskMemFree(name);
continue;
} else {
@@ -3352,6 +754,9 @@ TEST(NetUtilTest, GetNetworkList) {
EXPECT_LT(it->interface_index, 1u << 24u); // Must fit 0.x.x.x.
EXPECT_NE(it->interface_index, 0u); // 0 means to use default.
}
+ if (it->type == NetworkChangeNotifier::CONNECTION_WIFI) {
+ EXPECT_NE(WIFI_PHY_LAYER_PROTOCOL_NONE, GetWifiPHYLayerProtocol());
+ }
#elif !defined(OS_ANDROID)
char name[IF_NAMESIZE];
EXPECT_TRUE(if_indextoname(it->interface_index, name));
@@ -3360,88 +765,6 @@ TEST(NetUtilTest, GetNetworkList) {
}
}
-static const base::FilePath::CharType* kSafePortableBasenames[] = {
- FILE_PATH_LITERAL("a"),
- FILE_PATH_LITERAL("a.txt"),
- FILE_PATH_LITERAL("a b.txt"),
- FILE_PATH_LITERAL("a-b.txt"),
- FILE_PATH_LITERAL("My Computer"),
- FILE_PATH_LITERAL(" Computer"),
-};
-
-static const base::FilePath::CharType* kUnsafePortableBasenames[] = {
- FILE_PATH_LITERAL(""),
- FILE_PATH_LITERAL("."),
- FILE_PATH_LITERAL(".."),
- FILE_PATH_LITERAL("..."),
- FILE_PATH_LITERAL("con"),
- FILE_PATH_LITERAL("con.zip"),
- FILE_PATH_LITERAL("NUL"),
- FILE_PATH_LITERAL("NUL.zip"),
- FILE_PATH_LITERAL(".a"),
- FILE_PATH_LITERAL("a."),
- FILE_PATH_LITERAL("a\"a"),
- FILE_PATH_LITERAL("a<a"),
- FILE_PATH_LITERAL("a>a"),
- FILE_PATH_LITERAL("a?a"),
- FILE_PATH_LITERAL("a/"),
- FILE_PATH_LITERAL("a\\"),
- FILE_PATH_LITERAL("a "),
- FILE_PATH_LITERAL("a . ."),
- FILE_PATH_LITERAL("My Computer.{a}"),
- FILE_PATH_LITERAL("My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}"),
-#if !defined(OS_WIN)
- FILE_PATH_LITERAL("a\\a"),
-#endif
-};
-
-static const base::FilePath::CharType* kSafePortableRelativePaths[] = {
- FILE_PATH_LITERAL("a/a"),
-#if defined(OS_WIN)
- FILE_PATH_LITERAL("a\\a"),
-#endif
-};
-
-TEST(NetUtilTest, IsSafePortablePathComponent) {
- for (size_t i = 0 ; i < arraysize(kSafePortableBasenames); ++i) {
- EXPECT_TRUE(IsSafePortablePathComponent(base::FilePath(
- kSafePortableBasenames[i]))) << kSafePortableBasenames[i];
- }
- for (size_t i = 0 ; i < arraysize(kUnsafePortableBasenames); ++i) {
- EXPECT_FALSE(IsSafePortablePathComponent(base::FilePath(
- kUnsafePortableBasenames[i]))) << kUnsafePortableBasenames[i];
- }
- for (size_t i = 0 ; i < arraysize(kSafePortableRelativePaths); ++i) {
- EXPECT_FALSE(IsSafePortablePathComponent(base::FilePath(
- kSafePortableRelativePaths[i]))) << kSafePortableRelativePaths[i];
- }
-}
-
-TEST(NetUtilTest, IsSafePortableRelativePath) {
- base::FilePath safe_dirname(FILE_PATH_LITERAL("a"));
- for (size_t i = 0 ; i < arraysize(kSafePortableBasenames); ++i) {
- EXPECT_TRUE(IsSafePortableRelativePath(base::FilePath(
- kSafePortableBasenames[i]))) << kSafePortableBasenames[i];
- EXPECT_TRUE(IsSafePortableRelativePath(safe_dirname.Append(base::FilePath(
- kSafePortableBasenames[i])))) << kSafePortableBasenames[i];
- }
- for (size_t i = 0 ; i < arraysize(kSafePortableRelativePaths); ++i) {
- EXPECT_TRUE(IsSafePortableRelativePath(base::FilePath(
- kSafePortableRelativePaths[i]))) << kSafePortableRelativePaths[i];
- EXPECT_TRUE(IsSafePortableRelativePath(safe_dirname.Append(base::FilePath(
- kSafePortableRelativePaths[i])))) << kSafePortableRelativePaths[i];
- }
- for (size_t i = 0 ; i < arraysize(kUnsafePortableBasenames); ++i) {
- EXPECT_FALSE(IsSafePortableRelativePath(base::FilePath(
- kUnsafePortableBasenames[i]))) << kUnsafePortableBasenames[i];
- if (!base::FilePath::StringType(kUnsafePortableBasenames[i]).empty()) {
- EXPECT_FALSE(IsSafePortableRelativePath(safe_dirname.Append(
- base::FilePath(kUnsafePortableBasenames[i]))))
- << kUnsafePortableBasenames[i];
- }
- }
-}
-
struct NonUniqueNameTestData {
bool is_unique;
const char* hostname;
diff --git a/chromium/net/base/net_util_win.cc b/chromium/net/base/net_util_win.cc
index d0efe392dc2..cac92e39d54 100644
--- a/chromium/net/base/net_util_win.cc
+++ b/chromium/net/base/net_util_win.cc
@@ -24,6 +24,8 @@
#include "net/base/net_errors.h"
#include "url/gurl.h"
+namespace net {
+
namespace {
struct WlanApi {
@@ -81,64 +83,28 @@ struct WlanApi {
bool initialized;
};
-} // namespace
-
-namespace net {
-
-bool FileURLToFilePath(const GURL& url, base::FilePath* file_path) {
- *file_path = base::FilePath();
- std::wstring& file_path_str = const_cast<std::wstring&>(file_path->value());
- file_path_str.clear();
-
- if (!url.is_valid())
- return false;
-
- std::string path;
- std::string host = url.host();
- if (host.empty()) {
- // URL contains no host, the path is the filename. In this case, the path
- // will probably be preceeded with a slash, as in "/C:/foo.txt", so we
- // trim out that here.
- path = url.path();
- size_t first_non_slash = path.find_first_not_of("/\\");
- if (first_non_slash != std::string::npos && first_non_slash > 0)
- path.erase(0, first_non_slash);
- } else {
- // URL contains a host: this means it's UNC. We keep the preceeding slash
- // on the path.
- path = "\\\\";
- path.append(host);
- path.append(url.path());
+// Converts Windows defined types to NetworkInterfaceType.
+NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(DWORD ifType) {
+ // Bail out for pre-Vista versions of Windows which are documented to give
+ // inaccurate results like returning Ethernet for WiFi.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366058.aspx
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return NetworkChangeNotifier::CONNECTION_UNKNOWN;
+
+ NetworkChangeNotifier::ConnectionType type =
+ NetworkChangeNotifier::CONNECTION_UNKNOWN;
+ if (ifType == IF_TYPE_ETHERNET_CSMACD) {
+ type = NetworkChangeNotifier::CONNECTION_ETHERNET;
+ } else if (ifType == IF_TYPE_IEEE80211) {
+ type = NetworkChangeNotifier::CONNECTION_WIFI;
}
-
- if (path.empty())
- return false;
- std::replace(path.begin(), path.end(), '/', '\\');
-
- // GURL stores strings as percent-encoded UTF-8, this will undo if possible.
- path = UnescapeURLComponent(path,
- UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
-
- if (!IsStringUTF8(path)) {
- // Not UTF-8, assume encoding is native codepage and we're done. We know we
- // are giving the conversion function a nonempty string, and it may fail if
- // the given string is not in the current encoding and give us an empty
- // string back. We detect this and report failure.
- file_path_str = base::SysNativeMBToWide(path);
- return !file_path_str.empty();
- }
- file_path_str.assign(UTF8ToWide(path));
-
- // We used to try too hard and see if |path| made up entirely of
- // the 1st 256 characters in the Unicode was a zero-extended UTF-16.
- // If so, we converted it to 'Latin-1' and checked if the result was UTF-8.
- // If the check passed, we converted the result to UTF-8.
- // Otherwise, we treated the result as the native OS encoding.
- // However, that led to http://crbug.com/4619 and http://crbug.com/14153
- return true;
+ // TODO(mallinath) - Cellular?
+ return type;
}
-bool GetNetworkList(NetworkInterfaceList* networks) {
+} // namespace
+
+bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
// GetAdaptersAddresses() may require IO operations.
base::ThreadRestrictions::AssertIOAllowed();
bool is_xp = base::win::GetVersion() < base::win::VERSION_VISTA;
@@ -159,6 +125,11 @@ bool GetNetworkList(NetworkInterfaceList* networks) {
return false;
}
+ // These two variables are used below when this method is asked to pick a
+ // IPv6 address which has the shortest lifetime.
+ ULONG ipv6_valid_lifetime = 0;
+ scoped_ptr<NetworkInterface> ipv6_address;
+
for (IP_ADAPTER_ADDRESSES *adapter = adapters; adapter != NULL;
adapter = adapter->Next) {
// Ignore the loopback device.
@@ -170,7 +141,14 @@ bool GetNetworkList(NetworkInterfaceList* networks) {
continue;
}
- std::string name = adapter->AdapterName;
+ // Ignore any HOST side vmware adapters with a description like:
+ // VMware Virtual Ethernet Adapter for VMnet1
+ // but don't ignore any GUEST side adapters with a description like:
+ // VMware Accelerated AMD PCNet Adapter #2
+ if (policy == EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES &&
+ strstr(adapter->AdapterName, "VMnet") != NULL) {
+ continue;
+ }
for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
address; address = address->Next) {
@@ -202,14 +180,38 @@ bool GetNetworkList(NetworkInterfaceList* networks) {
}
uint32 index =
(family == AF_INET) ? adapter->IfIndex : adapter->Ipv6IfIndex;
+ // Pick one IPv6 address with least valid lifetime.
+ // The reason we are checking |ValidLifeftime| as there is no other
+ // way identifying the interface type. Usually (and most likely) temp
+ // IPv6 will have a shorter ValidLifetime value then the permanent
+ // interface.
+ if (family == AF_INET6 &&
+ (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE)) {
+ if (ipv6_valid_lifetime == 0 ||
+ ipv6_valid_lifetime > address->ValidLifetime) {
+ ipv6_valid_lifetime = address->ValidLifetime;
+ ipv6_address.reset(new NetworkInterface(adapter->AdapterName,
+ base::SysWideToNativeMB(adapter->FriendlyName),
+ index,
+ GetNetworkInterfaceType(adapter->IfType),
+ endpoint.address(),
+ net_prefix));
+ continue;
+ }
+ }
networks->push_back(
- NetworkInterface(adapter->AdapterName, index, endpoint.address(),
- net_prefix));
+ NetworkInterface(adapter->AdapterName,
+ base::SysWideToNativeMB(adapter->FriendlyName),
+ index, GetNetworkInterfaceType(adapter->IfType),
+ endpoint.address(), net_prefix));
}
}
}
}
+ if (ipv6_address.get()) {
+ networks->push_back(*(ipv6_address.get()));
+ }
return true;
}
diff --git a/chromium/net/base/network_change_notifier.cc b/chromium/net/base/network_change_notifier.cc
index fad9440c5bf..c55be07d731 100644
--- a/chromium/net/base/network_change_notifier.cc
+++ b/chromium/net/base/network_change_notifier.cc
@@ -6,6 +6,7 @@
#include "base/metrics/histogram.h"
#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier_factory.h"
@@ -13,6 +14,12 @@
#include "net/url_request/url_request.h"
#include "url/gurl.h"
+#if defined(OS_ANDROID)
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/android/network_library.h"
+#endif
+
#if defined(OS_WIN)
#include "net/base/network_change_notifier_win.h"
#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -66,16 +73,26 @@ class HistogramWatcher
// because the only other interface, |NotifyDataReceived| is also
// only called from the network thread.
void Init() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(g_network_change_notifier);
NetworkChangeNotifier::AddConnectionTypeObserver(this);
NetworkChangeNotifier::AddIPAddressObserver(this);
NetworkChangeNotifier::AddDNSObserver(this);
NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
- virtual ~HistogramWatcher() {}
+ virtual ~HistogramWatcher() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(g_network_change_notifier);
+ NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ NetworkChangeNotifier::RemoveDNSObserver(this);
+ NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+ }
// NetworkChangeNotifier::IPAddressObserver implementation.
virtual void OnIPAddressChanged() OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_MEDIUM_TIMES("NCN.IPAddressChange",
SinceLast(&last_ip_address_change_));
UMA_HISTOGRAM_MEDIUM_TIMES(
@@ -86,6 +103,7 @@ class HistogramWatcher
// NetworkChangeNotifier::ConnectionTypeObserver implementation.
virtual void OnConnectionTypeChanged(
NetworkChangeNotifier::ConnectionType type) OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
base::TimeTicks now = base::TimeTicks::Now();
int32 kilobytes_read = bytes_read_since_last_connection_change_ / 1000;
base::TimeDelta state_duration = SinceLast(&last_connection_change_);
@@ -133,6 +151,11 @@ class HistogramWatcher
UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnNone",
fastest_RTT_since_last_connection_change_);
break;
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnBluetooth",
+ first_byte_after_connection_change_);
+ UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnBluetooth",
+ fastest_RTT_since_last_connection_change_);
}
}
if (peak_kbps_since_last_connection_change_) {
@@ -165,6 +188,10 @@ class HistogramWatcher
UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnNone",
peak_kbps_since_last_connection_change_);
break;
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnBluetooth",
+ peak_kbps_since_last_connection_change_);
+ break;
}
}
switch (last_connection_type_) {
@@ -196,6 +223,10 @@ class HistogramWatcher
UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnNone", state_duration);
UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnNone", kilobytes_read);
break;
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnBluetooth", state_duration);
+ UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnBluetooth", kilobytes_read);
+ break;
}
if (type != NetworkChangeNotifier::CONNECTION_NONE) {
@@ -216,6 +247,9 @@ class HistogramWatcher
} else {
UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineChange", state_duration);
}
+
+ NetworkChangeNotifier::LogOperatorCodeHistogram(type);
+
UMA_HISTOGRAM_MEDIUM_TIMES(
"NCN.IPAddressChangeToConnectionTypeChange",
now - last_ip_address_change_);
@@ -229,6 +263,7 @@ class HistogramWatcher
// NetworkChangeNotifier::DNSObserver implementation.
virtual void OnDNSChanged() OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_MEDIUM_TIMES("NCN.DNSConfigChange",
SinceLast(&last_dns_change_));
}
@@ -236,6 +271,7 @@ class HistogramWatcher
// NetworkChangeNotifier::NetworkChangeObserver implementation.
virtual void OnNetworkChanged(
NetworkChangeNotifier::ConnectionType type) OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (type != NetworkChangeNotifier::CONNECTION_NONE) {
UMA_HISTOGRAM_MEDIUM_TIMES("NCN.NetworkOnlineChange",
SinceLast(&last_network_change_));
@@ -248,6 +284,7 @@ class HistogramWatcher
// Record histogram data whenever we receive a packet. Should only be called
// from the network thread.
void NotifyDataReceived(const URLRequest& request, int bytes_read) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (IsLocalhost(request.url().host()) ||
!request.url().SchemeIsHTTPOrHTTPS()) {
return;
@@ -337,6 +374,8 @@ class HistogramWatcher
// Erring on the conservative side is hopefully offset by taking the maximum.
int32 peak_kbps_since_last_connection_change_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(HistogramWatcher);
};
@@ -377,17 +416,22 @@ class NetworkChangeNotifier::NetworkChangeCalculator
pending_connection_type_(CONNECTION_NONE) {}
void Init() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(g_network_change_notifier);
AddConnectionTypeObserver(this);
AddIPAddressObserver(this);
}
virtual ~NetworkChangeCalculator() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(g_network_change_notifier);
RemoveConnectionTypeObserver(this);
RemoveIPAddressObserver(this);
}
// NetworkChangeNotifier::IPAddressObserver implementation.
virtual void OnIPAddressChanged() OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE
? params_.ip_address_offline_delay_ : params_.ip_address_online_delay_;
// Cancels any previous timer.
@@ -396,6 +440,7 @@ class NetworkChangeNotifier::NetworkChangeCalculator
// NetworkChangeNotifier::ConnectionTypeObserver implementation.
virtual void OnConnectionTypeChanged(ConnectionType type) OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
pending_connection_type_ = type;
base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE
? params_.connection_type_offline_delay_
@@ -406,6 +451,7 @@ class NetworkChangeNotifier::NetworkChangeCalculator
private:
void Notify() {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Don't bother signaling about dead connections.
if (have_announced_ &&
(last_announced_connection_type_ == CONNECTION_NONE) &&
@@ -432,9 +478,14 @@ class NetworkChangeNotifier::NetworkChangeCalculator
ConnectionType pending_connection_type_;
// Used to delay notifications so duplicates can be combined.
base::OneShotTimer<NetworkChangeCalculator> timer_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeCalculator);
};
NetworkChangeNotifier::~NetworkChangeNotifier() {
+ network_change_calculator_.reset();
DCHECK_EQ(this, g_network_change_notifier);
g_network_change_notifier = NULL;
}
@@ -502,13 +553,14 @@ const char* NetworkChangeNotifier::ConnectionTypeToString(
"CONNECTION_2G",
"CONNECTION_3G",
"CONNECTION_4G",
- "CONNECTION_NONE"
+ "CONNECTION_NONE",
+ "CONNECTION_BLUETOOTH"
};
COMPILE_ASSERT(
arraysize(kConnectionTypeNames) ==
- NetworkChangeNotifier::CONNECTION_NONE + 1,
+ NetworkChangeNotifier::CONNECTION_LAST + 1,
ConnectionType_name_count_mismatch);
- if (type < CONNECTION_UNKNOWN || type > CONNECTION_NONE) {
+ if (type < CONNECTION_UNKNOWN || type > CONNECTION_LAST) {
NOTREACHED();
return "CONNECTION_INVALID";
}
@@ -518,8 +570,10 @@ const char* NetworkChangeNotifier::ConnectionTypeToString(
// static
void NetworkChangeNotifier::NotifyDataReceived(const URLRequest& request,
int bytes_read) {
- if (!g_network_change_notifier)
+ if (!g_network_change_notifier ||
+ !g_network_change_notifier->histogram_watcher_) {
return;
+ }
g_network_change_notifier->histogram_watcher_->NotifyDataReceived(request,
bytes_read);
}
@@ -528,9 +582,36 @@ void NetworkChangeNotifier::NotifyDataReceived(const URLRequest& request,
void NetworkChangeNotifier::InitHistogramWatcher() {
if (!g_network_change_notifier)
return;
+ g_network_change_notifier->histogram_watcher_.reset(new HistogramWatcher());
g_network_change_notifier->histogram_watcher_->Init();
}
+// static
+void NetworkChangeNotifier::ShutdownHistogramWatcher() {
+ if (!g_network_change_notifier)
+ return;
+ g_network_change_notifier->histogram_watcher_.reset();
+}
+
+// static
+void NetworkChangeNotifier::LogOperatorCodeHistogram(ConnectionType type) {
+#if defined(OS_ANDROID)
+ // On a connection type change to 2/3/4G, log the network operator MCC/MNC.
+ // Log zero in other cases.
+ unsigned mcc_mnc = 0;
+ if (type == NetworkChangeNotifier::CONNECTION_2G ||
+ type == NetworkChangeNotifier::CONNECTION_3G ||
+ type == NetworkChangeNotifier::CONNECTION_4G) {
+ // Log zero if not perfectly converted.
+ if (!base::StringToUint(
+ net::android::GetTelephonyNetworkOperator(), &mcc_mnc)) {
+ mcc_mnc = 0;
+ }
+ }
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NCN.NetworkOperatorMCCMNC", mcc_mnc);
+#endif
+}
+
#if defined(OS_LINUX)
// static
const internal::AddressTrackerLinux*
@@ -558,6 +639,7 @@ bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type) {
case CONNECTION_ETHERNET:
case CONNECTION_WIFI:
case CONNECTION_NONE:
+ case CONNECTION_BLUETOOTH:
is_cellular = false;
break;
}
@@ -644,7 +726,6 @@ NetworkChangeNotifier::NetworkChangeNotifier(
new ObserverListThreadSafe<NetworkChangeObserver>(
ObserverListBase<NetworkChangeObserver>::NOTIFY_EXISTING_ONLY)),
network_state_(new NetworkState()),
- histogram_watcher_(new HistogramWatcher()),
network_change_calculator_(new NetworkChangeCalculator(params)) {
DCHECK(!g_network_change_notifier);
g_network_change_notifier = this;
diff --git a/chromium/net/base/network_change_notifier.h b/chromium/net/base/network_change_notifier.h
index 2ac6f578109..6b801bc7c42 100644
--- a/chromium/net/base/network_change_notifier.h
+++ b/chromium/net/base/network_change_notifier.h
@@ -32,16 +32,19 @@ class AddressTrackerLinux;
// destroyed on the same thread.
class NET_EXPORT NetworkChangeNotifier {
public:
- // Using the terminology of the Network Information API:
- // http://www.w3.org/TR/netinfo-api.
+ // This is a superset of the connection types in the NetInfo v3 specification:
+ // http://w3c.github.io/netinfo/.
enum ConnectionType {
- CONNECTION_UNKNOWN = 0, // A connection exists, but its type is unknown.
+ CONNECTION_UNKNOWN = 0, // A connection exists, but its type is unknown.
+ // Also used as a default value.
CONNECTION_ETHERNET = 1,
CONNECTION_WIFI = 2,
CONNECTION_2G = 3,
CONNECTION_3G = 4,
CONNECTION_4G = 5,
- CONNECTION_NONE = 6 // No connection.
+ CONNECTION_NONE = 6, // No connection.
+ CONNECTION_BLUETOOTH = 7,
+ CONNECTION_LAST = CONNECTION_BLUETOOTH
};
class NET_EXPORT IPAddressObserver {
@@ -224,8 +227,17 @@ class NET_EXPORT NetworkChangeNotifier {
// Register the Observer callbacks for producing histogram data. This
// should be called from the network thread to avoid race conditions.
+ // ShutdownHistogramWatcher() must be called prior to NetworkChangeNotifier
+ // destruction.
static void InitHistogramWatcher();
+ // Unregister the Observer callbacks for producing histogram data. This
+ // should be called from the network thread to avoid race conditions.
+ static void ShutdownHistogramWatcher();
+
+ // Log the |NCN.NetworkOperatorMCCMNC| histogram.
+ static void LogOperatorCodeHistogram(ConnectionType type);
+
// Allows a second NetworkChangeNotifier to be created for unit testing, so
// the test suite can create a MockNetworkChangeNotifier, but platform
// specific NetworkChangeNotifiers can also be created for testing. To use,
diff --git a/chromium/net/base/network_change_notifier_linux.cc b/chromium/net/base/network_change_notifier_linux.cc
index 19da249679a..3fe96562c04 100644
--- a/chromium/net/base/network_change_notifier_linux.cc
+++ b/chromium/net/base/network_change_notifier_linux.cc
@@ -46,7 +46,8 @@ NetworkChangeNotifierLinux::Thread::Thread()
base::Bind(&NetworkChangeNotifier::
NotifyObserversOfIPAddressChange),
base::Bind(&NetworkChangeNotifier::
- NotifyObserversOfConnectionTypeChange)) {
+ NotifyObserversOfConnectionTypeChange),
+ base::Bind(base::DoNothing)) {
}
NetworkChangeNotifierLinux::Thread::~Thread() {
diff --git a/chromium/net/base/network_change_notifier_mac.cc b/chromium/net/base/network_change_notifier_mac.cc
index d75f0f8cae3..7fa97b48b61 100644
--- a/chromium/net/base/network_change_notifier_mac.cc
+++ b/chromium/net/base/network_change_notifier_mac.cc
@@ -265,8 +265,7 @@ void NetworkChangeNotifierMac::ReachabilityCallback(
#if defined(OS_IOS)
// On iOS, the SCDynamicStore API does not exist, and we use the reachability
// API to detect IP address changes instead.
- if (new_type != CONNECTION_NONE)
- NotifyObserversOfIPAddressChange();
+ NotifyObserversOfIPAddressChange();
#endif // defined(OS_IOS)
}
diff --git a/chromium/net/base/network_change_notifier_win.cc b/chromium/net/base/network_change_notifier_win.cc
index 569793fee6e..77a72b0d693 100644
--- a/chromium/net/base/network_change_notifier_win.cc
+++ b/chromium/net/base/network_change_notifier_win.cc
@@ -62,8 +62,6 @@ NetworkChangeNotifierWin::NetworkChangeNotifierWin()
last_computed_connection_type_ == CONNECTION_NONE) {
memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
addr_overlapped_.hEvent = WSACreateEvent();
- dns_config_service_thread_->StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
}
NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
@@ -291,6 +289,12 @@ void NetworkChangeNotifierWin::WatchForAddressChange() {
bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() {
DCHECK(CalledOnValidThread());
+
+ if (!dns_config_service_thread_->IsRunning()) {
+ dns_config_service_thread_->StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+ }
+
HANDLE handle = NULL;
DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
if (ret != ERROR_IO_PENDING)
diff --git a/chromium/net/base/network_change_notifier_win.h b/chromium/net/base/network_change_notifier_win.h
index 177bad2f7f7..7b75c15dee3 100644
--- a/chromium/net/base/network_change_notifier_win.h
+++ b/chromium/net/base/network_change_notifier_win.h
@@ -59,7 +59,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
// Does the actual work to determine the current connection type.
// It is not thread safe, see crbug.com/324913.
- ConnectionType RecomputeCurrentConnectionType() const;
+ virtual ConnectionType RecomputeCurrentConnectionType() const;
void SetCurrentConnectionType(ConnectionType connection_type);
diff --git a/chromium/net/base/network_change_notifier_win_unittest.cc b/chromium/net/base/network_change_notifier_win_unittest.cc
index 96dcd13e587..979105f18b2 100644
--- a/chromium/net/base/network_change_notifier_win_unittest.cc
+++ b/chromium/net/base/network_change_notifier_win_unittest.cc
@@ -30,9 +30,9 @@ class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin {
set_is_watching(false);
}
- // From NetworkChangeNotifier.
+ // From NetworkChangeNotifierWin.
virtual NetworkChangeNotifier::ConnectionType
- GetCurrentConnectionType() const OVERRIDE {
+ RecomputeCurrentConnectionType() const OVERRIDE {
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
diff --git a/chromium/net/base/network_delegate.cc b/chromium/net/base/network_delegate.cc
index 834769c84e1..d30a4ab4c88 100644
--- a/chromium/net/base/network_delegate.cc
+++ b/chromium/net/base/network_delegate.cc
@@ -39,12 +39,16 @@ int NetworkDelegate::NotifyHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
DCHECK(CalledOnValidThread());
DCHECK(original_response_headers);
DCHECK(!callback.is_null());
- return OnHeadersReceived(request, callback, original_response_headers,
- override_response_headers);
+ return OnHeadersReceived(request,
+ callback,
+ original_response_headers,
+ override_response_headers,
+ allowed_unsafe_redirect_url);
}
void NetworkDelegate::NotifyResponseStarted(URLRequest* request) {
@@ -102,12 +106,6 @@ int NetworkDelegate::NotifyBeforeSocketStreamConnect(
return OnBeforeSocketStreamConnect(socket, callback);
}
-void NetworkDelegate::NotifyRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state) {
- DCHECK(CalledOnValidThread());
- OnRequestWaitStateChange(request, state);
-}
-
bool NetworkDelegate::CanGetCookies(const URLRequest& request,
const CookieList& cookie_list) {
DCHECK(CalledOnValidThread());
@@ -161,7 +159,8 @@ int NetworkDelegate::OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
return OK;
}
@@ -226,8 +225,4 @@ int NetworkDelegate::OnBeforeSocketStreamConnect(
return OK;
}
-void NetworkDelegate::OnRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state) {
-}
-
} // namespace net
diff --git a/chromium/net/base/network_delegate.h b/chromium/net/base/network_delegate.h
index 21c8e6530f6..4be320b9305 100644
--- a/chromium/net/base/network_delegate.h
+++ b/chromium/net/base/network_delegate.h
@@ -51,14 +51,6 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
};
typedef base::Callback<void(AuthRequiredResponse)> AuthCallback;
- enum RequestWaitState {
- REQUEST_WAIT_STATE_CACHE_START,
- REQUEST_WAIT_STATE_CACHE_FINISH,
- REQUEST_WAIT_STATE_NETWORK_START,
- REQUEST_WAIT_STATE_NETWORK_FINISH,
- REQUEST_WAIT_STATE_RESET
- };
-
virtual ~NetworkDelegate() {}
// Notification interface called by the network stack. Note that these
@@ -77,7 +69,8 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers);
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url);
void NotifyBeforeRedirect(URLRequest* request,
const GURL& new_location);
void NotifyResponseStarted(URLRequest* request);
@@ -103,21 +96,21 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
int NotifyBeforeSocketStreamConnect(SocketStream* socket,
const CompletionCallback& callback);
- void NotifyRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state);
-
private:
// This is the interface for subclasses of NetworkDelegate to implement. These
// member functions will be called by the respective public notification
// member function, which will perform basic sanity checking.
// Called before a request is sent. Allows the delegate to rewrite the URL
- // being fetched by modifying |new_url|. |callback| and |new_url| are valid
- // only until OnURLRequestDestroyed is called for this request. Returns a net
- // status code, generally either OK to continue with the request or
- // ERR_IO_PENDING if the result is not ready yet. A status code other than OK
- // and ERR_IO_PENDING will cancel the request and report the status code as
- // the reason.
+ // being fetched by modifying |new_url|. If set, the URL must be valid. The
+ // reference fragment from the original URL is not automatically appended to
+ // |new_url|; callers are responsible for copying the reference fragment if
+ // desired.
+ // |callback| and |new_url| are valid only until OnURLRequestDestroyed is
+ // called for this request. Returns a net status code, generally either OK to
+ // continue with the request or ERR_IO_PENDING if the result is not ready yet.
+ // A status code other than OK and ERR_IO_PENDING will cancel the request and
+ // report the status code as the reason.
//
// The default implementation returns OK (continue with request).
virtual int OnBeforeURLRequest(URLRequest* request,
@@ -144,6 +137,11 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
// network, these must not be modified. |override_response_headers| can be set
// to new values, that should be considered as overriding
// |original_response_headers|.
+ // If the response is a redirect, and the Location response header value is
+ // identical to |allowed_unsafe_redirect_url|, then the redirect is never
+ // blocked and the reference fragment is not copied from the original URL
+ // to the redirection target.
+ //
// |callback|, |original_response_headers|, and |override_response_headers|
// are only valid until OnURLRequestDestroyed is called for this request.
// See OnBeforeURLRequest for return value description. Returns OK by default.
@@ -151,7 +149,8 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers);
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url);
// Called right after a redirect response code was received.
// |new_location| is only valid until OnURLRequestDestroyed is called for this
@@ -237,13 +236,6 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe {
// See OnBeforeURLRequest for return value description. Returns OK by default.
virtual int OnBeforeSocketStreamConnect(
SocketStream* socket, const CompletionCallback& callback);
-
- // Called when the completion of a URLRequest is blocking on a cache
- // action or a network action, or when that is no longer the case.
- // REQUEST_WAIT_STATE_RESET indicates for a given URLRequest
- // cancellation of any pending waits for this request.
- virtual void OnRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state);
};
} // namespace net
diff --git a/chromium/net/base/network_time_notifier.cc b/chromium/net/base/network_time_notifier.cc
deleted file mode 100644
index 6dd0d3dde1e..00000000000
--- a/chromium/net/base/network_time_notifier.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/network_time_notifier.h"
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/i18n/time_formatting.h"
-#include "base/location.h"
-#include "base/message_loop/message_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-
-namespace {
-
-// Clock resolution is platform dependent.
-#if defined(OS_WIN)
-const int64 kTicksResolutionMs = base::Time::kMinLowResolutionThresholdMs;
-#else
-const int64 kTicksResolutionMs = 1; // Assume 1ms for non-windows platforms.
-#endif
-
-// Number of time measurements performed in a given network time calculation.
-const int kNumTimeMeasurements = 5;
-
-} // namespace
-
-namespace net {
-
-NetworkTimeNotifier::NetworkTimeNotifier(
- scoped_ptr<base::TickClock> tick_clock) {
- tick_clock_ = tick_clock.Pass();
-}
-
-NetworkTimeNotifier::~NetworkTimeNotifier() {}
-
-void NetworkTimeNotifier::UpdateNetworkTime(const base::Time& network_time,
- const base::TimeDelta& resolution,
- const base::TimeDelta& latency,
- const base::TimeTicks& post_time) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DVLOG(1) << "Network time updating to "
- << UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(network_time));
- // Update network time on every request to limit dependency on ticks lag.
- // TODO(mad): Find a heuristic to avoid augmenting the
- // network_time_uncertainty_ too much by a particularly long latency.
- // Maybe only update when the the new time either improves in accuracy or
- // drifts too far from |network_time_|.
- network_time_ = network_time;
-
- // Calculate the delay since the network time was received.
- base::TimeTicks now = tick_clock_->NowTicks();
- base::TimeDelta task_delay = now - post_time;
- // Estimate that the time was set midway through the latency time.
- network_time_ticks_ = now - task_delay - latency / 2;
-
- // Can't assume a better time than the resolution of the given time
- // and 5 ticks measurements are involved, each with their own uncertainty.
- // 1 & 2 are the ones used to compute the latency, 3 is the Now() from when
- // this task was posted, 4 is the Now() above and 5 will be the Now() used in
- // GetNetworkTime().
- network_time_uncertainty_ =
- resolution + latency + kNumTimeMeasurements *
- base::TimeDelta::FromMilliseconds(kTicksResolutionMs);
-
- for (size_t i = 0; i < observers_.size(); ++i) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(observers_[i],
- network_time_,
- network_time_ticks_,
- network_time_uncertainty_));
- }
-}
-
-void NetworkTimeNotifier::AddObserver(
- const ObserverCallback& observer_callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- observers_.push_back(observer_callback);
- if (!network_time_.is_null()) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(observer_callback,
- network_time_,
- network_time_ticks_,
- network_time_uncertainty_));
- }
-}
-
-} // namespace net
diff --git a/chromium/net/base/network_time_notifier.h b/chromium/net/base/network_time_notifier.h
deleted file mode 100644
index 62e234863a0..00000000000
--- a/chromium/net/base/network_time_notifier.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_BASE_NETWORK_TIME_NOTIFIER_H_
-#define NET_BASE_NETWORK_TIME_NOTIFIER_H_
-
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "net/base/net_export.h"
-
-namespace net {
-
-// A class that receives updates for and maintains network time. Network time
-// sources can pass updates through UpdateNetworkTime, and network time
-// consumers can register as observers. This class is not thread-safe.
-class NET_EXPORT NetworkTimeNotifier {
- public:
- // Callback for observers to receive network time updates.
- // The parameters are:
- // const base::Time& network_time - the new network time.
- // const base::TimeTicks& network_time_ticks - the ticks time that corresponds
- // with |network_time|.
- // const base::TimeDelta& network_time_uncertainty - the uncertainty
- // associated with the new network time.
- typedef base::Callback<void(const base::Time&,
- const base::TimeTicks&,
- const base::TimeDelta&)> ObserverCallback;
-
- // Takes ownership of |tick_clock|.
- explicit NetworkTimeNotifier(scoped_ptr<base::TickClock> tick_clock);
- ~NetworkTimeNotifier();
-
- // Calculates corresponding time ticks according to the given parameters and
- // notifies observers. The provided |network_time| is precise at the given
- // |resolution| and represent the time between now and up to |latency| +
- // (now - |post_time|) ago.
- void UpdateNetworkTime(const base::Time& network_time,
- const base::TimeDelta& resolution,
- const base::TimeDelta& latency,
- const base::TimeTicks& post_time);
-
- // |observer_callback| will invoked every time the network time is updated, or
- // if a network time is already available when AddObserver is called.
- void AddObserver(const ObserverCallback& observer_callback);
-
- private:
- base::ThreadChecker thread_checker_;
-
- // For querying current time ticks.
- scoped_ptr<base::TickClock> tick_clock_;
-
- // The network time based on last call to UpdateNetworkTime().
- base::Time network_time_;
-
- // The estimated local time from |tick_clock| that corresponds with
- // |network_time|. Assumes the actual network time measurement was performed
- // midway through the latency time, and does not account for suspect/resume
- // events since the network time was measured.
- // See UpdateNetworkTime(..) implementation for details.
- base::TimeTicks network_time_ticks_;
-
- // Uncertainty of |network_time_| based on added inaccuracies/resolution.
- // See UpdateNetworkTime(..) implementation for details.
- base::TimeDelta network_time_uncertainty_;
-
- // List of network time update observers.
- // A vector of callbacks is used, rather than an ObserverList, so that the
- // lifetime of the observer can be bound to the callback.
- std::vector<ObserverCallback> observers_;
-
- DISALLOW_COPY_AND_ASSIGN(NetworkTimeNotifier);
-};
-
-} // namespace net
-
-#endif // NET_BASE_NETWORK_TIME_NOTIFIER_H_
diff --git a/chromium/net/base/nss_memio.c b/chromium/net/base/nss_memio.c
index 51012e608c4..57054033774 100644
--- a/chromium/net/base/nss_memio.c
+++ b/chromium/net/base/nss_memio.c
@@ -100,6 +100,7 @@ static void memio_buffer_destroy(struct memio_buffer *mb)
{
free(mb->buf);
mb->buf = NULL;
+ mb->bufsize = 0;
mb->head = 0;
mb->tail = 0;
}
@@ -441,17 +442,21 @@ void memio_PutReadResult(memio_Private *secret, int bytes_read)
}
}
-void memio_GetWriteParams(memio_Private *secret,
- const char **buf1, unsigned int *len1,
- const char **buf2, unsigned int *len2)
+int memio_GetWriteParams(memio_Private *secret,
+ const char **buf1, unsigned int *len1,
+ const char **buf2, unsigned int *len2)
{
struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
PR_ASSERT(mb->bufsize);
+ if (mb->last_err)
+ return mb->last_err;
+
*buf1 = &mb->buf[mb->head];
*len1 = memio_buffer_used_contiguous(mb);
*buf2 = mb->buf;
*len2 = memio_buffer_wrapped_bytes(mb);
+ return 0;
}
void memio_PutWriteResult(memio_Private *secret, int bytes_written)
diff --git a/chromium/net/base/nss_memio.h b/chromium/net/base/nss_memio.h
index 8481d15e7a7..b2b873bdca3 100644
--- a/chromium/net/base/nss_memio.h
+++ b/chromium/net/base/nss_memio.h
@@ -79,12 +79,13 @@ int memio_GetReadableBufferSize(memio_Private *secret);
void memio_PutReadResult(memio_Private *secret, int bytes_read);
/* Ask memio what data it has to send to the network.
- * Returns up to two buffers of data by writing the positions and lengths into
- * |buf1|, |len1| and |buf2|, |len2|.
+ * If there was previous a write error, the NSPR error code is returned.
+ * Otherwise, it returns 0 and provides up to two buffers of data by
+ * writing the positions and lengths into |buf1|, |len1| and |buf2|, |len2|.
*/
-void memio_GetWriteParams(memio_Private *secret,
- const char **buf1, unsigned int *len1,
- const char **buf2, unsigned int *len2);
+int memio_GetWriteParams(memio_Private *secret,
+ const char **buf1, unsigned int *len1,
+ const char **buf2, unsigned int *len2);
/* Tell memio how many bytes were sent to the network.
* If bytes_written is < 0, it is treated as an NSPR error code.
diff --git a/chromium/net/base/platform_mime_util_win.cc b/chromium/net/base/platform_mime_util_win.cc
index 421ddba1077..fcac48faab5 100644
--- a/chromium/net/base/platform_mime_util_win.cc
+++ b/chromium/net/base/platform_mime_util_win.cc
@@ -19,7 +19,7 @@ bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
base::win::RegKey(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ).ReadValue(
L"Content Type", &value);
if (!value.empty()) {
- *result = WideToUTF8(value);
+ *result = base::WideToUTF8(value);
return true;
}
return false;
@@ -27,7 +27,8 @@ bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
bool PlatformMimeUtil::GetPreferredExtensionForMimeType(
const std::string& mime_type, base::FilePath::StringType* ext) const {
- std::wstring key(L"MIME\\Database\\Content Type\\" + UTF8ToWide(mime_type));
+ std::wstring key(
+ L"MIME\\Database\\Content Type\\" + base::UTF8ToWide(mime_type));
if (base::win::RegKey(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ).ReadValue(
L"Extension", ext) != ERROR_SUCCESS) {
return false;
diff --git a/chromium/net/base/privacy_mode.h b/chromium/net/base/privacy_mode.h
index 082ef26fb2f..4d455cfb866 100644
--- a/chromium/net/base/privacy_mode.h
+++ b/chromium/net/base/privacy_mode.h
@@ -8,13 +8,12 @@
namespace net {
// Privacy Mode is enabled if cookies to particular site are blocked, so
-// Channel ID is disabled on that connection (https or spdy).
+// Channel ID is disabled on that connection (https, spdy or quic).
enum PrivacyMode {
- kPrivacyModeDisabled = 0,
- kPrivacyModeEnabled = 1,
+ PRIVACY_MODE_DISABLED = 0,
+ PRIVACY_MODE_ENABLED = 1,
};
}; // namespace net
#endif // NET_BASE_PRIVACY_MODE_H_
-
diff --git a/chromium/net/base/registry_controlled_domains/BUILD.gn b/chromium/net/base/registry_controlled_domains/BUILD.gn
new file mode 100644
index 00000000000..89f4d4640b7
--- /dev/null
+++ b/chromium/net/base/registry_controlled_domains/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright (c) 2014 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.
+
+action_foreach("registry_controlled_domains") {
+ script = "//net/tools/tld_cleanup/make_dafsa.py"
+ sources = [
+ "effective_tld_names.gperf",
+ "effective_tld_names_unittest1.gperf",
+ "effective_tld_names_unittest2.gperf",
+ "effective_tld_names_unittest3.gperf",
+ "effective_tld_names_unittest4.gperf",
+ "effective_tld_names_unittest5.gperf",
+ "effective_tld_names_unittest6.gperf",
+ ]
+ outputs = [
+ "${target_gen_dir}/{{source_name_part}}-inc.cc"
+ ]
+ args = [
+ "{{source}}",
+ rebase_path("${target_gen_dir}/{{source_name_part}}-inc.cc", root_build_dir)
+ ]
+}
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names.cc b/chromium/net/base/registry_controlled_domains/effective_tld_names.cc
deleted file mode 100644
index 1fa77a702e0..00000000000
--- a/chromium/net/base/registry_controlled_domains/effective_tld_names.cc
+++ /dev/null
@@ -1,47055 +0,0 @@
-/* C++ code produced by gperf version 3.0.3 */
-/* Command-line: gperf -a -L C++ -C -c -o -t -k '*' -NFindDomain -P -K name_offset -D -m 10 effective_tld_names.gperf */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-#line 1 "effective_tld_names.gperf"
-
-// Copyright 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.
-
-// This file is generated by net/tools/tld_cleanup/.
-// DO NOT MANUALLY EDIT!
-#line 9 "effective_tld_names.gperf"
-struct DomainRule {
- int name_offset;
- int type; // flags: 1: exception, 2: wildcard, 4: private
-};
-
-#define TOTAL_KEYWORDS 6375
-#define MIN_WORD_LENGTH 2
-#define MAX_WORD_LENGTH 43
-#define MIN_HASH_VALUE 8
-#define MAX_HASH_VALUE 170276
-/* maximum key range = 170269, duplicates = 0 */
-
-class Perfect_Hash
-{
-private:
- static inline unsigned int hash (const char *str, unsigned int len);
-public:
- static const struct DomainRule *FindDomain (const char *str, unsigned int len);
-};
-
-inline unsigned int
-Perfect_Hash::hash (register const char *str, register unsigned int len)
-{
- static const unsigned int asso_values[] =
- {
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 434, 45, 402, 9, 114,
- 4, 8, 2, 3, 1, 2, 1, 2, 74, 24819,
- 4, 10, 16557, 244, 3, 1, 1, 1, 1, 1,
- 1, 170277, 170277, 1, 25, 16, 6, 15, 12, 11,
- 5, 10, 9, 8, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 906, 394, 26936,
- 8, 242, 28033, 10542, 1450, 450, 671, 1852, 78, 713,
- 191, 4, 1002, 279, 47, 37, 288, 124, 1074, 3503,
- 6105, 585, 10, 58, 102, 13, 29084, 4, 111, 1,
- 3, 2, 42941, 3276, 48382, 480, 9056, 20956, 33679, 14,
- 11166, 13469, 34929, 660, 6669, 1943, 1230, 16306, 334, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277, 170277,
- 170277, 170277
- };
- register int hval = len;
-
- switch (hval)
- {
- default:
- hval += asso_values[(unsigned char)str[42]];
- /*FALLTHROUGH*/
- case 42:
- hval += asso_values[(unsigned char)str[41]];
- /*FALLTHROUGH*/
- case 41:
- hval += asso_values[(unsigned char)str[40]];
- /*FALLTHROUGH*/
- case 40:
- hval += asso_values[(unsigned char)str[39]];
- /*FALLTHROUGH*/
- case 39:
- hval += asso_values[(unsigned char)str[38]];
- /*FALLTHROUGH*/
- case 38:
- hval += asso_values[(unsigned char)str[37]];
- /*FALLTHROUGH*/
- case 37:
- hval += asso_values[(unsigned char)str[36]];
- /*FALLTHROUGH*/
- case 36:
- hval += asso_values[(unsigned char)str[35]];
- /*FALLTHROUGH*/
- case 35:
- hval += asso_values[(unsigned char)str[34]];
- /*FALLTHROUGH*/
- case 34:
- hval += asso_values[(unsigned char)str[33]];
- /*FALLTHROUGH*/
- case 33:
- hval += asso_values[(unsigned char)str[32]];
- /*FALLTHROUGH*/
- case 32:
- hval += asso_values[(unsigned char)str[31]];
- /*FALLTHROUGH*/
- case 31:
- hval += asso_values[(unsigned char)str[30]];
- /*FALLTHROUGH*/
- case 30:
- hval += asso_values[(unsigned char)str[29]];
- /*FALLTHROUGH*/
- case 29:
- hval += asso_values[(unsigned char)str[28]];
- /*FALLTHROUGH*/
- case 28:
- hval += asso_values[(unsigned char)str[27]];
- /*FALLTHROUGH*/
- case 27:
- hval += asso_values[(unsigned char)str[26]];
- /*FALLTHROUGH*/
- case 26:
- hval += asso_values[(unsigned char)str[25]];
- /*FALLTHROUGH*/
- case 25:
- hval += asso_values[(unsigned char)str[24]];
- /*FALLTHROUGH*/
- case 24:
- hval += asso_values[(unsigned char)str[23]];
- /*FALLTHROUGH*/
- case 23:
- hval += asso_values[(unsigned char)str[22]];
- /*FALLTHROUGH*/
- case 22:
- hval += asso_values[(unsigned char)str[21]];
- /*FALLTHROUGH*/
- case 21:
- hval += asso_values[(unsigned char)str[20]];
- /*FALLTHROUGH*/
- case 20:
- hval += asso_values[(unsigned char)str[19]];
- /*FALLTHROUGH*/
- case 19:
- hval += asso_values[(unsigned char)str[18]];
- /*FALLTHROUGH*/
- case 18:
- hval += asso_values[(unsigned char)str[17]];
- /*FALLTHROUGH*/
- case 17:
- hval += asso_values[(unsigned char)str[16]];
- /*FALLTHROUGH*/
- case 16:
- hval += asso_values[(unsigned char)str[15]];
- /*FALLTHROUGH*/
- case 15:
- hval += asso_values[(unsigned char)str[14]];
- /*FALLTHROUGH*/
- case 14:
- hval += asso_values[(unsigned char)str[13]];
- /*FALLTHROUGH*/
- case 13:
- hval += asso_values[(unsigned char)str[12]];
- /*FALLTHROUGH*/
- case 12:
- hval += asso_values[(unsigned char)str[11]+1];
- /*FALLTHROUGH*/
- case 11:
- hval += asso_values[(unsigned char)str[10]];
- /*FALLTHROUGH*/
- case 10:
- hval += asso_values[(unsigned char)str[9]+1];
- /*FALLTHROUGH*/
- case 9:
- hval += asso_values[(unsigned char)str[8]];
- /*FALLTHROUGH*/
- case 8:
- hval += asso_values[(unsigned char)str[7]+1];
- /*FALLTHROUGH*/
- case 7:
- hval += asso_values[(unsigned char)str[6]+3];
- /*FALLTHROUGH*/
- case 6:
- hval += asso_values[(unsigned char)str[5]+16];
- /*FALLTHROUGH*/
- case 5:
- hval += asso_values[(unsigned char)str[4]];
- /*FALLTHROUGH*/
- case 4:
- hval += asso_values[(unsigned char)str[3]+14];
- /*FALLTHROUGH*/
- case 3:
- hval += asso_values[(unsigned char)str[2]+13];
- /*FALLTHROUGH*/
- case 2:
- hval += asso_values[(unsigned char)str[1]];
- /*FALLTHROUGH*/
- case 1:
- hval += asso_values[(unsigned char)str[0]+26];
- break;
- }
- return hval;
-}
-
-struct stringpool_t
- {
- char stringpool_str0[sizeof("io")];
- char stringpool_str1[sizeof("gov")];
- char stringpool_str2[sizeof("gd")];
- char stringpool_str3[sizeof("id")];
- char stringpool_str4[sizeof("edu")];
- char stringpool_str5[sizeof("co")];
- char stringpool_str6[sizeof("gop")];
- char stringpool_str7[sizeof("cd")];
- char stringpool_str8[sizeof("cz")];
- char stringpool_str9[sizeof("gov.do")];
- char stringpool_str10[sizeof("com")];
- char stringpool_str11[sizeof("gob.do")];
- char stringpool_str12[sizeof("gov.om")];
- char stringpool_str13[sizeof("edu.do")];
- char stringpool_str14[sizeof("gov.dm")];
- char stringpool_str15[sizeof("gs")];
- char stringpool_str16[sizeof("is")];
- char stringpool_str17[sizeof("edu.om")];
- char stringpool_str18[sizeof("es")];
- char stringpool_str19[sizeof("edu.dm")];
- char stringpool_str20[sizeof("com.do")];
- char stringpool_str21[sizeof("gr")];
- char stringpool_str22[sizeof("ir")];
- char stringpool_str23[sizeof("hr")];
- char stringpool_str24[sizeof("er")];
- char stringpool_str25[sizeof("com.om")];
- char stringpool_str26[sizeof("com.dm")];
- char stringpool_str27[sizeof("cr")];
- char stringpool_str28[sizeof("ao")];
- char stringpool_str29[sizeof("gov.rs")];
- char stringpool_str30[sizeof("ad")];
- char stringpool_str31[sizeof("az")];
- char stringpool_str32[sizeof("edu.rs")];
- char stringpool_str33[sizeof("com.so")];
- char stringpool_str34[sizeof("gl")];
- char stringpool_str35[sizeof("il")];
- char stringpool_str36[sizeof("com.ro")];
- char stringpool_str37[sizeof("gov.sc")];
- char stringpool_str38[sizeof("cl")];
- char stringpool_str39[sizeof("homes")];
- char stringpool_str40[sizeof("as")];
- char stringpool_str41[sizeof("gov.lr")];
- char stringpool_str42[sizeof("edu.sc")];
- char stringpool_str43[sizeof("gov.sb")];
- char stringpool_str44[sizeof("edu.lr")];
- char stringpool_str45[sizeof("ar")];
- char stringpool_str46[sizeof("bo")];
- char stringpool_str47[sizeof("edu.sb")];
- char stringpool_str48[sizeof("com.sc")];
- char stringpool_str49[sizeof("bd")];
- char stringpool_str50[sizeof("bz")];
- char stringpool_str51[sizeof("fo")];
- char stringpool_str52[sizeof("com.lr")];
- char stringpool_str53[sizeof("com.sb")];
- char stringpool_str54[sizeof("arpa")];
- char stringpool_str55[sizeof("gu")];
- char stringpool_str56[sizeof("art.do")];
- char stringpool_str57[sizeof("hu")];
- char stringpool_str58[sizeof("eu")];
- char stringpool_str59[sizeof("gov.lc")];
- char stringpool_str60[sizeof("cool")];
- char stringpool_str61[sizeof("condos")];
- char stringpool_str62[sizeof("guru")];
- char stringpool_str63[sizeof("al")];
- char stringpool_str64[sizeof("cu")];
- char stringpool_str65[sizeof("edu.lc")];
- char stringpool_str66[sizeof("bs")];
- char stringpool_str67[sizeof("gov.lb")];
- char stringpool_str68[sizeof("edu.lb")];
- char stringpool_str69[sizeof("br")];
- char stringpool_str70[sizeof("com.lc")];
- char stringpool_str71[sizeof("gov.lk")];
- char stringpool_str72[sizeof("gov.sl")];
- char stringpool_str73[sizeof("fr")];
- char stringpool_str74[sizeof("edu.lk")];
- char stringpool_str75[sizeof("com.lb")];
- char stringpool_str76[sizeof("edu.sl")];
- char stringpool_str77[sizeof("com.de")];
- char stringpool_str78[sizeof("com.lk")];
- char stringpool_str79[sizeof("com.sl")];
- char stringpool_str80[sizeof("g.bg")];
- char stringpool_str81[sizeof("i.bg")];
- char stringpool_str82[sizeof("h.bg")];
- char stringpool_str83[sizeof("e.bg")];
- char stringpool_str84[sizeof("6.bg")];
- char stringpool_str85[sizeof("2.bg")];
- char stringpool_str86[sizeof("au")];
- char stringpool_str87[sizeof("9.bg")];
- char stringpool_str88[sizeof("8.bg")];
- char stringpool_str89[sizeof("7.bg")];
- char stringpool_str90[sizeof("5.bg")];
- char stringpool_str91[sizeof("4.bg")];
- char stringpool_str92[sizeof("c.bg")];
- char stringpool_str93[sizeof("q.bg")];
- char stringpool_str94[sizeof("3.bg")];
- char stringpool_str95[sizeof("1.bg")];
- char stringpool_str96[sizeof("gn")];
- char stringpool_str97[sizeof("in")];
- char stringpool_str98[sizeof("hn")];
- char stringpool_str99[sizeof("int")];
- char stringpool_str100[sizeof("g.se")];
- char stringpool_str101[sizeof("i.se")];
- char stringpool_str102[sizeof("h.se")];
- char stringpool_str103[sizeof("e.se")];
- char stringpool_str104[sizeof("0.bg")];
- char stringpool_str105[sizeof("cn")];
- char stringpool_str106[sizeof("grp.lk")];
- char stringpool_str107[sizeof("com.re")];
- char stringpool_str108[sizeof("arts.ro")];
- char stringpool_str109[sizeof("c.se")];
- char stringpool_str110[sizeof("gov.nr")];
- char stringpool_str111[sizeof("edu.nr")];
- char stringpool_str112[sizeof("blue")];
- char stringpool_str113[sizeof("com.nr")];
- char stringpool_str114[sizeof("a.bg")];
- char stringpool_str115[sizeof("autos")];
- char stringpool_str116[sizeof("eus")];
- char stringpool_str117[sizeof("arts.co")];
- char stringpool_str118[sizeof("ge")];
- char stringpool_str119[sizeof("ie")];
- char stringpool_str120[sizeof("info")];
- char stringpool_str121[sizeof("ee")];
- char stringpool_str122[sizeof("hof.no")];
- char stringpool_str123[sizeof("an")];
- char stringpool_str124[sizeof("a.se")];
- char stringpool_str125[sizeof("house")];
- char stringpool_str126[sizeof("horse")];
- char stringpool_str127[sizeof("gob.es")];
- char stringpool_str128[sizeof("b.br")];
- char stringpool_str129[sizeof("edu.es")];
- char stringpool_str130[sizeof("b.bg")];
- char stringpool_str131[sizeof("com.es")];
- char stringpool_str132[sizeof("gq")];
- char stringpool_str133[sizeof("iq")];
- char stringpool_str134[sizeof("f.bg")];
- char stringpool_str135[sizeof("gt")];
- char stringpool_str136[sizeof("it")];
- char stringpool_str137[sizeof("ht")];
- char stringpool_str138[sizeof("et")];
- char stringpool_str139[sizeof("bn")];
- char stringpool_str140[sizeof("gov.ec")];
- char stringpool_str141[sizeof("gob.ec")];
- char stringpool_str142[sizeof("b.se")];
- char stringpool_str143[sizeof("ae")];
- char stringpool_str144[sizeof("edu.ec")];
- char stringpool_str145[sizeof("f.se")];
- char stringpool_str146[sizeof("gov.to")];
- char stringpool_str147[sizeof("glass")];
- char stringpool_str148[sizeof("gov.tj")];
- char stringpool_str149[sizeof("com.ec")];
- char stringpool_str150[sizeof("edu.to")];
- char stringpool_str151[sizeof("gov.tm")];
- char stringpool_str152[sizeof("aero")];
- char stringpool_str153[sizeof("edu.tj")];
- char stringpool_str154[sizeof("edu.tm")];
- char stringpool_str155[sizeof("com.to")];
- char stringpool_str156[sizeof("gov.sa")];
- char stringpool_str157[sizeof("com.tj")];
- char stringpool_str158[sizeof("com.tm")];
- char stringpool_str159[sizeof("aq")];
- char stringpool_str160[sizeof("int.lk")];
- char stringpool_str161[sizeof("edu.sa")];
- char stringpool_str162[sizeof("gov.sd")];
- char stringpool_str163[sizeof("info.ro")];
- char stringpool_str164[sizeof("be")];
- char stringpool_str165[sizeof("at")];
- char stringpool_str166[sizeof("edu.sd")];
- char stringpool_str167[sizeof("com.sa")];
- char stringpool_str168[sizeof("ceo")];
- char stringpool_str169[sizeof("com.sd")];
- char stringpool_str170[sizeof("auto.pl")];
- char stringpool_str171[sizeof("isa.us")];
- char stringpool_str172[sizeof("gov.la")];
- char stringpool_str173[sizeof("codes")];
- char stringpool_str174[sizeof("info.co")];
- char stringpool_str175[sizeof("edu.la")];
- char stringpool_str176[sizeof("gov.ee")];
- char stringpool_str177[sizeof("asso.ci")];
- char stringpool_str178[sizeof("edu.ee")];
- char stringpool_str179[sizeof("bt")];
- char stringpool_str180[sizeof("com.la")];
- char stringpool_str181[sizeof("com.ee")];
- char stringpool_str182[sizeof("info.la")];
- char stringpool_str183[sizeof("gov.tl")];
- char stringpool_str184[sizeof("info.pl")];
- char stringpool_str185[sizeof("gov.bs")];
- char stringpool_str186[sizeof("gov.br")];
- char stringpool_str187[sizeof("gov.bo")];
- char stringpool_str188[sizeof("gob.bo")];
- char stringpool_str189[sizeof("gov.ua")];
- char stringpool_str190[sizeof("edu.bs")];
- char stringpool_str191[sizeof("edu.br")];
- char stringpool_str192[sizeof("edu.bo")];
- char stringpool_str193[sizeof("gov.bm")];
- char stringpool_str194[sizeof("edu.ua")];
- char stringpool_str195[sizeof("info.ki")];
- char stringpool_str196[sizeof("edu.bm")];
- char stringpool_str197[sizeof("com.bs")];
- char stringpool_str198[sizeof("com.br")];
- char stringpool_str199[sizeof("com.bo")];
- char stringpool_str200[sizeof("zone")];
- char stringpool_str201[sizeof("com.ua")];
- char stringpool_str202[sizeof("com.bm")];
- char stringpool_str203[sizeof("gi")];
- char stringpool_str204[sizeof("gov.bb")];
- char stringpool_str205[sizeof("esp.br")];
- char stringpool_str206[sizeof("best")];
- char stringpool_str207[sizeof("estate")];
- char stringpool_str208[sizeof("ci")];
- char stringpool_str209[sizeof("edu.bb")];
- char stringpool_str210[sizeof("gov.iq")];
- char stringpool_str211[sizeof("gov.is")];
- char stringpool_str212[sizeof("gov.ir")];
- char stringpool_str213[sizeof("adv.br")];
- char stringpool_str214[sizeof("edu.iq")];
- char stringpool_str215[sizeof("edu.is")];
- char stringpool_str216[sizeof("com.bb")];
- char stringpool_str217[sizeof("adm.br")];
- char stringpool_str218[sizeof("mo")];
- char stringpool_str219[sizeof("com.iq")];
- char stringpool_str220[sizeof("com.is")];
- char stringpool_str221[sizeof("md")];
- char stringpool_str222[sizeof("com.io")];
- char stringpool_str223[sizeof("mz")];
- char stringpool_str224[sizeof("gift")];
- char stringpool_str225[sizeof("bb")];
- char stringpool_str226[sizeof("com.im")];
- char stringpool_str227[sizeof("int.tj")];
- char stringpool_str228[sizeof("beer")];
- char stringpool_str229[sizeof("com.na")];
- char stringpool_str230[sizeof("ai")];
- char stringpool_str231[sizeof("z.bg")];
- char stringpool_str232[sizeof("art.br")];
- char stringpool_str233[sizeof("ms")];
- char stringpool_str234[sizeof("country")];
- char stringpool_str235[sizeof("fot.br")];
- char stringpool_str236[sizeof("mr")];
- char stringpool_str237[sizeof("info.pr")];
- char stringpool_str238[sizeof("z.se")];
- char stringpool_str239[sizeof("gov.nc.tr")];
- char stringpool_str240[sizeof("moe")];
- char stringpool_str241[sizeof("gov.rw")];
- char stringpool_str242[sizeof("edu.rw")];
- char stringpool_str243[sizeof("bi")];
- char stringpool_str244[sizeof("fst.br")];
- char stringpool_str245[sizeof("fet.no")];
- char stringpool_str246[sizeof("ml")];
- char stringpool_str247[sizeof("int.la")];
- char stringpool_str248[sizeof("fi")];
- char stringpool_str249[sizeof("com.rw")];
- char stringpool_str250[sizeof("center")];
- char stringpool_str251[sizeof("asso.dz")];
- char stringpool_str252[sizeof("gotdns.com")];
- char stringpool_str253[sizeof("gov.qa")];
- char stringpool_str254[sizeof("edu.qa")];
- char stringpool_str255[sizeof("info.bb")];
- char stringpool_str256[sizeof("fla.no")];
- char stringpool_str257[sizeof("info.az")];
- char stringpool_str258[sizeof("gy")];
- char stringpool_str259[sizeof("gifu.jp")];
- char stringpool_str260[sizeof("gov.ie")];
- char stringpool_str261[sizeof("com.qa")];
- char stringpool_str262[sizeof("info.pk")];
- char stringpool_str263[sizeof("cy")];
- char stringpool_str264[sizeof("int.bo")];
- char stringpool_str265[sizeof("mu")];
- char stringpool_str266[sizeof("cnt.br")];
- char stringpool_str267[sizeof("blog.br")];
- char stringpool_str268[sizeof("inf.br")];
- char stringpool_str269[sizeof("flog.br")];
- char stringpool_str270[sizeof("c.la")];
- char stringpool_str271[sizeof("assn.lk")];
- char stringpool_str272[sizeof("int.is")];
- char stringpool_str273[sizeof("m.bg")];
- char stringpool_str274[sizeof("uz")];
- char stringpool_str275[sizeof("mn")];
- char stringpool_str276[sizeof("m.se")];
- char stringpool_str277[sizeof("by")];
- char stringpool_str278[sizeof("gov.ba")];
- char stringpool_str279[sizeof("gov.jo")];
- char stringpool_str280[sizeof("edu.ba")];
- char stringpool_str281[sizeof("us")];
- char stringpool_str282[sizeof("edu.jo")];
- char stringpool_str283[sizeof("fosnes.no")];
- char stringpool_str284[sizeof("com.ba")];
- char stringpool_str285[sizeof("com.jo")];
- char stringpool_str286[sizeof("coffee")];
- char stringpool_str287[sizeof("gm")];
- char stringpool_str288[sizeof("im")];
- char stringpool_str289[sizeof("hm")];
- char stringpool_str290[sizeof("me")];
- char stringpool_str291[sizeof("firm.ro")];
- char stringpool_str292[sizeof("cm")];
- char stringpool_str293[sizeof("int.rw")];
- char stringpool_str294[sizeof("hemnes.no")];
- char stringpool_str295[sizeof("gov.mr")];
- char stringpool_str296[sizeof("gov.mo")];
- char stringpool_str297[sizeof("edu.mo")];
- char stringpool_str298[sizeof("com.mo")];
- char stringpool_str299[sizeof("firm.co")];
- char stringpool_str300[sizeof("mq")];
- char stringpool_str301[sizeof("g12.br")];
- char stringpool_str302[sizeof("fed.us")];
- char stringpool_str303[sizeof("mt")];
- char stringpool_str304[sizeof("moda")];
- char stringpool_str305[sizeof("am")];
- char stringpool_str306[sizeof("bj")];
- char stringpool_str307[sizeof("meet")];
- char stringpool_str308[sizeof("fj")];
- char stringpool_str309[sizeof("gov.tw")];
- char stringpool_str310[sizeof("menu")];
- char stringpool_str311[sizeof("gov.mk")];
- char stringpool_str312[sizeof("blogspot.no")];
- char stringpool_str313[sizeof("idv.tw")];
- char stringpool_str314[sizeof("edu.tw")];
- char stringpool_str315[sizeof("gol.no")];
- char stringpool_str316[sizeof("edu.mk")];
- char stringpool_str317[sizeof("hol.no")];
- char stringpool_str318[sizeof("blogspot.cz")];
- char stringpool_str319[sizeof("com.tw")];
- char stringpool_str320[sizeof("com.mk")];
- char stringpool_str321[sizeof("bm")];
- char stringpool_str322[sizeof("blogspot.ro")];
- char stringpool_str323[sizeof("fm")];
- char stringpool_str324[sizeof("gov.ml")];
- char stringpool_str325[sizeof("bid")];
- char stringpool_str326[sizeof("u.bg")];
- char stringpool_str327[sizeof("edu.ml")];
- char stringpool_str328[sizeof("com.ml")];
- char stringpool_str329[sizeof("gov.me")];
- char stringpool_str330[sizeof("ato.br")];
- char stringpool_str331[sizeof("u.se")];
- char stringpool_str332[sizeof("edu.me")];
- char stringpool_str333[sizeof("blogspot.nl")];
- char stringpool_str334[sizeof("gdansk.pl")];
- char stringpool_str335[sizeof("ind.br")];
- char stringpool_str336[sizeof("cim.br")];
- char stringpool_str337[sizeof("eng.br")];
- char stringpool_str338[sizeof("info.mv")];
- char stringpool_str339[sizeof("algard.no")];
- char stringpool_str340[sizeof("museum")];
- char stringpool_str341[sizeof("aip.ee")];
- char stringpool_str342[sizeof("cng.br")];
- char stringpool_str343[sizeof("blogspot.kr")];
- char stringpool_str344[sizeof("fin.ec")];
- char stringpool_str345[sizeof("ga")];
- char stringpool_str346[sizeof("codespot.com")];
- char stringpool_str347[sizeof("blogspot.td")];
- char stringpool_str348[sizeof("isla.pr")];
- char stringpool_str349[sizeof("haus")];
- char stringpool_str350[sizeof("ca")];
- char stringpool_str351[sizeof("qa")];
- char stringpool_str352[sizeof("cat")];
- char stringpool_str353[sizeof("gov.as")];
- char stringpool_str354[sizeof("cab")];
- char stringpool_str355[sizeof("gob.ar")];
- char stringpool_str356[sizeof("edu.ar")];
- char stringpool_str357[sizeof("eid.no")];
- char stringpool_str358[sizeof("com.ar")];
- char stringpool_str359[sizeof("mod.uk")];
- char stringpool_str360[sizeof("uno")];
- char stringpool_str361[sizeof("frosta.no")];
- char stringpool_str362[sizeof("gov.ac")];
- char stringpool_str363[sizeof("aero.mv")];
- char stringpool_str364[sizeof("edu.ac")];
- char stringpool_str365[sizeof("com.ac")];
- char stringpool_str366[sizeof("fie.ee")];
- char stringpool_str367[sizeof("blogspot.com")];
- char stringpool_str368[sizeof("fnd.br")];
- char stringpool_str369[sizeof("edu.bi")];
- char stringpool_str370[sizeof("gp")];
- char stringpool_str371[sizeof("gov.ma")];
- char stringpool_str372[sizeof("ba")];
- char stringpool_str373[sizeof("inf.mk")];
- char stringpool_str374[sizeof("cards")];
- char stringpool_str375[sizeof("bar")];
- char stringpool_str376[sizeof("com.bi")];
- char stringpool_str377[sizeof("blogspot.mr")];
- char stringpool_str378[sizeof("museum.no")];
- char stringpool_str379[sizeof("gov.ps")];
- char stringpool_str380[sizeof("gov.pr")];
- char stringpool_str381[sizeof("gov.al")];
- char stringpool_str382[sizeof("med.om")];
- char stringpool_str383[sizeof("edu.ps")];
- char stringpool_str384[sizeof("edu.pr")];
- char stringpool_str385[sizeof("asso.bj")];
- char stringpool_str386[sizeof("edu.al")];
- char stringpool_str387[sizeof("biz")];
- char stringpool_str388[sizeof("casa")];
- char stringpool_str389[sizeof("com.ps")];
- char stringpool_str390[sizeof("com.pr")];
- char stringpool_str391[sizeof("qsl.br")];
- char stringpool_str392[sizeof("com.al")];
- char stringpool_str393[sizeof("gov.ae")];
- char stringpool_str394[sizeof("zm")];
- char stringpool_str395[sizeof("est.pr")];
- char stringpool_str396[sizeof("blogspot.re")];
- char stringpool_str397[sizeof("bio.br")];
- char stringpool_str398[sizeof("my")];
- char stringpool_str399[sizeof("unsa.ba")];
- char stringpool_str400[sizeof("gov.pk")];
- char stringpool_str401[sizeof("gob.pk")];
- char stringpool_str402[sizeof("farm")];
- char stringpool_str403[sizeof("edu.pk")];
- char stringpool_str404[sizeof("gop.pk")];
- char stringpool_str405[sizeof("cv")];
- char stringpool_str406[sizeof("com.pk")];
- char stringpool_str407[sizeof("club")];
- char stringpool_str408[sizeof("conf.lv")];
- char stringpool_str409[sizeof("zlg.br")];
- char stringpool_str410[sizeof("homeip.net")];
- char stringpool_str411[sizeof("int.ar")];
- char stringpool_str412[sizeof("gov.pl")];
- char stringpool_str413[sizeof("mus.br")];
- char stringpool_str414[sizeof("imb.br")];
- char stringpool_str415[sizeof("gov.vc")];
- char stringpool_str416[sizeof("edu.pl")];
- char stringpool_str417[sizeof("consulado.st")];
- char stringpool_str418[sizeof("gon.pk")];
- char stringpool_str419[sizeof("edu.vc")];
- char stringpool_str420[sizeof("emp.br")];
- char stringpool_str421[sizeof("i.ph")];
- char stringpool_str422[sizeof("com.pl")];
- char stringpool_str423[sizeof("gob.pe")];
- char stringpool_str424[sizeof("com.vc")];
- char stringpool_str425[sizeof("edu.pe")];
- char stringpool_str426[sizeof("gsm.pl")];
- char stringpool_str427[sizeof("com.pe")];
- char stringpool_str428[sizeof("asia")];
- char stringpool_str429[sizeof("gos.pk")];
- char stringpool_str430[sizeof("globo")];
- char stringpool_str431[sizeof("mm")];
- char stringpool_str432[sizeof("gov.mw")];
- char stringpool_str433[sizeof("career")];
- char stringpool_str434[sizeof("gov.ve")];
- char stringpool_str435[sizeof("edu.mw")];
- char stringpool_str436[sizeof("art.pl")];
- char stringpool_str437[sizeof("edu.ve")];
- char stringpool_str438[sizeof("qpon")];
- char stringpool_str439[sizeof("com.mw")];
- char stringpool_str440[sizeof("com.ve")];
- char stringpool_str441[sizeof("biz.nr")];
- char stringpool_str442[sizeof("za")];
- char stringpool_str443[sizeof("uy")];
- char stringpool_str444[sizeof("its.me")];
- char stringpool_str445[sizeof("amursk.ru")];
- char stringpool_str446[sizeof("blogspot.de")];
- char stringpool_str447[sizeof("glogow.pl")];
- char stringpool_str448[sizeof("is-a-cpa.com")];
- char stringpool_str449[sizeof("med.ec")];
- char stringpool_str450[sizeof("camera")];
- char stringpool_str451[sizeof("gob.pa")];
- char stringpool_str452[sizeof("com.nf")];
- char stringpool_str453[sizeof("edu.pa")];
- char stringpool_str454[sizeof("blogspot.se")];
- char stringpool_str455[sizeof("blogspot.com.es")];
- char stringpool_str456[sizeof("gda.pl")];
- char stringpool_str457[sizeof("hurdal.no")];
- char stringpool_str458[sizeof("com.pa")];
- char stringpool_str459[sizeof("build")];
- char stringpool_str460[sizeof("alvdal.no")];
- char stringpool_str461[sizeof("med.sa")];
- char stringpool_str462[sizeof("asso.km")];
- char stringpool_str463[sizeof("med.sd")];
- char stringpool_str464[sizeof("biz.tj")];
- char stringpool_str465[sizeof("habmer.no")];
- char stringpool_str466[sizeof("blogspot.pt")];
- char stringpool_str467[sizeof("futbol")];
- char stringpool_str468[sizeof("blogspot.hu")];
- char stringpool_str469[sizeof("fjaler.no")];
- char stringpool_str470[sizeof("moscow")];
- char stringpool_str471[sizeof("med.ee")];
- char stringpool_str472[sizeof("ma")];
- char stringpool_str473[sizeof("aseral.no")];
- char stringpool_str474[sizeof("int.mw")];
- char stringpool_str475[sizeof("blogspot.co.nz")];
- char stringpool_str476[sizeof("x.bg")];
- char stringpool_str477[sizeof("med.br")];
- char stringpool_str478[sizeof("com.aw")];
- char stringpool_str479[sizeof("museum.tt")];
- char stringpool_str480[sizeof("x.se")];
- char stringpool_str481[sizeof("far.br")];
- char stringpool_str482[sizeof("gh")];
- char stringpool_str483[sizeof("ch")];
- char stringpool_str484[sizeof("asso.fr")];
- char stringpool_str485[sizeof("atm.pl")];
- char stringpool_str486[sizeof("quebec")];
- char stringpool_str487[sizeof("blogspot.com.br")];
- char stringpool_str488[sizeof("biz.bb")];
- char stringpool_str489[sizeof("mp")];
- char stringpool_str490[sizeof("gov.bf")];
- char stringpool_str491[sizeof("bieszczady.pl")];
- char stringpool_str492[sizeof("com.hr")];
- char stringpool_str493[sizeof("guide")];
- char stringpool_str494[sizeof("gal")];
- char stringpool_str495[sizeof("bmd.br")];
- char stringpool_str496[sizeof("ingatlan.hu")];
- char stringpool_str497[sizeof("mil")];
- char stringpool_str498[sizeof("events")];
- char stringpool_str499[sizeof("gov.hk")];
- char stringpool_str500[sizeof("com.ai")];
- char stringpool_str501[sizeof("idv.hk")];
- char stringpool_str502[sizeof("edu.hk")];
- char stringpool_str503[sizeof("mil.do")];
- char stringpool_str504[sizeof("firm.in")];
- char stringpool_str505[sizeof("com.hk")];
- char stringpool_str506[sizeof("eng.pro")];
- char stringpool_str507[sizeof("bh")];
- char stringpool_str508[sizeof("mv")];
- char stringpool_str509[sizeof("hapmir.no")];
- char stringpool_str510[sizeof("belgorod.ru")];
- char stringpool_str511[sizeof("ua")];
- char stringpool_str512[sizeof("e12.ve")];
- char stringpool_str513[sizeof("hemsedal.no")];
- char stringpool_str514[sizeof("mango")];
- char stringpool_str515[sizeof("evenes.no")];
- char stringpool_str516[sizeof("miami")];
- char stringpool_str517[sizeof("engerdal.no")];
- char stringpool_str518[sizeof("blogspot.in")];
- char stringpool_str519[sizeof("hvaler.no")];
- char stringpool_str520[sizeof("mielno.pl")];
- char stringpool_str521[sizeof("hadsel.no")];
- char stringpool_str522[sizeof("bronnoysund.no")];
- char stringpool_str523[sizeof("blogspot.ie")];
- char stringpool_str524[sizeof("blogspot.ca")];
- char stringpool_str525[sizeof("com.vi")];
- char stringpool_str526[sizeof("etc.br")];
- char stringpool_str527[sizeof("broke-it.net")];
- char stringpool_str528[sizeof("mil.no")];
- char stringpool_str529[sizeof("mincom.tn")];
- char stringpool_str530[sizeof("asso.re")];
- char stringpool_str531[sizeof("blogspot.co.il")];
- char stringpool_str532[sizeof("fhv.se")];
- char stringpool_str533[sizeof("blogspot.it")];
- char stringpool_str534[sizeof("mielec.pl")];
- char stringpool_str535[sizeof("fish")];
- char stringpool_str536[sizeof("e164.arpa")];
- char stringpool_str537[sizeof("mie.jp")];
- char stringpool_str538[sizeof("ing.pa")];
- char stringpool_str539[sizeof("eti.br")];
- char stringpool_str540[sizeof("biz.id")];
- char stringpool_str541[sizeof("mat.br")];
- char stringpool_str542[sizeof("bindal.no")];
- char stringpool_str543[sizeof("mil.ec")];
- char stringpool_str544[sizeof("mil.to")];
- char stringpool_str545[sizeof("hareid.no")];
- char stringpool_str546[sizeof("mil.tj")];
- char stringpool_str547[sizeof("mil.tm")];
- char stringpool_str548[sizeof("abo.pa")];
- char stringpool_str549[sizeof("fyresdal.no")];
- char stringpool_str550[sizeof("gallery")];
- char stringpool_str551[sizeof("hk")];
- char stringpool_str552[sizeof("builders")];
- char stringpool_str553[sizeof("blogspot.cv")];
- char stringpool_str554[sizeof("ck")];
- char stringpool_str555[sizeof("fhs.no")];
- char stringpool_str556[sizeof("gov.km")];
- char stringpool_str557[sizeof("broker.aero")];
- char stringpool_str558[sizeof("edu.km")];
- char stringpool_str559[sizeof("aid.pl")];
- char stringpool_str560[sizeof("com.km")];
- char stringpool_str561[sizeof("meland.no")];
- char stringpool_str562[sizeof("mil.br")];
- char stringpool_str563[sizeof("mil.bo")];
- char stringpool_str564[sizeof("mh")];
- char stringpool_str565[sizeof("gaular.no")];
- char stringpool_str566[sizeof("fk")];
- char stringpool_str567[sizeof("isernia.it")];
- char stringpool_str568[sizeof("mil.iq")];
- char stringpool_str569[sizeof("gov.kp")];
- char stringpool_str570[sizeof("ws")];
- char stringpool_str571[sizeof("ullensaker.no")];
- char stringpool_str572[sizeof("blogspot.com.ar")];
- char stringpool_str573[sizeof("edu.kp")];
- char stringpool_str574[sizeof("gov.af")];
- char stringpool_str575[sizeof("com.kp")];
- char stringpool_str576[sizeof("edu.af")];
- char stringpool_str577[sizeof("com.af")];
- char stringpool_str578[sizeof("biz.pr")];
- char stringpool_str579[sizeof("biella.it")];
- char stringpool_str580[sizeof("mil.rw")];
- char stringpool_str581[sizeof("blogspot.com.au")];
- char stringpool_str582[sizeof("med.pro")];
- char stringpool_str583[sizeof("author.aero")];
- char stringpool_str584[sizeof("bar.pro")];
- char stringpool_str585[sizeof("ass.km")];
- char stringpool_str586[sizeof("mil.qa")];
- char stringpool_str587[sizeof("fuossko.no")];
- char stringpool_str588[sizeof("fam.pk")];
- char stringpool_str589[sizeof("edu.pf")];
- char stringpool_str590[sizeof("biz.pk")];
- char stringpool_str591[sizeof("com.pf")];
- char stringpool_str592[sizeof("med.pl")];
- char stringpool_str593[sizeof("fuel.aero")];
- char stringpool_str594[sizeof("urn.arpa")];
- char stringpool_str595[sizeof("w.bg")];
- char stringpool_str596[sizeof("eidfjord.no")];
- char stringpool_str597[sizeof("w.se")];
- char stringpool_str598[sizeof("biz.pl")];
- char stringpool_str599[sizeof("arendal.no")];
- char stringpool_str600[sizeof("irc.pl")];
- char stringpool_str601[sizeof("miasta.pl")];
- char stringpool_str602[sizeof("mil.ba")];
- char stringpool_str603[sizeof("mil.jo")];
- char stringpool_str604[sizeof("web.do")];
- char stringpool_str605[sizeof("biz.mw")];
- char stringpool_str606[sizeof("blogspot.ch")];
- char stringpool_str607[sizeof("fuettertdasnetz.de")];
- char stringpool_str608[sizeof("googleapis.com")];
- char stringpool_str609[sizeof("bronnoy.no")];
- char stringpool_str610[sizeof("mil.id")];
- char stringpool_str611[sizeof("inderoy.no")];
- char stringpool_str612[sizeof("cpa.pro")];
- char stringpool_str613[sizeof("museum.mv")];
- char stringpool_str614[sizeof("blogspot.gr")];
- char stringpool_str615[sizeof("etnedal.no")];
- char stringpool_str616[sizeof("careers")];
- char stringpool_str617[sizeof("med.pa")];
- char stringpool_str618[sizeof("mil.tw")];
- char stringpool_str619[sizeof("xyz")];
- char stringpool_str620[sizeof("mk")];
- char stringpool_str621[sizeof("web.lk")];
- char stringpool_str622[sizeof("hjartdal.no")];
- char stringpool_str623[sizeof("foggia.it")];
- char stringpool_str624[sizeof("brussels.museum")];
- char stringpool_str625[sizeof("marker.no")];
- char stringpool_str626[sizeof("modena.it")];
- char stringpool_str627[sizeof("bamble.no")];
- char stringpool_str628[sizeof("ip6.arpa")];
- char stringpool_str629[sizeof("ferrara.it")];
- char stringpool_str630[sizeof("imperia.it")];
- char stringpool_str631[sizeof("blogspot.co.at")];
- char stringpool_str632[sizeof("flesberg.no")];
- char stringpool_str633[sizeof("bergamo.it")];
- char stringpool_str634[sizeof("aircraft.aero")];
- char stringpool_str635[sizeof("mil.ar")];
- char stringpool_str636[sizeof("gov.ki")];
- char stringpool_str637[sizeof("edu.ki")];
- char stringpool_str638[sizeof("hjelmeland.no")];
- char stringpool_str639[sizeof("wed")];
- char stringpool_str640[sizeof("mil.ac")];
- char stringpool_str641[sizeof("com.ki")];
- char stringpool_str642[sizeof("meldal.no")];
- char stringpool_str643[sizeof("github.io")];
- char stringpool_str644[sizeof("web.tj")];
- char stringpool_str645[sizeof("black")];
- char stringpool_str646[sizeof("uk")];
- char stringpool_str647[sizeof("cheap")];
- char stringpool_str648[sizeof("horology.museum")];
- char stringpool_str649[sizeof("mil.al")];
- char stringpool_str650[sizeof("wroc.pl")];
- char stringpool_str651[sizeof("work")];
- char stringpool_str652[sizeof("wien")];
- char stringpool_str653[sizeof("mil.ae")];
- char stringpool_str654[sizeof("caravan")];
- char stringpool_str655[sizeof("works")];
- char stringpool_str656[sizeof("eidsvoll.no")];
- char stringpool_str657[sizeof("folldal.no")];
- char stringpool_str658[sizeof("gjesdal.no")];
- char stringpool_str659[sizeof("mil.pl")];
- char stringpool_str660[sizeof("mandal.no")];
- char stringpool_str661[sizeof("mil.vc")];
- char stringpool_str662[sizeof("mail.pl")];
- char stringpool_str663[sizeof("melhus.no")];
- char stringpool_str664[sizeof("immobilien")];
- char stringpool_str665[sizeof("mil.pe")];
- char stringpool_str666[sizeof("cleaning")];
- char stringpool_str667[sizeof("fredrikstad.no")];
- char stringpool_str668[sizeof("asmatart.museum")];
- char stringpool_str669[sizeof("hamaroy.no")];
- char stringpool_str670[sizeof("catania.it")];
- char stringpool_str671[sizeof("mil.ve")];
- char stringpool_str672[sizeof("museum.om")];
- char stringpool_str673[sizeof("genova.it")];
- char stringpool_str674[sizeof("badaddja.no")];
- char stringpool_str675[sizeof("iris.arpa")];
- char stringpool_str676[sizeof("flanders.museum")];
- char stringpool_str677[sizeof("creation.museum")];
- char stringpool_str678[sizeof("milano.it")];
- char stringpool_str679[sizeof("gausdal.no")];
- char stringpool_str680[sizeof("ancona.it")];
- char stringpool_str681[sizeof("matera.it")];
- char stringpool_str682[sizeof("budapest")];
- char stringpool_str683[sizeof("modalen.no")];
- char stringpool_str684[sizeof("gotdns.org")];
- char stringpool_str685[sizeof("blogspot.dk")];
- char stringpool_str686[sizeof("eidsberg.no")];
- char stringpool_str687[sizeof("aremark.no")];
- char stringpool_str688[sizeof("club.aero")];
- char stringpool_str689[sizeof("blogspot.sk")];
- char stringpool_str690[sizeof("web.id")];
- char stringpool_str691[sizeof("is-a-llama.com")];
- char stringpool_str692[sizeof("izhevsk.ru")];
- char stringpool_str693[sizeof("hita.oita.jp")];
- char stringpool_str694[sizeof("benevento.it")];
- char stringpool_str695[sizeof("wang")];
- char stringpool_str696[sizeof("blogspot.hk")];
- char stringpool_str697[sizeof("ibaraki.jp")];
- char stringpool_str698[sizeof("holtalen.no")];
- char stringpool_str699[sizeof("fauske.no")];
- char stringpool_str700[sizeof("uri.arpa")];
- char stringpool_str701[sizeof("masfjorden.no")];
- char stringpool_str702[sizeof("airguard.museum")];
- char stringpool_str703[sizeof("baidar.no")];
- char stringpool_str704[sizeof("american.museum")];
- char stringpool_str705[sizeof("blogspot.co.uk")];
- char stringpool_str706[sizeof("montreal.museum")];
- char stringpool_str707[sizeof("guitars")];
- char stringpool_str708[sizeof("bargains")];
- char stringpool_str709[sizeof("estate.museum")];
- char stringpool_str710[sizeof("wtc")];
- char stringpool_str711[sizeof("uzhgorod.ua")];
- char stringpool_str712[sizeof("bydgoszcz.pl")];
- char stringpool_str713[sizeof("web.pk")];
- char stringpool_str714[sizeof("cartoonart.museum")];
- char stringpool_str715[sizeof("caa.aero")];
- char stringpool_str716[sizeof("kz")];
- char stringpool_str717[sizeof("cloudfront.net")];
- char stringpool_str718[sizeof("cultural.museum")];
- char stringpool_str719[sizeof("center.museum")];
- char stringpool_str720[sizeof("kr")];
- char stringpool_str721[sizeof("catering")];
- char stringpool_str722[sizeof("gov.ru")];
- char stringpool_str723[sizeof("edu.ru")];
- char stringpool_str724[sizeof("brunel.museum")];
- char stringpool_str725[sizeof("com.ru")];
- char stringpool_str726[sizeof("mil.kr")];
- char stringpool_str727[sizeof("mil.km")];
- char stringpool_str728[sizeof("web.ve")];
- char stringpool_str729[sizeof("chtr.k12.ma.us")];
- char stringpool_str730[sizeof("czeladz.pl")];
- char stringpool_str731[sizeof("kobe.jp")];
- char stringpool_str732[sizeof("kred")];
- char stringpool_str733[sizeof("in-addr.arpa")];
- char stringpool_str734[sizeof("k.bg")];
- char stringpool_str735[sizeof("web.nf")];
- char stringpool_str736[sizeof("kn")];
- char stringpool_str737[sizeof("k.se")];
- char stringpool_str738[sizeof("biz.ki")];
- char stringpool_str739[sizeof("mragowo.pl")];
- char stringpool_str740[sizeof("gw")];
- char stringpool_str741[sizeof("cw")];
- char stringpool_str742[sizeof("ke")];
- char stringpool_str743[sizeof("gov.ws")];
- char stringpool_str744[sizeof("int.ru")];
- char stringpool_str745[sizeof("edu.ws")];
- char stringpool_str746[sizeof("com.ws")];
- char stringpool_str747[sizeof("wegrow.pl")];
- char stringpool_str748[sizeof("gov.sg")];
- char stringpool_str749[sizeof("aw")];
- char stringpool_str750[sizeof("edu.sg")];
- char stringpool_str751[sizeof("ascoli-piceno.it")];
- char stringpool_str752[sizeof("com.sg")];
- char stringpool_str753[sizeof("columbia.museum")];
- char stringpool_str754[sizeof("erotica.hu")];
- char stringpool_str755[sizeof("crafts.museum")];
- char stringpool_str756[sizeof("gildeskal.no")];
- char stringpool_str757[sizeof("krd")];
- char stringpool_str758[sizeof("bw")];
- char stringpool_str759[sizeof("erotika.hu")];
- char stringpool_str760[sizeof("audnedaln.no")];
- char stringpool_str761[sizeof("circus.museum")];
- char stringpool_str762[sizeof("blogspot.jp")];
- char stringpool_str763[sizeof("aomori.aomori.jp")];
- char stringpool_str764[sizeof("brasil.museum")];
- char stringpool_str765[sizeof("com.ug")];
- char stringpool_str766[sizeof("guernsey.museum")];
- char stringpool_str767[sizeof("aomori.jp")];
- char stringpool_str768[sizeof("gov.ng")];
- char stringpool_str769[sizeof("edu.ng")];
- char stringpool_str770[sizeof("ki")];
- char stringpool_str771[sizeof("com.ng")];
- char stringpool_str772[sizeof("kim")];
- char stringpool_str773[sizeof("grandrapids.museum")];
- char stringpool_str774[sizeof("cagliari.it")];
- char stringpool_str775[sizeof("fhsk.se")];
- char stringpool_str776[sizeof("gov.eg")];
- char stringpool_str777[sizeof("edu.eg")];
- char stringpool_str778[sizeof("gjemnes.no")];
- char stringpool_str779[sizeof("historical.museum")];
- char stringpool_str780[sizeof("magadan.ru")];
- char stringpool_str781[sizeof("com.eg")];
- char stringpool_str782[sizeof("alessandria.it")];
- char stringpool_str783[sizeof("asso.gp")];
- char stringpool_str784[sizeof("crimea.ua")];
- char stringpool_str785[sizeof("miners.museum")];
- char stringpool_str786[sizeof("zw")];
- char stringpool_str787[sizeof("memorial.museum")];
- char stringpool_str788[sizeof("ky")];
- char stringpool_str789[sizeof("webhop.net")];
- char stringpool_str790[sizeof("meeres.museum")];
- char stringpool_str791[sizeof("granvin.no")];
- char stringpool_str792[sizeof("contemporary.museum")];
- char stringpool_str793[sizeof("bir.ru")];
- char stringpool_str794[sizeof("k12.ec")];
- char stringpool_str795[sizeof("eun.eg")];
- char stringpool_str796[sizeof("evenassi.no")];
- char stringpool_str797[sizeof("baikal.ru")];
- char stringpool_str798[sizeof("warszawa.pl")];
- char stringpool_str799[sizeof("clothing")];
- char stringpool_str800[sizeof("mw")];
- char stringpool_str801[sizeof("km")];
- char stringpool_str802[sizeof("cupcake.is")];
- char stringpool_str803[sizeof("castle.museum")];
- char stringpool_str804[sizeof("mobi")];
- char stringpool_str805[sizeof("enebakk.no")];
- char stringpool_str806[sizeof("gov.mu")];
- char stringpool_str807[sizeof("beskidy.pl")];
- char stringpool_str808[sizeof("udm.ru")];
- char stringpool_str809[sizeof("com.mu")];
- char stringpool_str810[sizeof("canada.museum")];
- char stringpool_str811[sizeof("cbg.ru")];
- char stringpool_str812[sizeof("hagebostad.no")];
- char stringpool_str813[sizeof("usarts.museum")];
- char stringpool_str814[sizeof("catering.aero")];
- char stringpool_str815[sizeof("beardu.no")];
- char stringpool_str816[sizeof("info.at")];
- char stringpool_str817[sizeof("gorlice.pl")];
- char stringpool_str818[sizeof("celtic.museum")];
- char stringpool_str819[sizeof("altoadige.it")];
- char stringpool_str820[sizeof("fundacio.museum")];
- char stringpool_str821[sizeof("moscow.museum")];
- char stringpool_str822[sizeof("warmia.pl")];
- char stringpool_str823[sizeof("bielawa.pl")];
- char stringpool_str824[sizeof("anthro.museum")];
- char stringpool_str825[sizeof("moareke.no")];
- char stringpool_str826[sizeof("avellino.it")];
- char stringpool_str827[sizeof("massacarrara.it")];
- char stringpool_str828[sizeof("como.it")];
- char stringpool_str829[sizeof("fylkesbibl.no")];
- char stringpool_str830[sizeof("gov.au")];
- char stringpool_str831[sizeof("ise.mie.jp")];
- char stringpool_str832[sizeof("broadcast.museum")];
- char stringpool_str833[sizeof("edu.au")];
- char stringpool_str834[sizeof("mansions.museum")];
- char stringpool_str835[sizeof("com.au")];
- char stringpool_str836[sizeof("quebec.museum")];
- char stringpool_str837[sizeof("gov.mg")];
- char stringpool_str838[sizeof("ullensvang.no")];
- char stringpool_str839[sizeof("edu.mg")];
- char stringpool_str840[sizeof("com.mg")];
- char stringpool_str841[sizeof("muncie.museum")];
- char stringpool_str842[sizeof("e-burg.ru")];
- char stringpool_str843[sizeof("kp")];
- char stringpool_str844[sizeof("baseball.museum")];
- char stringpool_str845[sizeof("hinode.tokyo.jp")];
- char stringpool_str846[sizeof("cinema.museum")];
- char stringpool_str847[sizeof("asn.au")];
- char stringpool_str848[sizeof("florist")];
- char stringpool_str849[sizeof("andebu.no")];
- char stringpool_str850[sizeof("enna.it")];
- char stringpool_str851[sizeof("blogspot.tw")];
- char stringpool_str852[sizeof("karasjok.no")];
- char stringpool_str853[sizeof("meraker.no")];
- char stringpool_str854[sizeof("capebreton.museum")];
- char stringpool_str855[sizeof("com.ag")];
- char stringpool_str856[sizeof("ina.ibaraki.jp")];
- char stringpool_str857[sizeof("unbi.ba")];
- char stringpool_str858[sizeof("berkeley.museum")];
- char stringpool_str859[sizeof("maryland.museum")];
- char stringpool_str860[sizeof("holiday")];
- char stringpool_str861[sizeof("email")];
- char stringpool_str862[sizeof("mini")];
- char stringpool_str863[sizeof("malselv.no")];
- char stringpool_str864[sizeof("hellas.museum")];
- char stringpool_str865[sizeof("zaporizhzhe.ua")];
- char stringpool_str866[sizeof("hirosaki.aomori.jp")];
- char stringpool_str867[sizeof("usgarden.museum")];
- char stringpool_str868[sizeof("chieti.it")];
- char stringpool_str869[sizeof("better-than.tv")];
- char stringpool_str870[sizeof("qld.au")];
- char stringpool_str871[sizeof("carraramassa.it")];
- char stringpool_str872[sizeof("figueres.museum")];
- char stringpool_str873[sizeof("gonohe.aomori.jp")];
- char stringpool_str874[sizeof("watch")];
- char stringpool_str875[sizeof("chernovtsy.ua")];
- char stringpool_str876[sizeof("aejrie.no")];
- char stringpool_str877[sizeof("bjarkoy.no")];
- char stringpool_str878[sizeof("forlicesena.it")];
- char stringpool_str879[sizeof("brindisi.it")];
- char stringpool_str880[sizeof("cesenaforli.it")];
- char stringpool_str881[sizeof("foundation")];
- char stringpool_str882[sizeof("meguro.tokyo.jp")];
- char stringpool_str883[sizeof("museum.mw")];
- char stringpool_str884[sizeof("kh")];
- char stringpool_str885[sizeof("mosreg.ru")];
- char stringpool_str886[sizeof("akrehamn.no")];
- char stringpool_str887[sizeof("zaporizhzhia.ua")];
- char stringpool_str888[sizeof("uda.nara.jp")];
- char stringpool_str889[sizeof("undersea.museum")];
- char stringpool_str890[sizeof("cruises")];
- char stringpool_str891[sizeof("eisenbahn.museum")];
- char stringpool_str892[sizeof("mil.ru")];
- char stringpool_str893[sizeof("wroclaw.pl")];
- char stringpool_str894[sizeof("eidskog.no")];
- char stringpool_str895[sizeof("aviation.museum")];
- char stringpool_str896[sizeof("k12.or.us")];
- char stringpool_str897[sizeof("hamura.tokyo.jp")];
- char stringpool_str898[sizeof("k12.dc.us")];
- char stringpool_str899[sizeof("mifune.kumamoto.jp")];
- char stringpool_str900[sizeof("minato.tokyo.jp")];
- char stringpool_str901[sizeof("k12.ok.us")];
- char stringpool_str902[sizeof("k12.sc.us")];
- char stringpool_str903[sizeof("ballooning.aero")];
- char stringpool_str904[sizeof("muroto.kochi.jp")];
- char stringpool_str905[sizeof("ambulance.museum")];
- char stringpool_str906[sizeof("elblag.pl")];
- char stringpool_str907[sizeof("ina.saitama.jp")];
- char stringpool_str908[sizeof("k12.de.us")];
- char stringpool_str909[sizeof("utsira.no")];
- char stringpool_str910[sizeof("assassination.museum")];
- char stringpool_str911[sizeof("zhitomir.ua")];
- char stringpool_str912[sizeof("americanart.museum")];
- char stringpool_str913[sizeof("kostroma.ru")];
- char stringpool_str914[sizeof("krakow.pl")];
- char stringpool_str915[sizeof("k12.nj.us")];
- char stringpool_str916[sizeof("k12.nm.us")];
- char stringpool_str917[sizeof("incheon.kr")];
- char stringpool_str918[sizeof("bunkyo.tokyo.jp")];
- char stringpool_str919[sizeof("k12.nc.us")];
- char stringpool_str920[sizeof("gorizia.it")];
- char stringpool_str921[sizeof("engine.aero")];
- char stringpool_str922[sizeof("contemporaryart.museum")];
- char stringpool_str923[sizeof("carrara-massa.it")];
- char stringpool_str924[sizeof("astrakhan.ru")];
- char stringpool_str925[sizeof("k12.ne.us")];
- char stringpool_str926[sizeof("crew.aero")];
- char stringpool_str927[sizeof("alaska.museum")];
- char stringpool_str928[sizeof("mil.ng")];
- char stringpool_str929[sizeof("bearalvahki.no")];
- char stringpool_str930[sizeof("heguri.nara.jp")];
- char stringpool_str931[sizeof("k12.la.us")];
- char stringpool_str932[sizeof("mitsue.nara.jp")];
- char stringpool_str933[sizeof("usantiques.museum")];
- char stringpool_str934[sizeof("misasa.tottori.jp")];
- char stringpool_str935[sizeof("cheltenham.museum")];
- char stringpool_str936[sizeof("consultant.aero")];
- char stringpool_str937[sizeof("imageandsound.museum")];
- char stringpool_str938[sizeof("epilepsy.museum")];
- char stringpool_str939[sizeof("mil.eg")];
- char stringpool_str940[sizeof("madrid.museum")];
- char stringpool_str941[sizeof("aquila.it")];
- char stringpool_str942[sizeof("gobo.wakayama.jp")];
- char stringpool_str943[sizeof("malbork.pl")];
- char stringpool_str944[sizeof("k12.vi")];
- char stringpool_str945[sizeof("hinohara.tokyo.jp")];
- char stringpool_str946[sizeof("k12.nd.us")];
- char stringpool_str947[sizeof("blackfriday")];
- char stringpool_str948[sizeof("gov.kg")];
- char stringpool_str949[sizeof("ashoro.hokkaido.jp")];
- char stringpool_str950[sizeof("building.museum")];
- char stringpool_str951[sizeof("edu.kg")];
- char stringpool_str952[sizeof("city.sendai.jp")];
- char stringpool_str953[sizeof("com.kg")];
- char stringpool_str954[sizeof("franziskaner.museum")];
- char stringpool_str955[sizeof("kemerovo.ru")];
- char stringpool_str956[sizeof("k12.il.us")];
- char stringpool_str957[sizeof("casino.hu")];
- char stringpool_str958[sizeof("mihara.kochi.jp")];
- char stringpool_str959[sizeof("marine.ru")];
- char stringpool_str960[sizeof("k12.ri.us")];
- char stringpool_str961[sizeof("australia.museum")];
- char stringpool_str962[sizeof("business")];
- char stringpool_str963[sizeof("furano.hokkaido.jp")];
- char stringpool_str964[sizeof("kragero.no")];
- char stringpool_str965[sizeof("mobara.chiba.jp")];
- char stringpool_str966[sizeof("k12.ms.us")];
- char stringpool_str967[sizeof("k12.mo.us")];
- char stringpool_str968[sizeof("k12.ia.us")];
- char stringpool_str969[sizeof("k12.id.us")];
- char stringpool_str970[sizeof("chocolate.museum")];
- char stringpool_str971[sizeof("giehtavuoatna.no")];
- char stringpool_str972[sizeof("americana.museum")];
- char stringpool_str973[sizeof("maintenance.aero")];
- char stringpool_str974[sizeof("baltimore.museum")];
- char stringpool_str975[sizeof("k12.me.us")];
- char stringpool_str976[sizeof("minnesota.museum")];
- char stringpool_str977[sizeof("mil.mg")];
- char stringpool_str978[sizeof("klodzko.pl")];
- char stringpool_str979[sizeof("children.museum")];
- char stringpool_str980[sizeof("flekkefjord.no")];
- char stringpool_str981[sizeof("monticello.museum")];
- char stringpool_str982[sizeof("k12.as.us")];
- char stringpool_str983[sizeof("k12.ar.us")];
- char stringpool_str984[sizeof("fuso.aichi.jp")];
- char stringpool_str985[sizeof("certification.aero")];
- char stringpool_str986[sizeof("bilbao.museum")];
- char stringpool_str987[sizeof("college")];
- char stringpool_str988[sizeof("historichouses.museum")];
- char stringpool_str989[sizeof("championship.aero")];
- char stringpool_str990[sizeof("bihoro.hokkaido.jp")];
- char stringpool_str991[sizeof("grosseto.it")];
- char stringpool_str992[sizeof("k12.ak.us")];
- char stringpool_str993[sizeof("chernivtsi.ua")];
- char stringpool_str994[sizeof("gyeonggi.kr")];
- char stringpool_str995[sizeof("k12.ma.us")];
- char stringpool_str996[sizeof("kitchen")];
- char stringpool_str997[sizeof("k12.md.us")];
- char stringpool_str998[sizeof("k12.pr.us")];
- char stringpool_str999[sizeof("k12.al.us")];
- char stringpool_str1000[sizeof("barcelona.museum")];
- char stringpool_str1001[sizeof("ami.ibaraki.jp")];
- char stringpool_str1002[sizeof("federation.aero")];
- char stringpool_str1003[sizeof("kvafjord.no")];
- char stringpool_str1004[sizeof("malopolska.pl")];
- char stringpool_str1005[sizeof("karlsoy.no")];
- char stringpool_str1006[sizeof("bungoono.oita.jp")];
- char stringpool_str1007[sizeof("holmestrand.no")];
- char stringpool_str1008[sizeof("mesaverde.museum")];
- char stringpool_str1009[sizeof("microlight.aero")];
- char stringpool_str1010[sizeof("americanantiques.museum")];
- char stringpool_str1011[sizeof("webhop.org")];
- char stringpool_str1012[sizeof("ethnology.museum")];
- char stringpool_str1013[sizeof("ina.nagano.jp")];
- char stringpool_str1014[sizeof("kusu.oita.jp")];
- char stringpool_str1015[sizeof("beeldengeluid.museum")];
- char stringpool_str1016[sizeof("elverum.no")];
- char stringpool_str1017[sizeof("ibaraki.osaka.jp")];
- char stringpool_str1018[sizeof("city.sapporo.jp")];
- char stringpool_str1019[sizeof("haboro.hokkaido.jp")];
- char stringpool_str1020[sizeof("chernigov.ua")];
- char stringpool_str1021[sizeof("cx")];
- char stringpool_str1022[sizeof("consulting")];
- char stringpool_str1023[sizeof("kherson.ua")];
- char stringpool_str1024[sizeof("aurskog-holand.no")];
- char stringpool_str1025[sizeof("k12.pa.us")];
- char stringpool_str1026[sizeof("kalmykia.ru")];
- char stringpool_str1027[sizeof("gov.sh")];
- char stringpool_str1028[sizeof("ax")];
- char stringpool_str1029[sizeof("k12.mi.us")];
- char stringpool_str1030[sizeof("com.sh")];
- char stringpool_str1031[sizeof("foundation.museum")];
- char stringpool_str1032[sizeof("k-uralsk.ru")];
- char stringpool_str1033[sizeof("geisei.kochi.jp")];
- char stringpool_str1034[sizeof("aknoluokta.no")];
- char stringpool_str1035[sizeof("adachi.tokyo.jp")];
- char stringpool_str1036[sizeof("chelyabinsk.ru")];
- char stringpool_str1037[sizeof("andasuolo.no")];
- char stringpool_str1038[sizeof("k12.va.us")];
- char stringpool_str1039[sizeof("wolomin.pl")];
- char stringpool_str1040[sizeof("windmill.museum")];
- char stringpool_str1041[sizeof("manchester.museum")];
- char stringpool_str1042[sizeof("matsusaka.mie.jp")];
- char stringpool_str1043[sizeof("ink")];
- char stringpool_str1044[sizeof("civilwar.museum")];
- char stringpool_str1045[sizeof("fortmissoula.museum")];
- char stringpool_str1046[sizeof("axa")];
- char stringpool_str1047[sizeof("midtre-gauldal.no")];
- char stringpool_str1048[sizeof("midori.gunma.jp")];
- char stringpool_str1049[sizeof("elburg.museum")];
- char stringpool_str1050[sizeof("kristiansand.no")];
- char stringpool_str1051[sizeof("karelia.ru")];
- char stringpool_str1052[sizeof("hiraya.nagano.jp")];
- char stringpool_str1053[sizeof("missoula.museum")];
- char stringpool_str1054[sizeof("filatelia.museum")];
- char stringpool_str1055[sizeof("murata.miyagi.jp")];
- char stringpool_str1056[sizeof("freemasonry.museum")];
- char stringpool_str1057[sizeof("mizuho.tokyo.jp")];
- char stringpool_str1058[sizeof("gov.bh")];
- char stringpool_str1059[sizeof("engineer.aero")];
- char stringpool_str1060[sizeof("edu.bh")];
- char stringpool_str1061[sizeof("iizuna.nagano.jp")];
- char stringpool_str1062[sizeof("motoyama.kochi.jp")];
- char stringpool_str1063[sizeof("k12.vi.us")];
- char stringpool_str1064[sizeof("com.bh")];
- char stringpool_str1065[sizeof("atsuma.hokkaido.jp")];
- char stringpool_str1066[sizeof("equipment")];
- char stringpool_str1067[sizeof("collection.museum")];
- char stringpool_str1068[sizeof("bajddar.no")];
- char stringpool_str1069[sizeof("mx")];
- char stringpool_str1070[sizeof("from.hr")];
- char stringpool_str1071[sizeof("imabari.ehime.jp")];
- char stringpool_str1072[sizeof("midori.chiba.jp")];
- char stringpool_str1073[sizeof("cherkassy.ua")];
- char stringpool_str1074[sizeof("izumisano.osaka.jp")];
- char stringpool_str1075[sizeof("misato.miyagi.jp")];
- char stringpool_str1076[sizeof("isleofman.museum")];
- char stringpool_str1077[sizeof("konyvelo.hu")];
- char stringpool_str1078[sizeof("minamiise.mie.jp")];
- char stringpool_str1079[sizeof("info.au")];
- char stringpool_str1080[sizeof("voto")];
- char stringpool_str1081[sizeof("monash")];
- char stringpool_str1082[sizeof("mitane.akita.jp")];
- char stringpool_str1083[sizeof("bike")];
- char stringpool_str1084[sizeof("hasama.oita.jp")];
- char stringpool_str1085[sizeof("mitaka.tokyo.jp")];
- char stringpool_str1086[sizeof("vote")];
- char stringpool_str1087[sizeof("k12.ks.us")];
- char stringpool_str1088[sizeof("is-found.org")];
- char stringpool_str1089[sizeof("kw")];
- char stringpool_str1090[sizeof("misato.akita.jp")];
- char stringpool_str1091[sizeof("matta-varjjat.no")];
- char stringpool_str1092[sizeof("vu")];
- char stringpool_str1093[sizeof("conf.au")];
- char stringpool_str1094[sizeof("minato.osaka.jp")];
- char stringpool_str1095[sizeof("mihama.chiba.jp")];
- char stringpool_str1096[sizeof("goshiki.hyogo.jp")];
- char stringpool_str1097[sizeof("gotemba.shizuoka.jp")];
- char stringpool_str1098[sizeof("hazu.aichi.jp")];
- char stringpool_str1099[sizeof("v.bg")];
- char stringpool_str1100[sizeof("vn")];
- char stringpool_str1101[sizeof("consulting.aero")];
- char stringpool_str1102[sizeof("mihama.aichi.jp")];
- char stringpool_str1103[sizeof("cherkasy.ua")];
- char stringpool_str1104[sizeof("mil.kg")];
- char stringpool_str1105[sizeof("mito.ibaraki.jp")];
- char stringpool_str1106[sizeof("ve")];
- char stringpool_str1107[sizeof("edogawa.tokyo.jp")];
- char stringpool_str1108[sizeof("hidaka.kochi.jp")];
- char stringpool_str1109[sizeof("chernihiv.ua")];
- char stringpool_str1110[sizeof("hyllestad.no")];
- char stringpool_str1111[sizeof("inagawa.hyogo.jp")];
- char stringpool_str1112[sizeof("cranbrook.museum")];
- char stringpool_str1113[sizeof("urbino-pesaro.it")];
- char stringpool_str1114[sizeof("annaka.gunma.jp")];
- char stringpool_str1115[sizeof("krasnoyarsk.ru")];
- char stringpool_str1116[sizeof("mari-el.ru")];
- char stringpool_str1117[sizeof("fareast.ru")];
- char stringpool_str1118[sizeof("hamada.shimane.jp")];
- char stringpool_str1119[sizeof("andriabarlettatrani.it")];
- char stringpool_str1120[sizeof("mo-i-rana.no")];
- char stringpool_str1121[sizeof("miho.ibaraki.jp")];
- char stringpool_str1122[sizeof("amur.ru")];
- char stringpool_str1123[sizeof("karasjohka.no")];
- char stringpool_str1124[sizeof("blogspot.mx")];
- char stringpool_str1125[sizeof("kristiansund.no")];
- char stringpool_str1126[sizeof("assisi.museum")];
- char stringpool_str1127[sizeof("minami.kyoto.jp")];
- char stringpool_str1128[sizeof("konskowola.pl")];
- char stringpool_str1129[sizeof("finearts.museum")];
- char stringpool_str1130[sizeof("humanities.museum")];
- char stringpool_str1131[sizeof("vi")];
- char stringpool_str1132[sizeof("gov.ph")];
- char stringpool_str1133[sizeof("environment.museum")];
- char stringpool_str1134[sizeof("edu.ph")];
- char stringpool_str1135[sizeof("misato.shimane.jp")];
- char stringpool_str1136[sizeof("com.ph")];
- char stringpool_str1137[sizeof("zoological.museum")];
- char stringpool_str1138[sizeof("chosei.chiba.jp")];
- char stringpool_str1139[sizeof("kvitsoy.no")];
- char stringpool_str1140[sizeof("gangwon.kr")];
- char stringpool_str1141[sizeof("gok.pk")];
- char stringpool_str1142[sizeof("biratori.hokkaido.jp")];
- char stringpool_str1143[sizeof("andria-trani-barletta.it")];
- char stringpool_str1144[sizeof("vlog.br")];
- char stringpool_str1145[sizeof("gjerdrum.no")];
- char stringpool_str1146[sizeof("minano.saitama.jp")];
- char stringpool_str1147[sizeof("bjerkreim.no")];
- char stringpool_str1148[sizeof("city.kitakyushu.jp")];
- char stringpool_str1149[sizeof("vegas")];
- char stringpool_str1150[sizeof("vdonsk.ru")];
- char stringpool_str1151[sizeof("hiranai.aomori.jp")];
- char stringpool_str1152[sizeof("furubira.hokkaido.jp")];
- char stringpool_str1153[sizeof("aya.miyazaki.jp")];
- char stringpool_str1154[sizeof("elk.pl")];
- char stringpool_str1155[sizeof("vet.br")];
- char stringpool_str1156[sizeof("hasuda.saitama.jp")];
- char stringpool_str1157[sizeof("misato.saitama.jp")];
- char stringpool_str1158[sizeof("esashi.hokkaido.jp")];
- char stringpool_str1159[sizeof("karate.museum")];
- char stringpool_str1160[sizeof("childrens.museum")];
- char stringpool_str1161[sizeof("watarai.mie.jp")];
- char stringpool_str1162[sizeof("hachioji.tokyo.jp")];
- char stringpool_str1163[sizeof("geelvinck.museum")];
- char stringpool_str1164[sizeof("kms.ru")];
- char stringpool_str1165[sizeof("matsue.shimane.jp")];
- char stringpool_str1166[sizeof("construction")];
- char stringpool_str1167[sizeof("military.museum")];
- char stringpool_str1168[sizeof("itabashi.tokyo.jp")];
- char stringpool_str1169[sizeof("isen.kagoshima.jp")];
- char stringpool_str1170[sizeof("arakawa.tokyo.jp")];
- char stringpool_str1171[sizeof("va")];
- char stringpool_str1172[sizeof("mobi.gp")];
- char stringpool_str1173[sizeof("masuda.shimane.jp")];
- char stringpool_str1174[sizeof("kautokeino.no")];
- char stringpool_str1175[sizeof("macerata.it")];
- char stringpool_str1176[sizeof("koenig.ru")];
- char stringpool_str1177[sizeof("mil.sh")];
- char stringpool_str1178[sizeof("mypets.ws")];
- char stringpool_str1179[sizeof("chel.ru")];
- char stringpool_str1180[sizeof("voronezh.ru")];
- char stringpool_str1181[sizeof("fitjar.no")];
- char stringpool_str1182[sizeof("asti.it")];
- char stringpool_str1183[sizeof("misawa.aomori.jp")];
- char stringpool_str1184[sizeof("kotoura.tottori.jp")];
- char stringpool_str1185[sizeof("fukuoka.jp")];
- char stringpool_str1186[sizeof("vennesla.no")];
- char stringpool_str1187[sizeof("vladimir.ru")];
- char stringpool_str1188[sizeof("chuo.chiba.jp")];
- char stringpool_str1189[sizeof("isa.kagoshima.jp")];
- char stringpool_str1190[sizeof("bifuka.hokkaido.jp")];
- char stringpool_str1191[sizeof("mediocampidano.it")];
- char stringpool_str1192[sizeof("bashkiria.ru")];
- char stringpool_str1193[sizeof("kurobe.toyama.jp")];
- char stringpool_str1194[sizeof("arakawa.saitama.jp")];
- char stringpool_str1195[sizeof("hidaka.hokkaido.jp")];
- char stringpool_str1196[sizeof("vestre-toten.no")];
- char stringpool_str1197[sizeof("babia-gora.pl")];
- char stringpool_str1198[sizeof("mihara.hiroshima.jp")];
- char stringpool_str1199[sizeof("civilization.museum")];
- char stringpool_str1200[sizeof("gyeongnam.kr")];
- char stringpool_str1201[sizeof("khv.ru")];
- char stringpool_str1202[sizeof("fuefuki.yamanashi.jp")];
- char stringpool_str1203[sizeof("verdal.no")];
- char stringpool_str1204[sizeof("genkai.saga.jp")];
- char stringpool_str1205[sizeof("chuo.osaka.jp")];
- char stringpool_str1206[sizeof("irkutsk.ru")];
- char stringpool_str1207[sizeof("mimata.miyazaki.jp")];
- char stringpool_str1208[sizeof("hamatama.saga.jp")];
- char stringpool_str1209[sizeof("cadaques.museum")];
- char stringpool_str1210[sizeof("chattanooga.museum")];
- char stringpool_str1211[sizeof("aizumi.tokushima.jp")];
- char stringpool_str1212[sizeof("minami.tokushima.jp")];
- char stringpool_str1213[sizeof("viterbo.it")];
- char stringpool_str1214[sizeof("usa.oita.jp")];
- char stringpool_str1215[sizeof("futtsu.chiba.jp")];
- char stringpool_str1216[sizeof("worse-than.tv")];
- char stringpool_str1217[sizeof("british-library.uk")];
- char stringpool_str1218[sizeof("britishcolumbia.museum")];
- char stringpool_str1219[sizeof("choshi.chiba.jp")];
- char stringpool_str1220[sizeof("archaeology.museum")];
- char stringpool_str1221[sizeof("kuzbass.ru")];
- char stringpool_str1222[sizeof("kharkov.ua")];
- char stringpool_str1223[sizeof("kids.us")];
- char stringpool_str1224[sizeof("kameoka.kyoto.jp")];
- char stringpool_str1225[sizeof("vodka")];
- char stringpool_str1226[sizeof("kvalsund.no")];
- char stringpool_str1227[sizeof("komoro.nagano.jp")];
- char stringpool_str1228[sizeof("kurotaki.nara.jp")];
- char stringpool_str1229[sizeof("verona.it")];
- char stringpool_str1230[sizeof("gliwice.pl")];
- char stringpool_str1231[sizeof("kanmaki.nara.jp")];
- char stringpool_str1232[sizeof("zachpomor.pl")];
- char stringpool_str1233[sizeof("hashikami.aomori.jp")];
- char stringpool_str1234[sizeof("minamiizu.shizuoka.jp")];
- char stringpool_str1235[sizeof("eigersund.no")];
- char stringpool_str1236[sizeof("venezia.it")];
- char stringpool_str1237[sizeof("arts.museum")];
- char stringpool_str1238[sizeof("matsubara.osaka.jp")];
- char stringpool_str1239[sizeof("mishima.shizuoka.jp")];
- char stringpool_str1240[sizeof("chiyoda.tokyo.jp")];
- char stringpool_str1241[sizeof("villas")];
- char stringpool_str1242[sizeof("mil.ph")];
- char stringpool_str1243[sizeof("fortworth.museum")];
- char stringpool_str1244[sizeof("k12.wa.us")];
- char stringpool_str1245[sizeof("bari.it")];
- char stringpool_str1246[sizeof("vinnica.ua")];
- char stringpool_str1247[sizeof("coal.museum")];
- char stringpool_str1248[sizeof("mosjoen.no")];
- char stringpool_str1249[sizeof("michigan.museum")];
- char stringpool_str1250[sizeof("kirovograd.ua")];
- char stringpool_str1251[sizeof("kota.aichi.jp")];
- char stringpool_str1252[sizeof("iraq.museum")];
- char stringpool_str1253[sizeof("iron.museum")];
- char stringpool_str1254[sizeof("bonn.museum")];
- char stringpool_str1255[sizeof("glas.museum")];
- char stringpool_str1256[sizeof("hachijo.tokyo.jp")];
- char stringpool_str1257[sizeof("vestre-slidre.no")];
- char stringpool_str1258[sizeof("watari.miyagi.jp")];
- char stringpool_str1259[sizeof("moseushi.hokkaido.jp")];
- char stringpool_str1260[sizeof("fribourg.museum")];
- char stringpool_str1261[sizeof("ventures")];
- char stringpool_str1262[sizeof("ebetsu.hokkaido.jp")];
- char stringpool_str1263[sizeof("varese.it")];
- char stringpool_str1264[sizeof("amakusa.kumamoto.jp")];
- char stringpool_str1265[sizeof("frog.museum")];
- char stringpool_str1266[sizeof("hidaka.saitama.jp")];
- char stringpool_str1267[sizeof("archaeological.museum")];
- char stringpool_str1268[sizeof("childrensgarden.museum")];
- char stringpool_str1269[sizeof("arboretum.museum")];
- char stringpool_str1270[sizeof("ariake.saga.jp")];
- char stringpool_str1271[sizeof("iyo.ehime.jp")];
- char stringpool_str1272[sizeof("kazo.saitama.jp")];
- char stringpool_str1273[sizeof("budejju.no")];
- char stringpool_str1274[sizeof("bern.museum")];
- char stringpool_str1275[sizeof("ikaruga.nara.jp")];
- char stringpool_str1276[sizeof("k12.wi.us")];
- char stringpool_str1277[sizeof("assabu.hokkaido.jp")];
- char stringpool_str1278[sizeof("kharkiv.ua")];
- char stringpool_str1279[sizeof("moma.museum")];
- char stringpool_str1280[sizeof("akishima.tokyo.jp")];
- char stringpool_str1281[sizeof("evje-og-hornnes.no")];
- char stringpool_str1282[sizeof("haugesund.no")];
- char stringpool_str1283[sizeof("misaki.osaka.jp")];
- char stringpool_str1284[sizeof("abashiri.hokkaido.jp")];
- char stringpool_str1285[sizeof("mizusawa.iwate.jp")];
- char stringpool_str1286[sizeof("aquarium.museum")];
- char stringpool_str1287[sizeof("koza.wakayama.jp")];
- char stringpool_str1288[sizeof("kashiba.nara.jp")];
- char stringpool_str1289[sizeof("ujitawara.kyoto.jp")];
- char stringpool_str1290[sizeof("no")];
- char stringpool_str1291[sizeof("nz")];
- char stringpool_str1292[sizeof("civilisation.museum")];
- char stringpool_str1293[sizeof("hirado.nagasaki.jp")];
- char stringpool_str1294[sizeof("nr")];
- char stringpool_str1295[sizeof("gov.sx")];
- char stringpool_str1296[sizeof("ishikawa.jp")];
- char stringpool_str1297[sizeof("muroran.hokkaido.jp")];
- char stringpool_str1298[sizeof("nom.ro")];
- char stringpool_str1299[sizeof("nl")];
- char stringpool_str1300[sizeof("kita.kyoto.jp")];
- char stringpool_str1301[sizeof("hiroshima.jp")];
- char stringpool_str1302[sizeof("iki.nagasaki.jp")];
- char stringpool_str1303[sizeof("exeter.museum")];
- char stringpool_str1304[sizeof("kira.aichi.jp")];
- char stringpool_str1305[sizeof("nu")];
- char stringpool_str1306[sizeof("anjo.aichi.jp")];
- char stringpool_str1307[sizeof("moroyama.saitama.jp")];
- char stringpool_str1308[sizeof("vercelli.it")];
- char stringpool_str1309[sizeof("n.bg")];
- char stringpool_str1310[sizeof("grajewo.pl")];
- char stringpool_str1311[sizeof("nom.re")];
- char stringpool_str1312[sizeof("n.se")];
- char stringpool_str1313[sizeof("koganei.tokyo.jp")];
- char stringpool_str1314[sizeof("kyotamba.kyoto.jp")];
- char stringpool_str1315[sizeof("kuroishi.aomori.jp")];
- char stringpool_str1316[sizeof("cody.museum")];
- char stringpool_str1317[sizeof("nsn.us")];
- char stringpool_str1318[sizeof("ne")];
- char stringpool_str1319[sizeof("net")];
- char stringpool_str1320[sizeof("urausu.hokkaido.jp")];
- char stringpool_str1321[sizeof("kodaira.tokyo.jp")];
- char stringpool_str1322[sizeof("net.do")];
- char stringpool_str1323[sizeof("nom.es")];
- char stringpool_str1324[sizeof("net.om")];
- char stringpool_str1325[sizeof("net.dm")];
- char stringpool_str1326[sizeof("net.so")];
- char stringpool_str1327[sizeof("ivano-frankivsk.ua")];
- char stringpool_str1328[sizeof("nom.tm")];
- char stringpool_str1329[sizeof("net.sc")];
- char stringpool_str1330[sizeof("net.lr")];
- char stringpool_str1331[sizeof("net.sb")];
- char stringpool_str1332[sizeof("minamiyamashiro.kyoto.jp")];
- char stringpool_str1333[sizeof("qld.edu.au")];
- char stringpool_str1334[sizeof("kviteseid.no")];
- char stringpool_str1335[sizeof("net.lc")];
- char stringpool_str1336[sizeof("kuju.oita.jp")];
- char stringpool_str1337[sizeof("notteroy.no")];
- char stringpool_str1338[sizeof("net.lb")];
- char stringpool_str1339[sizeof("nls.uk")];
- char stringpool_str1340[sizeof("net.lk")];
- char stringpool_str1341[sizeof("net.sl")];
- char stringpool_str1342[sizeof("not.br")];
- char stringpool_str1343[sizeof("nom.br")];
- char stringpool_str1344[sizeof("columbus.museum")];
- char stringpool_str1345[sizeof("machida.tokyo.jp")];
- char stringpool_str1346[sizeof("net.nr")];
- char stringpool_str1347[sizeof("ni")];
- char stringpool_str1348[sizeof("maritimo.museum")];
- char stringpool_str1349[sizeof("civilaviation.aero")];
- char stringpool_str1350[sizeof("unjarga.no")];
- char stringpool_str1351[sizeof("koga.ibaraki.jp")];
- char stringpool_str1352[sizeof("vacations")];
- char stringpool_str1353[sizeof("copenhagen.museum")];
- char stringpool_str1354[sizeof("minamiboso.chiba.jp")];
- char stringpool_str1355[sizeof("mitsuke.niigata.jp")];
- char stringpool_str1356[sizeof("farm.museum")];
- char stringpool_str1357[sizeof("monmouth.museum")];
- char stringpool_str1358[sizeof("net.ec")];
- char stringpool_str1359[sizeof("katori.chiba.jp")];
- char stringpool_str1360[sizeof("health.museum")];
- char stringpool_str1361[sizeof("net.to")];
- char stringpool_str1362[sizeof("net.tj")];
- char stringpool_str1363[sizeof("net.tm")];
- char stringpool_str1364[sizeof("kamo.kyoto.jp")];
- char stringpool_str1365[sizeof("kizu.kyoto.jp")];
- char stringpool_str1366[sizeof("net.sa")];
- char stringpool_str1367[sizeof("net.sd")];
- char stringpool_str1368[sizeof("bill.museum")];
- char stringpool_str1369[sizeof("hatoyama.saitama.jp")];
- char stringpool_str1370[sizeof("net.la")];
- char stringpool_str1371[sizeof("arao.kumamoto.jp")];
- char stringpool_str1372[sizeof("film.museum")];
- char stringpool_str1373[sizeof("hanamaki.iwate.jp")];
- char stringpool_str1374[sizeof("net.bs")];
- char stringpool_str1375[sizeof("net.br")];
- char stringpool_str1376[sizeof("net.bo")];
- char stringpool_str1377[sizeof("net.ua")];
- char stringpool_str1378[sizeof("net.bm")];
- char stringpool_str1379[sizeof("bahn.museum")];
- char stringpool_str1380[sizeof("net.bb")];
- char stringpool_str1381[sizeof("ntr.br")];
- char stringpool_str1382[sizeof("net.iq")];
- char stringpool_str1383[sizeof("net.is")];
- char stringpool_str1384[sizeof("net.ir")];
- char stringpool_str1385[sizeof("buzz")];
- char stringpool_str1386[sizeof("net.im")];
- char stringpool_str1387[sizeof("minakami.gunma.jp")];
- char stringpool_str1388[sizeof("inashiki.ibaraki.jp")];
- char stringpool_str1389[sizeof("gob.mx")];
- char stringpool_str1390[sizeof("edu.mx")];
- char stringpool_str1391[sizeof("gojome.akita.jp")];
- char stringpool_str1392[sizeof("com.mx")];
- char stringpool_str1393[sizeof("maibara.shiga.jp")];
- char stringpool_str1394[sizeof("gyeongbuk.kr")];
- char stringpool_str1395[sizeof("cahcesuolo.no")];
- char stringpool_str1396[sizeof("bialystok.pl")];
- char stringpool_str1397[sizeof("kagami.kochi.jp")];
- char stringpool_str1398[sizeof("net.rw")];
- char stringpool_str1399[sizeof("warabi.saitama.jp")];
- char stringpool_str1400[sizeof("kumenan.okayama.jp")];
- char stringpool_str1401[sizeof("vyatka.ru")];
- char stringpool_str1402[sizeof("net.qa")];
- char stringpool_str1403[sizeof("asso.ht")];
- char stringpool_str1404[sizeof("katsuragi.nara.jp")];
- char stringpool_str1405[sizeof("info.ht")];
- char stringpool_str1406[sizeof("minowa.nagano.jp")];
- char stringpool_str1407[sizeof("katano.osaka.jp")];
- char stringpool_str1408[sizeof("haibara.shizuoka.jp")];
- char stringpool_str1409[sizeof("nysa.pl")];
- char stringpool_str1410[sizeof("msk.ru")];
- char stringpool_str1411[sizeof("na")];
- char stringpool_str1412[sizeof("minamata.kumamoto.jp")];
- char stringpool_str1413[sizeof("hiji.oita.jp")];
- char stringpool_str1414[sizeof("net.ba")];
- char stringpool_str1415[sizeof("net.jo")];
- char stringpool_str1416[sizeof("williamsburg.museum")];
- char stringpool_str1417[sizeof("neustar")];
- char stringpool_str1418[sizeof("name")];
- char stringpool_str1419[sizeof("mill.museum")];
- char stringpool_str1420[sizeof("vestnes.no")];
- char stringpool_str1421[sizeof("net.mo")];
- char stringpool_str1422[sizeof("kasaoka.okayama.jp")];
- char stringpool_str1423[sizeof("net.id")];
- char stringpool_str1424[sizeof("uenohara.yamanashi.jp")];
- char stringpool_str1425[sizeof("np")];
- char stringpool_str1426[sizeof("nara.jp")];
- char stringpool_str1427[sizeof("name.qa")];
- char stringpool_str1428[sizeof("nel.uk")];
- char stringpool_str1429[sizeof("net.tw")];
- char stringpool_str1430[sizeof("net.mk")];
- char stringpool_str1431[sizeof("net.je")];
- char stringpool_str1432[sizeof("vrn.ru")];
- char stringpool_str1433[sizeof("namsos.no")];
- char stringpool_str1434[sizeof("name.jo")];
- char stringpool_str1435[sizeof("kitami.hokkaido.jp")];
- char stringpool_str1436[sizeof("bale.museum")];
- char stringpool_str1437[sizeof("net.ml")];
- char stringpool_str1438[sizeof("net.me")];
- char stringpool_str1439[sizeof("arai.shizuoka.jp")];
- char stringpool_str1440[sizeof("name.my")];
- char stringpool_str1441[sizeof("nom.pl")];
- char stringpool_str1442[sizeof("nom.pe")];
- char stringpool_str1443[sizeof("kumano.hiroshima.jp")];
- char stringpool_str1444[sizeof("net.ar")];
- char stringpool_str1445[sizeof("net.ac")];
- char stringpool_str1446[sizeof("wazuka.kyoto.jp")];
- char stringpool_str1447[sizeof("name.mk")];
- char stringpool_str1448[sizeof("nom.ad")];
- char stringpool_str1449[sizeof("firm.ht")];
- char stringpool_str1450[sizeof("name.pr")];
- char stringpool_str1451[sizeof("net.ma")];
- char stringpool_str1452[sizeof("arezzo.it")];
- char stringpool_str1453[sizeof("kazuno.akita.jp")];
- char stringpool_str1454[sizeof("net.ps")];
- char stringpool_str1455[sizeof("net.pr")];
- char stringpool_str1456[sizeof("net.al")];
- char stringpool_str1457[sizeof("mashiki.kumamoto.jp")];
- char stringpool_str1458[sizeof("net.ae")];
- char stringpool_str1459[sizeof("name.az")];
- char stringpool_str1460[sizeof("nom.pa")];
- char stringpool_str1461[sizeof("net.pk")];
- char stringpool_str1462[sizeof("iwanai.hokkaido.jp")];
- char stringpool_str1463[sizeof("kita.osaka.jp")];
- char stringpool_str1464[sizeof("barreau.bj")];
- char stringpool_str1465[sizeof("net.pl")];
- char stringpool_str1466[sizeof("net.vc")];
- char stringpool_str1467[sizeof("net.pe")];
- char stringpool_str1468[sizeof("kisarazu.chiba.jp")];
- char stringpool_str1469[sizeof("kumatori.osaka.jp")];
- char stringpool_str1470[sizeof("novara.it")];
- char stringpool_str1471[sizeof("net.mw")];
- char stringpool_str1472[sizeof("net.ve")];
- char stringpool_str1473[sizeof("beauxarts.museum")];
- char stringpool_str1474[sizeof("izumiotsu.osaka.jp")];
- char stringpool_str1475[sizeof("funahashi.toyama.jp")];
- char stringpool_str1476[sizeof("gg")];
- char stringpool_str1477[sizeof("eg")];
- char stringpool_str1478[sizeof("komaki.aichi.jp")];
- char stringpool_str1479[sizeof("cg")];
- char stringpool_str1480[sizeof("kunneppu.hokkaido.jp")];
- char stringpool_str1481[sizeof("gov.gr")];
- char stringpool_str1482[sizeof("kitaakita.akita.jp")];
- char stringpool_str1483[sizeof("edu.gr")];
- char stringpool_str1484[sizeof("net.nf")];
- char stringpool_str1485[sizeof("com.gr")];
- char stringpool_str1486[sizeof("net.pa")];
- char stringpool_str1487[sizeof("hokksund.no")];
- char stringpool_str1488[sizeof("khakassia.ru")];
- char stringpool_str1489[sizeof("ag")];
- char stringpool_str1490[sizeof("xxx")];
- char stringpool_str1491[sizeof("ama.shimane.jp")];
- char stringpool_str1492[sizeof("ggee")];
- char stringpool_str1493[sizeof("gushikami.okinawa.jp")];
- char stringpool_str1494[sizeof("bg")];
- char stringpool_str1495[sizeof("vlaanderen.museum")];
- char stringpool_str1496[sizeof("health.vn")];
- char stringpool_str1497[sizeof("name.mv")];
- char stringpool_str1498[sizeof("edu.gp")];
- char stringpool_str1499[sizeof("gov.ge")];
- char stringpool_str1500[sizeof("com.gp")];
- char stringpool_str1501[sizeof("edu.ge")];
- char stringpool_str1502[sizeof("freiburg.museum")];
- char stringpool_str1503[sizeof("nic.uk")];
- char stringpool_str1504[sizeof("com.ge")];
- char stringpool_str1505[sizeof("utah.museum")];
- char stringpool_str1506[sizeof("khabarovsk.ru")];
- char stringpool_str1507[sizeof("hitoyoshi.kumamoto.jp")];
- char stringpool_str1508[sizeof("workshop.museum")];
- char stringpool_str1509[sizeof("urakawa.hokkaido.jp")];
- char stringpool_str1510[sizeof("kai.yamanashi.jp")];
- char stringpool_str1511[sizeof("agro.pl")];
- char stringpool_str1512[sizeof("antiques.museum")];
- char stringpool_str1513[sizeof("net.ai")];
- char stringpool_str1514[sizeof("nic.tr")];
- char stringpool_str1515[sizeof("nic.tj")];
- char stringpool_str1516[sizeof("net.hk")];
- char stringpool_str1517[sizeof("nittedal.no")];
- char stringpool_str1518[sizeof("info.sd")];
- char stringpool_str1519[sizeof("maebashi.gunma.jp")];
- char stringpool_str1520[sizeof("name.eg")];
- char stringpool_str1521[sizeof("notodden.no")];
- char stringpool_str1522[sizeof("austrheim.no")];
- char stringpool_str1523[sizeof("moriya.ibaraki.jp")];
- char stringpool_str1524[sizeof("nissedal.no")];
- char stringpool_str1525[sizeof("hasami.nagasaki.jp")];
- char stringpool_str1526[sizeof("mining.museum")];
- char stringpool_str1527[sizeof("nom.km")];
- char stringpool_str1528[sizeof("kozaki.chiba.jp")];
- char stringpool_str1529[sizeof("net.vi")];
- char stringpool_str1530[sizeof("kosaka.akita.jp")];
- char stringpool_str1531[sizeof("k12.oh.us")];
- char stringpool_str1532[sizeof("ggf.br")];
- char stringpool_str1533[sizeof("kraanghke.no")];
- char stringpool_str1534[sizeof("kadoma.osaka.jp")];
- char stringpool_str1535[sizeof("norddal.no")];
- char stringpool_str1536[sizeof("agr.br")];
- char stringpool_str1537[sizeof("mg")];
- char stringpool_str1538[sizeof("mari.ru")];
- char stringpool_str1539[sizeof("minamioguni.kumamoto.jp")];
- char stringpool_str1540[sizeof("nagoya")];
- char stringpool_str1541[sizeof("gov.gi")];
- char stringpool_str1542[sizeof("versailles.museum")];
- char stringpool_str1543[sizeof("edu.gi")];
- char stringpool_str1544[sizeof("hirara.okinawa.jp")];
- char stringpool_str1545[sizeof("com.gi")];
- char stringpool_str1546[sizeof("k12.nh.us")];
- char stringpool_str1547[sizeof("ro")];
- char stringpool_str1548[sizeof("misaki.okayama.jp")];
- char stringpool_str1549[sizeof("ginoza.okinawa.jp")];
- char stringpool_str1550[sizeof("ug")];
- char stringpool_str1551[sizeof("rs")];
- char stringpool_str1552[sizeof("kagawa.jp")];
- char stringpool_str1553[sizeof("nesodden.no")];
- char stringpool_str1554[sizeof("vindafjord.no")];
- char stringpool_str1555[sizeof("esan.hokkaido.jp")];
- char stringpool_str1556[sizeof("net.af")];
- char stringpool_str1557[sizeof("ru")];
- char stringpool_str1558[sizeof("naples.it")];
- char stringpool_str1559[sizeof("moriyoshi.akita.jp")];
- char stringpool_str1560[sizeof("kitaaiki.nagano.jp")];
- char stringpool_str1561[sizeof("naustdal.no")];
- char stringpool_str1562[sizeof("chiryu.aichi.jp")];
- char stringpool_str1563[sizeof("kasama.ibaraki.jp")];
- char stringpool_str1564[sizeof("r.bg")];
- char stringpool_str1565[sizeof("kannami.shizuoka.jp")];
- char stringpool_str1566[sizeof("r.se")];
- char stringpool_str1567[sizeof("blogspot.fr")];
- char stringpool_str1568[sizeof("minami-alps.yamanashi.jp")];
- char stringpool_str1569[sizeof("minobu.yamanashi.jp")];
- char stringpool_str1570[sizeof("urasoe.okinawa.jp")];
- char stringpool_str1571[sizeof("izu.shizuoka.jp")];
- char stringpool_str1572[sizeof("nesseby.no")];
- char stringpool_str1573[sizeof("kui.hiroshima.jp")];
- char stringpool_str1574[sizeof("re")];
- char stringpool_str1575[sizeof("akabira.hokkaido.jp")];
- char stringpool_str1576[sizeof("aso.kumamoto.jp")];
- char stringpool_str1577[sizeof("ren")];
- char stringpool_str1578[sizeof("rodeo")];
- char stringpool_str1579[sizeof("hokuto.hokkaido.jp")];
- char stringpool_str1580[sizeof("kijo.miyazaki.jp")];
- char stringpool_str1581[sizeof("city.kawasaki.jp")];
- char stringpool_str1582[sizeof("rest")];
- char stringpool_str1583[sizeof("ruhr")];
- char stringpool_str1584[sizeof("nordre-land.no")];
- char stringpool_str1585[sizeof("kumamoto.jp")];
- char stringpool_str1586[sizeof("home.dyndns.org")];
- char stringpool_str1587[sizeof("uvic.museum")];
- char stringpool_str1588[sizeof("vantaa.museum")];
- char stringpool_str1589[sizeof("kitaura.miyazaki.jp")];
- char stringpool_str1590[sizeof("blogspot.sg")];
- char stringpool_str1591[sizeof("nordkapp.no")];
- char stringpool_str1592[sizeof("fuoisku.no")];
- char stringpool_str1593[sizeof("red")];
- char stringpool_str1594[sizeof("asakuchi.okayama.jp")];
- char stringpool_str1595[sizeof("napoli.it")];
- char stringpool_str1596[sizeof("net.ki")];
- char stringpool_str1597[sizeof("blogspot.fi")];
- char stringpool_str1598[sizeof("ito.shizuoka.jp")];
- char stringpool_str1599[sizeof("utazas.hu")];
- char stringpool_str1600[sizeof("hamamatsu.shizuoka.jp")];
- char stringpool_str1601[sizeof("himeshima.oita.jp")];
- char stringpool_str1602[sizeof("agents.aero")];
- char stringpool_str1603[sizeof("farmequipment.museum")];
- char stringpool_str1604[sizeof("chikusei.ibaraki.jp")];
- char stringpool_str1605[sizeof("repair")];
- char stringpool_str1606[sizeof("chitose.hokkaido.jp")];
- char stringpool_str1607[sizeof("hadano.kanagawa.jp")];
- char stringpool_str1608[sizeof("mod.gi")];
- char stringpool_str1609[sizeof("rennesoy.no")];
- char stringpool_str1610[sizeof("embaixada.st")];
- char stringpool_str1611[sizeof("veterinaire.km")];
- char stringpool_str1612[sizeof("wiki")];
- char stringpool_str1613[sizeof("notaires.km")];
- char stringpool_str1614[sizeof("ishikari.hokkaido.jp")];
- char stringpool_str1615[sizeof("muko.kyoto.jp")];
- char stringpool_str1616[sizeof("nordreisa.no")];
- char stringpool_str1617[sizeof("mil.ge")];
- char stringpool_str1618[sizeof("avoues.fr")];
- char stringpool_str1619[sizeof("honjyo.akita.jp")];
- char stringpool_str1620[sizeof("aeroport.fr")];
- char stringpool_str1621[sizeof("hanamigawa.chiba.jp")];
- char stringpool_str1622[sizeof("kamchatka.ru")];
- char stringpool_str1623[sizeof("wiki.br")];
- char stringpool_str1624[sizeof("vestvagoy.no")];
- char stringpool_str1625[sizeof("higashikurume.tokyo.jp")];
- char stringpool_str1626[sizeof("vic.au")];
- char stringpool_str1627[sizeof("krodsherad.no")];
- char stringpool_str1628[sizeof("uto.kumamoto.jp")];
- char stringpool_str1629[sizeof("kamioka.akita.jp")];
- char stringpool_str1630[sizeof("nov.ru")];
- char stringpool_str1631[sizeof("kunisaki.oita.jp")];
- char stringpool_str1632[sizeof("kitadaito.okinawa.jp")];
- char stringpool_str1633[sizeof("mashike.hokkaido.jp")];
- char stringpool_str1634[sizeof("mikasa.hokkaido.jp")];
- char stringpool_str1635[sizeof("hirakata.osaka.jp")];
- char stringpool_str1636[sizeof("rec.ro")];
- char stringpool_str1637[sizeof("fukuroi.shizuoka.jp")];
- char stringpool_str1638[sizeof("kids.museum")];
- char stringpool_str1639[sizeof("info.hu")];
- char stringpool_str1640[sizeof("namsskogan.no")];
- char stringpool_str1641[sizeof("vevelstad.no")];
- char stringpool_str1642[sizeof("upow.gov.pl")];
- char stringpool_str1643[sizeof("ginowan.okinawa.jp")];
- char stringpool_str1644[sizeof("kuzumaki.iwate.jp")];
- char stringpool_str1645[sizeof("chichibu.saitama.jp")];
- char stringpool_str1646[sizeof("net.ru")];
- char stringpool_str1647[sizeof("nebraska.museum")];
- char stringpool_str1648[sizeof("izunokuni.shizuoka.jp")];
- char stringpool_str1649[sizeof("kumamoto.kumamoto.jp")];
- char stringpool_str1650[sizeof("kami.miyagi.jp")];
- char stringpool_str1651[sizeof("kembuchi.hokkaido.jp")];
- char stringpool_str1652[sizeof("kosa.kumamoto.jp")];
- char stringpool_str1653[sizeof("nome.pt")];
- char stringpool_str1654[sizeof("kushima.miyazaki.jp")];
- char stringpool_str1655[sizeof("network")];
- char stringpool_str1656[sizeof("rocks")];
- char stringpool_str1657[sizeof("hakuba.nagano.jp")];
- char stringpool_str1658[sizeof("net.ws")];
- char stringpool_str1659[sizeof("musashino.tokyo.jp")];
- char stringpool_str1660[sizeof("rec.br")];
- char stringpool_str1661[sizeof("rybnik.pl")];
- char stringpool_str1662[sizeof("net.sg")];
- char stringpool_str1663[sizeof("rindal.no")];
- char stringpool_str1664[sizeof("is-a-candidate.org")];
- char stringpool_str1665[sizeof("kami.kochi.jp")];
- char stringpool_str1666[sizeof("nature.museum")];
- char stringpool_str1667[sizeof("kumiyama.kyoto.jp")];
- char stringpool_str1668[sizeof("zamami.okinawa.jp")];
- char stringpool_str1669[sizeof("horokanai.hokkaido.jp")];
- char stringpool_str1670[sizeof("gokase.miyazaki.jp")];
- char stringpool_str1671[sizeof("volgograd.ru")];
- char stringpool_str1672[sizeof("komi.ru")];
- char stringpool_str1673[sizeof("bolt.hu")];
- char stringpool_str1674[sizeof("kushiro.hokkaido.jp")];
- char stringpool_str1675[sizeof("net.ng")];
- char stringpool_str1676[sizeof("cesena-forli.it")];
- char stringpool_str1677[sizeof("nes.akershus.no")];
- char stringpool_str1678[sizeof("kashima.kumamoto.jp")];
- char stringpool_str1679[sizeof("airtraffic.aero")];
- char stringpool_str1680[sizeof("net.eg")];
- char stringpool_str1681[sizeof("kishiwada.osaka.jp")];
- char stringpool_str1682[sizeof("rel.pl")];
- char stringpool_str1683[sizeof("uslivinghistory.museum")];
- char stringpool_str1684[sizeof("hokuto.yamanashi.jp")];
- char stringpool_str1685[sizeof("frankfurt.museum")];
- char stringpool_str1686[sizeof("mykolaiv.ua")];
- char stringpool_str1687[sizeof("ibestad.no")];
- char stringpool_str1688[sizeof("wakasa.tottori.jp")];
- char stringpool_str1689[sizeof("kagoshima.jp")];
- char stringpool_str1690[sizeof("city.hu")];
- char stringpool_str1691[sizeof("from-me.org")];
- char stringpool_str1692[sizeof("kanoya.kagoshima.jp")];
- char stringpool_str1693[sizeof("fukaya.saitama.jp")];
- char stringpool_str1694[sizeof("nom.mg")];
- char stringpool_str1695[sizeof("net.mu")];
- char stringpool_str1696[sizeof("katsuura.chiba.jp")];
- char stringpool_str1697[sizeof("kashiwara.osaka.jp")];
- char stringpool_str1698[sizeof("fukudomi.saga.jp")];
- char stringpool_str1699[sizeof("kariya.aichi.jp")];
- char stringpool_str1700[sizeof("rzeszow.pl")];
- char stringpool_str1701[sizeof("zama.kanagawa.jp")];
- char stringpool_str1702[sizeof("novosibirsk.ru")];
- char stringpool_str1703[sizeof("rep.kp")];
- char stringpool_str1704[sizeof("noheji.aomori.jp")];
- char stringpool_str1705[sizeof("hachirogata.akita.jp")];
- char stringpool_str1706[sizeof("higashiyoshino.nara.jp")];
- char stringpool_str1707[sizeof("nagano.jp")];
- char stringpool_str1708[sizeof("vik.no")];
- char stringpool_str1709[sizeof("katsushika.tokyo.jp")];
- char stringpool_str1710[sizeof("wake.okayama.jp")];
- char stringpool_str1711[sizeof("monzabrianza.it")];
- char stringpool_str1712[sizeof("film.hu")];
- char stringpool_str1713[sizeof("so")];
- char stringpool_str1714[sizeof("sd")];
- char stringpool_str1715[sizeof("sz")];
- char stringpool_str1716[sizeof("nom.ag")];
- char stringpool_str1717[sizeof("net.au")];
- char stringpool_str1718[sizeof("sr")];
- char stringpool_str1719[sizeof("aogashima.tokyo.jp")];
- char stringpool_str1720[sizeof("nord-fron.no")];
- char stringpool_str1721[sizeof("sl")];
- char stringpool_str1722[sizeof("rentals")];
- char stringpool_str1723[sizeof("iijima.nagano.jp")];
- char stringpool_str1724[sizeof("kutchan.hokkaido.jp")];
- char stringpool_str1725[sizeof("monza-brianza.it")];
- char stringpool_str1726[sizeof("su")];
- char stringpool_str1727[sizeof("matsuura.nagasaki.jp")];
- char stringpool_str1728[sizeof("sohu")];
- char stringpool_str1729[sizeof("fuji.shizuoka.jp")];
- char stringpool_str1730[sizeof("higashiyamato.tokyo.jp")];
- char stringpool_str1731[sizeof("harstad.no")];
- char stringpool_str1732[sizeof("s.bg")];
- char stringpool_str1733[sizeof("fujimi.nagano.jp")];
- char stringpool_str1734[sizeof("sn")];
- char stringpool_str1735[sizeof("viajes")];
- char stringpool_str1736[sizeof("s.se")];
- char stringpool_str1737[sizeof("rec.nf")];
- char stringpool_str1738[sizeof("res.aero")];
- char stringpool_str1739[sizeof("rich")];
- char stringpool_str1740[sizeof("monzaebrianza.it")];
- char stringpool_str1741[sizeof("se")];
- char stringpool_str1742[sizeof("misato.wakayama.jp")];
- char stringpool_str1743[sizeof("net.ag")];
- char stringpool_str1744[sizeof("mitoyo.kagawa.jp")];
- char stringpool_str1745[sizeof("st")];
- char stringpool_str1746[sizeof("ragusa.it")];
- char stringpool_str1747[sizeof("minamifurano.hokkaido.jp")];
- char stringpool_str1748[sizeof("fujieda.shizuoka.jp")];
- char stringpool_str1749[sizeof("kg")];
- char stringpool_str1750[sizeof("vladikavkaz.ru")];
- char stringpool_str1751[sizeof("sld.do")];
- char stringpool_str1752[sizeof("higashiyama.kyoto.jp")];
- char stringpool_str1753[sizeof("sb")];
- char stringpool_str1754[sizeof("voting")];
- char stringpool_str1755[sizeof("chuo.fukuoka.jp")];
- char stringpool_str1756[sizeof("qld.gov.au")];
- char stringpool_str1757[sizeof("agdenes.no")];
- char stringpool_str1758[sizeof("si")];
- char stringpool_str1759[sizeof("srv.br")];
- char stringpool_str1760[sizeof("is-a-linux-user.org")];
- char stringpool_str1761[sizeof("kin.okinawa.jp")];
- char stringpool_str1762[sizeof("utashinai.hokkaido.jp")];
- char stringpool_str1763[sizeof("entomology.museum")];
- char stringpool_str1764[sizeof("kazimierz-dolny.pl")];
- char stringpool_str1765[sizeof("morioka.iwate.jp")];
- char stringpool_str1766[sizeof("rendalen.no")];
- char stringpool_str1767[sizeof("ringerike.no")];
- char stringpool_str1768[sizeof("kamaishi.iwate.jp")];
- char stringpool_str1769[sizeof("sy")];
- char stringpool_str1770[sizeof("nagoya.jp")];
- char stringpool_str1771[sizeof("habikino.osaka.jp")];
- char stringpool_str1772[sizeof("solar")];
- char stringpool_str1773[sizeof("wakayama.jp")];
- char stringpool_str1774[sizeof("snaase.no")];
- char stringpool_str1775[sizeof("sm")];
- char stringpool_str1776[sizeof("snoasa.no")];
- char stringpool_str1777[sizeof("k12.tx.us")];
- char stringpool_str1778[sizeof("slg.br")];
- char stringpool_str1779[sizeof("numata.gunma.jp")];
- char stringpool_str1780[sizeof("neat-url.com")];
- char stringpool_str1781[sizeof("iwatsuki.saitama.jp")];
- char stringpool_str1782[sizeof("nagasaki.jp")];
- char stringpool_str1783[sizeof("kobayashi.miyazaki.jp")];
- char stringpool_str1784[sizeof("norilsk.ru")];
- char stringpool_str1785[sizeof("strand.no")];
- char stringpool_str1786[sizeof("sa")];
- char stringpool_str1787[sizeof("kitakami.iwate.jp")];
- char stringpool_str1788[sizeof("sortland.no")];
- char stringpool_str1789[sizeof("mihama.wakayama.jp")];
- char stringpool_str1790[sizeof("nemuro.hokkaido.jp")];
- char stringpool_str1791[sizeof("sel.no")];
- char stringpool_str1792[sizeof("kvinnherad.no")];
- char stringpool_str1793[sizeof("kokonoe.oita.jp")];
- char stringpool_str1794[sizeof("motobu.okinawa.jp")];
- char stringpool_str1795[sizeof("sv")];
- char stringpool_str1796[sizeof("fujimi.saitama.jp")];
- char stringpool_str1797[sizeof("nalchik.ru")];
- char stringpool_str1798[sizeof("egersund.no")];
- char stringpool_str1799[sizeof("nore-og-uvdal.no")];
- char stringpool_str1800[sizeof("soc.lk")];
- char stringpool_str1801[sizeof("is-lost.org")];
- char stringpool_str1802[sizeof("hashimoto.wakayama.jp")];
- char stringpool_str1803[sizeof("uwajima.ehime.jp")];
- char stringpool_str1804[sizeof("ueno.gunma.jp")];
- char stringpool_str1805[sizeof("net.kg")];
- char stringpool_str1806[sizeof("solund.no")];
- char stringpool_str1807[sizeof("rw")];
- char stringpool_str1808[sizeof("kitahata.saga.jp")];
- char stringpool_str1809[sizeof("sos.pl")];
- char stringpool_str1810[sizeof("kuriyama.hokkaido.jp")];
- char stringpool_str1811[sizeof("nkz.ru")];
- char stringpool_str1812[sizeof("nagaokakyo.kyoto.jp")];
- char stringpool_str1813[sizeof("a.prod.fastly.net")];
- char stringpool_str1814[sizeof("higashinaruse.akita.jp")];
- char stringpool_str1815[sizeof("saga.jp")];
- char stringpool_str1816[sizeof("kamikoani.akita.jp")];
- char stringpool_str1817[sizeof("nahari.kochi.jp")];
- char stringpool_str1818[sizeof("aibetsu.hokkaido.jp")];
- char stringpool_str1819[sizeof("fujisawa.iwate.jp")];
- char stringpool_str1820[sizeof("numata.hokkaido.jp")];
- char stringpool_str1821[sizeof("uki.kumamoto.jp")];
- char stringpool_str1822[sizeof("koshimizu.hokkaido.jp")];
- char stringpool_str1823[sizeof("zentsuji.kagawa.jp")];
- char stringpool_str1824[sizeof("kasumigaura.ibaraki.jp")];
- char stringpool_str1825[sizeof("fukuyama.hiroshima.jp")];
- char stringpool_str1826[sizeof("russia.museum")];
- char stringpool_str1827[sizeof("riik.ee")];
- char stringpool_str1828[sizeof("webhop.info")];
- char stringpool_str1829[sizeof("sh")];
- char stringpool_str1830[sizeof("higashi.okinawa.jp")];
- char stringpool_str1831[sizeof("rnd.ru")];
- char stringpool_str1832[sizeof("sells-it.net")];
- char stringpool_str1833[sizeof("samara.ru")];
- char stringpool_str1834[sizeof("marnardal.no")];
- char stringpool_str1835[sizeof("fukushima.jp")];
- char stringpool_str1836[sizeof("solutions")];
- char stringpool_str1837[sizeof("embetsu.hokkaido.jp")];
- char stringpool_str1838[sizeof("biei.hokkaido.jp")];
- char stringpool_str1839[sizeof("shoes")];
- char stringpool_str1840[sizeof("sirdal.no")];
- char stringpool_str1841[sizeof("sld.pa")];
- char stringpool_str1842[sizeof("wakuya.miyagi.jp")];
- char stringpool_str1843[sizeof("nes.buskerud.no")];
- char stringpool_str1844[sizeof("kamiichi.toyama.jp")];
- char stringpool_str1845[sizeof("kounosu.saitama.jp")];
- char stringpool_str1846[sizeof("koshigaya.saitama.jp")];
- char stringpool_str1847[sizeof("kagamino.okayama.jp")];
- char stringpool_str1848[sizeof("railroad.museum")];
- char stringpool_str1849[sizeof("karumai.iwate.jp")];
- char stringpool_str1850[sizeof("sshn.se")];
- char stringpool_str1851[sizeof("sondrio.it")];
- char stringpool_str1852[sizeof("hidaka.wakayama.jp")];
- char stringpool_str1853[sizeof("musashimurayama.tokyo.jp")];
- char stringpool_str1854[sizeof("higashimurayama.tokyo.jp")];
- char stringpool_str1855[sizeof("romskog.no")];
- char stringpool_str1856[sizeof("sk")];
- char stringpool_str1857[sizeof("roma.it")];
- char stringpool_str1858[sizeof("kadena.okinawa.jp")];
- char stringpool_str1859[sizeof("kusatsu.shiga.jp")];
- char stringpool_str1860[sizeof("nagara.chiba.jp")];
- char stringpool_str1861[sizeof("noshiro.akita.jp")];
- char stringpool_str1862[sizeof("rome.it")];
- char stringpool_str1863[sizeof("suldal.no")];
- char stringpool_str1864[sizeof("kotohira.kagawa.jp")];
- char stringpool_str1865[sizeof("singles")];
- char stringpool_str1866[sizeof("sigdal.no")];
- char stringpool_str1867[sizeof("kitamoto.saitama.jp")];
- char stringpool_str1868[sizeof("naturalhistory.museum")];
- char stringpool_str1869[sizeof("mihama.fukui.jp")];
- char stringpool_str1870[sizeof("net.sh")];
- char stringpool_str1871[sizeof("higashisumiyoshi.osaka.jp")];
- char stringpool_str1872[sizeof("selfip.com")];
- char stringpool_str1873[sizeof("kure.hiroshima.jp")];
- char stringpool_str1874[sizeof("kunitomi.miyazaki.jp")];
- char stringpool_str1875[sizeof("kanazawa.ishikawa.jp")];
- char stringpool_str1876[sizeof("ushiku.ibaraki.jp")];
- char stringpool_str1877[sizeof("systems")];
- char stringpool_str1878[sizeof("saarland")];
- char stringpool_str1879[sizeof("nagano.nagano.jp")];
- char stringpool_str1880[sizeof("vaksdal.no")];
- char stringpool_str1881[sizeof("mukawa.hokkaido.jp")];
- char stringpool_str1882[sizeof("higashiizu.shizuoka.jp")];
- char stringpool_str1883[sizeof("rubtsovsk.ru")];
- char stringpool_str1884[sizeof("selfip.net")];
- char stringpool_str1885[sizeof("noda.chiba.jp")];
- char stringpool_str1886[sizeof("sunndal.no")];
- char stringpool_str1887[sizeof("surnadal.no")];
- char stringpool_str1888[sizeof("k12.ga.us")];
- char stringpool_str1889[sizeof("net.th")];
- char stringpool_str1890[sizeof("sec.ps")];
- char stringpool_str1891[sizeof("axis.museum")];
- char stringpool_str1892[sizeof("kaszuby.pl")];
- char stringpool_str1893[sizeof("nesoddtangen.no")];
- char stringpool_str1894[sizeof("sogndal.no")];
- char stringpool_str1895[sizeof("kamikawa.hokkaido.jp")];
- char stringpool_str1896[sizeof("net.bh")];
- char stringpool_str1897[sizeof("svalbard.no")];
- char stringpool_str1898[sizeof("marylhurst.museum")];
- char stringpool_str1899[sizeof("stranda.no")];
- char stringpool_str1900[sizeof("stordal.no")];
- char stringpool_str1901[sizeof("kaluga.ru")];
- char stringpool_str1902[sizeof("nannestad.no")];
- char stringpool_str1903[sizeof("kakuda.miyagi.jp")];
- char stringpool_str1904[sizeof("nose.osaka.jp")];
- char stringpool_str1905[sizeof("natori.miyagi.jp")];
- char stringpool_str1906[sizeof("etajima.hiroshima.jp")];
- char stringpool_str1907[sizeof("savona.it")];
- char stringpool_str1908[sizeof("sorreisa.no")];
- char stringpool_str1909[sizeof("ena.gifu.jp")];
- char stringpool_str1910[sizeof("godo.gifu.jp")];
- char stringpool_str1911[sizeof("nishio.aichi.jp")];
- char stringpool_str1912[sizeof("koga.fukuoka.jp")];
- char stringpool_str1913[sizeof("sondre-land.no")];
- char stringpool_str1914[sizeof("riodejaneiro.museum")];
- char stringpool_str1915[sizeof("sauherad.no")];
- char stringpool_str1916[sizeof("nieruchomosci.pl")];
- char stringpool_str1917[sizeof("alaheadju.no")];
- char stringpool_str1918[sizeof("groundhandling.aero")];
- char stringpool_str1919[sizeof("anthropology.museum")];
- char stringpool_str1920[sizeof("matsumoto.nagano.jp")];
- char stringpool_str1921[sizeof("settlers.museum")];
- char stringpool_str1922[sizeof("amsterdam.museum")];
- char stringpool_str1923[sizeof("morotsuka.miyazaki.jp")];
- char stringpool_str1924[sizeof("siellak.no")];
- char stringpool_str1925[sizeof("higashiosaka.osaka.jp")];
- char stringpool_str1926[sizeof("hikawa.shimane.jp")];
- char stringpool_str1927[sizeof("gifu.gifu.jp")];
- char stringpool_str1928[sizeof("isesaki.gunma.jp")];
- char stringpool_str1929[sizeof("ryukyu")];
- char stringpool_str1930[sizeof("higashiizumo.shimane.jp")];
- char stringpool_str1931[sizeof("iglesias-carbonia.it")];
- char stringpool_str1932[sizeof("itakura.gunma.jp")];
- char stringpool_str1933[sizeof("naruto.tokushima.jp")];
- char stringpool_str1934[sizeof("fujimino.saitama.jp")];
- char stringpool_str1935[sizeof("name.hr")];
- char stringpool_str1936[sizeof("niihama.ehime.jp")];
- char stringpool_str1937[sizeof("rollag.no")];
- char stringpool_str1938[sizeof("matsubushi.saitama.jp")];
- char stringpool_str1939[sizeof("kitakata.miyazaki.jp")];
- char stringpool_str1940[sizeof("unazuki.toyama.jp")];
- char stringpool_str1941[sizeof("exhibition.museum")];
- char stringpool_str1942[sizeof("hida.gifu.jp")];
- char stringpool_str1943[sizeof("kurashiki.okayama.jp")];
- char stringpool_str1944[sizeof("skodje.no")];
- char stringpool_str1945[sizeof("net.ph")];
- char stringpool_str1946[sizeof("maniwa.okayama.jp")];
- char stringpool_str1947[sizeof("sassari.it")];
- char stringpool_str1948[sizeof("fujiidera.osaka.jp")];
- char stringpool_str1949[sizeof("y.bg")];
- char stringpool_str1950[sizeof("y.se")];
- char stringpool_str1951[sizeof("saotome.st")];
- char stringpool_str1952[sizeof("kashihara.nara.jp")];
- char stringpool_str1953[sizeof("ye")];
- char stringpool_str1954[sizeof("stokke.no")];
- char stringpool_str1955[sizeof("skanland.no")];
- char stringpool_str1956[sizeof("ninja")];
- char stringpool_str1957[sizeof("kaisei.kanagawa.jp")];
- char stringpool_str1958[sizeof("yt")];
- char stringpool_str1959[sizeof("ski.no")];
- char stringpool_str1960[sizeof("fujikawa.yamanashi.jp")];
- char stringpool_str1961[sizeof("usui.fukuoka.jp")];
- char stringpool_str1962[sizeof("global.ssl.fastly.net")];
- char stringpool_str1963[sizeof("mino.gifu.jp")];
- char stringpool_str1964[sizeof("gov.gh")];
- char stringpool_str1965[sizeof("edu.gh")];
- char stringpool_str1966[sizeof("amagasaki.hyogo.jp")];
- char stringpool_str1967[sizeof("ikusaka.nagano.jp")];
- char stringpool_str1968[sizeof("com.gh")];
- char stringpool_str1969[sizeof("square.museum")];
- char stringpool_str1970[sizeof("kamikawa.saitama.jp")];
- char stringpool_str1971[sizeof("kicks-ass.org")];
- char stringpool_str1972[sizeof("rifu.miyagi.jp")];
- char stringpool_str1973[sizeof("reviews")];
- char stringpool_str1974[sizeof("reggioemilia.it")];
- char stringpool_str1975[sizeof("kaho.fukuoka.jp")];
- char stringpool_str1976[sizeof("suedtirol.it")];
- char stringpool_str1977[sizeof("simbirsk.ru")];
- char stringpool_str1978[sizeof("kamiamakusa.kumamoto.jp")];
- char stringpool_str1979[sizeof("hotel.lk")];
- char stringpool_str1980[sizeof("saratov.ru")];
- char stringpool_str1981[sizeof("gamagori.aichi.jp")];
- char stringpool_str1982[sizeof("kaga.ishikawa.jp")];
- char stringpool_str1983[sizeof("uonuma.niigata.jp")];
- char stringpool_str1984[sizeof("exposed")];
- char stringpool_str1985[sizeof("saltdal.no")];
- char stringpool_str1986[sizeof("kahoku.yamagata.jp")];
- char stringpool_str1987[sizeof("fujikawa.shizuoka.jp")];
- char stringpool_str1988[sizeof("nobeoka.miyazaki.jp")];
- char stringpool_str1989[sizeof("kumejima.okinawa.jp")];
- char stringpool_str1990[sizeof("stalbans.museum")];
- char stringpool_str1991[sizeof("fujinomiya.shizuoka.jp")];
- char stringpool_str1992[sizeof("haebaru.okinawa.jp")];
- char stringpool_str1993[sizeof("rimini.it")];
- char stringpool_str1994[sizeof("cuneo.it")];
- char stringpool_str1995[sizeof("naumburg.museum")];
- char stringpool_str1996[sizeof("ishikawa.okinawa.jp")];
- char stringpool_str1997[sizeof("nagareyama.chiba.jp")];
- char stringpool_str1998[sizeof("nord-aurdal.no")];
- char stringpool_str1999[sizeof("kamisu.ibaraki.jp")];
- char stringpool_str2000[sizeof("akaiwa.okayama.jp")];
- char stringpool_str2001[sizeof("stv.ru")];
- char stringpool_str2002[sizeof("furukawa.miyagi.jp")];
- char stringpool_str2003[sizeof("skedsmo.no")];
- char stringpool_str2004[sizeof("chikuma.nagano.jp")];
- char stringpool_str2005[sizeof("aga.niigata.jp")];
- char stringpool_str2006[sizeof("kushimoto.wakayama.jp")];
- char stringpool_str2007[sizeof("vic.edu.au")];
- char stringpool_str2008[sizeof("hikimi.shimane.jp")];
- char stringpool_str2009[sizeof("kongsberg.no")];
- char stringpool_str2010[sizeof("himeji.hyogo.jp")];
- char stringpool_str2011[sizeof("aoste.it")];
- char stringpool_str2012[sizeof("fermo.it")];
- char stringpool_str2013[sizeof("hirono.fukushima.jp")];
- char stringpool_str2014[sizeof("nagasu.kumamoto.jp")];
- char stringpool_str2015[sizeof("vg")];
- char stringpool_str2016[sizeof("naha.okinawa.jp")];
- char stringpool_str2017[sizeof("marumori.miyagi.jp")];
- char stringpool_str2018[sizeof("kitahiroshima.hokkaido.jp")];
- char stringpool_str2019[sizeof("kuki.saitama.jp")];
- char stringpool_str2020[sizeof("zao.miyagi.jp")];
- char stringpool_str2021[sizeof("nagaoka.niigata.jp")];
- char stringpool_str2022[sizeof("busan.kr")];
- char stringpool_str2023[sizeof("gamo.shiga.jp")];
- char stringpool_str2024[sizeof("naturalhistorymuseum.museum")];
- char stringpool_str2025[sizeof("nago.okinawa.jp")];
- char stringpool_str2026[sizeof("iitate.fukushima.jp")];
- char stringpool_str2027[sizeof("sumy.ua")];
- char stringpool_str2028[sizeof("nerima.tokyo.jp")];
- char stringpool_str2029[sizeof("futaba.fukushima.jp")];
- char stringpool_str2030[sizeof("sexy")];
- char stringpool_str2031[sizeof("snz.ru")];
- char stringpool_str2032[sizeof("sandnes.no")];
- char stringpool_str2033[sizeof("narusawa.yamanashi.jp")];
- char stringpool_str2034[sizeof("nirasaki.yamanashi.jp")];
- char stringpool_str2035[sizeof("vgs.no")];
- char stringpool_str2036[sizeof("sumoto.kumamoto.jp")];
- char stringpool_str2037[sizeof("hirata.fukushima.jp")];
- char stringpool_str2038[sizeof("bozen.it")];
- char stringpool_str2039[sizeof("suisse.museum")];
- char stringpool_str2040[sizeof("czest.pl")];
- char stringpool_str2041[sizeof("mitake.gifu.jp")];
- char stringpool_str2042[sizeof("siedlce.pl")];
- char stringpool_str2043[sizeof("saitama.jp")];
- char stringpool_str2044[sizeof("furudono.fukushima.jp")];
- char stringpool_str2045[sizeof("wodzislaw.pl")];
- char stringpool_str2046[sizeof("karikatur.museum")];
- char stringpool_str2047[sizeof("selfip.org")];
- char stringpool_str2048[sizeof("graz.museum")];
- char stringpool_str2049[sizeof("spb.ru")];
- char stringpool_str2050[sizeof("ashibetsu.hokkaido.jp")];
- char stringpool_str2051[sizeof("ulsan.kr")];
- char stringpool_str2052[sizeof("kimobetsu.hokkaido.jp")];
- char stringpool_str2053[sizeof("aosta.it")];
- char stringpool_str2054[sizeof("hakone.kanagawa.jp")];
- char stringpool_str2055[sizeof("skoczow.pl")];
- char stringpool_str2056[sizeof("sandefjord.no")];
- char stringpool_str2057[sizeof("wassamu.hokkaido.jp")];
- char stringpool_str2058[sizeof("mizunami.gifu.jp")];
- char stringpool_str2059[sizeof("hobol.no")];
- char stringpool_str2060[sizeof("fujisato.akita.jp")];
- char stringpool_str2061[sizeof("snillfjord.no")];
- char stringpool_str2062[sizeof("recreation.aero")];
- char stringpool_str2063[sizeof("bomlo.no")];
- char stringpool_str2064[sizeof("genoa.it")];
- char stringpool_str2065[sizeof("kujukuri.chiba.jp")];
- char stringpool_str2066[sizeof("svelvik.no")];
- char stringpool_str2067[sizeof("greta.fr")];
- char stringpool_str2068[sizeof("kunstsammlung.museum")];
- char stringpool_str2069[sizeof("asnes.no")];
- char stringpool_str2070[sizeof("stathelle.no")];
- char stringpool_str2071[sizeof("fukumitsu.toyama.jp")];
- char stringpool_str2072[sizeof("sex.pl")];
- char stringpool_str2073[sizeof("herad.no")];
- char stringpool_str2074[sizeof("cambridge.museum")];
- char stringpool_str2075[sizeof("recipes")];
- char stringpool_str2076[sizeof("aland.fi")];
- char stringpool_str2077[sizeof("katsuragi.wakayama.jp")];
- char stringpool_str2078[sizeof("nagatoro.saitama.jp")];
- char stringpool_str2079[sizeof("6bone.pl")];
- char stringpool_str2080[sizeof("mil.gh")];
- char stringpool_str2081[sizeof("nagi.okayama.jp")];
- char stringpool_str2082[sizeof("kuromatsunai.hokkaido.jp")];
- char stringpool_str2083[sizeof("aogaki.hyogo.jp")];
- char stringpool_str2084[sizeof("mikawa.yamagata.jp")];
- char stringpool_str2085[sizeof("nakhodka.ru")];
- char stringpool_str2086[sizeof("hisayama.fukuoka.jp")];
- char stringpool_str2087[sizeof("floro.no")];
- char stringpool_str2088[sizeof("forde.no")];
- char stringpool_str2089[sizeof("wajiki.tokushima.jp")];
- char stringpool_str2090[sizeof("kikonai.hokkaido.jp")];
- char stringpool_str2091[sizeof("sendai.jp")];
- char stringpool_str2092[sizeof("kahoku.ishikawa.jp")];
- char stringpool_str2093[sizeof("naganohara.gunma.jp")];
- char stringpool_str2094[sizeof("ardal.no")];
- char stringpool_str2095[sizeof("sandnessjoen.no")];
- char stringpool_str2096[sizeof("altai.ru")];
- char stringpool_str2097[sizeof("chiyoda.gunma.jp")];
- char stringpool_str2098[sizeof("hyogo.jp")];
- char stringpool_str2099[sizeof("kanagawa.jp")];
- char stringpool_str2100[sizeof("kvinesdal.no")];
- char stringpool_str2101[sizeof("stjordal.no")];
- char stringpool_str2102[sizeof("higashimatsuyama.saitama.jp")];
- char stringpool_str2103[sizeof("hirokawa.fukuoka.jp")];
- char stringpool_str2104[sizeof("frogn.no")];
- char stringpool_str2105[sizeof("grane.no")];
- char stringpool_str2106[sizeof("saga.saga.jp")];
- char stringpool_str2107[sizeof("hemne.no")];
- char stringpool_str2108[sizeof("sandiego.museum")];
- char stringpool_str2109[sizeof("milan.it")];
- char stringpool_str2110[sizeof("chungbuk.kr")];
- char stringpool_str2111[sizeof("coloradoplateau.museum")];
- char stringpool_str2112[sizeof("nsk.ru")];
- char stringpool_str2113[sizeof("asahikawa.hokkaido.jp")];
- char stringpool_str2114[sizeof("motosu.gifu.jp")];
- char stringpool_str2115[sizeof("agrinet.tn")];
- char stringpool_str2116[sizeof("mbone.pl")];
- char stringpool_str2117[sizeof("nyny.museum")];
- char stringpool_str2118[sizeof("mizumaki.fukuoka.jp")];
- char stringpool_str2119[sizeof("santabarbara.museum")];
- char stringpool_str2120[sizeof("k12.gu.us")];
- char stringpool_str2121[sizeof("hurum.no")];
- char stringpool_str2122[sizeof("hatogaya.saitama.jp")];
- char stringpool_str2123[sizeof("gunma.jp")];
- char stringpool_str2124[sizeof("nativeamerican.museum")];
- char stringpool_str2125[sizeof("a.ssl.fastly.net")];
- char stringpool_str2126[sizeof("kitagawa.kochi.jp")];
- char stringpool_str2127[sizeof("bytom.pl")];
- char stringpool_str2128[sizeof("fjell.no")];
- char stringpool_str2129[sizeof("minami.fukuoka.jp")];
- char stringpool_str2130[sizeof("b.ssl.fastly.net")];
- char stringpool_str2131[sizeof("higashikagura.hokkaido.jp")];
- char stringpool_str2132[sizeof("gulen.no")];
- char stringpool_str2133[sizeof("narita.chiba.jp")];
- char stringpool_str2134[sizeof("hamar.no")];
- char stringpool_str2135[sizeof("kaizuka.osaka.jp")];
- char stringpool_str2136[sizeof("niikappu.hokkaido.jp")];
- char stringpool_str2137[sizeof("kudoyama.wakayama.jp")];
- char stringpool_str2138[sizeof("udine.it")];
- char stringpool_str2139[sizeof("kitayama.wakayama.jp")];
- char stringpool_str2140[sizeof("csiro.au")];
- char stringpool_str2141[sizeof("adult.ht")];
- char stringpool_str2142[sizeof("nagiso.nagano.jp")];
- char stringpool_str2143[sizeof("rahkkeravju.no")];
- char stringpool_str2144[sizeof("bjugn.no")];
- char stringpool_str2145[sizeof("andoy.no")];
- char stringpool_str2146[sizeof("kamisato.saitama.jp")];
- char stringpool_str2147[sizeof("yufu.oita.jp")];
- char stringpool_str2148[sizeof("social")];
- char stringpool_str2149[sizeof("higashihiroshima.hiroshima.jp")];
- char stringpool_str2150[sizeof("bardu.no")];
- char stringpool_str2151[sizeof("iwaizumi.iwate.jp")];
- char stringpool_str2152[sizeof("zagan.pl")];
- char stringpool_str2153[sizeof("soundandvision.museum")];
- char stringpool_str2154[sizeof("net.mx")];
- char stringpool_str2155[sizeof("ryokami.saitama.jp")];
- char stringpool_str2156[sizeof("southcarolina.museum")];
- char stringpool_str2157[sizeof("game-host.org")];
- char stringpool_str2158[sizeof("stor-elvdal.no")];
- char stringpool_str2159[sizeof("bandai.fukushima.jp")];
- char stringpool_str2160[sizeof("reklam.hu")];
- char stringpool_str2161[sizeof("flora.no")];
- char stringpool_str2162[sizeof("nagawa.nagano.jp")];
- char stringpool_str2163[sizeof("seto.aichi.jp")];
- char stringpool_str2164[sizeof("homelinux.org")];
- char stringpool_str2165[sizeof("surgeonshall.museum")];
- char stringpool_str2166[sizeof("frana.no")];
- char stringpool_str2167[sizeof("molde.no")];
- char stringpool_str2168[sizeof("kinokawa.wakayama.jp")];
- char stringpool_str2169[sizeof("giske.no")];
- char stringpool_str2170[sizeof("fujiyoshida.yamanashi.jp")];
- char stringpool_str2171[sizeof("saintlouis.museum")];
- char stringpool_str2172[sizeof("gujo.gifu.jp")];
- char stringpool_str2173[sizeof("murayama.yamagata.jp")];
- char stringpool_str2174[sizeof("hitra.no")];
- char stringpool_str2175[sizeof("modum.no")];
- char stringpool_str2176[sizeof("kashima.saga.jp")];
- char stringpool_str2177[sizeof("iwanuma.miyagi.jp")];
- char stringpool_str2178[sizeof("haram.no")];
- char stringpool_str2179[sizeof("minokamo.gifu.jp")];
- char stringpool_str2180[sizeof("higashiyodogawa.osaka.jp")];
- char stringpool_str2181[sizeof("shoo.okayama.jp")];
- char stringpool_str2182[sizeof("froya.no")];
- char stringpool_str2183[sizeof("ustka.pl")];
- char stringpool_str2184[sizeof("iizuka.fukuoka.jp")];
- char stringpool_str2185[sizeof("akashi.hyogo.jp")];
- char stringpool_str2186[sizeof("rennebu.no")];
- char stringpool_str2187[sizeof("barum.no")];
- char stringpool_str2188[sizeof("sx")];
- char stringpool_str2189[sizeof("yachts")];
- char stringpool_str2190[sizeof("ng")];
- char stringpool_str2191[sizeof("stpetersburg.museum")];
- char stringpool_str2192[sizeof("hotel.hu")];
- char stringpool_str2193[sizeof("vologda.ru")];
- char stringpool_str2194[sizeof("roma.museum")];
- char stringpool_str2195[sizeof("yao.osaka.jp")];
- char stringpool_str2196[sizeof("wajima.ishikawa.jp")];
- char stringpool_str2197[sizeof("koto.shiga.jp")];
- char stringpool_str2198[sizeof("nagasaki.nagasaki.jp")];
- char stringpool_str2199[sizeof("balat.no")];
- char stringpool_str2200[sizeof("meloy.no")];
- char stringpool_str2201[sizeof("hakusan.ishikawa.jp")];
- char stringpool_str2202[sizeof("wlocl.pl")];
- char stringpool_str2203[sizeof("ringebu.no")];
- char stringpool_str2204[sizeof("kimino.wakayama.jp")];
- char stringpool_str2205[sizeof("minamiaiki.nagano.jp")];
- char stringpool_str2206[sizeof("ngo.lk")];
- char stringpool_str2207[sizeof("net.gr")];
- char stringpool_str2208[sizeof("sosa.chiba.jp")];
- char stringpool_str2209[sizeof("nishiizu.shizuoka.jp")];
- char stringpool_str2210[sizeof("heroy.nordland.no")];
- char stringpool_str2211[sizeof("nakano.tokyo.jp")];
- char stringpool_str2212[sizeof("santacruz.museum")];
- char stringpool_str2213[sizeof("slattum.no")];
- char stringpool_str2214[sizeof("masoy.no")];
- char stringpool_str2215[sizeof("kamikitayama.nara.jp")];
- char stringpool_str2216[sizeof("wanouchi.gifu.jp")];
- char stringpool_str2217[sizeof("net.gp")];
- char stringpool_str2218[sizeof("murakami.niigata.jp")];
- char stringpool_str2219[sizeof("inatsuki.fukuoka.jp")];
- char stringpool_str2220[sizeof("net.ge")];
- char stringpool_str2221[sizeof("ashiya.hyogo.jp")];
- char stringpool_str2222[sizeof("matsushima.miyagi.jp")];
- char stringpool_str2223[sizeof("minamisanriku.miyagi.jp")];
- char stringpool_str2224[sizeof("gmina.pl")];
- char stringpool_str2225[sizeof("show.aero")];
- char stringpool_str2226[sizeof("namerikawa.toyama.jp")];
- char stringpool_str2227[sizeof("salangen.no")];
- char stringpool_str2228[sizeof("matsumoto.kagoshima.jp")];
- char stringpool_str2229[sizeof("susaki.kochi.jp")];
- char stringpool_str2230[sizeof("galsa.no")];
- char stringpool_str2231[sizeof("halsa.no")];
- char stringpool_str2232[sizeof("hotel.tz")];
- char stringpool_str2233[sizeof("kurate.fukuoka.jp")];
- char stringpool_str2234[sizeof("forum.hu")];
- char stringpool_str2235[sizeof("kanuma.tochigi.jp")];
- char stringpool_str2236[sizeof("ehime.jp")];
- char stringpool_str2237[sizeof("szkola.pl")];
- char stringpool_str2238[sizeof("kouzushima.tokyo.jp")];
- char stringpool_str2239[sizeof("saroma.hokkaido.jp")];
- char stringpool_str2240[sizeof("games.hu")];
- char stringpool_str2241[sizeof("alstahaug.no")];
- char stringpool_str2242[sizeof("minamimaki.nagano.jp")];
- char stringpool_str2243[sizeof("steiermark.museum")];
- char stringpool_str2244[sizeof("numazu.shizuoka.jp")];
- char stringpool_str2245[sizeof("aichi.jp")];
- char stringpool_str2246[sizeof("stalowa-wola.pl")];
- char stringpool_str2247[sizeof("yono.saitama.jp")];
- char stringpool_str2248[sizeof("kamogawa.chiba.jp")];
- char stringpool_str2249[sizeof("udono.mie.jp")];
- char stringpool_str2250[sizeof("kurume.fukuoka.jp")];
- char stringpool_str2251[sizeof("kamagaya.chiba.jp")];
- char stringpool_str2252[sizeof("makinohara.shizuoka.jp")];
- char stringpool_str2253[sizeof("southwest.museum")];
- char stringpool_str2254[sizeof("kosuge.yamanashi.jp")];
- char stringpool_str2255[sizeof("rankoshi.hokkaido.jp")];
- char stringpool_str2256[sizeof("ngo.pl")];
- char stringpool_str2257[sizeof("nishikatsura.yamanashi.jp")];
- char stringpool_str2258[sizeof("komatsushima.tokushima.jp")];
- char stringpool_str2259[sizeof("kutno.pl")];
- char stringpool_str2260[sizeof("shinjo.nara.jp")];
- char stringpool_str2261[sizeof("harima.hyogo.jp")];
- char stringpool_str2262[sizeof("kasahara.gifu.jp")];
- char stringpool_str2263[sizeof("miharu.fukushima.jp")];
- char stringpool_str2264[sizeof("aikawa.kanagawa.jp")];
- char stringpool_str2265[sizeof("shinto.gunma.jp")];
- char stringpool_str2266[sizeof("kepno.pl")];
- char stringpool_str2267[sizeof("gov.sy")];
- char stringpool_str2268[sizeof("edu.sy")];
- char stringpool_str2269[sizeof("com.sy")];
- char stringpool_str2270[sizeof("sumida.tokyo.jp")];
- char stringpool_str2271[sizeof("yamanashi.jp")];
- char stringpool_str2272[sizeof("gov.ly")];
- char stringpool_str2273[sizeof("ayabe.kyoto.jp")];
- char stringpool_str2274[sizeof("edu.ly")];
- char stringpool_str2275[sizeof("ashiya.fukuoka.jp")];
- char stringpool_str2276[sizeof("com.ly")];
- char stringpool_str2277[sizeof("kakinoki.shimane.jp")];
- char stringpool_str2278[sizeof("edu.uy")];
- char stringpool_str2279[sizeof("com.uy")];
- char stringpool_str2280[sizeof("o.bg")];
- char stringpool_str2281[sizeof("soo.kagoshima.jp")];
- char stringpool_str2282[sizeof("o.se")];
- char stringpool_str2283[sizeof("yamato.kumamoto.jp")];
- char stringpool_str2284[sizeof("stjordalshalsen.no")];
- char stringpool_str2285[sizeof("kumagaya.saitama.jp")];
- char stringpool_str2286[sizeof("stuttgart.museum")];
- char stringpool_str2287[sizeof("chita.ru")];
- char stringpool_str2288[sizeof("gub.uy")];
- char stringpool_str2289[sizeof("rishiri.hokkaido.jp")];
- char stringpool_str2290[sizeof("hanawa.fukushima.jp")];
- char stringpool_str2291[sizeof("org")];
- char stringpool_str2292[sizeof("org.do")];
- char stringpool_str2293[sizeof("org.om")];
- char stringpool_str2294[sizeof("kamitonda.wakayama.jp")];
- char stringpool_str2295[sizeof("org.dm")];
- char stringpool_str2296[sizeof("suzaka.nagano.jp")];
- char stringpool_str2297[sizeof("org.so")];
- char stringpool_str2298[sizeof("org.rs")];
- char stringpool_str2299[sizeof("org.ro")];
- char stringpool_str2300[sizeof("kuban.ru")];
- char stringpool_str2301[sizeof("gov.by")];
- char stringpool_str2302[sizeof("ralingen.no")];
- char stringpool_str2303[sizeof("org.sc")];
- char stringpool_str2304[sizeof("org.ls")];
- char stringpool_str2305[sizeof("org.lr")];
- char stringpool_str2306[sizeof("org.sb")];
- char stringpool_str2307[sizeof("com.by")];
- char stringpool_str2308[sizeof("nakano.nagano.jp")];
- char stringpool_str2309[sizeof("izumi.osaka.jp")];
- char stringpool_str2310[sizeof("noto.ishikawa.jp")];
- char stringpool_str2311[sizeof("org.lc")];
- char stringpool_str2312[sizeof("org.lb")];
- char stringpool_str2313[sizeof("soja.okayama.jp")];
- char stringpool_str2314[sizeof("org.lk")];
- char stringpool_str2315[sizeof("org.sl")];
- char stringpool_str2316[sizeof("akita.jp")];
- char stringpool_str2317[sizeof("shiroi.chiba.jp")];
- char stringpool_str2318[sizeof("org.se")];
- char stringpool_str2319[sizeof("inabe.mie.jp")];
- char stringpool_str2320[sizeof("shisui.chiba.jp")];
- char stringpool_str2321[sizeof("odo.br")];
- char stringpool_str2322[sizeof("abeno.osaka.jp")];
- char stringpool_str2323[sizeof("org.nr")];
- char stringpool_str2324[sizeof("iwate.jp")];
- char stringpool_str2325[sizeof("yamada.toyama.jp")];
- char stringpool_str2326[sizeof("kokubunji.tokyo.jp")];
- char stringpool_str2327[sizeof("oita.jp")];
- char stringpool_str2328[sizeof("kitagawa.miyazaki.jp")];
- char stringpool_str2329[sizeof("kyoto.jp")];
- char stringpool_str2330[sizeof("org.es")];
- char stringpool_str2331[sizeof("sokndal.no")];
- char stringpool_str2332[sizeof("org.ec")];
- char stringpool_str2333[sizeof("nakadomari.aomori.jp")];
- char stringpool_str2334[sizeof("org.to")];
- char stringpool_str2335[sizeof("org.tj")];
- char stringpool_str2336[sizeof("org.tm")];
- char stringpool_str2337[sizeof("org.sa")];
- char stringpool_str2338[sizeof("org.sd")];
- char stringpool_str2339[sizeof("kasuya.fukuoka.jp")];
- char stringpool_str2340[sizeof("asahi.mie.jp")];
- char stringpool_str2341[sizeof("vic.gov.au")];
- char stringpool_str2342[sizeof("org.la")];
- char stringpool_str2343[sizeof("org.ee")];
- char stringpool_str2344[sizeof("yasuda.kochi.jp")];
- char stringpool_str2345[sizeof("om")];
- char stringpool_str2346[sizeof("izumo.shimane.jp")];
- char stringpool_str2347[sizeof("gov.my")];
- char stringpool_str2348[sizeof("edu.my")];
- char stringpool_str2349[sizeof("org.bs")];
- char stringpool_str2350[sizeof("org.br")];
- char stringpool_str2351[sizeof("org.bo")];
- char stringpool_str2352[sizeof("yura.wakayama.jp")];
- char stringpool_str2353[sizeof("org.ua")];
- char stringpool_str2354[sizeof("com.my")];
- char stringpool_str2355[sizeof("org.bm")];
- char stringpool_str2356[sizeof("sex.hu")];
- char stringpool_str2357[sizeof("ashikaga.tochigi.jp")];
- char stringpool_str2358[sizeof("onl")];
- char stringpool_str2359[sizeof("gotsu.shimane.jp")];
- char stringpool_str2360[sizeof("org.bb")];
- char stringpool_str2361[sizeof("org.iq")];
- char stringpool_str2362[sizeof("org.is")];
- char stringpool_str2363[sizeof("org.ir")];
- char stringpool_str2364[sizeof("osoyro.no")];
- char stringpool_str2365[sizeof("org.im")];
- char stringpool_str2366[sizeof("ilawa.pl")];
- char stringpool_str2367[sizeof("org.na")];
- char stringpool_str2368[sizeof("nishimera.miyazaki.jp")];
- char stringpool_str2369[sizeof("finnoy.no")];
- char stringpool_str2370[sizeof("yamazoe.nara.jp")];
- char stringpool_str2371[sizeof("komatsu.ishikawa.jp")];
- char stringpool_str2372[sizeof("org.qa")];
- char stringpool_str2373[sizeof("maizuru.kyoto.jp")];
- char stringpool_str2374[sizeof("fukusaki.hyogo.jp")];
- char stringpool_str2375[sizeof("moriyama.shiga.jp")];
- char stringpool_str2376[sizeof("notaires.fr")];
- char stringpool_str2377[sizeof("honai.ehime.jp")];
- char stringpool_str2378[sizeof("kyuragi.saga.jp")];
- char stringpool_str2379[sizeof("org.ba")];
- char stringpool_str2380[sizeof("gov.py")];
- char stringpool_str2381[sizeof("org.jo")];
- char stringpool_str2382[sizeof("edu.py")];
- char stringpool_str2383[sizeof("com.py")];
- char stringpool_str2384[sizeof("chiba.jp")];
- char stringpool_str2385[sizeof("yatsuka.shimane.jp")];
- char stringpool_str2386[sizeof("susono.shizuoka.jp")];
- char stringpool_str2387[sizeof("org.mo")];
- char stringpool_str2388[sizeof("naie.hokkaido.jp")];
- char stringpool_str2389[sizeof("naka.ibaraki.jp")];
- char stringpool_str2390[sizeof("med.ly")];
- char stringpool_str2391[sizeof("saitama.saitama.jp")];
- char stringpool_str2392[sizeof("org.tw")];
- char stringpool_str2393[sizeof("org.mk")];
- char stringpool_str2394[sizeof("zarow.pl")];
- char stringpool_str2395[sizeof("hikone.shiga.jp")];
- char stringpool_str2396[sizeof("ayagawa.kagawa.jp")];
- char stringpool_str2397[sizeof("atsugi.kanagawa.jp")];
- char stringpool_str2398[sizeof("org.je")];
- char stringpool_str2399[sizeof("suginami.tokyo.jp")];
- char stringpool_str2400[sizeof("munakata.fukuoka.jp")];
- char stringpool_str2401[sizeof("org.ml")];
- char stringpool_str2402[sizeof("org.me")];
- char stringpool_str2403[sizeof("ovh")];
- char stringpool_str2404[sizeof("akkeshi.hokkaido.jp")];
- char stringpool_str2405[sizeof("nord-odal.no")];
- char stringpool_str2406[sizeof("org.bw")];
- char stringpool_str2407[sizeof("ainan.ehime.jp")];
- char stringpool_str2408[sizeof("orland.no")];
- char stringpool_str2409[sizeof("org.ar")];
- char stringpool_str2410[sizeof("org.ac")];
- char stringpool_str2411[sizeof("spjelkavik.no")];
- char stringpool_str2412[sizeof("kadogawa.miyazaki.jp")];
- char stringpool_str2413[sizeof("org.bi")];
- char stringpool_str2414[sizeof("org.ma")];
- char stringpool_str2415[sizeof("org.ps")];
- char stringpool_str2416[sizeof("org.pr")];
- char stringpool_str2417[sizeof("org.al")];
- char stringpool_str2418[sizeof("colonialwilliamsburg.museum")];
- char stringpool_str2419[sizeof("org.ae")];
- char stringpool_str2420[sizeof("fujioka.gunma.jp")];
- char stringpool_str2421[sizeof("org.pk")];
- char stringpool_str2422[sizeof("mytis.ru")];
- char stringpool_str2423[sizeof("shimoda.shizuoka.jp")];
- char stringpool_str2424[sizeof("asago.hyogo.jp")];
- char stringpool_str2425[sizeof("averoy.no")];
- char stringpool_str2426[sizeof("org.pl")];
- char stringpool_str2427[sizeof("ishikawa.fukushima.jp")];
- char stringpool_str2428[sizeof("org.vc")];
- char stringpool_str2429[sizeof("org.pe")];
- char stringpool_str2430[sizeof("mutsu.aomori.jp")];
- char stringpool_str2431[sizeof("kafjord.no")];
- char stringpool_str2432[sizeof("yaroslavl.ru")];
- char stringpool_str2433[sizeof("org.mw")];
- char stringpool_str2434[sizeof("org.ve")];
- char stringpool_str2435[sizeof("nikaho.akita.jp")];
- char stringpool_str2436[sizeof("klabu.no")];
- char stringpool_str2437[sizeof("mil.sy")];
- char stringpool_str2438[sizeof("nishiokoppe.hokkaido.jp")];
- char stringpool_str2439[sizeof("settsu.osaka.jp")];
- char stringpool_str2440[sizeof("yusuhara.kochi.jp")];
- char stringpool_str2441[sizeof("katagami.akita.jp")];
- char stringpool_str2442[sizeof("kirov.ru")];
- char stringpool_str2443[sizeof("org.pa")];
- char stringpool_str2444[sizeof("bizen.okayama.jp")];
- char stringpool_str2445[sizeof("usuki.oita.jp")];
- char stringpool_str2446[sizeof("mil.uy")];
- char stringpool_str2447[sizeof("kazan.ru")];
- char stringpool_str2448[sizeof("matsuda.kanagawa.jp")];
- char stringpool_str2449[sizeof("hokkaido.jp")];
- char stringpool_str2450[sizeof("wakasa.fukui.jp")];
- char stringpool_str2451[sizeof("ishinomaki.miyagi.jp")];
- char stringpool_str2452[sizeof("nonoichi.ishikawa.jp")];
- char stringpool_str2453[sizeof("unnan.shimane.jp")];
- char stringpool_str2454[sizeof("media.pl")];
- char stringpool_str2455[sizeof("shiksha")];
- char stringpool_str2456[sizeof("kaneyama.fukushima.jp")];
- char stringpool_str2457[sizeof("gwangju.kr")];
- char stringpool_str2458[sizeof("osteroy.no")];
- char stringpool_str2459[sizeof("org.ai")];
- char stringpool_str2460[sizeof("org.hk")];
- char stringpool_str2461[sizeof("gov.ky")];
- char stringpool_str2462[sizeof("edu.ky")];
- char stringpool_str2463[sizeof("semine.miyagi.jp")];
- char stringpool_str2464[sizeof("com.ky")];
- char stringpool_str2465[sizeof("nishiawakura.okayama.jp")];
- char stringpool_str2466[sizeof("sakhalin.ru")];
- char stringpool_str2467[sizeof("mil.by")];
- char stringpool_str2468[sizeof("isehara.kanagawa.jp")];
- char stringpool_str2469[sizeof("is-a-patsfan.org")];
- char stringpool_str2470[sizeof("ibara.okayama.jp")];
- char stringpool_str2471[sizeof("itami.hyogo.jp")];
- char stringpool_str2472[sizeof("matsukawa.nagano.jp")];
- char stringpool_str2473[sizeof("nakanojo.gunma.jp")];
- char stringpool_str2474[sizeof("org.vi")];
- char stringpool_str2475[sizeof("nikolaev.ua")];
- char stringpool_str2476[sizeof("kaneyama.yamagata.jp")];
- char stringpool_str2477[sizeof("kursk.ru")];
- char stringpool_str2478[sizeof("naoshima.kagawa.jp")];
- char stringpool_str2479[sizeof("aizumisato.fukushima.jp")];
- char stringpool_str2480[sizeof("shimokitayama.nara.jp")];
- char stringpool_str2481[sizeof("kamishihoro.hokkaido.jp")];
- char stringpool_str2482[sizeof("fukagawa.hokkaido.jp")];
- char stringpool_str2483[sizeof("ovre-eiker.no")];
- char stringpool_str2484[sizeof("oppdal.no")];
- char stringpool_str2485[sizeof("org.km")];
- char stringpool_str2486[sizeof("yotsukaido.chiba.jp")];
- char stringpool_str2487[sizeof("viking.museum")];
- char stringpool_str2488[sizeof("shintoku.hokkaido.jp")];
- char stringpool_str2489[sizeof("mil.my")];
- char stringpool_str2490[sizeof("shimamoto.osaka.jp")];
- char stringpool_str2491[sizeof("org.kp")];
- char stringpool_str2492[sizeof("shimada.shizuoka.jp")];
- char stringpool_str2493[sizeof("org.af")];
- char stringpool_str2494[sizeof("shiojiri.nagano.jp")];
- char stringpool_str2495[sizeof("ota.tokyo.jp")];
- char stringpool_str2496[sizeof("minoh.osaka.jp")];
- char stringpool_str2497[sizeof("fukushima.hokkaido.jp")];
- char stringpool_str2498[sizeof("yatomi.aichi.jp")];
- char stringpool_str2499[sizeof("mazury.pl")];
- char stringpool_str2500[sizeof("iruma.saitama.jp")];
- char stringpool_str2501[sizeof("net.gg")];
- char stringpool_str2502[sizeof("kochi.jp")];
- char stringpool_str2503[sizeof("mamurogawa.yamagata.jp")];
- char stringpool_str2504[sizeof("org.pf")];
- char stringpool_str2505[sizeof("oksnes.no")];
- char stringpool_str2506[sizeof("kani.gifu.jp")];
- char stringpool_str2507[sizeof("hanno.saitama.jp")];
- char stringpool_str2508[sizeof("oystre-slidre.no")];
- char stringpool_str2509[sizeof("gosen.niigata.jp")];
- char stringpool_str2510[sizeof("odessa.ua")];
- char stringpool_str2511[sizeof("mil.py")];
- char stringpool_str2512[sizeof("otsuka")];
- char stringpool_str2513[sizeof("ikoma.nara.jp")];
- char stringpool_str2514[sizeof("yoshioka.gunma.jp")];
- char stringpool_str2515[sizeof("ostroda.pl")];
- char stringpool_str2516[sizeof("sasebo.nagasaki.jp")];
- char stringpool_str2517[sizeof("minamiminowa.nagano.jp")];
- char stringpool_str2518[sizeof("silk.museum")];
- char stringpool_str2519[sizeof("ome.tokyo.jp")];
- char stringpool_str2520[sizeof("higashikawa.hokkaido.jp")];
- char stringpool_str2521[sizeof("varggat.no")];
- char stringpool_str2522[sizeof("kashiwazaki.niigata.jp")];
- char stringpool_str2523[sizeof("oygarden.no")];
- char stringpool_str2524[sizeof("sugito.saitama.jp")];
- char stringpool_str2525[sizeof("olecko.pl")];
- char stringpool_str2526[sizeof("yoshimi.saitama.jp")];
- char stringpool_str2527[sizeof("ikeda.osaka.jp")];
- char stringpool_str2528[sizeof("org.ki")];
- char stringpool_str2529[sizeof("minamidaito.okinawa.jp")];
- char stringpool_str2530[sizeof("belau.pw")];
- char stringpool_str2531[sizeof("kitagata.saga.jp")];
- char stringpool_str2532[sizeof("soka.saitama.jp")];
- char stringpool_str2533[sizeof("is-a-knight.org")];
- char stringpool_str2534[sizeof("shinjuku.tokyo.jp")];
- char stringpool_str2535[sizeof("cieszyn.pl")];
- char stringpool_str2536[sizeof("hanyu.saitama.jp")];
- char stringpool_str2537[sizeof("yoshida.saitama.jp")];
- char stringpool_str2538[sizeof("oita.oita.jp")];
- char stringpool_str2539[sizeof("ulvik.no")];
- char stringpool_str2540[sizeof("yashiro.hyogo.jp")];
- char stringpool_str2541[sizeof("chikuhoku.nagano.jp")];
- char stringpool_str2542[sizeof("sg")];
- char stringpool_str2543[sizeof("botany.museum")];
- char stringpool_str2544[sizeof("kouhoku.saga.jp")];
- char stringpool_str2545[sizeof("shonai.yamagata.jp")];
- char stringpool_str2546[sizeof("sakurai.nara.jp")];
- char stringpool_str2547[sizeof("shiraoka.saitama.jp")];
- char stringpool_str2548[sizeof("hakata.fukuoka.jp")];
- char stringpool_str2549[sizeof("nosegawa.nara.jp")];
- char stringpool_str2550[sizeof("karatsu.saga.jp")];
- char stringpool_str2551[sizeof("akita.akita.jp")];
- char stringpool_str2552[sizeof("wakayama.wakayama.jp")];
- char stringpool_str2553[sizeof("asaka.saitama.jp")];
- char stringpool_str2554[sizeof("county.museum")];
- char stringpool_str2555[sizeof("asker.no")];
- char stringpool_str2556[sizeof("shimoji.okinawa.jp")];
- char stringpool_str2557[sizeof("yuzhno-sakhalinsk.ru")];
- char stringpool_str2558[sizeof("chikushino.fukuoka.jp")];
- char stringpool_str2559[sizeof("kouyama.kagoshima.jp")];
- char stringpool_str2560[sizeof("shinjo.yamagata.jp")];
- char stringpool_str2561[sizeof("kanonji.kagawa.jp")];
- char stringpool_str2562[sizeof("yoshida.shizuoka.jp")];
- char stringpool_str2563[sizeof("akagi.shimane.jp")];
- char stringpool_str2564[sizeof("szczecin.pl")];
- char stringpool_str2565[sizeof("seranishi.hiroshima.jp")];
- char stringpool_str2566[sizeof("konin.pl")];
- char stringpool_str2567[sizeof("nakamura.kochi.jp")];
- char stringpool_str2568[sizeof("fukui.jp")];
- char stringpool_str2569[sizeof("koryo.nara.jp")];
- char stringpool_str2570[sizeof("ikata.ehime.jp")];
- char stringpool_str2571[sizeof("ide.kyoto.jp")];
- char stringpool_str2572[sizeof("inami.wakayama.jp")];
- char stringpool_str2573[sizeof("shinjo.okayama.jp")];
- char stringpool_str2574[sizeof("sukumo.kochi.jp")];
- char stringpool_str2575[sizeof("oirase.aomori.jp")];
- char stringpool_str2576[sizeof("rakkestad.no")];
- char stringpool_str2577[sizeof("setouchi.okayama.jp")];
- char stringpool_str2578[sizeof("aridagawa.wakayama.jp")];
- char stringpool_str2579[sizeof("yasaka.nagano.jp")];
- char stringpool_str2580[sizeof("is-a-bruinsfan.org")];
- char stringpool_str2581[sizeof("hirogawa.wakayama.jp")];
- char stringpool_str2582[sizeof("ine.kyoto.jp")];
- char stringpool_str2583[sizeof("higashine.yamagata.jp")];
- char stringpool_str2584[sizeof("kamikawa.hyogo.jp")];
- char stringpool_str2585[sizeof("hongo.hiroshima.jp")];
- char stringpool_str2586[sizeof("shintomi.miyazaki.jp")];
- char stringpool_str2587[sizeof("org.ru")];
- char stringpool_str2588[sizeof("askoy.no")];
- char stringpool_str2589[sizeof("is-by.us")];
- char stringpool_str2590[sizeof("buzen.fukuoka.jp")];
- char stringpool_str2591[sizeof("hanggliding.aero")];
- char stringpool_str2592[sizeof("aisho.shiga.jp")];
- char stringpool_str2593[sizeof("choyo.kumamoto.jp")];
- char stringpool_str2594[sizeof("higashiomi.shiga.jp")];
- char stringpool_str2595[sizeof("kunimi.fukushima.jp")];
- char stringpool_str2596[sizeof("yoichi.hokkaido.jp")];
- char stringpool_str2597[sizeof("org.ws")];
- char stringpool_str2598[sizeof("fedje.no")];
- char stringpool_str2599[sizeof("myoko.niigata.jp")];
- char stringpool_str2600[sizeof("org.sg")];
- char stringpool_str2601[sizeof("go.tz")];
- char stringpool_str2602[sizeof("ed.ao")];
- char stringpool_str2603[sizeof("stange.no")];
- char stringpool_str2604[sizeof("co.ao")];
- char stringpool_str2605[sizeof("co.sz")];
- char stringpool_str2606[sizeof("co.uz")];
- char stringpool_str2607[sizeof("co.tz")];
- char stringpool_str2608[sizeof("ostrowwlkp.pl")];
- char stringpool_str2609[sizeof("yashio.saitama.jp")];
- char stringpool_str2610[sizeof("id.us")];
- char stringpool_str2611[sizeof("co.us")];
- char stringpool_str2612[sizeof("co.ls")];
- char stringpool_str2613[sizeof("bykle.no")];
- char stringpool_str2614[sizeof("media.hu")];
- char stringpool_str2615[sizeof("nomi.ishikawa.jp")];
- char stringpool_str2616[sizeof("nuernberg.museum")];
- char stringpool_str2617[sizeof("org.ug")];
- char stringpool_str2618[sizeof("az.us")];
- char stringpool_str2619[sizeof("oristano.it")];
- char stringpool_str2620[sizeof("il.us")];
- char stringpool_str2621[sizeof("co.no")];
- char stringpool_str2622[sizeof("kusatsu.gunma.jp")];
- char stringpool_str2623[sizeof("as.us")];
- char stringpool_str2624[sizeof("id.au")];
- char stringpool_str2625[sizeof("ar.us")];
- char stringpool_str2626[sizeof("shiraoi.hokkaido.jp")];
- char stringpool_str2627[sizeof("vefsn.no")];
- char stringpool_str2628[sizeof("gu.us")];
- char stringpool_str2629[sizeof("co.rs")];
- char stringpool_str2630[sizeof("org.ng")];
- char stringpool_str2631[sizeof("al.us")];
- char stringpool_str2632[sizeof("hl.no")];
- char stringpool_str2633[sizeof("co.nl")];
- char stringpool_str2634[sizeof("co.mu")];
- char stringpool_str2635[sizeof("org.eg")];
- char stringpool_str2636[sizeof("fl.us")];
- char stringpool_str2637[sizeof("shibecha.hokkaido.jp")];
- char stringpool_str2638[sizeof("in.us")];
- char stringpool_str2639[sizeof("al.no")];
- char stringpool_str2640[sizeof("co.ae")];
- char stringpool_str2641[sizeof("it.ao")];
- char stringpool_str2642[sizeof("co.st")];
- char stringpool_str2643[sizeof("co.tt")];
- char stringpool_str2644[sizeof("co.at")];
- char stringpool_str2645[sizeof("hiroo.hokkaido.jp")];
- char stringpool_str2646[sizeof("co.me")];
- char stringpool_str2647[sizeof("aukra.no")];
- char stringpool_str2648[sizeof("go.cr")];
- char stringpool_str2649[sizeof("bu.no")];
- char stringpool_str2650[sizeof("ed.cr")];
- char stringpool_str2651[sizeof("ct.us")];
- char stringpool_str2652[sizeof("in.rs")];
- char stringpool_str2653[sizeof("co.cr")];
- char stringpool_str2654[sizeof("gr.com")];
- char stringpool_str2655[sizeof("bd.se")];
- char stringpool_str2656[sizeof("naturbruksgymn.se")];
- char stringpool_str2657[sizeof("co.cl")];
- char stringpool_str2658[sizeof("ar.com")];
- char stringpool_str2659[sizeof("ebino.miyazaki.jp")];
- char stringpool_str2660[sizeof("hu.com")];
- char stringpool_str2661[sizeof("eu.com")];
- char stringpool_str2662[sizeof("atami.shizuoka.jp")];
- char stringpool_str2663[sizeof("koriyama.fukushima.jp")];
- char stringpool_str2664[sizeof("br.com")];
- char stringpool_str2665[sizeof("hyuga.miyazaki.jp")];
- char stringpool_str2666[sizeof("org.mu")];
- char stringpool_str2667[sizeof("gd.cn")];
- char stringpool_str2668[sizeof("gz.cn")];
- char stringpool_str2669[sizeof("kanan.osaka.jp")];
- char stringpool_str2670[sizeof("hi.us")];
- char stringpool_str2671[sizeof("cn.com")];
- char stringpool_str2672[sizeof("nuremberg.museum")];
- char stringpool_str2673[sizeof("gs.cn")];
- char stringpool_str2674[sizeof("karmoy.no")];
- char stringpool_str2675[sizeof("orskog.no")];
- char stringpool_str2676[sizeof("mo.us")];
- char stringpool_str2677[sizeof("md.us")];
- char stringpool_str2678[sizeof("bushey.museum")];
- char stringpool_str2679[sizeof("hl.cn")];
- char stringpool_str2680[sizeof("ms.us")];
- char stringpool_str2681[sizeof("okinawa")];
- char stringpool_str2682[sizeof("shinichi.hiroshima.jp")];
- char stringpool_str2683[sizeof("york.museum")];
- char stringpool_str2684[sizeof("id.ly")];
- char stringpool_str2685[sizeof("chigasaki.kanagawa.jp")];
- char stringpool_str2686[sizeof("shikaoi.hokkaido.jp")];
- char stringpool_str2687[sizeof("mr.no")];
- char stringpool_str2688[sizeof("go.kr")];
- char stringpool_str2689[sizeof("co.kr")];
- char stringpool_str2690[sizeof("vardo.no")];
- char stringpool_str2691[sizeof("org.au")];
- char stringpool_str2692[sizeof("hn.cn")];
- char stringpool_str2693[sizeof("semboku.akita.jp")];
- char stringpool_str2694[sizeof("hs.kr")];
- char stringpool_str2695[sizeof("es.kr")];
- char stringpool_str2696[sizeof("go.tj")];
- char stringpool_str2697[sizeof("co.tj")];
- char stringpool_str2698[sizeof("gb.com")];
- char stringpool_str2699[sizeof("org.mg")];
- char stringpool_str2700[sizeof("yatsushiro.kumamoto.jp")];
- char stringpool_str2701[sizeof("mn.us")];
- char stringpool_str2702[sizeof("he.cn")];
- char stringpool_str2703[sizeof("co.gy")];
- char stringpool_str2704[sizeof("co.tm")];
- char stringpool_str2705[sizeof("go.ci")];
- char stringpool_str2706[sizeof("me.tz")];
- char stringpool_str2707[sizeof("yokohama")];
- char stringpool_str2708[sizeof("ed.ci")];
- char stringpool_str2709[sizeof("co.om")];
- char stringpool_str2710[sizeof("co.ci")];
- char stringpool_str2711[sizeof("sakura.chiba.jp")];
- char stringpool_str2712[sizeof("sakaiminato.tottori.jp")];
- char stringpool_str2713[sizeof("me.us")];
- char stringpool_str2714[sizeof("cq.cn")];
- char stringpool_str2715[sizeof("mt.us")];
- char stringpool_str2716[sizeof("hm.no")];
- char stringpool_str2717[sizeof("stargard.pl")];
- char stringpool_str2718[sizeof("arida.wakayama.jp")];
- char stringpool_str2719[sizeof("bibai.hokkaido.jp")];
- char stringpool_str2720[sizeof("hb.cn")];
- char stringpool_str2721[sizeof("vadso.no")];
- char stringpool_str2722[sizeof("org.ag")];
- char stringpool_str2723[sizeof("fi.cr")];
- char stringpool_str2724[sizeof("yakutia.ru")];
- char stringpool_str2725[sizeof("hi.cn")];
- char stringpool_str2726[sizeof("co.ua")];
- char stringpool_str2727[sizeof("sera.hiroshima.jp")];
- char stringpool_str2728[sizeof("fm.no")];
- char stringpool_str2729[sizeof("okayama.jp")];
- char stringpool_str2730[sizeof("ga.us")];
- char stringpool_str2731[sizeof("ia.us")];
- char stringpool_str2732[sizeof("inami.toyama.jp")];
- char stringpool_str2733[sizeof("ostroleka.pl")];
- char stringpool_str2734[sizeof("mo.cn")];
- char stringpool_str2735[sizeof("ca.us")];
- char stringpool_str2736[sizeof("cr.ua")];
- char stringpool_str2737[sizeof("mi.us")];
- char stringpool_str2738[sizeof("co.ma")];
- char stringpool_str2739[sizeof("shobara.hiroshima.jp")];
- char stringpool_str2740[sizeof("koka.shiga.jp")];
- char stringpool_str2741[sizeof("ut.us")];
- char stringpool_str2742[sizeof("meiwa.mie.jp")];
- char stringpool_str2743[sizeof("us.com")];
- char stringpool_str2744[sizeof("aioi.hyogo.jp")];
- char stringpool_str2745[sizeof("ha.no")];
- char stringpool_str2746[sizeof("co.na")];
- char stringpool_str2747[sizeof("erimo.hokkaido.jp")];
- char stringpool_str2748[sizeof("yamatokoriyama.nara.jp")];
- char stringpool_str2749[sizeof("nishihara.kumamoto.jp")];
- char stringpool_str2750[sizeof("aa.no")];
- char stringpool_str2751[sizeof("vagan.no")];
- char stringpool_str2752[sizeof("gv.ao")];
- char stringpool_str2753[sizeof("id.lv")];
- char stringpool_str2754[sizeof("in.ua")];
- char stringpool_str2755[sizeof("cn.ua")];
- char stringpool_str2756[sizeof("iz.hr")];
- char stringpool_str2757[sizeof("kitakata.fukushima.jp")];
- char stringpool_str2758[sizeof("ms.kr")];
- char stringpool_str2759[sizeof("inagi.tokyo.jp")];
- char stringpool_str2760[sizeof("kiryu.gunma.jp")];
- char stringpool_str2761[sizeof("sorfold.no")];
- char stringpool_str2762[sizeof("k12.ny.us")];
- char stringpool_str2763[sizeof("varoy.no")];
- char stringpool_str2764[sizeof("in.na")];
- char stringpool_str2765[sizeof("co.ca")];
- char stringpool_str2766[sizeof("org.hu")];
- char stringpool_str2767[sizeof("co.hu")];
- char stringpool_str2768[sizeof("md.ci")];
- char stringpool_str2769[sizeof("volda.no")];
- char stringpool_str2770[sizeof("bj.cn")];
- char stringpool_str2771[sizeof("stateofdelaware.museum")];
- char stringpool_str2772[sizeof("fj.cn")];
- char stringpool_str2773[sizeof("inawashiro.fukushima.jp")];
- char stringpool_str2774[sizeof("saikai.nagasaki.jp")];
- char stringpool_str2775[sizeof("ishigaki.okinawa.jp")];
- char stringpool_str2776[sizeof("nuoro.it")];
- char stringpool_str2777[sizeof("agrigento.it")];
- char stringpool_str2778[sizeof("bv.nl")];
- char stringpool_str2779[sizeof("gv.at")];
- char stringpool_str2780[sizeof("ha.cn")];
- char stringpool_str2781[sizeof("satosho.okayama.jp")];
- char stringpool_str2782[sizeof("kagamiishi.fukushima.jp")];
- char stringpool_str2783[sizeof("muika.niigata.jp")];
- char stringpool_str2784[sizeof("ma.us")];
- char stringpool_str2785[sizeof("valle.no")];
- char stringpool_str2786[sizeof("city.yokohama.jp")];
- char stringpool_str2787[sizeof("fussa.tokyo.jp")];
- char stringpool_str2788[sizeof("go.th")];
- char stringpool_str2789[sizeof("co.bi")];
- char stringpool_str2790[sizeof("co.th")];
- char stringpool_str2791[sizeof("zj.cn")];
- char stringpool_str2792[sizeof("rovigo.it")];
- char stringpool_str2793[sizeof("nishiwaki.hyogo.jp")];
- char stringpool_str2794[sizeof("minamiawaji.hyogo.jp")];
- char stringpool_str2795[sizeof("zt.ua")];
- char stringpool_str2796[sizeof("za.com")];
- char stringpool_str2797[sizeof("uy.com")];
- char stringpool_str2798[sizeof("nishihara.okinawa.jp")];
- char stringpool_str2799[sizeof("honjo.akita.jp")];
- char stringpool_str2800[sizeof("nogata.fukuoka.jp")];
- char stringpool_str2801[sizeof("ikeda.fukui.jp")];
- char stringpool_str2802[sizeof("uz.ua")];
- char stringpool_str2803[sizeof("suzu.ishikawa.jp")];
- char stringpool_str2804[sizeof("ah.no")];
- char stringpool_str2805[sizeof("ikawa.akita.jp")];
- char stringpool_str2806[sizeof("kaminoyama.yamagata.jp")];
- char stringpool_str2807[sizeof("ab.ca")];
- char stringpool_str2808[sizeof("in.th")];
- char stringpool_str2809[sizeof("kanegasaki.iwate.jp")];
- char stringpool_str2810[sizeof("chijiwa.nagasaki.jp")];
- char stringpool_str2811[sizeof("konan.shiga.jp")];
- char stringpool_str2812[sizeof("sakado.saitama.jp")];
- char stringpool_str2813[sizeof("us.na")];
- char stringpool_str2814[sizeof("xz.cn")];
- char stringpool_str2815[sizeof("kariwa.niigata.jp")];
- char stringpool_str2816[sizeof("oji.nara.jp")];
- char stringpool_str2817[sizeof("orenburg.ru")];
- char stringpool_str2818[sizeof("fh.se")];
- char stringpool_str2819[sizeof("am.br")];
- char stringpool_str2820[sizeof("org.kg")];
- char stringpool_str2821[sizeof("gs.oslo.no")];
- char stringpool_str2822[sizeof("soma.fukushima.jp")];
- char stringpool_str2823[sizeof("selfip.info")];
- char stringpool_str2824[sizeof("asahi.toyama.jp")];
- char stringpool_str2825[sizeof("fm.br")];
- char stringpool_str2826[sizeof("kasai.hyogo.jp")];
- char stringpool_str2827[sizeof("yorkshire.museum")];
- char stringpool_str2828[sizeof("okinawa.jp")];
- char stringpool_str2829[sizeof("xn--45q11c")];
- char stringpool_str2830[sizeof("motegi.tochigi.jp")];
- char stringpool_str2831[sizeof("co.ba")];
- char stringpool_str2832[sizeof("ca.na")];
- char stringpool_str2833[sizeof("uji.kyoto.jp")];
- char stringpool_str2834[sizeof("qh.cn")];
- char stringpool_str2835[sizeof("randaberg.no")];
- char stringpool_str2836[sizeof("ak.us")];
- char stringpool_str2837[sizeof("nishiazai.shiga.jp")];
- char stringpool_str2838[sizeof("ah.cn")];
- char stringpool_str2839[sizeof("ngo.ph")];
- char stringpool_str2840[sizeof("cv.ua")];
- char stringpool_str2841[sizeof("shiiba.miyazaki.jp")];
- char stringpool_str2842[sizeof("kosei.shiga.jp")];
- char stringpool_str2843[sizeof("fuchu.hiroshima.jp")];
- char stringpool_str2844[sizeof("bl.uk")];
- char stringpool_str2845[sizeof("shikabe.hokkaido.jp")];
- char stringpool_str2846[sizeof("katashina.gunma.jp")];
- char stringpool_str2847[sizeof("mb.ca")];
- char stringpool_str2848[sizeof("stockholm.museum")];
- char stringpool_str2849[sizeof("nachikatsuura.wakayama.jp")];
- char stringpool_str2850[sizeof("kanna.gunma.jp")];
- char stringpool_str2851[sizeof("video.hu")];
- char stringpool_str2852[sizeof("kanra.gunma.jp")];
- char stringpool_str2853[sizeof("ec")];
- char stringpool_str2854[sizeof("cc")];
- char stringpool_str2855[sizeof("gov.co")];
- char stringpool_str2856[sizeof("edu.co")];
- char stringpool_str2857[sizeof("gov.cm")];
- char stringpool_str2858[sizeof("com.co")];
- char stringpool_str2859[sizeof("chofu.tokyo.jp")];
- char stringpool_str2860[sizeof("ac")];
- char stringpool_str2861[sizeof("ketrzyn.pl")];
- char stringpool_str2862[sizeof("koshu.yamanashi.jp")];
- char stringpool_str2863[sizeof("omasvuotna.no")];
- char stringpool_str2864[sizeof("gov.cl")];
- char stringpool_str2865[sizeof("gob.cl")];
- char stringpool_str2866[sizeof("sakuho.nagano.jp")];
- char stringpool_str2867[sizeof("actor")];
- char stringpool_str2868[sizeof("svizzera.museum")];
- char stringpool_str2869[sizeof("zp.ua")];
- char stringpool_str2870[sizeof("fudai.iwate.jp")];
- char stringpool_str2871[sizeof("int.co")];
- char stringpool_str2872[sizeof("overhalla.no")];
- char stringpool_str2873[sizeof("hk.cn")];
- char stringpool_str2874[sizeof("shimamaki.hokkaido.jp")];
- char stringpool_str2875[sizeof("xj.cn")];
- char stringpool_str2876[sizeof("mi.th")];
- char stringpool_str2877[sizeof("abira.hokkaido.jp")];
- char stringpool_str2878[sizeof("gov.cd")];
- char stringpool_str2879[sizeof("xn--90a3ac")];
- char stringpool_str2880[sizeof("wi.us")];
- char stringpool_str2881[sizeof("monza.it")];
- char stringpool_str2882[sizeof("ako.hyogo.jp")];
- char stringpool_str2883[sizeof("minamiuonuma.niigata.jp")];
- char stringpool_str2884[sizeof("fuchu.toyama.jp")];
- char stringpool_str2885[sizeof("nishiarita.saga.jp")];
- char stringpool_str2886[sizeof("hofu.yamaguchi.jp")];
- char stringpool_str2887[sizeof("wy.us")];
- char stringpool_str2888[sizeof("ecn.br")];
- char stringpool_str2889[sizeof("urawa.saitama.jp")];
- char stringpool_str2890[sizeof("xn--d1acj3b")];
- char stringpool_str2891[sizeof("mc")];
- char stringpool_str2892[sizeof("edu.cw")];
- char stringpool_str2893[sizeof("org.sh")];
- char stringpool_str2894[sizeof("yamanashi.yamanashi.jp")];
- char stringpool_str2895[sizeof("xn--55qx5d")];
- char stringpool_str2896[sizeof("eco.br")];
- char stringpool_str2897[sizeof("fuchu.tokyo.jp")];
- char stringpool_str2898[sizeof("com.cw")];
- char stringpool_str2899[sizeof("fujisawa.kanagawa.jp")];
- char stringpool_str2900[sizeof("ikeda.hokkaido.jp")];
- char stringpool_str2901[sizeof("iwade.wakayama.jp")];
- char stringpool_str2902[sizeof("edu.ci")];
- char stringpool_str2903[sizeof("com.ci")];
- char stringpool_str2904[sizeof("academy")];
- char stringpool_str2905[sizeof("ck.ua")];
- char stringpool_str2906[sizeof("setagaya.tokyo.jp")];
- char stringpool_str2907[sizeof("makurazaki.kagoshima.jp")];
- char stringpool_str2908[sizeof("uk.com")];
- char stringpool_str2909[sizeof("meiwa.gunma.jp")];
- char stringpool_str2910[sizeof("k12.ky.us")];
- char stringpool_str2911[sizeof("xn--55qx5d.cn")];
- char stringpool_str2912[sizeof("namegawa.saitama.jp")];
- char stringpool_str2913[sizeof("wa.us")];
- char stringpool_str2914[sizeof("oizumi.gunma.jp")];
- char stringpool_str2915[sizeof("int.ci")];
- char stringpool_str2916[sizeof("sobetsu.hokkaido.jp")];
- char stringpool_str2917[sizeof("org.bh")];
- char stringpool_str2918[sizeof("wa.au")];
- char stringpool_str2919[sizeof("matsuyama.ehime.jp")];
- char stringpool_str2920[sizeof("ws.na")];
- char stringpool_str2921[sizeof("kanzaki.saga.jp")];
- char stringpool_str2922[sizeof("otsuki.kochi.jp")];
- char stringpool_str2923[sizeof("xn--io0a7i")];
- char stringpool_str2924[sizeof("izena.okinawa.jp")];
- char stringpool_str2925[sizeof("xn--slat-5na.no")];
- char stringpool_str2926[sizeof("kozagawa.wakayama.jp")];
- char stringpool_str2927[sizeof("wv.us")];
- char stringpool_str2928[sizeof("yamatotakada.nara.jp")];
- char stringpool_str2929[sizeof("xn--zfr164b")];
- char stringpool_str2930[sizeof("blogspot.be")];
- char stringpool_str2931[sizeof("iide.yamagata.jp")];
- char stringpool_str2932[sizeof("xn--p1ai")];
- char stringpool_str2933[sizeof("yamato.kanagawa.jp")];
- char stringpool_str2934[sizeof("gf")];
- char stringpool_str2935[sizeof("sakaki.nagano.jp")];
- char stringpool_str2936[sizeof("cf")];
- char stringpool_str2937[sizeof("kosai.shizuoka.jp")];
- char stringpool_str2938[sizeof("mk.ua")];
- char stringpool_str2939[sizeof("com.fr")];
- char stringpool_str2940[sizeof("af")];
- char stringpool_str2941[sizeof("xn--io0a7i.cn")];
- char stringpool_str2942[sizeof("naka.hiroshima.jp")];
- char stringpool_str2943[sizeof("bf")];
- char stringpool_str2944[sizeof("kikugawa.shizuoka.jp")];
- char stringpool_str2945[sizeof("ks.us")];
- char stringpool_str2946[sizeof("aca.pro")];
- char stringpool_str2947[sizeof("askim.no")];
- char stringpool_str2948[sizeof("nesna.no")];
- char stringpool_str2949[sizeof("yokote.akita.jp")];
- char stringpool_str2950[sizeof("higashimatsushima.miyagi.jp")];
- char stringpool_str2951[sizeof("yurihonjo.akita.jp")];
- char stringpool_str2952[sizeof("orkdal.no")];
- char stringpool_str2953[sizeof("online.museum")];
- char stringpool_str2954[sizeof("asso.mc")];
- char stringpool_str2955[sizeof("family.museum")];
- char stringpool_str2956[sizeof("sakawa.kochi.jp")];
- char stringpool_str2957[sizeof("onna.okinawa.jp")];
- char stringpool_str2958[sizeof("go.id")];
- char stringpool_str2959[sizeof("xn--smla-hra.no")];
- char stringpool_str2960[sizeof("sannohe.aomori.jp")];
- char stringpool_str2961[sizeof("co.id")];
- char stringpool_str2962[sizeof("rikuzentakata.iwate.jp")];
- char stringpool_str2963[sizeof("kyowa.akita.jp")];
- char stringpool_str2964[sizeof("id.ir")];
- char stringpool_str2965[sizeof("omi.nagano.jp")];
- char stringpool_str2966[sizeof("blogspot.bj")];
- char stringpool_str2967[sizeof("co.ir")];
- char stringpool_str2968[sizeof("xn--seral-lra.no")];
- char stringpool_str2969[sizeof("co.mw")];
- char stringpool_str2970[sizeof("org.ph")];
- char stringpool_str2971[sizeof("naroy.no")];
- char stringpool_str2972[sizeof("xn--3bst00m")];
- char stringpool_str2973[sizeof("chuo.yamanashi.jp")];
- char stringpool_str2974[sizeof("xn--kpry57d")];
- char stringpool_str2975[sizeof("kr.com")];
- char stringpool_str2976[sizeof("info.ec")];
- char stringpool_str2977[sizeof("co.rw")];
- char stringpool_str2978[sizeof("uruma.okinawa.jp")];
- char stringpool_str2979[sizeof("yokohama.jp")];
- char stringpool_str2980[sizeof("sakata.yamagata.jp")];
- char stringpool_str2981[sizeof("mil.co")];
- char stringpool_str2982[sizeof("yamanakako.yamanashi.jp")];
- char stringpool_str2983[sizeof("vestby.no")];
- char stringpool_str2984[sizeof("honjo.saitama.jp")];
- char stringpool_str2985[sizeof("omsk.ru")];
- char stringpool_str2986[sizeof("co.in")];
- char stringpool_str2987[sizeof("komae.tokyo.jp")];
- char stringpool_str2988[sizeof("mil.cl")];
- char stringpool_str2989[sizeof("xn--skjk-soa.no")];
- char stringpool_str2990[sizeof("yokoze.saitama.jp")];
- char stringpool_str2991[sizeof("go.it")];
- char stringpool_str2992[sizeof("co.it")];
- char stringpool_str2993[sizeof("kakegawa.shizuoka.jp")];
- char stringpool_str2994[sizeof("cz.it")];
- char stringpool_str2995[sizeof("grong.no")];
- char stringpool_str2996[sizeof("xn--j1amh")];
- char stringpool_str2997[sizeof("contractors")];
- char stringpool_str2998[sizeof("yamashina.kyoto.jp")];
- char stringpool_str2999[sizeof("is.it")];
- char stringpool_str3000[sizeof("zgrad.ru")];
- char stringpool_str3001[sizeof("yomitan.okinawa.jp")];
- char stringpool_str3002[sizeof("gr.it")];
- char stringpool_str3003[sizeof("cs.it")];
- char stringpool_str3004[sizeof("shizuoka.jp")];
- char stringpool_str3005[sizeof("cr.it")];
- char stringpool_str3006[sizeof("ao.it")];
- char stringpool_str3007[sizeof("ae.org")];
- char stringpool_str3008[sizeof("cl.it")];
- char stringpool_str3009[sizeof("shishikui.tokushima.jp")];
- char stringpool_str3010[sizeof("brescia.it")];
- char stringpool_str3011[sizeof("ar.it")];
- char stringpool_str3012[sizeof("bo.it")];
- char stringpool_str3013[sizeof("xn--yer-zna.no")];
- char stringpool_str3014[sizeof("ky.us")];
- char stringpool_str3015[sizeof("bz.it")];
- char stringpool_str3016[sizeof("xn--rdal-poa.no")];
- char stringpool_str3017[sizeof("al.it")];
- char stringpool_str3018[sizeof("bs.it")];
- char stringpool_str3019[sizeof("br.it")];
- char stringpool_str3020[sizeof("fr.it")];
- char stringpool_str3021[sizeof("xn--od0alg.cn")];
- char stringpool_str3022[sizeof("bl.it")];
- char stringpool_str3023[sizeof("en.it")];
- char stringpool_str3024[sizeof("iwata.shizuoka.jp")];
- char stringpool_str3025[sizeof("cn.it")];
- char stringpool_str3026[sizeof("blogspot.cf")];
- char stringpool_str3027[sizeof("2000.hu")];
- char stringpool_str3028[sizeof("motorcycles")];
- char stringpool_str3029[sizeof("ge.it")];
- char stringpool_str3030[sizeof("an.it")];
- char stringpool_str3031[sizeof("itano.tokushima.jp")];
- char stringpool_str3032[sizeof("ce.it")];
- char stringpool_str3033[sizeof("rieti.it")];
- char stringpool_str3034[sizeof("xn--rland-uua.no")];
- char stringpool_str3035[sizeof("bn.it")];
- char stringpool_str3036[sizeof("ct.it")];
- char stringpool_str3037[sizeof("is-an-actress.com")];
- char stringpool_str3038[sizeof("aq.it")];
- char stringpool_str3039[sizeof("vagsoy.no")];
- char stringpool_str3040[sizeof("at.it")];
- char stringpool_str3041[sizeof("xn--mely-ira.no")];
- char stringpool_str3042[sizeof("fe.it")];
- char stringpool_str3043[sizeof("is-a-caterer.com")];
- char stringpool_str3044[sizeof("bt.it")];
- char stringpool_str3045[sizeof("nagahama.shiga.jp")];
- char stringpool_str3046[sizeof("cb.it")];
- char stringpool_str3047[sizeof("okutama.tokyo.jp")];
- char stringpool_str3048[sizeof("ks.ua")];
- char stringpool_str3049[sizeof("kr.ua")];
- char stringpool_str3050[sizeof("yonabaru.okinawa.jp")];
- char stringpool_str3051[sizeof("co.im")];
- char stringpool_str3052[sizeof("roros.no")];
- char stringpool_str3053[sizeof("sanuki.kagawa.jp")];
- char stringpool_str3054[sizeof("omi.niigata.jp")];
- char stringpool_str3055[sizeof("xn--sr-aurdal-l8a.no")];
- char stringpool_str3056[sizeof("ci.it")];
- char stringpool_str3057[sizeof("us.org")];
- char stringpool_str3058[sizeof("kitanakagusuku.okinawa.jp")];
- char stringpool_str3059[sizeof("do")];
- char stringpool_str3060[sizeof("nakijin.okinawa.jp")];
- char stringpool_str3061[sizeof("dz")];
- char stringpool_str3062[sizeof("mo.it")];
- char stringpool_str3063[sizeof("yakumo.hokkaido.jp")];
- char stringpool_str3064[sizeof("ms.it")];
- char stringpool_str3065[sizeof("web.co")];
- char stringpool_str3066[sizeof("edu.sn")];
- char stringpool_str3067[sizeof("is-slick.com")];
- char stringpool_str3068[sizeof("com.sn")];
- char stringpool_str3069[sizeof("bi.it")];
- char stringpool_str3070[sizeof("xn--troms-zua.no")];
- char stringpool_str3071[sizeof("fi.it")];
- char stringpool_str3072[sizeof("hiratsuka.kanagawa.jp")];
- char stringpool_str3073[sizeof("ozu.ehime.jp")];
- char stringpool_str3074[sizeof("is-an-artist.com")];
- char stringpool_str3075[sizeof("coop")];
- char stringpool_str3076[sizeof("navigation.aero")];
- char stringpool_str3077[sizeof("xn--asky-ira.no")];
- char stringpool_str3078[sizeof("art.sn")];
- char stringpool_str3079[sizeof("feedback")];
- char stringpool_str3080[sizeof("koori.fukushima.jp")];
- char stringpool_str3081[sizeof("oshima.tokyo.jp")];
- char stringpool_str3082[sizeof("d.bg")];
- char stringpool_str3083[sizeof("grue.no")];
- char stringpool_str3084[sizeof("coop.mw")];
- char stringpool_str3085[sizeof("xn--rros-gra.no")];
- char stringpool_str3086[sizeof("d.se")];
- char stringpool_str3087[sizeof("ud.it")];
- char stringpool_str3088[sizeof("mn.it")];
- char stringpool_str3089[sizeof("dnp")];
- char stringpool_str3090[sizeof("ukiha.fukuoka.jp")];
- char stringpool_str3091[sizeof("omachi.nagano.jp")];
- char stringpool_str3092[sizeof("enterprises")];
- char stringpool_str3093[sizeof("zgora.pl")];
- char stringpool_str3094[sizeof("alta.no")];
- char stringpool_str3095[sizeof("de")];
- char stringpool_str3096[sizeof("im.it")];
- char stringpool_str3097[sizeof("k12.wy.us")];
- char stringpool_str3098[sizeof("sumoto.hyogo.jp")];
- char stringpool_str3099[sizeof("me.it")];
- char stringpool_str3100[sizeof("co.bw")];
- char stringpool_str3101[sizeof("arna.no")];
- char stringpool_str3102[sizeof("kamakura.kanagawa.jp")];
- char stringpool_str3103[sizeof("cloudcontrolled.com")];
- char stringpool_str3104[sizeof("mt.it")];
- char stringpool_str3105[sizeof("info.na")];
- char stringpool_str3106[sizeof("gov.tn")];
- char stringpool_str3107[sizeof("my.id")];
- char stringpool_str3108[sizeof("aure.no")];
- char stringpool_str3109[sizeof("com.tn")];
- char stringpool_str3110[sizeof("coop.br")];
- char stringpool_str3111[sizeof("xn--merker-kua.no")];
- char stringpool_str3112[sizeof("fm.it")];
- char stringpool_str3113[sizeof("xn--sr-odal-q1a.no")];
- char stringpool_str3114[sizeof("coop.py")];
- char stringpool_str3115[sizeof("horten.no")];
- char stringpool_str3116[sizeof("xn--i1b6b1a6a2e")];
- char stringpool_str3117[sizeof("xn--sknit-yqa.no")];
- char stringpool_str3118[sizeof("mb.it")];
- char stringpool_str3119[sizeof("info.nr")];
- char stringpool_str3120[sizeof("gran.no")];
- char stringpool_str3121[sizeof("fusa.no")];
- char stringpool_str3122[sizeof("ca.it")];
- char stringpool_str3123[sizeof("dep.no")];
- char stringpool_str3124[sizeof("mi.it")];
- char stringpool_str3125[sizeof("gov.in")];
- char stringpool_str3126[sizeof("edu.in")];
- char stringpool_str3127[sizeof("honefoss.no")];
- char stringpool_str3128[sizeof("shingo.aomori.jp")];
- char stringpool_str3129[sizeof("etne.no")];
- char stringpool_str3130[sizeof("bodo.no")];
- char stringpool_str3131[sizeof("arq.br")];
- char stringpool_str3132[sizeof("xn--smna-gra.no")];
- char stringpool_str3133[sizeof("ba.it")];
- char stringpool_str3134[sizeof("xn--trgstad-r1a.no")];
- char stringpool_str3135[sizeof("za.org")];
- char stringpool_str3136[sizeof("risor.no")];
- char stringpool_str3137[sizeof("city.kobe.jp")];
- char stringpool_str3138[sizeof("mormon")];
- char stringpool_str3139[sizeof("starnberg.museum")];
- char stringpool_str3140[sizeof("is-a-painter.com")];
- char stringpool_str3141[sizeof("berg.no")];
- char stringpool_str3142[sizeof("istmein.de")];
- char stringpool_str3143[sizeof("yame.fukuoka.jp")];
- char stringpool_str3144[sizeof("iheya.okinawa.jp")];
- char stringpool_str3145[sizeof("ap.it")];
- char stringpool_str3146[sizeof("ens.tn")];
- char stringpool_str3147[sizeof("xn--risa-5na.no")];
- char stringpool_str3148[sizeof("arts.nf")];
- char stringpool_str3149[sizeof("km.ua")];
- char stringpool_str3150[sizeof("is-an-actor.com")];
- char stringpool_str3151[sizeof("av.it")];
- char stringpool_str3152[sizeof("dj")];
- char stringpool_str3153[sizeof("from-co.net")];
- char stringpool_str3154[sizeof("xn--mli-tla.no")];
- char stringpool_str3155[sizeof("moss.no")];
- char stringpool_str3156[sizeof("xn--linds-pra.no")];
- char stringpool_str3157[sizeof("from-la.net")];
- char stringpool_str3158[sizeof("xn--yfro4i67o")];
- char stringpool_str3159[sizeof("xn--55qx5d.hk")];
- char stringpool_str3160[sizeof("dm")];
- char stringpool_str3161[sizeof("hole.no")];
- char stringpool_str3162[sizeof("gov.mn")];
- char stringpool_str3163[sizeof("historisch.museum")];
- char stringpool_str3164[sizeof("edu.mn")];
- char stringpool_str3165[sizeof("info.nf")];
- char stringpool_str3166[sizeof("yakumo.shimane.jp")];
- char stringpool_str3167[sizeof("from-ma.com")];
- char stringpool_str3168[sizeof("gen.in")];
- char stringpool_str3169[sizeof("coop.mv")];
- char stringpool_str3170[sizeof("ind.tn")];
- char stringpool_str3171[sizeof("steigen.no")];
- char stringpool_str3172[sizeof("xn--risr-ira.no")];
- char stringpool_str3173[sizeof("from-ca.com")];
- char stringpool_str3174[sizeof("from-mo.com")];
- char stringpool_str3175[sizeof("rodoy.no")];
- char stringpool_str3176[sizeof("from-ri.com")];
- char stringpool_str3177[sizeof("berlin")];
- char stringpool_str3178[sizeof("from-mi.com")];
- char stringpool_str3179[sizeof("omachi.saga.jp")];
- char stringpool_str3180[sizeof("bergen.no")];
- char stringpool_str3181[sizeof("hammerfest.no")];
- char stringpool_str3182[sizeof("romsa.no")];
- char stringpool_str3183[sizeof("from-pa.com")];
- char stringpool_str3184[sizeof("suli.hu")];
- char stringpool_str3185[sizeof("from-or.com")];
- char stringpool_str3186[sizeof("swiebodzin.pl")];
- char stringpool_str3187[sizeof("from-az.net")];
- char stringpool_str3188[sizeof("from-ky.com")];
- char stringpool_str3189[sizeof("wf")];
- char stringpool_str3190[sizeof("fin.tn")];
- char stringpool_str3191[sizeof("from-ok.com")];
- char stringpool_str3192[sizeof("edu.an")];
- char stringpool_str3193[sizeof("ind.in")];
- char stringpool_str3194[sizeof("camp")];
- char stringpool_str3195[sizeof("com.an")];
- char stringpool_str3196[sizeof("iwaki.fukushima.jp")];
- char stringpool_str3197[sizeof("okegawa.saitama.jp")];
- char stringpool_str3198[sizeof("iwate.iwate.jp")];
- char stringpool_str3199[sizeof("merseine.nu")];
- char stringpool_str3200[sizeof("from-pr.com")];
- char stringpool_str3201[sizeof("amot.no")];
- char stringpool_str3202[sizeof("niki.hokkaido.jp")];
- char stringpool_str3203[sizeof("okayama.okayama.jp")];
- char stringpool_str3204[sizeof("xn--trany-yua.no")];
- char stringpool_str3205[sizeof("ch.it")];
- char stringpool_str3206[sizeof("kv.ua")];
- char stringpool_str3207[sizeof("community")];
- char stringpool_str3208[sizeof("from-al.com")];
- char stringpool_str3209[sizeof("gov.pn")];
- char stringpool_str3210[sizeof("from-mn.com")];
- char stringpool_str3211[sizeof("edu.pn")];
- char stringpool_str3212[sizeof("eniwa.hokkaido.jp")];
- char stringpool_str3213[sizeof("omaezaki.shizuoka.jp")];
- char stringpool_str3214[sizeof("agrar.hu")];
- char stringpool_str3215[sizeof("bialowieza.pl")];
- char stringpool_str3216[sizeof("gov.vn")];
- char stringpool_str3217[sizeof("edu.vn")];
- char stringpool_str3218[sizeof("homeunix.com")];
- char stringpool_str3219[sizeof("com.vn")];
- char stringpool_str3220[sizeof("nakasatsunai.hokkaido.jp")];
- char stringpool_str3221[sizeof("xn--io0a7i.hk")];
- char stringpool_str3222[sizeof("oxford.museum")];
- char stringpool_str3223[sizeof("firm.nf")];
- char stringpool_str3224[sizeof("aerodrome.aero")];
- char stringpool_str3225[sizeof("from-ar.com")];
- char stringpool_str3226[sizeof("gov.cu")];
- char stringpool_str3227[sizeof("edu.cu")];
- char stringpool_str3228[sizeof("is-a-socialist.com")];
- char stringpool_str3229[sizeof("cremona.it")];
- char stringpool_str3230[sizeof("com.cu")];
- char stringpool_str3231[sizeof("rygge.no")];
- char stringpool_str3232[sizeof("kofu.yamanashi.jp")];
- char stringpool_str3233[sizeof("kunigami.okinawa.jp")];
- char stringpool_str3234[sizeof("folkebibl.no")];
- char stringpool_str3235[sizeof("xn--rennesy-v1a.no")];
- char stringpool_str3236[sizeof("from-ak.com")];
- char stringpool_str3237[sizeof("kagoshima.kagoshima.jp")];
- char stringpool_str3238[sizeof("motorcycle.museum")];
- char stringpool_str3239[sizeof("is-a-doctor.com")];
- char stringpool_str3240[sizeof("net.sy")];
- char stringpool_str3241[sizeof("from-wa.com")];
- char stringpool_str3242[sizeof("xn--bmlo-gra.no")];
- char stringpool_str3243[sizeof("coop.km")];
- char stringpool_str3244[sizeof("bo.telemark.no")];
- char stringpool_str3245[sizeof("fukui.fukui.jp")];
- char stringpool_str3246[sizeof("net.ly")];
- char stringpool_str3247[sizeof("homeunix.net")];
- char stringpool_str3248[sizeof("int.vn")];
- char stringpool_str3249[sizeof("from-wi.com")];
- char stringpool_str3250[sizeof("net.uy")];
- char stringpool_str3251[sizeof("radom.pl")];
- char stringpool_str3252[sizeof("from-wy.com")];
- char stringpool_str3253[sizeof("from-il.com")];
- char stringpool_str3254[sizeof("from-ia.com")];
- char stringpool_str3255[sizeof("sunagawa.hokkaido.jp")];
- char stringpool_str3256[sizeof("inf.cu")];
- char stringpool_str3257[sizeof("kh.ua")];
- char stringpool_str3258[sizeof("nakaniikawa.toyama.jp")];
- char stringpool_str3259[sizeof("rissa.no")];
- char stringpool_str3260[sizeof("dni.us")];
- char stringpool_str3261[sizeof("xn--rde-ula.no")];
- char stringpool_str3262[sizeof("otsuki.yamanashi.jp")];
- char stringpool_str3263[sizeof("shikokuchuo.ehime.jp")];
- char stringpool_str3264[sizeof("xn--hobl-ira.no")];
- char stringpool_str3265[sizeof("control.aero")];
- char stringpool_str3266[sizeof("osakasayama.osaka.jp")];
- char stringpool_str3267[sizeof("kasuga.hyogo.jp")];
- char stringpool_str3268[sizeof("ivgu.no")];
- char stringpool_str3269[sizeof("gob.hn")];
- char stringpool_str3270[sizeof("edu.hn")];
- char stringpool_str3271[sizeof("insurance.aero")];
- char stringpool_str3272[sizeof("com.hn")];
- char stringpool_str3273[sizeof("getmyip.com")];
- char stringpool_str3274[sizeof("dance")];
- char stringpool_str3275[sizeof("kaita.hiroshima.jp")];
- char stringpool_str3276[sizeof("donetsk.ua")];
- char stringpool_str3277[sizeof("onomichi.hiroshima.jp")];
- char stringpool_str3278[sizeof("xn--sandy-yua.no")];
- char stringpool_str3279[sizeof("dnsalias.com")];
- char stringpool_str3280[sizeof("grozny.ru")];
- char stringpool_str3281[sizeof("itoigawa.niigata.jp")];
- char stringpool_str3282[sizeof("from-in.com")];
- char stringpool_str3283[sizeof("beiarn.no")];
- char stringpool_str3284[sizeof("seoul.kr")];
- char stringpool_str3285[sizeof("xn--msy-ula0h.no")];
- char stringpool_str3286[sizeof("xn--4gbrim")];
- char stringpool_str3287[sizeof("batsfjord.no")];
- char stringpool_str3288[sizeof("dnsalias.net")];
- char stringpool_str3289[sizeof("xn--lcvr32d.hk")];
- char stringpool_str3290[sizeof("radoy.no")];
- char stringpool_str3291[sizeof("yonago.tottori.jp")];
- char stringpool_str3292[sizeof("artgallery.museum")];
- char stringpool_str3293[sizeof("in-the-band.net")];
- char stringpool_str3294[sizeof("yoshikawa.saitama.jp")];
- char stringpool_str3295[sizeof("historisches.museum")];
- char stringpool_str3296[sizeof("from-wv.com")];
- char stringpool_str3297[sizeof("rauma.no")];
- char stringpool_str3298[sizeof("from-fl.com")];
- char stringpool_str3299[sizeof("xn--od0alg.hk")];
- char stringpool_str3300[sizeof("from-ms.com")];
- char stringpool_str3301[sizeof("ninomiya.kanagawa.jp")];
- char stringpool_str3302[sizeof("cooking")];
- char stringpool_str3303[sizeof("xn--ngbc5azd")];
- char stringpool_str3304[sizeof("is-a-democrat.com")];
- char stringpool_str3305[sizeof("dk")];
- char stringpool_str3306[sizeof("webhop.biz")];
- char stringpool_str3307[sizeof("from-ks.com")];
- char stringpool_str3308[sizeof("dynalias.com")];
- char stringpool_str3309[sizeof("gov.kn")];
- char stringpool_str3310[sizeof("edu.kn")];
- char stringpool_str3311[sizeof("net.my")];
- char stringpool_str3312[sizeof("volyn.ua")];
- char stringpool_str3313[sizeof("halden.no")];
- char stringpool_str3314[sizeof("salzburg.museum")];
- char stringpool_str3315[sizeof("xn--vard-jra.no")];
- char stringpool_str3316[sizeof("kurogi.fukuoka.jp")];
- char stringpool_str3317[sizeof("xn--lrenskog-54a.no")];
- char stringpool_str3318[sizeof("xn--mjndalen-64a.no")];
- char stringpool_str3319[sizeof("elvendrell.museum")];
- char stringpool_str3320[sizeof("histoire.museum")];
- char stringpool_str3321[sizeof("mil.in")];
- char stringpool_str3322[sizeof("ambulance.aero")];
- char stringpool_str3323[sizeof("org.mx")];
- char stringpool_str3324[sizeof("frosinone.it")];
- char stringpool_str3325[sizeof("sopot.pl")];
- char stringpool_str3326[sizeof("sasayama.hyogo.jp")];
- char stringpool_str3327[sizeof("dynalias.net")];
- char stringpool_str3328[sizeof("association.aero")];
- char stringpool_str3329[sizeof("holdings")];
- char stringpool_str3330[sizeof("otofuke.hokkaido.jp")];
- char stringpool_str3331[sizeof("belluno.it")];
- char stringpool_str3332[sizeof("act.au")];
- char stringpool_str3333[sizeof("co.je")];
- char stringpool_str3334[sizeof("is-a-rockstar.com")];
- char stringpool_str3335[sizeof("biz.vn")];
- char stringpool_str3336[sizeof("yokaichiba.chiba.jp")];
- char stringpool_str3337[sizeof("namegata.ibaraki.jp")];
- char stringpool_str3338[sizeof("yasuoka.nagano.jp")];
- char stringpool_str3339[sizeof("bahccavuotna.no")];
- char stringpool_str3340[sizeof("net.py")];
- char stringpool_str3341[sizeof("xn--lesund-hua.no")];
- char stringpool_str3342[sizeof("iamallama.com")];
- char stringpool_str3343[sizeof("nanmoku.gunma.jp")];
- char stringpool_str3344[sizeof("dyndns.tv")];
- char stringpool_str3345[sizeof("medecin.km")];
- char stringpool_str3346[sizeof("xn--hery-ira.nordland.no")];
- char stringpool_str3347[sizeof("magazine.aero")];
- char stringpool_str3348[sizeof("nakanoto.ishikawa.jp")];
- char stringpool_str3349[sizeof("drobak.no")];
- char stringpool_str3350[sizeof("balsfjord.no")];
- char stringpool_str3351[sizeof("messina.it")];
- char stringpool_str3352[sizeof("gx.cn")];
- char stringpool_str3353[sizeof("xn--od0aq3b.hk")];
- char stringpool_str3354[sizeof("brussel.museum")];
- char stringpool_str3355[sizeof("xn--rst-0na.no")];
- char stringpool_str3356[sizeof("funagata.yamagata.jp")];
- char stringpool_str3357[sizeof("frogans")];
- char stringpool_str3358[sizeof("gemological.museum")];
- char stringpool_str3359[sizeof("mjondalen.no")];
- char stringpool_str3360[sizeof("oshino.yamanashi.jp")];
- char stringpool_str3361[sizeof("here-for-more.info")];
- char stringpool_str3362[sizeof("com.gy")];
- char stringpool_str3363[sizeof("from-de.com")];
- char stringpool_str3364[sizeof("maison")];
- char stringpool_str3365[sizeof("iki.fi")];
- char stringpool_str3366[sizeof("xn--mgb2ddes")];
- char stringpool_str3367[sizeof("xn--80aswg")];
- char stringpool_str3368[sizeof("artdeco.museum")];
- char stringpool_str3369[sizeof("kasuga.fukuoka.jp")];
- char stringpool_str3370[sizeof("francaise.museum")];
- char stringpool_str3371[sizeof("gose.nara.jp")];
- char stringpool_str3372[sizeof("xn--lury-ira.no")];
- char stringpool_str3373[sizeof("international")];
- char stringpool_str3374[sizeof("wa.edu.au")];
- char stringpool_str3375[sizeof("from-oh.com")];
- char stringpool_str3376[sizeof("yamaga.kumamoto.jp")];
- char stringpool_str3377[sizeof("crotone.it")];
- char stringpool_str3378[sizeof("is-a-anarchist.com")];
- char stringpool_str3379[sizeof("siena.it")];
- char stringpool_str3380[sizeof("defense.tn")];
- char stringpool_str3381[sizeof("history.museum")];
- char stringpool_str3382[sizeof("k12.co.us")];
- char stringpool_str3383[sizeof("dielddanuorri.no")];
- char stringpool_str3384[sizeof("xn--zf0avx.hk")];
- char stringpool_str3385[sizeof("vt.us")];
- char stringpool_str3386[sizeof("rikubetsu.hokkaido.jp")];
- char stringpool_str3387[sizeof("stord.no")];
- char stringpool_str3388[sizeof("org.gr")];
- char stringpool_str3389[sizeof("eastcoast.museum")];
- char stringpool_str3390[sizeof("shinshinotsu.hokkaido.jp")];
- char stringpool_str3391[sizeof("midatlantic.museum")];
- char stringpool_str3392[sizeof("obihiro.hokkaido.jp")];
- char stringpool_str3393[sizeof("association.museum")];
- char stringpool_str3394[sizeof("raisa.no")];
- char stringpool_str3395[sizeof("store.ro")];
- char stringpool_str3396[sizeof("go.jp")];
- char stringpool_str3397[sizeof("kr.it")];
- char stringpool_str3398[sizeof("ed.jp")];
- char stringpool_str3399[sizeof("co.jp")];
- char stringpool_str3400[sizeof("sogne.no")];
- char stringpool_str3401[sizeof("org.gp")];
- char stringpool_str3402[sizeof("fishing")];
- char stringpool_str3403[sizeof("cloudcontrolapp.com")];
- char stringpool_str3404[sizeof("org.ge")];
- char stringpool_str3405[sizeof("shioya.tochigi.jp")];
- char stringpool_str3406[sizeof("store.st")];
- char stringpool_str3407[sizeof("gr.jp")];
- char stringpool_str3408[sizeof("vi.us")];
- char stringpool_str3409[sizeof("yoro.gifu.jp")];
- char stringpool_str3410[sizeof("ando.nara.jp")];
- char stringpool_str3411[sizeof("ad.jp")];
- char stringpool_str3412[sizeof("countryestate.museum")];
- char stringpool_str3413[sizeof("encyclopedic.museum")];
- char stringpool_str3414[sizeof("yuki.ibaraki.jp")];
- char stringpool_str3415[sizeof("nishi.osaka.jp")];
- char stringpool_str3416[sizeof("hagi.yamaguchi.jp")];
- char stringpool_str3417[sizeof("murmansk.ru")];
- char stringpool_str3418[sizeof("chambagri.fr")];
- char stringpool_str3419[sizeof("xn--vgan-qoa.no")];
- char stringpool_str3420[sizeof("sorum.no")];
- char stringpool_str3421[sizeof("oumu.hokkaido.jp")];
- char stringpool_str3422[sizeof("giessen.museum")];
- char stringpool_str3423[sizeof("xn--zf0ao64a.tw")];
- char stringpool_str3424[sizeof("mil.hn")];
- char stringpool_str3425[sizeof("boston.museum")];
- char stringpool_str3426[sizeof("afjord.no")];
- char stringpool_str3427[sizeof("vikna.no")];
- char stringpool_str3428[sizeof("k12.ca.us")];
- char stringpool_str3429[sizeof("xn--uc0ay4a.hk")];
- char stringpool_str3430[sizeof("drammen.no")];
- char stringpool_str3431[sizeof("net.ky")];
- char stringpool_str3432[sizeof("valley.museum")];
- char stringpool_str3433[sizeof("wielun.pl")];
- char stringpool_str3434[sizeof("mallorca.museum")];
- char stringpool_str3435[sizeof("xn--b-5ga.nordland.no")];
- char stringpool_str3436[sizeof("amusement.aero")];
- char stringpool_str3437[sizeof("xn--muost-0qa.no")];
- char stringpool_str3438[sizeof("xn--karmy-yua.no")];
- char stringpool_str3439[sizeof("xn--mgbab2bd")];
- char stringpool_str3440[sizeof("ozu.kumamoto.jp")];
- char stringpool_str3441[sizeof("xn--osyro-wua.no")];
- char stringpool_str3442[sizeof("miki.hyogo.jp")];
- char stringpool_str3443[sizeof("florida.museum")];
- char stringpool_str3444[sizeof("birkenes.no")];
- char stringpool_str3445[sizeof("awaji.hyogo.jp")];
- char stringpool_str3446[sizeof("gloppen.no")];
- char stringpool_str3447[sizeof("stryn.no")];
- char stringpool_str3448[sizeof("moskenes.no")];
- char stringpool_str3449[sizeof("komforb.se")];
- char stringpool_str3450[sizeof("somna.no")];
- char stringpool_str3451[sizeof("orkanger.no")];
- char stringpool_str3452[sizeof("campobasso.it")];
- char stringpool_str3453[sizeof("mx.na")];
- char stringpool_str3454[sizeof("niimi.okayama.jp")];
- char stringpool_str3455[sizeof("org.gi")];
- char stringpool_str3456[sizeof("va.us")];
- char stringpool_str3457[sizeof("handson.museum")];
- char stringpool_str3458[sizeof("ino.kochi.jp")];
- char stringpool_str3459[sizeof("hobby-site.com")];
- char stringpool_str3460[sizeof("homeunix.org")];
- char stringpool_str3461[sizeof("xn--mlatvuopmi-s4a.no")];
- char stringpool_str3462[sizeof("hammarfeasta.no")];
- char stringpool_str3463[sizeof("va.no")];
- char stringpool_str3464[sizeof("saka.hiroshima.jp")];
- char stringpool_str3465[sizeof("nakagawa.nagano.jp")];
- char stringpool_str3466[sizeof("seljord.no")];
- char stringpool_str3467[sizeof("gniezno.pl")];
- char stringpool_str3468[sizeof("austin.museum")];
- char stringpool_str3469[sizeof("dyndns.org")];
- char stringpool_str3470[sizeof("vn.ua")];
- char stringpool_str3471[sizeof("snasa.no")];
- char stringpool_str3472[sizeof("eastafrica.museum")];
- char stringpool_str3473[sizeof("bristol.museum")];
- char stringpool_str3474[sizeof("berlin.museum")];
- char stringpool_str3475[sizeof("balsan.it")];
- char stringpool_str3476[sizeof("shiroishi.saga.jp")];
- char stringpool_str3477[sizeof("modern.museum")];
- char stringpool_str3478[sizeof("campidanomedio.it")];
- char stringpool_str3479[sizeof("radio.br")];
- char stringpool_str3480[sizeof("charter.aero")];
- char stringpool_str3481[sizeof("botanical.museum")];
- char stringpool_str3482[sizeof("ama.aichi.jp")];
- char stringpool_str3483[sizeof("xn--vg-yiab.no")];
- char stringpool_str3484[sizeof("sakuragawa.ibaraki.jp")];
- char stringpool_str3485[sizeof("kitagata.gifu.jp")];
- char stringpool_str3486[sizeof("shimokawa.hokkaido.jp")];
- char stringpool_str3487[sizeof("kyowa.hokkaido.jp")];
- char stringpool_str3488[sizeof("chiropractic.museum")];
- char stringpool_str3489[sizeof("costume.museum")];
- char stringpool_str3490[sizeof("nakagawa.hokkaido.jp")];
- char stringpool_str3491[sizeof("carrier.museum")];
- char stringpool_str3492[sizeof("shizuoka.shizuoka.jp")];
- char stringpool_str3493[sizeof("frei.no")];
- char stringpool_str3494[sizeof("asahi.nagano.jp")];
- char stringpool_str3495[sizeof("dnsalias.org")];
- char stringpool_str3496[sizeof("slask.pl")];
- char stringpool_str3497[sizeof("bauern.museum")];
- char stringpool_str3498[sizeof("desi")];
- char stringpool_str3499[sizeof("clinton.museum")];
- char stringpool_str3500[sizeof("gallery.museum")];
- char stringpool_str3501[sizeof("nakagawa.tokushima.jp")];
- char stringpool_str3502[sizeof("helsinki.museum")];
- char stringpool_str3503[sizeof("futsu.nagasaki.jp")];
- char stringpool_str3504[sizeof("shikama.miyagi.jp")];
- char stringpool_str3505[sizeof("k12.fl.us")];
- char stringpool_str3506[sizeof("dvrdns.org")];
- char stringpool_str3507[sizeof("xn--uc0atv.hk")];
- char stringpool_str3508[sizeof("hizen.saga.jp")];
- char stringpool_str3509[sizeof("garden.museum")];
- char stringpool_str3510[sizeof("smola.no")];
- char stringpool_str3511[sizeof("is-leet.com")];
- char stringpool_str3512[sizeof("xn--pgbs0dh")];
- char stringpool_str3513[sizeof("badajoz.museum")];
- char stringpool_str3514[sizeof("shonai.fukuoka.jp")];
- char stringpool_str3515[sizeof("xn--mot-tla.no")];
- char stringpool_str3516[sizeof("salat.no")];
- char stringpool_str3517[sizeof("deatnu.no")];
- char stringpool_str3518[sizeof("miasa.nagano.jp")];
- char stringpool_str3519[sizeof("mobi.na")];
- char stringpool_str3520[sizeof("inuyama.aichi.jp")];
- char stringpool_str3521[sizeof("sauda.no")];
- char stringpool_str3522[sizeof("divtasvuodna.no")];
- char stringpool_str3523[sizeof("is-a-green.com")];
- char stringpool_str3524[sizeof("dynalias.org")];
- char stringpool_str3525[sizeof("oppegard.no")];
- char stringpool_str3526[sizeof("corporation.museum")];
- char stringpool_str3527[sizeof("selbu.no")];
- char stringpool_str3528[sizeof("xn--6qq986b3xl")];
- char stringpool_str3529[sizeof("seirou.niigata.jp")];
- char stringpool_str3530[sizeof("mansion.museum")];
- char stringpool_str3531[sizeof("cechire.com")];
- char stringpool_str3532[sizeof("hattfjelldal.no")];
- char stringpool_str3533[sizeof("from-ga.com")];
- char stringpool_str3534[sizeof("from-mt.com")];
- char stringpool_str3535[sizeof("imari.saga.jp")];
- char stringpool_str3536[sizeof("from-ct.com")];
- char stringpool_str3537[sizeof("isshiki.aichi.jp")];
- char stringpool_str3538[sizeof("alabama.museum")];
- char stringpool_str3539[sizeof("okinawa.okinawa.jp")];
- char stringpool_str3540[sizeof("burghof.museum")];
- char stringpool_str3541[sizeof("castres.museum")];
- char stringpool_str3542[sizeof("shinagawa.tokyo.jp")];
- char stringpool_str3543[sizeof("naklo.pl")];
- char stringpool_str3544[sizeof("kamigori.hyogo.jp")];
- char stringpool_str3545[sizeof("community.museum")];
- char stringpool_str3546[sizeof("culture.museum")];
- char stringpool_str3547[sizeof("ushuaia.museum")];
- char stringpool_str3548[sizeof("astronomy.museum")];
- char stringpool_str3549[sizeof("cincinnati.museum")];
- char stringpool_str3550[sizeof("izumi.kagoshima.jp")];
- char stringpool_str3551[sizeof("farmers.museum")];
- char stringpool_str3552[sizeof("art.museum")];
- char stringpool_str3553[sizeof("yonezawa.yamagata.jp")];
- char stringpool_str3554[sizeof("xn--krager-gya.no")];
- char stringpool_str3555[sizeof("nakama.fukuoka.jp")];
- char stringpool_str3556[sizeof("skaun.no")];
- char stringpool_str3557[sizeof("nanbu.yamanashi.jp")];
- char stringpool_str3558[sizeof("annefrank.museum")];
- char stringpool_str3559[sizeof("massa-carrara.it")];
- char stringpool_str3560[sizeof("aarborte.no")];
- char stringpool_str3561[sizeof("vc")];
- char stringpool_str3562[sizeof("sanok.pl")];
- char stringpool_str3563[sizeof("osakikamijima.hiroshima.jp")];
- char stringpool_str3564[sizeof("xn--kfjord-iua.no")];
- char stringpool_str3565[sizeof("okinoshima.shimane.jp")];
- char stringpool_str3566[sizeof("oiso.kanagawa.jp")];
- char stringpool_str3567[sizeof("xn--koluokta-7ya57h.no")];
- char stringpool_str3568[sizeof("halloffame.museum")];
- char stringpool_str3569[sizeof("gets-it.net")];
- char stringpool_str3570[sizeof("gov.dz")];
- char stringpool_str3571[sizeof("edu.dz")];
- char stringpool_str3572[sizeof("ps")];
- char stringpool_str3573[sizeof("com.dz")];
- char stringpool_str3574[sizeof("pr")];
- char stringpool_str3575[sizeof("chino.nagano.jp")];
- char stringpool_str3576[sizeof("yasu.shiga.jp")];
- char stringpool_str3577[sizeof("is-a-personaltrainer.com")];
- char stringpool_str3578[sizeof("xn--1qqw23a")];
- char stringpool_str3579[sizeof("mihama.mie.jp")];
- char stringpool_str3580[sizeof("pl")];
- char stringpool_str3581[sizeof("bus.museum")];
- char stringpool_str3582[sizeof("kvam.no")];
- char stringpool_str3583[sizeof("xn--vgsy-qoa0j.no")];
- char stringpool_str3584[sizeof("xn--bievt-0qa.no")];
- char stringpool_str3585[sizeof("mobi.ng")];
- char stringpool_str3586[sizeof("post")];
- char stringpool_str3587[sizeof("kurgan.ru")];
- char stringpool_str3588[sizeof("art.dz")];
- char stringpool_str3589[sizeof("kaufen")];
- char stringpool_str3590[sizeof("pub")];
- char stringpool_str3591[sizeof("hamburg.museum")];
- char stringpool_str3592[sizeof("pro")];
- char stringpool_str3593[sizeof("asahi.yamagata.jp")];
- char stringpool_str3594[sizeof("com.uz")];
- char stringpool_str3595[sizeof("historicalsociety.museum")];
- char stringpool_str3596[sizeof("mazowsze.pl")];
- char stringpool_str3597[sizeof("onga.fukuoka.jp")];
- char stringpool_str3598[sizeof("pro.om")];
- char stringpool_str3599[sizeof("p.bg")];
- char stringpool_str3600[sizeof("pn")];
- char stringpool_str3601[sizeof("p.se")];
- char stringpool_str3602[sizeof("groks-the.info")];
- char stringpool_str3603[sizeof("wildlife.museum")];
- char stringpool_str3604[sizeof("pe")];
- char stringpool_str3605[sizeof("nd.us")];
- char stringpool_str3606[sizeof("western.museum")];
- char stringpool_str3607[sizeof("air.museum")];
- char stringpool_str3608[sizeof("ikeda.nagano.jp")];
- char stringpool_str3609[sizeof("and.museum")];
- char stringpool_str3610[sizeof("dlugoleka.pl")];
- char stringpool_str3611[sizeof("arita.saga.jp")];
- char stringpool_str3612[sizeof("durham.museum")];
- char stringpool_str3613[sizeof("pt")];
- char stringpool_str3614[sizeof("agano.niigata.jp")];
- char stringpool_str3615[sizeof("yamada.fukuoka.jp")];
- char stringpool_str3616[sizeof("horonobe.hokkaido.jp")];
- char stringpool_str3617[sizeof("amli.no")];
- char stringpool_str3618[sizeof("yamato.fukushima.jp")];
- char stringpool_str3619[sizeof("nl.no")];
- char stringpool_str3620[sizeof("communication.museum")];
- char stringpool_str3621[sizeof("gov.bz")];
- char stringpool_str3622[sizeof("edu.bz")];
- char stringpool_str3623[sizeof("communications.museum")];
- char stringpool_str3624[sizeof("com.bz")];
- char stringpool_str3625[sizeof("xn--ystre-slidre-ujb.no")];
- char stringpool_str3626[sizeof("pro.ec")];
- char stringpool_str3627[sizeof("xn--mgbaam7a8h")];
- char stringpool_str3628[sizeof("anan.nagano.jp")];
- char stringpool_str3629[sizeof("pub.sa")];
- char stringpool_str3630[sizeof("ne.tz")];
- char stringpool_str3631[sizeof("xn--wgbh1c")];
- char stringpool_str3632[sizeof("missile.museum")];
- char stringpool_str3633[sizeof("ne.us")];
- char stringpool_str3634[sizeof("ulm.museum")];
- char stringpool_str3635[sizeof("no.com")];
- char stringpool_str3636[sizeof("unzen.nagasaki.jp")];
- char stringpool_str3637[sizeof("homebuilt.aero")];
- char stringpool_str3638[sizeof("funabashi.chiba.jp")];
- char stringpool_str3639[sizeof("pro.br")];
- char stringpool_str3640[sizeof("k12.tn.us")];
- char stringpool_str3641[sizeof("botanicalgarden.museum")];
- char stringpool_str3642[sizeof("yasugi.shimane.jp")];
- char stringpool_str3643[sizeof("dallas.museum")];
- char stringpool_str3644[sizeof("sklep.pl")];
- char stringpool_str3645[sizeof("py")];
- char stringpool_str3646[sizeof("for-the.biz")];
- char stringpool_str3647[sizeof("nt.no")];
- char stringpool_str3648[sizeof("nt.ro")];
- char stringpool_str3649[sizeof("per.la")];
- char stringpool_str3650[sizeof("nt.au")];
- char stringpool_str3651[sizeof("marburg.museum")];
- char stringpool_str3652[sizeof("pro.na")];
- char stringpool_str3653[sizeof("usa.museum")];
- char stringpool_str3654[sizeof("iida.nagano.jp")];
- char stringpool_str3655[sizeof("presse.ml")];
- char stringpool_str3656[sizeof("xn--hcesuolo-7ya35b.no")];
- char stringpool_str3657[sizeof("rovno.ua")];
- char stringpool_str3658[sizeof("xn--uc0atv.tw")];
- char stringpool_str3659[sizeof("matsuno.ehime.jp")];
- char stringpool_str3660[sizeof("chirurgiens-dentistes.fr")];
- char stringpool_str3661[sizeof("pm")];
- char stringpool_str3662[sizeof("kustanai.ru")];
- char stringpool_str3663[sizeof("can.museum")];
- char stringpool_str3664[sizeof("prof.pr")];
- char stringpool_str3665[sizeof("k12.in.us")];
- char stringpool_str3666[sizeof("groks-this.info")];
- char stringpool_str3667[sizeof("casadelamoneda.museum")];
- char stringpool_str3668[sizeof("amami.kagoshima.jp")];
- char stringpool_str3669[sizeof("sport.hu")];
- char stringpool_str3670[sizeof("xn--wcvs22d.hk")];
- char stringpool_str3671[sizeof("skien.no")];
- char stringpool_str3672[sizeof("xn--trna-woa.no")];
- char stringpool_str3673[sizeof("dell-ogliastra.it")];
- char stringpool_str3674[sizeof("ny.us")];
- char stringpool_str3675[sizeof("hara.nagano.jp")];
- char stringpool_str3676[sizeof("xn--bearalvhki-y4a.no")];
- char stringpool_str3677[sizeof("presse.ci")];
- char stringpool_str3678[sizeof("ureshino.mie.jp")];
- char stringpool_str3679[sizeof("city.nagoya.jp")];
- char stringpool_str3680[sizeof("onjuku.chiba.jp")];
- char stringpool_str3681[sizeof("satsumasendai.kagoshima.jp")];
- char stringpool_str3682[sizeof("hioki.kagoshima.jp")];
- char stringpool_str3683[sizeof("shiga.jp")];
- char stringpool_str3684[sizeof("pa")];
- char stringpool_str3685[sizeof("nj.us")];
- char stringpool_str3686[sizeof("xn--drbak-wua.no")];
- char stringpool_str3687[sizeof("ritto.shiga.jp")];
- char stringpool_str3688[sizeof("gov.az")];
- char stringpool_str3689[sizeof("edu.az")];
- char stringpool_str3690[sizeof("kicks-ass.net")];
- char stringpool_str3691[sizeof("com.az")];
- char stringpool_str3692[sizeof("nm.us")];
- char stringpool_str3693[sizeof("parts")];
- char stringpool_str3694[sizeof("british.museum")];
- char stringpool_str3695[sizeof("xn--kprw13d")];
- char stringpool_str3696[sizeof("appspot.com")];
- char stringpool_str3697[sizeof("k12.mn.us")];
- char stringpool_str3698[sizeof("shiroishi.miyagi.jp")];
- char stringpool_str3699[sizeof("xn--wgbl6a")];
- char stringpool_str3700[sizeof("boutique")];
- char stringpool_str3701[sizeof("ne.kr")];
- char stringpool_str3702[sizeof("urbinopesaro.it")];
- char stringpool_str3703[sizeof("global.prod.fastly.net")];
- char stringpool_str3704[sizeof("heritage.museum")];
- char stringpool_str3705[sizeof("nakayama.yamagata.jp")];
- char stringpool_str3706[sizeof("ueda.nagano.jp")];
- char stringpool_str3707[sizeof("pink")];
- char stringpool_str3708[sizeof("dudinka.ru")];
- char stringpool_str3709[sizeof("wloclawek.pl")];
- char stringpool_str3710[sizeof("int.az")];
- char stringpool_str3711[sizeof("kamisunagawa.hokkaido.jp")];
- char stringpool_str3712[sizeof("arteducation.museum")];
- char stringpool_str3713[sizeof("pro.pr")];
- char stringpool_str3714[sizeof("nanbu.tottori.jp")];
- char stringpool_str3715[sizeof("presse.km")];
- char stringpool_str3716[sizeof("yamagata.jp")];
- char stringpool_str3717[sizeof("nishi.fukuoka.jp")];
- char stringpool_str3718[sizeof("plo.ps")];
- char stringpool_str3719[sizeof("higashiagatsuma.gunma.jp")];
- char stringpool_str3720[sizeof("pila.pl")];
- char stringpool_str3721[sizeof("hakui.ishikawa.jp")];
- char stringpool_str3722[sizeof("nanto.toyama.jp")];
- char stringpool_str3723[sizeof("nanae.hokkaido.jp")];
- char stringpool_str3724[sizeof("to")];
- char stringpool_str3725[sizeof("td")];
- char stringpool_str3726[sizeof("port.fr")];
- char stringpool_str3727[sizeof("tz")];
- char stringpool_str3728[sizeof("kiho.mie.jp")];
- char stringpool_str3729[sizeof("goto.nagasaki.jp")];
- char stringpool_str3730[sizeof("rishirifuji.hokkaido.jp")];
- char stringpool_str3731[sizeof("tr")];
- char stringpool_str3732[sizeof("nv.us")];
- char stringpool_str3733[sizeof("tl")];
- char stringpool_str3734[sizeof("rivne.ua")];
- char stringpool_str3735[sizeof("cosenza.it")];
- char stringpool_str3736[sizeof("nm.cn")];
- char stringpool_str3737[sizeof("yamal.ru")];
- char stringpool_str3738[sizeof("geology.museum")];
- char stringpool_str3739[sizeof("tools")];
- char stringpool_str3740[sizeof("mad.museum")];
- char stringpool_str3741[sizeof("matsumae.hokkaido.jp")];
- char stringpool_str3742[sizeof("ns.ca")];
- char stringpool_str3743[sizeof("t.bg")];
- char stringpool_str3744[sizeof("blogsite.org")];
- char stringpool_str3745[sizeof("tn")];
- char stringpool_str3746[sizeof("psc.br")];
- char stringpool_str3747[sizeof("t.se")];
- char stringpool_str3748[sizeof("oga.akita.jp")];
- char stringpool_str3749[sizeof("ph")];
- char stringpool_str3750[sizeof("pics")];
- char stringpool_str3751[sizeof("shichikashuku.miyagi.jp")];
- char stringpool_str3752[sizeof("aki.kochi.jp")];
- char stringpool_str3753[sizeof("nl.ca")];
- char stringpool_str3754[sizeof("sango.nara.jp")];
- char stringpool_str3755[sizeof("zoology.museum")];
- char stringpool_str3756[sizeof("pri.ee")];
- char stringpool_str3757[sizeof("nu.ca")];
- char stringpool_str3758[sizeof("psi.br")];
- char stringpool_str3759[sizeof("komono.mie.jp")];
- char stringpool_str3760[sizeof("per.nf")];
- char stringpool_str3761[sizeof("sejny.pl")];
- char stringpool_str3762[sizeof("tt")];
- char stringpool_str3763[sizeof("xn--srum-gra.no")];
- char stringpool_str3764[sizeof("akune.kagoshima.jp")];
- char stringpool_str3765[sizeof("photo")];
- char stringpool_str3766[sizeof("photos")];
- char stringpool_str3767[sizeof("seika.kyoto.jp")];
- char stringpool_str3768[sizeof("manno.kagawa.jp")];
- char stringpool_str3769[sizeof("otoineppu.hokkaido.jp")];
- char stringpool_str3770[sizeof("xn--80adxhks")];
- char stringpool_str3771[sizeof("interactive.museum")];
- char stringpool_str3772[sizeof("tromso.no")];
- char stringpool_str3773[sizeof("kumano.mie.jp")];
- char stringpool_str3774[sizeof("vs.it")];
- char stringpool_str3775[sizeof("partners")];
- char stringpool_str3776[sizeof("vr.it")];
- char stringpool_str3777[sizeof("org.gg")];
- char stringpool_str3778[sizeof("nt.ca")];
- char stringpool_str3779[sizeof("nh.us")];
- char stringpool_str3780[sizeof("go.ug")];
- char stringpool_str3781[sizeof("shimabara.nagasaki.jp")];
- char stringpool_str3782[sizeof("ppg.br")];
- char stringpool_str3783[sizeof("xn--vler-qoa.hedmark.no")];
- char stringpool_str3784[sizeof("co.ug")];
- char stringpool_str3785[sizeof("co.ag")];
- char stringpool_str3786[sizeof("tips")];
- char stringpool_str3787[sizeof("yaotsu.gifu.jp")];
- char stringpool_str3788[sizeof("hirono.iwate.jp")];
- char stringpool_str3789[sizeof("ibaraki.ibaraki.jp")];
- char stringpool_str3790[sizeof("chizu.tottori.jp")];
- char stringpool_str3791[sizeof("kirkenes.no")];
- char stringpool_str3792[sizeof("nango.fukushima.jp")];
- char stringpool_str3793[sizeof("nb.ca")];
- char stringpool_str3794[sizeof("tur.br")];
- char stringpool_str3795[sizeof("xn--rholt-mra.no")];
- char stringpool_str3796[sizeof("kisosaki.mie.jp")];
- char stringpool_str3797[sizeof("mil.tz")];
- char stringpool_str3798[sizeof("bokn.no")];
- char stringpool_str3799[sizeof("co.gg")];
- char stringpool_str3800[sizeof("california.museum")];
- char stringpool_str3801[sizeof("ve.it")];
- char stringpool_str3802[sizeof("pk")];
- char stringpool_str3803[sizeof("gov.kz")];
- char stringpool_str3804[sizeof("edu.kz")];
- char stringpool_str3805[sizeof("com.kz")];
- char stringpool_str3806[sizeof("sandoy.no")];
- char stringpool_str3807[sizeof("vt.it")];
- char stringpool_str3808[sizeof("xn--mgbayh7gpa")];
- char stringpool_str3809[sizeof("tromsa.no")];
- char stringpool_str3810[sizeof("nishikawa.yamagata.jp")];
- char stringpool_str3811[sizeof("tj")];
- char stringpool_str3812[sizeof("kommune.no")];
- char stringpool_str3813[sizeof("biz.az")];
- char stringpool_str3814[sizeof("tm")];
- char stringpool_str3815[sizeof("trd.br")];
- char stringpool_str3816[sizeof("coldwar.museum")];
- char stringpool_str3817[sizeof("vb.it")];
- char stringpool_str3818[sizeof("teo.br")];
- char stringpool_str3819[sizeof("automotive.museum")];
- char stringpool_str3820[sizeof("troandin.no")];
- char stringpool_str3821[sizeof("vi.it")];
- char stringpool_str3822[sizeof("herokuapp.com")];
- char stringpool_str3823[sizeof("tel")];
- char stringpool_str3824[sizeof("eiheiji.fukui.jp")];
- char stringpool_str3825[sizeof("yuzawa.niigata.jp")];
- char stringpool_str3826[sizeof("khmelnitskiy.ua")];
- char stringpool_str3827[sizeof("today")];
- char stringpool_str3828[sizeof("tienda")];
- char stringpool_str3829[sizeof("pistoia.it")];
- char stringpool_str3830[sizeof("catanzaro.it")];
- char stringpool_str3831[sizeof("elb.amazonaws.com")];
- char stringpool_str3832[sizeof("imizu.toyama.jp")];
- char stringpool_str3833[sizeof("tattoo")];
- char stringpool_str3834[sizeof("prd.km")];
- char stringpool_str3835[sizeof("ayase.kanagawa.jp")];
- char stringpool_str3836[sizeof("kakogawa.hyogo.jp")];
- char stringpool_str3837[sizeof("herokussl.com")];
- char stringpool_str3838[sizeof("sakura.tochigi.jp")];
- char stringpool_str3839[sizeof("palana.ru")];
- char stringpool_str3840[sizeof("notogawa.shiga.jp")];
- char stringpool_str3841[sizeof("xn--vhquv")];
- char stringpool_str3842[sizeof("gateway.museum")];
- char stringpool_str3843[sizeof("sveio.no")];
- char stringpool_str3844[sizeof("equipment.aero")];
- char stringpool_str3845[sizeof("iwakura.aichi.jp")];
- char stringpool_str3846[sizeof("voss.no")];
- char stringpool_str3847[sizeof("ookuwa.nagano.jp")];
- char stringpool_str3848[sizeof("tur.ar")];
- char stringpool_str3849[sizeof("nc")];
- char stringpool_str3850[sizeof("from-hi.com")];
- char stringpool_str3851[sizeof("tv")];
- char stringpool_str3852[sizeof("gov.cx")];
- char stringpool_str3853[sizeof("nom.co")];
- char stringpool_str3854[sizeof("act.edu.au")];
- char stringpool_str3855[sizeof("tmp.br")];
- char stringpool_str3856[sizeof("tysnes.no")];
- char stringpool_str3857[sizeof("mil.az")];
- char stringpool_str3858[sizeof("birdart.museum")];
- char stringpool_str3859[sizeof("shirahama.wakayama.jp")];
- char stringpool_str3860[sizeof("is-a-musician.com")];
- char stringpool_str3861[sizeof("rl.no")];
- char stringpool_str3862[sizeof("fineart.museum")];
- char stringpool_str3863[sizeof("yabuki.fukushima.jp")];
- char stringpool_str3864[sizeof("va.it")];
- char stringpool_str3865[sizeof("ryuoh.shiga.jp")];
- char stringpool_str3866[sizeof("pescara.it")];
- char stringpool_str3867[sizeof("dyndns.ws")];
- char stringpool_str3868[sizeof("tozsde.hu")];
- char stringpool_str3869[sizeof("net.co")];
- char stringpool_str3870[sizeof("ohkura.yamagata.jp")];
- char stringpool_str3871[sizeof("trader.aero")];
- char stringpool_str3872[sizeof("mine.nu")];
- char stringpool_str3873[sizeof("rebun.hokkaido.jp")];
- char stringpool_str3874[sizeof("war.museum")];
- char stringpool_str3875[sizeof("ro.com")];
- char stringpool_str3876[sizeof("palermo.it")];
- char stringpool_str3877[sizeof("portland.museum")];
- char stringpool_str3878[sizeof("karpacz.pl")];
- char stringpool_str3879[sizeof("vv.it")];
- char stringpool_str3880[sizeof("th")];
- char stringpool_str3881[sizeof("tottori.jp")];
- char stringpool_str3882[sizeof("vega.no")];
- char stringpool_str3883[sizeof("mulhouse.museum")];
- char stringpool_str3884[sizeof("washingtondc.museum")];
- char stringpool_str3885[sizeof("ru.com")];
- char stringpool_str3886[sizeof("chonan.chiba.jp")];
- char stringpool_str3887[sizeof("suita.osaka.jp")];
- char stringpool_str3888[sizeof("samukawa.kanagawa.jp")];
- char stringpool_str3889[sizeof("ath.cx")];
- char stringpool_str3890[sizeof("ri.us")];
- char stringpool_str3891[sizeof("safety.aero")];
- char stringpool_str3892[sizeof("trento.it")];
- char stringpool_str3893[sizeof("ooshika.nagano.jp")];
- char stringpool_str3894[sizeof("medio-campidano.it")];
- char stringpool_str3895[sizeof("plumbing")];
- char stringpool_str3896[sizeof("maritime.museum")];
- char stringpool_str3897[sizeof("hannan.osaka.jp")];
- char stringpool_str3898[sizeof("from-ut.com")];
- char stringpool_str3899[sizeof("kchr.ru")];
- char stringpool_str3900[sizeof("khmelnytskyi.ua")];
- char stringpool_str3901[sizeof("xn--mk0axi.hk")];
- char stringpool_str3902[sizeof("seiyo.ehime.jp")];
- char stringpool_str3903[sizeof("wada.nagano.jp")];
- char stringpool_str3904[sizeof("indian.museum")];
- char stringpool_str3905[sizeof("net.cw")];
- char stringpool_str3906[sizeof("miura.kanagawa.jp")];
- char stringpool_str3907[sizeof("andriatranibarletta.it")];
- char stringpool_str3908[sizeof("xn--klbu-woa.no")];
- char stringpool_str3909[sizeof("yamamoto.miyagi.jp")];
- char stringpool_str3910[sizeof("tk")];
- char stringpool_str3911[sizeof("shima.mie.jp")];
- char stringpool_str3912[sizeof("chihayaakasaka.osaka.jp")];
- char stringpool_str3913[sizeof("net.ci")];
- char stringpool_str3914[sizeof("re.kr")];
- char stringpool_str3915[sizeof("ogi.saga.jp")];
- char stringpool_str3916[sizeof("zushi.kanagawa.jp")];
- char stringpool_str3917[sizeof("workinggroup.aero")];
- char stringpool_str3918[sizeof("firenze.it")];
- char stringpool_str3919[sizeof("watchandclock.museum")];
- char stringpool_str3920[sizeof("vang.no")];
- char stringpool_str3921[sizeof("kameyama.mie.jp")];
- char stringpool_str3922[sizeof("valer.ostfold.no")];
- char stringpool_str3923[sizeof("xn--hpmir-xqa.no")];
- char stringpool_str3924[sizeof("satte.saitama.jp")];
- char stringpool_str3925[sizeof("teramo.it")];
- char stringpool_str3926[sizeof("pomorskie.pl")];
- char stringpool_str3927[sizeof("muenchen.museum")];
- char stringpool_str3928[sizeof("tysfjord.no")];
- char stringpool_str3929[sizeof("yugawa.fukushima.jp")];
- char stringpool_str3930[sizeof("vaga.no")];
- char stringpool_str3931[sizeof("mil.kz")];
- char stringpool_str3932[sizeof("public.museum")];
- char stringpool_str3933[sizeof("nf")];
- char stringpool_str3934[sizeof("plants.museum")];
- char stringpool_str3935[sizeof("homelinux.net")];
- char stringpool_str3936[sizeof("nom.fr")];
- char stringpool_str3937[sizeof("tra.kp")];
- char stringpool_str3938[sizeof("verran.no")];
- char stringpool_str3939[sizeof("tonsberg.no")];
- char stringpool_str3940[sizeof("xn--vrggt-xqad.no")];
- char stringpool_str3941[sizeof("pw")];
- char stringpool_str3942[sizeof("yamagata.nagano.jp")];
- char stringpool_str3943[sizeof("dating")];
- char stringpool_str3944[sizeof("tourism.tn")];
- char stringpool_str3945[sizeof("ichiba.tokushima.jp")];
- char stringpool_str3946[sizeof("homelinux.com")];
- char stringpool_str3947[sizeof("pasadena.museum")];
- char stringpool_str3948[sizeof("hatsukaichi.hiroshima.jp")];
- char stringpool_str3949[sizeof("divttasvuotna.no")];
- char stringpool_str3950[sizeof("ebina.kanagawa.jp")];
- char stringpool_str3951[sizeof("padova.it")];
- char stringpool_str3952[sizeof("dellogliastra.it")];
- char stringpool_str3953[sizeof("presidio.museum")];
- char stringpool_str3954[sizeof("kinko.kagoshima.jp")];
- char stringpool_str3955[sizeof("sanda.hyogo.jp")];
- char stringpool_str3956[sizeof("saiki.oita.jp")];
- char stringpool_str3957[sizeof("xn--leagaviika-52b.no")];
- char stringpool_str3958[sizeof("zakopane.pl")];
- char stringpool_str3959[sizeof("tingvoll.no")];
- char stringpool_str3960[sizeof("kiso.nagano.jp")];
- char stringpool_str3961[sizeof("portal.museum")];
- char stringpool_str3962[sizeof("nanao.ishikawa.jp")];
- char stringpool_str3963[sizeof("topology.museum")];
- char stringpool_str3964[sizeof("per.sg")];
- char stringpool_str3965[sizeof("veterinaire.fr")];
- char stringpool_str3966[sizeof("publ.pt")];
- char stringpool_str3967[sizeof("isa-geek.com")];
- char stringpool_str3968[sizeof("xn--j6w193g")];
- char stringpool_str3969[sizeof("xn--brum-voa.no")];
- char stringpool_str3970[sizeof("sakahogi.gifu.jp")];
- char stringpool_str3971[sizeof("environmentalconservation.museum")];
- char stringpool_str3972[sizeof("akiruno.tokyo.jp")];
- char stringpool_str3973[sizeof("chesapeakebay.museum")];
- char stringpool_str3974[sizeof("surrey.museum")];
- char stringpool_str3975[sizeof("isa-geek.net")];
- char stringpool_str3976[sizeof("no.it")];
- char stringpool_str3977[sizeof("higashikagawa.kagawa.jp")];
- char stringpool_str3978[sizeof("indianapolis.museum")];
- char stringpool_str3979[sizeof("ora.gunma.jp")];
- char stringpool_str3980[sizeof("otsu.shiga.jp")];
- char stringpool_str3981[sizeof("pilots.museum")];
- char stringpool_str3982[sizeof("ptz.ru")];
- char stringpool_str3983[sizeof("polkowice.pl")];
- char stringpool_str3984[sizeof("nu.it")];
- char stringpool_str3985[sizeof("yahiko.niigata.jp")];
- char stringpool_str3986[sizeof("dinosaur.museum")];
- char stringpool_str3987[sizeof("bo.nordland.no")];
- char stringpool_str3988[sizeof("starostwo.gov.pl")];
- char stringpool_str3989[sizeof("okuizumo.shimane.jp")];
- char stringpool_str3990[sizeof("indianmarket.museum")];
- char stringpool_str3991[sizeof("portlligat.museum")];
- char stringpool_str3992[sizeof("rs.ba")];
- char stringpool_str3993[sizeof("aguni.okinawa.jp")];
- char stringpool_str3994[sizeof("org.gh")];
- char stringpool_str3995[sizeof("express.aero")];
- char stringpool_str3996[sizeof("shiso.hyogo.jp")];
- char stringpool_str3997[sizeof("rv.ua")];
- char stringpool_str3998[sizeof("seiro.niigata.jp")];
- char stringpool_str3999[sizeof("preservation.museum")];
- char stringpool_str4000[sizeof("ota.gunma.jp")];
- char stringpool_str4001[sizeof("ichihara.chiba.jp")];
- char stringpool_str4002[sizeof("minamiashigara.kanagawa.jp")];
- char stringpool_str4003[sizeof("umi.fukuoka.jp")];
- char stringpool_str4004[sizeof("xn--ses554g")];
- char stringpool_str4005[sizeof("itayanagi.aomori.jp")];
- char stringpool_str4006[sizeof("nt.edu.au")];
- char stringpool_str4007[sizeof("xn--6frz82g")];
- char stringpool_str4008[sizeof("sabae.fukui.jp")];
- char stringpool_str4009[sizeof("soeda.fukuoka.jp")];
- char stringpool_str4010[sizeof("ternopil.ua")];
- char stringpool_str4011[sizeof("hamatonbetsu.hokkaido.jp")];
- char stringpool_str4012[sizeof("prd.mg")];
- char stringpool_str4013[sizeof("xn--3ds443g")];
- char stringpool_str4014[sizeof("productions")];
- char stringpool_str4015[sizeof("tom.ru")];
- char stringpool_str4016[sizeof("tarnobrzeg.pl")];
- char stringpool_str4017[sizeof("trentino.it")];
- char stringpool_str4018[sizeof("ichikawa.chiba.jp")];
- char stringpool_str4019[sizeof("sd.us")];
- char stringpool_str4020[sizeof("onagawa.miyagi.jp")];
- char stringpool_str4021[sizeof("bergbau.museum")];
- char stringpool_str4022[sizeof("kitakyushu.jp")];
- char stringpool_str4023[sizeof("sagamihara.kanagawa.jp")];
- char stringpool_str4024[sizeof("chicago.museum")];
- char stringpool_str4025[sizeof("pisa.it")];
- char stringpool_str4026[sizeof("ham-radio-op.net")];
- char stringpool_str4027[sizeof("sydney.museum")];
- char stringpool_str4028[sizeof("asaminami.hiroshima.jp")];
- char stringpool_str4029[sizeof("panama.museum")];
- char stringpool_str4030[sizeof("kumakogen.ehime.jp")];
- char stringpool_str4031[sizeof("ichinomiya.chiba.jp")];
- char stringpool_str4032[sizeof("tw")];
- char stringpool_str4033[sizeof("yamagata.ibaraki.jp")];
- char stringpool_str4034[sizeof("koebenhavn.museum")];
- char stringpool_str4035[sizeof("vibovalentia.it")];
- char stringpool_str4036[sizeof("virtuel.museum")];
- char stringpool_str4037[sizeof("selje.no")];
- char stringpool_str4038[sizeof("hachinohe.aomori.jp")];
- char stringpool_str4039[sizeof("skjak.no")];
- char stringpool_str4040[sizeof("na.it")];
- char stringpool_str4041[sizeof("paderborn.museum")];
- char stringpool_str4042[sizeof("venice.it")];
- char stringpool_str4043[sizeof("tourism.pl")];
- char stringpool_str4044[sizeof("oguchi.aichi.jp")];
- char stringpool_str4045[sizeof("virtual.museum")];
- char stringpool_str4046[sizeof("xn--krehamn-dxa.no")];
- char stringpool_str4047[sizeof("net.tn")];
- char stringpool_str4048[sizeof("st.no")];
- char stringpool_str4049[sizeof("xn--lrdal-sra.no")];
- char stringpool_str4050[sizeof("happou.akita.jp")];
- char stringpool_str4051[sizeof("xn--ygbi2ammx")];
- char stringpool_str4052[sizeof("sd.cn")];
- char stringpool_str4053[sizeof("torsken.no")];
- char stringpool_str4054[sizeof("kyonan.chiba.jp")];
- char stringpool_str4055[sizeof("tosu.saga.jp")];
- char stringpool_str4056[sizeof("xn--bod-2na.no")];
- char stringpool_str4057[sizeof("pyatigorsk.ru")];
- char stringpool_str4058[sizeof("se.com")];
- char stringpool_str4059[sizeof("net.in")];
- char stringpool_str4060[sizeof("coop.ht")];
- char stringpool_str4061[sizeof("yabu.hyogo.jp")];
- char stringpool_str4062[sizeof("sn.cn")];
- char stringpool_str4063[sizeof("production.aero")];
- char stringpool_str4064[sizeof("daejeon.kr")];
- char stringpool_str4065[sizeof("seki.gifu.jp")];
- char stringpool_str4066[sizeof("xn--rady-ira.no")];
- char stringpool_str4067[sizeof("virginia.museum")];
- char stringpool_str4068[sizeof("namie.fukushima.jp")];
- char stringpool_str4069[sizeof("medizinhistorisches.museum")];
- char stringpool_str4070[sizeof("tsu.mie.jp")];
- char stringpool_str4071[sizeof("saito.miyazaki.jp")];
- char stringpool_str4072[sizeof("transport.museum")];
- char stringpool_str4073[sizeof("name.na")];
- char stringpool_str4074[sizeof("vegarshei.no")];
- char stringpool_str4075[sizeof("ag.it")];
- char stringpool_str4076[sizeof("xn--xhq521b")];
- char stringpool_str4077[sizeof("bg.it")];
- char stringpool_str4078[sizeof("tsukumi.oita.jp")];
- char stringpool_str4079[sizeof("fg.it")];
- char stringpool_str4080[sizeof("kg.kr")];
- char stringpool_str4081[sizeof("pvt.k12.ma.us")];
- char stringpool_str4082[sizeof("net.an")];
- char stringpool_str4083[sizeof("sa.au")];
- char stringpool_str4084[sizeof("nat.tn")];
- char stringpool_str4085[sizeof("rec.co")];
- char stringpool_str4086[sizeof("tottori.tottori.jp")];
- char stringpool_str4087[sizeof("shiki.saitama.jp")];
- char stringpool_str4088[sizeof("kyotango.kyoto.jp")];
- char stringpool_str4089[sizeof("sor-odal.no")];
- char stringpool_str4090[sizeof("is-a-guru.com")];
- char stringpool_str4091[sizeof("verbania.it")];
- char stringpool_str4092[sizeof("net.pn")];
- char stringpool_str4093[sizeof("xn--s-1fa.no")];
- char stringpool_str4094[sizeof("k12.az.us")];
- char stringpool_str4095[sizeof("xn--l-1fa.no")];
- char stringpool_str4096[sizeof("toda.saitama.jp")];
- char stringpool_str4097[sizeof("net.vn")];
- char stringpool_str4098[sizeof("sa.com")];
- char stringpool_str4099[sizeof("kashima.ibaraki.jp")];
- char stringpool_str4100[sizeof("net.cu")];
- char stringpool_str4101[sizeof("sa.cr")];
- char stringpool_str4102[sizeof("xn--krjohka-hwab49j.no")];
- char stringpool_str4103[sizeof("xn--vestvgy-ixa6o.no")];
- char stringpool_str4104[sizeof("sb.ua")];
- char stringpool_str4105[sizeof("tara.saga.jp")];
- char stringpool_str4106[sizeof("podhale.pl")];
- char stringpool_str4107[sizeof("opole.pl")];
- char stringpool_str4108[sizeof("gov.gn")];
- char stringpool_str4109[sizeof("edu.gn")];
- char stringpool_str4110[sizeof("xn--nry-yla5g.no")];
- char stringpool_str4111[sizeof("com.gn")];
- char stringpool_str4112[sizeof("bedzin.pl")];
- char stringpool_str4113[sizeof("torino.it")];
- char stringpool_str4114[sizeof("kamoenai.hokkaido.jp")];
- char stringpool_str4115[sizeof("xn--sr-fron-q1a.no")];
- char stringpool_str4116[sizeof("shibukawa.gunma.jp")];
- char stringpool_str4117[sizeof("kunohe.iwate.jp")];
- char stringpool_str4118[sizeof("ro.it")];
- char stringpool_str4119[sizeof("isa-geek.org")];
- char stringpool_str4120[sizeof("name.ng")];
- char stringpool_str4121[sizeof("vision")];
- char stringpool_str4122[sizeof("net.hn")];
- char stringpool_str4123[sizeof("orsta.no")];
- char stringpool_str4124[sizeof("morimachi.shizuoka.jp")];
- char stringpool_str4125[sizeof("ogasawara.tokyo.jp")];
- char stringpool_str4126[sizeof("sm.ua")];
- char stringpool_str4127[sizeof("higashiura.aichi.jp")];
- char stringpool_str4128[sizeof("sakyo.kyoto.jp")];
- char stringpool_str4129[sizeof("uchihara.ibaraki.jp")];
- char stringpool_str4130[sizeof("rn.it")];
- char stringpool_str4131[sizeof("yamanouchi.nagano.jp")];
- char stringpool_str4132[sizeof("asakawa.fukushima.jp")];
- char stringpool_str4133[sizeof("tome.miyagi.jp")];
- char stringpool_str4134[sizeof("re.it")];
- char stringpool_str4135[sizeof("nic.in")];
- char stringpool_str4136[sizeof("isahaya.nagasaki.jp")];
- char stringpool_str4137[sizeof("tas.au")];
- char stringpool_str4138[sizeof("perm.ru")];
- char stringpool_str4139[sizeof("us-east-1.amazonaws.com")];
- char stringpool_str4140[sizeof("xn--gls-elac.no")];
- char stringpool_str4141[sizeof("oishida.yamagata.jp")];
- char stringpool_str4142[sizeof("sh.cn")];
- char stringpool_str4143[sizeof("kartuzy.pl")];
- char stringpool_str4144[sizeof("palmsprings.museum")];
- char stringpool_str4145[sizeof("ri.it")];
- char stringpool_str4146[sizeof("osaka.jp")];
- char stringpool_str4147[sizeof("teledata.mz")];
- char stringpool_str4148[sizeof("chikujo.fukuoka.jp")];
- char stringpool_str4149[sizeof("tosa.kochi.jp")];
- char stringpool_str4150[sizeof("net.kn")];
- char stringpool_str4151[sizeof("tonami.toyama.jp")];
- char stringpool_str4152[sizeof("plantation.museum")];
- char stringpool_str4153[sizeof("togane.chiba.jp")];
- char stringpool_str4154[sizeof("mishima.fukushima.jp")];
- char stringpool_str4155[sizeof("shari.hokkaido.jp")];
- char stringpool_str4156[sizeof("from-sd.com")];
- char stringpool_str4157[sizeof("pruszkow.pl")];
- char stringpool_str4158[sizeof("tenkawa.nara.jp")];
- char stringpool_str4159[sizeof("from-md.com")];
- char stringpool_str4160[sizeof("sc")];
- char stringpool_str4161[sizeof("aizubange.fukushima.jp")];
- char stringpool_str4162[sizeof("nyc.mn")];
- char stringpool_str4163[sizeof("nx.cn")];
- char stringpool_str4164[sizeof("rost.no")];
- char stringpool_str4165[sizeof("shizukuishi.iwate.jp")];
- char stringpool_str4166[sizeof("dnsdojo.org")];
- char stringpool_str4167[sizeof("rm.it")];
- char stringpool_str4168[sizeof("xn--rskog-uua.no")];
- char stringpool_str4169[sizeof("tadaoka.osaka.jp")];
- char stringpool_str4170[sizeof("act.gov.au")];
- char stringpool_str4171[sizeof("xn--vry-yla5g.no")];
- char stringpool_str4172[sizeof("yugawara.kanagawa.jp")];
- char stringpool_str4173[sizeof("xn--bjddar-pta.no")];
- char stringpool_str4174[sizeof("xn--lhppi-xqa.no")];
- char stringpool_str4175[sizeof("roan.no")];
- char stringpool_str4176[sizeof("tomari.hokkaido.jp")];
- char stringpool_str4177[sizeof("togura.nagano.jp")];
- char stringpool_str4178[sizeof("sch.lk")];
- char stringpool_str4179[sizeof("togo.aichi.jp")];
- char stringpool_str4180[sizeof("ra.it")];
- char stringpool_str4181[sizeof("rotorcraft.aero")];
- char stringpool_str4182[sizeof("television.museum")];
- char stringpool_str4183[sizeof("sch.uk")];
- char stringpool_str4184[sizeof("rnu.tn")];
- char stringpool_str4185[sizeof("norfolk.museum")];
- char stringpool_str4186[sizeof("bolzano.it")];
- char stringpool_str4187[sizeof("net.gy")];
- char stringpool_str4188[sizeof("itoman.okinawa.jp")];
- char stringpool_str4189[sizeof("xn--bdddj-mrabd.no")];
- char stringpool_str4190[sizeof("is-a-soxfan.org")];
- char stringpool_str4191[sizeof("imakane.hokkaido.jp")];
- char stringpool_str4192[sizeof("xn--h-2fa.no")];
- char stringpool_str4193[sizeof("rns.tn")];
- char stringpool_str4194[sizeof("sch.sa")];
- char stringpool_str4195[sizeof("showa.gunma.jp")];
- char stringpool_str4196[sizeof("namdalseid.no")];
- char stringpool_str4197[sizeof("from-id.com")];
- char stringpool_str4198[sizeof("date.hokkaido.jp")];
- char stringpool_str4199[sizeof("tone.ibaraki.jp")];
- char stringpool_str4200[sizeof("sch.ir")];
- char stringpool_str4201[sizeof("modelling.aero")];
- char stringpool_str4202[sizeof("trani-andria-barletta.it")];
- char stringpool_str4203[sizeof("res.in")];
- char stringpool_str4204[sizeof("yuasa.wakayama.jp")];
- char stringpool_str4205[sizeof("sch.qa")];
- char stringpool_str4206[sizeof("tahara.aichi.jp")];
- char stringpool_str4207[sizeof("hokuryu.hokkaido.jp")];
- char stringpool_str4208[sizeof("knowsitall.info")];
- char stringpool_str4209[sizeof("tananger.no")];
- char stringpool_str4210[sizeof("kasukabe.saitama.jp")];
- char stringpool_str4211[sizeof("is-a-hard-worker.com")];
- char stringpool_str4212[sizeof("training")];
- char stringpool_str4213[sizeof("sch.jo")];
- char stringpool_str4214[sizeof("homedns.org")];
- char stringpool_str4215[sizeof("tanabe.kyoto.jp")];
- char stringpool_str4216[sizeof("tuva.ru")];
- char stringpool_str4217[sizeof("ne.jp")];
- char stringpool_str4218[sizeof("coastaldefence.museum")];
- char stringpool_str4219[sizeof("sch.id")];
- char stringpool_str4220[sizeof("photography")];
- char stringpool_str4221[sizeof("rana.no")];
- char stringpool_str4222[sizeof("bologna.it")];
- char stringpool_str4223[sizeof("sk.ca")];
- char stringpool_str4224[sizeof("kainan.tokushima.jp")];
- char stringpool_str4225[sizeof("xn--bjarky-fya.no")];
- char stringpool_str4226[sizeof("yoita.niigata.jp")];
- char stringpool_str4227[sizeof("torino.museum")];
- char stringpool_str4228[sizeof("sekikawa.niigata.jp")];
- char stringpool_str4229[sizeof("tvedestrand.no")];
- char stringpool_str4230[sizeof("nara.nara.jp")];
- char stringpool_str4231[sizeof("xn--9dbhblg6di.museum")];
- char stringpool_str4232[sizeof("test.ru")];
- char stringpool_str4233[sizeof("hikari.yamaguchi.jp")];
- char stringpool_str4234[sizeof("management")];
- char stringpool_str4235[sizeof("xn--vads-jra.no")];
- char stringpool_str4236[sizeof("volkenkunde.museum")];
- char stringpool_str4237[sizeof("dyndns.info")];
- char stringpool_str4238[sizeof("tatarstan.ru")];
- char stringpool_str4239[sizeof("ohtawara.tochigi.jp")];
- char stringpool_str4240[sizeof("tomobe.ibaraki.jp")];
- char stringpool_str4241[sizeof("tokyo")];
- char stringpool_str4242[sizeof("alesund.no")];
- char stringpool_str4243[sizeof("embroidery.museum")];
- char stringpool_str4244[sizeof("xn--gmq050i.hk")];
- char stringpool_str4245[sizeof("blogdns.org")];
- char stringpool_str4246[sizeof("sch.ae")];
- char stringpool_str4247[sizeof("rade.no")];
- char stringpool_str4248[sizeof("chungnam.kr")];
- char stringpool_str4249[sizeof("is-a-blogger.com")];
- char stringpool_str4250[sizeof("store.nf")];
- char stringpool_str4251[sizeof("noda.iwate.jp")];
- char stringpool_str4252[sizeof("odate.akita.jp")];
- char stringpool_str4253[sizeof("showa.yamanashi.jp")];
- char stringpool_str4254[sizeof("yokawa.hyogo.jp")];
- char stringpool_str4255[sizeof("school.na")];
- char stringpool_str4256[sizeof("yakage.okayama.jp")];
- char stringpool_str4257[sizeof("research.aero")];
- char stringpool_str4258[sizeof("matsuzaki.shizuoka.jp")];
- char stringpool_str4259[sizeof("fetsund.no")];
- char stringpool_str4260[sizeof("yalta.ua")];
- char stringpool_str4261[sizeof("yn.cn")];
- char stringpool_str4262[sizeof("tula.ru")];
- char stringpool_str4263[sizeof("sakai.osaka.jp")];
- char stringpool_str4264[sizeof("emergency.aero")];
- char stringpool_str4265[sizeof("walbrzych.pl")];
- char stringpool_str4266[sizeof("samegawa.fukushima.jp")];
- char stringpool_str4267[sizeof("higashitsuno.kochi.jp")];
- char stringpool_str4268[sizeof("toshima.tokyo.jp")];
- char stringpool_str4269[sizeof("brumunddal.no")];
- char stringpool_str4270[sizeof("ogliastra.it")];
- char stringpool_str4271[sizeof("xn--rsta-fra.no")];
- char stringpool_str4272[sizeof("yonaguni.okinawa.jp")];
- char stringpool_str4273[sizeof("ringsaker.no")];
- char stringpool_str4274[sizeof("xn--givuotna-8ya.no")];
- char stringpool_str4275[sizeof("tsuyama.okayama.jp")];
- char stringpool_str4276[sizeof("so.it")];
- char stringpool_str4277[sizeof("tagajo.miyagi.jp")];
- char stringpool_str4278[sizeof("ss.it")];
- char stringpool_str4279[sizeof("sr.it")];
- char stringpool_str4280[sizeof("przeworsk.pl")];
- char stringpool_str4281[sizeof("hekinan.aichi.jp")];
- char stringpool_str4282[sizeof("forsand.no")];
- char stringpool_str4283[sizeof("hornindal.no")];
- char stringpool_str4284[sizeof("hitachiota.ibaraki.jp")];
- char stringpool_str4285[sizeof("traeumtgerade.de")];
- char stringpool_str4286[sizeof("oyodo.nara.jp")];
- char stringpool_str4287[sizeof("shingu.wakayama.jp")];
- char stringpool_str4288[sizeof("org.sy")];
- char stringpool_str4289[sizeof("xn--mxtq1m.hk")];
- char stringpool_str4290[sizeof("nabari.mie.jp")];
- char stringpool_str4291[sizeof("tver.ru")];
- char stringpool_str4292[sizeof("kunitachi.tokyo.jp")];
- char stringpool_str4293[sizeof("aurland.no")];
- char stringpool_str4294[sizeof("org.ly")];
- char stringpool_str4295[sizeof("sor-varanger.no")];
- char stringpool_str4296[sizeof("farsund.no")];
- char stringpool_str4297[sizeof("ravenna.it")];
- char stringpool_str4298[sizeof("org.uy")];
- char stringpool_str4299[sizeof("inazawa.aichi.jp")];
- char stringpool_str4300[sizeof("tochigi.jp")];
- char stringpool_str4301[sizeof("froland.no")];
- char stringpool_str4302[sizeof("trani-barletta-andria.it")];
- char stringpool_str4303[sizeof("dgca.aero")];
- char stringpool_str4304[sizeof("kalisz.pl")];
- char stringpool_str4305[sizeof("nanyo.yamagata.jp")];
- char stringpool_str4306[sizeof("si.it")];
- char stringpool_str4307[sizeof("shiogama.miyagi.jp")];
- char stringpool_str4308[sizeof("midsund.no")];
- char stringpool_str4309[sizeof("dali.museum")];
- char stringpool_str4310[sizeof("tateyama.toyama.jp")];
- char stringpool_str4311[sizeof("yorii.saitama.jp")];
- char stringpool_str4312[sizeof("nedre-eiker.no")];
- char stringpool_str4313[sizeof("hitachinaka.ibaraki.jp")];
- char stringpool_str4314[sizeof("uchinomi.kagawa.jp")];
- char stringpool_str4315[sizeof("kamimine.saga.jp")];
- char stringpool_str4316[sizeof("aizuwakamatsu.fukushima.jp")];
- char stringpool_str4317[sizeof("kuji.iwate.jp")];
- char stringpool_str4318[sizeof("research.museum")];
- char stringpool_str4319[sizeof("georgia.museum")];
- char stringpool_str4320[sizeof("nakagawa.fukuoka.jp")];
- char stringpool_str4321[sizeof("selfip.biz")];
- char stringpool_str4322[sizeof("olawa.pl")];
- char stringpool_str4323[sizeof("nagai.yamagata.jp")];
- char stringpool_str4324[sizeof("naamesjevuemie.no")];
- char stringpool_str4325[sizeof("gs.tr.no")];
- char stringpool_str4326[sizeof("gs.ol.no")];
- char stringpool_str4327[sizeof("gs.mr.no")];
- char stringpool_str4328[sizeof("taxi.br")];
- char stringpool_str4329[sizeof("niiza.saitama.jp")];
- char stringpool_str4330[sizeof("sund.no")];
- char stringpool_str4331[sizeof("gs.nl.no")];
- char stringpool_str4332[sizeof("gs.rl.no")];
- char stringpool_str4333[sizeof("nanjo.okinawa.jp")];
- char stringpool_str4334[sizeof("tsushima.aichi.jp")];
- char stringpool_str4335[sizeof("jo")];
- char stringpool_str4336[sizeof("jobs")];
- char stringpool_str4337[sizeof("org.my")];
- char stringpool_str4338[sizeof("saijo.ehime.jp")];
- char stringpool_str4339[sizeof("sa.it")];
- char stringpool_str4340[sizeof("gs.st.no")];
- char stringpool_str4341[sizeof("gov.st")];
- char stringpool_str4342[sizeof("net.dz")];
- char stringpool_str4343[sizeof("edu.st")];
- char stringpool_str4344[sizeof("gouv.rw")];
- char stringpool_str4345[sizeof("com.st")];
- char stringpool_str4346[sizeof("gouv.ml")];
- char stringpool_str4347[sizeof("vicenza.it")];
- char stringpool_str4348[sizeof("gov.lt")];
- char stringpool_str4349[sizeof("xn--mgba3a4fra")];
- char stringpool_str4350[sizeof("sp.it")];
- char stringpool_str4351[sizeof("gs.nt.no")];
- char stringpool_str4352[sizeof("yamakita.kanagawa.jp")];
- char stringpool_str4353[sizeof("net.uz")];
- char stringpool_str4354[sizeof("gouv.ci")];
- char stringpool_str4355[sizeof("j.bg")];
- char stringpool_str4356[sizeof("bygland.no")];
- char stringpool_str4357[sizeof("scotland.museum")];
- char stringpool_str4358[sizeof("stat.no")];
- char stringpool_str4359[sizeof("ichikawamisato.yamanashi.jp")];
- char stringpool_str4360[sizeof("xn--mgba3a4f16a")];
- char stringpool_str4361[sizeof("sa.edu.au")];
- char stringpool_str4362[sizeof("sv.it")];
- char stringpool_str4363[sizeof("je")];
- char stringpool_str4364[sizeof("slupsk.pl")];
- char stringpool_str4365[sizeof("date.fukushima.jp")];
- char stringpool_str4366[sizeof("repbody.aero")];
- char stringpool_str4367[sizeof("tohnosho.chiba.jp")];
- char stringpool_str4368[sizeof("kamijima.ehime.jp")];
- char stringpool_str4369[sizeof("sola.no")];
- char stringpool_str4370[sizeof("gov.tt")];
- char stringpool_str4371[sizeof("info.tz")];
- char stringpool_str4372[sizeof("edu.tt")];
- char stringpool_str4373[sizeof("org.py")];
- char stringpool_str4374[sizeof("aoki.nagano.jp")];
- char stringpool_str4375[sizeof("com.tt")];
- char stringpool_str4376[sizeof("iveland.no")];
- char stringpool_str4377[sizeof("xn--55qw42g")];
- char stringpool_str4378[sizeof("gouv.sn")];
- char stringpool_str4379[sizeof("pesarourbino.it")];
- char stringpool_str4380[sizeof("hobby-site.org")];
- char stringpool_str4381[sizeof("tateyama.chiba.jp")];
- char stringpool_str4382[sizeof("gov.bt")];
- char stringpool_str4383[sizeof("jor.br")];
- char stringpool_str4384[sizeof("telekommunikation.museum")];
- char stringpool_str4385[sizeof("edu.bt")];
- char stringpool_str4386[sizeof("sula.no")];
- char stringpool_str4387[sizeof("com.bt")];
- char stringpool_str4388[sizeof("shibuya.tokyo.jp")];
- char stringpool_str4389[sizeof("jet.uk")];
- char stringpool_str4390[sizeof("net.bz")];
- char stringpool_str4391[sizeof("tjeldsund.no")];
- char stringpool_str4392[sizeof("school.museum")];
- char stringpool_str4393[sizeof("gov.it")];
- char stringpool_str4394[sizeof("idrett.no")];
- char stringpool_str4395[sizeof("edu.it")];
- char stringpool_str4396[sizeof("gs.tm.no")];
- char stringpool_str4397[sizeof("int.tt")];
- char stringpool_str4398[sizeof("higashichichibu.saitama.jp")];
- char stringpool_str4399[sizeof("pisz.pl")];
- char stringpool_str4400[sizeof("intl.tn")];
- char stringpool_str4401[sizeof("nisshin.aichi.jp")];
- char stringpool_str4402[sizeof("info.tn")];
- char stringpool_str4403[sizeof("ulan-ude.ru")];
- char stringpool_str4404[sizeof("shimosuwa.nagano.jp")];
- char stringpool_str4405[sizeof("tree.museum")];
- char stringpool_str4406[sizeof("azumino.nagano.jp")];
- char stringpool_str4407[sizeof("jus.br")];
- char stringpool_str4408[sizeof("gs.aa.no")];
- char stringpool_str4409[sizeof("sanjo.niigata.jp")];
- char stringpool_str4410[sizeof("xn--mgba3a4fra.ir")];
- char stringpool_str4411[sizeof("jm")];
- char stringpool_str4412[sizeof("photography.museum")];
- char stringpool_str4413[sizeof("edu.mt")];
- char stringpool_str4414[sizeof("com.mt")];
- char stringpool_str4415[sizeof("xn--mgba3a4f16a.ir")];
- char stringpool_str4416[sizeof("gs.fm.no")];
- char stringpool_str4417[sizeof("storfjord.no")];
- char stringpool_str4418[sizeof("chikugo.fukuoka.jp")];
- char stringpool_str4419[sizeof("hitachiomiya.ibaraki.jp")];
- char stringpool_str4420[sizeof("edunet.tn")];
- char stringpool_str4421[sizeof("tamano.okayama.jp")];
- char stringpool_str4422[sizeof("reggiocalabria.it")];
- char stringpool_str4423[sizeof("gouv.bj")];
- char stringpool_str4424[sizeof("songdalen.no")];
- char stringpool_str4425[sizeof("gs.bu.no")];
- char stringpool_str4426[sizeof("gs.hl.no")];
- char stringpool_str4427[sizeof("misugi.mie.jp")];
- char stringpool_str4428[sizeof("philadelphia.museum")];
- char stringpool_str4429[sizeof("net.az")];
- char stringpool_str4430[sizeof("higashi.fukuoka.jp")];
- char stringpool_str4431[sizeof("abu.yamaguchi.jp")];
- char stringpool_str4432[sizeof("yanagawa.fukuoka.jp")];
- char stringpool_str4433[sizeof("time.museum")];
- char stringpool_str4434[sizeof("toei.aichi.jp")];
- char stringpool_str4435[sizeof("jp")];
- char stringpool_str4436[sizeof("nichinan.tottori.jp")];
- char stringpool_str4437[sizeof("reggio-calabria.it")];
- char stringpool_str4438[sizeof("game.tw")];
- char stringpool_str4439[sizeof("gov.pt")];
- char stringpool_str4440[sizeof("edu.pt")];
- char stringpool_str4441[sizeof("com.pt")];
- char stringpool_str4442[sizeof("utazu.kagawa.jp")];
- char stringpool_str4443[sizeof("gouv.km")];
- char stringpool_str4444[sizeof("nyc.museum")];
- char stringpool_str4445[sizeof("sells-for-u.com")];
- char stringpool_str4446[sizeof("club.tw")];
- char stringpool_str4447[sizeof("omihachiman.shiga.jp")];
- char stringpool_str4448[sizeof("org.ky")];
- char stringpool_str4449[sizeof("gjerstad.no")];
- char stringpool_str4450[sizeof("yoka.hyogo.jp")];
- char stringpool_str4451[sizeof("jur.pro")];
- char stringpool_str4452[sizeof("yuza.yamagata.jp")];
- char stringpool_str4453[sizeof("int.pt")];
- char stringpool_str4454[sizeof("reggio-emilia.it")];
- char stringpool_str4455[sizeof("gs.ah.no")];
- char stringpool_str4456[sizeof("yk.ca")];
- char stringpool_str4457[sizeof("mosvik.no")];
- char stringpool_str4458[sizeof("jondal.no")];
- char stringpool_str4459[sizeof("gouv.fr")];
- char stringpool_str4460[sizeof("pg")];
- char stringpool_str4461[sizeof("sch.ng")];
- char stringpool_str4462[sizeof("skedsmokorset.no")];
- char stringpool_str4463[sizeof("flakstad.no")];
- char stringpool_str4464[sizeof("biz.tt")];
- char stringpool_str4465[sizeof("shop.pl")];
- char stringpool_str4466[sizeof("yaese.okinawa.jp")];
- char stringpool_str4467[sizeof("baghdad.museum")];
- char stringpool_str4468[sizeof("gjovik.no")];
- char stringpool_str4469[sizeof("udmurtia.ru")];
- char stringpool_str4470[sizeof("hakodate.hokkaido.jp")];
- char stringpool_str4471[sizeof("tsk.ru")];
- char stringpool_str4472[sizeof("edu.ht")];
- char stringpool_str4473[sizeof("com.ht")];
- char stringpool_str4474[sizeof("taxi.aero")];
- char stringpool_str4475[sizeof("taishi.osaka.jp")];
- char stringpool_str4476[sizeof("grimstad.no")];
- char stringpool_str4477[sizeof("sakai.fukui.jp")];
- char stringpool_str4478[sizeof("realestate.pl")];
- char stringpool_str4479[sizeof("gs.hm.no")];
- char stringpool_str4480[sizeof("gamvik.no")];
- char stringpool_str4481[sizeof("showa.fukushima.jp")];
- char stringpool_str4482[sizeof("mil.st")];
- char stringpool_str4483[sizeof("art.ht")];
- char stringpool_str4484[sizeof("usenet.pl")];
- char stringpool_str4485[sizeof("sasaguri.fukuoka.jp")];
- char stringpool_str4486[sizeof("sor-aurdal.no")];
- char stringpool_str4487[sizeof("ube.yamaguchi.jp")];
- char stringpool_str4488[sizeof("hasvik.no")];
- char stringpool_str4489[sizeof("tomiya.miyagi.jp")];
- char stringpool_str4490[sizeof("toride.ibaraki.jp")];
- char stringpool_str4491[sizeof("presse.fr")];
- char stringpool_str4492[sizeof("ne.ug")];
- char stringpool_str4493[sizeof("village.museum")];
- char stringpool_str4494[sizeof("muosat.no")];
- char stringpool_str4495[sizeof("higashi.fukushima.jp")];
- char stringpool_str4496[sizeof("settlement.museum")];
- char stringpool_str4497[sizeof("industries")];
- char stringpool_str4498[sizeof("otaki.saitama.jp")];
- char stringpool_str4499[sizeof("tondabayashi.osaka.jp")];
- char stringpool_str4500[sizeof("nantan.kyoto.jp")];
- char stringpool_str4501[sizeof("nagato.yamaguchi.jp")];
- char stringpool_str4502[sizeof("net.kz")];
- char stringpool_str4503[sizeof("sx.cn")];
- char stringpool_str4504[sizeof("sapporo.jp")];
- char stringpool_str4505[sizeof("biz.at")];
- char stringpool_str4506[sizeof("shika.ishikawa.jp")];
- char stringpool_str4507[sizeof("syzran.ru")];
- char stringpool_str4508[sizeof("tabayama.yamanashi.jp")];
- char stringpool_str4509[sizeof("oskol.ru")];
- char stringpool_str4510[sizeof("educator.aero")];
- char stringpool_str4511[sizeof("tank.museum")];
- char stringpool_str4512[sizeof("tas.edu.au")];
- char stringpool_str4513[sizeof("odesa.ua")];
- char stringpool_str4514[sizeof("smolensk.ru")];
- char stringpool_str4515[sizeof("kolobrzeg.pl")];
- char stringpool_str4516[sizeof("national.museum")];
- char stringpool_str4517[sizeof("hashima.gifu.jp")];
- char stringpool_str4518[sizeof("caserta.it")];
- char stringpool_str4519[sizeof("supplies")];
- char stringpool_str4520[sizeof("journalist.aero")];
- char stringpool_str4521[sizeof("tsumagoi.gunma.jp")];
- char stringpool_str4522[sizeof("salerno.it")];
- char stringpool_str4523[sizeof("univ.sn")];
- char stringpool_str4524[sizeof("jessheim.no")];
- char stringpool_str4525[sizeof("sciences.museum")];
- char stringpool_str4526[sizeof("sci.eg")];
- char stringpool_str4527[sizeof("buryatia.ru")];
- char stringpool_str4528[sizeof("xn--davvenjrga-y4a.no")];
- char stringpool_str4529[sizeof("tamamura.gunma.jp")];
- char stringpool_str4530[sizeof("scrapper-site.net")];
- char stringpool_str4531[sizeof("tsubame.niigata.jp")];
- char stringpool_str4532[sizeof("shingu.hyogo.jp")];
- char stringpool_str4533[sizeof("mashiko.tochigi.jp")];
- char stringpool_str4534[sizeof("med.ht")];
- char stringpool_str4535[sizeof("education")];
- char stringpool_str4536[sizeof("pvt.ge")];
- char stringpool_str4537[sizeof("ibigawa.gifu.jp")];
- char stringpool_str4538[sizeof("tg")];
- char stringpool_str4539[sizeof("hichiso.gifu.jp")];
- char stringpool_str4540[sizeof("tamayu.shimane.jp")];
- char stringpool_str4541[sizeof("malvik.no")];
- char stringpool_str4542[sizeof("nakagusuku.okinawa.jp")];
- char stringpool_str4543[sizeof("balestrand.no")];
- char stringpool_str4544[sizeof("utsunomiya.tochigi.jp")];
- char stringpool_str4545[sizeof("jolster.no")];
- char stringpool_str4546[sizeof("taku.saga.jp")];
- char stringpool_str4547[sizeof("trondheim.no")];
- char stringpool_str4548[sizeof("wakkanai.hokkaido.jp")];
- char stringpool_str4549[sizeof("xn--gjvik-wua.no")];
- char stringpool_str4550[sizeof("station.museum")];
- char stringpool_str4551[sizeof("chukotka.ru")];
- char stringpool_str4552[sizeof("jevnaker.no")];
- char stringpool_str4553[sizeof("or.tz")];
- char stringpool_str4554[sizeof("simple-url.com")];
- char stringpool_str4555[sizeof("yokosuka.kanagawa.jp")];
- char stringpool_str4556[sizeof("or.us")];
- char stringpool_str4557[sizeof("ninohe.iwate.jp")];
- char stringpool_str4558[sizeof("monza-e-della-brianza.it")];
- char stringpool_str4559[sizeof("oz.au")];
- char stringpool_str4560[sizeof("journalism.museum")];
- char stringpool_str4561[sizeof("forgot.her.name")];
- char stringpool_str4562[sizeof("ol.no")];
- char stringpool_str4563[sizeof("magnitka.ru")];
- char stringpool_str4564[sizeof("philadelphiaarea.museum")];
- char stringpool_str4565[sizeof("tarumizu.kagoshima.jp")];
- char stringpool_str4566[sizeof("or.mu")];
- char stringpool_str4567[sizeof("xn--gmqw5a.hk")];
- char stringpool_str4568[sizeof("sanfrancisco.museum")];
- char stringpool_str4569[sizeof("sibenik.museum")];
- char stringpool_str4570[sizeof("perugia.it")];
- char stringpool_str4571[sizeof("obama.fukui.jp")];
- char stringpool_str4572[sizeof("entertainment.aero")];
- char stringpool_str4573[sizeof("or.at")];
- char stringpool_str4574[sizeof("brand.se")];
- char stringpool_str4575[sizeof("or.cr")];
- char stringpool_str4576[sizeof("kuroiso.tochigi.jp")];
- char stringpool_str4577[sizeof("odawara.kanagawa.jp")];
- char stringpool_str4578[sizeof("software.aero")];
- char stringpool_str4579[sizeof("support")];
- char stringpool_str4580[sizeof("shiwa.iwate.jp")];
- char stringpool_str4581[sizeof("yamagata.gifu.jp")];
- char stringpool_str4582[sizeof("omuta.fukuoka.jp")];
- char stringpool_str4583[sizeof("carbonia-iglesias.it")];
- char stringpool_str4584[sizeof("chikuho.fukuoka.jp")];
- char stringpool_str4585[sizeof("otaru.hokkaido.jp")];
- char stringpool_str4586[sizeof("fukuchiyama.kyoto.jp")];
- char stringpool_str4587[sizeof("kamitsue.oita.jp")];
- char stringpool_str4588[sizeof("is-a-geek.org")];
- char stringpool_str4589[sizeof("shingu.fukuoka.jp")];
- char stringpool_str4590[sizeof("forgot.his.name")];
- char stringpool_str4591[sizeof("or.kr")];
- char stringpool_str4592[sizeof("omiya.saitama.jp")];
- char stringpool_str4593[sizeof("ono.hyogo.jp")];
- char stringpool_str4594[sizeof("or.ci")];
- char stringpool_str4595[sizeof("flight.aero")];
- char stringpool_str4596[sizeof("tozawa.yamagata.jp")];
- char stringpool_str4597[sizeof("siracusa.it")];
- char stringpool_str4598[sizeof("takatori.nara.jp")];
- char stringpool_str4599[sizeof("otake.hiroshima.jp")];
- char stringpool_str4600[sizeof("museet.museum")];
- char stringpool_str4601[sizeof("tsushima.nagasaki.jp")];
- char stringpool_str4602[sizeof("od.ua")];
- char stringpool_str4603[sizeof("cargo.aero")];
- char stringpool_str4604[sizeof("info.tt")];
- char stringpool_str4605[sizeof("kasugai.aichi.jp")];
- char stringpool_str4606[sizeof("ozora.hokkaido.jp")];
- char stringpool_str4607[sizeof("is-a-photographer.com")];
- char stringpool_str4608[sizeof("tsukui.kanagawa.jp")];
- char stringpool_str4609[sizeof("group.aero")];
- char stringpool_str4610[sizeof("aero.tt")];
- char stringpool_str4611[sizeof("xn--rhqv96g")];
- char stringpool_str4612[sizeof("or.na")];
- char stringpool_str4613[sizeof("flights")];
- char stringpool_str4614[sizeof("waw.pl")];
- char stringpool_str4615[sizeof("jeju.kr")];
- char stringpool_str4616[sizeof("sagae.yamagata.jp")];
- char stringpool_str4617[sizeof("owani.aomori.jp")];
- char stringpool_str4618[sizeof("muenster.museum")];
- char stringpool_str4619[sizeof("mobi.tz")];
- char stringpool_str4620[sizeof("cmw.ru")];
- char stringpool_str4621[sizeof("ryugasaki.ibaraki.jp")];
- char stringpool_str4622[sizeof("nanporo.hokkaido.jp")];
- char stringpool_str4623[sizeof("daegu.kr")];
- char stringpool_str4624[sizeof("sukagawa.fukushima.jp")];
- char stringpool_str4625[sizeof("illustration.museum")];
- char stringpool_str4626[sizeof("on.ca")];
- char stringpool_str4627[sizeof("jeonnam.kr")];
- char stringpool_str4628[sizeof("ojiya.niigata.jp")];
- char stringpool_str4629[sizeof("jar.ru")];
- char stringpool_str4630[sizeof("fujishiro.ibaraki.jp")];
- char stringpool_str4631[sizeof("tarama.okinawa.jp")];
- char stringpool_str4632[sizeof("agriculture.museum")];
- char stringpool_str4633[sizeof("taketa.oita.jp")];
- char stringpool_str4634[sizeof("otobe.hokkaido.jp")];
- char stringpool_str4635[sizeof("oh.us")];
- char stringpool_str4636[sizeof("or.bi")];
- char stringpool_str4637[sizeof("or.th")];
- char stringpool_str4638[sizeof("house.museum")];
- char stringpool_str4639[sizeof("dovre.no")];
- char stringpool_str4640[sizeof("glass.museum")];
- char stringpool_str4641[sizeof("noboribetsu.hokkaido.jp")];
- char stringpool_str4642[sizeof("writesthisblog.com")];
- char stringpool_str4643[sizeof("readmyblog.org")];
- char stringpool_str4644[sizeof("gorge.museum")];
- char stringpool_str4645[sizeof("hino.tottori.jp")];
- char stringpool_str4646[sizeof("kamo.niigata.jp")];
- char stringpool_str4647[sizeof("ichikawa.hyogo.jp")];
- char stringpool_str4648[sizeof("narashino.chiba.jp")];
- char stringpool_str4649[sizeof("toho.fukuoka.jp")];
- char stringpool_str4650[sizeof("oketo.hokkaido.jp")];
- char stringpool_str4651[sizeof("johana.toyama.jp")];
- char stringpool_str4652[sizeof("takamori.kumamoto.jp")];
- char stringpool_str4653[sizeof("is-a-bulls-fan.com")];
- char stringpool_str4654[sizeof("scienceandindustry.museum")];
- char stringpool_str4655[sizeof("hawaii.museum")];
- char stringpool_str4656[sizeof("field.museum")];
- char stringpool_str4657[sizeof("kitashiobara.fukushima.jp")];
- char stringpool_str4658[sizeof("komagane.nagano.jp")];
- char stringpool_str4659[sizeof("force.museum")];
- char stringpool_str4660[sizeof("ok.us")];
- char stringpool_str4661[sizeof("sande.xn--mre-og-romsdal-qqb.no")];
- char stringpool_str4662[sizeof("nichinan.miyazaki.jp")];
- char stringpool_str4663[sizeof("otama.fukushima.jp")];
- char stringpool_str4664[sizeof("swidnica.pl")];
- char stringpool_str4665[sizeof("cyber.museum")];
- char stringpool_str4666[sizeof("kainan.wakayama.jp")];
- char stringpool_str4667[sizeof("union.aero")];
- char stringpool_str4668[sizeof("tonaki.okinawa.jp")];
- char stringpool_str4669[sizeof("bible.museum")];
- char stringpool_str4670[sizeof("oyabe.toyama.jp")];
- char stringpool_str4671[sizeof("cymru.museum")];
- char stringpool_str4672[sizeof("amber.museum")];
- char stringpool_str4673[sizeof("sherbrooke.museum")];
- char stringpool_str4674[sizeof("suzuka.mie.jp")];
- char stringpool_str4675[sizeof("k12.ut.us")];
- char stringpool_str4676[sizeof("steinkjer.no")];
- char stringpool_str4677[sizeof("donna.no")];
- char stringpool_str4678[sizeof("sellsyourhome.org")];
- char stringpool_str4679[sizeof("kamifurano.hokkaido.jp")];
- char stringpool_str4680[sizeof("takaoka.toyama.jp")];
- char stringpool_str4681[sizeof("shacknet.nu")];
- char stringpool_str4682[sizeof("asuke.aichi.jp")];
- char stringpool_str4683[sizeof("takasaki.gunma.jp")];
- char stringpool_str4684[sizeof("obira.hokkaido.jp")];
- char stringpool_str4685[sizeof("dyroy.no")];
- char stringpool_str4686[sizeof("hino.tokyo.jp")];
- char stringpool_str4687[sizeof("railway.museum")];
- char stringpool_str4688[sizeof("sciencesnaturelles.museum")];
- char stringpool_str4689[sizeof("sakegawa.yamagata.jp")];
- char stringpool_str4690[sizeof("money.museum")];
- char stringpool_str4691[sizeof("tsurugashima.saitama.jp")];
- char stringpool_str4692[sizeof("works.aero")];
- char stringpool_str4693[sizeof("aisai.aichi.jp")];
- char stringpool_str4694[sizeof("org.co")];
- char stringpool_str4695[sizeof("educational.museum")];
- char stringpool_str4696[sizeof("tako.chiba.jp")];
- char stringpool_str4697[sizeof("basel.museum")];
- char stringpool_str4698[sizeof("rockart.museum")];
- char stringpool_str4699[sizeof("uozu.toyama.jp")];
- char stringpool_str4700[sizeof("tomakomai.hokkaido.jp")];
- char stringpool_str4701[sizeof("sciencehistory.museum")];
- char stringpool_str4702[sizeof("tsugaru.aomori.jp")];
- char stringpool_str4703[sizeof("yamagata.yamagata.jp")];
- char stringpool_str4704[sizeof("rochester.museum")];
- char stringpool_str4705[sizeof("tanohata.iwate.jp")];
- char stringpool_str4706[sizeof("space-to-rent.com")];
- char stringpool_str4707[sizeof("ls")];
- char stringpool_str4708[sizeof("lr")];
- char stringpool_str4709[sizeof("gob.sv")];
- char stringpool_str4710[sizeof("edu.sv")];
- char stringpool_str4711[sizeof("www.ro")];
- char stringpool_str4712[sizeof("com.sv")];
- char stringpool_str4713[sizeof("nishiaizu.fukushima.jp")];
- char stringpool_str4714[sizeof("shitara.aichi.jp")];
- char stringpool_str4715[sizeof("tomisato.chiba.jp")];
- char stringpool_str4716[sizeof("gov.lv")];
- char stringpool_str4717[sizeof("soni.nara.jp")];
- char stringpool_str4718[sizeof("edu.lv")];
- char stringpool_str4719[sizeof("com.lv")];
- char stringpool_str4720[sizeof("lu")];
- char stringpool_str4721[sizeof("k12.mt.us")];
- char stringpool_str4722[sizeof("l.bg")];
- char stringpool_str4723[sizeof("l.se")];
- char stringpool_str4724[sizeof("lom.no")];
- char stringpool_str4725[sizeof("asn.lv")];
- char stringpool_str4726[sizeof("joshkar-ola.ru")];
- char stringpool_str4727[sizeof("university.museum")];
- char stringpool_str4728[sizeof("lt")];
- char stringpool_str4729[sizeof("marketplace.aero")];
- char stringpool_str4730[sizeof("takamori.nagano.jp")];
- char stringpool_str4731[sizeof("org.cw")];
- char stringpool_str4732[sizeof("education.museum")];
- char stringpool_str4733[sizeof("itako.ibaraki.jp")];
- char stringpool_str4734[sizeof("lb")];
- char stringpool_str4735[sizeof("tanabe.wakayama.jp")];
- char stringpool_str4736[sizeof("bando.ibaraki.jp")];
- char stringpool_str4737[sizeof("xn--mgb9awbf")];
- char stringpool_str4738[sizeof("org.ci")];
- char stringpool_str4739[sizeof("li")];
- char stringpool_str4740[sizeof("other.nf")];
- char stringpool_str4741[sizeof("shimane.jp")];
- char stringpool_str4742[sizeof("limo")];
- char stringpool_str4743[sizeof("k12.vt.us")];
- char stringpool_str4744[sizeof("teshikaga.hokkaido.jp")];
- char stringpool_str4745[sizeof("net.gn")];
- char stringpool_str4746[sizeof("xn--aurskog-hland-jnb.no")];
- char stringpool_str4747[sizeof("seihi.nagasaki.jp")];
- char stringpool_str4748[sizeof("info.vn")];
- char stringpool_str4749[sizeof("ly")];
- char stringpool_str4750[sizeof("santafe.museum")];
- char stringpool_str4751[sizeof("luster.no")];
- char stringpool_str4752[sizeof("tajiri.osaka.jp")];
- char stringpool_str4753[sizeof("os.hedmark.no")];
- char stringpool_str4754[sizeof("technology")];
- char stringpool_str4755[sizeof("joso.ibaraki.jp")];
- char stringpool_str4756[sizeof("lunner.no")];
- char stringpool_str4757[sizeof("kunstunddesign.museum")];
- char stringpool_str4758[sizeof("okuma.fukushima.jp")];
- char stringpool_str4759[sizeof("handa.aichi.jp")];
- char stringpool_str4760[sizeof("chuo.tokyo.jp")];
- char stringpool_str4761[sizeof("ltd.lk")];
- char stringpool_str4762[sizeof("gov.mv")];
- char stringpool_str4763[sizeof("edu.mv")];
- char stringpool_str4764[sizeof("scienceandhistory.museum")];
- char stringpool_str4765[sizeof("com.mv")];
- char stringpool_str4766[sizeof("baths.museum")];
- char stringpool_str4767[sizeof("sweden.museum")];
- char stringpool_str4768[sizeof("expert")];
- char stringpool_str4769[sizeof("takayama.gunma.jp")];
- char stringpool_str4770[sizeof("lib.ee")];
- char stringpool_str4771[sizeof("xn--dyry-ira.no")];
- char stringpool_str4772[sizeof("naturhistorisches.museum")];
- char stringpool_str4773[sizeof("lebork.pl")];
- char stringpool_str4774[sizeof("farmstead.museum")];
- char stringpool_str4775[sizeof("shimoichi.nara.jp")];
- char stringpool_str4776[sizeof("tobetsu.hokkaido.jp")];
- char stringpool_str4777[sizeof("bungotakada.oita.jp")];
- char stringpool_str4778[sizeof("la")];
- char stringpool_str4779[sizeof("sue.fukuoka.jp")];
- char stringpool_str4780[sizeof("int.mv")];
- char stringpool_str4781[sizeof("leg.br")];
- char stringpool_str4782[sizeof("or.id")];
- char stringpool_str4783[sizeof("jefferson.museum")];
- char stringpool_str4784[sizeof("clock.museum")];
- char stringpool_str4785[sizeof("uhren.museum")];
- char stringpool_str4786[sizeof("sennan.osaka.jp")];
- char stringpool_str4787[sizeof("christmas")];
- char stringpool_str4788[sizeof("land")];
- char stringpool_str4789[sizeof("lierne.no")];
- char stringpool_str4790[sizeof("takayama.nagano.jp")];
- char stringpool_str4791[sizeof("lv")];
- char stringpool_str4792[sizeof("tas.gov.au")];
- char stringpool_str4793[sizeof("link")];
- char stringpool_str4794[sizeof("asahi.ibaraki.jp")];
- char stringpool_str4795[sizeof("anan.tokushima.jp")];
- char stringpool_str4796[sizeof("jeonbuk.kr")];
- char stringpool_str4797[sizeof("nishinoshima.shimane.jp")];
- char stringpool_str4798[sizeof("nankoku.kochi.jp")];
- char stringpool_str4799[sizeof("takahama.aichi.jp")];
- char stringpool_str4800[sizeof("mordovia.ru")];
- char stringpool_str4801[sizeof("lel.br")];
- char stringpool_str4802[sizeof("or.it")];
- char stringpool_str4803[sizeof("takazaki.miyazaki.jp")];
- char stringpool_str4804[sizeof("gdynia.pl")];
- char stringpool_str4805[sizeof("tokushima.jp")];
- char stringpool_str4806[sizeof("lerdal.no")];
- char stringpool_str4807[sizeof("ski.museum")];
- char stringpool_str4808[sizeof("limanowa.pl")];
- char stringpool_str4809[sizeof("ot.it")];
- char stringpool_str4810[sizeof("krokstadelva.no")];
- char stringpool_str4811[sizeof("shirosato.ibaraki.jp")];
- char stringpool_str4812[sizeof("xn--berlevg-jxa.no")];
- char stringpool_str4813[sizeof("mima.tokushima.jp")];
- char stringpool_str4814[sizeof("lapy.pl")];
- char stringpool_str4815[sizeof("yusui.kagoshima.jp")];
- char stringpool_str4816[sizeof("lindas.no")];
- char stringpool_str4817[sizeof("mil.lv")];
- char stringpool_str4818[sizeof("augustow.pl")];
- char stringpool_str4819[sizeof("kiev.ua")];
- char stringpool_str4820[sizeof("matsushige.tokushima.jp")];
- char stringpool_str4821[sizeof("tamatsukuri.ibaraki.jp")];
- char stringpool_str4822[sizeof("lib.or.us")];
- char stringpool_str4823[sizeof("dontexist.org")];
- char stringpool_str4824[sizeof("lugansk.ua")];
- char stringpool_str4825[sizeof("lib.dc.us")];
- char stringpool_str4826[sizeof("info.ve")];
- char stringpool_str4827[sizeof("kikuchi.kumamoto.jp")];
- char stringpool_str4828[sizeof("lib.ok.us")];
- char stringpool_str4829[sizeof("biz.mv")];
- char stringpool_str4830[sizeof("lib.sc.us")];
- char stringpool_str4831[sizeof("kiwi")];
- char stringpool_str4832[sizeof("institute")];
- char stringpool_str4833[sizeof("lib.de.us")];
- char stringpool_str4834[sizeof("tsukigata.hokkaido.jp")];
- char stringpool_str4835[sizeof("rokunohe.aomori.jp")];
- char stringpool_str4836[sizeof("lk")];
- char stringpool_str4837[sizeof("yoshino.nara.jp")];
- char stringpool_str4838[sizeof("lib.nj.us")];
- char stringpool_str4839[sizeof("lib.nm.us")];
- char stringpool_str4840[sizeof("chuvashia.ru")];
- char stringpool_str4841[sizeof("lib.nc.us")];
- char stringpool_str4842[sizeof("shinkamigoto.nagasaki.jp")];
- char stringpool_str4843[sizeof("rg.it")];
- char stringpool_str4844[sizeof("osen.no")];
- char stringpool_str4845[sizeof("takasu.hokkaido.jp")];
- char stringpool_str4846[sizeof("izumozaki.niigata.jp")];
- char stringpool_str4847[sizeof("lib.ne.us")];
- char stringpool_str4848[sizeof("keisen.fukuoka.jp")];
- char stringpool_str4849[sizeof("lib.sd.us")];
- char stringpool_str4850[sizeof("shimodate.ibaraki.jp")];
- char stringpool_str4851[sizeof("org.sn")];
- char stringpool_str4852[sizeof("odda.no")];
- char stringpool_str4853[sizeof("lib.la.us")];
- char stringpool_str4854[sizeof("beppu.oita.jp")];
- char stringpool_str4855[sizeof("fukushima.fukushima.jp")];
- char stringpool_str4856[sizeof("bryansk.ru")];
- char stringpool_str4857[sizeof("lebesby.no")];
- char stringpool_str4858[sizeof("lardal.no")];
- char stringpool_str4859[sizeof("mobi.tt")];
- char stringpool_str4860[sizeof("technology.museum")];
- char stringpool_str4861[sizeof("koge.tottori.jp")];
- char stringpool_str4862[sizeof("berlevag.no")];
- char stringpool_str4863[sizeof("takehara.hiroshima.jp")];
- char stringpool_str4864[sizeof("off.ai")];
- char stringpool_str4865[sizeof("wales.museum")];
- char stringpool_str4866[sizeof("lib.nd.us")];
- char stringpool_str4867[sizeof("koeln.museum")];
- char stringpool_str4868[sizeof("stjohn.museum")];
- char stringpool_str4869[sizeof("boldlygoingnowhere.org")];
- char stringpool_str4870[sizeof("chita.aichi.jp")];
- char stringpool_str4871[sizeof("mil.mv")];
- char stringpool_str4872[sizeof("ginan.gifu.jp")];
- char stringpool_str4873[sizeof("lib.il.us")];
- char stringpool_str4874[sizeof("xn--langevg-jxa.no")];
- char stringpool_str4875[sizeof("org.tn")];
- char stringpool_str4876[sizeof("prato.it")];
- char stringpool_str4877[sizeof("konan.aichi.jp")];
- char stringpool_str4878[sizeof("lib.ri.us")];
- char stringpool_str4879[sizeof("kasamatsu.gifu.jp")];
- char stringpool_str4880[sizeof("siljan.no")];
- char stringpool_str4881[sizeof("oslo.no")];
- char stringpool_str4882[sizeof("kunst.museum")];
- char stringpool_str4883[sizeof("marugame.kagawa.jp")];
- char stringpool_str4884[sizeof("lib.ms.us")];
- char stringpool_str4885[sizeof("lib.mo.us")];
- char stringpool_str4886[sizeof("lib.ia.us")];
- char stringpool_str4887[sizeof("lib.id.us")];
- char stringpool_str4888[sizeof("mantova.it")];
- char stringpool_str4889[sizeof("org.in")];
- char stringpool_str4890[sizeof("karasuyama.tochigi.jp")];
- char stringpool_str4891[sizeof("askvoll.no")];
- char stringpool_str4892[sizeof("xn--ggaviika-8ya47h.no")];
- char stringpool_str4893[sizeof("oyer.no")];
- char stringpool_str4894[sizeof("takaishi.osaka.jp")];
- char stringpool_str4895[sizeof("lipetsk.ru")];
- char stringpool_str4896[sizeof("koto.tokyo.jp")];
- char stringpool_str4897[sizeof("okawa.fukuoka.jp")];
- char stringpool_str4898[sizeof("lib.me.us")];
- char stringpool_str4899[sizeof("tsurugi.ishikawa.jp")];
- char stringpool_str4900[sizeof("voagat.no")];
- char stringpool_str4901[sizeof("lib.as.us")];
- char stringpool_str4902[sizeof("lib.ar.us")];
- char stringpool_str4903[sizeof("yosemite.museum")];
- char stringpool_str4904[sizeof("lib.ak.us")];
- char stringpool_str4905[sizeof("lyngdal.no")];
- char stringpool_str4906[sizeof("lib.ma.us")];
- char stringpool_str4907[sizeof("media.aero")];
- char stringpool_str4908[sizeof("gaivuotna.no")];
- char stringpool_str4909[sizeof("lib.md.us")];
- char stringpool_str4910[sizeof("lib.pr.us")];
- char stringpool_str4911[sizeof("lib.al.us")];
- char stringpool_str4912[sizeof("onojo.fukuoka.jp")];
- char stringpool_str4913[sizeof("org.mn")];
- char stringpool_str4914[sizeof("himi.toyama.jp")];
- char stringpool_str4915[sizeof("kyiv.ua")];
- char stringpool_str4916[sizeof("kawasaki.jp")];
- char stringpool_str4917[sizeof("ryazan.ru")];
- char stringpool_str4918[sizeof("minamitane.kagoshima.jp")];
- char stringpool_str4919[sizeof("org.an")];
- char stringpool_str4920[sizeof("ushistory.museum")];
- char stringpool_str4921[sizeof("lillesand.no")];
- char stringpool_str4922[sizeof("tachikawa.tokyo.jp")];
- char stringpool_str4923[sizeof("nakai.kanagawa.jp")];
- char stringpool_str4924[sizeof("klepp.no")];
- char stringpool_str4925[sizeof("ivanovo.ru")];
- char stringpool_str4926[sizeof("kita.tokyo.jp")];
- char stringpool_str4927[sizeof("lib.pa.us")];
- char stringpool_str4928[sizeof("austevoll.no")];
- char stringpool_str4929[sizeof("lib.mi.us")];
- char stringpool_str4930[sizeof("ogawara.miyagi.jp")];
- char stringpool_str4931[sizeof("org.pn")];
- char stringpool_str4932[sizeof("lib.va.us")];
- char stringpool_str4933[sizeof("takaharu.miyazaki.jp")];
- char stringpool_str4934[sizeof("nagakute.aichi.jp")];
- char stringpool_str4935[sizeof("org.vn")];
- char stringpool_str4936[sizeof("hiraizumi.iwate.jp")];
- char stringpool_str4937[sizeof("lanbib.se")];
- char stringpool_str4938[sizeof("accident-investigation.aero")];
- char stringpool_str4939[sizeof("takahagi.ibaraki.jp")];
- char stringpool_str4940[sizeof("org.cu")];
- char stringpool_str4941[sizeof("xn--hylandet-54a.no")];
- char stringpool_str4942[sizeof("gyokuto.kumamoto.jp")];
- char stringpool_str4943[sizeof("perso.sn")];
- char stringpool_str4944[sizeof("saves-the-whales.com")];
- char stringpool_str4945[sizeof("legnica.pl")];
- char stringpool_str4946[sizeof("tokorozawa.saitama.jp")];
- char stringpool_str4947[sizeof("alto-adige.it")];
- char stringpool_str4948[sizeof("yaizu.shizuoka.jp")];
- char stringpool_str4949[sizeof("lib.vi.us")];
- char stringpool_str4950[sizeof("nakagyo.kyoto.jp")];
- char stringpool_str4951[sizeof("press.ma")];
- char stringpool_str4952[sizeof("swinoujscie.pl")];
- char stringpool_str4953[sizeof("co.lc")];
- char stringpool_str4954[sizeof("sakae.nagano.jp")];
- char stringpool_str4955[sizeof("luxe")];
- char stringpool_str4956[sizeof("ac.sz")];
- char stringpool_str4957[sizeof("ac.tz")];
- char stringpool_str4958[sizeof("org.hn")];
- char stringpool_str4959[sizeof("sykkylven.no")];
- char stringpool_str4960[sizeof("s3-sa-east-1.amazonaws.com")];
- char stringpool_str4961[sizeof("takahashi.okayama.jp")];
- char stringpool_str4962[sizeof("lib.ks.us")];
- char stringpool_str4963[sizeof("honbetsu.hokkaido.jp")];
- char stringpool_str4964[sizeof("daito.osaka.jp")];
- char stringpool_str4965[sizeof("ac.rs")];
- char stringpool_str4966[sizeof("kopervik.no")];
- char stringpool_str4967[sizeof("ac.mu")];
- char stringpool_str4968[sizeof("nyuzen.toyama.jp")];
- char stringpool_str4969[sizeof("nrw")];
- char stringpool_str4970[sizeof("ac.ru")];
- char stringpool_str4971[sizeof("ac.se")];
- char stringpool_str4972[sizeof("yachimata.chiba.jp")];
- char stringpool_str4973[sizeof("ac.ae")];
- char stringpool_str4974[sizeof("qc.com")];
- char stringpool_str4975[sizeof("shimane.shimane.jp")];
- char stringpool_str4976[sizeof("lezajsk.pl")];
- char stringpool_str4977[sizeof("lib.hi.us")];
- char stringpool_str4978[sizeof("ac.at")];
- char stringpool_str4979[sizeof("ac.me")];
- char stringpool_str4980[sizeof("ac.gn")];
- char stringpool_str4981[sizeof("porsgrunn.no")];
- char stringpool_str4982[sizeof("ac.cr")];
- char stringpool_str4983[sizeof("labour.museum")];
- char stringpool_str4984[sizeof("kawahara.tottori.jp")];
- char stringpool_str4985[sizeof("tsuruoka.yamagata.jp")];
- char stringpool_str4986[sizeof("org.kn")];
- char stringpool_str4987[sizeof("yamada.iwate.jp")];
- char stringpool_str4988[sizeof("net.st")];
- char stringpool_str4989[sizeof("ac.cn")];
- char stringpool_str4990[sizeof("parma.it")];
- char stringpool_str4991[sizeof("kawaba.gunma.jp")];
- char stringpool_str4992[sizeof("iwama.ibaraki.jp")];
- char stringpool_str4993[sizeof("zhytomyr.ua")];
- char stringpool_str4994[sizeof("mombetsu.hokkaido.jp")];
- char stringpool_str4995[sizeof("namikata.ehime.jp")];
- char stringpool_str4996[sizeof("landes.museum")];
- char stringpool_str4997[sizeof("sumita.iwate.jp")];
- char stringpool_str4998[sizeof("nishigo.fukushima.jp")];
- char stringpool_str4999[sizeof("artanddesign.museum")];
- char stringpool_str5000[sizeof("media.museum")];
- char stringpool_str5001[sizeof("ac.kr")];
- char stringpool_str5002[sizeof("shichinohe.aomori.jp")];
- char stringpool_str5003[sizeof("perso.ht")];
- char stringpool_str5004[sizeof("ac.tj")];
- char stringpool_str5005[sizeof("terni.it")];
- char stringpool_str5006[sizeof("timekeeping.museum")];
- char stringpool_str5007[sizeof("tagami.niigata.jp")];
- char stringpool_str5008[sizeof("jetzt")];
- char stringpool_str5009[sizeof("net.tt")];
- char stringpool_str5010[sizeof("padua.it")];
- char stringpool_str5011[sizeof("ac.ci")];
- char stringpool_str5012[sizeof("land-4-sale.us")];
- char stringpool_str5013[sizeof("tochio.niigata.jp")];
- char stringpool_str5014[sizeof("tatebayashi.gunma.jp")];
- char stringpool_str5015[sizeof("gouv.ht")];
- char stringpool_str5016[sizeof("net.bt")];
- char stringpool_str5017[sizeof("xn--hery-ira.xn--mre-og-romsdal-qqb.no")];
- char stringpool_str5018[sizeof("obuse.nagano.jp")];
- char stringpool_str5019[sizeof("drangedal.no")];
- char stringpool_str5020[sizeof("nesset.no")];
- char stringpool_str5021[sizeof("nakatane.kagoshima.jp")];
- char stringpool_str5022[sizeof("yahaba.iwate.jp")];
- char stringpool_str5023[sizeof("ikeda.gifu.jp")];
- char stringpool_str5024[sizeof("cc.na")];
- char stringpool_str5025[sizeof("ac.ma")];
- char stringpool_str5026[sizeof("lighting")];
- char stringpool_str5027[sizeof("ouda.nara.jp")];
- char stringpool_str5028[sizeof("ogata.akita.jp")];
- char stringpool_str5029[sizeof("kudamatsu.yamaguchi.jp")];
- char stringpool_str5030[sizeof("moriguchi.osaka.jp")];
- char stringpool_str5031[sizeof("pittsburgh.museum")];
- char stringpool_str5032[sizeof("anamizu.ishikawa.jp")];
- char stringpool_str5033[sizeof("oe.yamagata.jp")];
- char stringpool_str5034[sizeof("jinsekikogen.hiroshima.jp")];
- char stringpool_str5035[sizeof("juif.museum")];
- char stringpool_str5036[sizeof("xn--gildeskl-g0a.no")];
- char stringpool_str5037[sizeof("or.jp")];
- char stringpool_str5038[sizeof("ontario.museum")];
- char stringpool_str5039[sizeof("gc.ca")];
- char stringpool_str5040[sizeof("qc.ca")];
- char stringpool_str5041[sizeof("net.mt")];
- char stringpool_str5042[sizeof("kisofukushima.nagano.jp")];
- char stringpool_str5043[sizeof("takizawa.iwate.jp")];
- char stringpool_str5044[sizeof("oregon.museum")];
- char stringpool_str5045[sizeof("bc.ca")];
- char stringpool_str5046[sizeof("ac.be")];
- char stringpool_str5047[sizeof("la-spezia.it")];
- char stringpool_str5048[sizeof("otari.nagano.jp")];
- char stringpool_str5049[sizeof("barlettatraniandria.it")];
- char stringpool_str5050[sizeof("doshi.yamanashi.jp")];
- char stringpool_str5051[sizeof("obu.aichi.jp")];
- char stringpool_str5052[sizeof("uchinada.ishikawa.jp")];
- char stringpool_str5053[sizeof("net.pt")];
- char stringpool_str5054[sizeof("leirvik.no")];
- char stringpool_str5055[sizeof("ac.th")];
- char stringpool_str5056[sizeof("essex.museum")];
- char stringpool_str5057[sizeof("miyazaki.jp")];
- char stringpool_str5058[sizeof("oshu.iwate.jp")];
- char stringpool_str5059[sizeof("mugi.tokushima.jp")];
- char stringpool_str5060[sizeof("furniture.museum")];
- char stringpool_str5061[sizeof("gangaviika.no")];
- char stringpool_str5062[sizeof("k12.nv.us")];
- char stringpool_str5063[sizeof("kochi.kochi.jp")];
- char stringpool_str5064[sizeof("barletta-trani-andria.it")];
- char stringpool_str5065[sizeof("gob.gt")];
- char stringpool_str5066[sizeof("edu.gt")];
- char stringpool_str5067[sizeof("takikawa.hokkaido.jp")];
- char stringpool_str5068[sizeof("com.gt")];
- char stringpool_str5069[sizeof("ebiz.tw")];
- char stringpool_str5070[sizeof("narvik.no")];
- char stringpool_str5071[sizeof("kawanishi.nara.jp")];
- char stringpool_str5072[sizeof("tomioka.gunma.jp")];
- char stringpool_str5073[sizeof("otaki.nagano.jp")];
- char stringpool_str5074[sizeof("tainai.niigata.jp")];
- char stringpool_str5075[sizeof("opoczno.pl")];
- char stringpool_str5076[sizeof("hoyanger.no")];
- char stringpool_str5077[sizeof("navuotna.no")];
- char stringpool_str5078[sizeof("is-saved.org")];
- char stringpool_str5079[sizeof("kuchinotsu.nagasaki.jp")];
- char stringpool_str5080[sizeof("oseto.nagasaki.jp")];
- char stringpool_str5081[sizeof("net.ht")];
- char stringpool_str5082[sizeof("name.tj")];
- char stringpool_str5083[sizeof("tydal.no")];
- char stringpool_str5084[sizeof("targi.pl")];
- char stringpool_str5085[sizeof("shop.ht")];
- char stringpool_str5086[sizeof("if.ua")];
- char stringpool_str5087[sizeof("chikuzen.fukuoka.jp")];
- char stringpool_str5088[sizeof("tjome.no")];
- char stringpool_str5089[sizeof("olsztyn.pl")];
- char stringpool_str5090[sizeof("lib.wa.us")];
- char stringpool_str5091[sizeof("levanger.no")];
- char stringpool_str5092[sizeof("saigawa.fukuoka.jp")];
- char stringpool_str5093[sizeof("ind.gt")];
- char stringpool_str5094[sizeof("perso.tn")];
- char stringpool_str5095[sizeof("co.pl")];
- char stringpool_str5096[sizeof("trana.no")];
- char stringpool_str5097[sizeof("saku.nagano.jp")];
- char stringpool_str5098[sizeof("nakamichi.yamanashi.jp")];
- char stringpool_str5099[sizeof("nakatombetsu.hokkaido.jp")];
- char stringpool_str5100[sizeof("shinyoshitomi.fukuoka.jp")];
- char stringpool_str5101[sizeof("kaminokawa.tochigi.jp")];
- char stringpool_str5102[sizeof("co.pn")];
- char stringpool_str5103[sizeof("ogano.saitama.jp")];
- char stringpool_str5104[sizeof("lib.wi.us")];
- char stringpool_str5105[sizeof("ogose.saitama.jp")];
- char stringpool_str5106[sizeof("de.us")];
- char stringpool_str5107[sizeof("freight.aero")];
- char stringpool_str5108[sizeof("turek.pl")];
- char stringpool_str5109[sizeof("xn--80asehdb")];
- char stringpool_str5110[sizeof("xn--45brj9c")];
- char stringpool_str5111[sizeof("xn--s9brj9c")];
- char stringpool_str5112[sizeof("latina.it")];
- char stringpool_str5113[sizeof("taishi.hyogo.jp")];
- char stringpool_str5114[sizeof("osaki.miyagi.jp")];
- char stringpool_str5115[sizeof("vinnytsia.ua")];
- char stringpool_str5116[sizeof("tolga.no")];
- char stringpool_str5117[sizeof("kamiizumi.saitama.jp")];
- char stringpool_str5118[sizeof("usculture.museum")];
- char stringpool_str5119[sizeof("report")];
- char stringpool_str5120[sizeof("de.com")];
- char stringpool_str5121[sizeof("xn--tysvr-vra.no")];
- char stringpool_str5122[sizeof("lavangen.no")];
- char stringpool_str5123[sizeof("tomsk.ru")];
- char stringpool_str5124[sizeof("kawakami.nara.jp")];
- char stringpool_str5125[sizeof("iwamizawa.hokkaido.jp")];
- char stringpool_str5126[sizeof("narviika.no")];
- char stringpool_str5127[sizeof("tamakawa.fukushima.jp")];
- char stringpool_str5128[sizeof("rnrt.tn")];
- char stringpool_str5129[sizeof("xn--q9jyb4c")];
- char stringpool_str5130[sizeof("gs.svalbard.no")];
- char stringpool_str5131[sizeof("oguni.kumamoto.jp")];
- char stringpool_str5132[sizeof("miyada.nagano.jp")];
- char stringpool_str5133[sizeof("taketomi.okinawa.jp")];
- char stringpool_str5134[sizeof("brandywinevalley.museum")];
- char stringpool_str5135[sizeof("oceanographique.museum")];
- char stringpool_str5136[sizeof("openair.museum")];
- char stringpool_str5137[sizeof("xn--loabt-0qa.no")];
- char stringpool_str5138[sizeof("xn--rdy-0nab.no")];
- char stringpool_str5139[sizeof("shinshiro.aichi.jp")];
- char stringpool_str5140[sizeof("iiyama.nagano.jp")];
- char stringpool_str5141[sizeof("takatsuki.osaka.jp")];
- char stringpool_str5142[sizeof("dr.na")];
- char stringpool_str5143[sizeof("ostre-toten.no")];
- char stringpool_str5144[sizeof("org.dz")];
- char stringpool_str5145[sizeof("dn.ua")];
- char stringpool_str5146[sizeof("mil.gt")];
- char stringpool_str5147[sizeof("miyota.nagano.jp")];
- char stringpool_str5148[sizeof("org.sz")];
- char stringpool_str5149[sizeof("krym.ua")];
- char stringpool_str5150[sizeof("org.uz")];
- char stringpool_str5151[sizeof("xn--nqv7f")];
- char stringpool_str5152[sizeof("obama.nagasaki.jp")];
- char stringpool_str5153[sizeof("kanie.aichi.jp")];
- char stringpool_str5154[sizeof("is-very-bad.org")];
- char stringpool_str5155[sizeof("omura.nagasaki.jp")];
- char stringpool_str5156[sizeof("ac.id")];
- char stringpool_str5157[sizeof("est-le-patron.com")];
- char stringpool_str5158[sizeof("tomika.gifu.jp")];
- char stringpool_str5159[sizeof("miyake.nara.jp")];
- char stringpool_str5160[sizeof("turen.tn")];
- char stringpool_str5161[sizeof("raholt.no")];
- char stringpool_str5162[sizeof("ac.ir")];
- char stringpool_str5163[sizeof("org.bz")];
- char stringpool_str5164[sizeof("xn--p1acf")];
- char stringpool_str5165[sizeof("ac.mw")];
- char stringpool_str5166[sizeof("xn--fl-zia.no")];
- char stringpool_str5167[sizeof("ac.rw")];
- char stringpool_str5168[sizeof("okaya.nagano.jp")];
- char stringpool_str5169[sizeof("ouchi.saga.jp")];
- char stringpool_str5170[sizeof("tychy.pl")];
- char stringpool_str5171[sizeof("nishikata.tochigi.jp")];
- char stringpool_str5172[sizeof("ac.in")];
- char stringpool_str5173[sizeof("turin.it")];
- char stringpool_str5174[sizeof("kibichuo.okayama.jp")];
- char stringpool_str5175[sizeof("xn--fiqz9s")];
- char stringpool_str5176[sizeof("ono.fukui.jp")];
- char stringpool_str5177[sizeof("fc.it")];
- char stringpool_str5178[sizeof("shimofusa.chiba.jp")];
- char stringpool_str5179[sizeof("xn--h2brj9c")];
- char stringpool_str5180[sizeof("pavia.it")];
- char stringpool_str5181[sizeof("xn--fjord-lra.no")];
- char stringpool_str5182[sizeof("sarpsborg.no")];
- char stringpool_str5183[sizeof("marketing")];
- char stringpool_str5184[sizeof("umaji.kochi.jp")];
- char stringpool_str5185[sizeof("plc.ly")];
- char stringpool_str5186[sizeof("xn--fiqs8s")];
- char stringpool_str5187[sizeof("dp.ua")];
- char stringpool_str5188[sizeof("likescandy.com")];
- char stringpool_str5189[sizeof("kakamigahara.gifu.jp")];
- char stringpool_str5190[sizeof("org.az")];
- char stringpool_str5191[sizeof("ogori.fukuoka.jp")];
- char stringpool_str5192[sizeof("niigata.jp")];
- char stringpool_str5193[sizeof("kiyose.tokyo.jp")];
- char stringpool_str5194[sizeof("rawa-maz.pl")];
- char stringpool_str5195[sizeof("tokashiki.okinawa.jp")];
- char stringpool_str5196[sizeof("mc.it")];
- char stringpool_str5197[sizeof("ac.im")];
- char stringpool_str5198[sizeof("gov.cn")];
- char stringpool_str5199[sizeof("edu.cn")];
- char stringpool_str5200[sizeof("com.cn")];
- char stringpool_str5201[sizeof("komvux.se")];
- char stringpool_str5202[sizeof("cci.fr")];
- char stringpool_str5203[sizeof("avocat.fr")];
- char stringpool_str5204[sizeof("barrell-of-knowledge.info")];
- char stringpool_str5205[sizeof("kesennuma.miyagi.jp")];
- char stringpool_str5206[sizeof("kawakami.nagano.jp")];
- char stringpool_str5207[sizeof("lib.oh.us")];
- char stringpool_str5208[sizeof("nsw.au")];
- char stringpool_str5209[sizeof("kawajima.saitama.jp")];
- char stringpool_str5210[sizeof("lodi.it")];
- char stringpool_str5211[sizeof("ohda.shimane.jp")];
- char stringpool_str5212[sizeof("est-a-la-maison.com")];
- char stringpool_str5213[sizeof("og.ao")];
- char stringpool_str5214[sizeof("operaunite.com")];
- char stringpool_str5215[sizeof("heimatunduhren.museum")];
- char stringpool_str5216[sizeof("est-a-la-masion.com")];
- char stringpool_str5217[sizeof("tranoy.no")];
- char stringpool_str5218[sizeof("shop.hu")];
- char stringpool_str5219[sizeof("lib.nh.us")];
- char stringpool_str5220[sizeof("or.ug")];
- char stringpool_str5221[sizeof("tenri.nara.jp")];
- char stringpool_str5222[sizeof("losangeles.museum")];
- char stringpool_str5223[sizeof("tagawa.fukuoka.jp")];
- char stringpool_str5224[sizeof("rel.ht")];
- char stringpool_str5225[sizeof("neues.museum")];
- char stringpool_str5226[sizeof("xn--frya-hra.no")];
- char stringpool_str5227[sizeof("democrat")];
- char stringpool_str5228[sizeof("kawanehon.shizuoka.jp")];
- char stringpool_str5229[sizeof("name.tt")];
- char stringpool_str5230[sizeof("rhcloud.com")];
- char stringpool_str5231[sizeof("nozawaonsen.nagano.jp")];
- char stringpool_str5232[sizeof("ftpaccess.cc")];
- char stringpool_str5233[sizeof("xn--ciqpn.hk")];
- char stringpool_str5234[sizeof("savannahga.museum")];
- char stringpool_str5235[sizeof("nishinomiya.hyogo.jp")];
- char stringpool_str5236[sizeof("xn--nnx388a")];
- char stringpool_str5237[sizeof("ohira.miyagi.jp")];
- char stringpool_str5238[sizeof("kawatana.nagasaki.jp")];
- char stringpool_str5239[sizeof("luxembourg.museum")];
- char stringpool_str5240[sizeof("togakushi.nagano.jp")];
- char stringpool_str5241[sizeof("ochi.kochi.jp")];
- char stringpool_str5242[sizeof("est-mon-blogueur.com")];
- char stringpool_str5243[sizeof("tranby.no")];
- char stringpool_str5244[sizeof("k12.wv.us")];
- char stringpool_str5245[sizeof("for-better.biz")];
- char stringpool_str5246[sizeof("org.kz")];
- char stringpool_str5247[sizeof("koya.wakayama.jp")];
- char stringpool_str5248[sizeof("dyn-o-saur.com")];
- char stringpool_str5249[sizeof("oto.fukuoka.jp")];
- char stringpool_str5250[sizeof("miyazaki.miyazaki.jp")];
- char stringpool_str5251[sizeof("surgut.ru")];
- char stringpool_str5252[sizeof("naval.museum")];
- char stringpool_str5253[sizeof("is-an-anarchist.com")];
- char stringpool_str5254[sizeof("yokkaichi.mie.jp")];
- char stringpool_str5255[sizeof("ranzan.saitama.jp")];
- char stringpool_str5256[sizeof("xn--tjme-hra.no")];
- char stringpool_str5257[sizeof("xn--srfold-bya.no")];
- char stringpool_str5258[sizeof("xn--brnnysund-m8ac.no")];
- char stringpool_str5259[sizeof("xn--aroport-bya.ci")];
- char stringpool_str5260[sizeof("is-a-liberal.com")];
- char stringpool_str5261[sizeof("is-a-landscaper.com")];
- char stringpool_str5262[sizeof("medecin.fr")];
- char stringpool_str5263[sizeof("tokushima.tokushima.jp")];
- char stringpool_str5264[sizeof("shimogo.fukushima.jp")];
- char stringpool_str5265[sizeof("on-the-web.tv")];
- char stringpool_str5266[sizeof("omitama.ibaraki.jp")];
- char stringpool_str5267[sizeof("assedic.fr")];
- char stringpool_str5268[sizeof("tateshina.nagano.jp")];
- char stringpool_str5269[sizeof("lodingen.no")];
- char stringpool_str5270[sizeof("tsuiki.fukuoka.jp")];
- char stringpool_str5271[sizeof("asso.nc")];
- char stringpool_str5272[sizeof("miyazu.kyoto.jp")];
- char stringpool_str5273[sizeof("xn--snase-nra.no")];
- char stringpool_str5274[sizeof("xn--nqv7fs00ema")];
- char stringpool_str5275[sizeof("go.pw")];
- char stringpool_str5276[sizeof("ed.pw")];
- char stringpool_str5277[sizeof("co.pw")];
- char stringpool_str5278[sizeof("xn--flor-jra.no")];
- char stringpool_str5279[sizeof("dyndns.biz")];
- char stringpool_str5280[sizeof("mil.cn")];
- char stringpool_str5281[sizeof("north.museum")];
- char stringpool_str5282[sizeof("xn--jrpeland-54a.no")];
- char stringpool_str5283[sizeof("buyshouses.net")];
- char stringpool_str5284[sizeof("ono.fukushima.jp")];
- char stringpool_str5285[sizeof("kawaminami.miyazaki.jp")];
- char stringpool_str5286[sizeof("tsuru.yamanashi.jp")];
- char stringpool_str5287[sizeof("xn--jlster-bya.no")];
- char stringpool_str5288[sizeof("mochizuki.nagano.jp")];
- char stringpool_str5289[sizeof("servebbs.com")];
- char stringpool_str5290[sizeof("xn--srreisa-q1a.no")];
- char stringpool_str5291[sizeof("jelenia-gora.pl")];
- char stringpool_str5292[sizeof("net.lv")];
- char stringpool_str5293[sizeof("conference.aero")];
- char stringpool_str5294[sizeof("taiki.mie.jp")];
- char stringpool_str5295[sizeof("is-an-accountant.com")];
- char stringpool_str5296[sizeof("is-a-player.com")];
- char stringpool_str5297[sizeof("oregontrail.museum")];
- char stringpool_str5298[sizeof("lans.museum")];
- char stringpool_str5299[sizeof("from-sc.com")];
- char stringpool_str5300[sizeof("ohi.fukui.jp")];
- char stringpool_str5301[sizeof("sado.niigata.jp")];
- char stringpool_str5302[sizeof("servebbs.net")];
- char stringpool_str5303[sizeof("higashishirakawa.gifu.jp")];
- char stringpool_str5304[sizeof("valer.hedmark.no")];
- char stringpool_str5305[sizeof("is-a-libertarian.com")];
- char stringpool_str5306[sizeof("store.bb")];
- char stringpool_str5307[sizeof("xn--snsa-roa.no")];
- char stringpool_str5308[sizeof("council.aero")];
- char stringpool_str5309[sizeof("vanylven.no")];
- char stringpool_str5310[sizeof("xn--rmskog-bya.no")];
- char stringpool_str5311[sizeof("is-uberleet.com")];
- char stringpool_str5312[sizeof("from-dc.com")];
- char stringpool_str5313[sizeof("xn--vegrshei-c0a.no")];
- char stringpool_str5314[sizeof("takahama.fukui.jp")];
- char stringpool_str5315[sizeof("paragliding.aero")];
- char stringpool_str5316[sizeof("kayabe.hokkaido.jp")];
- char stringpool_str5317[sizeof("leksvik.no")];
- char stringpool_str5318[sizeof("takagi.nagano.jp")];
- char stringpool_str5319[sizeof("betainabox.com")];
- char stringpool_str5320[sizeof("ogawa.saitama.jp")];
- char stringpool_str5321[sizeof("okoppe.hokkaido.jp")];
- char stringpool_str5322[sizeof("for-our.info")];
- char stringpool_str5323[sizeof("googlecode.com")];
- char stringpool_str5324[sizeof("takasago.hyogo.jp")];
- char stringpool_str5325[sizeof("manx.museum")];
- char stringpool_str5326[sizeof("net.mv")];
- char stringpool_str5327[sizeof("dnsdojo.net")];
- char stringpool_str5328[sizeof("shiriuchi.hokkaido.jp")];
- char stringpool_str5329[sizeof("nnov.ru")];
- char stringpool_str5330[sizeof("kawazu.shizuoka.jp")];
- char stringpool_str5331[sizeof("dyndns-remote.com")];
- char stringpool_str5332[sizeof("xn--snes-poa.no")];
- char stringpool_str5333[sizeof("takayama.gifu.jp")];
- char stringpool_str5334[sizeof("dnsdojo.com")];
- char stringpool_str5335[sizeof("xn--lgrd-poac.no")];
- char stringpool_str5336[sizeof("pr.us")];
- char stringpool_str5337[sizeof("agency")];
- char stringpool_str5338[sizeof("xn--dnna-gra.no")];
- char stringpool_str5339[sizeof("hayakawa.yamanashi.jp")];
- char stringpool_str5340[sizeof("xn--finny-yua.no")];
- char stringpool_str5341[sizeof("pskov.ru")];
- char stringpool_str5342[sizeof("ujiie.tochigi.jp")];
- char stringpool_str5343[sizeof("academy.museum")];
- char stringpool_str5344[sizeof("name.vn")];
- char stringpool_str5345[sizeof("tsuno.miyazaki.jp")];
- char stringpool_str5346[sizeof("judygarland.museum")];
- char stringpool_str5347[sizeof("living.museum")];
- char stringpool_str5348[sizeof("dyndns-pics.com")];
- char stringpool_str5349[sizeof("kiyama.saga.jp")];
- char stringpool_str5350[sizeof("from-ny.net")];
- char stringpool_str5351[sizeof("suwalki.pl")];
- char stringpool_str5352[sizeof("s3-fips-us-gov-west-1.amazonaws.com")];
- char stringpool_str5353[sizeof("pb.ao")];
- char stringpool_str5354[sizeof("tajimi.gifu.jp")];
- char stringpool_str5355[sizeof("xn--ksnes-uua.no")];
- char stringpool_str5356[sizeof("ac.jp")];
- char stringpool_str5357[sizeof("dyndns-home.com")];
- char stringpool_str5358[sizeof("takarazuka.hyogo.jp")];
- char stringpool_str5359[sizeof("birthplace.museum")];
- char stringpool_str5360[sizeof("takata.fukuoka.jp")];
- char stringpool_str5361[sizeof("os.hordaland.no")];
- char stringpool_str5362[sizeof("is-very-evil.org")];
- char stringpool_str5363[sizeof("stavern.no")];
- char stringpool_str5364[sizeof("fujikawaguchiko.yamanashi.jp")];
- char stringpool_str5365[sizeof("mibu.tochigi.jp")];
- char stringpool_str5366[sizeof("xn--slt-elab.no")];
- char stringpool_str5367[sizeof("tokyo.jp")];
- char stringpool_str5368[sizeof("endofinternet.net")];
- char stringpool_str5369[sizeof("bato.tochigi.jp")];
- char stringpool_str5370[sizeof("skanit.no")];
- char stringpool_str5371[sizeof("culturalcenter.museum")];
- char stringpool_str5372[sizeof("intelligence.museum")];
- char stringpool_str5373[sizeof("isteingeek.de")];
- char stringpool_str5374[sizeof("company")];
- char stringpool_str5375[sizeof("sarufutsu.hokkaido.jp")];
- char stringpool_str5376[sizeof("government.aero")];
- char stringpool_str5377[sizeof("localhistory.museum")];
- char stringpool_str5378[sizeof("xn--lgbbat1ad8j")];
- char stringpool_str5379[sizeof("haga.tochigi.jp")];
- char stringpool_str5380[sizeof("pulawy.pl")];
- char stringpool_str5381[sizeof("pe.kr")];
- char stringpool_str5382[sizeof("xn--blt-elab.no")];
- char stringpool_str5383[sizeof("lib.tx.us")];
- char stringpool_str5384[sizeof("ascolipiceno.it")];
- char stringpool_str5385[sizeof("pa.us")];
- char stringpool_str5386[sizeof("from-nv.com")];
- char stringpool_str5387[sizeof("pl.ua")];
- char stringpool_str5388[sizeof("blogdns.net")];
- char stringpool_str5389[sizeof("pp.az")];
- char stringpool_str5390[sizeof("oki.fukuoka.jp")];
- char stringpool_str5391[sizeof("museumcenter.museum")];
- char stringpool_str5392[sizeof("vibo-valentia.it")];
- char stringpool_str5393[sizeof("stavropol.ru")];
- char stringpool_str5394[sizeof("from-nj.com")];
- char stringpool_str5395[sizeof("bahcavuotna.no")];
- char stringpool_str5396[sizeof("services.aero")];
- char stringpool_str5397[sizeof("vf.no")];
- char stringpool_str5398[sizeof("blogdns.com")];
- char stringpool_str5399[sizeof("matsudo.chiba.jp")];
- char stringpool_str5400[sizeof("daiwa.hiroshima.jp")];
- char stringpool_str5401[sizeof("graphics")];
- char stringpool_str5402[sizeof("karuizawa.nagano.jp")];
- char stringpool_str5403[sizeof("tokke.no")];
- char stringpool_str5404[sizeof("domains")];
- char stringpool_str5405[sizeof("tamba.hyogo.jp")];
- char stringpool_str5406[sizeof("pp.ru")];
- char stringpool_str5407[sizeof("pp.se")];
- char stringpool_str5408[sizeof("sevastopol.ua")];
- char stringpool_str5409[sizeof("sebastopol.ua")];
- char stringpool_str5410[sizeof("lacaixa")];
- char stringpool_str5411[sizeof("is-an-entertainer.com")];
- char stringpool_str5412[sizeof("is-gone.com")];
- char stringpool_str5413[sizeof("ogimi.okinawa.jp")];
- char stringpool_str5414[sizeof("vladivostok.ru")];
- char stringpool_str5415[sizeof("yuu.yamaguchi.jp")];
- char stringpool_str5416[sizeof("oshima.yamaguchi.jp")];
- char stringpool_str5417[sizeof("dyndns-mail.com")];
- char stringpool_str5418[sizeof("from-nm.com")];
- char stringpool_str5419[sizeof("tr.no")];
- char stringpool_str5420[sizeof("pe.ca")];
- char stringpool_str5421[sizeof("sannan.hyogo.jp")];
- char stringpool_str5422[sizeof("tn.us")];
- char stringpool_str5423[sizeof("malatvuopmi.no")];
- char stringpool_str5424[sizeof("servebbs.org")];
- char stringpool_str5425[sizeof("katsuyama.fukui.jp")];
- char stringpool_str5426[sizeof("uryu.hokkaido.jp")];
- char stringpool_str5427[sizeof("ltd.gi")];
- char stringpool_str5428[sizeof("og.it")];
- char stringpool_str5429[sizeof("red.sv")];
- char stringpool_str5430[sizeof("toki.gifu.jp")];
- char stringpool_str5431[sizeof("olbia-tempio.it")];
- char stringpool_str5432[sizeof("kiyosu.aichi.jp")];
- char stringpool_str5433[sizeof("xn--brnny-wuac.no")];
- char stringpool_str5434[sizeof("from-ne.com")];
- char stringpool_str5435[sizeof("pp.ua")];
- char stringpool_str5436[sizeof("xn--mosjen-eya.no")];
- char stringpool_str5437[sizeof("nasushiobara.tochigi.jp")];
- char stringpool_str5438[sizeof("xn--mgbbh1a71e")];
- char stringpool_str5439[sizeof("is-a-designer.com")];
- char stringpool_str5440[sizeof("xn--mgberp4a5d4ar")];
- char stringpool_str5441[sizeof("owariasahi.aichi.jp")];
- char stringpool_str5442[sizeof("tm.no")];
- char stringpool_str5443[sizeof("tm.ro")];
- char stringpool_str5444[sizeof("bremanger.no")];
- char stringpool_str5445[sizeof("tohma.hokkaido.jp")];
- char stringpool_str5446[sizeof("tm.se")];
- char stringpool_str5447[sizeof("dyndns-ip.com")];
- char stringpool_str5448[sizeof("flatanger.no")];
- char stringpool_str5449[sizeof("gratangen.no")];
- char stringpool_str5450[sizeof("xn--sgne-gra.no")];
- char stringpool_str5451[sizeof("from-nh.com")];
- char stringpool_str5452[sizeof("tm.fr")];
- char stringpool_str5453[sizeof("campidano-medio.it")];
- char stringpool_str5454[sizeof("tv.sd")];
- char stringpool_str5455[sizeof("tv.tz")];
- char stringpool_str5456[sizeof("nc.us")];
- char stringpool_str5457[sizeof("is-a-bookkeeper.com")];
- char stringpool_str5458[sizeof("xn--lns-qla.museum")];
- char stringpool_str5459[sizeof("shirakawa.fukushima.jp")];
- char stringpool_str5460[sizeof("yamatsuri.fukushima.jp")];
- char stringpool_str5461[sizeof("tj.cn")];
- char stringpool_str5462[sizeof("lib.ga.us")];
- char stringpool_str5463[sizeof("te.ua")];
- char stringpool_str5464[sizeof("xn--o3cw4h")];
- char stringpool_str5465[sizeof("tonosho.kagawa.jp")];
- char stringpool_str5466[sizeof("org.gn")];
- char stringpool_str5467[sizeof("xn--mgbqly7cvafr")];
- char stringpool_str5468[sizeof("xn--ygarden-p1a.no")];
- char stringpool_str5469[sizeof("tenei.fukushima.jp")];
- char stringpool_str5470[sizeof("xn--skierv-uta.no")];
- char stringpool_str5471[sizeof("nsw.edu.au")];
- char stringpool_str5472[sizeof("takino.hyogo.jp")];
- char stringpool_str5473[sizeof("okagaki.fukuoka.jp")];
- char stringpool_str5474[sizeof("aeroclub.aero")];
- char stringpool_str5475[sizeof("is-a-student.com")];
- char stringpool_str5476[sizeof("oyamazaki.kyoto.jp")];
- char stringpool_str5477[sizeof("miyoshi.saitama.jp")];
- char stringpool_str5478[sizeof("state.museum")];
- char stringpool_str5479[sizeof("miyakonojo.miyazaki.jp")];
- char stringpool_str5480[sizeof("stadt.museum")];
- char stringpool_str5481[sizeof("olkusz.pl")];
- char stringpool_str5482[sizeof("yamaguchi.jp")];
- char stringpool_str5483[sizeof("livinghistory.museum")];
- char stringpool_str5484[sizeof("miyashiro.saitama.jp")];
- char stringpool_str5485[sizeof("joetsu.niigata.jp")];
- char stringpool_str5486[sizeof("mitou.yamaguchi.jp")];
- char stringpool_str5487[sizeof("is-a-financialadvisor.com")];
- char stringpool_str5488[sizeof("aerobatic.aero")];
- char stringpool_str5489[sizeof("database.museum")];
- char stringpool_str5490[sizeof("steam.museum")];
- char stringpool_str5491[sizeof("niigata.niigata.jp")];
- char stringpool_str5492[sizeof("botanicgarden.museum")];
- char stringpool_str5493[sizeof("sch.ly")];
- char stringpool_str5494[sizeof("xn--laheadju-7ya.no")];
- char stringpool_str5495[sizeof("denmark.museum")];
- char stringpool_str5496[sizeof("tm.hu")];
- char stringpool_str5497[sizeof("xn--skjervy-v1a.no")];
- char stringpool_str5498[sizeof("jamal.ru")];
- char stringpool_str5499[sizeof("penza.ru")];
- char stringpool_str5500[sizeof("is-certified.com")];
- char stringpool_str5501[sizeof("pf")];
- char stringpool_str5502[sizeof("tm.km")];
- char stringpool_str5503[sizeof("gliding.aero")];
- char stringpool_str5504[sizeof("shimonita.gunma.jp")];
- char stringpool_str5505[sizeof("dyndns-at-home.com")];
- char stringpool_str5506[sizeof("tv.bo")];
- char stringpool_str5507[sizeof("tv.na")];
- char stringpool_str5508[sizeof("usdecorativearts.museum")];
- char stringpool_str5509[sizeof("tc")];
- char stringpool_str5510[sizeof("tv.br")];
- char stringpool_str5511[sizeof("xn--b-5ga.telemark.no")];
- char stringpool_str5512[sizeof("airline.aero")];
- char stringpool_str5513[sizeof("yekaterinburg.ru")];
- char stringpool_str5514[sizeof("xn--unup4y")];
- char stringpool_str5515[sizeof("taito.tokyo.jp")];
- char stringpool_str5516[sizeof("agematsu.nagano.jp")];
- char stringpool_str5517[sizeof("heroy.more-og-romsdal.no")];
- char stringpool_str5518[sizeof("lorenskog.no")];
- char stringpool_str5519[sizeof("andria-barletta-trani.it")];
- char stringpool_str5520[sizeof("togitsu.nagasaki.jp")];
- char stringpool_str5521[sizeof("caltanissetta.it")];
- char stringpool_str5522[sizeof("prd.fr")];
- char stringpool_str5523[sizeof("huissier-justice.fr")];
- char stringpool_str5524[sizeof("shell.museum")];
- char stringpool_str5525[sizeof("ballangen.no")];
- char stringpool_str5526[sizeof("yamanobe.yamagata.jp")];
- char stringpool_str5527[sizeof("kawaue.gifu.jp")];
- char stringpool_str5528[sizeof("westfalen.museum")];
- char stringpool_str5529[sizeof("serveftp.net")];
- char stringpool_str5530[sizeof("xn--mlselv-iua.no")];
- char stringpool_str5531[sizeof("space.museum")];
- char stringpool_str5532[sizeof("xn--frna-woa.no")];
- char stringpool_str5533[sizeof("vc.it")];
- char stringpool_str5534[sizeof("yukuhashi.fukuoka.jp")];
- char stringpool_str5535[sizeof("tokigawa.saitama.jp")];
- char stringpool_str5536[sizeof("tadotsu.kagawa.jp")];
- char stringpool_str5537[sizeof("po.it")];
- char stringpool_str5538[sizeof("pd.it")];
- char stringpool_str5539[sizeof("pz.it")];
- char stringpool_str5540[sizeof("shimizu.shizuoka.jp")];
- char stringpool_str5541[sizeof("dominic.ua")];
- char stringpool_str5542[sizeof("pr.it")];
- char stringpool_str5543[sizeof("salvadordali.museum")];
- char stringpool_str5544[sizeof("ac.ug")];
- char stringpool_str5545[sizeof("sowa.ibaraki.jp")];
- char stringpool_str5546[sizeof("wallonie.museum")];
- char stringpool_str5547[sizeof("pu.it")];
- char stringpool_str5548[sizeof("kobierzyce.pl")];
- char stringpool_str5549[sizeof("kiyosato.hokkaido.jp")];
- char stringpool_str5550[sizeof("pn.it")];
- char stringpool_str5551[sizeof("ddr.museum")];
- char stringpool_str5552[sizeof("pe.it")];
- char stringpool_str5553[sizeof("gs.jan-mayen.no")];
- char stringpool_str5554[sizeof("pt.it")];
- char stringpool_str5555[sizeof("does-it.net")];
- char stringpool_str5556[sizeof("taiki.hokkaido.jp")];
- char stringpool_str5557[sizeof("kawasaki.miyagi.jp")];
- char stringpool_str5558[sizeof("skole.museum")];
- char stringpool_str5559[sizeof("salem.museum")];
- char stringpool_str5560[sizeof("pi.it")];
- char stringpool_str5561[sizeof("suifu.ibaraki.jp")];
- char stringpool_str5562[sizeof("net.gt")];
- char stringpool_str5563[sizeof("taira.toyama.jp")];
- char stringpool_str5564[sizeof("medical.museum")];
- char stringpool_str5565[sizeof("shiranuka.hokkaido.jp")];
- char stringpool_str5566[sizeof("hayashima.okayama.jp")];
- char stringpool_str5567[sizeof("sanagochi.tokushima.jp")];
- char stringpool_str5568[sizeof("sodegaura.chiba.jp")];
- char stringpool_str5569[sizeof("is-a-conservative.com")];
- char stringpool_str5570[sizeof("tf")];
- char stringpool_str5571[sizeof("christiansburg.museum")];
- char stringpool_str5572[sizeof("leangaviika.no")];
- char stringpool_str5573[sizeof("nf.ca")];
- char stringpool_str5574[sizeof("lavagis.no")];
- char stringpool_str5575[sizeof("watch-and-clock.museum")];
- char stringpool_str5576[sizeof("royrvik.no")];
- char stringpool_str5577[sizeof("xn--vre-eiker-k8a.no")];
- char stringpool_str5578[sizeof("pa.it")];
- char stringpool_str5579[sizeof("plc.co.im")];
- char stringpool_str5580[sizeof("kawagoe.mie.jp")];
- char stringpool_str5581[sizeof("issmarterthanyou.com")];
- char stringpool_str5582[sizeof("omotego.fukushima.jp")];
- char stringpool_str5583[sizeof("pv.it")];
- char stringpool_str5584[sizeof("takatsuki.shiga.jp")];
- char stringpool_str5585[sizeof("podlasie.pl")];
- char stringpool_str5586[sizeof("pesaro-urbino.it")];
- char stringpool_str5587[sizeof("miyagi.jp")];
- char stringpool_str5588[sizeof("nayoro.hokkaido.jp")];
- char stringpool_str5589[sizeof("is-a-lawyer.com")];
- char stringpool_str5590[sizeof("airport.aero")];
- char stringpool_str5591[sizeof("air-surveillance.aero")];
- char stringpool_str5592[sizeof("to.it")];
- char stringpool_str5593[sizeof("ts.it")];
- char stringpool_str5594[sizeof("tgory.pl")];
- char stringpool_str5595[sizeof("tr.it")];
- char stringpool_str5596[sizeof("delaware.museum")];
- char stringpool_str5597[sizeof("xn--xkc2dl3a5ee0h")];
- char stringpool_str5598[sizeof("taka.hyogo.jp")];
- char stringpool_str5599[sizeof("xn--sknland-fxa.no")];
- char stringpool_str5600[sizeof("tw.cn")];
- char stringpool_str5601[sizeof("carboniaiglesias.it")];
- char stringpool_str5602[sizeof("supply")];
- char stringpool_str5603[sizeof("tn.it")];
- char stringpool_str5604[sizeof("takashima.shiga.jp")];
- char stringpool_str5605[sizeof("te.it")];
- char stringpool_str5606[sizeof("shinanomachi.nagano.jp")];
- char stringpool_str5607[sizeof("lib.gu.us")];
- char stringpool_str5608[sizeof("boleslawiec.pl")];
- char stringpool_str5609[sizeof("kawara.fukuoka.jp")];
- char stringpool_str5610[sizeof("pro.vn")];
- char stringpool_str5611[sizeof("takahata.yamagata.jp")];
- char stringpool_str5612[sizeof("kawakita.ishikawa.jp")];
- char stringpool_str5613[sizeof("kawanishi.hyogo.jp")];
- char stringpool_str5614[sizeof("dyndns-web.com")];
- char stringpool_str5615[sizeof("oguni.yamagata.jp")];
- char stringpool_str5616[sizeof("kashiwa.chiba.jp")];
- char stringpool_str5617[sizeof("shimizu.hokkaido.jp")];
- char stringpool_str5618[sizeof("shakotan.hokkaido.jp")];
- char stringpool_str5619[sizeof("achi.nagano.jp")];
- char stringpool_str5620[sizeof("xn--hbmer-xqa.no")];
- char stringpool_str5621[sizeof("tt.im")];
- char stringpool_str5622[sizeof("takko.aomori.jp")];
- char stringpool_str5623[sizeof("org.st")];
- char stringpool_str5624[sizeof("serveftp.org")];
- char stringpool_str5625[sizeof("is-a-geek.net")];
- char stringpool_str5626[sizeof("pordenone.it")];
- char stringpool_str5627[sizeof("xn--xkc2al3hye2a")];
- char stringpool_str5628[sizeof("miyoshi.hiroshima.jp")];
- char stringpool_str5629[sizeof("shijonawate.osaka.jp")];
- char stringpool_str5630[sizeof("otsuchi.iwate.jp")];
- char stringpool_str5631[sizeof("yoshinogari.saga.jp")];
- char stringpool_str5632[sizeof("ta.it")];
- char stringpool_str5633[sizeof("is-a-geek.com")];
- char stringpool_str5634[sizeof("news.hu")];
- char stringpool_str5635[sizeof("org.tt")];
- char stringpool_str5636[sizeof("accident-prevention.aero")];
- char stringpool_str5637[sizeof("tp.it")];
- char stringpool_str5638[sizeof("yazu.tottori.jp")];
- char stringpool_str5639[sizeof("time.no")];
- char stringpool_str5640[sizeof("artcenter.museum")];
- char stringpool_str5641[sizeof("tv.it")];
- char stringpool_str5642[sizeof("is-a-nurse.com")];
- char stringpool_str5643[sizeof("org.bt")];
- char stringpool_str5644[sizeof("detroit.museum")];
- char stringpool_str5645[sizeof("okazaki.aichi.jp")];
- char stringpool_str5646[sizeof("masaki.ehime.jp")];
- char stringpool_str5647[sizeof("moka.tochigi.jp")];
- char stringpool_str5648[sizeof("tinn.no")];
- char stringpool_str5649[sizeof("xn--sr-varanger-ggb.no")];
- char stringpool_str5650[sizeof("poznan.pl")];
- char stringpool_str5651[sizeof("katowice.pl")];
- char stringpool_str5652[sizeof("kawagoe.saitama.jp")];
- char stringpool_str5653[sizeof("youth.museum")];
- char stringpool_str5654[sizeof("dyndns-at-work.com")];
- char stringpool_str5655[sizeof("z-2.compute-1.amazonaws.com")];
- char stringpool_str5656[sizeof("ichinomiya.aichi.jp")];
- char stringpool_str5657[sizeof("whaling.museum")];
- char stringpool_str5658[sizeof("org.mt")];
- char stringpool_str5659[sizeof("xn--holtlen-hxa.no")];
- char stringpool_str5660[sizeof("palace.museum")];
- char stringpool_str5661[sizeof("newmexico.museum")];
- char stringpool_str5662[sizeof("tv.im")];
- char stringpool_str5663[sizeof("tana.no")];
- char stringpool_str5664[sizeof("indiana.museum")];
- char stringpool_str5665[sizeof("xn--node")];
- char stringpool_str5666[sizeof("dyndns-wiki.com")];
- char stringpool_str5667[sizeof("kvanangen.no")];
- char stringpool_str5668[sizeof("delmenhorst.museum")];
- char stringpool_str5669[sizeof("tyumen.ru")];
- char stringpool_str5670[sizeof("sc.tz")];
- char stringpool_str5671[sizeof("org.pt")];
- char stringpool_str5672[sizeof("sc.us")];
- char stringpool_str5673[sizeof("net.cn")];
- char stringpool_str5674[sizeof("piacenza.it")];
- char stringpool_str5675[sizeof("kyotanabe.kyoto.jp")];
- char stringpool_str5676[sizeof("miyoshi.tokushima.jp")];
- char stringpool_str5677[sizeof("porsangu.no")];
- char stringpool_str5678[sizeof("yawata.kyoto.jp")];
- char stringpool_str5679[sizeof("dynathome.net")];
- char stringpool_str5680[sizeof("shirakawa.gifu.jp")];
- char stringpool_str5681[sizeof("echizen.fukui.jp")];
- char stringpool_str5682[sizeof("org.ht")];
- char stringpool_str5683[sizeof("sc.cn")];
- char stringpool_str5684[sizeof("voyage")];
- char stringpool_str5685[sizeof("oamishirasato.chiba.jp")];
- char stringpool_str5686[sizeof("xn--rhkkervju-01af.no")];
- char stringpool_str5687[sizeof("daisen.akita.jp")];
- char stringpool_str5688[sizeof("sc.kr")];
- char stringpool_str5689[sizeof("is-very-good.org")];
- char stringpool_str5690[sizeof("tx.us")];
- char stringpool_str5691[sizeof("toba.mie.jp")];
- char stringpool_str5692[sizeof("dyndns-work.com")];
- char stringpool_str5693[sizeof("xn--bidr-5nac.no")];
- char stringpool_str5694[sizeof("urayasu.chiba.jp")];
- char stringpool_str5695[sizeof("pharmacy.museum")];
- char stringpool_str5696[sizeof("xn--bhcavuotna-s4a.no")];
- char stringpool_str5697[sizeof("sf.no")];
- char stringpool_str5698[sizeof("linz.museum")];
- char stringpool_str5699[sizeof("tanagura.fukushima.jp")];
- char stringpool_str5700[sizeof("sor-fron.no")];
- char stringpool_str5701[sizeof("loten.no")];
- char stringpool_str5702[sizeof("minamiechizen.fukui.jp")];
- char stringpool_str5703[sizeof("skierva.no")];
- char stringpool_str5704[sizeof("trolley.museum")];
- char stringpool_str5705[sizeof("yawara.ibaraki.jp")];
- char stringpool_str5706[sizeof("thruhere.net")];
- char stringpool_str5707[sizeof("lecco.it")];
- char stringpool_str5708[sizeof("nowaruda.pl")];
- char stringpool_str5709[sizeof("uscountryestate.museum")];
- char stringpool_str5710[sizeof("ne.pw")];
- char stringpool_str5711[sizeof("rc.it")];
- char stringpool_str5712[sizeof("bryne.no")];
- char stringpool_str5713[sizeof("miyama.fukuoka.jp")];
- char stringpool_str5714[sizeof("lecce.it")];
- char stringpool_str5715[sizeof("luroy.no")];
- char stringpool_str5716[sizeof("ogawa.nagano.jp")];
- char stringpool_str5717[sizeof("taiji.wakayama.jp")];
- char stringpool_str5718[sizeof("takamatsu.kagawa.jp")];
- char stringpool_str5719[sizeof("shikatsu.aichi.jp")];
- char stringpool_str5720[sizeof("xn--ldingen-q1a.no")];
- char stringpool_str5721[sizeof("tado.mie.jp")];
- char stringpool_str5722[sizeof("phoenix.museum")];
- char stringpool_str5723[sizeof("decorativearts.museum")];
- char stringpool_str5724[sizeof("hitachi.ibaraki.jp")];
- char stringpool_str5725[sizeof("kawanishi.yamagata.jp")];
- char stringpool_str5726[sizeof("tono.iwate.jp")];
- char stringpool_str5727[sizeof("tendo.yamagata.jp")];
- char stringpool_str5728[sizeof("xn--c1avg")];
- char stringpool_str5729[sizeof("skjervoy.no")];
- char stringpool_str5730[sizeof("lucca.it")];
- char stringpool_str5731[sizeof("ichinohe.iwate.jp")];
- char stringpool_str5732[sizeof("exchange.aero")];
- char stringpool_str5733[sizeof("miyako.fukuoka.jp")];
- char stringpool_str5734[sizeof("parliament.uk")];
- char stringpool_str5735[sizeof("naturalsciences.museum")];
- char stringpool_str5736[sizeof("shibata.niigata.jp")];
- char stringpool_str5737[sizeof("shibetsu.hokkaido.jp")];
- char stringpool_str5738[sizeof("properties")];
- char stringpool_str5739[sizeof("tomigusuku.okinawa.jp")];
- char stringpool_str5740[sizeof("trustee.museum")];
- char stringpool_str5741[sizeof("pol.dz")];
- char stringpool_str5742[sizeof("co.ve")];
- char stringpool_str5743[sizeof("dontexist.net")];
- char stringpool_str5744[sizeof("theater.museum")];
- char stringpool_str5745[sizeof("kawai.nara.jp")];
- char stringpool_str5746[sizeof("dontexist.com")];
- char stringpool_str5747[sizeof("nasu.tochigi.jp")];
- char stringpool_str5748[sizeof("kawamata.fukushima.jp")];
- char stringpool_str5749[sizeof("co.vi")];
- char stringpool_str5750[sizeof("hu.net")];
- char stringpool_str5751[sizeof("js.cn")];
- char stringpool_str5752[sizeof("xn--h1aegh.museum")];
- char stringpool_str5753[sizeof("jl.cn")];
- char stringpool_str5754[sizeof("zgorzelec.pl")];
- char stringpool_str5755[sizeof("pharmaciens.km")];
- char stringpool_str5756[sizeof("pro.az")];
- char stringpool_str5757[sizeof("xn--sandnessjen-ogb.no")];
- char stringpool_str5758[sizeof("niepce.museum")];
- char stringpool_str5759[sizeof("gb.net")];
- char stringpool_str5760[sizeof("tranibarlettaandria.it")];
- char stringpool_str5761[sizeof("xn--tn0ag.hk")];
- char stringpool_str5762[sizeof("potenza.it")];
- char stringpool_str5763[sizeof("otago.museum")];
- char stringpool_str5764[sizeof("tempioolbia.it")];
- char stringpool_str5765[sizeof("sakai.ibaraki.jp")];
- char stringpool_str5766[sizeof("traniandriabarletta.it")];
- char stringpool_str5767[sizeof("xn--andy-ira.no")];
- char stringpool_str5768[sizeof("sekigahara.gifu.jp")];
- char stringpool_str5769[sizeof("nikko.tochigi.jp")];
- char stringpool_str5770[sizeof("iglesiascarbonia.it")];
- char stringpool_str5771[sizeof("szczytno.pl")];
- char stringpool_str5772[sizeof("arkhangelsk.ru")];
- char stringpool_str5773[sizeof("endofinternet.org")];
- char stringpool_str5774[sizeof("xn--mgbc0a9azcg")];
- char stringpool_str5775[sizeof("miyawaka.fukuoka.jp")];
- char stringpool_str5776[sizeof("xn--unjrga-rta.no")];
- char stringpool_str5777[sizeof("is-a-hunter.com")];
- char stringpool_str5778[sizeof("is-an-engineer.com")];
- char stringpool_str5779[sizeof("shimotsuma.ibaraki.jp")];
- char stringpool_str5780[sizeof("sayama.osaka.jp")];
- char stringpool_str5781[sizeof("paroch.k12.ma.us")];
- char stringpool_str5782[sizeof("tobe.ehime.jp")];
- char stringpool_str5783[sizeof("za.net")];
- char stringpool_str5784[sizeof("tsukuba.ibaraki.jp")];
- char stringpool_str5785[sizeof("from-nd.com")];
- char stringpool_str5786[sizeof("toon.ehime.jp")];
- char stringpool_str5787[sizeof("is-a-chef.org")];
- char stringpool_str5788[sizeof("otoyo.kochi.jp")];
- char stringpool_str5789[sizeof("trainer.aero")];
- char stringpool_str5790[sizeof("lubin.pl")];
- char stringpool_str5791[sizeof("xn--porsgu-sta26f.no")];
- char stringpool_str5792[sizeof("diamonds")];
- char stringpool_str5793[sizeof("org.sv")];
- char stringpool_str5794[sizeof("kihoku.ehime.jp")];
- char stringpool_str5795[sizeof("org.lv")];
- char stringpool_str5796[sizeof("sayama.saitama.jp")];
- char stringpool_str5797[sizeof("ichinoseki.iwate.jp")];
- char stringpool_str5798[sizeof("tamaki.mie.jp")];
- char stringpool_str5799[sizeof("oharu.aichi.jp")];
- char stringpool_str5800[sizeof("oarai.ibaraki.jp")];
- char stringpool_str5801[sizeof("tatsuno.hyogo.jp")];
- char stringpool_str5802[sizeof("tabuse.yamaguchi.jp")];
- char stringpool_str5803[sizeof("org.mv")];
- char stringpool_str5804[sizeof("doomdns.org")];
- char stringpool_str5805[sizeof("uk.net")];
- char stringpool_str5806[sizeof("xn--ostery-fya.no")];
- char stringpool_str5807[sizeof("is-a-celticsfan.org")];
- char stringpool_str5808[sizeof("gs.sf.no")];
- char stringpool_str5809[sizeof("gs.of.no")];
- char stringpool_str5810[sizeof("xn--hnefoss-q1a.no")];
- char stringpool_str5811[sizeof("ap-northeast-1.compute.amazonaws.com")];
- char stringpool_str5812[sizeof("omaha.museum")];
- char stringpool_str5813[sizeof("stuff-4-sale.us")];
- char stringpool_str5814[sizeof("nogi.tochigi.jp")];
- char stringpool_str5815[sizeof("pomorze.pl")];
- char stringpool_str5816[sizeof("xn--mgberp4a5d4a87g")];
- char stringpool_str5817[sizeof("misconfused.org")];
- char stringpool_str5818[sizeof("xn--kranghke-b0a.no")];
- char stringpool_str5819[sizeof("tm.mg")];
- char stringpool_str5820[sizeof("schlesisches.museum")];
- char stringpool_str5821[sizeof("at-band-camp.net")];
- char stringpool_str5822[sizeof("lib.ny.us")];
- char stringpool_str5823[sizeof("xn--bhccavuotna-k7a.no")];
- char stringpool_str5824[sizeof("xn--krdsherad-m8a.no")];
- char stringpool_str5825[sizeof("satx.museum")];
- char stringpool_str5826[sizeof("artsandcrafts.museum")];
- char stringpool_str5827[sizeof("adygeya.ru")];
- char stringpool_str5828[sizeof("sosnowiec.pl")];
- char stringpool_str5829[sizeof("kimitsu.chiba.jp")];
- char stringpool_str5830[sizeof("izumizaki.fukushima.jp")];
- char stringpool_str5831[sizeof("textile.museum")];
- char stringpool_str5832[sizeof("eu.int")];
- char stringpool_str5833[sizeof("yaita.tochigi.jp")];
- char stringpool_str5834[sizeof("sells-for-less.com")];
- char stringpool_str5835[sizeof("servegame.org")];
- char stringpool_str5836[sizeof("xn--stjrdal-s1a.no")];
- char stringpool_str5837[sizeof("jgora.pl")];
- char stringpool_str5838[sizeof("jpn.com")];
- char stringpool_str5839[sizeof("tomi.nagano.jp")];
- char stringpool_str5840[sizeof("england.museum")];
- char stringpool_str5841[sizeof("finland.museum")];
- char stringpool_str5842[sizeof("s3.amazonaws.com")];
- char stringpool_str5843[sizeof("myphotos.cc")];
- char stringpool_str5844[sizeof("sano.tochigi.jp")];
- char stringpool_str5845[sizeof("tsukiyono.gunma.jp")];
- char stringpool_str5846[sizeof("mutsuzawa.chiba.jp")];
- char stringpool_str5847[sizeof("xn--hgebostad-g3a.no")];
- char stringpool_str5848[sizeof("luxury")];
- char stringpool_str5849[sizeof("from-tx.com")];
- char stringpool_str5850[sizeof("pg.it")];
- char stringpool_str5851[sizeof("schoenbrunn.museum")];
- char stringpool_str5852[sizeof("xn--oppegrd-ixa.no")];
- char stringpool_str5853[sizeof("olbiatempio.it")];
- char stringpool_str5854[sizeof("lib.ky.us")];
- char stringpool_str5855[sizeof("tsuwano.shimane.jp")];
- char stringpool_str5856[sizeof("from-tn.com")];
- char stringpool_str5857[sizeof("eu-west-1.compute.amazonaws.com")];
- char stringpool_str5858[sizeof("nakatsugawa.gifu.jp")];
- char stringpool_str5859[sizeof("xn--vler-qoa.xn--stfold-9xa.no")];
- char stringpool_str5860[sizeof("taiwa.miyagi.jp")];
- char stringpool_str5861[sizeof("tsuruga.fukui.jp")];
- char stringpool_str5862[sizeof("xn--rlingen-mxa.no")];
- char stringpool_str5863[sizeof("xn--lten-gra.no")];
- char stringpool_str5864[sizeof("design.aero")];
- char stringpool_str5865[sizeof("schokoladen.museum")];
- char stringpool_str5866[sizeof("lutsk.ua")];
- char stringpool_str5867[sizeof("us-west-2.compute.amazonaws.com")];
- char stringpool_str5868[sizeof("dagestan.ru")];
- char stringpool_str5869[sizeof("chippubetsu.hokkaido.jp")];
- char stringpool_str5870[sizeof("lesja.no")];
- char stringpool_str5871[sizeof("us-west-1.compute.amazonaws.com")];
- char stringpool_str5872[sizeof("computer")];
- char stringpool_str5873[sizeof("kawai.iwate.jp")];
- char stringpool_str5874[sizeof("sciencecenter.museum")];
- char stringpool_str5875[sizeof("ln.cn")];
- char stringpool_str5876[sizeof("sciencecenters.museum")];
- char stringpool_str5877[sizeof("ap-southeast-2.compute.amazonaws.com")];
- char stringpool_str5878[sizeof("of.no")];
- char stringpool_str5879[sizeof("journal.aero")];
- char stringpool_str5880[sizeof("jorpeland.no")];
- char stringpool_str5881[sizeof("ap-southeast-1.compute.amazonaws.com")];
- char stringpool_str5882[sizeof("juedisches.museum")];
- char stringpool_str5883[sizeof("guovdageaidnu.no")];
- char stringpool_str5884[sizeof("la.us")];
- char stringpool_str5885[sizeof("szex.hu")];
- char stringpool_str5886[sizeof("kiyokawa.kanagawa.jp")];
- char stringpool_str5887[sizeof("science.museum")];
- char stringpool_str5888[sizeof("jx.cn")];
- char stringpool_str5889[sizeof("donostia.museum")];
- char stringpool_str5890[sizeof("davvesiida.no")];
- char stringpool_str5891[sizeof("lt.ua")];
- char stringpool_str5892[sizeof("yachiyo.ibaraki.jp")];
- char stringpool_str5893[sizeof("lib.wy.us")];
- char stringpool_str5894[sizeof("hembygdsforbund.museum")];
- char stringpool_str5895[sizeof("cc.sd.us")];
- char stringpool_str5896[sizeof("cc.az.us")];
- char stringpool_str5897[sizeof("k12.ct.us")];
- char stringpool_str5898[sizeof("cc.as.us")];
- char stringpool_str5899[sizeof("cc.ar.us")];
- char stringpool_str5900[sizeof("org.gt")];
- char stringpool_str5901[sizeof("cc.or.us")];
- char stringpool_str5902[sizeof("cc.mo.us")];
- char stringpool_str5903[sizeof("cc.md.us")];
- char stringpool_str5904[sizeof("cc.al.us")];
- char stringpool_str5905[sizeof("cc.ms.us")];
- char stringpool_str5906[sizeof("project.museum")];
- char stringpool_str5907[sizeof("samnanger.no")];
- char stringpool_str5908[sizeof("cc.nd.us")];
- char stringpool_str5909[sizeof("okawa.kochi.jp")];
- char stringpool_str5910[sizeof("neyagawa.osaka.jp")];
- char stringpool_str5911[sizeof("cc.tn.us")];
- char stringpool_str5912[sizeof("tosashimizu.kochi.jp")];
- char stringpool_str5913[sizeof("lakas.hu")];
- char stringpool_str5914[sizeof("shirako.chiba.jp")];
- char stringpool_str5915[sizeof("judaica.museum")];
- char stringpool_str5916[sizeof("cc.gu.us")];
- char stringpool_str5917[sizeof("cc.mn.us")];
- char stringpool_str5918[sizeof("cc.co.us")];
- char stringpool_str5919[sizeof("cc.de.us")];
- char stringpool_str5920[sizeof("cc.ut.us")];
- char stringpool_str5921[sizeof("cc.me.us")];
- char stringpool_str5922[sizeof("ichikai.tochigi.jp")];
- char stringpool_str5923[sizeof("dazaifu.fukuoka.jp")];
- char stringpool_str5924[sizeof("cc.ne.us")];
- char stringpool_str5925[sizeof("cc.mt.us")];
- char stringpool_str5926[sizeof("tokoname.aichi.jp")];
- char stringpool_str5927[sizeof("cc.fl.us")];
- char stringpool_str5928[sizeof("xn--stjrdalshalsen-sqb.no")];
- char stringpool_str5929[sizeof("um.gov.pl")];
- char stringpool_str5930[sizeof("atlanta.museum")];
- char stringpool_str5931[sizeof("lv.ua")];
- char stringpool_str5932[sizeof("oyama.tochigi.jp")];
- char stringpool_str5933[sizeof("cc.mi.us")];
- char stringpool_str5934[sizeof("cc.ri.us")];
- char stringpool_str5935[sizeof("cc.ct.us")];
- char stringpool_str5936[sizeof("lc")];
- char stringpool_str5937[sizeof("design.museum")];
- char stringpool_str5938[sizeof("cc.ks.us")];
- char stringpool_str5939[sizeof("is-a-techie.com")];
- char stringpool_str5940[sizeof("www.ck")];
- char stringpool_str5941[sizeof("of.by")];
- char stringpool_str5942[sizeof("computer.museum")];
- char stringpool_str5943[sizeof("cc.ny.us")];
- char stringpool_str5944[sizeof("lukow.pl")];
- char stringpool_str5945[sizeof("cc.nj.us")];
- char stringpool_str5946[sizeof("cc.nm.us")];
- char stringpool_str5947[sizeof("jerusalem.museum")];
- char stringpool_str5948[sizeof("tachiarai.fukuoka.jp")];
- char stringpool_str5949[sizeof("cc.la.us")];
- char stringpool_str5950[sizeof("lomza.pl")];
- char stringpool_str5951[sizeof("cc.ma.us")];
- char stringpool_str5952[sizeof("coop.tt")];
- char stringpool_str5953[sizeof("schweiz.museum")];
- char stringpool_str5954[sizeof("cc.ga.us")];
- char stringpool_str5955[sizeof("sc.ug")];
- char stringpool_str5956[sizeof("barrel-of-knowledge.info")];
- char stringpool_str5957[sizeof("sande.more-og-romsdal.no")];
- char stringpool_str5958[sizeof("anpachi.gifu.jp")];
- char stringpool_str5959[sizeof("cc.ky.us")];
- char stringpool_str5960[sizeof("cc.nv.us")];
- char stringpool_str5961[sizeof("cc.ca.us")];
- char stringpool_str5962[sizeof("isumi.chiba.jp")];
- char stringpool_str5963[sizeof("pharmacien.fr")];
- char stringpool_str5964[sizeof("go.dyndns.org")];
- char stringpool_str5965[sizeof("cc.oh.us")];
- char stringpool_str5966[sizeof("cc.hi.us")];
- char stringpool_str5967[sizeof("cc.nh.us")];
- char stringpool_str5968[sizeof("shibata.miyagi.jp")];
- char stringpool_str5969[sizeof("is-into-anime.com")];
- char stringpool_str5970[sizeof("fukuchi.fukuoka.jp")];
- char stringpool_str5971[sizeof("taki.mie.jp")];
- char stringpool_str5972[sizeof("inzai.chiba.jp")];
- char stringpool_str5973[sizeof("cc.ak.us")];
- char stringpool_str5974[sizeof("cc.ok.us")];
- char stringpool_str5975[sizeof("depot.museum")];
- char stringpool_str5976[sizeof("dolls.museum")];
- char stringpool_str5977[sizeof("wa.gov.au")];
- char stringpool_str5978[sizeof("spydeberg.no")];
- char stringpool_str5979[sizeof("iwafune.tochigi.jp")];
- char stringpool_str5980[sizeof("taishin.fukushima.jp")];
- char stringpool_str5981[sizeof("lo.it")];
- char stringpool_str5982[sizeof("lib.co.us")];
- char stringpool_str5983[sizeof("ohira.tochigi.jp")];
- char stringpool_str5984[sizeof("student.aero")];
- char stringpool_str5985[sizeof("pro.tt")];
- char stringpool_str5986[sizeof("lu.it")];
- char stringpool_str5987[sizeof("s3-us-west-1.amazonaws.com")];
- char stringpool_str5988[sizeof("s3-us-west-2.amazonaws.com")];
- char stringpool_str5989[sizeof("asahi.chiba.jp")];
- char stringpool_str5990[sizeof("le.it")];
- char stringpool_str5991[sizeof("takanabe.miyazaki.jp")];
- char stringpool_str5992[sizeof("lt.it")];
- char stringpool_str5993[sizeof("lib.ca.us")];
- char stringpool_str5994[sizeof("iwakuni.yamaguchi.jp")];
- char stringpool_str5995[sizeof("s3-eu-west-1.amazonaws.com")];
- char stringpool_str5996[sizeof("org.cn")];
- char stringpool_str5997[sizeof("ltd.co.im")];
- char stringpool_str5998[sizeof("li.it")];
- char stringpool_str5999[sizeof("abiko.chiba.jp")];
- char stringpool_str6000[sizeof("london")];
- char stringpool_str6001[sizeof("seaport.museum")];
- char stringpool_str6002[sizeof("jogasz.hu")];
- char stringpool_str6003[sizeof("kiwa.mie.jp")];
- char stringpool_str6004[sizeof("lund.no")];
- char stringpool_str6005[sizeof("priv.pl")];
- char stringpool_str6006[sizeof("uw.gov.pl")];
- char stringpool_str6007[sizeof("tsuchiura.ibaraki.jp")];
- char stringpool_str6008[sizeof("starachowice.pl")];
- char stringpool_str6009[sizeof("lier.no")];
- char stringpool_str6010[sizeof("music.museum")];
- char stringpool_str6011[sizeof("from-va.com")];
- char stringpool_str6012[sizeof("lib.fl.us")];
- char stringpool_str6013[sizeof("pro.ht")];
- char stringpool_str6014[sizeof("cc.id.us")];
- char stringpool_str6015[sizeof("lebtimnetz.de")];
- char stringpool_str6016[sizeof("cc.il.us")];
- char stringpool_str6017[sizeof("tobishima.aichi.jp")];
- char stringpool_str6018[sizeof("shirataka.yamagata.jp")];
- char stringpool_str6019[sizeof("or.pw")];
- char stringpool_str6020[sizeof("cc.in.us")];
- char stringpool_str6021[sizeof("cc.wi.us")];
- char stringpool_str6022[sizeof("travel")];
- char stringpool_str6023[sizeof("architecture.museum")];
- char stringpool_str6024[sizeof("trogstad.no")];
- char stringpool_str6025[sizeof("kuwana.mie.jp")];
- char stringpool_str6026[sizeof("teaches-yoga.com")];
- char stringpool_str6027[sizeof("cc.wy.us")];
- char stringpool_str6028[sizeof("computerhistory.museum")];
- char stringpool_str6029[sizeof("lyngen.no")];
- char stringpool_str6030[sizeof("pol.ht")];
- char stringpool_str6031[sizeof("ostrowiec.pl")];
- char stringpool_str6032[sizeof("jamison.museum")];
- char stringpool_str6033[sizeof("daigo.ibaraki.jp")];
- char stringpool_str6034[sizeof("tynset.no")];
- char stringpool_str6035[sizeof("cc.wa.us")];
- char stringpool_str6036[sizeof("travel.pl")];
- char stringpool_str6037[sizeof("tatsuno.nagano.jp")];
- char stringpool_str6038[sizeof("joboji.iwate.jp")];
- char stringpool_str6039[sizeof("travel.tt")];
- char stringpool_str6040[sizeof("test.tj")];
- char stringpool_str6041[sizeof("livorno.it")];
- char stringpool_str6042[sizeof("cc.wv.us")];
- char stringpool_str6043[sizeof("xn--ryken-vua.no")];
- char stringpool_str6044[sizeof("cc.ia.us")];
- char stringpool_str6045[sizeof("lindesnes.no")];
- char stringpool_str6046[sizeof("priv.me")];
- char stringpool_str6047[sizeof("shunan.yamaguchi.jp")];
- char stringpool_str6048[sizeof("lib.tn.us")];
- char stringpool_str6049[sizeof("treviso.it")];
- char stringpool_str6050[sizeof("press.aero")];
- char stringpool_str6051[sizeof("press.se")];
- char stringpool_str6052[sizeof("scrapping.cc")];
- char stringpool_str6053[sizeof("leirfjord.no")];
- char stringpool_str6054[sizeof("tysvar.no")];
- char stringpool_str6055[sizeof("lib.in.us")];
- char stringpool_str6056[sizeof("harvestcelebration.museum")];
- char stringpool_str6057[sizeof("lib.mn.us")];
- char stringpool_str6058[sizeof("laquila.it")];
- char stringpool_str6059[sizeof("taranto.it")];
- char stringpool_str6060[sizeof("tsunan.niigata.jp")];
- char stringpool_str6061[sizeof("lahppi.no")];
- char stringpool_str6062[sizeof("bruxelles.museum")];
- char stringpool_str6063[sizeof("london.museum")];
- char stringpool_str6064[sizeof("xn--avery-yua.no")];
- char stringpool_str6065[sizeof("xn--ryrvik-bya.no")];
- char stringpool_str6066[sizeof("obanazawa.yamagata.jp")];
- char stringpool_str6067[sizeof("leitungsen.de")];
- char stringpool_str6068[sizeof("lincoln.museum")];
- char stringpool_str6069[sizeof("pilot.aero")];
- char stringpool_str6070[sizeof("parti.se")];
- char stringpool_str6071[sizeof("larsson.museum")];
- char stringpool_str6072[sizeof("cc.tx.us")];
- char stringpool_str6073[sizeof("press.museum")];
- char stringpool_str6074[sizeof("dc.us")];
- char stringpool_str6075[sizeof("luzern.museum")];
- char stringpool_str6076[sizeof("pubol.museum")];
- char stringpool_str6077[sizeof("ac.pr")];
- char stringpool_str6078[sizeof("towada.aomori.jp")];
- char stringpool_str6079[sizeof("oceanographic.museum")];
- char stringpool_str6080[sizeof("laakesvuemie.no")];
- char stringpool_str6081[sizeof("z-1.compute-1.amazonaws.com")];
- char stringpool_str6082[sizeof("monzaedellabrianza.it")];
- char stringpool_str6083[sizeof("for-more.biz")];
- char stringpool_str6084[sizeof("stuff-4-sale.org")];
- char stringpool_str6085[sizeof("tsaritsyn.ru")];
- char stringpool_str6086[sizeof("trieste.it")];
- char stringpool_str6087[sizeof("for-some.biz")];
- char stringpool_str6088[sizeof("xn--3e0b707e")];
- char stringpool_str6089[sizeof("priv.at")];
- char stringpool_str6090[sizeof("torahime.shiga.jp")];
- char stringpool_str6091[sizeof("from-vt.com")];
- char stringpool_str6092[sizeof("turystyka.pl")];
- char stringpool_str6093[sizeof("ac.pa")];
- char stringpool_str6094[sizeof("se.net")];
- char stringpool_str6095[sizeof("planetarium.museum")];
- char stringpool_str6096[sizeof("miyama.mie.jp")];
- char stringpool_str6097[sizeof("homeftp.org")];
- char stringpool_str6098[sizeof("omigawa.chiba.jp")];
- char stringpool_str6099[sizeof("xn--fiq64b")];
- char stringpool_str6100[sizeof("museumvereniging.museum")];
- char stringpool_str6101[sizeof("s3-ap-northeast-1.amazonaws.com")];
- char stringpool_str6102[sizeof("paleo.museum")];
- char stringpool_str6103[sizeof("trust.museum")];
- char stringpool_str6104[sizeof("toga.toyama.jp")];
- char stringpool_str6105[sizeof("xn--kvnangen-k0a.no")];
- char stringpool_str6106[sizeof("office-on-the.net")];
- char stringpool_str6107[sizeof("lancashire.museum")];
- char stringpool_str6108[sizeof("ogaki.gifu.jp")];
- char stringpool_str6109[sizeof("tsuno.kochi.jp")];
- char stringpool_str6110[sizeof("praxi")];
- char stringpool_str6111[sizeof("vaapste.no")];
- char stringpool_str6112[sizeof("pro.mv")];
- char stringpool_str6113[sizeof("s3-ap-southeast-2.amazonaws.com")];
- char stringpool_str6114[sizeof("s3-ap-southeast-1.amazonaws.com")];
- char stringpool_str6115[sizeof("tochigi.tochigi.jp")];
- char stringpool_str6116[sizeof("tama.tokyo.jp")];
- char stringpool_str6117[sizeof("ogawa.ibaraki.jp")];
- char stringpool_str6118[sizeof("kawanabe.kagoshima.jp")];
- char stringpool_str6119[sizeof("xn--fiq228c5hs")];
- char stringpool_str6120[sizeof("touch.museum")];
- char stringpool_str6121[sizeof("discovery.museum")];
- char stringpool_str6122[sizeof("s3-us-gov-west-1.amazonaws.com")];
- char stringpool_str6123[sizeof("leka.no")];
- char stringpool_str6124[sizeof("newyork.museum")];
- char stringpool_str6125[sizeof("trysil.no")];
- char stringpool_str6126[sizeof("lajolla.museum")];
- char stringpool_str6127[sizeof("isa-hockeynut.com")];
- char stringpool_str6128[sizeof("tsuruta.aomori.jp")];
- char stringpool_str6129[sizeof("sayo.hyogo.jp")];
- char stringpool_str6130[sizeof("poltava.ua")];
- char stringpool_str6131[sizeof("podzone.org")];
- char stringpool_str6132[sizeof("lg.ua")];
- char stringpool_str6133[sizeof("lib.az.us")];
- char stringpool_str6134[sizeof("sa-east-1.compute.amazonaws.com")];
- char stringpool_str6135[sizeof("xn--btsfjord-9za.no")];
- char stringpool_str6136[sizeof("newspaper.museum")];
- char stringpool_str6137[sizeof("ug.gov.pl")];
- char stringpool_str6138[sizeof("likes-pie.com")];
- char stringpool_str6139[sizeof("paris.museum")];
- char stringpool_str6140[sizeof("takinoue.hokkaido.jp")];
- char stringpool_str6141[sizeof("lillehammer.no")];
- char stringpool_str6142[sizeof("bellevue.museum")];
- char stringpool_str6143[sizeof("nrw.museum")];
- char stringpool_str6144[sizeof("tambov.ru")];
- char stringpool_str6145[sizeof("texas.museum")];
- char stringpool_str6146[sizeof("is-a-republican.com")];
- char stringpool_str6147[sizeof("miyako.iwate.jp")];
- char stringpool_str6148[sizeof("tawaramoto.nara.jp")];
- char stringpool_str6149[sizeof("forli-cesena.it")];
- char stringpool_str6150[sizeof("philately.museum")];
- char stringpool_str6151[sizeof("s3-website-us-east-1.amazonaws.com")];
- char stringpool_str6152[sizeof("tarui.gifu.jp")];
- char stringpool_str6153[sizeof("s3-website-sa-east-1.amazonaws.com")];
- char stringpool_str6154[sizeof("xn--cg4bki")];
- char stringpool_str6155[sizeof("sologne.museum")];
- char stringpool_str6156[sizeof("game-server.cc")];
- char stringpool_str6157[sizeof("doesntexist.org")];
- char stringpool_str6158[sizeof("xn--kvfjord-nxa.no")];
- char stringpool_str6159[sizeof("town.museum")];
- char stringpool_str6160[sizeof("parachuting.aero")];
- char stringpool_str6161[sizeof("gs.va.no")];
- char stringpool_str6162[sizeof("tokuyama.yamaguchi.jp")];
- char stringpool_str6163[sizeof("newhampshire.museum")];
- char stringpool_str6164[sizeof("florence.it")];
- char stringpool_str6165[sizeof("tsubetsu.hokkaido.jp")];
- char stringpool_str6166[sizeof("from-nc.com")];
- char stringpool_str6167[sizeof("tokamachi.niigata.jp")];
- char stringpool_str6168[sizeof("miyoshi.aichi.jp")];
- char stringpool_str6169[sizeof("xn--ogbpf8fl")];
- char stringpool_str6170[sizeof("xn--mgbqly7c0a67fbc")];
- char stringpool_str6171[sizeof("historyofscience.museum")];
- char stringpool_str6172[sizeof("oryol.ru")];
- char stringpool_str6173[sizeof("so.gov.pl")];
- char stringpool_str6174[sizeof("toyama.jp")];
- char stringpool_str6175[sizeof("sr.gov.pl")];
- char stringpool_str6176[sizeof("dyndns-server.com")];
- char stringpool_str6177[sizeof("is-a-chef.net")];
- char stringpool_str6178[sizeof("tm.mc")];
- char stringpool_str6179[sizeof("xn--czr694b")];
- char stringpool_str6180[sizeof("nationalheritage.museum")];
- char stringpool_str6181[sizeof("xn--czrs0t")];
- char stringpool_str6182[sizeof("bievat.no")];
- char stringpool_str6183[sizeof("is-a-chef.com")];
- char stringpool_str6184[sizeof("xn--mgbtf8fl")];
- char stringpool_str6185[sizeof("pippu.hokkaido.jp")];
- char stringpool_str6186[sizeof("elasticbeanstalk.com")];
- char stringpool_str6187[sizeof("s3-website-ap-southeast-2.amazonaws.com")];
- char stringpool_str6188[sizeof("xn--comunicaes-v6a2o.museum")];
- char stringpool_str6189[sizeof("oi.kanagawa.jp")];
- char stringpool_str6190[sizeof("toyama.toyama.jp")];
- char stringpool_str6191[sizeof("takanezawa.tochigi.jp")];
- char stringpool_str6192[sizeof("s3-website-ap-southeast-1.amazonaws.com")];
- char stringpool_str6193[sizeof("sa.gov.au")];
- char stringpool_str6194[sizeof("xn--czru2d")];
- char stringpool_str6195[sizeof("s3-website-ap-northeast-1.amazonaws.com")];
- char stringpool_str6196[sizeof("toyooka.hyogo.jp")];
- char stringpool_str6197[sizeof("toyone.aichi.jp")];
- char stringpool_str6198[sizeof("doomdns.com")];
- char stringpool_str6199[sizeof("toyo.kochi.jp")];
- char stringpool_str6200[sizeof("tsuga.tochigi.jp")];
- char stringpool_str6201[sizeof("s3-website-us-west-2.amazonaws.com")];
- char stringpool_str6202[sizeof("shinonsen.hyogo.jp")];
- char stringpool_str6203[sizeof("xn--moreke-jua.no")];
- char stringpool_str6204[sizeof("tokai.aichi.jp")];
- char stringpool_str6205[sizeof("toyota.aichi.jp")];
- char stringpool_str6206[sizeof("s3-website-us-west-1.amazonaws.com")];
- char stringpool_str6207[sizeof("is-very-sweet.org")];
- char stringpool_str6208[sizeof("xn--hyanger-q1a.no")];
- char stringpool_str6209[sizeof("kawachinagano.osaka.jp")];
- char stringpool_str6210[sizeof("toyono.osaka.jp")];
- char stringpool_str6211[sizeof("nishitosa.kochi.jp")];
- char stringpool_str6212[sizeof("tokai.ibaraki.jp")];
- char stringpool_str6213[sizeof("pc.it")];
- char stringpool_str6214[sizeof("xn--nvuotna-hwa.no")];
- char stringpool_str6215[sizeof("sande.vestfold.no")];
- char stringpool_str6216[sizeof("jobs.tt")];
- char stringpool_str6217[sizeof("xn--kvitsy-fya.no")];
- char stringpool_str6218[sizeof("royken.no")];
- char stringpool_str6219[sizeof("tm.pl")];
- char stringpool_str6220[sizeof("xn--eveni-0qa01ga.no")];
- char stringpool_str6221[sizeof("nishinoomote.kagoshima.jp")];
- char stringpool_str6222[sizeof("kommunalforbund.se")];
- char stringpool_str6223[sizeof("xn--nmesjevuemie-tcba.no")];
- char stringpool_str6224[sizeof("priv.hu")];
- char stringpool_str6225[sizeof("suwa.nagano.jp")];
- char stringpool_str6226[sizeof("toyako.hokkaido.jp")];
- char stringpool_str6227[sizeof("toyonaka.osaka.jp")];
- char stringpool_str6228[sizeof("toyokawa.aichi.jp")];
- char stringpool_str6229[sizeof("egyptian.museum")];
- char stringpool_str6230[sizeof("yokoshibahikari.chiba.jp")];
- char stringpool_str6231[sizeof("toyotomi.hokkaido.jp")];
- char stringpool_str6232[sizeof("lg.jp")];
- char stringpool_str6233[sizeof("plaza.museum")];
- char stringpool_str6234[sizeof("shimonoseki.yamaguchi.jp")];
- char stringpool_str6235[sizeof("principe.st")];
- char stringpool_str6236[sizeof("kongsvinger.no")];
- char stringpool_str6237[sizeof("toyoura.hokkaido.jp")];
- char stringpool_str6238[sizeof("newjersey.museum")];
- char stringpool_str6239[sizeof("prochowice.pl")];
- char stringpool_str6240[sizeof("tsubata.ishikawa.jp")];
- char stringpool_str6241[sizeof("lenvik.no")];
- char stringpool_str6242[sizeof("society.museum")];
- char stringpool_str6243[sizeof("is-a-cubicle-slave.com")];
- char stringpool_str6244[sizeof("dreamhosters.com")];
- char stringpool_str6245[sizeof("xn--gecrj9c")];
- char stringpool_str6246[sizeof("larvik.no")];
- char stringpool_str6247[sizeof("lib.ut.us")];
- char stringpool_str6248[sizeof("law.pro")];
- char stringpool_str6249[sizeof("dnepropetrovsk.ua")];
- char stringpool_str6250[sizeof("loabat.no")];
- char stringpool_str6251[sizeof("niyodogawa.kochi.jp")];
- char stringpool_str6252[sizeof("lib.mt.us")];
- char stringpool_str6253[sizeof("uchiko.ehime.jp")];
- char stringpool_str6254[sizeof("porsanger.no")];
- char stringpool_str6255[sizeof("lib.vt.us")];
- char stringpool_str6256[sizeof("louvre.museum")];
- char stringpool_str6257[sizeof("toya.hokkaido.jp")];
- char stringpool_str6258[sizeof("dnipropetrovsk.ua")];
- char stringpool_str6259[sizeof("tempio-olbia.it")];
- char stringpool_str6260[sizeof("vossevangen.no")];
- char stringpool_str6261[sizeof("ac.vn")];
- char stringpool_str6262[sizeof("xn--sndre-land-0cb.no")];
- char stringpool_str6263[sizeof("yanaizu.fukushima.jp")];
- char stringpool_str6264[sizeof("tcm.museum")];
- char stringpool_str6265[sizeof("trapani.it")];
- char stringpool_str6266[sizeof("xn--frde-gra.no")];
- char stringpool_str6267[sizeof("kawaguchi.saitama.jp")];
- char stringpool_str6268[sizeof("skiptvet.no")];
- char stringpool_str6269[sizeof("trading.aero")];
- char stringpool_str6270[sizeof("xn--tnsberg-q1a.no")];
- char stringpool_str6271[sizeof("sakae.chiba.jp")];
- char stringpool_str6272[sizeof("loppa.no")];
- char stringpool_str6273[sizeof("spy.museum")];
- char stringpool_str6274[sizeof("labor.museum")];
- char stringpool_str6275[sizeof("dyndns-blog.com")];
- char stringpool_str6276[sizeof("is-a-nascarfan.com")];
- char stringpool_str6277[sizeof("saskatchewan.museum")];
- char stringpool_str6278[sizeof("yawatahama.ehime.jp")];
- char stringpool_str6279[sizeof("joyo.kyoto.jp")];
- char stringpool_str6280[sizeof("lviv.ua")];
- char stringpool_str6281[sizeof("dyndns-free.com")];
- char stringpool_str6282[sizeof("xn--czrw28b.tw")];
- char stringpool_str6283[sizeof("otaki.chiba.jp")];
- char stringpool_str6284[sizeof("dyndns-office.com")];
- char stringpool_str6285[sizeof("ruovat.no")];
- char stringpool_str6286[sizeof("xn--lt-liac.no")];
- char stringpool_str6287[sizeof("jewish.museum")];
- char stringpool_str6288[sizeof("yachiyo.chiba.jp")];
- char stringpool_str6289[sizeof("lib.nv.us")];
- char stringpool_str6290[sizeof("jewishart.museum")];
- char stringpool_str6291[sizeof("langevag.no")];
- char stringpool_str6292[sizeof("jan-mayen.no")];
- char stringpool_str6293[sizeof("homeftp.net")];
- char stringpool_str6294[sizeof("directory")];
- char stringpool_str6295[sizeof("loyalist.museum")];
- char stringpool_str6296[sizeof("natuurwetenschappen.museum")];
- char stringpool_str6297[sizeof("is-a-teacher.com")];
- char stringpool_str6298[sizeof("s3-website-us-gov-west-1.amazonaws.com")];
- char stringpool_str6299[sizeof("is-not-certified.com")];
- char stringpool_str6300[sizeof("cc.sc.us")];
- char stringpool_str6301[sizeof("science-fiction.museum")];
- char stringpool_str6302[sizeof("cc.dc.us")];
- char stringpool_str6303[sizeof("cc.nc.us")];
- char stringpool_str6304[sizeof("is-into-cars.com")];
- char stringpool_str6305[sizeof("is-into-cartoons.com")];
- char stringpool_str6306[sizeof("podzone.net")];
- char stringpool_str6307[sizeof("ofunato.iwate.jp")];
- char stringpool_str6308[sizeof("doesntexist.com")];
- char stringpool_str6309[sizeof("is-a-therapist.com")];
- char stringpool_str6310[sizeof("nationalfirearms.museum")];
- char stringpool_str6311[sizeof("toyosato.shiga.jp")];
- char stringpool_str6312[sizeof("cc.pr.us")];
- char stringpool_str6313[sizeof("lc.it")];
- char stringpool_str6314[sizeof("national-library-scotland.uk")];
- char stringpool_str6315[sizeof("cc.pa.us")];
- char stringpool_str6316[sizeof("is-with-theband.com")];
- char stringpool_str6317[sizeof("davvenjarga.no")];
- char stringpool_str6318[sizeof("priv.no")];
- char stringpool_str6319[sizeof("convent.museum")];
- char stringpool_str6320[sizeof("xn--indery-fya.no")];
- char stringpool_str6321[sizeof("po.gov.pl")];
- char stringpool_str6322[sizeof("pa.gov.pl")];
- char stringpool_str6323[sizeof("compute.amazonaws.com")];
- char stringpool_str6324[sizeof("compute-1.amazonaws.com")];
- char stringpool_str6325[sizeof("experts-comptables.fr")];
- char stringpool_str6326[sizeof("leasing.aero")];
- char stringpool_str6327[sizeof("xn--54b7fta0cc")];
- char stringpool_str6328[sizeof("is-very-nice.org")];
- char stringpool_str6329[sizeof("xn--fpcrj9c3d")];
- char stringpool_str6330[sizeof("jfk.museum")];
- char stringpool_str6331[sizeof("leikanger.no")];
- char stringpool_str6332[sizeof("lucerne.museum")];
- char stringpool_str6333[sizeof("jp.net")];
- char stringpool_str6334[sizeof("xn--mtta-vrjjat-k7af.no")];
- char stringpool_str6335[sizeof("gs.vf.no")];
- char stringpool_str6336[sizeof("laspezia.it")];
- char stringpool_str6337[sizeof("is-into-games.com")];
- char stringpool_str6338[sizeof("resistance.museum")];
- char stringpool_str6339[sizeof("pc.pl")];
- char stringpool_str6340[sizeof("scientist.aero")];
- char stringpool_str6341[sizeof("s3-website-eu-west-1.amazonaws.com")];
- char stringpool_str6342[sizeof("xn--hmmrfeasta-s4ac.no")];
- char stringpool_str6343[sizeof("newport.museum")];
- char stringpool_str6344[sizeof("stavanger.no")];
- char stringpool_str6345[sizeof("toyoake.aichi.jp")];
- char stringpool_str6346[sizeof("jewelry.museum")];
- char stringpool_str6347[sizeof("cc.vt.us")];
- char stringpool_str6348[sizeof("cc.vi.us")];
- char stringpool_str6349[sizeof("toyota.yamaguchi.jp")];
- char stringpool_str6350[sizeof("cc.va.us")];
- char stringpool_str6351[sizeof("hoylandet.no")];
- char stringpool_str6352[sizeof("corvette.museum")];
- char stringpool_str6353[sizeof("toyohashi.aichi.jp")];
- char stringpool_str6354[sizeof("lib.ct.us")];
- char stringpool_str6355[sizeof("air-traffic-control.aero")];
- char stringpool_str6356[sizeof("us-gov-west-1.compute.amazonaws.com")];
- char stringpool_str6357[sizeof("jaworzno.pl")];
- char stringpool_str6358[sizeof("posts-and-telecommunications.museum")];
- char stringpool_str6359[sizeof("powiat.pl")];
- char stringpool_str6360[sizeof("passenger-association.aero")];
- char stringpool_str6361[sizeof("logistics.aero")];
- char stringpool_str6362[sizeof("lewismiller.museum")];
- char stringpool_str6363[sizeof("xn--stre-toten-zcb.no")];
- char stringpool_str6364[sizeof("toyotsu.fukuoka.jp")];
- char stringpool_str6365[sizeof("pacific.museum")];
- char stringpool_str6366[sizeof("xn--nttery-byae.no")];
- char stringpool_str6367[sizeof("shimotsuke.tochigi.jp")];
- char stringpool_str6368[sizeof("geometre-expert.fr")];
- char stringpool_str6369[sizeof("endoftheinternet.org")];
- char stringpool_str6370[sizeof("skydiving.aero")];
- char stringpool_str6371[sizeof("xn--correios-e-telecomunicaes-ghc29a.museum")];
- char stringpool_str6372[sizeof("lowicz.pl")];
- char stringpool_str6373[sizeof("xn--clchc0ea0b2g2a9gcd")];
- char stringpool_str6374[sizeof("xn--fzc2c9e2c")];
- };
-static const struct stringpool_t stringpool_contents =
- {
- "io",
- "gov",
- "gd",
- "id",
- "edu",
- "co",
- "gop",
- "cd",
- "cz",
- "gov.do",
- "com",
- "gob.do",
- "gov.om",
- "edu.do",
- "gov.dm",
- "gs",
- "is",
- "edu.om",
- "es",
- "edu.dm",
- "com.do",
- "gr",
- "ir",
- "hr",
- "er",
- "com.om",
- "com.dm",
- "cr",
- "ao",
- "gov.rs",
- "ad",
- "az",
- "edu.rs",
- "com.so",
- "gl",
- "il",
- "com.ro",
- "gov.sc",
- "cl",
- "homes",
- "as",
- "gov.lr",
- "edu.sc",
- "gov.sb",
- "edu.lr",
- "ar",
- "bo",
- "edu.sb",
- "com.sc",
- "bd",
- "bz",
- "fo",
- "com.lr",
- "com.sb",
- "arpa",
- "gu",
- "art.do",
- "hu",
- "eu",
- "gov.lc",
- "cool",
- "condos",
- "guru",
- "al",
- "cu",
- "edu.lc",
- "bs",
- "gov.lb",
- "edu.lb",
- "br",
- "com.lc",
- "gov.lk",
- "gov.sl",
- "fr",
- "edu.lk",
- "com.lb",
- "edu.sl",
- "com.de",
- "com.lk",
- "com.sl",
- "g.bg",
- "i.bg",
- "h.bg",
- "e.bg",
- "6.bg",
- "2.bg",
- "au",
- "9.bg",
- "8.bg",
- "7.bg",
- "5.bg",
- "4.bg",
- "c.bg",
- "q.bg",
- "3.bg",
- "1.bg",
- "gn",
- "in",
- "hn",
- "int",
- "g.se",
- "i.se",
- "h.se",
- "e.se",
- "0.bg",
- "cn",
- "grp.lk",
- "com.re",
- "arts.ro",
- "c.se",
- "gov.nr",
- "edu.nr",
- "blue",
- "com.nr",
- "a.bg",
- "autos",
- "eus",
- "arts.co",
- "ge",
- "ie",
- "info",
- "ee",
- "hof.no",
- "an",
- "a.se",
- "house",
- "horse",
- "gob.es",
- "b.br",
- "edu.es",
- "b.bg",
- "com.es",
- "gq",
- "iq",
- "f.bg",
- "gt",
- "it",
- "ht",
- "et",
- "bn",
- "gov.ec",
- "gob.ec",
- "b.se",
- "ae",
- "edu.ec",
- "f.se",
- "gov.to",
- "glass",
- "gov.tj",
- "com.ec",
- "edu.to",
- "gov.tm",
- "aero",
- "edu.tj",
- "edu.tm",
- "com.to",
- "gov.sa",
- "com.tj",
- "com.tm",
- "aq",
- "int.lk",
- "edu.sa",
- "gov.sd",
- "info.ro",
- "be",
- "at",
- "edu.sd",
- "com.sa",
- "ceo",
- "com.sd",
- "auto.pl",
- "isa.us",
- "gov.la",
- "codes",
- "info.co",
- "edu.la",
- "gov.ee",
- "asso.ci",
- "edu.ee",
- "bt",
- "com.la",
- "com.ee",
- "info.la",
- "gov.tl",
- "info.pl",
- "gov.bs",
- "gov.br",
- "gov.bo",
- "gob.bo",
- "gov.ua",
- "edu.bs",
- "edu.br",
- "edu.bo",
- "gov.bm",
- "edu.ua",
- "info.ki",
- "edu.bm",
- "com.bs",
- "com.br",
- "com.bo",
- "zone",
- "com.ua",
- "com.bm",
- "gi",
- "gov.bb",
- "esp.br",
- "best",
- "estate",
- "ci",
- "edu.bb",
- "gov.iq",
- "gov.is",
- "gov.ir",
- "adv.br",
- "edu.iq",
- "edu.is",
- "com.bb",
- "adm.br",
- "mo",
- "com.iq",
- "com.is",
- "md",
- "com.io",
- "mz",
- "gift",
- "bb",
- "com.im",
- "int.tj",
- "beer",
- "com.na",
- "ai",
- "z.bg",
- "art.br",
- "ms",
- "country",
- "fot.br",
- "mr",
- "info.pr",
- "z.se",
- "gov.nc.tr",
- "moe",
- "gov.rw",
- "edu.rw",
- "bi",
- "fst.br",
- "fet.no",
- "ml",
- "int.la",
- "fi",
- "com.rw",
- "center",
- "asso.dz",
- "gotdns.com",
- "gov.qa",
- "edu.qa",
- "info.bb",
- "fla.no",
- "info.az",
- "gy",
- "gifu.jp",
- "gov.ie",
- "com.qa",
- "info.pk",
- "cy",
- "int.bo",
- "mu",
- "cnt.br",
- "blog.br",
- "inf.br",
- "flog.br",
- "c.la",
- "assn.lk",
- "int.is",
- "m.bg",
- "uz",
- "mn",
- "m.se",
- "by",
- "gov.ba",
- "gov.jo",
- "edu.ba",
- "us",
- "edu.jo",
- "fosnes.no",
- "com.ba",
- "com.jo",
- "coffee",
- "gm",
- "im",
- "hm",
- "me",
- "firm.ro",
- "cm",
- "int.rw",
- "hemnes.no",
- "gov.mr",
- "gov.mo",
- "edu.mo",
- "com.mo",
- "firm.co",
- "mq",
- "g12.br",
- "fed.us",
- "mt",
- "moda",
- "am",
- "bj",
- "meet",
- "fj",
- "gov.tw",
- "menu",
- "gov.mk",
- "blogspot.no",
- "idv.tw",
- "edu.tw",
- "gol.no",
- "edu.mk",
- "hol.no",
- "blogspot.cz",
- "com.tw",
- "com.mk",
- "bm",
- "blogspot.ro",
- "fm",
- "gov.ml",
- "bid",
- "u.bg",
- "edu.ml",
- "com.ml",
- "gov.me",
- "ato.br",
- "u.se",
- "edu.me",
- "blogspot.nl",
- "gdansk.pl",
- "ind.br",
- "cim.br",
- "eng.br",
- "info.mv",
- "algard.no",
- "museum",
- "aip.ee",
- "cng.br",
- "blogspot.kr",
- "fin.ec",
- "ga",
- "codespot.com",
- "blogspot.td",
- "isla.pr",
- "haus",
- "ca",
- "qa",
- "cat",
- "gov.as",
- "cab",
- "gob.ar",
- "edu.ar",
- "eid.no",
- "com.ar",
- "mod.uk",
- "uno",
- "frosta.no",
- "gov.ac",
- "aero.mv",
- "edu.ac",
- "com.ac",
- "fie.ee",
- "blogspot.com",
- "fnd.br",
- "edu.bi",
- "gp",
- "gov.ma",
- "ba",
- "inf.mk",
- "cards",
- "bar",
- "com.bi",
- "blogspot.mr",
- "museum.no",
- "gov.ps",
- "gov.pr",
- "gov.al",
- "med.om",
- "edu.ps",
- "edu.pr",
- "asso.bj",
- "edu.al",
- "biz",
- "casa",
- "com.ps",
- "com.pr",
- "qsl.br",
- "com.al",
- "gov.ae",
- "zm",
- "est.pr",
- "blogspot.re",
- "bio.br",
- "my",
- "unsa.ba",
- "gov.pk",
- "gob.pk",
- "farm",
- "edu.pk",
- "gop.pk",
- "cv",
- "com.pk",
- "club",
- "conf.lv",
- "zlg.br",
- "homeip.net",
- "int.ar",
- "gov.pl",
- "mus.br",
- "imb.br",
- "gov.vc",
- "edu.pl",
- "consulado.st",
- "gon.pk",
- "edu.vc",
- "emp.br",
- "i.ph",
- "com.pl",
- "gob.pe",
- "com.vc",
- "edu.pe",
- "gsm.pl",
- "com.pe",
- "asia",
- "gos.pk",
- "globo",
- "mm",
- "gov.mw",
- "career",
- "gov.ve",
- "edu.mw",
- "art.pl",
- "edu.ve",
- "qpon",
- "com.mw",
- "com.ve",
- "biz.nr",
- "za",
- "uy",
- "its.me",
- "amursk.ru",
- "blogspot.de",
- "glogow.pl",
- "is-a-cpa.com",
- "med.ec",
- "camera",
- "gob.pa",
- "com.nf",
- "edu.pa",
- "blogspot.se",
- "blogspot.com.es",
- "gda.pl",
- "hurdal.no",
- "com.pa",
- "build",
- "alvdal.no",
- "med.sa",
- "asso.km",
- "med.sd",
- "biz.tj",
- "habmer.no",
- "blogspot.pt",
- "futbol",
- "blogspot.hu",
- "fjaler.no",
- "moscow",
- "med.ee",
- "ma",
- "aseral.no",
- "int.mw",
- "blogspot.co.nz",
- "x.bg",
- "med.br",
- "com.aw",
- "museum.tt",
- "x.se",
- "far.br",
- "gh",
- "ch",
- "asso.fr",
- "atm.pl",
- "quebec",
- "blogspot.com.br",
- "biz.bb",
- "mp",
- "gov.bf",
- "bieszczady.pl",
- "com.hr",
- "guide",
- "gal",
- "bmd.br",
- "ingatlan.hu",
- "mil",
- "events",
- "gov.hk",
- "com.ai",
- "idv.hk",
- "edu.hk",
- "mil.do",
- "firm.in",
- "com.hk",
- "eng.pro",
- "bh",
- "mv",
- "hapmir.no",
- "belgorod.ru",
- "ua",
- "e12.ve",
- "hemsedal.no",
- "mango",
- "evenes.no",
- "miami",
- "engerdal.no",
- "blogspot.in",
- "hvaler.no",
- "mielno.pl",
- "hadsel.no",
- "bronnoysund.no",
- "blogspot.ie",
- "blogspot.ca",
- "com.vi",
- "etc.br",
- "broke-it.net",
- "mil.no",
- "mincom.tn",
- "asso.re",
- "blogspot.co.il",
- "fhv.se",
- "blogspot.it",
- "mielec.pl",
- "fish",
- "e164.arpa",
- "mie.jp",
- "ing.pa",
- "eti.br",
- "biz.id",
- "mat.br",
- "bindal.no",
- "mil.ec",
- "mil.to",
- "hareid.no",
- "mil.tj",
- "mil.tm",
- "abo.pa",
- "fyresdal.no",
- "gallery",
- "hk",
- "builders",
- "blogspot.cv",
- "ck",
- "fhs.no",
- "gov.km",
- "broker.aero",
- "edu.km",
- "aid.pl",
- "com.km",
- "meland.no",
- "mil.br",
- "mil.bo",
- "mh",
- "gaular.no",
- "fk",
- "isernia.it",
- "mil.iq",
- "gov.kp",
- "ws",
- "ullensaker.no",
- "blogspot.com.ar",
- "edu.kp",
- "gov.af",
- "com.kp",
- "edu.af",
- "com.af",
- "biz.pr",
- "biella.it",
- "mil.rw",
- "blogspot.com.au",
- "med.pro",
- "author.aero",
- "bar.pro",
- "ass.km",
- "mil.qa",
- "fuossko.no",
- "fam.pk",
- "edu.pf",
- "biz.pk",
- "com.pf",
- "med.pl",
- "fuel.aero",
- "urn.arpa",
- "w.bg",
- "eidfjord.no",
- "w.se",
- "biz.pl",
- "arendal.no",
- "irc.pl",
- "miasta.pl",
- "mil.ba",
- "mil.jo",
- "web.do",
- "biz.mw",
- "blogspot.ch",
- "fuettertdasnetz.de",
- "googleapis.com",
- "bronnoy.no",
- "mil.id",
- "inderoy.no",
- "cpa.pro",
- "museum.mv",
- "blogspot.gr",
- "etnedal.no",
- "careers",
- "med.pa",
- "mil.tw",
- "xyz",
- "mk",
- "web.lk",
- "hjartdal.no",
- "foggia.it",
- "brussels.museum",
- "marker.no",
- "modena.it",
- "bamble.no",
- "ip6.arpa",
- "ferrara.it",
- "imperia.it",
- "blogspot.co.at",
- "flesberg.no",
- "bergamo.it",
- "aircraft.aero",
- "mil.ar",
- "gov.ki",
- "edu.ki",
- "hjelmeland.no",
- "wed",
- "mil.ac",
- "com.ki",
- "meldal.no",
- "github.io",
- "web.tj",
- "black",
- "uk",
- "cheap",
- "horology.museum",
- "mil.al",
- "wroc.pl",
- "work",
- "wien",
- "mil.ae",
- "caravan",
- "works",
- "eidsvoll.no",
- "folldal.no",
- "gjesdal.no",
- "mil.pl",
- "mandal.no",
- "mil.vc",
- "mail.pl",
- "melhus.no",
- "immobilien",
- "mil.pe",
- "cleaning",
- "fredrikstad.no",
- "asmatart.museum",
- "hamaroy.no",
- "catania.it",
- "mil.ve",
- "museum.om",
- "genova.it",
- "badaddja.no",
- "iris.arpa",
- "flanders.museum",
- "creation.museum",
- "milano.it",
- "gausdal.no",
- "ancona.it",
- "matera.it",
- "budapest",
- "modalen.no",
- "gotdns.org",
- "blogspot.dk",
- "eidsberg.no",
- "aremark.no",
- "club.aero",
- "blogspot.sk",
- "web.id",
- "is-a-llama.com",
- "izhevsk.ru",
- "hita.oita.jp",
- "benevento.it",
- "wang",
- "blogspot.hk",
- "ibaraki.jp",
- "holtalen.no",
- "fauske.no",
- "uri.arpa",
- "masfjorden.no",
- "airguard.museum",
- "baidar.no",
- "american.museum",
- "blogspot.co.uk",
- "montreal.museum",
- "guitars",
- "bargains",
- "estate.museum",
- "wtc",
- "uzhgorod.ua",
- "bydgoszcz.pl",
- "web.pk",
- "cartoonart.museum",
- "caa.aero",
- "kz",
- "cloudfront.net",
- "cultural.museum",
- "center.museum",
- "kr",
- "catering",
- "gov.ru",
- "edu.ru",
- "brunel.museum",
- "com.ru",
- "mil.kr",
- "mil.km",
- "web.ve",
- "chtr.k12.ma.us",
- "czeladz.pl",
- "kobe.jp",
- "kred",
- "in-addr.arpa",
- "k.bg",
- "web.nf",
- "kn",
- "k.se",
- "biz.ki",
- "mragowo.pl",
- "gw",
- "cw",
- "ke",
- "gov.ws",
- "int.ru",
- "edu.ws",
- "com.ws",
- "wegrow.pl",
- "gov.sg",
- "aw",
- "edu.sg",
- "ascoli-piceno.it",
- "com.sg",
- "columbia.museum",
- "erotica.hu",
- "crafts.museum",
- "gildeskal.no",
- "krd",
- "bw",
- "erotika.hu",
- "audnedaln.no",
- "circus.museum",
- "blogspot.jp",
- "aomori.aomori.jp",
- "brasil.museum",
- "com.ug",
- "guernsey.museum",
- "aomori.jp",
- "gov.ng",
- "edu.ng",
- "ki",
- "com.ng",
- "kim",
- "grandrapids.museum",
- "cagliari.it",
- "fhsk.se",
- "gov.eg",
- "edu.eg",
- "gjemnes.no",
- "historical.museum",
- "magadan.ru",
- "com.eg",
- "alessandria.it",
- "asso.gp",
- "crimea.ua",
- "miners.museum",
- "zw",
- "memorial.museum",
- "ky",
- "webhop.net",
- "meeres.museum",
- "granvin.no",
- "contemporary.museum",
- "bir.ru",
- "k12.ec",
- "eun.eg",
- "evenassi.no",
- "baikal.ru",
- "warszawa.pl",
- "clothing",
- "mw",
- "km",
- "cupcake.is",
- "castle.museum",
- "mobi",
- "enebakk.no",
- "gov.mu",
- "beskidy.pl",
- "udm.ru",
- "com.mu",
- "canada.museum",
- "cbg.ru",
- "hagebostad.no",
- "usarts.museum",
- "catering.aero",
- "beardu.no",
- "info.at",
- "gorlice.pl",
- "celtic.museum",
- "altoadige.it",
- "fundacio.museum",
- "moscow.museum",
- "warmia.pl",
- "bielawa.pl",
- "anthro.museum",
- "moareke.no",
- "avellino.it",
- "massacarrara.it",
- "como.it",
- "fylkesbibl.no",
- "gov.au",
- "ise.mie.jp",
- "broadcast.museum",
- "edu.au",
- "mansions.museum",
- "com.au",
- "quebec.museum",
- "gov.mg",
- "ullensvang.no",
- "edu.mg",
- "com.mg",
- "muncie.museum",
- "e-burg.ru",
- "kp",
- "baseball.museum",
- "hinode.tokyo.jp",
- "cinema.museum",
- "asn.au",
- "florist",
- "andebu.no",
- "enna.it",
- "blogspot.tw",
- "karasjok.no",
- "meraker.no",
- "capebreton.museum",
- "com.ag",
- "ina.ibaraki.jp",
- "unbi.ba",
- "berkeley.museum",
- "maryland.museum",
- "holiday",
- "email",
- "mini",
- "malselv.no",
- "hellas.museum",
- "zaporizhzhe.ua",
- "hirosaki.aomori.jp",
- "usgarden.museum",
- "chieti.it",
- "better-than.tv",
- "qld.au",
- "carraramassa.it",
- "figueres.museum",
- "gonohe.aomori.jp",
- "watch",
- "chernovtsy.ua",
- "aejrie.no",
- "bjarkoy.no",
- "forlicesena.it",
- "brindisi.it",
- "cesenaforli.it",
- "foundation",
- "meguro.tokyo.jp",
- "museum.mw",
- "kh",
- "mosreg.ru",
- "akrehamn.no",
- "zaporizhzhia.ua",
- "uda.nara.jp",
- "undersea.museum",
- "cruises",
- "eisenbahn.museum",
- "mil.ru",
- "wroclaw.pl",
- "eidskog.no",
- "aviation.museum",
- "k12.or.us",
- "hamura.tokyo.jp",
- "k12.dc.us",
- "mifune.kumamoto.jp",
- "minato.tokyo.jp",
- "k12.ok.us",
- "k12.sc.us",
- "ballooning.aero",
- "muroto.kochi.jp",
- "ambulance.museum",
- "elblag.pl",
- "ina.saitama.jp",
- "k12.de.us",
- "utsira.no",
- "assassination.museum",
- "zhitomir.ua",
- "americanart.museum",
- "kostroma.ru",
- "krakow.pl",
- "k12.nj.us",
- "k12.nm.us",
- "incheon.kr",
- "bunkyo.tokyo.jp",
- "k12.nc.us",
- "gorizia.it",
- "engine.aero",
- "contemporaryart.museum",
- "carrara-massa.it",
- "astrakhan.ru",
- "k12.ne.us",
- "crew.aero",
- "alaska.museum",
- "mil.ng",
- "bearalvahki.no",
- "heguri.nara.jp",
- "k12.la.us",
- "mitsue.nara.jp",
- "usantiques.museum",
- "misasa.tottori.jp",
- "cheltenham.museum",
- "consultant.aero",
- "imageandsound.museum",
- "epilepsy.museum",
- "mil.eg",
- "madrid.museum",
- "aquila.it",
- "gobo.wakayama.jp",
- "malbork.pl",
- "k12.vi",
- "hinohara.tokyo.jp",
- "k12.nd.us",
- "blackfriday",
- "gov.kg",
- "ashoro.hokkaido.jp",
- "building.museum",
- "edu.kg",
- "city.sendai.jp",
- "com.kg",
- "franziskaner.museum",
- "kemerovo.ru",
- "k12.il.us",
- "casino.hu",
- "mihara.kochi.jp",
- "marine.ru",
- "k12.ri.us",
- "australia.museum",
- "business",
- "furano.hokkaido.jp",
- "kragero.no",
- "mobara.chiba.jp",
- "k12.ms.us",
- "k12.mo.us",
- "k12.ia.us",
- "k12.id.us",
- "chocolate.museum",
- "giehtavuoatna.no",
- "americana.museum",
- "maintenance.aero",
- "baltimore.museum",
- "k12.me.us",
- "minnesota.museum",
- "mil.mg",
- "klodzko.pl",
- "children.museum",
- "flekkefjord.no",
- "monticello.museum",
- "k12.as.us",
- "k12.ar.us",
- "fuso.aichi.jp",
- "certification.aero",
- "bilbao.museum",
- "college",
- "historichouses.museum",
- "championship.aero",
- "bihoro.hokkaido.jp",
- "grosseto.it",
- "k12.ak.us",
- "chernivtsi.ua",
- "gyeonggi.kr",
- "k12.ma.us",
- "kitchen",
- "k12.md.us",
- "k12.pr.us",
- "k12.al.us",
- "barcelona.museum",
- "ami.ibaraki.jp",
- "federation.aero",
- "kvafjord.no",
- "malopolska.pl",
- "karlsoy.no",
- "bungoono.oita.jp",
- "holmestrand.no",
- "mesaverde.museum",
- "microlight.aero",
- "americanantiques.museum",
- "webhop.org",
- "ethnology.museum",
- "ina.nagano.jp",
- "kusu.oita.jp",
- "beeldengeluid.museum",
- "elverum.no",
- "ibaraki.osaka.jp",
- "city.sapporo.jp",
- "haboro.hokkaido.jp",
- "chernigov.ua",
- "cx",
- "consulting",
- "kherson.ua",
- "aurskog-holand.no",
- "k12.pa.us",
- "kalmykia.ru",
- "gov.sh",
- "ax",
- "k12.mi.us",
- "com.sh",
- "foundation.museum",
- "k-uralsk.ru",
- "geisei.kochi.jp",
- "aknoluokta.no",
- "adachi.tokyo.jp",
- "chelyabinsk.ru",
- "andasuolo.no",
- "k12.va.us",
- "wolomin.pl",
- "windmill.museum",
- "manchester.museum",
- "matsusaka.mie.jp",
- "ink",
- "civilwar.museum",
- "fortmissoula.museum",
- "axa",
- "midtre-gauldal.no",
- "midori.gunma.jp",
- "elburg.museum",
- "kristiansand.no",
- "karelia.ru",
- "hiraya.nagano.jp",
- "missoula.museum",
- "filatelia.museum",
- "murata.miyagi.jp",
- "freemasonry.museum",
- "mizuho.tokyo.jp",
- "gov.bh",
- "engineer.aero",
- "edu.bh",
- "iizuna.nagano.jp",
- "motoyama.kochi.jp",
- "k12.vi.us",
- "com.bh",
- "atsuma.hokkaido.jp",
- "equipment",
- "collection.museum",
- "bajddar.no",
- "mx",
- "from.hr",
- "imabari.ehime.jp",
- "midori.chiba.jp",
- "cherkassy.ua",
- "izumisano.osaka.jp",
- "misato.miyagi.jp",
- "isleofman.museum",
- "konyvelo.hu",
- "minamiise.mie.jp",
- "info.au",
- "voto",
- "monash",
- "mitane.akita.jp",
- "bike",
- "hasama.oita.jp",
- "mitaka.tokyo.jp",
- "vote",
- "k12.ks.us",
- "is-found.org",
- "kw",
- "misato.akita.jp",
- "matta-varjjat.no",
- "vu",
- "conf.au",
- "minato.osaka.jp",
- "mihama.chiba.jp",
- "goshiki.hyogo.jp",
- "gotemba.shizuoka.jp",
- "hazu.aichi.jp",
- "v.bg",
- "vn",
- "consulting.aero",
- "mihama.aichi.jp",
- "cherkasy.ua",
- "mil.kg",
- "mito.ibaraki.jp",
- "ve",
- "edogawa.tokyo.jp",
- "hidaka.kochi.jp",
- "chernihiv.ua",
- "hyllestad.no",
- "inagawa.hyogo.jp",
- "cranbrook.museum",
- "urbino-pesaro.it",
- "annaka.gunma.jp",
- "krasnoyarsk.ru",
- "mari-el.ru",
- "fareast.ru",
- "hamada.shimane.jp",
- "andriabarlettatrani.it",
- "mo-i-rana.no",
- "miho.ibaraki.jp",
- "amur.ru",
- "karasjohka.no",
- "blogspot.mx",
- "kristiansund.no",
- "assisi.museum",
- "minami.kyoto.jp",
- "konskowola.pl",
- "finearts.museum",
- "humanities.museum",
- "vi",
- "gov.ph",
- "environment.museum",
- "edu.ph",
- "misato.shimane.jp",
- "com.ph",
- "zoological.museum",
- "chosei.chiba.jp",
- "kvitsoy.no",
- "gangwon.kr",
- "gok.pk",
- "biratori.hokkaido.jp",
- "andria-trani-barletta.it",
- "vlog.br",
- "gjerdrum.no",
- "minano.saitama.jp",
- "bjerkreim.no",
- "city.kitakyushu.jp",
- "vegas",
- "vdonsk.ru",
- "hiranai.aomori.jp",
- "furubira.hokkaido.jp",
- "aya.miyazaki.jp",
- "elk.pl",
- "vet.br",
- "hasuda.saitama.jp",
- "misato.saitama.jp",
- "esashi.hokkaido.jp",
- "karate.museum",
- "childrens.museum",
- "watarai.mie.jp",
- "hachioji.tokyo.jp",
- "geelvinck.museum",
- "kms.ru",
- "matsue.shimane.jp",
- "construction",
- "military.museum",
- "itabashi.tokyo.jp",
- "isen.kagoshima.jp",
- "arakawa.tokyo.jp",
- "va",
- "mobi.gp",
- "masuda.shimane.jp",
- "kautokeino.no",
- "macerata.it",
- "koenig.ru",
- "mil.sh",
- "mypets.ws",
- "chel.ru",
- "voronezh.ru",
- "fitjar.no",
- "asti.it",
- "misawa.aomori.jp",
- "kotoura.tottori.jp",
- "fukuoka.jp",
- "vennesla.no",
- "vladimir.ru",
- "chuo.chiba.jp",
- "isa.kagoshima.jp",
- "bifuka.hokkaido.jp",
- "mediocampidano.it",
- "bashkiria.ru",
- "kurobe.toyama.jp",
- "arakawa.saitama.jp",
- "hidaka.hokkaido.jp",
- "vestre-toten.no",
- "babia-gora.pl",
- "mihara.hiroshima.jp",
- "civilization.museum",
- "gyeongnam.kr",
- "khv.ru",
- "fuefuki.yamanashi.jp",
- "verdal.no",
- "genkai.saga.jp",
- "chuo.osaka.jp",
- "irkutsk.ru",
- "mimata.miyazaki.jp",
- "hamatama.saga.jp",
- "cadaques.museum",
- "chattanooga.museum",
- "aizumi.tokushima.jp",
- "minami.tokushima.jp",
- "viterbo.it",
- "usa.oita.jp",
- "futtsu.chiba.jp",
- "worse-than.tv",
- "british-library.uk",
- "britishcolumbia.museum",
- "choshi.chiba.jp",
- "archaeology.museum",
- "kuzbass.ru",
- "kharkov.ua",
- "kids.us",
- "kameoka.kyoto.jp",
- "vodka",
- "kvalsund.no",
- "komoro.nagano.jp",
- "kurotaki.nara.jp",
- "verona.it",
- "gliwice.pl",
- "kanmaki.nara.jp",
- "zachpomor.pl",
- "hashikami.aomori.jp",
- "minamiizu.shizuoka.jp",
- "eigersund.no",
- "venezia.it",
- "arts.museum",
- "matsubara.osaka.jp",
- "mishima.shizuoka.jp",
- "chiyoda.tokyo.jp",
- "villas",
- "mil.ph",
- "fortworth.museum",
- "k12.wa.us",
- "bari.it",
- "vinnica.ua",
- "coal.museum",
- "mosjoen.no",
- "michigan.museum",
- "kirovograd.ua",
- "kota.aichi.jp",
- "iraq.museum",
- "iron.museum",
- "bonn.museum",
- "glas.museum",
- "hachijo.tokyo.jp",
- "vestre-slidre.no",
- "watari.miyagi.jp",
- "moseushi.hokkaido.jp",
- "fribourg.museum",
- "ventures",
- "ebetsu.hokkaido.jp",
- "varese.it",
- "amakusa.kumamoto.jp",
- "frog.museum",
- "hidaka.saitama.jp",
- "archaeological.museum",
- "childrensgarden.museum",
- "arboretum.museum",
- "ariake.saga.jp",
- "iyo.ehime.jp",
- "kazo.saitama.jp",
- "budejju.no",
- "bern.museum",
- "ikaruga.nara.jp",
- "k12.wi.us",
- "assabu.hokkaido.jp",
- "kharkiv.ua",
- "moma.museum",
- "akishima.tokyo.jp",
- "evje-og-hornnes.no",
- "haugesund.no",
- "misaki.osaka.jp",
- "abashiri.hokkaido.jp",
- "mizusawa.iwate.jp",
- "aquarium.museum",
- "koza.wakayama.jp",
- "kashiba.nara.jp",
- "ujitawara.kyoto.jp",
- "no",
- "nz",
- "civilisation.museum",
- "hirado.nagasaki.jp",
- "nr",
- "gov.sx",
- "ishikawa.jp",
- "muroran.hokkaido.jp",
- "nom.ro",
- "nl",
- "kita.kyoto.jp",
- "hiroshima.jp",
- "iki.nagasaki.jp",
- "exeter.museum",
- "kira.aichi.jp",
- "nu",
- "anjo.aichi.jp",
- "moroyama.saitama.jp",
- "vercelli.it",
- "n.bg",
- "grajewo.pl",
- "nom.re",
- "n.se",
- "koganei.tokyo.jp",
- "kyotamba.kyoto.jp",
- "kuroishi.aomori.jp",
- "cody.museum",
- "nsn.us",
- "ne",
- "net",
- "urausu.hokkaido.jp",
- "kodaira.tokyo.jp",
- "net.do",
- "nom.es",
- "net.om",
- "net.dm",
- "net.so",
- "ivano-frankivsk.ua",
- "nom.tm",
- "net.sc",
- "net.lr",
- "net.sb",
- "minamiyamashiro.kyoto.jp",
- "qld.edu.au",
- "kviteseid.no",
- "net.lc",
- "kuju.oita.jp",
- "notteroy.no",
- "net.lb",
- "nls.uk",
- "net.lk",
- "net.sl",
- "not.br",
- "nom.br",
- "columbus.museum",
- "machida.tokyo.jp",
- "net.nr",
- "ni",
- "maritimo.museum",
- "civilaviation.aero",
- "unjarga.no",
- "koga.ibaraki.jp",
- "vacations",
- "copenhagen.museum",
- "minamiboso.chiba.jp",
- "mitsuke.niigata.jp",
- "farm.museum",
- "monmouth.museum",
- "net.ec",
- "katori.chiba.jp",
- "health.museum",
- "net.to",
- "net.tj",
- "net.tm",
- "kamo.kyoto.jp",
- "kizu.kyoto.jp",
- "net.sa",
- "net.sd",
- "bill.museum",
- "hatoyama.saitama.jp",
- "net.la",
- "arao.kumamoto.jp",
- "film.museum",
- "hanamaki.iwate.jp",
- "net.bs",
- "net.br",
- "net.bo",
- "net.ua",
- "net.bm",
- "bahn.museum",
- "net.bb",
- "ntr.br",
- "net.iq",
- "net.is",
- "net.ir",
- "buzz",
- "net.im",
- "minakami.gunma.jp",
- "inashiki.ibaraki.jp",
- "gob.mx",
- "edu.mx",
- "gojome.akita.jp",
- "com.mx",
- "maibara.shiga.jp",
- "gyeongbuk.kr",
- "cahcesuolo.no",
- "bialystok.pl",
- "kagami.kochi.jp",
- "net.rw",
- "warabi.saitama.jp",
- "kumenan.okayama.jp",
- "vyatka.ru",
- "net.qa",
- "asso.ht",
- "katsuragi.nara.jp",
- "info.ht",
- "minowa.nagano.jp",
- "katano.osaka.jp",
- "haibara.shizuoka.jp",
- "nysa.pl",
- "msk.ru",
- "na",
- "minamata.kumamoto.jp",
- "hiji.oita.jp",
- "net.ba",
- "net.jo",
- "williamsburg.museum",
- "neustar",
- "name",
- "mill.museum",
- "vestnes.no",
- "net.mo",
- "kasaoka.okayama.jp",
- "net.id",
- "uenohara.yamanashi.jp",
- "np",
- "nara.jp",
- "name.qa",
- "nel.uk",
- "net.tw",
- "net.mk",
- "net.je",
- "vrn.ru",
- "namsos.no",
- "name.jo",
- "kitami.hokkaido.jp",
- "bale.museum",
- "net.ml",
- "net.me",
- "arai.shizuoka.jp",
- "name.my",
- "nom.pl",
- "nom.pe",
- "kumano.hiroshima.jp",
- "net.ar",
- "net.ac",
- "wazuka.kyoto.jp",
- "name.mk",
- "nom.ad",
- "firm.ht",
- "name.pr",
- "net.ma",
- "arezzo.it",
- "kazuno.akita.jp",
- "net.ps",
- "net.pr",
- "net.al",
- "mashiki.kumamoto.jp",
- "net.ae",
- "name.az",
- "nom.pa",
- "net.pk",
- "iwanai.hokkaido.jp",
- "kita.osaka.jp",
- "barreau.bj",
- "net.pl",
- "net.vc",
- "net.pe",
- "kisarazu.chiba.jp",
- "kumatori.osaka.jp",
- "novara.it",
- "net.mw",
- "net.ve",
- "beauxarts.museum",
- "izumiotsu.osaka.jp",
- "funahashi.toyama.jp",
- "gg",
- "eg",
- "komaki.aichi.jp",
- "cg",
- "kunneppu.hokkaido.jp",
- "gov.gr",
- "kitaakita.akita.jp",
- "edu.gr",
- "net.nf",
- "com.gr",
- "net.pa",
- "hokksund.no",
- "khakassia.ru",
- "ag",
- "xxx",
- "ama.shimane.jp",
- "ggee",
- "gushikami.okinawa.jp",
- "bg",
- "vlaanderen.museum",
- "health.vn",
- "name.mv",
- "edu.gp",
- "gov.ge",
- "com.gp",
- "edu.ge",
- "freiburg.museum",
- "nic.uk",
- "com.ge",
- "utah.museum",
- "khabarovsk.ru",
- "hitoyoshi.kumamoto.jp",
- "workshop.museum",
- "urakawa.hokkaido.jp",
- "kai.yamanashi.jp",
- "agro.pl",
- "antiques.museum",
- "net.ai",
- "nic.tr",
- "nic.tj",
- "net.hk",
- "nittedal.no",
- "info.sd",
- "maebashi.gunma.jp",
- "name.eg",
- "notodden.no",
- "austrheim.no",
- "moriya.ibaraki.jp",
- "nissedal.no",
- "hasami.nagasaki.jp",
- "mining.museum",
- "nom.km",
- "kozaki.chiba.jp",
- "net.vi",
- "kosaka.akita.jp",
- "k12.oh.us",
- "ggf.br",
- "kraanghke.no",
- "kadoma.osaka.jp",
- "norddal.no",
- "agr.br",
- "mg",
- "mari.ru",
- "minamioguni.kumamoto.jp",
- "nagoya",
- "gov.gi",
- "versailles.museum",
- "edu.gi",
- "hirara.okinawa.jp",
- "com.gi",
- "k12.nh.us",
- "ro",
- "misaki.okayama.jp",
- "ginoza.okinawa.jp",
- "ug",
- "rs",
- "kagawa.jp",
- "nesodden.no",
- "vindafjord.no",
- "esan.hokkaido.jp",
- "net.af",
- "ru",
- "naples.it",
- "moriyoshi.akita.jp",
- "kitaaiki.nagano.jp",
- "naustdal.no",
- "chiryu.aichi.jp",
- "kasama.ibaraki.jp",
- "r.bg",
- "kannami.shizuoka.jp",
- "r.se",
- "blogspot.fr",
- "minami-alps.yamanashi.jp",
- "minobu.yamanashi.jp",
- "urasoe.okinawa.jp",
- "izu.shizuoka.jp",
- "nesseby.no",
- "kui.hiroshima.jp",
- "re",
- "akabira.hokkaido.jp",
- "aso.kumamoto.jp",
- "ren",
- "rodeo",
- "hokuto.hokkaido.jp",
- "kijo.miyazaki.jp",
- "city.kawasaki.jp",
- "rest",
- "ruhr",
- "nordre-land.no",
- "kumamoto.jp",
- "home.dyndns.org",
- "uvic.museum",
- "vantaa.museum",
- "kitaura.miyazaki.jp",
- "blogspot.sg",
- "nordkapp.no",
- "fuoisku.no",
- "red",
- "asakuchi.okayama.jp",
- "napoli.it",
- "net.ki",
- "blogspot.fi",
- "ito.shizuoka.jp",
- "utazas.hu",
- "hamamatsu.shizuoka.jp",
- "himeshima.oita.jp",
- "agents.aero",
- "farmequipment.museum",
- "chikusei.ibaraki.jp",
- "repair",
- "chitose.hokkaido.jp",
- "hadano.kanagawa.jp",
- "mod.gi",
- "rennesoy.no",
- "embaixada.st",
- "veterinaire.km",
- "wiki",
- "notaires.km",
- "ishikari.hokkaido.jp",
- "muko.kyoto.jp",
- "nordreisa.no",
- "mil.ge",
- "avoues.fr",
- "honjyo.akita.jp",
- "aeroport.fr",
- "hanamigawa.chiba.jp",
- "kamchatka.ru",
- "wiki.br",
- "vestvagoy.no",
- "higashikurume.tokyo.jp",
- "vic.au",
- "krodsherad.no",
- "uto.kumamoto.jp",
- "kamioka.akita.jp",
- "nov.ru",
- "kunisaki.oita.jp",
- "kitadaito.okinawa.jp",
- "mashike.hokkaido.jp",
- "mikasa.hokkaido.jp",
- "hirakata.osaka.jp",
- "rec.ro",
- "fukuroi.shizuoka.jp",
- "kids.museum",
- "info.hu",
- "namsskogan.no",
- "vevelstad.no",
- "upow.gov.pl",
- "ginowan.okinawa.jp",
- "kuzumaki.iwate.jp",
- "chichibu.saitama.jp",
- "net.ru",
- "nebraska.museum",
- "izunokuni.shizuoka.jp",
- "kumamoto.kumamoto.jp",
- "kami.miyagi.jp",
- "kembuchi.hokkaido.jp",
- "kosa.kumamoto.jp",
- "nome.pt",
- "kushima.miyazaki.jp",
- "network",
- "rocks",
- "hakuba.nagano.jp",
- "net.ws",
- "musashino.tokyo.jp",
- "rec.br",
- "rybnik.pl",
- "net.sg",
- "rindal.no",
- "is-a-candidate.org",
- "kami.kochi.jp",
- "nature.museum",
- "kumiyama.kyoto.jp",
- "zamami.okinawa.jp",
- "horokanai.hokkaido.jp",
- "gokase.miyazaki.jp",
- "volgograd.ru",
- "komi.ru",
- "bolt.hu",
- "kushiro.hokkaido.jp",
- "net.ng",
- "cesena-forli.it",
- "nes.akershus.no",
- "kashima.kumamoto.jp",
- "airtraffic.aero",
- "net.eg",
- "kishiwada.osaka.jp",
- "rel.pl",
- "uslivinghistory.museum",
- "hokuto.yamanashi.jp",
- "frankfurt.museum",
- "mykolaiv.ua",
- "ibestad.no",
- "wakasa.tottori.jp",
- "kagoshima.jp",
- "city.hu",
- "from-me.org",
- "kanoya.kagoshima.jp",
- "fukaya.saitama.jp",
- "nom.mg",
- "net.mu",
- "katsuura.chiba.jp",
- "kashiwara.osaka.jp",
- "fukudomi.saga.jp",
- "kariya.aichi.jp",
- "rzeszow.pl",
- "zama.kanagawa.jp",
- "novosibirsk.ru",
- "rep.kp",
- "noheji.aomori.jp",
- "hachirogata.akita.jp",
- "higashiyoshino.nara.jp",
- "nagano.jp",
- "vik.no",
- "katsushika.tokyo.jp",
- "wake.okayama.jp",
- "monzabrianza.it",
- "film.hu",
- "so",
- "sd",
- "sz",
- "nom.ag",
- "net.au",
- "sr",
- "aogashima.tokyo.jp",
- "nord-fron.no",
- "sl",
- "rentals",
- "iijima.nagano.jp",
- "kutchan.hokkaido.jp",
- "monza-brianza.it",
- "su",
- "matsuura.nagasaki.jp",
- "sohu",
- "fuji.shizuoka.jp",
- "higashiyamato.tokyo.jp",
- "harstad.no",
- "s.bg",
- "fujimi.nagano.jp",
- "sn",
- "viajes",
- "s.se",
- "rec.nf",
- "res.aero",
- "rich",
- "monzaebrianza.it",
- "se",
- "misato.wakayama.jp",
- "net.ag",
- "mitoyo.kagawa.jp",
- "st",
- "ragusa.it",
- "minamifurano.hokkaido.jp",
- "fujieda.shizuoka.jp",
- "kg",
- "vladikavkaz.ru",
- "sld.do",
- "higashiyama.kyoto.jp",
- "sb",
- "voting",
- "chuo.fukuoka.jp",
- "qld.gov.au",
- "agdenes.no",
- "si",
- "srv.br",
- "is-a-linux-user.org",
- "kin.okinawa.jp",
- "utashinai.hokkaido.jp",
- "entomology.museum",
- "kazimierz-dolny.pl",
- "morioka.iwate.jp",
- "rendalen.no",
- "ringerike.no",
- "kamaishi.iwate.jp",
- "sy",
- "nagoya.jp",
- "habikino.osaka.jp",
- "solar",
- "wakayama.jp",
- "snaase.no",
- "sm",
- "snoasa.no",
- "k12.tx.us",
- "slg.br",
- "numata.gunma.jp",
- "neat-url.com",
- "iwatsuki.saitama.jp",
- "nagasaki.jp",
- "kobayashi.miyazaki.jp",
- "norilsk.ru",
- "strand.no",
- "sa",
- "kitakami.iwate.jp",
- "sortland.no",
- "mihama.wakayama.jp",
- "nemuro.hokkaido.jp",
- "sel.no",
- "kvinnherad.no",
- "kokonoe.oita.jp",
- "motobu.okinawa.jp",
- "sv",
- "fujimi.saitama.jp",
- "nalchik.ru",
- "egersund.no",
- "nore-og-uvdal.no",
- "soc.lk",
- "is-lost.org",
- "hashimoto.wakayama.jp",
- "uwajima.ehime.jp",
- "ueno.gunma.jp",
- "net.kg",
- "solund.no",
- "rw",
- "kitahata.saga.jp",
- "sos.pl",
- "kuriyama.hokkaido.jp",
- "nkz.ru",
- "nagaokakyo.kyoto.jp",
- "a.prod.fastly.net",
- "higashinaruse.akita.jp",
- "saga.jp",
- "kamikoani.akita.jp",
- "nahari.kochi.jp",
- "aibetsu.hokkaido.jp",
- "fujisawa.iwate.jp",
- "numata.hokkaido.jp",
- "uki.kumamoto.jp",
- "koshimizu.hokkaido.jp",
- "zentsuji.kagawa.jp",
- "kasumigaura.ibaraki.jp",
- "fukuyama.hiroshima.jp",
- "russia.museum",
- "riik.ee",
- "webhop.info",
- "sh",
- "higashi.okinawa.jp",
- "rnd.ru",
- "sells-it.net",
- "samara.ru",
- "marnardal.no",
- "fukushima.jp",
- "solutions",
- "embetsu.hokkaido.jp",
- "biei.hokkaido.jp",
- "shoes",
- "sirdal.no",
- "sld.pa",
- "wakuya.miyagi.jp",
- "nes.buskerud.no",
- "kamiichi.toyama.jp",
- "kounosu.saitama.jp",
- "koshigaya.saitama.jp",
- "kagamino.okayama.jp",
- "railroad.museum",
- "karumai.iwate.jp",
- "sshn.se",
- "sondrio.it",
- "hidaka.wakayama.jp",
- "musashimurayama.tokyo.jp",
- "higashimurayama.tokyo.jp",
- "romskog.no",
- "sk",
- "roma.it",
- "kadena.okinawa.jp",
- "kusatsu.shiga.jp",
- "nagara.chiba.jp",
- "noshiro.akita.jp",
- "rome.it",
- "suldal.no",
- "kotohira.kagawa.jp",
- "singles",
- "sigdal.no",
- "kitamoto.saitama.jp",
- "naturalhistory.museum",
- "mihama.fukui.jp",
- "net.sh",
- "higashisumiyoshi.osaka.jp",
- "selfip.com",
- "kure.hiroshima.jp",
- "kunitomi.miyazaki.jp",
- "kanazawa.ishikawa.jp",
- "ushiku.ibaraki.jp",
- "systems",
- "saarland",
- "nagano.nagano.jp",
- "vaksdal.no",
- "mukawa.hokkaido.jp",
- "higashiizu.shizuoka.jp",
- "rubtsovsk.ru",
- "selfip.net",
- "noda.chiba.jp",
- "sunndal.no",
- "surnadal.no",
- "k12.ga.us",
- "net.th",
- "sec.ps",
- "axis.museum",
- "kaszuby.pl",
- "nesoddtangen.no",
- "sogndal.no",
- "kamikawa.hokkaido.jp",
- "net.bh",
- "svalbard.no",
- "marylhurst.museum",
- "stranda.no",
- "stordal.no",
- "kaluga.ru",
- "nannestad.no",
- "kakuda.miyagi.jp",
- "nose.osaka.jp",
- "natori.miyagi.jp",
- "etajima.hiroshima.jp",
- "savona.it",
- "sorreisa.no",
- "ena.gifu.jp",
- "godo.gifu.jp",
- "nishio.aichi.jp",
- "koga.fukuoka.jp",
- "sondre-land.no",
- "riodejaneiro.museum",
- "sauherad.no",
- "nieruchomosci.pl",
- "alaheadju.no",
- "groundhandling.aero",
- "anthropology.museum",
- "matsumoto.nagano.jp",
- "settlers.museum",
- "amsterdam.museum",
- "morotsuka.miyazaki.jp",
- "siellak.no",
- "higashiosaka.osaka.jp",
- "hikawa.shimane.jp",
- "gifu.gifu.jp",
- "isesaki.gunma.jp",
- "ryukyu",
- "higashiizumo.shimane.jp",
- "iglesias-carbonia.it",
- "itakura.gunma.jp",
- "naruto.tokushima.jp",
- "fujimino.saitama.jp",
- "name.hr",
- "niihama.ehime.jp",
- "rollag.no",
- "matsubushi.saitama.jp",
- "kitakata.miyazaki.jp",
- "unazuki.toyama.jp",
- "exhibition.museum",
- "hida.gifu.jp",
- "kurashiki.okayama.jp",
- "skodje.no",
- "net.ph",
- "maniwa.okayama.jp",
- "sassari.it",
- "fujiidera.osaka.jp",
- "y.bg",
- "y.se",
- "saotome.st",
- "kashihara.nara.jp",
- "ye",
- "stokke.no",
- "skanland.no",
- "ninja",
- "kaisei.kanagawa.jp",
- "yt",
- "ski.no",
- "fujikawa.yamanashi.jp",
- "usui.fukuoka.jp",
- "global.ssl.fastly.net",
- "mino.gifu.jp",
- "gov.gh",
- "edu.gh",
- "amagasaki.hyogo.jp",
- "ikusaka.nagano.jp",
- "com.gh",
- "square.museum",
- "kamikawa.saitama.jp",
- "kicks-ass.org",
- "rifu.miyagi.jp",
- "reviews",
- "reggioemilia.it",
- "kaho.fukuoka.jp",
- "suedtirol.it",
- "simbirsk.ru",
- "kamiamakusa.kumamoto.jp",
- "hotel.lk",
- "saratov.ru",
- "gamagori.aichi.jp",
- "kaga.ishikawa.jp",
- "uonuma.niigata.jp",
- "exposed",
- "saltdal.no",
- "kahoku.yamagata.jp",
- "fujikawa.shizuoka.jp",
- "nobeoka.miyazaki.jp",
- "kumejima.okinawa.jp",
- "stalbans.museum",
- "fujinomiya.shizuoka.jp",
- "haebaru.okinawa.jp",
- "rimini.it",
- "cuneo.it",
- "naumburg.museum",
- "ishikawa.okinawa.jp",
- "nagareyama.chiba.jp",
- "nord-aurdal.no",
- "kamisu.ibaraki.jp",
- "akaiwa.okayama.jp",
- "stv.ru",
- "furukawa.miyagi.jp",
- "skedsmo.no",
- "chikuma.nagano.jp",
- "aga.niigata.jp",
- "kushimoto.wakayama.jp",
- "vic.edu.au",
- "hikimi.shimane.jp",
- "kongsberg.no",
- "himeji.hyogo.jp",
- "aoste.it",
- "fermo.it",
- "hirono.fukushima.jp",
- "nagasu.kumamoto.jp",
- "vg",
- "naha.okinawa.jp",
- "marumori.miyagi.jp",
- "kitahiroshima.hokkaido.jp",
- "kuki.saitama.jp",
- "zao.miyagi.jp",
- "nagaoka.niigata.jp",
- "busan.kr",
- "gamo.shiga.jp",
- "naturalhistorymuseum.museum",
- "nago.okinawa.jp",
- "iitate.fukushima.jp",
- "sumy.ua",
- "nerima.tokyo.jp",
- "futaba.fukushima.jp",
- "sexy",
- "snz.ru",
- "sandnes.no",
- "narusawa.yamanashi.jp",
- "nirasaki.yamanashi.jp",
- "vgs.no",
- "sumoto.kumamoto.jp",
- "hirata.fukushima.jp",
- "bozen.it",
- "suisse.museum",
- "czest.pl",
- "mitake.gifu.jp",
- "siedlce.pl",
- "saitama.jp",
- "furudono.fukushima.jp",
- "wodzislaw.pl",
- "karikatur.museum",
- "selfip.org",
- "graz.museum",
- "spb.ru",
- "ashibetsu.hokkaido.jp",
- "ulsan.kr",
- "kimobetsu.hokkaido.jp",
- "aosta.it",
- "hakone.kanagawa.jp",
- "skoczow.pl",
- "sandefjord.no",
- "wassamu.hokkaido.jp",
- "mizunami.gifu.jp",
- "hobol.no",
- "fujisato.akita.jp",
- "snillfjord.no",
- "recreation.aero",
- "bomlo.no",
- "genoa.it",
- "kujukuri.chiba.jp",
- "svelvik.no",
- "greta.fr",
- "kunstsammlung.museum",
- "asnes.no",
- "stathelle.no",
- "fukumitsu.toyama.jp",
- "sex.pl",
- "herad.no",
- "cambridge.museum",
- "recipes",
- "aland.fi",
- "katsuragi.wakayama.jp",
- "nagatoro.saitama.jp",
- "6bone.pl",
- "mil.gh",
- "nagi.okayama.jp",
- "kuromatsunai.hokkaido.jp",
- "aogaki.hyogo.jp",
- "mikawa.yamagata.jp",
- "nakhodka.ru",
- "hisayama.fukuoka.jp",
- "floro.no",
- "forde.no",
- "wajiki.tokushima.jp",
- "kikonai.hokkaido.jp",
- "sendai.jp",
- "kahoku.ishikawa.jp",
- "naganohara.gunma.jp",
- "ardal.no",
- "sandnessjoen.no",
- "altai.ru",
- "chiyoda.gunma.jp",
- "hyogo.jp",
- "kanagawa.jp",
- "kvinesdal.no",
- "stjordal.no",
- "higashimatsuyama.saitama.jp",
- "hirokawa.fukuoka.jp",
- "frogn.no",
- "grane.no",
- "saga.saga.jp",
- "hemne.no",
- "sandiego.museum",
- "milan.it",
- "chungbuk.kr",
- "coloradoplateau.museum",
- "nsk.ru",
- "asahikawa.hokkaido.jp",
- "motosu.gifu.jp",
- "agrinet.tn",
- "mbone.pl",
- "nyny.museum",
- "mizumaki.fukuoka.jp",
- "santabarbara.museum",
- "k12.gu.us",
- "hurum.no",
- "hatogaya.saitama.jp",
- "gunma.jp",
- "nativeamerican.museum",
- "a.ssl.fastly.net",
- "kitagawa.kochi.jp",
- "bytom.pl",
- "fjell.no",
- "minami.fukuoka.jp",
- "b.ssl.fastly.net",
- "higashikagura.hokkaido.jp",
- "gulen.no",
- "narita.chiba.jp",
- "hamar.no",
- "kaizuka.osaka.jp",
- "niikappu.hokkaido.jp",
- "kudoyama.wakayama.jp",
- "udine.it",
- "kitayama.wakayama.jp",
- "csiro.au",
- "adult.ht",
- "nagiso.nagano.jp",
- "rahkkeravju.no",
- "bjugn.no",
- "andoy.no",
- "kamisato.saitama.jp",
- "yufu.oita.jp",
- "social",
- "higashihiroshima.hiroshima.jp",
- "bardu.no",
- "iwaizumi.iwate.jp",
- "zagan.pl",
- "soundandvision.museum",
- "net.mx",
- "ryokami.saitama.jp",
- "southcarolina.museum",
- "game-host.org",
- "stor-elvdal.no",
- "bandai.fukushima.jp",
- "reklam.hu",
- "flora.no",
- "nagawa.nagano.jp",
- "seto.aichi.jp",
- "homelinux.org",
- "surgeonshall.museum",
- "frana.no",
- "molde.no",
- "kinokawa.wakayama.jp",
- "giske.no",
- "fujiyoshida.yamanashi.jp",
- "saintlouis.museum",
- "gujo.gifu.jp",
- "murayama.yamagata.jp",
- "hitra.no",
- "modum.no",
- "kashima.saga.jp",
- "iwanuma.miyagi.jp",
- "haram.no",
- "minokamo.gifu.jp",
- "higashiyodogawa.osaka.jp",
- "shoo.okayama.jp",
- "froya.no",
- "ustka.pl",
- "iizuka.fukuoka.jp",
- "akashi.hyogo.jp",
- "rennebu.no",
- "barum.no",
- "sx",
- "yachts",
- "ng",
- "stpetersburg.museum",
- "hotel.hu",
- "vologda.ru",
- "roma.museum",
- "yao.osaka.jp",
- "wajima.ishikawa.jp",
- "koto.shiga.jp",
- "nagasaki.nagasaki.jp",
- "balat.no",
- "meloy.no",
- "hakusan.ishikawa.jp",
- "wlocl.pl",
- "ringebu.no",
- "kimino.wakayama.jp",
- "minamiaiki.nagano.jp",
- "ngo.lk",
- "net.gr",
- "sosa.chiba.jp",
- "nishiizu.shizuoka.jp",
- "heroy.nordland.no",
- "nakano.tokyo.jp",
- "santacruz.museum",
- "slattum.no",
- "masoy.no",
- "kamikitayama.nara.jp",
- "wanouchi.gifu.jp",
- "net.gp",
- "murakami.niigata.jp",
- "inatsuki.fukuoka.jp",
- "net.ge",
- "ashiya.hyogo.jp",
- "matsushima.miyagi.jp",
- "minamisanriku.miyagi.jp",
- "gmina.pl",
- "show.aero",
- "namerikawa.toyama.jp",
- "salangen.no",
- "matsumoto.kagoshima.jp",
- "susaki.kochi.jp",
- "galsa.no",
- "halsa.no",
- "hotel.tz",
- "kurate.fukuoka.jp",
- "forum.hu",
- "kanuma.tochigi.jp",
- "ehime.jp",
- "szkola.pl",
- "kouzushima.tokyo.jp",
- "saroma.hokkaido.jp",
- "games.hu",
- "alstahaug.no",
- "minamimaki.nagano.jp",
- "steiermark.museum",
- "numazu.shizuoka.jp",
- "aichi.jp",
- "stalowa-wola.pl",
- "yono.saitama.jp",
- "kamogawa.chiba.jp",
- "udono.mie.jp",
- "kurume.fukuoka.jp",
- "kamagaya.chiba.jp",
- "makinohara.shizuoka.jp",
- "southwest.museum",
- "kosuge.yamanashi.jp",
- "rankoshi.hokkaido.jp",
- "ngo.pl",
- "nishikatsura.yamanashi.jp",
- "komatsushima.tokushima.jp",
- "kutno.pl",
- "shinjo.nara.jp",
- "harima.hyogo.jp",
- "kasahara.gifu.jp",
- "miharu.fukushima.jp",
- "aikawa.kanagawa.jp",
- "shinto.gunma.jp",
- "kepno.pl",
- "gov.sy",
- "edu.sy",
- "com.sy",
- "sumida.tokyo.jp",
- "yamanashi.jp",
- "gov.ly",
- "ayabe.kyoto.jp",
- "edu.ly",
- "ashiya.fukuoka.jp",
- "com.ly",
- "kakinoki.shimane.jp",
- "edu.uy",
- "com.uy",
- "o.bg",
- "soo.kagoshima.jp",
- "o.se",
- "yamato.kumamoto.jp",
- "stjordalshalsen.no",
- "kumagaya.saitama.jp",
- "stuttgart.museum",
- "chita.ru",
- "gub.uy",
- "rishiri.hokkaido.jp",
- "hanawa.fukushima.jp",
- "org",
- "org.do",
- "org.om",
- "kamitonda.wakayama.jp",
- "org.dm",
- "suzaka.nagano.jp",
- "org.so",
- "org.rs",
- "org.ro",
- "kuban.ru",
- "gov.by",
- "ralingen.no",
- "org.sc",
- "org.ls",
- "org.lr",
- "org.sb",
- "com.by",
- "nakano.nagano.jp",
- "izumi.osaka.jp",
- "noto.ishikawa.jp",
- "org.lc",
- "org.lb",
- "soja.okayama.jp",
- "org.lk",
- "org.sl",
- "akita.jp",
- "shiroi.chiba.jp",
- "org.se",
- "inabe.mie.jp",
- "shisui.chiba.jp",
- "odo.br",
- "abeno.osaka.jp",
- "org.nr",
- "iwate.jp",
- "yamada.toyama.jp",
- "kokubunji.tokyo.jp",
- "oita.jp",
- "kitagawa.miyazaki.jp",
- "kyoto.jp",
- "org.es",
- "sokndal.no",
- "org.ec",
- "nakadomari.aomori.jp",
- "org.to",
- "org.tj",
- "org.tm",
- "org.sa",
- "org.sd",
- "kasuya.fukuoka.jp",
- "asahi.mie.jp",
- "vic.gov.au",
- "org.la",
- "org.ee",
- "yasuda.kochi.jp",
- "om",
- "izumo.shimane.jp",
- "gov.my",
- "edu.my",
- "org.bs",
- "org.br",
- "org.bo",
- "yura.wakayama.jp",
- "org.ua",
- "com.my",
- "org.bm",
- "sex.hu",
- "ashikaga.tochigi.jp",
- "onl",
- "gotsu.shimane.jp",
- "org.bb",
- "org.iq",
- "org.is",
- "org.ir",
- "osoyro.no",
- "org.im",
- "ilawa.pl",
- "org.na",
- "nishimera.miyazaki.jp",
- "finnoy.no",
- "yamazoe.nara.jp",
- "komatsu.ishikawa.jp",
- "org.qa",
- "maizuru.kyoto.jp",
- "fukusaki.hyogo.jp",
- "moriyama.shiga.jp",
- "notaires.fr",
- "honai.ehime.jp",
- "kyuragi.saga.jp",
- "org.ba",
- "gov.py",
- "org.jo",
- "edu.py",
- "com.py",
- "chiba.jp",
- "yatsuka.shimane.jp",
- "susono.shizuoka.jp",
- "org.mo",
- "naie.hokkaido.jp",
- "naka.ibaraki.jp",
- "med.ly",
- "saitama.saitama.jp",
- "org.tw",
- "org.mk",
- "zarow.pl",
- "hikone.shiga.jp",
- "ayagawa.kagawa.jp",
- "atsugi.kanagawa.jp",
- "org.je",
- "suginami.tokyo.jp",
- "munakata.fukuoka.jp",
- "org.ml",
- "org.me",
- "ovh",
- "akkeshi.hokkaido.jp",
- "nord-odal.no",
- "org.bw",
- "ainan.ehime.jp",
- "orland.no",
- "org.ar",
- "org.ac",
- "spjelkavik.no",
- "kadogawa.miyazaki.jp",
- "org.bi",
- "org.ma",
- "org.ps",
- "org.pr",
- "org.al",
- "colonialwilliamsburg.museum",
- "org.ae",
- "fujioka.gunma.jp",
- "org.pk",
- "mytis.ru",
- "shimoda.shizuoka.jp",
- "asago.hyogo.jp",
- "averoy.no",
- "org.pl",
- "ishikawa.fukushima.jp",
- "org.vc",
- "org.pe",
- "mutsu.aomori.jp",
- "kafjord.no",
- "yaroslavl.ru",
- "org.mw",
- "org.ve",
- "nikaho.akita.jp",
- "klabu.no",
- "mil.sy",
- "nishiokoppe.hokkaido.jp",
- "settsu.osaka.jp",
- "yusuhara.kochi.jp",
- "katagami.akita.jp",
- "kirov.ru",
- "org.pa",
- "bizen.okayama.jp",
- "usuki.oita.jp",
- "mil.uy",
- "kazan.ru",
- "matsuda.kanagawa.jp",
- "hokkaido.jp",
- "wakasa.fukui.jp",
- "ishinomaki.miyagi.jp",
- "nonoichi.ishikawa.jp",
- "unnan.shimane.jp",
- "media.pl",
- "shiksha",
- "kaneyama.fukushima.jp",
- "gwangju.kr",
- "osteroy.no",
- "org.ai",
- "org.hk",
- "gov.ky",
- "edu.ky",
- "semine.miyagi.jp",
- "com.ky",
- "nishiawakura.okayama.jp",
- "sakhalin.ru",
- "mil.by",
- "isehara.kanagawa.jp",
- "is-a-patsfan.org",
- "ibara.okayama.jp",
- "itami.hyogo.jp",
- "matsukawa.nagano.jp",
- "nakanojo.gunma.jp",
- "org.vi",
- "nikolaev.ua",
- "kaneyama.yamagata.jp",
- "kursk.ru",
- "naoshima.kagawa.jp",
- "aizumisato.fukushima.jp",
- "shimokitayama.nara.jp",
- "kamishihoro.hokkaido.jp",
- "fukagawa.hokkaido.jp",
- "ovre-eiker.no",
- "oppdal.no",
- "org.km",
- "yotsukaido.chiba.jp",
- "viking.museum",
- "shintoku.hokkaido.jp",
- "mil.my",
- "shimamoto.osaka.jp",
- "org.kp",
- "shimada.shizuoka.jp",
- "org.af",
- "shiojiri.nagano.jp",
- "ota.tokyo.jp",
- "minoh.osaka.jp",
- "fukushima.hokkaido.jp",
- "yatomi.aichi.jp",
- "mazury.pl",
- "iruma.saitama.jp",
- "net.gg",
- "kochi.jp",
- "mamurogawa.yamagata.jp",
- "org.pf",
- "oksnes.no",
- "kani.gifu.jp",
- "hanno.saitama.jp",
- "oystre-slidre.no",
- "gosen.niigata.jp",
- "odessa.ua",
- "mil.py",
- "otsuka",
- "ikoma.nara.jp",
- "yoshioka.gunma.jp",
- "ostroda.pl",
- "sasebo.nagasaki.jp",
- "minamiminowa.nagano.jp",
- "silk.museum",
- "ome.tokyo.jp",
- "higashikawa.hokkaido.jp",
- "varggat.no",
- "kashiwazaki.niigata.jp",
- "oygarden.no",
- "sugito.saitama.jp",
- "olecko.pl",
- "yoshimi.saitama.jp",
- "ikeda.osaka.jp",
- "org.ki",
- "minamidaito.okinawa.jp",
- "belau.pw",
- "kitagata.saga.jp",
- "soka.saitama.jp",
- "is-a-knight.org",
- "shinjuku.tokyo.jp",
- "cieszyn.pl",
- "hanyu.saitama.jp",
- "yoshida.saitama.jp",
- "oita.oita.jp",
- "ulvik.no",
- "yashiro.hyogo.jp",
- "chikuhoku.nagano.jp",
- "sg",
- "botany.museum",
- "kouhoku.saga.jp",
- "shonai.yamagata.jp",
- "sakurai.nara.jp",
- "shiraoka.saitama.jp",
- "hakata.fukuoka.jp",
- "nosegawa.nara.jp",
- "karatsu.saga.jp",
- "akita.akita.jp",
- "wakayama.wakayama.jp",
- "asaka.saitama.jp",
- "county.museum",
- "asker.no",
- "shimoji.okinawa.jp",
- "yuzhno-sakhalinsk.ru",
- "chikushino.fukuoka.jp",
- "kouyama.kagoshima.jp",
- "shinjo.yamagata.jp",
- "kanonji.kagawa.jp",
- "yoshida.shizuoka.jp",
- "akagi.shimane.jp",
- "szczecin.pl",
- "seranishi.hiroshima.jp",
- "konin.pl",
- "nakamura.kochi.jp",
- "fukui.jp",
- "koryo.nara.jp",
- "ikata.ehime.jp",
- "ide.kyoto.jp",
- "inami.wakayama.jp",
- "shinjo.okayama.jp",
- "sukumo.kochi.jp",
- "oirase.aomori.jp",
- "rakkestad.no",
- "setouchi.okayama.jp",
- "aridagawa.wakayama.jp",
- "yasaka.nagano.jp",
- "is-a-bruinsfan.org",
- "hirogawa.wakayama.jp",
- "ine.kyoto.jp",
- "higashine.yamagata.jp",
- "kamikawa.hyogo.jp",
- "hongo.hiroshima.jp",
- "shintomi.miyazaki.jp",
- "org.ru",
- "askoy.no",
- "is-by.us",
- "buzen.fukuoka.jp",
- "hanggliding.aero",
- "aisho.shiga.jp",
- "choyo.kumamoto.jp",
- "higashiomi.shiga.jp",
- "kunimi.fukushima.jp",
- "yoichi.hokkaido.jp",
- "org.ws",
- "fedje.no",
- "myoko.niigata.jp",
- "org.sg",
- "go.tz",
- "ed.ao",
- "stange.no",
- "co.ao",
- "co.sz",
- "co.uz",
- "co.tz",
- "ostrowwlkp.pl",
- "yashio.saitama.jp",
- "id.us",
- "co.us",
- "co.ls",
- "bykle.no",
- "media.hu",
- "nomi.ishikawa.jp",
- "nuernberg.museum",
- "org.ug",
- "az.us",
- "oristano.it",
- "il.us",
- "co.no",
- "kusatsu.gunma.jp",
- "as.us",
- "id.au",
- "ar.us",
- "shiraoi.hokkaido.jp",
- "vefsn.no",
- "gu.us",
- "co.rs",
- "org.ng",
- "al.us",
- "hl.no",
- "co.nl",
- "co.mu",
- "org.eg",
- "fl.us",
- "shibecha.hokkaido.jp",
- "in.us",
- "al.no",
- "co.ae",
- "it.ao",
- "co.st",
- "co.tt",
- "co.at",
- "hiroo.hokkaido.jp",
- "co.me",
- "aukra.no",
- "go.cr",
- "bu.no",
- "ed.cr",
- "ct.us",
- "in.rs",
- "co.cr",
- "gr.com",
- "bd.se",
- "naturbruksgymn.se",
- "co.cl",
- "ar.com",
- "ebino.miyazaki.jp",
- "hu.com",
- "eu.com",
- "atami.shizuoka.jp",
- "koriyama.fukushima.jp",
- "br.com",
- "hyuga.miyazaki.jp",
- "org.mu",
- "gd.cn",
- "gz.cn",
- "kanan.osaka.jp",
- "hi.us",
- "cn.com",
- "nuremberg.museum",
- "gs.cn",
- "karmoy.no",
- "orskog.no",
- "mo.us",
- "md.us",
- "bushey.museum",
- "hl.cn",
- "ms.us",
- "okinawa",
- "shinichi.hiroshima.jp",
- "york.museum",
- "id.ly",
- "chigasaki.kanagawa.jp",
- "shikaoi.hokkaido.jp",
- "mr.no",
- "go.kr",
- "co.kr",
- "vardo.no",
- "org.au",
- "hn.cn",
- "semboku.akita.jp",
- "hs.kr",
- "es.kr",
- "go.tj",
- "co.tj",
- "gb.com",
- "org.mg",
- "yatsushiro.kumamoto.jp",
- "mn.us",
- "he.cn",
- "co.gy",
- "co.tm",
- "go.ci",
- "me.tz",
- "yokohama",
- "ed.ci",
- "co.om",
- "co.ci",
- "sakura.chiba.jp",
- "sakaiminato.tottori.jp",
- "me.us",
- "cq.cn",
- "mt.us",
- "hm.no",
- "stargard.pl",
- "arida.wakayama.jp",
- "bibai.hokkaido.jp",
- "hb.cn",
- "vadso.no",
- "org.ag",
- "fi.cr",
- "yakutia.ru",
- "hi.cn",
- "co.ua",
- "sera.hiroshima.jp",
- "fm.no",
- "okayama.jp",
- "ga.us",
- "ia.us",
- "inami.toyama.jp",
- "ostroleka.pl",
- "mo.cn",
- "ca.us",
- "cr.ua",
- "mi.us",
- "co.ma",
- "shobara.hiroshima.jp",
- "koka.shiga.jp",
- "ut.us",
- "meiwa.mie.jp",
- "us.com",
- "aioi.hyogo.jp",
- "ha.no",
- "co.na",
- "erimo.hokkaido.jp",
- "yamatokoriyama.nara.jp",
- "nishihara.kumamoto.jp",
- "aa.no",
- "vagan.no",
- "gv.ao",
- "id.lv",
- "in.ua",
- "cn.ua",
- "iz.hr",
- "kitakata.fukushima.jp",
- "ms.kr",
- "inagi.tokyo.jp",
- "kiryu.gunma.jp",
- "sorfold.no",
- "k12.ny.us",
- "varoy.no",
- "in.na",
- "co.ca",
- "org.hu",
- "co.hu",
- "md.ci",
- "volda.no",
- "bj.cn",
- "stateofdelaware.museum",
- "fj.cn",
- "inawashiro.fukushima.jp",
- "saikai.nagasaki.jp",
- "ishigaki.okinawa.jp",
- "nuoro.it",
- "agrigento.it",
- "bv.nl",
- "gv.at",
- "ha.cn",
- "satosho.okayama.jp",
- "kagamiishi.fukushima.jp",
- "muika.niigata.jp",
- "ma.us",
- "valle.no",
- "city.yokohama.jp",
- "fussa.tokyo.jp",
- "go.th",
- "co.bi",
- "co.th",
- "zj.cn",
- "rovigo.it",
- "nishiwaki.hyogo.jp",
- "minamiawaji.hyogo.jp",
- "zt.ua",
- "za.com",
- "uy.com",
- "nishihara.okinawa.jp",
- "honjo.akita.jp",
- "nogata.fukuoka.jp",
- "ikeda.fukui.jp",
- "uz.ua",
- "suzu.ishikawa.jp",
- "ah.no",
- "ikawa.akita.jp",
- "kaminoyama.yamagata.jp",
- "ab.ca",
- "in.th",
- "kanegasaki.iwate.jp",
- "chijiwa.nagasaki.jp",
- "konan.shiga.jp",
- "sakado.saitama.jp",
- "us.na",
- "xz.cn",
- "kariwa.niigata.jp",
- "oji.nara.jp",
- "orenburg.ru",
- "fh.se",
- "am.br",
- "org.kg",
- "gs.oslo.no",
- "soma.fukushima.jp",
- "selfip.info",
- "asahi.toyama.jp",
- "fm.br",
- "kasai.hyogo.jp",
- "yorkshire.museum",
- "okinawa.jp",
- "xn--45q11c",
- "motegi.tochigi.jp",
- "co.ba",
- "ca.na",
- "uji.kyoto.jp",
- "qh.cn",
- "randaberg.no",
- "ak.us",
- "nishiazai.shiga.jp",
- "ah.cn",
- "ngo.ph",
- "cv.ua",
- "shiiba.miyazaki.jp",
- "kosei.shiga.jp",
- "fuchu.hiroshima.jp",
- "bl.uk",
- "shikabe.hokkaido.jp",
- "katashina.gunma.jp",
- "mb.ca",
- "stockholm.museum",
- "nachikatsuura.wakayama.jp",
- "kanna.gunma.jp",
- "video.hu",
- "kanra.gunma.jp",
- "ec",
- "cc",
- "gov.co",
- "edu.co",
- "gov.cm",
- "com.co",
- "chofu.tokyo.jp",
- "ac",
- "ketrzyn.pl",
- "koshu.yamanashi.jp",
- "omasvuotna.no",
- "gov.cl",
- "gob.cl",
- "sakuho.nagano.jp",
- "actor",
- "svizzera.museum",
- "zp.ua",
- "fudai.iwate.jp",
- "int.co",
- "overhalla.no",
- "hk.cn",
- "shimamaki.hokkaido.jp",
- "xj.cn",
- "mi.th",
- "abira.hokkaido.jp",
- "gov.cd",
- "xn--90a3ac",
- "wi.us",
- "monza.it",
- "ako.hyogo.jp",
- "minamiuonuma.niigata.jp",
- "fuchu.toyama.jp",
- "nishiarita.saga.jp",
- "hofu.yamaguchi.jp",
- "wy.us",
- "ecn.br",
- "urawa.saitama.jp",
- "xn--d1acj3b",
- "mc",
- "edu.cw",
- "org.sh",
- "yamanashi.yamanashi.jp",
- "xn--55qx5d",
- "eco.br",
- "fuchu.tokyo.jp",
- "com.cw",
- "fujisawa.kanagawa.jp",
- "ikeda.hokkaido.jp",
- "iwade.wakayama.jp",
- "edu.ci",
- "com.ci",
- "academy",
- "ck.ua",
- "setagaya.tokyo.jp",
- "makurazaki.kagoshima.jp",
- "uk.com",
- "meiwa.gunma.jp",
- "k12.ky.us",
- "xn--55qx5d.cn",
- "namegawa.saitama.jp",
- "wa.us",
- "oizumi.gunma.jp",
- "int.ci",
- "sobetsu.hokkaido.jp",
- "org.bh",
- "wa.au",
- "matsuyama.ehime.jp",
- "ws.na",
- "kanzaki.saga.jp",
- "otsuki.kochi.jp",
- "xn--io0a7i",
- "izena.okinawa.jp",
- "xn--slat-5na.no",
- "kozagawa.wakayama.jp",
- "wv.us",
- "yamatotakada.nara.jp",
- "xn--zfr164b",
- "blogspot.be",
- "iide.yamagata.jp",
- "xn--p1ai",
- "yamato.kanagawa.jp",
- "gf",
- "sakaki.nagano.jp",
- "cf",
- "kosai.shizuoka.jp",
- "mk.ua",
- "com.fr",
- "af",
- "xn--io0a7i.cn",
- "naka.hiroshima.jp",
- "bf",
- "kikugawa.shizuoka.jp",
- "ks.us",
- "aca.pro",
- "askim.no",
- "nesna.no",
- "yokote.akita.jp",
- "higashimatsushima.miyagi.jp",
- "yurihonjo.akita.jp",
- "orkdal.no",
- "online.museum",
- "asso.mc",
- "family.museum",
- "sakawa.kochi.jp",
- "onna.okinawa.jp",
- "go.id",
- "xn--smla-hra.no",
- "sannohe.aomori.jp",
- "co.id",
- "rikuzentakata.iwate.jp",
- "kyowa.akita.jp",
- "id.ir",
- "omi.nagano.jp",
- "blogspot.bj",
- "co.ir",
- "xn--seral-lra.no",
- "co.mw",
- "org.ph",
- "naroy.no",
- "xn--3bst00m",
- "chuo.yamanashi.jp",
- "xn--kpry57d",
- "kr.com",
- "info.ec",
- "co.rw",
- "uruma.okinawa.jp",
- "yokohama.jp",
- "sakata.yamagata.jp",
- "mil.co",
- "yamanakako.yamanashi.jp",
- "vestby.no",
- "honjo.saitama.jp",
- "omsk.ru",
- "co.in",
- "komae.tokyo.jp",
- "mil.cl",
- "xn--skjk-soa.no",
- "yokoze.saitama.jp",
- "go.it",
- "co.it",
- "kakegawa.shizuoka.jp",
- "cz.it",
- "grong.no",
- "xn--j1amh",
- "contractors",
- "yamashina.kyoto.jp",
- "is.it",
- "zgrad.ru",
- "yomitan.okinawa.jp",
- "gr.it",
- "cs.it",
- "shizuoka.jp",
- "cr.it",
- "ao.it",
- "ae.org",
- "cl.it",
- "shishikui.tokushima.jp",
- "brescia.it",
- "ar.it",
- "bo.it",
- "xn--yer-zna.no",
- "ky.us",
- "bz.it",
- "xn--rdal-poa.no",
- "al.it",
- "bs.it",
- "br.it",
- "fr.it",
- "xn--od0alg.cn",
- "bl.it",
- "en.it",
- "iwata.shizuoka.jp",
- "cn.it",
- "blogspot.cf",
- "2000.hu",
- "motorcycles",
- "ge.it",
- "an.it",
- "itano.tokushima.jp",
- "ce.it",
- "rieti.it",
- "xn--rland-uua.no",
- "bn.it",
- "ct.it",
- "is-an-actress.com",
- "aq.it",
- "vagsoy.no",
- "at.it",
- "xn--mely-ira.no",
- "fe.it",
- "is-a-caterer.com",
- "bt.it",
- "nagahama.shiga.jp",
- "cb.it",
- "okutama.tokyo.jp",
- "ks.ua",
- "kr.ua",
- "yonabaru.okinawa.jp",
- "co.im",
- "roros.no",
- "sanuki.kagawa.jp",
- "omi.niigata.jp",
- "xn--sr-aurdal-l8a.no",
- "ci.it",
- "us.org",
- "kitanakagusuku.okinawa.jp",
- "do",
- "nakijin.okinawa.jp",
- "dz",
- "mo.it",
- "yakumo.hokkaido.jp",
- "ms.it",
- "web.co",
- "edu.sn",
- "is-slick.com",
- "com.sn",
- "bi.it",
- "xn--troms-zua.no",
- "fi.it",
- "hiratsuka.kanagawa.jp",
- "ozu.ehime.jp",
- "is-an-artist.com",
- "coop",
- "navigation.aero",
- "xn--asky-ira.no",
- "art.sn",
- "feedback",
- "koori.fukushima.jp",
- "oshima.tokyo.jp",
- "d.bg",
- "grue.no",
- "coop.mw",
- "xn--rros-gra.no",
- "d.se",
- "ud.it",
- "mn.it",
- "dnp",
- "ukiha.fukuoka.jp",
- "omachi.nagano.jp",
- "enterprises",
- "zgora.pl",
- "alta.no",
- "de",
- "im.it",
- "k12.wy.us",
- "sumoto.hyogo.jp",
- "me.it",
- "co.bw",
- "arna.no",
- "kamakura.kanagawa.jp",
- "cloudcontrolled.com",
- "mt.it",
- "info.na",
- "gov.tn",
- "my.id",
- "aure.no",
- "com.tn",
- "coop.br",
- "xn--merker-kua.no",
- "fm.it",
- "xn--sr-odal-q1a.no",
- "coop.py",
- "horten.no",
- "xn--i1b6b1a6a2e",
- "xn--sknit-yqa.no",
- "mb.it",
- "info.nr",
- "gran.no",
- "fusa.no",
- "ca.it",
- "dep.no",
- "mi.it",
- "gov.in",
- "edu.in",
- "honefoss.no",
- "shingo.aomori.jp",
- "etne.no",
- "bodo.no",
- "arq.br",
- "xn--smna-gra.no",
- "ba.it",
- "xn--trgstad-r1a.no",
- "za.org",
- "risor.no",
- "city.kobe.jp",
- "mormon",
- "starnberg.museum",
- "is-a-painter.com",
- "berg.no",
- "istmein.de",
- "yame.fukuoka.jp",
- "iheya.okinawa.jp",
- "ap.it",
- "ens.tn",
- "xn--risa-5na.no",
- "arts.nf",
- "km.ua",
- "is-an-actor.com",
- "av.it",
- "dj",
- "from-co.net",
- "xn--mli-tla.no",
- "moss.no",
- "xn--linds-pra.no",
- "from-la.net",
- "xn--yfro4i67o",
- "xn--55qx5d.hk",
- "dm",
- "hole.no",
- "gov.mn",
- "historisch.museum",
- "edu.mn",
- "info.nf",
- "yakumo.shimane.jp",
- "from-ma.com",
- "gen.in",
- "coop.mv",
- "ind.tn",
- "steigen.no",
- "xn--risr-ira.no",
- "from-ca.com",
- "from-mo.com",
- "rodoy.no",
- "from-ri.com",
- "berlin",
- "from-mi.com",
- "omachi.saga.jp",
- "bergen.no",
- "hammerfest.no",
- "romsa.no",
- "from-pa.com",
- "suli.hu",
- "from-or.com",
- "swiebodzin.pl",
- "from-az.net",
- "from-ky.com",
- "wf",
- "fin.tn",
- "from-ok.com",
- "edu.an",
- "ind.in",
- "camp",
- "com.an",
- "iwaki.fukushima.jp",
- "okegawa.saitama.jp",
- "iwate.iwate.jp",
- "merseine.nu",
- "from-pr.com",
- "amot.no",
- "niki.hokkaido.jp",
- "okayama.okayama.jp",
- "xn--trany-yua.no",
- "ch.it",
- "kv.ua",
- "community",
- "from-al.com",
- "gov.pn",
- "from-mn.com",
- "edu.pn",
- "eniwa.hokkaido.jp",
- "omaezaki.shizuoka.jp",
- "agrar.hu",
- "bialowieza.pl",
- "gov.vn",
- "edu.vn",
- "homeunix.com",
- "com.vn",
- "nakasatsunai.hokkaido.jp",
- "xn--io0a7i.hk",
- "oxford.museum",
- "firm.nf",
- "aerodrome.aero",
- "from-ar.com",
- "gov.cu",
- "edu.cu",
- "is-a-socialist.com",
- "cremona.it",
- "com.cu",
- "rygge.no",
- "kofu.yamanashi.jp",
- "kunigami.okinawa.jp",
- "folkebibl.no",
- "xn--rennesy-v1a.no",
- "from-ak.com",
- "kagoshima.kagoshima.jp",
- "motorcycle.museum",
- "is-a-doctor.com",
- "net.sy",
- "from-wa.com",
- "xn--bmlo-gra.no",
- "coop.km",
- "bo.telemark.no",
- "fukui.fukui.jp",
- "net.ly",
- "homeunix.net",
- "int.vn",
- "from-wi.com",
- "net.uy",
- "radom.pl",
- "from-wy.com",
- "from-il.com",
- "from-ia.com",
- "sunagawa.hokkaido.jp",
- "inf.cu",
- "kh.ua",
- "nakaniikawa.toyama.jp",
- "rissa.no",
- "dni.us",
- "xn--rde-ula.no",
- "otsuki.yamanashi.jp",
- "shikokuchuo.ehime.jp",
- "xn--hobl-ira.no",
- "control.aero",
- "osakasayama.osaka.jp",
- "kasuga.hyogo.jp",
- "ivgu.no",
- "gob.hn",
- "edu.hn",
- "insurance.aero",
- "com.hn",
- "getmyip.com",
- "dance",
- "kaita.hiroshima.jp",
- "donetsk.ua",
- "onomichi.hiroshima.jp",
- "xn--sandy-yua.no",
- "dnsalias.com",
- "grozny.ru",
- "itoigawa.niigata.jp",
- "from-in.com",
- "beiarn.no",
- "seoul.kr",
- "xn--msy-ula0h.no",
- "xn--4gbrim",
- "batsfjord.no",
- "dnsalias.net",
- "xn--lcvr32d.hk",
- "radoy.no",
- "yonago.tottori.jp",
- "artgallery.museum",
- "in-the-band.net",
- "yoshikawa.saitama.jp",
- "historisches.museum",
- "from-wv.com",
- "rauma.no",
- "from-fl.com",
- "xn--od0alg.hk",
- "from-ms.com",
- "ninomiya.kanagawa.jp",
- "cooking",
- "xn--ngbc5azd",
- "is-a-democrat.com",
- "dk",
- "webhop.biz",
- "from-ks.com",
- "dynalias.com",
- "gov.kn",
- "edu.kn",
- "net.my",
- "volyn.ua",
- "halden.no",
- "salzburg.museum",
- "xn--vard-jra.no",
- "kurogi.fukuoka.jp",
- "xn--lrenskog-54a.no",
- "xn--mjndalen-64a.no",
- "elvendrell.museum",
- "histoire.museum",
- "mil.in",
- "ambulance.aero",
- "org.mx",
- "frosinone.it",
- "sopot.pl",
- "sasayama.hyogo.jp",
- "dynalias.net",
- "association.aero",
- "holdings",
- "otofuke.hokkaido.jp",
- "belluno.it",
- "act.au",
- "co.je",
- "is-a-rockstar.com",
- "biz.vn",
- "yokaichiba.chiba.jp",
- "namegata.ibaraki.jp",
- "yasuoka.nagano.jp",
- "bahccavuotna.no",
- "net.py",
- "xn--lesund-hua.no",
- "iamallama.com",
- "nanmoku.gunma.jp",
- "dyndns.tv",
- "medecin.km",
- "xn--hery-ira.nordland.no",
- "magazine.aero",
- "nakanoto.ishikawa.jp",
- "drobak.no",
- "balsfjord.no",
- "messina.it",
- "gx.cn",
- "xn--od0aq3b.hk",
- "brussel.museum",
- "xn--rst-0na.no",
- "funagata.yamagata.jp",
- "frogans",
- "gemological.museum",
- "mjondalen.no",
- "oshino.yamanashi.jp",
- "here-for-more.info",
- "com.gy",
- "from-de.com",
- "maison",
- "iki.fi",
- "xn--mgb2ddes",
- "xn--80aswg",
- "artdeco.museum",
- "kasuga.fukuoka.jp",
- "francaise.museum",
- "gose.nara.jp",
- "xn--lury-ira.no",
- "international",
- "wa.edu.au",
- "from-oh.com",
- "yamaga.kumamoto.jp",
- "crotone.it",
- "is-a-anarchist.com",
- "siena.it",
- "defense.tn",
- "history.museum",
- "k12.co.us",
- "dielddanuorri.no",
- "xn--zf0avx.hk",
- "vt.us",
- "rikubetsu.hokkaido.jp",
- "stord.no",
- "org.gr",
- "eastcoast.museum",
- "shinshinotsu.hokkaido.jp",
- "midatlantic.museum",
- "obihiro.hokkaido.jp",
- "association.museum",
- "raisa.no",
- "store.ro",
- "go.jp",
- "kr.it",
- "ed.jp",
- "co.jp",
- "sogne.no",
- "org.gp",
- "fishing",
- "cloudcontrolapp.com",
- "org.ge",
- "shioya.tochigi.jp",
- "store.st",
- "gr.jp",
- "vi.us",
- "yoro.gifu.jp",
- "ando.nara.jp",
- "ad.jp",
- "countryestate.museum",
- "encyclopedic.museum",
- "yuki.ibaraki.jp",
- "nishi.osaka.jp",
- "hagi.yamaguchi.jp",
- "murmansk.ru",
- "chambagri.fr",
- "xn--vgan-qoa.no",
- "sorum.no",
- "oumu.hokkaido.jp",
- "giessen.museum",
- "xn--zf0ao64a.tw",
- "mil.hn",
- "boston.museum",
- "afjord.no",
- "vikna.no",
- "k12.ca.us",
- "xn--uc0ay4a.hk",
- "drammen.no",
- "net.ky",
- "valley.museum",
- "wielun.pl",
- "mallorca.museum",
- "xn--b-5ga.nordland.no",
- "amusement.aero",
- "xn--muost-0qa.no",
- "xn--karmy-yua.no",
- "xn--mgbab2bd",
- "ozu.kumamoto.jp",
- "xn--osyro-wua.no",
- "miki.hyogo.jp",
- "florida.museum",
- "birkenes.no",
- "awaji.hyogo.jp",
- "gloppen.no",
- "stryn.no",
- "moskenes.no",
- "komforb.se",
- "somna.no",
- "orkanger.no",
- "campobasso.it",
- "mx.na",
- "niimi.okayama.jp",
- "org.gi",
- "va.us",
- "handson.museum",
- "ino.kochi.jp",
- "hobby-site.com",
- "homeunix.org",
- "xn--mlatvuopmi-s4a.no",
- "hammarfeasta.no",
- "va.no",
- "saka.hiroshima.jp",
- "nakagawa.nagano.jp",
- "seljord.no",
- "gniezno.pl",
- "austin.museum",
- "dyndns.org",
- "vn.ua",
- "snasa.no",
- "eastafrica.museum",
- "bristol.museum",
- "berlin.museum",
- "balsan.it",
- "shiroishi.saga.jp",
- "modern.museum",
- "campidanomedio.it",
- "radio.br",
- "charter.aero",
- "botanical.museum",
- "ama.aichi.jp",
- "xn--vg-yiab.no",
- "sakuragawa.ibaraki.jp",
- "kitagata.gifu.jp",
- "shimokawa.hokkaido.jp",
- "kyowa.hokkaido.jp",
- "chiropractic.museum",
- "costume.museum",
- "nakagawa.hokkaido.jp",
- "carrier.museum",
- "shizuoka.shizuoka.jp",
- "frei.no",
- "asahi.nagano.jp",
- "dnsalias.org",
- "slask.pl",
- "bauern.museum",
- "desi",
- "clinton.museum",
- "gallery.museum",
- "nakagawa.tokushima.jp",
- "helsinki.museum",
- "futsu.nagasaki.jp",
- "shikama.miyagi.jp",
- "k12.fl.us",
- "dvrdns.org",
- "xn--uc0atv.hk",
- "hizen.saga.jp",
- "garden.museum",
- "smola.no",
- "is-leet.com",
- "xn--pgbs0dh",
- "badajoz.museum",
- "shonai.fukuoka.jp",
- "xn--mot-tla.no",
- "salat.no",
- "deatnu.no",
- "miasa.nagano.jp",
- "mobi.na",
- "inuyama.aichi.jp",
- "sauda.no",
- "divtasvuodna.no",
- "is-a-green.com",
- "dynalias.org",
- "oppegard.no",
- "corporation.museum",
- "selbu.no",
- "xn--6qq986b3xl",
- "seirou.niigata.jp",
- "mansion.museum",
- "cechire.com",
- "hattfjelldal.no",
- "from-ga.com",
- "from-mt.com",
- "imari.saga.jp",
- "from-ct.com",
- "isshiki.aichi.jp",
- "alabama.museum",
- "okinawa.okinawa.jp",
- "burghof.museum",
- "castres.museum",
- "shinagawa.tokyo.jp",
- "naklo.pl",
- "kamigori.hyogo.jp",
- "community.museum",
- "culture.museum",
- "ushuaia.museum",
- "astronomy.museum",
- "cincinnati.museum",
- "izumi.kagoshima.jp",
- "farmers.museum",
- "art.museum",
- "yonezawa.yamagata.jp",
- "xn--krager-gya.no",
- "nakama.fukuoka.jp",
- "skaun.no",
- "nanbu.yamanashi.jp",
- "annefrank.museum",
- "massa-carrara.it",
- "aarborte.no",
- "vc",
- "sanok.pl",
- "osakikamijima.hiroshima.jp",
- "xn--kfjord-iua.no",
- "okinoshima.shimane.jp",
- "oiso.kanagawa.jp",
- "xn--koluokta-7ya57h.no",
- "halloffame.museum",
- "gets-it.net",
- "gov.dz",
- "edu.dz",
- "ps",
- "com.dz",
- "pr",
- "chino.nagano.jp",
- "yasu.shiga.jp",
- "is-a-personaltrainer.com",
- "xn--1qqw23a",
- "mihama.mie.jp",
- "pl",
- "bus.museum",
- "kvam.no",
- "xn--vgsy-qoa0j.no",
- "xn--bievt-0qa.no",
- "mobi.ng",
- "post",
- "kurgan.ru",
- "art.dz",
- "kaufen",
- "pub",
- "hamburg.museum",
- "pro",
- "asahi.yamagata.jp",
- "com.uz",
- "historicalsociety.museum",
- "mazowsze.pl",
- "onga.fukuoka.jp",
- "pro.om",
- "p.bg",
- "pn",
- "p.se",
- "groks-the.info",
- "wildlife.museum",
- "pe",
- "nd.us",
- "western.museum",
- "air.museum",
- "ikeda.nagano.jp",
- "and.museum",
- "dlugoleka.pl",
- "arita.saga.jp",
- "durham.museum",
- "pt",
- "agano.niigata.jp",
- "yamada.fukuoka.jp",
- "horonobe.hokkaido.jp",
- "amli.no",
- "yamato.fukushima.jp",
- "nl.no",
- "communication.museum",
- "gov.bz",
- "edu.bz",
- "communications.museum",
- "com.bz",
- "xn--ystre-slidre-ujb.no",
- "pro.ec",
- "xn--mgbaam7a8h",
- "anan.nagano.jp",
- "pub.sa",
- "ne.tz",
- "xn--wgbh1c",
- "missile.museum",
- "ne.us",
- "ulm.museum",
- "no.com",
- "unzen.nagasaki.jp",
- "homebuilt.aero",
- "funabashi.chiba.jp",
- "pro.br",
- "k12.tn.us",
- "botanicalgarden.museum",
- "yasugi.shimane.jp",
- "dallas.museum",
- "sklep.pl",
- "py",
- "for-the.biz",
- "nt.no",
- "nt.ro",
- "per.la",
- "nt.au",
- "marburg.museum",
- "pro.na",
- "usa.museum",
- "iida.nagano.jp",
- "presse.ml",
- "xn--hcesuolo-7ya35b.no",
- "rovno.ua",
- "xn--uc0atv.tw",
- "matsuno.ehime.jp",
- "chirurgiens-dentistes.fr",
- "pm",
- "kustanai.ru",
- "can.museum",
- "prof.pr",
- "k12.in.us",
- "groks-this.info",
- "casadelamoneda.museum",
- "amami.kagoshima.jp",
- "sport.hu",
- "xn--wcvs22d.hk",
- "skien.no",
- "xn--trna-woa.no",
- "dell-ogliastra.it",
- "ny.us",
- "hara.nagano.jp",
- "xn--bearalvhki-y4a.no",
- "presse.ci",
- "ureshino.mie.jp",
- "city.nagoya.jp",
- "onjuku.chiba.jp",
- "satsumasendai.kagoshima.jp",
- "hioki.kagoshima.jp",
- "shiga.jp",
- "pa",
- "nj.us",
- "xn--drbak-wua.no",
- "ritto.shiga.jp",
- "gov.az",
- "edu.az",
- "kicks-ass.net",
- "com.az",
- "nm.us",
- "parts",
- "british.museum",
- "xn--kprw13d",
- "appspot.com",
- "k12.mn.us",
- "shiroishi.miyagi.jp",
- "xn--wgbl6a",
- "boutique",
- "ne.kr",
- "urbinopesaro.it",
- "global.prod.fastly.net",
- "heritage.museum",
- "nakayama.yamagata.jp",
- "ueda.nagano.jp",
- "pink",
- "dudinka.ru",
- "wloclawek.pl",
- "int.az",
- "kamisunagawa.hokkaido.jp",
- "arteducation.museum",
- "pro.pr",
- "nanbu.tottori.jp",
- "presse.km",
- "yamagata.jp",
- "nishi.fukuoka.jp",
- "plo.ps",
- "higashiagatsuma.gunma.jp",
- "pila.pl",
- "hakui.ishikawa.jp",
- "nanto.toyama.jp",
- "nanae.hokkaido.jp",
- "to",
- "td",
- "port.fr",
- "tz",
- "kiho.mie.jp",
- "goto.nagasaki.jp",
- "rishirifuji.hokkaido.jp",
- "tr",
- "nv.us",
- "tl",
- "rivne.ua",
- "cosenza.it",
- "nm.cn",
- "yamal.ru",
- "geology.museum",
- "tools",
- "mad.museum",
- "matsumae.hokkaido.jp",
- "ns.ca",
- "t.bg",
- "blogsite.org",
- "tn",
- "psc.br",
- "t.se",
- "oga.akita.jp",
- "ph",
- "pics",
- "shichikashuku.miyagi.jp",
- "aki.kochi.jp",
- "nl.ca",
- "sango.nara.jp",
- "zoology.museum",
- "pri.ee",
- "nu.ca",
- "psi.br",
- "komono.mie.jp",
- "per.nf",
- "sejny.pl",
- "tt",
- "xn--srum-gra.no",
- "akune.kagoshima.jp",
- "photo",
- "photos",
- "seika.kyoto.jp",
- "manno.kagawa.jp",
- "otoineppu.hokkaido.jp",
- "xn--80adxhks",
- "interactive.museum",
- "tromso.no",
- "kumano.mie.jp",
- "vs.it",
- "partners",
- "vr.it",
- "org.gg",
- "nt.ca",
- "nh.us",
- "go.ug",
- "shimabara.nagasaki.jp",
- "ppg.br",
- "xn--vler-qoa.hedmark.no",
- "co.ug",
- "co.ag",
- "tips",
- "yaotsu.gifu.jp",
- "hirono.iwate.jp",
- "ibaraki.ibaraki.jp",
- "chizu.tottori.jp",
- "kirkenes.no",
- "nango.fukushima.jp",
- "nb.ca",
- "tur.br",
- "xn--rholt-mra.no",
- "kisosaki.mie.jp",
- "mil.tz",
- "bokn.no",
- "co.gg",
- "california.museum",
- "ve.it",
- "pk",
- "gov.kz",
- "edu.kz",
- "com.kz",
- "sandoy.no",
- "vt.it",
- "xn--mgbayh7gpa",
- "tromsa.no",
- "nishikawa.yamagata.jp",
- "tj",
- "kommune.no",
- "biz.az",
- "tm",
- "trd.br",
- "coldwar.museum",
- "vb.it",
- "teo.br",
- "automotive.museum",
- "troandin.no",
- "vi.it",
- "herokuapp.com",
- "tel",
- "eiheiji.fukui.jp",
- "yuzawa.niigata.jp",
- "khmelnitskiy.ua",
- "today",
- "tienda",
- "pistoia.it",
- "catanzaro.it",
- "elb.amazonaws.com",
- "imizu.toyama.jp",
- "tattoo",
- "prd.km",
- "ayase.kanagawa.jp",
- "kakogawa.hyogo.jp",
- "herokussl.com",
- "sakura.tochigi.jp",
- "palana.ru",
- "notogawa.shiga.jp",
- "xn--vhquv",
- "gateway.museum",
- "sveio.no",
- "equipment.aero",
- "iwakura.aichi.jp",
- "voss.no",
- "ookuwa.nagano.jp",
- "tur.ar",
- "nc",
- "from-hi.com",
- "tv",
- "gov.cx",
- "nom.co",
- "act.edu.au",
- "tmp.br",
- "tysnes.no",
- "mil.az",
- "birdart.museum",
- "shirahama.wakayama.jp",
- "is-a-musician.com",
- "rl.no",
- "fineart.museum",
- "yabuki.fukushima.jp",
- "va.it",
- "ryuoh.shiga.jp",
- "pescara.it",
- "dyndns.ws",
- "tozsde.hu",
- "net.co",
- "ohkura.yamagata.jp",
- "trader.aero",
- "mine.nu",
- "rebun.hokkaido.jp",
- "war.museum",
- "ro.com",
- "palermo.it",
- "portland.museum",
- "karpacz.pl",
- "vv.it",
- "th",
- "tottori.jp",
- "vega.no",
- "mulhouse.museum",
- "washingtondc.museum",
- "ru.com",
- "chonan.chiba.jp",
- "suita.osaka.jp",
- "samukawa.kanagawa.jp",
- "ath.cx",
- "ri.us",
- "safety.aero",
- "trento.it",
- "ooshika.nagano.jp",
- "medio-campidano.it",
- "plumbing",
- "maritime.museum",
- "hannan.osaka.jp",
- "from-ut.com",
- "kchr.ru",
- "khmelnytskyi.ua",
- "xn--mk0axi.hk",
- "seiyo.ehime.jp",
- "wada.nagano.jp",
- "indian.museum",
- "net.cw",
- "miura.kanagawa.jp",
- "andriatranibarletta.it",
- "xn--klbu-woa.no",
- "yamamoto.miyagi.jp",
- "tk",
- "shima.mie.jp",
- "chihayaakasaka.osaka.jp",
- "net.ci",
- "re.kr",
- "ogi.saga.jp",
- "zushi.kanagawa.jp",
- "workinggroup.aero",
- "firenze.it",
- "watchandclock.museum",
- "vang.no",
- "kameyama.mie.jp",
- "valer.ostfold.no",
- "xn--hpmir-xqa.no",
- "satte.saitama.jp",
- "teramo.it",
- "pomorskie.pl",
- "muenchen.museum",
- "tysfjord.no",
- "yugawa.fukushima.jp",
- "vaga.no",
- "mil.kz",
- "public.museum",
- "nf",
- "plants.museum",
- "homelinux.net",
- "nom.fr",
- "tra.kp",
- "verran.no",
- "tonsberg.no",
- "xn--vrggt-xqad.no",
- "pw",
- "yamagata.nagano.jp",
- "dating",
- "tourism.tn",
- "ichiba.tokushima.jp",
- "homelinux.com",
- "pasadena.museum",
- "hatsukaichi.hiroshima.jp",
- "divttasvuotna.no",
- "ebina.kanagawa.jp",
- "padova.it",
- "dellogliastra.it",
- "presidio.museum",
- "kinko.kagoshima.jp",
- "sanda.hyogo.jp",
- "saiki.oita.jp",
- "xn--leagaviika-52b.no",
- "zakopane.pl",
- "tingvoll.no",
- "kiso.nagano.jp",
- "portal.museum",
- "nanao.ishikawa.jp",
- "topology.museum",
- "per.sg",
- "veterinaire.fr",
- "publ.pt",
- "isa-geek.com",
- "xn--j6w193g",
- "xn--brum-voa.no",
- "sakahogi.gifu.jp",
- "environmentalconservation.museum",
- "akiruno.tokyo.jp",
- "chesapeakebay.museum",
- "surrey.museum",
- "isa-geek.net",
- "no.it",
- "higashikagawa.kagawa.jp",
- "indianapolis.museum",
- "ora.gunma.jp",
- "otsu.shiga.jp",
- "pilots.museum",
- "ptz.ru",
- "polkowice.pl",
- "nu.it",
- "yahiko.niigata.jp",
- "dinosaur.museum",
- "bo.nordland.no",
- "starostwo.gov.pl",
- "okuizumo.shimane.jp",
- "indianmarket.museum",
- "portlligat.museum",
- "rs.ba",
- "aguni.okinawa.jp",
- "org.gh",
- "express.aero",
- "shiso.hyogo.jp",
- "rv.ua",
- "seiro.niigata.jp",
- "preservation.museum",
- "ota.gunma.jp",
- "ichihara.chiba.jp",
- "minamiashigara.kanagawa.jp",
- "umi.fukuoka.jp",
- "xn--ses554g",
- "itayanagi.aomori.jp",
- "nt.edu.au",
- "xn--6frz82g",
- "sabae.fukui.jp",
- "soeda.fukuoka.jp",
- "ternopil.ua",
- "hamatonbetsu.hokkaido.jp",
- "prd.mg",
- "xn--3ds443g",
- "productions",
- "tom.ru",
- "tarnobrzeg.pl",
- "trentino.it",
- "ichikawa.chiba.jp",
- "sd.us",
- "onagawa.miyagi.jp",
- "bergbau.museum",
- "kitakyushu.jp",
- "sagamihara.kanagawa.jp",
- "chicago.museum",
- "pisa.it",
- "ham-radio-op.net",
- "sydney.museum",
- "asaminami.hiroshima.jp",
- "panama.museum",
- "kumakogen.ehime.jp",
- "ichinomiya.chiba.jp",
- "tw",
- "yamagata.ibaraki.jp",
- "koebenhavn.museum",
- "vibovalentia.it",
- "virtuel.museum",
- "selje.no",
- "hachinohe.aomori.jp",
- "skjak.no",
- "na.it",
- "paderborn.museum",
- "venice.it",
- "tourism.pl",
- "oguchi.aichi.jp",
- "virtual.museum",
- "xn--krehamn-dxa.no",
- "net.tn",
- "st.no",
- "xn--lrdal-sra.no",
- "happou.akita.jp",
- "xn--ygbi2ammx",
- "sd.cn",
- "torsken.no",
- "kyonan.chiba.jp",
- "tosu.saga.jp",
- "xn--bod-2na.no",
- "pyatigorsk.ru",
- "se.com",
- "net.in",
- "coop.ht",
- "yabu.hyogo.jp",
- "sn.cn",
- "production.aero",
- "daejeon.kr",
- "seki.gifu.jp",
- "xn--rady-ira.no",
- "virginia.museum",
- "namie.fukushima.jp",
- "medizinhistorisches.museum",
- "tsu.mie.jp",
- "saito.miyazaki.jp",
- "transport.museum",
- "name.na",
- "vegarshei.no",
- "ag.it",
- "xn--xhq521b",
- "bg.it",
- "tsukumi.oita.jp",
- "fg.it",
- "kg.kr",
- "pvt.k12.ma.us",
- "net.an",
- "sa.au",
- "nat.tn",
- "rec.co",
- "tottori.tottori.jp",
- "shiki.saitama.jp",
- "kyotango.kyoto.jp",
- "sor-odal.no",
- "is-a-guru.com",
- "verbania.it",
- "net.pn",
- "xn--s-1fa.no",
- "k12.az.us",
- "xn--l-1fa.no",
- "toda.saitama.jp",
- "net.vn",
- "sa.com",
- "kashima.ibaraki.jp",
- "net.cu",
- "sa.cr",
- "xn--krjohka-hwab49j.no",
- "xn--vestvgy-ixa6o.no",
- "sb.ua",
- "tara.saga.jp",
- "podhale.pl",
- "opole.pl",
- "gov.gn",
- "edu.gn",
- "xn--nry-yla5g.no",
- "com.gn",
- "bedzin.pl",
- "torino.it",
- "kamoenai.hokkaido.jp",
- "xn--sr-fron-q1a.no",
- "shibukawa.gunma.jp",
- "kunohe.iwate.jp",
- "ro.it",
- "isa-geek.org",
- "name.ng",
- "vision",
- "net.hn",
- "orsta.no",
- "morimachi.shizuoka.jp",
- "ogasawara.tokyo.jp",
- "sm.ua",
- "higashiura.aichi.jp",
- "sakyo.kyoto.jp",
- "uchihara.ibaraki.jp",
- "rn.it",
- "yamanouchi.nagano.jp",
- "asakawa.fukushima.jp",
- "tome.miyagi.jp",
- "re.it",
- "nic.in",
- "isahaya.nagasaki.jp",
- "tas.au",
- "perm.ru",
- "us-east-1.amazonaws.com",
- "xn--gls-elac.no",
- "oishida.yamagata.jp",
- "sh.cn",
- "kartuzy.pl",
- "palmsprings.museum",
- "ri.it",
- "osaka.jp",
- "teledata.mz",
- "chikujo.fukuoka.jp",
- "tosa.kochi.jp",
- "net.kn",
- "tonami.toyama.jp",
- "plantation.museum",
- "togane.chiba.jp",
- "mishima.fukushima.jp",
- "shari.hokkaido.jp",
- "from-sd.com",
- "pruszkow.pl",
- "tenkawa.nara.jp",
- "from-md.com",
- "sc",
- "aizubange.fukushima.jp",
- "nyc.mn",
- "nx.cn",
- "rost.no",
- "shizukuishi.iwate.jp",
- "dnsdojo.org",
- "rm.it",
- "xn--rskog-uua.no",
- "tadaoka.osaka.jp",
- "act.gov.au",
- "xn--vry-yla5g.no",
- "yugawara.kanagawa.jp",
- "xn--bjddar-pta.no",
- "xn--lhppi-xqa.no",
- "roan.no",
- "tomari.hokkaido.jp",
- "togura.nagano.jp",
- "sch.lk",
- "togo.aichi.jp",
- "ra.it",
- "rotorcraft.aero",
- "television.museum",
- "sch.uk",
- "rnu.tn",
- "norfolk.museum",
- "bolzano.it",
- "net.gy",
- "itoman.okinawa.jp",
- "xn--bdddj-mrabd.no",
- "is-a-soxfan.org",
- "imakane.hokkaido.jp",
- "xn--h-2fa.no",
- "rns.tn",
- "sch.sa",
- "showa.gunma.jp",
- "namdalseid.no",
- "from-id.com",
- "date.hokkaido.jp",
- "tone.ibaraki.jp",
- "sch.ir",
- "modelling.aero",
- "trani-andria-barletta.it",
- "res.in",
- "yuasa.wakayama.jp",
- "sch.qa",
- "tahara.aichi.jp",
- "hokuryu.hokkaido.jp",
- "knowsitall.info",
- "tananger.no",
- "kasukabe.saitama.jp",
- "is-a-hard-worker.com",
- "training",
- "sch.jo",
- "homedns.org",
- "tanabe.kyoto.jp",
- "tuva.ru",
- "ne.jp",
- "coastaldefence.museum",
- "sch.id",
- "photography",
- "rana.no",
- "bologna.it",
- "sk.ca",
- "kainan.tokushima.jp",
- "xn--bjarky-fya.no",
- "yoita.niigata.jp",
- "torino.museum",
- "sekikawa.niigata.jp",
- "tvedestrand.no",
- "nara.nara.jp",
- "xn--9dbhblg6di.museum",
- "test.ru",
- "hikari.yamaguchi.jp",
- "management",
- "xn--vads-jra.no",
- "volkenkunde.museum",
- "dyndns.info",
- "tatarstan.ru",
- "ohtawara.tochigi.jp",
- "tomobe.ibaraki.jp",
- "tokyo",
- "alesund.no",
- "embroidery.museum",
- "xn--gmq050i.hk",
- "blogdns.org",
- "sch.ae",
- "rade.no",
- "chungnam.kr",
- "is-a-blogger.com",
- "store.nf",
- "noda.iwate.jp",
- "odate.akita.jp",
- "showa.yamanashi.jp",
- "yokawa.hyogo.jp",
- "school.na",
- "yakage.okayama.jp",
- "research.aero",
- "matsuzaki.shizuoka.jp",
- "fetsund.no",
- "yalta.ua",
- "yn.cn",
- "tula.ru",
- "sakai.osaka.jp",
- "emergency.aero",
- "walbrzych.pl",
- "samegawa.fukushima.jp",
- "higashitsuno.kochi.jp",
- "toshima.tokyo.jp",
- "brumunddal.no",
- "ogliastra.it",
- "xn--rsta-fra.no",
- "yonaguni.okinawa.jp",
- "ringsaker.no",
- "xn--givuotna-8ya.no",
- "tsuyama.okayama.jp",
- "so.it",
- "tagajo.miyagi.jp",
- "ss.it",
- "sr.it",
- "przeworsk.pl",
- "hekinan.aichi.jp",
- "forsand.no",
- "hornindal.no",
- "hitachiota.ibaraki.jp",
- "traeumtgerade.de",
- "oyodo.nara.jp",
- "shingu.wakayama.jp",
- "org.sy",
- "xn--mxtq1m.hk",
- "nabari.mie.jp",
- "tver.ru",
- "kunitachi.tokyo.jp",
- "aurland.no",
- "org.ly",
- "sor-varanger.no",
- "farsund.no",
- "ravenna.it",
- "org.uy",
- "inazawa.aichi.jp",
- "tochigi.jp",
- "froland.no",
- "trani-barletta-andria.it",
- "dgca.aero",
- "kalisz.pl",
- "nanyo.yamagata.jp",
- "si.it",
- "shiogama.miyagi.jp",
- "midsund.no",
- "dali.museum",
- "tateyama.toyama.jp",
- "yorii.saitama.jp",
- "nedre-eiker.no",
- "hitachinaka.ibaraki.jp",
- "uchinomi.kagawa.jp",
- "kamimine.saga.jp",
- "aizuwakamatsu.fukushima.jp",
- "kuji.iwate.jp",
- "research.museum",
- "georgia.museum",
- "nakagawa.fukuoka.jp",
- "selfip.biz",
- "olawa.pl",
- "nagai.yamagata.jp",
- "naamesjevuemie.no",
- "gs.tr.no",
- "gs.ol.no",
- "gs.mr.no",
- "taxi.br",
- "niiza.saitama.jp",
- "sund.no",
- "gs.nl.no",
- "gs.rl.no",
- "nanjo.okinawa.jp",
- "tsushima.aichi.jp",
- "jo",
- "jobs",
- "org.my",
- "saijo.ehime.jp",
- "sa.it",
- "gs.st.no",
- "gov.st",
- "net.dz",
- "edu.st",
- "gouv.rw",
- "com.st",
- "gouv.ml",
- "vicenza.it",
- "gov.lt",
- "xn--mgba3a4fra",
- "sp.it",
- "gs.nt.no",
- "yamakita.kanagawa.jp",
- "net.uz",
- "gouv.ci",
- "j.bg",
- "bygland.no",
- "scotland.museum",
- "stat.no",
- "ichikawamisato.yamanashi.jp",
- "xn--mgba3a4f16a",
- "sa.edu.au",
- "sv.it",
- "je",
- "slupsk.pl",
- "date.fukushima.jp",
- "repbody.aero",
- "tohnosho.chiba.jp",
- "kamijima.ehime.jp",
- "sola.no",
- "gov.tt",
- "info.tz",
- "edu.tt",
- "org.py",
- "aoki.nagano.jp",
- "com.tt",
- "iveland.no",
- "xn--55qw42g",
- "gouv.sn",
- "pesarourbino.it",
- "hobby-site.org",
- "tateyama.chiba.jp",
- "gov.bt",
- "jor.br",
- "telekommunikation.museum",
- "edu.bt",
- "sula.no",
- "com.bt",
- "shibuya.tokyo.jp",
- "jet.uk",
- "net.bz",
- "tjeldsund.no",
- "school.museum",
- "gov.it",
- "idrett.no",
- "edu.it",
- "gs.tm.no",
- "int.tt",
- "higashichichibu.saitama.jp",
- "pisz.pl",
- "intl.tn",
- "nisshin.aichi.jp",
- "info.tn",
- "ulan-ude.ru",
- "shimosuwa.nagano.jp",
- "tree.museum",
- "azumino.nagano.jp",
- "jus.br",
- "gs.aa.no",
- "sanjo.niigata.jp",
- "xn--mgba3a4fra.ir",
- "jm",
- "photography.museum",
- "edu.mt",
- "com.mt",
- "xn--mgba3a4f16a.ir",
- "gs.fm.no",
- "storfjord.no",
- "chikugo.fukuoka.jp",
- "hitachiomiya.ibaraki.jp",
- "edunet.tn",
- "tamano.okayama.jp",
- "reggiocalabria.it",
- "gouv.bj",
- "songdalen.no",
- "gs.bu.no",
- "gs.hl.no",
- "misugi.mie.jp",
- "philadelphia.museum",
- "net.az",
- "higashi.fukuoka.jp",
- "abu.yamaguchi.jp",
- "yanagawa.fukuoka.jp",
- "time.museum",
- "toei.aichi.jp",
- "jp",
- "nichinan.tottori.jp",
- "reggio-calabria.it",
- "game.tw",
- "gov.pt",
- "edu.pt",
- "com.pt",
- "utazu.kagawa.jp",
- "gouv.km",
- "nyc.museum",
- "sells-for-u.com",
- "club.tw",
- "omihachiman.shiga.jp",
- "org.ky",
- "gjerstad.no",
- "yoka.hyogo.jp",
- "jur.pro",
- "yuza.yamagata.jp",
- "int.pt",
- "reggio-emilia.it",
- "gs.ah.no",
- "yk.ca",
- "mosvik.no",
- "jondal.no",
- "gouv.fr",
- "pg",
- "sch.ng",
- "skedsmokorset.no",
- "flakstad.no",
- "biz.tt",
- "shop.pl",
- "yaese.okinawa.jp",
- "baghdad.museum",
- "gjovik.no",
- "udmurtia.ru",
- "hakodate.hokkaido.jp",
- "tsk.ru",
- "edu.ht",
- "com.ht",
- "taxi.aero",
- "taishi.osaka.jp",
- "grimstad.no",
- "sakai.fukui.jp",
- "realestate.pl",
- "gs.hm.no",
- "gamvik.no",
- "showa.fukushima.jp",
- "mil.st",
- "art.ht",
- "usenet.pl",
- "sasaguri.fukuoka.jp",
- "sor-aurdal.no",
- "ube.yamaguchi.jp",
- "hasvik.no",
- "tomiya.miyagi.jp",
- "toride.ibaraki.jp",
- "presse.fr",
- "ne.ug",
- "village.museum",
- "muosat.no",
- "higashi.fukushima.jp",
- "settlement.museum",
- "industries",
- "otaki.saitama.jp",
- "tondabayashi.osaka.jp",
- "nantan.kyoto.jp",
- "nagato.yamaguchi.jp",
- "net.kz",
- "sx.cn",
- "sapporo.jp",
- "biz.at",
- "shika.ishikawa.jp",
- "syzran.ru",
- "tabayama.yamanashi.jp",
- "oskol.ru",
- "educator.aero",
- "tank.museum",
- "tas.edu.au",
- "odesa.ua",
- "smolensk.ru",
- "kolobrzeg.pl",
- "national.museum",
- "hashima.gifu.jp",
- "caserta.it",
- "supplies",
- "journalist.aero",
- "tsumagoi.gunma.jp",
- "salerno.it",
- "univ.sn",
- "jessheim.no",
- "sciences.museum",
- "sci.eg",
- "buryatia.ru",
- "xn--davvenjrga-y4a.no",
- "tamamura.gunma.jp",
- "scrapper-site.net",
- "tsubame.niigata.jp",
- "shingu.hyogo.jp",
- "mashiko.tochigi.jp",
- "med.ht",
- "education",
- "pvt.ge",
- "ibigawa.gifu.jp",
- "tg",
- "hichiso.gifu.jp",
- "tamayu.shimane.jp",
- "malvik.no",
- "nakagusuku.okinawa.jp",
- "balestrand.no",
- "utsunomiya.tochigi.jp",
- "jolster.no",
- "taku.saga.jp",
- "trondheim.no",
- "wakkanai.hokkaido.jp",
- "xn--gjvik-wua.no",
- "station.museum",
- "chukotka.ru",
- "jevnaker.no",
- "or.tz",
- "simple-url.com",
- "yokosuka.kanagawa.jp",
- "or.us",
- "ninohe.iwate.jp",
- "monza-e-della-brianza.it",
- "oz.au",
- "journalism.museum",
- "forgot.her.name",
- "ol.no",
- "magnitka.ru",
- "philadelphiaarea.museum",
- "tarumizu.kagoshima.jp",
- "or.mu",
- "xn--gmqw5a.hk",
- "sanfrancisco.museum",
- "sibenik.museum",
- "perugia.it",
- "obama.fukui.jp",
- "entertainment.aero",
- "or.at",
- "brand.se",
- "or.cr",
- "kuroiso.tochigi.jp",
- "odawara.kanagawa.jp",
- "software.aero",
- "support",
- "shiwa.iwate.jp",
- "yamagata.gifu.jp",
- "omuta.fukuoka.jp",
- "carbonia-iglesias.it",
- "chikuho.fukuoka.jp",
- "otaru.hokkaido.jp",
- "fukuchiyama.kyoto.jp",
- "kamitsue.oita.jp",
- "is-a-geek.org",
- "shingu.fukuoka.jp",
- "forgot.his.name",
- "or.kr",
- "omiya.saitama.jp",
- "ono.hyogo.jp",
- "or.ci",
- "flight.aero",
- "tozawa.yamagata.jp",
- "siracusa.it",
- "takatori.nara.jp",
- "otake.hiroshima.jp",
- "museet.museum",
- "tsushima.nagasaki.jp",
- "od.ua",
- "cargo.aero",
- "info.tt",
- "kasugai.aichi.jp",
- "ozora.hokkaido.jp",
- "is-a-photographer.com",
- "tsukui.kanagawa.jp",
- "group.aero",
- "aero.tt",
- "xn--rhqv96g",
- "or.na",
- "flights",
- "waw.pl",
- "jeju.kr",
- "sagae.yamagata.jp",
- "owani.aomori.jp",
- "muenster.museum",
- "mobi.tz",
- "cmw.ru",
- "ryugasaki.ibaraki.jp",
- "nanporo.hokkaido.jp",
- "daegu.kr",
- "sukagawa.fukushima.jp",
- "illustration.museum",
- "on.ca",
- "jeonnam.kr",
- "ojiya.niigata.jp",
- "jar.ru",
- "fujishiro.ibaraki.jp",
- "tarama.okinawa.jp",
- "agriculture.museum",
- "taketa.oita.jp",
- "otobe.hokkaido.jp",
- "oh.us",
- "or.bi",
- "or.th",
- "house.museum",
- "dovre.no",
- "glass.museum",
- "noboribetsu.hokkaido.jp",
- "writesthisblog.com",
- "readmyblog.org",
- "gorge.museum",
- "hino.tottori.jp",
- "kamo.niigata.jp",
- "ichikawa.hyogo.jp",
- "narashino.chiba.jp",
- "toho.fukuoka.jp",
- "oketo.hokkaido.jp",
- "johana.toyama.jp",
- "takamori.kumamoto.jp",
- "is-a-bulls-fan.com",
- "scienceandindustry.museum",
- "hawaii.museum",
- "field.museum",
- "kitashiobara.fukushima.jp",
- "komagane.nagano.jp",
- "force.museum",
- "ok.us",
- "sande.xn--mre-og-romsdal-qqb.no",
- "nichinan.miyazaki.jp",
- "otama.fukushima.jp",
- "swidnica.pl",
- "cyber.museum",
- "kainan.wakayama.jp",
- "union.aero",
- "tonaki.okinawa.jp",
- "bible.museum",
- "oyabe.toyama.jp",
- "cymru.museum",
- "amber.museum",
- "sherbrooke.museum",
- "suzuka.mie.jp",
- "k12.ut.us",
- "steinkjer.no",
- "donna.no",
- "sellsyourhome.org",
- "kamifurano.hokkaido.jp",
- "takaoka.toyama.jp",
- "shacknet.nu",
- "asuke.aichi.jp",
- "takasaki.gunma.jp",
- "obira.hokkaido.jp",
- "dyroy.no",
- "hino.tokyo.jp",
- "railway.museum",
- "sciencesnaturelles.museum",
- "sakegawa.yamagata.jp",
- "money.museum",
- "tsurugashima.saitama.jp",
- "works.aero",
- "aisai.aichi.jp",
- "org.co",
- "educational.museum",
- "tako.chiba.jp",
- "basel.museum",
- "rockart.museum",
- "uozu.toyama.jp",
- "tomakomai.hokkaido.jp",
- "sciencehistory.museum",
- "tsugaru.aomori.jp",
- "yamagata.yamagata.jp",
- "rochester.museum",
- "tanohata.iwate.jp",
- "space-to-rent.com",
- "ls",
- "lr",
- "gob.sv",
- "edu.sv",
- "www.ro",
- "com.sv",
- "nishiaizu.fukushima.jp",
- "shitara.aichi.jp",
- "tomisato.chiba.jp",
- "gov.lv",
- "soni.nara.jp",
- "edu.lv",
- "com.lv",
- "lu",
- "k12.mt.us",
- "l.bg",
- "l.se",
- "lom.no",
- "asn.lv",
- "joshkar-ola.ru",
- "university.museum",
- "lt",
- "marketplace.aero",
- "takamori.nagano.jp",
- "org.cw",
- "education.museum",
- "itako.ibaraki.jp",
- "lb",
- "tanabe.wakayama.jp",
- "bando.ibaraki.jp",
- "xn--mgb9awbf",
- "org.ci",
- "li",
- "other.nf",
- "shimane.jp",
- "limo",
- "k12.vt.us",
- "teshikaga.hokkaido.jp",
- "net.gn",
- "xn--aurskog-hland-jnb.no",
- "seihi.nagasaki.jp",
- "info.vn",
- "ly",
- "santafe.museum",
- "luster.no",
- "tajiri.osaka.jp",
- "os.hedmark.no",
- "technology",
- "joso.ibaraki.jp",
- "lunner.no",
- "kunstunddesign.museum",
- "okuma.fukushima.jp",
- "handa.aichi.jp",
- "chuo.tokyo.jp",
- "ltd.lk",
- "gov.mv",
- "edu.mv",
- "scienceandhistory.museum",
- "com.mv",
- "baths.museum",
- "sweden.museum",
- "expert",
- "takayama.gunma.jp",
- "lib.ee",
- "xn--dyry-ira.no",
- "naturhistorisches.museum",
- "lebork.pl",
- "farmstead.museum",
- "shimoichi.nara.jp",
- "tobetsu.hokkaido.jp",
- "bungotakada.oita.jp",
- "la",
- "sue.fukuoka.jp",
- "int.mv",
- "leg.br",
- "or.id",
- "jefferson.museum",
- "clock.museum",
- "uhren.museum",
- "sennan.osaka.jp",
- "christmas",
- "land",
- "lierne.no",
- "takayama.nagano.jp",
- "lv",
- "tas.gov.au",
- "link",
- "asahi.ibaraki.jp",
- "anan.tokushima.jp",
- "jeonbuk.kr",
- "nishinoshima.shimane.jp",
- "nankoku.kochi.jp",
- "takahama.aichi.jp",
- "mordovia.ru",
- "lel.br",
- "or.it",
- "takazaki.miyazaki.jp",
- "gdynia.pl",
- "tokushima.jp",
- "lerdal.no",
- "ski.museum",
- "limanowa.pl",
- "ot.it",
- "krokstadelva.no",
- "shirosato.ibaraki.jp",
- "xn--berlevg-jxa.no",
- "mima.tokushima.jp",
- "lapy.pl",
- "yusui.kagoshima.jp",
- "lindas.no",
- "mil.lv",
- "augustow.pl",
- "kiev.ua",
- "matsushige.tokushima.jp",
- "tamatsukuri.ibaraki.jp",
- "lib.or.us",
- "dontexist.org",
- "lugansk.ua",
- "lib.dc.us",
- "info.ve",
- "kikuchi.kumamoto.jp",
- "lib.ok.us",
- "biz.mv",
- "lib.sc.us",
- "kiwi",
- "institute",
- "lib.de.us",
- "tsukigata.hokkaido.jp",
- "rokunohe.aomori.jp",
- "lk",
- "yoshino.nara.jp",
- "lib.nj.us",
- "lib.nm.us",
- "chuvashia.ru",
- "lib.nc.us",
- "shinkamigoto.nagasaki.jp",
- "rg.it",
- "osen.no",
- "takasu.hokkaido.jp",
- "izumozaki.niigata.jp",
- "lib.ne.us",
- "keisen.fukuoka.jp",
- "lib.sd.us",
- "shimodate.ibaraki.jp",
- "org.sn",
- "odda.no",
- "lib.la.us",
- "beppu.oita.jp",
- "fukushima.fukushima.jp",
- "bryansk.ru",
- "lebesby.no",
- "lardal.no",
- "mobi.tt",
- "technology.museum",
- "koge.tottori.jp",
- "berlevag.no",
- "takehara.hiroshima.jp",
- "off.ai",
- "wales.museum",
- "lib.nd.us",
- "koeln.museum",
- "stjohn.museum",
- "boldlygoingnowhere.org",
- "chita.aichi.jp",
- "mil.mv",
- "ginan.gifu.jp",
- "lib.il.us",
- "xn--langevg-jxa.no",
- "org.tn",
- "prato.it",
- "konan.aichi.jp",
- "lib.ri.us",
- "kasamatsu.gifu.jp",
- "siljan.no",
- "oslo.no",
- "kunst.museum",
- "marugame.kagawa.jp",
- "lib.ms.us",
- "lib.mo.us",
- "lib.ia.us",
- "lib.id.us",
- "mantova.it",
- "org.in",
- "karasuyama.tochigi.jp",
- "askvoll.no",
- "xn--ggaviika-8ya47h.no",
- "oyer.no",
- "takaishi.osaka.jp",
- "lipetsk.ru",
- "koto.tokyo.jp",
- "okawa.fukuoka.jp",
- "lib.me.us",
- "tsurugi.ishikawa.jp",
- "voagat.no",
- "lib.as.us",
- "lib.ar.us",
- "yosemite.museum",
- "lib.ak.us",
- "lyngdal.no",
- "lib.ma.us",
- "media.aero",
- "gaivuotna.no",
- "lib.md.us",
- "lib.pr.us",
- "lib.al.us",
- "onojo.fukuoka.jp",
- "org.mn",
- "himi.toyama.jp",
- "kyiv.ua",
- "kawasaki.jp",
- "ryazan.ru",
- "minamitane.kagoshima.jp",
- "org.an",
- "ushistory.museum",
- "lillesand.no",
- "tachikawa.tokyo.jp",
- "nakai.kanagawa.jp",
- "klepp.no",
- "ivanovo.ru",
- "kita.tokyo.jp",
- "lib.pa.us",
- "austevoll.no",
- "lib.mi.us",
- "ogawara.miyagi.jp",
- "org.pn",
- "lib.va.us",
- "takaharu.miyazaki.jp",
- "nagakute.aichi.jp",
- "org.vn",
- "hiraizumi.iwate.jp",
- "lanbib.se",
- "accident-investigation.aero",
- "takahagi.ibaraki.jp",
- "org.cu",
- "xn--hylandet-54a.no",
- "gyokuto.kumamoto.jp",
- "perso.sn",
- "saves-the-whales.com",
- "legnica.pl",
- "tokorozawa.saitama.jp",
- "alto-adige.it",
- "yaizu.shizuoka.jp",
- "lib.vi.us",
- "nakagyo.kyoto.jp",
- "press.ma",
- "swinoujscie.pl",
- "co.lc",
- "sakae.nagano.jp",
- "luxe",
- "ac.sz",
- "ac.tz",
- "org.hn",
- "sykkylven.no",
- "s3-sa-east-1.amazonaws.com",
- "takahashi.okayama.jp",
- "lib.ks.us",
- "honbetsu.hokkaido.jp",
- "daito.osaka.jp",
- "ac.rs",
- "kopervik.no",
- "ac.mu",
- "nyuzen.toyama.jp",
- "nrw",
- "ac.ru",
- "ac.se",
- "yachimata.chiba.jp",
- "ac.ae",
- "qc.com",
- "shimane.shimane.jp",
- "lezajsk.pl",
- "lib.hi.us",
- "ac.at",
- "ac.me",
- "ac.gn",
- "porsgrunn.no",
- "ac.cr",
- "labour.museum",
- "kawahara.tottori.jp",
- "tsuruoka.yamagata.jp",
- "org.kn",
- "yamada.iwate.jp",
- "net.st",
- "ac.cn",
- "parma.it",
- "kawaba.gunma.jp",
- "iwama.ibaraki.jp",
- "zhytomyr.ua",
- "mombetsu.hokkaido.jp",
- "namikata.ehime.jp",
- "landes.museum",
- "sumita.iwate.jp",
- "nishigo.fukushima.jp",
- "artanddesign.museum",
- "media.museum",
- "ac.kr",
- "shichinohe.aomori.jp",
- "perso.ht",
- "ac.tj",
- "terni.it",
- "timekeeping.museum",
- "tagami.niigata.jp",
- "jetzt",
- "net.tt",
- "padua.it",
- "ac.ci",
- "land-4-sale.us",
- "tochio.niigata.jp",
- "tatebayashi.gunma.jp",
- "gouv.ht",
- "net.bt",
- "xn--hery-ira.xn--mre-og-romsdal-qqb.no",
- "obuse.nagano.jp",
- "drangedal.no",
- "nesset.no",
- "nakatane.kagoshima.jp",
- "yahaba.iwate.jp",
- "ikeda.gifu.jp",
- "cc.na",
- "ac.ma",
- "lighting",
- "ouda.nara.jp",
- "ogata.akita.jp",
- "kudamatsu.yamaguchi.jp",
- "moriguchi.osaka.jp",
- "pittsburgh.museum",
- "anamizu.ishikawa.jp",
- "oe.yamagata.jp",
- "jinsekikogen.hiroshima.jp",
- "juif.museum",
- "xn--gildeskl-g0a.no",
- "or.jp",
- "ontario.museum",
- "gc.ca",
- "qc.ca",
- "net.mt",
- "kisofukushima.nagano.jp",
- "takizawa.iwate.jp",
- "oregon.museum",
- "bc.ca",
- "ac.be",
- "la-spezia.it",
- "otari.nagano.jp",
- "barlettatraniandria.it",
- "doshi.yamanashi.jp",
- "obu.aichi.jp",
- "uchinada.ishikawa.jp",
- "net.pt",
- "leirvik.no",
- "ac.th",
- "essex.museum",
- "miyazaki.jp",
- "oshu.iwate.jp",
- "mugi.tokushima.jp",
- "furniture.museum",
- "gangaviika.no",
- "k12.nv.us",
- "kochi.kochi.jp",
- "barletta-trani-andria.it",
- "gob.gt",
- "edu.gt",
- "takikawa.hokkaido.jp",
- "com.gt",
- "ebiz.tw",
- "narvik.no",
- "kawanishi.nara.jp",
- "tomioka.gunma.jp",
- "otaki.nagano.jp",
- "tainai.niigata.jp",
- "opoczno.pl",
- "hoyanger.no",
- "navuotna.no",
- "is-saved.org",
- "kuchinotsu.nagasaki.jp",
- "oseto.nagasaki.jp",
- "net.ht",
- "name.tj",
- "tydal.no",
- "targi.pl",
- "shop.ht",
- "if.ua",
- "chikuzen.fukuoka.jp",
- "tjome.no",
- "olsztyn.pl",
- "lib.wa.us",
- "levanger.no",
- "saigawa.fukuoka.jp",
- "ind.gt",
- "perso.tn",
- "co.pl",
- "trana.no",
- "saku.nagano.jp",
- "nakamichi.yamanashi.jp",
- "nakatombetsu.hokkaido.jp",
- "shinyoshitomi.fukuoka.jp",
- "kaminokawa.tochigi.jp",
- "co.pn",
- "ogano.saitama.jp",
- "lib.wi.us",
- "ogose.saitama.jp",
- "de.us",
- "freight.aero",
- "turek.pl",
- "xn--80asehdb",
- "xn--45brj9c",
- "xn--s9brj9c",
- "latina.it",
- "taishi.hyogo.jp",
- "osaki.miyagi.jp",
- "vinnytsia.ua",
- "tolga.no",
- "kamiizumi.saitama.jp",
- "usculture.museum",
- "report",
- "de.com",
- "xn--tysvr-vra.no",
- "lavangen.no",
- "tomsk.ru",
- "kawakami.nara.jp",
- "iwamizawa.hokkaido.jp",
- "narviika.no",
- "tamakawa.fukushima.jp",
- "rnrt.tn",
- "xn--q9jyb4c",
- "gs.svalbard.no",
- "oguni.kumamoto.jp",
- "miyada.nagano.jp",
- "taketomi.okinawa.jp",
- "brandywinevalley.museum",
- "oceanographique.museum",
- "openair.museum",
- "xn--loabt-0qa.no",
- "xn--rdy-0nab.no",
- "shinshiro.aichi.jp",
- "iiyama.nagano.jp",
- "takatsuki.osaka.jp",
- "dr.na",
- "ostre-toten.no",
- "org.dz",
- "dn.ua",
- "mil.gt",
- "miyota.nagano.jp",
- "org.sz",
- "krym.ua",
- "org.uz",
- "xn--nqv7f",
- "obama.nagasaki.jp",
- "kanie.aichi.jp",
- "is-very-bad.org",
- "omura.nagasaki.jp",
- "ac.id",
- "est-le-patron.com",
- "tomika.gifu.jp",
- "miyake.nara.jp",
- "turen.tn",
- "raholt.no",
- "ac.ir",
- "org.bz",
- "xn--p1acf",
- "ac.mw",
- "xn--fl-zia.no",
- "ac.rw",
- "okaya.nagano.jp",
- "ouchi.saga.jp",
- "tychy.pl",
- "nishikata.tochigi.jp",
- "ac.in",
- "turin.it",
- "kibichuo.okayama.jp",
- "xn--fiqz9s",
- "ono.fukui.jp",
- "fc.it",
- "shimofusa.chiba.jp",
- "xn--h2brj9c",
- "pavia.it",
- "xn--fjord-lra.no",
- "sarpsborg.no",
- "marketing",
- "umaji.kochi.jp",
- "plc.ly",
- "xn--fiqs8s",
- "dp.ua",
- "likescandy.com",
- "kakamigahara.gifu.jp",
- "org.az",
- "ogori.fukuoka.jp",
- "niigata.jp",
- "kiyose.tokyo.jp",
- "rawa-maz.pl",
- "tokashiki.okinawa.jp",
- "mc.it",
- "ac.im",
- "gov.cn",
- "edu.cn",
- "com.cn",
- "komvux.se",
- "cci.fr",
- "avocat.fr",
- "barrell-of-knowledge.info",
- "kesennuma.miyagi.jp",
- "kawakami.nagano.jp",
- "lib.oh.us",
- "nsw.au",
- "kawajima.saitama.jp",
- "lodi.it",
- "ohda.shimane.jp",
- "est-a-la-maison.com",
- "og.ao",
- "operaunite.com",
- "heimatunduhren.museum",
- "est-a-la-masion.com",
- "tranoy.no",
- "shop.hu",
- "lib.nh.us",
- "or.ug",
- "tenri.nara.jp",
- "losangeles.museum",
- "tagawa.fukuoka.jp",
- "rel.ht",
- "neues.museum",
- "xn--frya-hra.no",
- "democrat",
- "kawanehon.shizuoka.jp",
- "name.tt",
- "rhcloud.com",
- "nozawaonsen.nagano.jp",
- "ftpaccess.cc",
- "xn--ciqpn.hk",
- "savannahga.museum",
- "nishinomiya.hyogo.jp",
- "xn--nnx388a",
- "ohira.miyagi.jp",
- "kawatana.nagasaki.jp",
- "luxembourg.museum",
- "togakushi.nagano.jp",
- "ochi.kochi.jp",
- "est-mon-blogueur.com",
- "tranby.no",
- "k12.wv.us",
- "for-better.biz",
- "org.kz",
- "koya.wakayama.jp",
- "dyn-o-saur.com",
- "oto.fukuoka.jp",
- "miyazaki.miyazaki.jp",
- "surgut.ru",
- "naval.museum",
- "is-an-anarchist.com",
- "yokkaichi.mie.jp",
- "ranzan.saitama.jp",
- "xn--tjme-hra.no",
- "xn--srfold-bya.no",
- "xn--brnnysund-m8ac.no",
- "xn--aroport-bya.ci",
- "is-a-liberal.com",
- "is-a-landscaper.com",
- "medecin.fr",
- "tokushima.tokushima.jp",
- "shimogo.fukushima.jp",
- "on-the-web.tv",
- "omitama.ibaraki.jp",
- "assedic.fr",
- "tateshina.nagano.jp",
- "lodingen.no",
- "tsuiki.fukuoka.jp",
- "asso.nc",
- "miyazu.kyoto.jp",
- "xn--snase-nra.no",
- "xn--nqv7fs00ema",
- "go.pw",
- "ed.pw",
- "co.pw",
- "xn--flor-jra.no",
- "dyndns.biz",
- "mil.cn",
- "north.museum",
- "xn--jrpeland-54a.no",
- "buyshouses.net",
- "ono.fukushima.jp",
- "kawaminami.miyazaki.jp",
- "tsuru.yamanashi.jp",
- "xn--jlster-bya.no",
- "mochizuki.nagano.jp",
- "servebbs.com",
- "xn--srreisa-q1a.no",
- "jelenia-gora.pl",
- "net.lv",
- "conference.aero",
- "taiki.mie.jp",
- "is-an-accountant.com",
- "is-a-player.com",
- "oregontrail.museum",
- "lans.museum",
- "from-sc.com",
- "ohi.fukui.jp",
- "sado.niigata.jp",
- "servebbs.net",
- "higashishirakawa.gifu.jp",
- "valer.hedmark.no",
- "is-a-libertarian.com",
- "store.bb",
- "xn--snsa-roa.no",
- "council.aero",
- "vanylven.no",
- "xn--rmskog-bya.no",
- "is-uberleet.com",
- "from-dc.com",
- "xn--vegrshei-c0a.no",
- "takahama.fukui.jp",
- "paragliding.aero",
- "kayabe.hokkaido.jp",
- "leksvik.no",
- "takagi.nagano.jp",
- "betainabox.com",
- "ogawa.saitama.jp",
- "okoppe.hokkaido.jp",
- "for-our.info",
- "googlecode.com",
- "takasago.hyogo.jp",
- "manx.museum",
- "net.mv",
- "dnsdojo.net",
- "shiriuchi.hokkaido.jp",
- "nnov.ru",
- "kawazu.shizuoka.jp",
- "dyndns-remote.com",
- "xn--snes-poa.no",
- "takayama.gifu.jp",
- "dnsdojo.com",
- "xn--lgrd-poac.no",
- "pr.us",
- "agency",
- "xn--dnna-gra.no",
- "hayakawa.yamanashi.jp",
- "xn--finny-yua.no",
- "pskov.ru",
- "ujiie.tochigi.jp",
- "academy.museum",
- "name.vn",
- "tsuno.miyazaki.jp",
- "judygarland.museum",
- "living.museum",
- "dyndns-pics.com",
- "kiyama.saga.jp",
- "from-ny.net",
- "suwalki.pl",
- "s3-fips-us-gov-west-1.amazonaws.com",
- "pb.ao",
- "tajimi.gifu.jp",
- "xn--ksnes-uua.no",
- "ac.jp",
- "dyndns-home.com",
- "takarazuka.hyogo.jp",
- "birthplace.museum",
- "takata.fukuoka.jp",
- "os.hordaland.no",
- "is-very-evil.org",
- "stavern.no",
- "fujikawaguchiko.yamanashi.jp",
- "mibu.tochigi.jp",
- "xn--slt-elab.no",
- "tokyo.jp",
- "endofinternet.net",
- "bato.tochigi.jp",
- "skanit.no",
- "culturalcenter.museum",
- "intelligence.museum",
- "isteingeek.de",
- "company",
- "sarufutsu.hokkaido.jp",
- "government.aero",
- "localhistory.museum",
- "xn--lgbbat1ad8j",
- "haga.tochigi.jp",
- "pulawy.pl",
- "pe.kr",
- "xn--blt-elab.no",
- "lib.tx.us",
- "ascolipiceno.it",
- "pa.us",
- "from-nv.com",
- "pl.ua",
- "blogdns.net",
- "pp.az",
- "oki.fukuoka.jp",
- "museumcenter.museum",
- "vibo-valentia.it",
- "stavropol.ru",
- "from-nj.com",
- "bahcavuotna.no",
- "services.aero",
- "vf.no",
- "blogdns.com",
- "matsudo.chiba.jp",
- "daiwa.hiroshima.jp",
- "graphics",
- "karuizawa.nagano.jp",
- "tokke.no",
- "domains",
- "tamba.hyogo.jp",
- "pp.ru",
- "pp.se",
- "sevastopol.ua",
- "sebastopol.ua",
- "lacaixa",
- "is-an-entertainer.com",
- "is-gone.com",
- "ogimi.okinawa.jp",
- "vladivostok.ru",
- "yuu.yamaguchi.jp",
- "oshima.yamaguchi.jp",
- "dyndns-mail.com",
- "from-nm.com",
- "tr.no",
- "pe.ca",
- "sannan.hyogo.jp",
- "tn.us",
- "malatvuopmi.no",
- "servebbs.org",
- "katsuyama.fukui.jp",
- "uryu.hokkaido.jp",
- "ltd.gi",
- "og.it",
- "red.sv",
- "toki.gifu.jp",
- "olbia-tempio.it",
- "kiyosu.aichi.jp",
- "xn--brnny-wuac.no",
- "from-ne.com",
- "pp.ua",
- "xn--mosjen-eya.no",
- "nasushiobara.tochigi.jp",
- "xn--mgbbh1a71e",
- "is-a-designer.com",
- "xn--mgberp4a5d4ar",
- "owariasahi.aichi.jp",
- "tm.no",
- "tm.ro",
- "bremanger.no",
- "tohma.hokkaido.jp",
- "tm.se",
- "dyndns-ip.com",
- "flatanger.no",
- "gratangen.no",
- "xn--sgne-gra.no",
- "from-nh.com",
- "tm.fr",
- "campidano-medio.it",
- "tv.sd",
- "tv.tz",
- "nc.us",
- "is-a-bookkeeper.com",
- "xn--lns-qla.museum",
- "shirakawa.fukushima.jp",
- "yamatsuri.fukushima.jp",
- "tj.cn",
- "lib.ga.us",
- "te.ua",
- "xn--o3cw4h",
- "tonosho.kagawa.jp",
- "org.gn",
- "xn--mgbqly7cvafr",
- "xn--ygarden-p1a.no",
- "tenei.fukushima.jp",
- "xn--skierv-uta.no",
- "nsw.edu.au",
- "takino.hyogo.jp",
- "okagaki.fukuoka.jp",
- "aeroclub.aero",
- "is-a-student.com",
- "oyamazaki.kyoto.jp",
- "miyoshi.saitama.jp",
- "state.museum",
- "miyakonojo.miyazaki.jp",
- "stadt.museum",
- "olkusz.pl",
- "yamaguchi.jp",
- "livinghistory.museum",
- "miyashiro.saitama.jp",
- "joetsu.niigata.jp",
- "mitou.yamaguchi.jp",
- "is-a-financialadvisor.com",
- "aerobatic.aero",
- "database.museum",
- "steam.museum",
- "niigata.niigata.jp",
- "botanicgarden.museum",
- "sch.ly",
- "xn--laheadju-7ya.no",
- "denmark.museum",
- "tm.hu",
- "xn--skjervy-v1a.no",
- "jamal.ru",
- "penza.ru",
- "is-certified.com",
- "pf",
- "tm.km",
- "gliding.aero",
- "shimonita.gunma.jp",
- "dyndns-at-home.com",
- "tv.bo",
- "tv.na",
- "usdecorativearts.museum",
- "tc",
- "tv.br",
- "xn--b-5ga.telemark.no",
- "airline.aero",
- "yekaterinburg.ru",
- "xn--unup4y",
- "taito.tokyo.jp",
- "agematsu.nagano.jp",
- "heroy.more-og-romsdal.no",
- "lorenskog.no",
- "andria-barletta-trani.it",
- "togitsu.nagasaki.jp",
- "caltanissetta.it",
- "prd.fr",
- "huissier-justice.fr",
- "shell.museum",
- "ballangen.no",
- "yamanobe.yamagata.jp",
- "kawaue.gifu.jp",
- "westfalen.museum",
- "serveftp.net",
- "xn--mlselv-iua.no",
- "space.museum",
- "xn--frna-woa.no",
- "vc.it",
- "yukuhashi.fukuoka.jp",
- "tokigawa.saitama.jp",
- "tadotsu.kagawa.jp",
- "po.it",
- "pd.it",
- "pz.it",
- "shimizu.shizuoka.jp",
- "dominic.ua",
- "pr.it",
- "salvadordali.museum",
- "ac.ug",
- "sowa.ibaraki.jp",
- "wallonie.museum",
- "pu.it",
- "kobierzyce.pl",
- "kiyosato.hokkaido.jp",
- "pn.it",
- "ddr.museum",
- "pe.it",
- "gs.jan-mayen.no",
- "pt.it",
- "does-it.net",
- "taiki.hokkaido.jp",
- "kawasaki.miyagi.jp",
- "skole.museum",
- "salem.museum",
- "pi.it",
- "suifu.ibaraki.jp",
- "net.gt",
- "taira.toyama.jp",
- "medical.museum",
- "shiranuka.hokkaido.jp",
- "hayashima.okayama.jp",
- "sanagochi.tokushima.jp",
- "sodegaura.chiba.jp",
- "is-a-conservative.com",
- "tf",
- "christiansburg.museum",
- "leangaviika.no",
- "nf.ca",
- "lavagis.no",
- "watch-and-clock.museum",
- "royrvik.no",
- "xn--vre-eiker-k8a.no",
- "pa.it",
- "plc.co.im",
- "kawagoe.mie.jp",
- "issmarterthanyou.com",
- "omotego.fukushima.jp",
- "pv.it",
- "takatsuki.shiga.jp",
- "podlasie.pl",
- "pesaro-urbino.it",
- "miyagi.jp",
- "nayoro.hokkaido.jp",
- "is-a-lawyer.com",
- "airport.aero",
- "air-surveillance.aero",
- "to.it",
- "ts.it",
- "tgory.pl",
- "tr.it",
- "delaware.museum",
- "xn--xkc2dl3a5ee0h",
- "taka.hyogo.jp",
- "xn--sknland-fxa.no",
- "tw.cn",
- "carboniaiglesias.it",
- "supply",
- "tn.it",
- "takashima.shiga.jp",
- "te.it",
- "shinanomachi.nagano.jp",
- "lib.gu.us",
- "boleslawiec.pl",
- "kawara.fukuoka.jp",
- "pro.vn",
- "takahata.yamagata.jp",
- "kawakita.ishikawa.jp",
- "kawanishi.hyogo.jp",
- "dyndns-web.com",
- "oguni.yamagata.jp",
- "kashiwa.chiba.jp",
- "shimizu.hokkaido.jp",
- "shakotan.hokkaido.jp",
- "achi.nagano.jp",
- "xn--hbmer-xqa.no",
- "tt.im",
- "takko.aomori.jp",
- "org.st",
- "serveftp.org",
- "is-a-geek.net",
- "pordenone.it",
- "xn--xkc2al3hye2a",
- "miyoshi.hiroshima.jp",
- "shijonawate.osaka.jp",
- "otsuchi.iwate.jp",
- "yoshinogari.saga.jp",
- "ta.it",
- "is-a-geek.com",
- "news.hu",
- "org.tt",
- "accident-prevention.aero",
- "tp.it",
- "yazu.tottori.jp",
- "time.no",
- "artcenter.museum",
- "tv.it",
- "is-a-nurse.com",
- "org.bt",
- "detroit.museum",
- "okazaki.aichi.jp",
- "masaki.ehime.jp",
- "moka.tochigi.jp",
- "tinn.no",
- "xn--sr-varanger-ggb.no",
- "poznan.pl",
- "katowice.pl",
- "kawagoe.saitama.jp",
- "youth.museum",
- "dyndns-at-work.com",
- "z-2.compute-1.amazonaws.com",
- "ichinomiya.aichi.jp",
- "whaling.museum",
- "org.mt",
- "xn--holtlen-hxa.no",
- "palace.museum",
- "newmexico.museum",
- "tv.im",
- "tana.no",
- "indiana.museum",
- "xn--node",
- "dyndns-wiki.com",
- "kvanangen.no",
- "delmenhorst.museum",
- "tyumen.ru",
- "sc.tz",
- "org.pt",
- "sc.us",
- "net.cn",
- "piacenza.it",
- "kyotanabe.kyoto.jp",
- "miyoshi.tokushima.jp",
- "porsangu.no",
- "yawata.kyoto.jp",
- "dynathome.net",
- "shirakawa.gifu.jp",
- "echizen.fukui.jp",
- "org.ht",
- "sc.cn",
- "voyage",
- "oamishirasato.chiba.jp",
- "xn--rhkkervju-01af.no",
- "daisen.akita.jp",
- "sc.kr",
- "is-very-good.org",
- "tx.us",
- "toba.mie.jp",
- "dyndns-work.com",
- "xn--bidr-5nac.no",
- "urayasu.chiba.jp",
- "pharmacy.museum",
- "xn--bhcavuotna-s4a.no",
- "sf.no",
- "linz.museum",
- "tanagura.fukushima.jp",
- "sor-fron.no",
- "loten.no",
- "minamiechizen.fukui.jp",
- "skierva.no",
- "trolley.museum",
- "yawara.ibaraki.jp",
- "thruhere.net",
- "lecco.it",
- "nowaruda.pl",
- "uscountryestate.museum",
- "ne.pw",
- "rc.it",
- "bryne.no",
- "miyama.fukuoka.jp",
- "lecce.it",
- "luroy.no",
- "ogawa.nagano.jp",
- "taiji.wakayama.jp",
- "takamatsu.kagawa.jp",
- "shikatsu.aichi.jp",
- "xn--ldingen-q1a.no",
- "tado.mie.jp",
- "phoenix.museum",
- "decorativearts.museum",
- "hitachi.ibaraki.jp",
- "kawanishi.yamagata.jp",
- "tono.iwate.jp",
- "tendo.yamagata.jp",
- "xn--c1avg",
- "skjervoy.no",
- "lucca.it",
- "ichinohe.iwate.jp",
- "exchange.aero",
- "miyako.fukuoka.jp",
- "parliament.uk",
- "naturalsciences.museum",
- "shibata.niigata.jp",
- "shibetsu.hokkaido.jp",
- "properties",
- "tomigusuku.okinawa.jp",
- "trustee.museum",
- "pol.dz",
- "co.ve",
- "dontexist.net",
- "theater.museum",
- "kawai.nara.jp",
- "dontexist.com",
- "nasu.tochigi.jp",
- "kawamata.fukushima.jp",
- "co.vi",
- "hu.net",
- "js.cn",
- "xn--h1aegh.museum",
- "jl.cn",
- "zgorzelec.pl",
- "pharmaciens.km",
- "pro.az",
- "xn--sandnessjen-ogb.no",
- "niepce.museum",
- "gb.net",
- "tranibarlettaandria.it",
- "xn--tn0ag.hk",
- "potenza.it",
- "otago.museum",
- "tempioolbia.it",
- "sakai.ibaraki.jp",
- "traniandriabarletta.it",
- "xn--andy-ira.no",
- "sekigahara.gifu.jp",
- "nikko.tochigi.jp",
- "iglesiascarbonia.it",
- "szczytno.pl",
- "arkhangelsk.ru",
- "endofinternet.org",
- "xn--mgbc0a9azcg",
- "miyawaka.fukuoka.jp",
- "xn--unjrga-rta.no",
- "is-a-hunter.com",
- "is-an-engineer.com",
- "shimotsuma.ibaraki.jp",
- "sayama.osaka.jp",
- "paroch.k12.ma.us",
- "tobe.ehime.jp",
- "za.net",
- "tsukuba.ibaraki.jp",
- "from-nd.com",
- "toon.ehime.jp",
- "is-a-chef.org",
- "otoyo.kochi.jp",
- "trainer.aero",
- "lubin.pl",
- "xn--porsgu-sta26f.no",
- "diamonds",
- "org.sv",
- "kihoku.ehime.jp",
- "org.lv",
- "sayama.saitama.jp",
- "ichinoseki.iwate.jp",
- "tamaki.mie.jp",
- "oharu.aichi.jp",
- "oarai.ibaraki.jp",
- "tatsuno.hyogo.jp",
- "tabuse.yamaguchi.jp",
- "org.mv",
- "doomdns.org",
- "uk.net",
- "xn--ostery-fya.no",
- "is-a-celticsfan.org",
- "gs.sf.no",
- "gs.of.no",
- "xn--hnefoss-q1a.no",
- "ap-northeast-1.compute.amazonaws.com",
- "omaha.museum",
- "stuff-4-sale.us",
- "nogi.tochigi.jp",
- "pomorze.pl",
- "xn--mgberp4a5d4a87g",
- "misconfused.org",
- "xn--kranghke-b0a.no",
- "tm.mg",
- "schlesisches.museum",
- "at-band-camp.net",
- "lib.ny.us",
- "xn--bhccavuotna-k7a.no",
- "xn--krdsherad-m8a.no",
- "satx.museum",
- "artsandcrafts.museum",
- "adygeya.ru",
- "sosnowiec.pl",
- "kimitsu.chiba.jp",
- "izumizaki.fukushima.jp",
- "textile.museum",
- "eu.int",
- "yaita.tochigi.jp",
- "sells-for-less.com",
- "servegame.org",
- "xn--stjrdal-s1a.no",
- "jgora.pl",
- "jpn.com",
- "tomi.nagano.jp",
- "england.museum",
- "finland.museum",
- "s3.amazonaws.com",
- "myphotos.cc",
- "sano.tochigi.jp",
- "tsukiyono.gunma.jp",
- "mutsuzawa.chiba.jp",
- "xn--hgebostad-g3a.no",
- "luxury",
- "from-tx.com",
- "pg.it",
- "schoenbrunn.museum",
- "xn--oppegrd-ixa.no",
- "olbiatempio.it",
- "lib.ky.us",
- "tsuwano.shimane.jp",
- "from-tn.com",
- "eu-west-1.compute.amazonaws.com",
- "nakatsugawa.gifu.jp",
- "xn--vler-qoa.xn--stfold-9xa.no",
- "taiwa.miyagi.jp",
- "tsuruga.fukui.jp",
- "xn--rlingen-mxa.no",
- "xn--lten-gra.no",
- "design.aero",
- "schokoladen.museum",
- "lutsk.ua",
- "us-west-2.compute.amazonaws.com",
- "dagestan.ru",
- "chippubetsu.hokkaido.jp",
- "lesja.no",
- "us-west-1.compute.amazonaws.com",
- "computer",
- "kawai.iwate.jp",
- "sciencecenter.museum",
- "ln.cn",
- "sciencecenters.museum",
- "ap-southeast-2.compute.amazonaws.com",
- "of.no",
- "journal.aero",
- "jorpeland.no",
- "ap-southeast-1.compute.amazonaws.com",
- "juedisches.museum",
- "guovdageaidnu.no",
- "la.us",
- "szex.hu",
- "kiyokawa.kanagawa.jp",
- "science.museum",
- "jx.cn",
- "donostia.museum",
- "davvesiida.no",
- "lt.ua",
- "yachiyo.ibaraki.jp",
- "lib.wy.us",
- "hembygdsforbund.museum",
- "cc.sd.us",
- "cc.az.us",
- "k12.ct.us",
- "cc.as.us",
- "cc.ar.us",
- "org.gt",
- "cc.or.us",
- "cc.mo.us",
- "cc.md.us",
- "cc.al.us",
- "cc.ms.us",
- "project.museum",
- "samnanger.no",
- "cc.nd.us",
- "okawa.kochi.jp",
- "neyagawa.osaka.jp",
- "cc.tn.us",
- "tosashimizu.kochi.jp",
- "lakas.hu",
- "shirako.chiba.jp",
- "judaica.museum",
- "cc.gu.us",
- "cc.mn.us",
- "cc.co.us",
- "cc.de.us",
- "cc.ut.us",
- "cc.me.us",
- "ichikai.tochigi.jp",
- "dazaifu.fukuoka.jp",
- "cc.ne.us",
- "cc.mt.us",
- "tokoname.aichi.jp",
- "cc.fl.us",
- "xn--stjrdalshalsen-sqb.no",
- "um.gov.pl",
- "atlanta.museum",
- "lv.ua",
- "oyama.tochigi.jp",
- "cc.mi.us",
- "cc.ri.us",
- "cc.ct.us",
- "lc",
- "design.museum",
- "cc.ks.us",
- "is-a-techie.com",
- "www.ck",
- "of.by",
- "computer.museum",
- "cc.ny.us",
- "lukow.pl",
- "cc.nj.us",
- "cc.nm.us",
- "jerusalem.museum",
- "tachiarai.fukuoka.jp",
- "cc.la.us",
- "lomza.pl",
- "cc.ma.us",
- "coop.tt",
- "schweiz.museum",
- "cc.ga.us",
- "sc.ug",
- "barrel-of-knowledge.info",
- "sande.more-og-romsdal.no",
- "anpachi.gifu.jp",
- "cc.ky.us",
- "cc.nv.us",
- "cc.ca.us",
- "isumi.chiba.jp",
- "pharmacien.fr",
- "go.dyndns.org",
- "cc.oh.us",
- "cc.hi.us",
- "cc.nh.us",
- "shibata.miyagi.jp",
- "is-into-anime.com",
- "fukuchi.fukuoka.jp",
- "taki.mie.jp",
- "inzai.chiba.jp",
- "cc.ak.us",
- "cc.ok.us",
- "depot.museum",
- "dolls.museum",
- "wa.gov.au",
- "spydeberg.no",
- "iwafune.tochigi.jp",
- "taishin.fukushima.jp",
- "lo.it",
- "lib.co.us",
- "ohira.tochigi.jp",
- "student.aero",
- "pro.tt",
- "lu.it",
- "s3-us-west-1.amazonaws.com",
- "s3-us-west-2.amazonaws.com",
- "asahi.chiba.jp",
- "le.it",
- "takanabe.miyazaki.jp",
- "lt.it",
- "lib.ca.us",
- "iwakuni.yamaguchi.jp",
- "s3-eu-west-1.amazonaws.com",
- "org.cn",
- "ltd.co.im",
- "li.it",
- "abiko.chiba.jp",
- "london",
- "seaport.museum",
- "jogasz.hu",
- "kiwa.mie.jp",
- "lund.no",
- "priv.pl",
- "uw.gov.pl",
- "tsuchiura.ibaraki.jp",
- "starachowice.pl",
- "lier.no",
- "music.museum",
- "from-va.com",
- "lib.fl.us",
- "pro.ht",
- "cc.id.us",
- "lebtimnetz.de",
- "cc.il.us",
- "tobishima.aichi.jp",
- "shirataka.yamagata.jp",
- "or.pw",
- "cc.in.us",
- "cc.wi.us",
- "travel",
- "architecture.museum",
- "trogstad.no",
- "kuwana.mie.jp",
- "teaches-yoga.com",
- "cc.wy.us",
- "computerhistory.museum",
- "lyngen.no",
- "pol.ht",
- "ostrowiec.pl",
- "jamison.museum",
- "daigo.ibaraki.jp",
- "tynset.no",
- "cc.wa.us",
- "travel.pl",
- "tatsuno.nagano.jp",
- "joboji.iwate.jp",
- "travel.tt",
- "test.tj",
- "livorno.it",
- "cc.wv.us",
- "xn--ryken-vua.no",
- "cc.ia.us",
- "lindesnes.no",
- "priv.me",
- "shunan.yamaguchi.jp",
- "lib.tn.us",
- "treviso.it",
- "press.aero",
- "press.se",
- "scrapping.cc",
- "leirfjord.no",
- "tysvar.no",
- "lib.in.us",
- "harvestcelebration.museum",
- "lib.mn.us",
- "laquila.it",
- "taranto.it",
- "tsunan.niigata.jp",
- "lahppi.no",
- "bruxelles.museum",
- "london.museum",
- "xn--avery-yua.no",
- "xn--ryrvik-bya.no",
- "obanazawa.yamagata.jp",
- "leitungsen.de",
- "lincoln.museum",
- "pilot.aero",
- "parti.se",
- "larsson.museum",
- "cc.tx.us",
- "press.museum",
- "dc.us",
- "luzern.museum",
- "pubol.museum",
- "ac.pr",
- "towada.aomori.jp",
- "oceanographic.museum",
- "laakesvuemie.no",
- "z-1.compute-1.amazonaws.com",
- "monzaedellabrianza.it",
- "for-more.biz",
- "stuff-4-sale.org",
- "tsaritsyn.ru",
- "trieste.it",
- "for-some.biz",
- "xn--3e0b707e",
- "priv.at",
- "torahime.shiga.jp",
- "from-vt.com",
- "turystyka.pl",
- "ac.pa",
- "se.net",
- "planetarium.museum",
- "miyama.mie.jp",
- "homeftp.org",
- "omigawa.chiba.jp",
- "xn--fiq64b",
- "museumvereniging.museum",
- "s3-ap-northeast-1.amazonaws.com",
- "paleo.museum",
- "trust.museum",
- "toga.toyama.jp",
- "xn--kvnangen-k0a.no",
- "office-on-the.net",
- "lancashire.museum",
- "ogaki.gifu.jp",
- "tsuno.kochi.jp",
- "praxi",
- "vaapste.no",
- "pro.mv",
- "s3-ap-southeast-2.amazonaws.com",
- "s3-ap-southeast-1.amazonaws.com",
- "tochigi.tochigi.jp",
- "tama.tokyo.jp",
- "ogawa.ibaraki.jp",
- "kawanabe.kagoshima.jp",
- "xn--fiq228c5hs",
- "touch.museum",
- "discovery.museum",
- "s3-us-gov-west-1.amazonaws.com",
- "leka.no",
- "newyork.museum",
- "trysil.no",
- "lajolla.museum",
- "isa-hockeynut.com",
- "tsuruta.aomori.jp",
- "sayo.hyogo.jp",
- "poltava.ua",
- "podzone.org",
- "lg.ua",
- "lib.az.us",
- "sa-east-1.compute.amazonaws.com",
- "xn--btsfjord-9za.no",
- "newspaper.museum",
- "ug.gov.pl",
- "likes-pie.com",
- "paris.museum",
- "takinoue.hokkaido.jp",
- "lillehammer.no",
- "bellevue.museum",
- "nrw.museum",
- "tambov.ru",
- "texas.museum",
- "is-a-republican.com",
- "miyako.iwate.jp",
- "tawaramoto.nara.jp",
- "forli-cesena.it",
- "philately.museum",
- "s3-website-us-east-1.amazonaws.com",
- "tarui.gifu.jp",
- "s3-website-sa-east-1.amazonaws.com",
- "xn--cg4bki",
- "sologne.museum",
- "game-server.cc",
- "doesntexist.org",
- "xn--kvfjord-nxa.no",
- "town.museum",
- "parachuting.aero",
- "gs.va.no",
- "tokuyama.yamaguchi.jp",
- "newhampshire.museum",
- "florence.it",
- "tsubetsu.hokkaido.jp",
- "from-nc.com",
- "tokamachi.niigata.jp",
- "miyoshi.aichi.jp",
- "xn--ogbpf8fl",
- "xn--mgbqly7c0a67fbc",
- "historyofscience.museum",
- "oryol.ru",
- "so.gov.pl",
- "toyama.jp",
- "sr.gov.pl",
- "dyndns-server.com",
- "is-a-chef.net",
- "tm.mc",
- "xn--czr694b",
- "nationalheritage.museum",
- "xn--czrs0t",
- "bievat.no",
- "is-a-chef.com",
- "xn--mgbtf8fl",
- "pippu.hokkaido.jp",
- "elasticbeanstalk.com",
- "s3-website-ap-southeast-2.amazonaws.com",
- "xn--comunicaes-v6a2o.museum",
- "oi.kanagawa.jp",
- "toyama.toyama.jp",
- "takanezawa.tochigi.jp",
- "s3-website-ap-southeast-1.amazonaws.com",
- "sa.gov.au",
- "xn--czru2d",
- "s3-website-ap-northeast-1.amazonaws.com",
- "toyooka.hyogo.jp",
- "toyone.aichi.jp",
- "doomdns.com",
- "toyo.kochi.jp",
- "tsuga.tochigi.jp",
- "s3-website-us-west-2.amazonaws.com",
- "shinonsen.hyogo.jp",
- "xn--moreke-jua.no",
- "tokai.aichi.jp",
- "toyota.aichi.jp",
- "s3-website-us-west-1.amazonaws.com",
- "is-very-sweet.org",
- "xn--hyanger-q1a.no",
- "kawachinagano.osaka.jp",
- "toyono.osaka.jp",
- "nishitosa.kochi.jp",
- "tokai.ibaraki.jp",
- "pc.it",
- "xn--nvuotna-hwa.no",
- "sande.vestfold.no",
- "jobs.tt",
- "xn--kvitsy-fya.no",
- "royken.no",
- "tm.pl",
- "xn--eveni-0qa01ga.no",
- "nishinoomote.kagoshima.jp",
- "kommunalforbund.se",
- "xn--nmesjevuemie-tcba.no",
- "priv.hu",
- "suwa.nagano.jp",
- "toyako.hokkaido.jp",
- "toyonaka.osaka.jp",
- "toyokawa.aichi.jp",
- "egyptian.museum",
- "yokoshibahikari.chiba.jp",
- "toyotomi.hokkaido.jp",
- "lg.jp",
- "plaza.museum",
- "shimonoseki.yamaguchi.jp",
- "principe.st",
- "kongsvinger.no",
- "toyoura.hokkaido.jp",
- "newjersey.museum",
- "prochowice.pl",
- "tsubata.ishikawa.jp",
- "lenvik.no",
- "society.museum",
- "is-a-cubicle-slave.com",
- "dreamhosters.com",
- "xn--gecrj9c",
- "larvik.no",
- "lib.ut.us",
- "law.pro",
- "dnepropetrovsk.ua",
- "loabat.no",
- "niyodogawa.kochi.jp",
- "lib.mt.us",
- "uchiko.ehime.jp",
- "porsanger.no",
- "lib.vt.us",
- "louvre.museum",
- "toya.hokkaido.jp",
- "dnipropetrovsk.ua",
- "tempio-olbia.it",
- "vossevangen.no",
- "ac.vn",
- "xn--sndre-land-0cb.no",
- "yanaizu.fukushima.jp",
- "tcm.museum",
- "trapani.it",
- "xn--frde-gra.no",
- "kawaguchi.saitama.jp",
- "skiptvet.no",
- "trading.aero",
- "xn--tnsberg-q1a.no",
- "sakae.chiba.jp",
- "loppa.no",
- "spy.museum",
- "labor.museum",
- "dyndns-blog.com",
- "is-a-nascarfan.com",
- "saskatchewan.museum",
- "yawatahama.ehime.jp",
- "joyo.kyoto.jp",
- "lviv.ua",
- "dyndns-free.com",
- "xn--czrw28b.tw",
- "otaki.chiba.jp",
- "dyndns-office.com",
- "ruovat.no",
- "xn--lt-liac.no",
- "jewish.museum",
- "yachiyo.chiba.jp",
- "lib.nv.us",
- "jewishart.museum",
- "langevag.no",
- "jan-mayen.no",
- "homeftp.net",
- "directory",
- "loyalist.museum",
- "natuurwetenschappen.museum",
- "is-a-teacher.com",
- "s3-website-us-gov-west-1.amazonaws.com",
- "is-not-certified.com",
- "cc.sc.us",
- "science-fiction.museum",
- "cc.dc.us",
- "cc.nc.us",
- "is-into-cars.com",
- "is-into-cartoons.com",
- "podzone.net",
- "ofunato.iwate.jp",
- "doesntexist.com",
- "is-a-therapist.com",
- "nationalfirearms.museum",
- "toyosato.shiga.jp",
- "cc.pr.us",
- "lc.it",
- "national-library-scotland.uk",
- "cc.pa.us",
- "is-with-theband.com",
- "davvenjarga.no",
- "priv.no",
- "convent.museum",
- "xn--indery-fya.no",
- "po.gov.pl",
- "pa.gov.pl",
- "compute.amazonaws.com",
- "compute-1.amazonaws.com",
- "experts-comptables.fr",
- "leasing.aero",
- "xn--54b7fta0cc",
- "is-very-nice.org",
- "xn--fpcrj9c3d",
- "jfk.museum",
- "leikanger.no",
- "lucerne.museum",
- "jp.net",
- "xn--mtta-vrjjat-k7af.no",
- "gs.vf.no",
- "laspezia.it",
- "is-into-games.com",
- "resistance.museum",
- "pc.pl",
- "scientist.aero",
- "s3-website-eu-west-1.amazonaws.com",
- "xn--hmmrfeasta-s4ac.no",
- "newport.museum",
- "stavanger.no",
- "toyoake.aichi.jp",
- "jewelry.museum",
- "cc.vt.us",
- "cc.vi.us",
- "toyota.yamaguchi.jp",
- "cc.va.us",
- "hoylandet.no",
- "corvette.museum",
- "toyohashi.aichi.jp",
- "lib.ct.us",
- "air-traffic-control.aero",
- "us-gov-west-1.compute.amazonaws.com",
- "jaworzno.pl",
- "posts-and-telecommunications.museum",
- "powiat.pl",
- "passenger-association.aero",
- "logistics.aero",
- "lewismiller.museum",
- "xn--stre-toten-zcb.no",
- "toyotsu.fukuoka.jp",
- "pacific.museum",
- "xn--nttery-byae.no",
- "shimotsuke.tochigi.jp",
- "geometre-expert.fr",
- "endoftheinternet.org",
- "skydiving.aero",
- "xn--correios-e-telecomunicaes-ghc29a.museum",
- "lowicz.pl",
- "xn--clchc0ea0b2g2a9gcd",
- "xn--fzc2c9e2c"
- };
-#define stringpool ((const char *) &stringpool_contents)
-const struct DomainRule *
-Perfect_Hash::FindDomain (register const char *str, register unsigned int len)
-{
- static const struct DomainRule wordlist[] =
- {
-#line 2409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str0, 0},
-#line 1822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1, 0},
-#line 1709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2, 0},
-#line 2272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3, 0},
-#line 1246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4, 0},
-#line 846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5, 0},
-#line 1800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6, 0},
-#line 729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str7, 0},
-#line 1107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str8, 0},
-#line 1849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str9, 0},
-#line 910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str10, 0},
-#line 1780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str11, 0},
-#line 1897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str12, 0},
-#line 1270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str13, 0},
-#line 1848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str14, 0},
-#line 1958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str15, 0},
-#line 2419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str16, 0},
-#line 1318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str17, 0},
-#line 1414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str18, 0},
-#line 1269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str19, 0},
-#line 939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str20, 0},
-#line 1935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str21, 0},
-#line 2412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str22, 0},
-#line 2237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str23, 0},
-#line 1410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str24, 2},
-#line 990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str25, 0},
-#line 938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str26, 0},
-#line 1079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str27, 0},
-#line 212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str28, 0},
-#line 1907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str29, 0},
-#line 79 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str30, 0},
-#line 365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str31, 0},
-#line 1331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str32, 0},
-#line 1014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str33, 0},
-#line 1750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str34, 0},
-#line 2306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str35, 2},
-#line 1003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str36, 0},
-#line 1912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str37, 0},
-#line 826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str38, 0},
-#line 2213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str39, 0},
-#line 271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str40, 0},
-#line 1878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str41, 0},
-#line 1336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str42, 0},
-#line 1911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str43, 0},
-#line 1302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str44, 0},
-#line 230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str45, 0},
-#line 548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str46, 0},
-#line 1335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str47, 0},
-#line 1008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str48, 0},
-#line 416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str49, 2},
-#line 622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str50, 0},
-#line 1528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str51, 0},
-#line 973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str52, 0},
-#line 1007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str53, 0},
-#line 252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str54, 0},
-#line 1983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str55, 2},
-#line 255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str56, 0},
-#line 2240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str57, 0},
-#line 1434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str58, 0},
-#line 1876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str59, 0},
-#line 1059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str60, 0},
-#line 1044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str61, 0},
-#line 1993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str62, 0},
-#line 152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str63, 0},
-#line 1094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str64, 0},
-#line 1300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str65, 0},
-#line 593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str66, 0},
-#line 1875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str67, 0},
-#line 1299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str68, 0},
-#line 568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str69, 0},
-#line 971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str70, 0},
-#line 1877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str71, 0},
-#line 1916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str72, 0},
-#line 1551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str73, 0},
-#line 1301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str74, 0},
-#line 970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str75, 0},
-#line 1339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str76, 0},
-#line 937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str77, 4},
-#line 972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str78, 0},
-#line 1012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str79, 0},
-#line 1683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str80, 0},
-#line 2251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str81, 0},
-#line 2006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str82, 0},
-#line 1226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str83, 0},
-#line 21 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str84, 0},
-#line 16 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str85, 0},
-#line 335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str86, 0},
-#line 25 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str87, 0},
-#line 24 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str88, 0},
-#line 23 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str89, 0},
-#line 20 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str90, 0},
-#line 19 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str91, 0},
-#line 624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str92, 0},
-#line 4640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str93, 0},
-#line 18 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str94, 0},
-#line 15 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str95, 0},
-#line 1763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str96, 0},
-#line 2320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str97, 0},
-#line 2186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str98, 0},
-#line 2386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str99, 0},
-#line 1684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str100, 0},
-#line 2253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str101, 0},
-#line 2007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str102, 0},
-#line 1227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str103, 0},
-#line 14 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str104, 0},
-#line 840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str105, 0},
-#line 1956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str106, 0},
-#line 1002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str107, 0},
-#line 269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str108, 0},
-#line 626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str109, 0},
-#line 1896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str110, 0},
-#line 1317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str111, 0},
-#line 543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str112, 0},
-#line 989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str113, 0},
-#line 26 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str114, 0},
-#line 349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str115, 0},
-#line 1439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str116, 0},
-#line 266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str117, 0},
-#line 1714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str118, 0},
-#line 2282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str119, 0},
-#line 2355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str120, 0},
-#line 1360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str121, 0},
-#line 2191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str122, 0},
-#line 190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str123, 0},
-#line 28 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str124, 0},
-#line 2233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str125, 0},
-#line 2228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str126, 0},
-#line 1782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str127, 0},
-#line 369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str128, 0},
-#line 1275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str129, 0},
-#line 368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str130, 0},
-#line 944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str131, 0},
-#line 1934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str132, 0},
-#line 2411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str133, 0},
-#line 1451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str134, 0},
-#line 1982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str135, 0},
-#line 2521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str136, 0},
-#line 2239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str137, 0},
-#line 1427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str138, 2},
-#line 546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str139, 2},
-#line 1851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str140, 0},
-#line 1781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str141, 0},
-#line 370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str142, 0},
-#line 86 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str143, 0},
-#line 1272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str144, 0},
-#line 1452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str145, 0},
-#line 1924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str146, 0},
-#line 1752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str147, 0},
-#line 1920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str148, 0},
-#line 941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str149, 0},
-#line 1346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str150, 0},
-#line 1922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str151, 0},
-#line 89 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str152, 0},
-#line 1344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str153, 0},
-#line 1345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str154, 0},
-#line 1021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str155, 0},
-#line 1910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str156, 0},
-#line 1018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str157, 0},
-#line 1019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str158, 0},
-#line 226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str159, 0},
-#line 2394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str160, 0},
-#line 1334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str161, 0},
-#line 1913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str162, 0},
-#line 2373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str163, 0},
-#line 418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str164, 0},
-#line 325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str165, 0},
-#line 1337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str166, 0},
-#line 1006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str167, 0},
-#line 735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str168, 0},
-#line 1009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str169, 0},
-#line 347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str170, 0},
-#line 2501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str171, 0},
-#line 1874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str172, 0},
-#line 899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str173, 0},
-#line 2360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str174, 0},
-#line 1298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str175, 0},
-#line 1852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str176, 0},
-#line 310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str177, 0},
-#line 1273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str178, 0},
-#line 595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str179, 0},
-#line 969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str180, 0},
-#line 942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str181, 0},
-#line 2365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str182, 0},
-#line 1921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str183, 0},
-#line 2371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str184, 0},
-#line 1837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str185, 0},
-#line 1836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str186, 0},
-#line 1835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str187, 0},
-#line 1778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str188, 0},
-#line 1927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str189, 0},
-#line 1261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str190, 0},
-#line 1260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str191, 0},
-#line 1259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str192, 0},
-#line 1834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str193, 0},
-#line 1349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str194, 0},
-#line 2364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str195, 0},
-#line 1258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str196, 0},
-#line 928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str197, 0},
-#line 927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str198, 0},
-#line 926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str199, 0},
-#line 6382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str200, 0},
-#line 1024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str201, 0},
-#line 925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str202, 0},
-#line 1733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str203, 0},
-#line 1831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str204, 0},
-#line 1418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str205, 0},
-#line 442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str206, 0},
-#line 1425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str207, 0},
-#line 805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str208, 0},
-#line 1255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str209, 0},
-#line 1862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str210, 0},
-#line 1864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str211, 0},
-#line 1863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str212, 0},
-#line 84 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str213, 0},
-#line 1287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str214, 0},
-#line 1288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str215, 0},
-#line 922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str216, 0},
-#line 82 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str217, 0},
-#line 3564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str218, 0},
-#line 960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str219, 0},
-#line 961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str220, 0},
-#line 3341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str221, 0},
-#line 959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str222, 0},
-#line 3682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str223, 2},
-#line 1736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str224, 0},
-#line 414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str225, 0},
-#line 958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str226, 0},
-#line 2400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str227, 0},
-#line 424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str228, 0},
-#line 986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str229, 0},
-#line 115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str230, 0},
-#line 6358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str231, 0},
-#line 254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str232, 0},
-#line 3631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str233, 0},
-#line 1074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str234, 0},
-#line 1548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str235, 0},
-#line 3628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str236, 0},
-#line 2372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str237, 0},
-#line 6359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str238, 0},
-#line 1894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str239, 0},
-#line 3586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str240, 0},
-#line 1909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str241, 0},
-#line 1333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str242, 0},
-#line 449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str243, 0},
-#line 1623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str244, 0},
-#line 1472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str245, 0},
-#line 3559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str246, 0},
-#line 2393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str247, 0},
-#line 1479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str248, 0},
-#line 1005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str249, 0},
-#line 733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str250, 0},
-#line 311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str251, 0},
-#line 1809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str252, 4},
-#line 1906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str253, 0},
-#line 1330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str254, 0},
-#line 2359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str255, 0},
-#line 1509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str256, 0},
-#line 2358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str257, 0},
-#line 2000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str258, 0},
-#line 1738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str259, 0},
-#line 1860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str260, 0},
-#line 1001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str261, 0},
-#line 2370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str262, 0},
-#line 1104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str263, 2},
-#line 2389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str264, 0},
-#line 3639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str265, 0},
-#line 845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str266, 0},
-#line 498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str267, 0},
-#line 2352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str268, 0},
-#line 1517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str269, 0},
-#line 625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str270, 4},
-#line 308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str271, 0},
-#line 2392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str272, 0},
-#line 3256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str273, 0},
-#line 5767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str274, 0},
-#line 3561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str275, 0},
-#line 3257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str276, 0},
-#line 617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str277, 0},
-#line 1830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str278, 0},
-#line 1866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str279, 0},
-#line 1254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str280, 0},
-#line 5730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str281, 0},
-#line 1290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str282, 0},
-#line 1547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str283, 0},
-#line 921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str284, 0},
-#line 962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str285, 0},
-#line 902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str286, 0},
-#line 1761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str287, 0},
-#line 2310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str288, 0},
-#line 2184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str289, 0},
-#line 3344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str290, 0},
-#line 1499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str291, 0},
-#line 838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str292, 0},
-#line 2399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str293, 0},
-#line 2089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str294, 0},
-#line 1889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str295, 0},
-#line 1888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str296, 0},
-#line 1310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str297, 0},
-#line 979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str298, 0},
-#line 1495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str299, 0},
-#line 3627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str300, 0},
-#line 1685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str301, 0},
-#line 1466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str302, 0},
-#line 3636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str303, 0},
-#line 3580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str304, 0},
-#line 169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str305, 0},
-#line 489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str306, 0},
-#line 3370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str307, 0},
-#line 1503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str308, 2},
-#line 1926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str309, 0},
-#line 3379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str310, 0},
-#line 1885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str311, 0},
-#line 534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str312, 4},
-#line 2281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str313, 0},
-#line 1348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str314, 0},
-#line 1795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str315, 0},
-#line 1307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str316, 0},
-#line 2198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str317, 0},
-#line 518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str318, 4},
-#line 1023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str319, 0},
-#line 977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str320, 0},
-#line 544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str321, 0},
-#line 537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str322, 4},
-#line 1523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str323, 0},
-#line 1886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str324, 0},
-#line 455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str325, 0},
-#line 5667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str326, 0},
-#line 1308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str327, 0},
-#line 978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str328, 0},
-#line 1883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str329, 0},
-#line 332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str330, 0},
-#line 5668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str331, 0},
-#line 1305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str332, 0},
-#line 533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str333, 4},
-#line 1712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str334, 0},
-#line 2341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str335, 0},
-#line 808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str336, 0},
-#line 1393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str337, 0},
-#line 2366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str338, 0},
-#line 162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str339, 0},
-#line 3660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str340, 0},
-#line 122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str341, 0},
-#line 844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str342, 0},
-#line 530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str343, 4},
-#line 1488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str344, 0},
-#line 1686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str345, 0},
-#line 900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str346, 4},
-#line 541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str347, 4},
-#line 2514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str348, 0},
-#line 2073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str349, 0},
-#line 627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str350, 0},
-#line 4641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str351, 0},
-#line 664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str352, 0},
-#line 1827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str353, 0},
-#line 632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str354, 0},
-#line 1777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str355, 0},
-#line 1251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str356, 0},
-#line 1365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str357, 0},
-#line 917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str358, 0},
-#line 3579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str359, 1},
-#line 5712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str360, 0},
-#line 1621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str361, 0},
-#line 1823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str362, 0},
-#line 90 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str363, 0},
-#line 1247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str364, 0},
-#line 911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str365, 0},
-#line 1482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str366, 0},
-#line 512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str367, 4},
-#line 1527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str368, 0},
-#line 1257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str369, 0},
-#line 1933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str370, 0},
-#line 1882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str371, 0},
-#line 372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str372, 0},
-#line 2354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str373, 0},
-#line 650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str374, 0},
-#line 395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str375, 0},
-#line 924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str376, 0},
-#line 531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str377, 4},
-#line 3663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str378, 0},
-#line 1903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str379, 0},
-#line 1902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str380, 0},
-#line 1826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str381, 0},
-#line 3353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str382, 0},
-#line 1327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str383, 0},
-#line 1326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str384, 0},
-#line 309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str385, 0},
-#line 1249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str386, 0},
-#line 473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str387, 0},
-#line 658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str388, 0},
-#line 998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str389, 0},
-#line 997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str390, 0},
-#line 4649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str391, 0},
-#line 915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str392, 0},
-#line 1824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str393, 0},
-#line 6381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str394, 2},
-#line 1424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str395, 0},
-#line 536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str396, 4},
-#line 467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str397, 0},
-#line 3675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str398, 0},
-#line 5713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str399, 0},
-#line 1899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str400, 0},
-#line 1788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str401, 0},
-#line 1457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str402, 0},
-#line 1323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str403, 0},
-#line 1801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str404, 0},
-#line 1100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str405, 0},
-#line 995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str406, 0},
-#line 835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str407, 0},
-#line 1046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str408, 0},
-#line 6380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str409, 0},
-#line 2209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str410, 4},
-#line 2387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str411, 0},
-#line 1900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str412, 0},
-#line 3656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str413, 0},
-#line 2316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str414, 0},
-#line 1928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str415, 0},
-#line 1324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str416, 0},
-#line 1049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str417, 0},
-#line 1796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str418, 0},
-#line 1351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str419, 0},
-#line 1385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str420, 0},
-#line 2252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str421, 0},
-#line 996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str422, 0},
-#line 1787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str423, 0},
-#line 1028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str424, 0},
-#line 1320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str425, 0},
-#line 1981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str426, 0},
-#line 992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str427, 0},
-#line 293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str428, 0},
-#line 1805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str429, 0},
-#line 1758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str430, 0},
-#line 3560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str431, 2},
-#line 1892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str432, 0},
-#line 651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str433, 0},
-#line 1929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str434, 0},
-#line 1313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str435, 0},
-#line 259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str436, 0},
-#line 1352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str437, 0},
-#line 4648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str438, 0},
-#line 983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str439, 0},
-#line 1029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str440, 0},
-#line 481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str441, 0},
-#line 6360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str442, 2},
-#line 5765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str443, 0},
-#line 2532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str444, 0},
-#line 188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str445, 0},
-#line 519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str446, 4},
-#line 1759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str447, 0},
-#line 2432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str448, 4},
-#line 3349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str449, 0},
-#line 639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str450, 0},
-#line 1786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str451, 0},
-#line 987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str452, 0},
-#line 1319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str453, 0},
-#line 538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str454, 4},
-#line 516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str455, 4},
-#line 1711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str456, 0},
-#line 2245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str457, 0},
-#line 991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str458, 0},
-#line 600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str459, 0},
-#line 168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str460, 0},
-#line 3357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str461, 0},
-#line 315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str462, 0},
-#line 3358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str463, 0},
-#line 485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str464, 0},
-#line 2011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str465, 0},
-#line 535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str466, 4},
-#line 1678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str467, 0},
-#line 525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str468, 4},
-#line 1505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str469, 0},
-#line 3612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str470, 0},
-#line 3350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str471, 0},
-#line 3258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str472, 0},
-#line 287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str473, 0},
-#line 2396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str474, 0},
-#line 510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str475, 4},
-#line 5958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str476, 0},
-#line 3348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str477, 0},
-#line 919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str478, 0},
-#line 3665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str479, 0},
-#line 5959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str480, 0},
-#line 1455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str481, 0},
-#line 1732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str482, 0},
-#line 741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str483, 0},
-#line 312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str484, 0},
-#line 331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str485, 0},
-#line 4650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str486, 0},
-#line 515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str487, 4},
-#line 476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str488, 0},
-#line 3626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str489, 0},
-#line 1832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str490, 0},
-#line 459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str491, 0},
-#line 956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str492, 0},
-#line 1987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str493, 0},
-#line 1689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str494, 0},
-#line 545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str495, 0},
-#line 2381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str496, 0},
-#line 3416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str497, 0},
-#line 1442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str498, 0},
-#line 1859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str499, 0},
-#line 914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str500, 0},
-#line 2280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str501, 0},
-#line 1283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str502, 0},
-#line 3429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str503, 0},
-#line 1497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str504, 0},
-#line 954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str505, 0},
-#line 1394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str506, 0},
-#line 448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str507, 0},
-#line 3671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str508, 0},
-#line 2053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str509, 0},
-#line 427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str510, 0},
-#line 5669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str511, 0},
-#line 1228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str512, 0},
-#line 2090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str513, 0},
-#line 3285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str514, 0},
-#line 1441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str515, 0},
-#line 3389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str516, 0},
-#line 1395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str517, 0},
-#line 527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str518, 4},
-#line 2247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str519, 0},
-#line 3402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str520, 0},
-#line 2018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str521, 0},
-#line 585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str522, 0},
-#line 526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str523, 4},
-#line 505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str524, 4},
-#line 1030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str525, 0},
-#line 1429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str526, 0},
-#line 582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str527, 4},
-#line 3449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str528, 0},
-#line 3499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str529, 0},
-#line 318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str530, 0},
-#line 509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str531, 4},
-#line 1478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str532, 0},
-#line 528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str533, 4},
-#line 3401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str534, 0},
-#line 1500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str535, 0},
-#line 1229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str536, 0},
-#line 3400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str537, 0},
-#line 2380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str538, 0},
-#line 1431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str539, 0},
-#line 477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str540, 0},
-#line 3315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str541, 0},
-#line 466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str542, 0},
-#line 3430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str543, 0},
-#line 3462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str544, 0},
-#line 2057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str545, 0},
-#line 3460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str546, 0},
-#line 3461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str547, 0},
-#line 37 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str548, 0},
-#line 1682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str549, 0},
-#line 1690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str550, 0},
-#line 2180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str551, 0},
-#line 601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str552, 0},
-#line 517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str553, 4},
-#line 824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str554, 2},
-#line 1476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str555, 0},
-#line 1869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str556, 0},
-#line 583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str557, 0},
-#line 1293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str558, 0},
-#line 118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str559, 0},
-#line 965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str560, 0},
-#line 3374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str561, 0},
-#line 3424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str562, 0},
-#line 3423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str563, 0},
-#line 3385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str564, 0},
-#line 1704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str565, 0},
-#line 1507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str566, 2},
-#line 2506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str567, 0},
-#line 3438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str568, 0},
-#line 1871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str569, 0},
-#line 5951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str570, 0},
-#line 5696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str571, 0},
-#line 513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str572, 4},
-#line 1295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str573, 0},
-#line 1825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str574, 0},
-#line 966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str575, 0},
-#line 1248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str576, 0},
-#line 912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str577, 0},
-#line 484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str578, 0},
-#line 458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str579, 0},
-#line 3456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str580, 0},
-#line 514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str581, 4},
-#line 3356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str582, 0},
-#line 346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str583, 0},
-#line 396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str584, 0},
-#line 303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str585, 0},
-#line 3454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str586, 0},
-#line 1668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str587, 0},
-#line 1453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str588, 0},
-#line 1321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str589, 0},
-#line 482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str590, 0},
-#line 993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str591, 0},
-#line 3355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str592, 0},
-#line 1630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str593, 0},
-#line 5727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str594, 0},
-#line 5878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str595, 0},
-#line 1366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str596, 0},
-#line 5879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str597, 0},
-#line 483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str598, 0},
-#line 244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str599, 0},
-#line 2414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str600, 0},
-#line 3391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str601, 0},
-#line 3422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str602, 0},
-#line 3439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str603, 0},
-#line 5913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str604, 0},
-#line 480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str605, 0},
-#line 507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str606, 4},
-#line 1631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str607, 4},
-#line 1798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str608, 4},
-#line 584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str609, 0},
-#line 3436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str610, 0},
-#line 2345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str611, 0},
-#line 1077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str612, 0},
-#line 3661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str613, 0},
-#line 523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str614, 4},
-#line 1433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str615, 0},
-#line 652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str616, 0},
-#line 3354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str617, 0},
-#line 3463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str618, 0},
-#line 6239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str619, 0},
-#line 3557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str620, 0},
-#line 5915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str621, 0},
-#line 2178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str622, 0},
-#line 1529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str623, 0},
-#line 589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str624, 0},
-#line 3298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str625, 0},
-#line 3583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str626, 0},
-#line 392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str627, 0},
-#line 2410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str628, 0},
-#line 1471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str629, 0},
-#line 2319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str630, 0},
-#line 508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str631, 4},
-#line 1514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str632, 0},
-#line 433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str633, 0},
-#line 126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str634, 0},
-#line 3420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str635, 0},
-#line 1868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str636, 0},
-#line 1292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str637, 0},
-#line 2179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str638, 0},
-#line 5924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str639, 0},
-#line 3417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str640, 0},
-#line 964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str641, 0},
-#line 3375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str642, 0},
-#line 1744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str643, 4},
-#line 5918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str644, 0},
-#line 496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str645, 0},
-#line 5690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str646, 2},
-#line 747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str647, 0},
-#line 2226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str648, 0},
-#line 3419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str649, 0},
-#line 5949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str650, 0},
-#line 5942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str651, 0},
-#line 5932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str652, 0},
-#line 3418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str653, 0},
-#line 647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str654, 0},
-#line 5944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str655, 0},
-#line 1369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str656, 0},
-#line 1531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str657, 0},
-#line 1748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str658, 0},
-#line 3452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str659, 0},
-#line 3284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str660, 0},
-#line 3466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str661, 0},
-#line 3269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str662, 0},
-#line 3376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str663, 0},
-#line 2318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str664, 0},
-#line 3450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str665, 0},
-#line 828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str666, 0},
-#line 1557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str667, 0},
-#line 298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str668, 0},
-#line 2037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str669, 0},
-#line 665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str670, 0},
-#line 3467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str671, 0},
-#line 3664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str672, 0},
-#line 1722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str673, 0},
-#line 375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str674, 0},
-#line 2415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str675, 0},
-#line 1511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str676, 0},
-#line 1084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str677, 0},
-#line 3469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str678, 0},
-#line 1705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str679, 0},
-#line 195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str680, 0},
-#line 3316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str681, 0},
-#line 598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str682, 0},
-#line 3581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str683, 0},
-#line 1810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str684, 4},
-#line 520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str685, 4},
-#line 1367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str686, 0},
-#line 243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str687, 0},
-#line 836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str688, 0},
-#line 540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str689, 4},
-#line 5914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str690, 0},
-#line 2451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str691, 4},
-#line 2554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str692, 0},
-#line 2170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str693, 0},
-#line 430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str694, 0},
-#line 5897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str695, 0},
-#line 524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str696, 4},
-#line 2258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str697, 0},
-#line 2203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str698, 0},
-#line 1463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str699, 0},
-#line 5726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str700, 0},
-#line 3307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str701, 0},
-#line 127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str702, 0},
-#line 381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str703, 0},
-#line 179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str704, 0},
-#line 511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str705, 4},
-#line 3595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str706, 0},
-#line 1988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str707, 0},
-#line 399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str708, 0},
-#line 1426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str709, 0},
-#line 5953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str710, 0},
-#line 5769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str711, 0},
-#line 618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str712, 0},
-#line 5917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str713, 0},
-#line 657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str714, 0},
-#line 631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str715, 0},
-#line 3074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str716, 0},
-#line 834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str717, 4},
-#line 1095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str718, 0},
-#line 734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str719, 0},
-#line 2982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str720, 0},
-#line 667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str721, 0},
-#line 1908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str722, 0},
-#line 1332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str723, 0},
-#line 587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str724, 0},
-#line 1004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str725, 0},
-#line 3442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str726, 0},
-#line 3441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str727, 0},
-#line 5919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str728, 0},
-#line 795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str729, 0},
-#line 1109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str730, 0},
-#line 2919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str731, 2},
-#line 2991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str732, 0},
-#line 2321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str733, 0},
-#line 2618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str734, 0},
-#line 5916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str735, 0},
-#line 2916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str736, 0},
-#line 2619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str737, 0},
-#line 478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str738, 0},
-#line 3630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str739, 0},
-#line 1997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str740, 0},
-#line 1102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str741, 0},
-#line 2830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str742, 2},
-#line 1931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str743, 0},
-#line 2398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str744, 0},
-#line 1354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str745, 0},
-#line 1032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str746, 0},
-#line 5925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str747, 0},
-#line 1914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str748, 0},
-#line 356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str749, 0},
-#line 1338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str750, 0},
-#line 285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str751, 0},
-#line 1010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str752, 0},
-#line 908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str753, 0},
-#line 1412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str754, 0},
-#line 1082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str755, 0},
-#line 1739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str756, 0},
-#line 2990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str757, 0},
-#line 616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str758, 0},
-#line 1413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str759, 0},
-#line 336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str760, 0},
-#line 811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str761, 0},
-#line 529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str762, 4},
-#line 217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str763, 0},
-#line 573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str764, 0},
-#line 1025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str765, 0},
-#line 1986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str766, 0},
-#line 218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str767, 0},
-#line 1895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str768, 0},
-#line 1316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str769, 0},
-#line 2849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str770, 0},
-#line 988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str771, 0},
-#line 2862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str772, 0},
-#line 1941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str773, 0},
-#line 634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str774, 0},
-#line 1477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str775, 0},
-#line 1853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str776, 0},
-#line 1274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str777, 0},
-#line 1745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str778, 0},
-#line 2163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str779, 0},
-#line 3265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str780, 0},
-#line 943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str781, 0},
-#line 160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str782, 0},
-#line 313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str783, 0},
-#line 1087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str784, 0},
-#line 3501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str785, 0},
-#line 6388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str786, 2},
-#line 3378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str787, 0},
-#line 3063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str788, 0},
-#line 5922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str789, 4},
-#line 3369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str790, 0},
-#line 1943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str791, 0},
-#line 1053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str792, 0},
-#line 468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str793, 0},
-#line 2630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str794, 0},
-#line 1438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str795, 0},
-#line 1440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str796, 0},
-#line 382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str797, 0},
-#line 5902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str798, 0},
-#line 831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str799, 0},
-#line 3672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str800, 0},
-#line 2913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str801, 0},
-#line 1099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str802, 4},
-#line 662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str803, 0},
-#line 3571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str804, 0},
-#line 1392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str805, 0},
-#line 1890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str806, 0},
-#line 441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str807, 0},
-#line 5678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str808, 0},
-#line 981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str809, 0},
-#line 645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str810, 0},
-#line 670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str811, 0},
-#line 2021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str812, 0},
-#line 5741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str813, 0},
-#line 668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str814, 0},
-#line 420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str815, 0},
-#line 2356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str816, 4},
-#line 1804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str817, 0},
-#line 732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str818, 0},
-#line 167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str819, 0},
-#line 1666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str820, 0},
-#line 3613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str821, 0},
-#line 5901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str822, 0},
-#line 457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str823, 0},
-#line 209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str824, 0},
-#line 3569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str825, 0},
-#line 351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str826, 0},
-#line 3313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str827, 0},
-#line 1037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str828, 0},
-#line 1681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str829, 0},
-#line 1828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str830, 0},
-#line 2503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str831, 0},
-#line 581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str832, 0},
-#line 1252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str833, 0},
-#line 3289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str834, 0},
-#line 918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str835, 0},
-#line 4651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str836, 0},
-#line 1884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str837, 0},
-#line 5697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str838, 0},
-#line 1306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str839, 0},
-#line 976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str840, 0},
-#line 3648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str841, 0},
-#line 1225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str842, 0},
-#line 2981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str843, 0},
-#line 407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str844, 0},
-#line 2143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str845, 0},
-#line 810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str846, 0},
-#line 299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str847, 0},
-#line 1521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str848, 0},
-#line 198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str849, 0},
-#line 1400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str850, 0},
-#line 542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str851, 4},
-#line 2753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str852, 0},
-#line 3380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str853, 0},
-#line 646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str854, 0},
-#line 913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str855, 0},
-#line 2328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str856, 0},
-#line 5705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str857, 0},
-#line 436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str858, 0},
-#line 3304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str859, 0},
-#line 2201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str860, 0},
-#line 1380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str861, 0},
-#line 3502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str862, 0},
-#line 3279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str863, 0},
-#line 2085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str864, 0},
-#line 6370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str865, 0},
-#line 2159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str866, 0},
-#line 5746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str867, 0},
-#line 761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str868, 0},
-#line 444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str869, 4},
-#line 4645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str870, 0},
-#line 655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str871, 0},
-#line 1484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str872, 0},
-#line 1797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str873, 0},
-#line 5907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str874, 0},
-#line 756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str875, 0},
-#line 88 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str876, 0},
-#line 491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str877, 0},
-#line 1542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str878, 0},
-#line 576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str879, 0},
-#line 738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str880, 0},
-#line 1549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str881, 0},
-#line 3371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str882, 0},
-#line 3662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str883, 0},
-#line 2839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str884, 2},
-#line 3617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str885, 0},
-#line 150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str886, 0},
-#line 6371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str887, 0},
-#line 5676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str888, 0},
-#line 5706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str889, 0},
-#line 1089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str890, 0},
-#line 1372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str891, 0},
-#line 3455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str892, 0},
-#line 5950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str893, 0},
-#line 1368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str894, 0},
-#line 353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str895, 0},
-#line 2659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str896, 0},
-#line 2043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str897, 0},
-#line 2628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str898, 0},
-#line 3403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str899, 0},
-#line 3498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str900, 0},
-#line 2658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str901, 0},
-#line 2663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str902, 0},
-#line 388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str903, 0},
-#line 3655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str904, 0},
-#line 178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str905, 0},
-#line 1375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str906, 0},
-#line 2330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str907, 0},
-#line 2629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str908, 0},
-#line 5760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str909, 0},
-#line 305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str910, 0},
-#line 6377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str911, 0},
-#line 182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str912, 0},
-#line 2966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str913, 0},
-#line 2988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str914, 0},
-#line 2653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str915, 0},
-#line 2654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str916, 0},
-#line 2340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str917, 0},
-#line 605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str918, 0},
-#line 2649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str919, 0},
-#line 1803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str920, 0},
-#line 1396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str921, 0},
-#line 1054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str922, 0},
-#line 654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str923, 0},
-#line 322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str924, 0},
-#line 2651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str925, 0},
-#line 1086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str926, 0},
-#line 159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str927, 0},
-#line 3448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str928, 0},
-#line 419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str929, 0},
-#line 2082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str930, 0},
-#line 2640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str931, 0},
-#line 3531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str932, 0},
-#line 5740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str933, 0},
-#line 3512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str934, 0},
-#line 749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str935, 0},
-#line 1050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str936, 0},
-#line 2313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str937, 0},
-#line 1407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str938, 0},
-#line 3431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str939, 0},
-#line 3263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str940, 0},
-#line 229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str941, 0},
-#line 1790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str942, 0},
-#line 3276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str943, 0},
-#line 2668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str944, 0},
-#line 2144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str945, 0},
-#line 2650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str946, 0},
-#line 497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str947, 0},
-#line 1867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str948, 0},
-#line 292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str949, 0},
-#line 602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str950, 0},
-#line 1291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str951, 0},
-#line 818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str952, 1},
-#line 963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str953, 0},
-#line 1556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str954, 0},
-#line 2833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str955, 0},
-#line 2636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str956, 0},
-#line 661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str957, 0},
-#line 3410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str958, 0},
-#line 3295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str959, 0},
-#line 2662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str960, 0},
-#line 344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str961, 0},
-#line 611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str962, 0},
-#line 1669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str963, 0},
-#line 2987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str964, 0},
-#line 3570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str965, 0},
-#line 2647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str966, 0},
-#line 2646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str967, 0},
-#line 2634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str968, 0},
-#line 2635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str969, 0},
-#line 787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str970, 0},
-#line 1734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str971, 0},
-#line 180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str972, 0},
-#line 3270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str973, 0},
-#line 391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str974, 0},
-#line 2643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str975, 0},
-#line 3504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str976, 0},
-#line 3445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str977, 0},
-#line 2912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str978, 0},
-#line 773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str979, 0},
-#line 1513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str980, 0},
-#line 3594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str981, 0},
-#line 2623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str982, 0},
-#line 2622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str983, 0},
-#line 1675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str984, 0},
-#line 736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str985, 0},
-#line 464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str986, 0},
-#line 905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str987, 0},
-#line 2165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str988, 0},
-#line 744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str989, 0},
-#line 462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str990, 0},
-#line 1952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str991, 0},
-#line 2620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str992, 0},
-#line 755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str993, 0},
-#line 2002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str994, 0},
-#line 2641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str995, 0},
-#line 2901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str996, 0},
-#line 2642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str997, 0},
-#line 2661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str998, 0},
-#line 2621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str999, 0},
-#line 397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1000, 0},
-#line 183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1001, 0},
-#line 1467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1002, 0},
-#line 3054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1003, 0},
-#line 3278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1004, 0},
-#line 2761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1005, 0},
-#line 603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1006, 0},
-#line 2202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1007, 0},
-#line 3382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1008, 0},
-#line 3394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1009, 0},
-#line 181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1010, 0},
-#line 5923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1011, 4},
-#line 1430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1012, 0},
-#line 2329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1013, 0},
-#line 3047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1014, 0},
-#line 423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1015, 0},
-#line 1379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1016, 0},
-#line 2259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1017, 0},
-#line 817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1018, 1},
-#line 2012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1019, 0},
-#line 753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1020, 0},
-#line 1103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1021, 0},
-#line 1051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1022, 0},
-#line 2845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1023, 0},
-#line 341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1024, 0},
-#line 2660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1025, 0},
-#line 2701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1026, 0},
-#line 1915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1027, 0},
-#line 358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1028, 0},
-#line 2644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1029, 0},
-#line 1011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1030, 0},
-#line 1550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1031, 0},
-#line 2617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1032, 0},
-#line 1717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1033, 0},
-#line 148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1034, 0},
-#line 81 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1035, 0},
-#line 750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1036, 0},
-#line 197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1037, 0},
-#line 2667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1038, 0},
-#line 5941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1039, 0},
-#line 5937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1040, 0},
-#line 3283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1041, 0},
-#line 3327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1042, 0},
-#line 2382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1043, 0},
-#line 823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1044, 0},
-#line 1544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1045, 0},
-#line 359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1046, 0},
-#line 3399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1047, 0},
-#line 3397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1048, 0},
-#line 1376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1049, 0},
-#line 2992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1050, 0},
-#line 2757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1051, 0},
-#line 2153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1052, 0},
-#line 3523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1053, 0},
-#line 1485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1054, 0},
-#line 3651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1055, 0},
-#line 1558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1056, 0},
-#line 3552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1057, 0},
-#line 1833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1058, 0},
-#line 1397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1059, 0},
-#line 1256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1060, 0},
-#line 2293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1061, 0},
-#line 3625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1062, 0},
-#line 2669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1063, 0},
-#line 923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1064, 0},
-#line 334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1065, 0},
-#line 1408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1066, 0},
-#line 904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1067, 0},
-#line 383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1068, 0},
-#line 3673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1069, 0},
-#line 1619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1070, 0},
-#line 2312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1071, 0},
-#line 3396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1072, 0},
-#line 751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1073, 0},
-#line 2559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1074, 0},
-#line 3514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1075, 0},
-#line 2515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1076, 0},
-#line 2954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1077, 0},
-#line 3487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1078, 0},
-#line 2357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1079, 0},
-#line 5868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1080, 0},
-#line 3591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1081, 0},
-#line 3527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1082, 0},
-#line 463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1083, 0},
-#line 2061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1084, 0},
-#line 3525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1085, 0},
-#line 5866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1086, 0},
-#line 2638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1087, 0},
-#line 2477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1088, 4},
-#line 3062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1089, 2},
-#line 3513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1090, 0},
-#line 3333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1091, 0},
-#line 5875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1092, 0},
-#line 1045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1093, 0},
-#line 3497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1094, 0},
-#line 3405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1095, 0},
-#line 1808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1096, 0},
-#line 1811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1097, 0},
-#line 2077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1098, 0},
-#line 5770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1099, 0},
-#line 5854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1100, 0},
-#line 1052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1101, 0},
-#line 3404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1102, 0},
-#line 752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1103, 0},
-#line 3440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1104, 0},
-#line 3528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1105, 0},
-#line 5797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1106, 0},
-#line 1245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1107, 0},
-#line 2103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1108, 0},
-#line 754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1109, 0},
-#line 2248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1110, 0},
-#line 2332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1111, 0},
-#line 1083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1112, 0},
-#line 5723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1113, 0},
-#line 206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1114, 0},
-#line 2989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1115, 0},
-#line 3293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1116, 0},
-#line 1456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1117, 0},
-#line 2034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1118, 0},
-#line 203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1119, 0},
-#line 3565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1120, 0},
-#line 3412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1121, 0},
-#line 187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1122, 0},
-#line 2752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1123, 0},
-#line 532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1124, 4},
-#line 2993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1125, 0},
-#line 307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1126, 0},
-#line 3478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1127, 0},
-#line 2953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1128, 0},
-#line 1491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1129, 0},
-#line 2244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1130, 0},
-#line 5825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1131, 0},
-#line 1898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1132, 0},
-#line 1405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1133, 0},
-#line 1322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1134, 0},
-#line 3516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1135, 0},
-#line 994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1136, 0},
-#line 6383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1137, 0},
-#line 790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1138, 0},
-#line 3061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1139, 0},
-#line 1701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1140, 0},
-#line 1793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1141, 0},
-#line 469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1142, 0},
-#line 202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1143, 0},
-#line 5853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1144, 0},
-#line 1746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1145, 0},
-#line 3496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1146, 0},
-#line 492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1147, 0},
-#line 814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1148, 1},
-#line 5802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1149, 0},
-#line 5796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1150, 0},
-#line 2149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1151, 0},
-#line 1671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1152, 0},
-#line 361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1153, 0},
-#line 1377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1154, 0},
-#line 5818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1155, 0},
-#line 2066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1156, 0},
-#line 3515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1157, 0},
-#line 1417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1158, 0},
-#line 2755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1159, 0},
-#line 774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1160, 0},
-#line 5905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1161, 0},
-#line 2015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1162, 0},
-#line 1716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1163, 0},
-#line 2915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1164, 0},
-#line 3321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1165, 0},
-#line 1048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1166, 0},
-#line 3470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1167, 0},
-#line 2523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1168, 0},
-#line 2505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1169, 0},
-#line 236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1170, 0},
-#line 5771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1171, 0},
-#line 3572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1172, 0},
-#line 3314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1173, 0},
-#line 2798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1174, 0},
-#line 3260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1175, 0},
-#line 2926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1176, 0},
-#line 3457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1177, 0},
-#line 3679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1178, 4},
-#line 748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1179, 0},
-#line 5863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1180, 0},
-#line 1502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1181, 0},
-#line 321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1182, 0},
-#line 3518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1183, 0},
-#line 2972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1184, 0},
-#line 1656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1185, 0},
-#line 5805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1186, 0},
-#line 5851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1187, 0},
-#line 799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1188, 0},
-#line 2500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1189, 0},
-#line 461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1190, 0},
-#line 3367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1191, 0},
-#line 409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1192, 0},
-#line 3033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1193, 0},
-#line 235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1194, 0},
-#line 2102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1195, 0},
-#line 5816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1196, 0},
-#line 374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1197, 0},
-#line 3409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1198, 0},
-#line 822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1199, 0},
-#line 2003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1200, 0},
-#line 2848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1201, 0},
-#line 1629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1202, 0},
-#line 5809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1203, 0},
-#line 1720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1204, 0},
-#line 801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1205, 0},
-#line 2416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1206, 0},
-#line 3473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1207, 0},
-#line 2038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1208, 0},
-#line 633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1209, 0},
-#line 746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1210, 0},
-#line 134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1211, 0},
-#line 3479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1212, 0},
-#line 5848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1213, 0},
-#line 5739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1214, 0},
-#line 1680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1215, 0},
-#line 5947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1216, 4},
-#line 578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1217, 1},
-#line 580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1218, 0},
-#line 791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1219, 0},
-#line 240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1220, 0},
-#line 3051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1221, 0},
-#line 2844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1222, 0},
-#line 2854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1223, 0},
-#line 2707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1224, 0},
-#line 5857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1225, 0},
-#line 3055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1226, 0},
-#line 2946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1227, 0},
-#line 3038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1228, 0},
-#line 5810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1229, 0},
-#line 1755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1230, 0},
-#line 2744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1231, 0},
-#line 6364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1232, 0},
-#line 2063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1233, 0},
-#line 3488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1234, 0},
-#line 1370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1235, 0},
-#line 5803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1236, 0},
-#line 267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1237, 0},
-#line 3317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1238, 0},
-#line 3521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1239, 0},
-#line 785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1240, 0},
-#line 5840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1241, 0},
-#line 3451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1242, 0},
-#line 1545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1243, 0},
-#line 2671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1244, 0},
-#line 400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1245, 0},
-#line 5842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1246, 0},
-#line 897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1247, 0},
-#line 3615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1248, 0},
-#line 3393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1249, 0},
-#line 2872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1250, 0},
-#line 2968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1251, 0},
-#line 2413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1252, 0},
-#line 2417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1253, 0},
-#line 560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1254, 0},
-#line 1751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1255, 0},
-#line 2013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1256, 0},
-#line 5815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1257, 0},
-#line 5906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1258, 0},
-#line 3614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1259, 0},
-#line 1562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1260, 0},
-#line 5806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1261, 0},
-#line 1232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1262, 0},
-#line 5790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1263, 0},
-#line 174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1264, 0},
-#line 1563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1265, 0},
-#line 2104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1266, 0},
-#line 239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1267, 0},
-#line 775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1268, 0},
-#line 238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1269, 0},
-#line 246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1270, 0},
-#line 2551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1271, 0},
-#line 2827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1272, 0},
-#line 599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1273, 0},
-#line 440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1274, 0},
-#line 2294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1275, 0},
-#line 2672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1276, 0},
-#line 304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1277, 0},
-#line 2843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1278, 0},
-#line 3589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1279, 0},
-#line 144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1280, 0},
-#line 1443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1281, 0},
-#line 2072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1282, 0},
-#line 3511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1283, 0},
-#line 33 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1284, 0},
-#line 3555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1285, 0},
-#line 228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1286, 0},
-#line 2978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1287, 0},
-#line 2772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1288, 0},
-#line 5689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1289, 0},
-#line 4023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1290, 0},
-#line 4108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1291, 2},
-#line 821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1292, 0},
-#line 2146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1293, 0},
-#line 4077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1294, 0},
-#line 1918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1295, 0},
-#line 2511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1296, 0},
-#line 3654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1297, 0},
-#line 4045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1298, 0},
-#line 4016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1299, 0},
-#line 2879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1300, 0},
-#line 2160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1301, 0},
-#line 2303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1302, 0},
-#line 1445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1303, 0},
-#line 2869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1304, 0},
-#line 4091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1305, 0},
-#line 205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1306, 0},
-#line 3611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1307, 0},
-#line 5808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1308, 0},
-#line 3683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1309, 0},
-#line 1939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1310, 0},
-#line 4044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1311, 0},
-#line 3684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1312, 0},
-#line 2930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1313, 0},
-#line 3067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1314, 0},
-#line 3035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1315, 0},
-#line 901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1316, 0},
-#line 4082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1317, 0},
-#line 3815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1318, 0},
-#line 3835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1319, 0},
-#line 5720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1320, 0},
-#line 2923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1321, 0},
-#line 3861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1322, 0},
-#line 4037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1323, 0},
-#line 3909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1324, 0},
-#line 3860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1325, 0},
-#line 3930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1326, 0},
-#line 2533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1327, 0},
-#line 4046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1328, 0},
-#line 3925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1329, 0},
-#line 3892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1330, 0},
-#line 3924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1331, 0},
-#line 3495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1332, 0},
-#line 4646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1333, 0},
-#line 3060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1334, 0},
-#line 3890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1335, 0},
-#line 3005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1336, 0},
-#line 4070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1337, 0},
-#line 3889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1338, 0},
-#line 4019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1339, 1},
-#line 3891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1340, 0},
-#line 3929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1341, 0},
-#line 4064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1342, 0},
-#line 4035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1343, 0},
-#line 909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1344, 0},
-#line 3261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1345, 0},
-#line 3908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1346, 0},
-#line 3966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1347, 2},
-#line 3297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1348, 0},
-#line 820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1349, 0},
-#line 5710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1350, 0},
-#line 2929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1351, 0},
-#line 5776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1352, 0},
-#line 1068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1353, 0},
-#line 3483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1354, 0},
-#line 3532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1355, 0},
-#line 1458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1356, 0},
-#line 3593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1357, 0},
-#line 3863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1358, 0},
-#line 2790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1359, 0},
-#line 2080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1360, 0},
-#line 3937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1361, 0},
-#line 3934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1362, 0},
-#line 3935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1363, 0},
-#line 2732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1364, 0},
-#line 2909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1365, 0},
-#line 3923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1366, 0},
-#line 3926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1367, 0},
-#line 465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1368, 0},
-#line 2069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1369, 0},
-#line 3888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1370, 0},
-#line 237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1371, 0},
-#line 1487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1372, 0},
-#line 2044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1373, 0},
-#line 3852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1374, 0},
-#line 3851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1375, 0},
-#line 3850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1376, 0},
-#line 3940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1377, 0},
-#line 3849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1378, 0},
-#line 380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1379, 0},
-#line 3847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1380, 0},
-#line 4090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1381, 0},
-#line 3878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1382, 0},
-#line 3880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1383, 0},
-#line 3879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1384, 0},
-#line 614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1385, 0},
-#line 3876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1386, 0},
-#line 3474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1387, 0},
-#line 2336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1388, 0},
-#line 1785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1389, 0},
-#line 1314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1390, 0},
-#line 1792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1391, 0},
-#line 984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1392, 0},
-#line 3268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1393, 0},
-#line 2001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1394, 0},
-#line 635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1395, 0},
-#line 452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1396, 0},
-#line 2680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1397, 0},
-#line 3922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1398, 0},
-#line 5900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1399, 0},
-#line 3016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1400, 0},
-#line 5877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1401, 0},
-#line 3920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1402, 0},
-#line 314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1403, 0},
-#line 2792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1404, 0},
-#line 2362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1405, 0},
-#line 3509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1406, 0},
-#line 2788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1407, 0},
-#line 2023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1408, 0},
-#line 4106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1409, 0},
-#line 3635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1410, 0},
-#line 3685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1411, 0},
-#line 3475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1412, 0},
-#line 2133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1413, 0},
-#line 3846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1414, 0},
-#line 3882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1415, 0},
-#line 5936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1416, 0},
-#line 3950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1417, 0},
-#line 3742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1418, 0},
-#line 3471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1419, 0},
-#line 5814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1420, 0},
-#line 3899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1421, 0},
-#line 2771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1422, 0},
-#line 3875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1423, 0},
-#line 5683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1424, 0},
-#line 4076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1425, 2},
-#line 3780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1426, 0},
-#line 3753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1427, 0},
-#line 3825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1428, 1},
-#line 3939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1429, 0},
-#line 3897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1430, 0},
-#line 3881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1431, 0},
-#line 5871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1432, 0},
-#line 3762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1433, 0},
-#line 3746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1434, 0},
-#line 2895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1435, 0},
-#line 385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1436, 0},
-#line 3898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1437, 0},
-#line 3896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1438, 0},
-#line 234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1439, 0},
-#line 3749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1440, 0},
-#line 4043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1441, 0},
-#line 4042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1442, 0},
-#line 3012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1443, 0},
-#line 3843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1444, 0},
-#line 3836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1445, 0},
-#line 5911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1446, 0},
-#line 3747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1447, 0},
-#line 4033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1448, 0},
-#line 1496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1449, 0},
-#line 3752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1450, 0},
-#line 3895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1451, 0},
-#line 245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1452, 0},
-#line 2828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1453, 0},
-#line 3917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1454, 0},
-#line 3916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1455, 0},
-#line 3841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1456, 0},
-#line 3309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1457, 0},
-#line 3837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1458, 0},
-#line 3743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1459, 0},
-#line 4041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1460, 0},
-#line 3913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1461, 0},
-#line 2545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1462, 0},
-#line 2880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1463, 0},
-#line 403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1464, 0},
-#line 3914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1465, 0},
-#line 3943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1466, 0},
-#line 3911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1467, 0},
-#line 2874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1468, 0},
-#line 3014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1469, 0},
-#line 4072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1470, 0},
-#line 3903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1471, 0},
-#line 3944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1472, 0},
-#line 421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1473, 0},
-#line 2558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1474, 0},
-#line 1665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1475, 0},
-#line 1729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1476, 0},
-#line 1361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1477, 0},
-#line 2938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1478, 0},
-#line 740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1479, 0},
-#line 3023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1480, 0},
-#line 1858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1481, 0},
-#line 2883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1482, 0},
-#line 1281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1483, 0},
-#line 3906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1484, 0},
-#line 951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1485, 0},
-#line 3910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1486, 0},
-#line 2194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1487, 0},
-#line 2842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1488, 0},
-#line 98 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1489, 0},
-#line 6238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1490, 0},
-#line 172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1491, 0},
-#line 1730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1492, 0},
-#line 1994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1493, 0},
-#line 446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1494, 0},
-#line 5849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1495, 0},
-#line 2081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1496, 0},
-#line 3748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1497, 0},
-#line 1280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1498, 0},
-#line 1854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1499, 0},
-#line 950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1500, 0},
-#line 1276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1501, 0},
-#line 1560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1502, 0},
-#line 3970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1503, 1},
-#line 946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1504, 0},
-#line 5755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1505, 0},
-#line 2841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1506, 0},
-#line 2175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1507, 0},
-#line 5946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1508, 0},
-#line 5718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1509, 0},
-#line 2689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1510, 0},
-#line 111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1511, 0},
-#line 211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1512, 0},
-#line 3840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1513, 0},
-#line 3969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1514, 1},
-#line 3968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1515, 0},
-#line 3872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1516, 0},
-#line 4012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1517, 0},
-#line 2374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1518, 0},
-#line 3264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1519, 0},
-#line 3744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1520, 0},
-#line 4068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1521, 0},
-#line 345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1522, 0},
-#line 3606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1523, 0},
-#line 4010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1524, 0},
-#line 2062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1525, 0},
-#line 3503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1526, 0},
-#line 4039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1527, 0},
-#line 2980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1528, 0},
-#line 3945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1529, 0},
-#line 2961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1530, 0},
-#line 2657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1531, 0},
-#line 1731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1532, 0},
-#line 2986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1533, 0},
-#line 2677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1534, 0},
-#line 4053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1535, 0},
-#line 106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1536, 0},
-#line 3384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1537, 0},
-#line 3294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1538, 0},
-#line 3491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1539, 0},
-#line 3709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1540, 0},
-#line 1856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1541, 0},
-#line 5812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1542, 0},
-#line 1278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1543, 0},
-#line 2150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1544, 0},
-#line 948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1545, 0},
-#line 2652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1546, 0},
-#line 4741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1547, 0},
-#line 3510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1548, 0},
-#line 1742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1549, 0},
-#line 5684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1550, 0},
-#line 4764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1551, 0},
-#line 2683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1552, 0},
-#line 3831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1553, 0},
-#line 5841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1554, 0},
-#line 1416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1555, 0},
-#line 3838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1556, 0},
-#line 4766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1557, 0},
-#line 3778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1558, 0},
-#line 3608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1559, 0},
-#line 2882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1560, 0},
-#line 3806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1561, 0},
-#line 780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1562, 0},
-#line 2769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1563, 0},
-#line 4652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1564, 0},
-#line 2746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1565, 0},
-#line 4653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1566, 0},
-#line 522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1567, 4},
-#line 3476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1568, 0},
-#line 3506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1569, 0},
-#line 5719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1570, 0},
-#line 2555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1571, 0},
-#line 3833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1572, 0},
-#line 3003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1573, 0},
-#line 4675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1574, 0},
-#line 138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1575, 0},
-#line 302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1576, 0},
-#line 4696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1577, 0},
-#line 4748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1578, 0},
-#line 2196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1579, 0},
-#line 2858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1580, 0},
-#line 813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1581, 1},
-#line 4710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1582, 0},
-#line 4769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1583, 0},
-#line 4055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1584, 0},
-#line 3010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1585, 0},
-#line 2204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1586, 4},
-#line 5762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1587, 0},
-#line 5787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1588, 0},
-#line 2899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1589, 0},
-#line 539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1590, 4},
-#line 4054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1591, 0},
-#line 1667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1592, 0},
-#line 4687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1593, 0},
-#line 283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1594, 0},
-#line 3779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1595, 0},
-#line 3884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1596, 0},
-#line 521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1597, 4},
-#line 2529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1598, 0},
-#line 5757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1599, 0},
-#line 2035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1600, 0},
-#line 2139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1601, 0},
-#line 105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1602, 0},
-#line 1459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1603, 0},
-#line 770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1604, 0},
-#line 4702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1605, 0},
-#line 783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1606, 0},
-#line 2017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1607, 0},
-#line 3578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1608, 0},
-#line 4699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1609, 0},
-#line 1381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1610, 0},
-#line 5820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1611, 0},
-#line 5933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1612, 0},
-#line 4066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1613, 0},
-#line 2509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1614, 0},
-#line 3645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1615, 0},
-#line 4056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1616, 0},
-#line 3432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1617, 0},
-#line 355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1618, 0},
-#line 2223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1619, 0},
-#line 95 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1620, 0},
-#line 2045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1621, 0},
-#line 2706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1622, 0},
-#line 5934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1623, 0},
-#line 5817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1624, 0},
-#line 2117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1625, 0},
-#line 5831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1626, 0},
-#line 2994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1627, 0},
-#line 5759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1628, 0},
-#line 2725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1629, 0},
-#line 4071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1630, 0},
-#line 3020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1631, 0},
-#line 2884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1632, 0},
-#line 3308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1633, 0},
-#line 3413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1634, 0},
-#line 2148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1635, 0},
-#line 4684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1636, 0},
-#line 1657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1637, 0},
-#line 2853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1638, 0},
-#line 2363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1639, 0},
-#line 3763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1640, 0},
-#line 5821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1641, 0},
-#line 5717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1642, 0},
-#line 1741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1643, 0},
-#line 3052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1644, 0},
-#line 760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1645, 0},
-#line 3921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1646, 0},
-#line 3823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1647, 0},
-#line 2563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1648, 0},
-#line 3011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1649, 0},
-#line 2710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1650, 0},
-#line 2832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1651, 0},
-#line 2959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1652, 0},
-#line 4047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1653, 0},
-#line 3043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1654, 0},
-#line 3948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1655, 0},
-#line 4747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1656, 0},
-#line 2027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1657, 0},
-#line 3947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1658, 0},
-#line 3658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1659, 0},
-#line 4681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1660, 0},
-#line 4775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1661, 0},
-#line 3927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1662, 0},
-#line 4723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1663, 0},
-#line 2425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1664, 4},
-#line 2709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1665, 0},
-#line 3802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1666, 0},
-#line 3017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1667, 0},
-#line 6368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1668, 0},
-#line 2225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1669, 0},
-#line 1794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1670, 0},
-#line 5859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1671, 0},
-#line 2942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1672, 0},
-#line 557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1673, 0},
-#line 3045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1674, 0},
-#line 3907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1675, 0},
-#line 737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1676, 0},
-#line 3828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1677, 0},
-#line 2775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1678, 0},
-#line 130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1679, 0},
-#line 3864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1680, 0},
-#line 2875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1681, 0},
-#line 4695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1682, 0},
-#line 5750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1683, 0},
-#line 2197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1684, 0},
-#line 1555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1685, 0},
-#line 3677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1686, 0},
-#line 2260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1687, 0},
-#line 5888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1688, 0},
-#line 2684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1689, 0},
-#line 812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1690, 0},
-#line 1588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1691, 4},
-#line 2748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1692, 0},
-#line 1649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1693, 0},
-#line 4040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1694, 0},
-#line 3901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1695, 0},
-#line 2795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1696, 0},
-#line 2778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1697, 0},
-#line 1652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1698, 0},
-#line 2760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1699, 0},
-#line 4781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1700, 0},
-#line 6367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1701, 0},
-#line 4073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1702, 0},
-#line 4701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1703, 0},
-#line 4032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1704, 0},
-#line 2016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1705, 0},
-#line 2132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1706, 0},
-#line 3693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1707, 0},
-#line 5836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1708, 0},
-#line 2794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1709, 0},
-#line 5891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1710, 0},
-#line 3599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1711, 0},
-#line 1486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1712, 0},
-#line 5153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1713, 0},
-#line 4941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1714, 0},
-#line 5308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1715, 0},
-#line 4034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1716, 0},
-#line 3844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1717, 0},
-#line 5206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1718, 0},
-#line 215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1719, 0},
-#line 4051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1720, 0},
-#line 5135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1721, 0},
-#line 4700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1722, 0},
-#line 2289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1723, 0},
-#line 3048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1724, 0},
-#line 3596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1725, 0},
-#line 5256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1726, 0},
-#line 3330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1727, 0},
-#line 5165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1728, 0},
-#line 1632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1729, 0},
-#line 2130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1730, 0},
-#line 2059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1731, 0},
-#line 4782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1732, 0},
-#line 1638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1733, 0},
-#line 5146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1734, 0},
-#line 5828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1735, 0},
-#line 4783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1736, 0},
-#line 4683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1737, 0},
-#line 4705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1738, 0},
-#line 4716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1739, 0},
-#line 3600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1740, 0},
-#line 4944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1741, 0},
-#line 3517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1742, 0},
-#line 3839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1743, 0},
-#line 3530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1744, 0},
-#line 5212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1745, 0},
-#line 4659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1746, 0},
-#line 3486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1747, 0},
-#line 1633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1748, 0},
-#line 2837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1749, 0},
-#line 5850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1750, 0},
-#line 5138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1751, 0},
-#line 2129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1752, 0},
-#line 4903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1753, 0},
-#line 5867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1754, 0},
-#line 800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1755, 0},
-#line 4647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1756, 0},
-#line 102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1757, 0},
-#line 5102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1758, 0},
-#line 5209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1759, 0},
-#line 2450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1760, 4},
-#line 2866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1761, 0},
-#line 5756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1762, 0},
-#line 1404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1763, 0},
-#line 2826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1764, 0},
-#line 3605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1765, 0},
-#line 4697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1766, 0},
-#line 4725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1767, 0},
-#line 2704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1768, 0},
-#line 5303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1769, 0},
-#line 3710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1770, 2},
-#line 2010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1771, 0},
-#line 5170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1772, 0},
-#line 5889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1773, 0},
-#line 5148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1774, 0},
-#line 5142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1775, 0},
-#line 5151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1776, 0},
-#line 2665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1777, 0},
-#line 5140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1778, 0},
-#line 4095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1779, 0},
-#line 3822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1780, 4},
-#line 2550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1781, 0},
-#line 3700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1782, 0},
-#line 2918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1783, 0},
-#line 4059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1784, 0},
-#line 5248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1785, 0},
-#line 4803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1786, 0},
-#line 2891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1787, 0},
-#line 5188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1788, 0},
-#line 3408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1789, 0},
-#line 3826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1790, 0},
-#line 4959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1791, 0},
-#line 3059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1792, 0},
-#line 2933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1793, 0},
-#line 3621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1794, 0},
-#line 5291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1795, 0},
-#line 1639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1796, 0},
-#line 3740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1797, 0},
-#line 1362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1798, 0},
-#line 4057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1799, 0},
-#line 5157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1800, 0},
-#line 2484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1801, 4},
-#line 2065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1802, 0},
-#line 5764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1803, 0},
-#line 5682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1804, 0},
-#line 3883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1805, 0},
-#line 5172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1806, 0},
-#line 4773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1807, 0},
-#line 2889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1808, 0},
-#line 5190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1809, 0},
-#line 3032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1810, 0},
-#line 4015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1811, 0},
-#line 3697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1812, 0},
-#line 27 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1813, 4},
-#line 2121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1814, 0},
-#line 4815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1815, 0},
-#line 2721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1816, 0},
-#line 3712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1817, 0},
-#line 116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1818, 0},
-#line 1644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1819, 0},
-#line 4096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1820, 0},
-#line 5693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1821, 0},
-#line 2964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1822, 0},
-#line 6373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1823, 0},
-#line 2784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1824, 0},
-#line 1662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1825, 0},
-#line 4771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1826, 0},
-#line 4719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1827, 0},
-#line 5921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1828, 4},
-#line 4998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1829, 0},
-#line 2108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1830, 0},
-#line 4737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1831, 0},
-#line 4970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1832, 4},
-#line 4855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1833, 0},
-#line 3301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1834, 0},
-#line 1661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1835, 0},
-#line 5173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1836, 0},
-#line 1382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1837, 0},
-#line 456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1838, 0},
-#line 5090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1839, 0},
-#line 5115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1840, 0},
-#line 5139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1841, 0},
-#line 5893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1842, 0},
-#line 3829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1843, 0},
-#line 2714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1844, 0},
-#line 2974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1845, 0},
-#line 2963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1846, 0},
-#line 2682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1847, 0},
-#line 4662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1848, 0},
-#line 2766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1849, 0},
-#line 5211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1850, 0},
-#line 5177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1851, 0},
-#line 2105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1852, 0},
-#line 3657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1853, 0},
-#line 2120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1854, 0},
-#line 4756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1855, 0},
-#line 5116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1856, 0},
-#line 4752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1857, 0},
-#line 2675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1858, 0},
-#line 3042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1859, 0},
-#line 3698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1860, 0},
-#line 4063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1861, 0},
-#line 4754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1862, 0},
-#line 5267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1863, 0},
-#line 2971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1864, 0},
-#line 5113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1865, 0},
-#line 5108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1866, 0},
-#line 2896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1867, 0},
-#line 3798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1868, 0},
-#line 3406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1869, 0},
-#line 3928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1870, 0},
-#line 2126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1871, 0},
-#line 4962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1872, 4},
-#line 3030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1873, 0},
-#line 3022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1874, 0},
-#line 2738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1875, 0},
-#line 5747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1876, 0},
-#line 5306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1877, 0},
-#line 4811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1878, 0},
-#line 3694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1879, 0},
-#line 5781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1880, 0},
-#line 3644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1881, 0},
-#line 2112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1882, 0},
-#line 4768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1883, 0},
-#line 4964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1884, 4},
-#line 4028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1885, 0},
-#line 5276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1886, 0},
-#line 5282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1887, 0},
-#line 2632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1888, 0},
-#line 3933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1889, 0},
-#line 4949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1890, 0},
-#line 360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1891, 0},
-#line 2786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1892, 0},
-#line 3832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1893, 0},
-#line 5163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1894, 0},
-#line 2717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1895, 0},
-#line 3848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1896, 0},
-#line 5293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1897, 0},
-#line 3305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1898, 0},
-#line 5249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1899, 0},
-#line 5241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1900, 0},
-#line 2702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1901, 0},
-#line 3772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1902, 0},
-#line 2699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1903, 0},
-#line 4061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1904, 0},
-#line 3797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1905, 0},
-#line 1428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1906, 0},
-#line 4899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1907, 0},
-#line 5187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1908, 0},
-#line 1387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1909, 0},
-#line 1791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1910, 0},
-#line 4006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1911, 0},
-#line 2928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1912, 0},
-#line 5176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1913, 0},
-#line 4727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1914, 0},
-#line 4896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1915, 0},
-#line 3974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1916, 0},
-#line 157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1917, 0},
-#line 1953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1918, 0},
-#line 210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1919, 0},
-#line 3325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1920, 0},
-#line 4990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1921, 0},
-#line 186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1922, 0},
-#line 3610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1923, 0},
-#line 5106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1924, 0},
-#line 2124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1925, 0},
-#line 2135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1926, 0},
-#line 1737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1927, 0},
-#line 2507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1928, 0},
-#line 4779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1929, 0},
-#line 2113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1930, 0},
-#line 2284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1931, 0},
-#line 2525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1932, 0},
-#line 3786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1933, 0},
-#line 1640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1934, 0},
-#line 3745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1935, 0},
-#line 3977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1936, 0},
-#line 4751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1937, 0},
-#line 3318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1938, 0},
-#line 2893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1939, 0},
-#line 5704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1940, 0},
-#line 1446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1941, 0},
-#line 2101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1942, 0},
-#line 3028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1943, 0},
-#line 5132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1944, 0},
-#line 3912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1945, 0},
-#line 3286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1946, 0},
-#line 4890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1947, 0},
-#line 1634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1948, 0},
-#line 6241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1949, 0},
-#line 6242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1950, 0},
-#line 4880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1951, 0},
-#line 2773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1952, 0},
-#line 6305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1953, 2},
-#line 5238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1954, 0},
-#line 5119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1955, 0},
-#line 3985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1956, 0},
-#line 2692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1957, 0},
-#line 6341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1958, 0},
-#line 5124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1959, 0},
-#line 1636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1960, 0},
-#line 5752 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1961, 0},
-#line 1757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1962, 4},
-#line 3505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1963, 0},
-#line 1855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1964, 0},
-#line 1277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1965, 0},
-#line 173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1966, 0},
-#line 2305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1967, 0},
-#line 947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1968, 0},
-#line 5205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1969, 0},
-#line 2719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1970, 0},
-#line 2852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1971, 4},
-#line 4718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1972, 0},
-#line 4711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1973, 0},
-#line 4692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1974, 0},
-#line 2686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1975, 0},
-#line 5258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1976, 0},
-#line 5111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1977, 0},
-#line 2711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1978, 0},
-#line 2231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1979, 0},
-#line 4882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1980, 0},
-#line 1693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1981, 0},
-#line 2679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1982, 0},
-#line 5715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1983, 0},
-#line 1449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1984, 0},
-#line 4852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1985, 0},
-#line 2688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1986, 0},
-#line 1635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1987, 0},
-#line 4026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1988, 0},
-#line 3015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1989, 0},
-#line 5215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1990, 0},
-#line 1641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1991, 0},
-#line 2019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1992, 0},
-#line 4722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1993, 0},
-#line 1098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1994, 0},
-#line 3805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1995, 0},
-#line 2512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1996, 0},
-#line 3699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1997, 0},
-#line 4050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1998, 0},
-#line 2728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str1999, 0},
-#line 140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2000, 0},
-#line 5255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2001, 0},
-#line 1673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2002, 0},
-#line 5121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2003, 0},
-#line 769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2004, 0},
-#line 100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2005, 0},
-#line 3044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2006, 0},
-#line 5832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2007, 0},
-#line 2136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2008, 0},
-#line 2950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2009, 0},
-#line 2138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2010, 0},
-#line 220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2011, 0},
-#line 1470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2012, 0},
-#line 2156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2013, 0},
-#line 3702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2014, 0},
-#line 5823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2015, 0},
-#line 3711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2016, 0},
-#line 3303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2017, 0},
-#line 2890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2018, 0},
-#line 3007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2019, 0},
-#line 6369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2020, 0},
-#line 3696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2021, 0},
-#line 609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2022, 0},
-#line 1698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2023, 0},
-#line 3799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2024, 0},
-#line 3708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2025, 0},
-#line 2290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2026, 0},
-#line 5273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2027, 0},
-#line 3827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2028, 0},
-#line 1677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2029, 0},
-#line 4995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2030, 0},
-#line 5152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2031, 0},
-#line 4866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2032, 0},
-#line 3785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2033, 0},
-#line 3988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2034, 0},
-#line 5824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2035, 0},
-#line 5272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2036, 0},
-#line 2151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2037, 0},
-#line 567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2038, 0},
-#line 5262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2039, 0},
-#line 1110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2040, 0},
-#line 3526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2041, 0},
-#line 5105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2042, 0},
-#line 4824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2043, 0},
-#line 1672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2044, 0},
-#line 5940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2045, 0},
-#line 2758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2046, 0},
-#line 4965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2047, 4},
-#line 1946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2048, 0},
-#line 5200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2049, 0},
-#line 288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2050, 0},
-#line 5699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2051, 0},
-#line 2865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2052, 0},
-#line 219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2053, 0},
-#line 2026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2054, 0},
-#line 5131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2055, 0},
-#line 4864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2056, 0},
-#line 5904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2057, 0},
-#line 3554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2058, 0},
-#line 2190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2059, 0},
-#line 1643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2060, 0},
-#line 5150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2061, 0},
-#line 4686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2062, 0},
-#line 559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2063, 0},
-#line 1721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2064, 0},
-#line 3006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2065, 0},
-#line 5295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2066, 0},
-#line 1947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2067, 0},
-#line 3026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2068, 0},
-#line 301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2069, 0},
-#line 5225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2070, 0},
-#line 1655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2071, 0},
-#line 4994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2072, 0},
-#line 2091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2073, 0},
-#line 638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2074, 0},
-#line 4685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2075, 0},
-#line 158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2076, 0},
-#line 2793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2077, 0},
-#line 3704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2078, 0},
-#line 22 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2079, 0},
-#line 3433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2080, 0},
-#line 3706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2081, 0},
-#line 3037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2082, 0},
-#line 214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2083, 0},
-#line 3414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2084, 0},
-#line 3737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2085, 0},
-#line 2161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2086, 0},
-#line 1522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2087, 0},
-#line 1538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2088, 0},
-#line 5885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2089, 0},
-#line 2859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2090, 0},
-#line 4974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2091, 2},
-#line 2687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2092, 0},
-#line 3695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2093, 0},
-#line 242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2094, 0},
-#line 4867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2095, 0},
-#line 165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2096, 0},
-#line 784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2097, 0},
-#line 2249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2098, 0},
-#line 2736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2099, 0},
-#line 3058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2100, 0},
-#line 5235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2101, 0},
-#line 2119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2102, 0},
-#line 2155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2103, 0},
-#line 1565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2104, 0},
-#line 1942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2105, 0},
-#line 4816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2106, 0},
-#line 2088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2107, 0},
-#line 4865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2108, 0},
-#line 3468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2109, 0},
-#line 797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2110, 0},
-#line 907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2111, 0},
-#line 4081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2112, 0},
-#line 280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2113, 0},
-#line 3624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2114, 0},
-#line 110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2115, 0},
-#line 3338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2116, 0},
-#line 4105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2117, 0},
-#line 3553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2118, 0},
-#line 4876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2119, 0},
-#line 2633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2120, 0},
-#line 2246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2121, 0},
-#line 2068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2122, 0},
-#line 1991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2123, 0},
-#line 3796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2124, 0},
-#line 29 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2125, 4},
-#line 2887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2126, 0},
-#line 621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2127, 0},
-#line 1506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2128, 0},
-#line 3477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2129, 0},
-#line 371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2130, 4},
-#line 2115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2131, 0},
-#line 1990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2132, 0},
-#line 3783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2133, 0},
-#line 2036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2134, 0},
-#line 2694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2135, 0},
-#line 3978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2136, 0},
-#line 3002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2137, 0},
-#line 5677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2138, 0},
-#line 2900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2139, 0},
-#line 1091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2140, 1},
-#line 83 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2141, 0},
-#line 3707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2142, 0},
-#line 4660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2143, 0},
-#line 493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2144, 0},
-#line 200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2145, 0},
-#line 2726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2146, 0},
-#line 6343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2147, 0},
-#line 5158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2148, 0},
-#line 2111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2149, 0},
-#line 398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2150, 0},
-#line 2539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2151, 0},
-#line 6365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2152, 0},
-#line 5193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2153, 0},
-#line 3904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2154, 0},
-#line 4777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2155, 0},
-#line 5194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2156, 0},
-#line 1694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2157, 4},
-#line 5239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2158, 0},
-#line 393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2159, 0},
-#line 4693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2160, 0},
-#line 1518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2161, 0},
-#line 3705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2162, 0},
-#line 4987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2163, 0},
-#line 2212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2164, 4},
-#line 5280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2165, 0},
-#line 1553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2166, 0},
-#line 3588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2167, 0},
-#line 2868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2168, 0},
-#line 1743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2169, 0},
-#line 1647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2170, 0},
-#line 4823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2171, 0},
-#line 1989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2172, 0},
-#line 3652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2173, 0},
-#line 2176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2174, 0},
-#line 3585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2175, 0},
-#line 2776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2176, 0},
-#line 2546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2177, 0},
-#line 2056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2178, 0},
-#line 3508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2179, 0},
-#line 2131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2180, 0},
-#line 5093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2181, 0},
-#line 1622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2182, 0},
-#line 5751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2183, 0},
-#line 2292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2184, 0},
-#line 141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2185, 0},
-#line 4698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2186, 0},
-#line 406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2187, 0},
-#line 5301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2188, 0},
-#line 6248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2189, 0},
-#line 3961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2190, 0},
-#line 5247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2191, 0},
-#line 2230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2192, 0},
-#line 5861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2193, 0},
-#line 4753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2194, 0},
-#line 6288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2195, 0},
-#line 5886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2196, 0},
-#line 2969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2197, 0},
-#line 3701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2198, 0},
-#line 384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2199, 0},
-#line 3377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2200, 0},
-#line 2029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2201, 0},
-#line 5938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2202, 0},
-#line 4724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2203, 0},
-#line 2863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2204, 0},
-#line 3480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2205, 0},
-#line 3962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2206, 0},
-#line 3869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2207, 0},
-#line 5191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2208, 0},
-#line 3998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2209, 0},
-#line 2097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2210, 0},
-#line 3729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2211, 0},
-#line 4877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2212, 0},
-#line 5137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2213, 0},
-#line 3311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2214, 0},
-#line 2720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2215, 0},
-#line 5898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2216, 0},
-#line 3868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2217, 0},
-#line 3650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2218, 0},
-#line 2337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2219, 0},
-#line 3865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2220, 0},
-#line 291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2221, 0},
-#line 3329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2222, 0},
-#line 3492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2223, 0},
-#line 1762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2224, 0},
-#line 5097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2225, 0},
-#line 3759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2226, 0},
-#line 4848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2227, 0},
-#line 3324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2228, 0},
-#line 5284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2229, 0},
-#line 1692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2230, 0},
-#line 2032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2231, 0},
-#line 2232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2232, 0},
-#line 3029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2233, 0},
-#line 1546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2234, 0},
-#line 2750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2235, 0},
-#line 1364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2236, 0},
-#line 5312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2237, 0},
-#line 2976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2238, 0},
-#line 4883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2239, 0},
-#line 1697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2240, 0},
-#line 163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2241, 0},
-#line 3489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2242, 0},
-#line 5231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2243, 0},
-#line 4097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2244, 0},
-#line 117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2245, 0},
-#line 5216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2246, 0},
-#line 6326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2247, 0},
-#line 2735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2248, 0},
-#line 5680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2249, 0},
-#line 3040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2250, 0},
-#line 2703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2251, 0},
-#line 3273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2252, 0},
-#line 5195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2253, 0},
-#line 2967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2254, 0},
-#line 4669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2255, 0},
-#line 3964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2256, 0},
-#line 4000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2257, 0},
-#line 2940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2258, 0},
-#line 3049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2259, 0},
-#line 5053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2260, 0},
-#line 2058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2261, 0},
-#line 2767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2262, 0},
-#line 3411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2263, 0},
-#line 119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2264, 0},
-#line 5061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2265, 0},
-#line 2834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2266, 0},
-#line 1919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2267, 0},
-#line 1343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2268, 0},
-#line 1017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2269, 0},
-#line 5269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2270, 0},
-#line 6273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2271, 0},
-#line 1881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2272, 0},
-#line 362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2273, 0},
-#line 1304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2274, 0},
-#line 290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2275, 0},
-#line 975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2276, 0},
-#line 2697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2277, 0},
-#line 1350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2278, 0},
-#line 1026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2279, 0},
-#line 4109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2280, 0},
-#line 5180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2281, 0},
-#line 4110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2282, 0},
-#line 6280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2283, 0},
-#line 5236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2284, 0},
-#line 3008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2285, 0},
-#line 5254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2286, 0},
-#line 782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2287, 0},
-#line 1985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2288, 0},
-#line 4728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2289, 0},
-#line 2046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2290, 0},
-#line 4257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2291, 0},
-#line 4285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2292, 0},
-#line 4341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2293, 0},
-#line 2730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2294, 0},
-#line 4284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2295, 0},
-#line 5288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2296, 0},
-#line 4366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2297, 0},
-#line 4355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2298, 0},
-#line 4354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2299, 0},
-#line 2999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2300, 0},
-#line 1839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2301, 0},
-#line 4666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2302, 0},
-#line 4359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2303, 0},
-#line 4322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2304, 0},
-#line 4321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2305, 0},
-#line 4358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2306, 0},
-#line 930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2307, 0},
-#line 3728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2308, 0},
-#line 2557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2309, 0},
-#line 4067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2310, 0},
-#line 4319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2311, 0},
-#line 4318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2312, 0},
-#line 5166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2313, 0},
-#line 4320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2314, 0},
-#line 4364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2315, 0},
-#line 146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2316, 0},
-#line 5077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2317, 0},
-#line 4361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2318, 0},
-#line 2331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2319, 0},
-#line 5083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2320, 0},
-#line 4129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2321, 0},
-#line 34 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2322, 0},
-#line 4340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2323, 0},
-#line 2549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2324, 0},
-#line 6261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2325, 0},
-#line 2934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2326, 0},
-#line 4167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2327, 0},
-#line 2888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2328, 0},
-#line 3070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2329, 0},
-#line 4290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2330, 0},
-#line 5168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2331, 0},
-#line 4287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2332, 0},
-#line 3716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2333, 0},
-#line 4374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2334, 0},
-#line 4371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2335, 0},
-#line 4372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2336, 0},
-#line 4357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2337, 0},
-#line 4360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2338, 0},
-#line 2785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2339, 0},
-#line 276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2340, 0},
-#line 5833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2341, 0},
-#line 4317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2342, 0},
-#line 4288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2343, 0},
-#line 6295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2344, 0},
-#line 4199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2345, 0},
-#line 2561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2346, 0},
-#line 1893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2347, 0},
-#line 1315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2348, 0},
-#line 4275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2349, 0},
-#line 4274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2350, 0},
-#line 4273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2351, 0},
-#line 6348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2352, 0},
-#line 4377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2353, 0},
-#line 985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2354, 0},
-#line 4272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2355, 0},
-#line 4993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2356, 0},
-#line 289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2357, 0},
-#line 4221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2358, 0},
-#line 1813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2359, 0},
-#line 4269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2360, 0},
-#line 4305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2361, 0},
-#line 4307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2362, 0},
-#line 4306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2363, 0},
-#line 4407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2364, 0},
-#line 4303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2365, 0},
-#line 2308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2366, 0},
-#line 4338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2367, 0},
-#line 4002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2368, 0},
-#line 1493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2369, 0},
-#line 6284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2370, 0},
-#line 2939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2371, 0},
-#line 4353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2372, 0},
-#line 3272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2373, 0},
-#line 1658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2374, 0},
-#line 3607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2375, 0},
-#line 4065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2376, 0},
-#line 2217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2377, 0},
-#line 3073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2378, 0},
-#line 4268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2379, 0},
-#line 1905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2380, 0},
-#line 4309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2381, 0},
-#line 1329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2382, 0},
-#line 1000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2383, 0},
-#line 758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2384, 0},
-#line 6299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2385, 0},
-#line 5285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2386, 0},
-#line 4331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2387, 0},
-#line 3713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2388, 0},
-#line 3715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2389, 0},
-#line 3352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2390, 0},
-#line 4825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2391, 0},
-#line 4376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2392, 0},
-#line 4328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2393, 0},
-#line 6372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2394, 0},
-#line 2137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2395, 0},
-#line 363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2396, 0},
-#line 333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2397, 0},
-#line 4308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2398, 0},
-#line 5259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2399, 0},
-#line 3647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2400, 0},
-#line 4329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2401, 0},
-#line 4326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2402, 0},
-#line 4440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2403, 0},
-#line 147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2404, 0},
-#line 4052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2405, 0},
-#line 4277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2406, 0},
-#line 120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2407, 0},
-#line 4389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2408, 0},
-#line 4265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2409, 0},
-#line 4258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2410, 0},
-#line 5201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2411, 0},
-#line 2676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2412, 0},
-#line 4271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2413, 0},
-#line 4325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2414, 0},
-#line 4350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2415, 0},
-#line 4349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2416, 0},
-#line 4263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2417, 0},
-#line 906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2418, 0},
-#line 4259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2419, 0},
-#line 1642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2420, 0},
-#line 4346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2421, 0},
-#line 3681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2422, 0},
-#line 5033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2423, 0},
-#line 273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2424, 0},
-#line 352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2425, 0},
-#line 4347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2426, 0},
-#line 2510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2427, 0},
-#line 4381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2428, 0},
-#line 4343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2429, 0},
-#line 3669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2430, 0},
-#line 2678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2431, 0},
-#line 6290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2432, 0},
-#line 4335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2433, 0},
-#line 4382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2434, 0},
-#line 3981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2435, 0},
-#line 2910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2436, 0},
-#line 3459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2437, 0},
-#line 4007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2438, 0},
-#line 4991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2439, 0},
-#line 6350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2440, 0},
-#line 2787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2441, 0},
-#line 2871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2442, 0},
-#line 4342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2443, 0},
-#line 488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2444, 0},
-#line 5753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2445, 0},
-#line 3465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2446, 0},
-#line 2825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2447, 0},
-#line 3319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2448, 0},
-#line 2193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2449, 0},
-#line 5887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2450, 0},
-#line 2513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2451, 0},
-#line 4049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2452, 0},
-#line 5711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2453, 0},
-#line 3364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2454, 0},
-#line 5023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2455, 0},
-#line 2740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2456, 0},
-#line 1998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2457, 0},
-#line 4408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2458, 0},
-#line 4262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2459, 0},
-#line 4299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2460, 0},
-#line 1872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2461, 0},
-#line 1296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2462, 0},
-#line 4973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2463, 0},
-#line 967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2464, 0},
-#line 3993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2465, 0},
-#line 4840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2466, 0},
-#line 3425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2467, 0},
-#line 2504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2468, 0},
-#line 2456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2469, 4},
-#line 2256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2470, 0},
-#line 2526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2471, 0},
-#line 3322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2472, 0},
-#line 3730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2473, 0},
-#line 4383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2474, 0},
-#line 3984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2475, 0},
-#line 2741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2476, 0},
-#line 3039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2477, 0},
-#line 3777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2478, 0},
-#line 135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2479, 0},
-#line 5040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2480, 0},
-#line 2727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2481, 0},
-#line 1648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2482, 0},
-#line 4441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2483, 0},
-#line 4236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2484, 0},
-#line 4312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2485, 0},
-#line 6339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2486, 0},
-#line 5837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2487, 0},
-#line 5062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2488, 0},
-#line 3447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2489, 0},
-#line 5028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2490, 0},
-#line 4314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2491, 0},
-#line 5026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2492, 0},
-#line 4260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2493, 0},
-#line 5066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2494, 0},
-#line 4416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2495, 0},
-#line 3507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2496, 0},
-#line 1660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2497, 0},
-#line 6298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2498, 0},
-#line 3335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2499, 0},
-#line 2418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2500, 0},
-#line 3866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2501, 0},
-#line 2921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2502, 0},
-#line 3281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2503, 0},
-#line 4344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2504, 0},
-#line 4188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2505, 0},
-#line 2742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2506, 0},
-#line 2051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2507, 0},
-#line 4451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2508, 0},
-#line 1807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2509, 0},
-#line 4128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2510, 0},
-#line 3453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2511, 0},
-#line 4433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2512, 0},
-#line 2304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2513, 0},
-#line 6338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2514, 0},
-#line 4410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2515, 0},
-#line 4888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2516, 0},
-#line 3490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2517, 0},
-#line 5110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2518, 0},
-#line 4205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2519, 0},
-#line 2116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2520, 0},
-#line 5791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2521, 0},
-#line 2779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2522, 0},
-#line 4449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2523, 0},
-#line 5260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2524, 0},
-#line 4196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2525, 0},
-#line 6335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2526, 0},
-#line 2301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2527, 0},
-#line 4311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2528, 0},
-#line 3484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2529, 0},
-#line 426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2530, 0},
-#line 2886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2531, 0},
-#line 5167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2532, 0},
-#line 2445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2533, 4},
-#line 5056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2534, 0},
-#line 807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2535, 0},
-#line 2052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2536, 0},
-#line 6332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2537, 0},
-#line 4168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2538, 0},
-#line 5700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2539, 0},
-#line 6293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2540, 0},
-#line 767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2541, 0},
-#line 4997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2542, 0},
-#line 565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2543, 0},
-#line 2973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2544, 0},
-#line 5092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2545, 0},
-#line 4846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2546, 0},
-#line 5074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2547, 0},
-#line 2024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2548, 0},
-#line 4062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2549, 0},
-#line 2756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2550, 0},
-#line 145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2551, 0},
-#line 5890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2552, 0},
-#line 281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2553, 0},
-#line 1076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2554, 0},
-#line 294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2555, 0},
-#line 5038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2556, 0},
-#line 6355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2557, 0},
-#line 771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2558, 0},
-#line 2975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2559, 0},
-#line 5055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2560, 0},
-#line 2747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2561, 0},
-#line 6333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2562, 0},
-#line 139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2563, 0},
-#line 5309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2564, 0},
-#line 4978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2565, 0},
-#line 2952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2566, 0},
-#line 3726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2567, 0},
-#line 1654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2568, 0},
-#line 2958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2569, 0},
-#line 2295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2570, 0},
-#line 2278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2571, 0},
-#line 2335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2572, 0},
-#line 5054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2573, 0},
-#line 5265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2574, 0},
-#line 4164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2575, 0},
-#line 4665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2576, 0},
-#line 4988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2577, 0},
-#line 248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2578, 0},
-#line 6291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2579, 0},
-#line 2423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2580, 4},
-#line 2154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2581, 0},
-#line 2351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2582, 0},
-#line 2122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2583, 0},
-#line 2718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2584, 0},
-#line 2220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2585, 0},
-#line 5063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2586, 0},
-#line 4356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2587, 0},
-#line 296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2588, 0},
-#line 2475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2589, 4},
-#line 613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2590, 0},
-#line 2049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2591, 0},
-#line 132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2592, 0},
-#line 792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2593, 0},
-#line 2123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2594, 0},
-#line 3019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2595, 0},
-#line 6309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2596, 0},
-#line 4385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2597, 0},
-#line 1468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2598, 0},
-#line 3678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2599, 0},
-#line 4362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2600, 0},
-#line 1775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2601, 0},
-#line 1240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2602, 0},
-#line 5217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2603, 0},
-#line 849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2604, 0},
-#line 885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2605, 0},
-#line 894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2606, 0},
-#line 890 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2607, 0},
-#line 4413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2608, 0},
-#line 6292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2609, 0},
-#line 2277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2610, 0},
-#line 893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2611, 0},
-#line 870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2612, 0},
-#line 620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2613, 0},
-#line 3362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2614, 0},
-#line 4048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2615, 0},
-#line 4094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2616, 0},
-#line 4378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2617, 0},
-#line 366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2618, 0},
-#line 4386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2619, 0},
-#line 2307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2620, 0},
-#line 877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2621, 4},
-#line 3041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2622, 0},
-#line 272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2623, 0},
-#line 2273 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2624, 0},
-#line 233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2625, 0},
-#line 5073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2626, 0},
-#line 5799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2627, 0},
-#line 1984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2628, 0},
-#line 882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2629, 0},
-#line 4339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2630, 0},
-#line 155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2631, 0},
-#line 2183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2632, 0},
-#line 876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2633, 4},
-#line 873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2634, 0},
-#line 4289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2635, 0},
-#line 1508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2636, 0},
-#line 5007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2637, 0},
-#line 2327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2638, 0},
-#line 154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2639, 0},
-#line 847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2640, 0},
-#line 2522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2641, 0},
-#line 884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2642, 0},
-#line 889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2643, 0},
-#line 850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2644, 0},
-#line 2158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2645, 0},
-#line 872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2646, 0},
-#line 338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2647, 0},
-#line 1766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2648, 0},
-#line 597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2649, 0},
-#line 1242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2650, 0},
-#line 1093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2651, 0},
-#line 2324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2652, 0},
-#line 857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2653, 0},
-#line 1936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2654, 4},
-#line 417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2655, 0},
-#line 3801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2656, 0},
-#line 856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2657, 0},
-#line 231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2658, 4},
-#line 1234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2659, 0},
-#line 2241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2660, 4},
-#line 1436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2661, 4},
-#line 328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2662, 0},
-#line 2957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2663, 0},
-#line 569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2664, 4},
-#line 2250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2665, 0},
-#line 4333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2666, 0},
-#line 1710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2667, 0},
-#line 2005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2668, 0},
-#line 2737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2669, 0},
-#line 2099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2670, 0},
-#line 841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2671, 4},
-#line 4099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2672, 0},
-#line 1962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2673, 0},
-#line 2762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2674, 0},
-#line 4390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2675, 0},
-#line 3568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2676, 0},
-#line 3343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2677, 0},
-#line 610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2678, 0},
-#line 2182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2679, 0},
-#line 3634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2680, 0},
-#line 4183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2681, 0},
-#line 5052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2682, 0},
-#line 6328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2683, 0},
-#line 2276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2684, 0},
-#line 762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2685, 0},
-#line 5019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2686, 0},
-#line 3629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2687, 0},
-#line 1771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2688, 0},
-#line 868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2689, 0},
-#line 5789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2690, 0},
-#line 4266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2691, 0},
-#line 2187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2692, 0},
-#line 4972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2693, 0},
-#line 2238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2694, 0},
-#line 1415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2695, 0},
-#line 1774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2696, 0},
-#line 887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2697, 0},
-#line 1706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2698, 4},
-#line 4327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2699, 0},
-#line 6300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2700, 0},
-#line 3563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2701, 0},
-#line 2079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2702, 0},
-#line 859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2703, 0},
-#line 888 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2704, 0},
-#line 1765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2705, 0},
-#line 3346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2706, 0},
-#line 6315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2707, 0},
-#line 1241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2708, 0},
-#line 878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2709, 0},
-#line 855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2710, 0},
-#line 4843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2711, 0},
-#line 4835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2712, 0},
-#line 3347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2713, 0},
-#line 1078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2714, 0},
-#line 3638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2715, 0},
-#line 2185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2716, 0},
-#line 5219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2717, 0},
-#line 247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2718, 0},
-#line 453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2719, 0},
-#line 2078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2720, 0},
-#line 5777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2721, 0},
-#line 4261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2722, 0},
-#line 1480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2723, 0},
-#line 6257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2724, 0},
-#line 2098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2725, 0},
-#line 891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2726, 0},
-#line 4977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2727, 0},
-#line 1526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2728, 0},
-#line 4177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2729, 0},
-#line 1687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2730, 0},
-#line 2254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2731, 0},
-#line 2334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2732, 0},
-#line 4411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2733, 0},
-#line 3566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2734, 0},
-#line 630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2735, 0},
-#line 1081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2736, 0},
-#line 3388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2737, 0},
-#line 871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2738, 0},
-#line 5089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2739, 0},
-#line 2932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2740, 0},
-#line 5754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2741, 0},
-#line 3373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2742, 0},
-#line 5735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2743, 4},
-#line 121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2744, 0},
-#line 2009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2745, 0},
-#line 875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2746, 0},
-#line 1411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2747, 0},
-#line 6281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2748, 0},
-#line 3996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2749, 0},
-#line 30 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2750, 0},
-#line 5779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2751, 0},
-#line 1995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2752, 0},
-#line 2275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2753, 0},
-#line 2326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2754, 0},
-#line 843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2755, 0},
-#line 2552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2756, 0},
-#line 2892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2757, 0},
-#line 3633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2758, 0},
-#line 2333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2759, 0},
-#line 2873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2760, 0},
-#line 5186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2761, 0},
-#line 2656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2762, 0},
-#line 5792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2763, 0},
-#line 2323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2764, 0},
-#line 854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2765, 4},
-#line 4302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2766, 0},
-#line 860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2767, 0},
-#line 3342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2768, 0},
-#line 5858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2769, 0},
-#line 490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2770, 0},
-#line 5224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2771, 0},
-#line 1504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2772, 0},
-#line 2338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2773, 0},
-#line 4821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2774, 0},
-#line 2508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2775, 0},
-#line 4098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2776, 0},
-#line 109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2777, 0},
-#line 615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2778, 0},
-#line 1996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2779, 0},
-#line 2008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2780, 0},
-#line 4891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2781, 0},
-#line 2681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2782, 0},
-#line 3643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2783, 0},
-#line 3259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2784, 0},
-#line 5784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2785, 0},
-#line 819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2786, 1},
-#line 1676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2787, 0},
-#line 1773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2788, 0},
-#line 852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2789, 0},
-#line 886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2790, 0},
-#line 6379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2791, 0},
-#line 4760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2792, 0},
-#line 4009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2793, 0},
-#line 3482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2794, 0},
-#line 6386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2795, 0},
-#line 6361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2796, 4},
-#line 5766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2797, 4},
-#line 3997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2798, 0},
-#line 2221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2799, 0},
-#line 4030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2800, 0},
-#line 2297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2801, 0},
-#line 5768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2802, 0},
-#line 5289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2803, 0},
-#line 114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2804, 0},
-#line 2296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2805, 0},
-#line 2724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2806, 0},
-#line 32 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2807, 0},
-#line 2325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2808, 0},
-#line 2739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2809, 0},
-#line 764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2810, 0},
-#line 2949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2811, 0},
-#line 4828 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2812, 0},
-#line 5736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2813, 0},
-#line 6240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2814, 0},
-#line 2759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2815, 0},
-#line 4170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2816, 0},
-#line 4256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2817, 0},
-#line 1475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2818, 0},
-#line 170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2819, 0},
-#line 4310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2820, 0},
-#line 1972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2821, 0},
-#line 5174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2822, 0},
-#line 4963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2823, 4},
-#line 278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2824, 0},
-#line 1524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2825, 0},
-#line 2768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2826, 0},
-#line 6329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2827, 0},
-#line 4184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2828, 0},
-#line 5966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2829, 0},
-#line 3620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2830, 0},
-#line 851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2831, 0},
-#line 629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2832, 0},
-#line 5687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2833, 0},
-#line 4644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2834, 0},
-#line 4668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2835, 0},
-#line 137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2836, 0},
-#line 3994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2837, 0},
-#line 113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2838, 0},
-#line 3963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2839, 0},
-#line 1101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2840, 0},
-#line 5014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2841, 0},
-#line 2962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2842, 0},
-#line 1625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2843, 0},
-#line 495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2844, 1},
-#line 5017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2845, 0},
-#line 2789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2846, 0},
-#line 3336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2847, 0},
-#line 5237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2848, 0},
-#line 3689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2849, 0},
-#line 2745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2850, 0},
-#line 5835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2851, 0},
-#line 2749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2852, 0},
-#line 1236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2853, 0},
-#line 671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2854, 0},
-#line 1845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2855, 0},
-#line 1266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2856, 0},
-#line 1843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2857, 0},
-#line 934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2858, 0},
-#line 788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2859, 0},
-#line 39 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2860, 0},
-#line 2836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2861, 0},
-#line 2965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2862, 0},
-#line 4204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2863, 0},
-#line 1842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2864, 0},
-#line 1779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2865, 0},
-#line 4842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2866, 0},
-#line 78 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2867, 0},
-#line 5296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2868, 0},
-#line 6385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2869, 0},
-#line 1628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2870, 0},
-#line 2391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2871, 0},
-#line 4439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2872, 0},
-#line 2181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2873, 0},
-#line 5027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2874, 0},
-#line 5960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2875, 0},
-#line 3387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2876, 0},
-#line 36 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2877, 0},
-#line 1841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2878, 0},
-#line 5978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2879, 0},
-#line 5930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2880, 0},
-#line 3598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2881, 0},
-#line 149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2882, 0},
-#line 3494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2883, 0},
-#line 1627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2884, 0},
-#line 3992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2885, 0},
-#line 2192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2886, 0},
-#line 5957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2887, 0},
-#line 1238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2888, 0},
-#line 5721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2889, 0},
-#line 6013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2890, 0},
-#line 3339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2891, 0},
-#line 1268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2892, 0},
-#line 4363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2893, 0},
-#line 6274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2894, 0},
-#line 5970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2895, 0},
-#line 1239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2896, 0},
-#line 1626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2897, 0},
-#line 936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2898, 0},
-#line 1645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2899, 0},
-#line 2299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2900, 0},
-#line 2537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2901, 0},
-#line 1264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2902, 0},
-#line 932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2903, 0},
-#line 70 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2904, 0},
-#line 825 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2905, 0},
-#line 4986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2906, 0},
-#line 3274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2907, 0},
-#line 5691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2908, 4},
-#line 3372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2909, 0},
-#line 2639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2910, 0},
-#line 5971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2911, 0},
-#line 3758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2912, 0},
-#line 5883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2913, 0},
-#line 4169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2914, 0},
-#line 2390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2915, 0},
-#line 5156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2916, 0},
-#line 4270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2917, 0},
-#line 5880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2918, 0},
-#line 3331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2919, 0},
-#line 5952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2920, 0},
-#line 2751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2921, 0},
-#line 4434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2922, 0},
-#line 6057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2923, 0},
-#line 2553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2924, 0},
-#line 6180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2925, 0},
-#line 2979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2926, 0},
-#line 5954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2927, 0},
-#line 6282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2928, 0},
-#line 6237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2929, 0},
-#line 503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2930, 4},
-#line 2288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2931, 0},
-#line 6145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2932, 0},
-#line 6279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2933, 0},
-#line 1728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2934, 0},
-#line 4836 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2935, 0},
-#line 739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2936, 0},
-#line 2960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2937, 0},
-#line 3558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2938, 0},
-#line 945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2939, 0},
-#line 96 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2940, 0},
-#line 6058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2941, 0},
-#line 3714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2942, 0},
-#line 445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2943, 0},
-#line 2861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2944, 0},
-#line 2998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2945, 0},
-#line 69 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2946, 0},
-#line 295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2947, 0},
-#line 3830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2948, 0},
-#line 6319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2949, 0},
-#line 2118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2950, 0},
-#line 6349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2951, 0},
-#line 4388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2952, 0},
-#line 4222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2953, 0},
-#line 316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2954, 0},
-#line 1454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2955, 0},
-#line 4838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2956, 0},
-#line 4223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2957, 0},
-#line 1768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2958, 0},
-#line 6182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2959, 0},
-#line 4873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2960, 0},
-#line 861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2961, 0},
-#line 4721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2962, 0},
-#line 3071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2963, 0},
-#line 2274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2964, 0},
-#line 4206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2965, 0},
-#line 504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2966, 4},
-#line 864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2967, 0},
-#line 6172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2968, 0},
-#line 874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2969, 0},
-#line 4345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2970, 0},
-#line 3784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2971, 0},
-#line 5962 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2972, 0},
-#line 803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2973, 0},
-#line 6069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2974, 0},
-#line 2983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2975, 4},
-#line 2361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2976, 0},
-#line 883 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2977, 0},
-#line 5728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2978, 0},
-#line 6316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2979, 2},
-#line 4837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2980, 0},
-#line 3428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2981, 0},
-#line 6272 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2982, 0},
-#line 5813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2983, 0},
-#line 2222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2984, 0},
-#line 4213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2985, 0},
-#line 863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2986, 0},
-#line 2936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2987, 0},
-#line 3426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2988, 0},
-#line 6177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2989, 0},
-#line 6320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2990, 0},
-#line 1769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2991, 0},
-#line 865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2992, 0},
-#line 2696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2993, 0},
-#line 1108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2994, 0},
-#line 1951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2995, 0},
-#line 6060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2996, 0},
-#line 1055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2997, 0},
-#line 6277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2998, 0},
-#line 2495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str2999, 0},
-#line 6376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3000, 0},
-#line 6321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3001, 0},
-#line 1937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3002, 0},
-#line 1090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3003, 0},
-#line 5087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3004, 0},
-#line 1080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3005, 0},
-#line 213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3006, 0},
-#line 87 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3007, 4},
-#line 827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3008, 0},
-#line 5081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3009, 0},
-#line 575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3010, 0},
-#line 232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3011, 0},
-#line 549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3012, 0},
-#line 6230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3013, 0},
-#line 3064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3014, 0},
-#line 623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3015, 0},
-#line 6150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3016, 0},
-#line 153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3017, 0},
-#line 594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3018, 0},
-#line 570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3019, 0},
-#line 1552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3020, 0},
-#line 6137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3021, 0},
-#line 494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3022, 0},
-#line 1386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3023, 0},
-#line 2547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3024, 0},
-#line 842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3025, 0},
-#line 506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3026, 4},
-#line 17 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3027, 0},
-#line 3623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3028, 0},
-#line 1715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3029, 0},
-#line 191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3030, 0},
-#line 2527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3031, 0},
-#line 730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3032, 0},
-#line 4717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3033, 0},
-#line 6159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3034, 0},
-#line 547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3035, 0},
-#line 1092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3036, 0},
-#line 2470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3037, 4},
-#line 227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3038, 0},
-#line 5780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3039, 0},
-#line 327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3040, 0},
-#line 6097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3041, 0},
-#line 1465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3042, 0},
-#line 2426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3043, 4},
-#line 596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3044, 0},
-#line 3690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3045, 0},
-#line 669 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3046, 0},
-#line 4191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3047, 0},
-#line 2997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3048, 0},
-#line 2985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3049, 0},
-#line 6322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3050, 0},
-#line 862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3051, 0},
-#line 4757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3052, 0},
-#line 4879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3053, 0},
-#line 4207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3054, 0},
-#line 6188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3055, 0},
-#line 806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3056, 0},
-#line 5737 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3057, 4},
-#line 2897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3058, 0},
-#line 1173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3059, 0},
-#line 3738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3060, 0},
-#line 1224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3061, 0},
-#line 3567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3062, 0},
-#line 6255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3063, 0},
-#line 3632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3064, 0},
-#line 5912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3065, 0},
-#line 1340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3066, 0},
-#line 2487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3067, 4},
-#line 1013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3068, 0},
-#line 450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3069, 0},
-#line 6204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3070, 0},
-#line 1481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3071, 0},
-#line 2152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3072, 0},
-#line 4454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3073, 0},
-#line 2472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3074, 4},
-#line 1060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3075, 0},
-#line 3808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3076, 0},
-#line 5982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3077, 0},
-#line 260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3078, 0},
-#line 1469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3079, 0},
-#line 2955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3080, 0},
-#line 4401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3081, 0},
-#line 1111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3082, 0},
-#line 1957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3083, 0},
-#line 1065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3084, 0},
-#line 6162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3085, 0},
-#line 1112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3086, 0},
-#line 5675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3087, 0},
-#line 3562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3088, 0},
-#line 1166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3089, 0},
-#line 5694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3090, 0},
-#line 4200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3091, 0},
-#line 1402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3092, 0},
-#line 6374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3093, 0},
-#line 164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3094, 0},
-#line 1132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3095, 0},
-#line 2311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3096, 0},
-#line 2674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3097, 0},
-#line 5271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3098, 0},
-#line 3345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3099, 0},
-#line 853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3100, 0},
-#line 251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3101, 0},
-#line 2705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3102, 0},
-#line 833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3103, 4},
-#line 3637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3104, 0},
-#line 2367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3105, 0},
-#line 1923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3106, 0},
-#line 3676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3107, 0},
-#line 339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3108, 0},
-#line 1020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3109, 0},
-#line 1061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3110, 0},
-#line 6098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3111, 0},
-#line 1525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3112, 0},
-#line 6190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3113, 0},
-#line 1066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3114, 0},
-#line 2229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3115, 0},
-#line 6055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3116, 0},
-#line 6178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3117, 0},
-#line 3337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3118, 0},
-#line 2369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3119, 0},
-#line 1940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3120, 0},
-#line 1674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3121, 0},
-#line 628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3122, 0},
-#line 1144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3123, 0},
-#line 3386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3124, 0},
-#line 1861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3125, 0},
-#line 1286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3126, 0},
-#line 2219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3127, 0},
-#line 5048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3128, 0},
-#line 1432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3129, 0},
-#line 552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3130, 0},
-#line 253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3131, 0},
-#line 6183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3132, 0},
-#line 373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3133, 0},
-#line 6202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3134, 0},
-#line 6363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3135, 4},
-#line 4730 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3136, 0},
-#line 815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3137, 1},
-#line 3609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3138, 0},
-#line 5220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3139, 0},
-#line 2455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3140, 4},
-#line 432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3141, 0},
-#line 2519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3142, 4},
-#line 6285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3143, 0},
-#line 2286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3144, 0},
-#line 224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3145, 0},
-#line 1401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3146, 0},
-#line 6157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3147, 0},
-#line 268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3148, 0},
-#line 2914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3149, 0},
-#line 2469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3150, 4},
-#line 350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3151, 0},
-#line 1158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3152, 0},
-#line 1572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3153, 4},
-#line 6118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3154, 0},
-#line 3618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3155, 0},
-#line 6089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3156, 0},
-#line 1585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3157, 4},
-#line 6231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3158, 0},
-#line 5972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3159, 0},
-#line 1161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3160, 0},
-#line 2200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3161, 0},
-#line 1887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3162, 0},
-#line 2166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3163, 0},
-#line 1309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3164, 0},
-#line 2368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3165, 0},
-#line 6256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3166, 0},
-#line 1586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3167, 4},
-#line 1719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3168, 0},
-#line 1064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3169, 0},
-#line 2344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3170, 0},
-#line 5232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3171, 0},
-#line 6158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3172, 0},
-#line 1571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3173, 4},
-#line 1591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3174, 4},
-#line 4749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3175, 0},
-#line 1607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3176, 4},
-#line 438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3177, 0},
-#line 1589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3178, 4},
-#line 4201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3179, 0},
-#line 435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3180, 0},
-#line 2042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3181, 0},
-#line 4755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3182, 0},
-#line 1605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3183, 4},
-#line 5268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3184, 0},
-#line 1604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3185, 4},
-#line 5299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3186, 0},
-#line 1570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3187, 4},
-#line 1584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3188, 4},
-#line 5928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3189, 0},
-#line 1489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3190, 0},
-#line 1603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3191, 4},
-#line 1250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3192, 0},
-#line 2343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3193, 0},
-#line 640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3194, 0},
-#line 916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3195, 0},
-#line 2540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3196, 0},
-#line 4180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3197, 0},
-#line 2548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3198, 0},
-#line 3381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3199, 4},
-#line 1606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3200, 4},
-#line 185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3201, 0},
-#line 3982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3202, 0},
-#line 4178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3203, 0},
-#line 6201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3204, 0},
-#line 742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3205, 0},
-#line 3053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3206, 0},
-#line 1035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3207, 0},
-#line 1568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3208, 4},
-#line 1901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3209, 0},
-#line 1590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3210, 4},
-#line 1325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3211, 0},
-#line 1399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3212, 0},
-#line 4202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3213, 0},
-#line 107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3214, 0},
-#line 451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3215, 0},
-#line 1930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3216, 0},
-#line 1353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3217, 0},
-#line 2214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3218, 4},
-#line 1031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3219, 0},
-#line 3732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3220, 0},
-#line 6059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3221, 0},
-#line 4444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3222, 0},
-#line 1498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3223, 0},
-#line 94 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3224, 0},
-#line 1569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3225, 4},
-#line 1846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3226, 0},
-#line 1267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3227, 0},
-#line 2462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3228, 4},
-#line 1085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3229, 0},
-#line 935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3230, 0},
-#line 4776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3231, 0},
-#line 2927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3232, 0},
-#line 3018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3233, 0},
-#line 1530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3234, 0},
-#line 6153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3235, 0},
-#line 1567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3236, 4},
-#line 2685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3237, 0},
-#line 3622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3238, 0},
-#line 2436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3239, 4},
-#line 3932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3240, 0},
-#line 1615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3241, 4},
-#line 5997 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3242, 0},
-#line 1063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3243, 0},
-#line 551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3244, 0},
-#line 1653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3245, 0},
-#line 3894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3246, 0},
-#line 2215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3247, 4},
-#line 2402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3248, 0},
-#line 1616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3249, 4},
-#line 3941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3250, 0},
-#line 4657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3251, 0},
-#line 1618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3252, 4},
-#line 1581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3253, 4},
-#line 1579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3254, 4},
-#line 5274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3255, 0},
-#line 2353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3256, 0},
-#line 2840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3257, 0},
-#line 3727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3258, 0},
-#line 4731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3259, 0},
-#line 1164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3260, 0},
-#line 6151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3261, 0},
-#line 4435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3262, 0},
-#line 5022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3263, 0},
-#line 6050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3264, 0},
-#line 1056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3265, 0},
-#line 4396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3266, 0},
-#line 2781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3267, 0},
-#line 2536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3268, 0},
-#line 1784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3269, 0},
-#line 1284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3270, 0},
-#line 2385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3271, 0},
-#line 955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3272, 0},
-#line 1726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3273, 4},
-#line 1122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3274, 0},
-#line 2693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3275, 0},
-#line 1180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3276, 0},
-#line 4228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3277, 0},
-#line 6171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3278, 0},
-#line 1167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3279, 4},
-#line 1955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3280, 0},
-#line 2530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3281, 0},
-#line 1582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3282, 4},
-#line 425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3283, 0},
-#line 4976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3284, 0},
-#line 6123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3285, 0},
-#line 5967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3286, 0},
-#line 412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3287, 0},
-#line 1168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3288, 4},
-#line 6082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3289, 0},
-#line 4658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3290, 0},
-#line 6323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3291, 0},
-#line 265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3292, 0},
-#line 2322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3293, 4},
-#line 6334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3294, 0},
-#line 2167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3295, 0},
-#line 1617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3296, 4},
-#line 4671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3297, 0},
-#line 1576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3298, 4},
-#line 6138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3299, 0},
-#line 1592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3300, 4},
-#line 3987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3301, 0},
-#line 1058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3302, 0},
-#line 6127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3303, 0},
-#line 2434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3304, 4},
-#line 1159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3305, 0},
-#line 5920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3306, 4},
-#line 1583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3307, 4},
-#line 1200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3308, 4},
-#line 1870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3309, 0},
-#line 1294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3310, 0},
-#line 3905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3311, 0},
-#line 5862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3312, 0},
-#line 2030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3313, 0},
-#line 4854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3314, 0},
-#line 6212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3315, 0},
-#line 3034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3316, 0},
-#line 6093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3317, 0},
-#line 6115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3318, 0},
-#line 1378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3319, 0},
-#line 2162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3320, 0},
-#line 3437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3321, 0},
-#line 177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3322, 0},
-#line 4336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3323, 0},
-#line 1620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3324, 0},
-#line 5181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3325, 0},
-#line 4887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3326, 0},
-#line 1201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3327, 4},
-#line 319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3328, 0},
-#line 2199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3329, 0},
-#line 4428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3330, 0},
-#line 429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3331, 0},
-#line 75 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3332, 0},
-#line 866 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3333, 0},
-#line 2461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3334, 4},
-#line 487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3335, 0},
-#line 6312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3336, 0},
-#line 3757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3337, 0},
-#line 6297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3338, 0},
-#line 379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3339, 0},
-#line 3919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3340, 0},
-#line 6085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3341, 0},
-#line 2255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3342, 4},
-#line 3771 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3343, 0},
-#line 1221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3344, 4},
-#line 3360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3345, 0},
-#line 6045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3346, 0},
-#line 3266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3347, 0},
-#line 3731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3348, 0},
-#line 1195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3349, 0},
-#line 390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3350, 0},
-#line 3383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3351, 0},
-#line 1999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3352, 0},
-#line 6139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3353, 0},
-#line 588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3354, 0},
-#line 6164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3355, 0},
-#line 1664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3356, 0},
-#line 1564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3357, 0},
-#line 1718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3358, 0},
-#line 3556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3359, 0},
-#line 4403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3360, 0},
-#line 2092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3361, 4},
-#line 953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3362, 0},
-#line 1575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3363, 4},
-#line 3271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3364, 0},
-#line 2302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3365, 4},
-#line 6099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3366, 0},
-#line 5977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3367, 0},
-#line 263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3368, 0},
-#line 2780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3369, 0},
-#line 1554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3370, 0},
-#line 1806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3371, 0},
-#line 6096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3372, 0},
-#line 2405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3373, 0},
-#line 5881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3374, 0},
-#line 1602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3375, 4},
-#line 6262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3376, 0},
-#line 1088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3377, 0},
-#line 2420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3378, 4},
-#line 5107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3379, 0},
-#line 1137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3380, 0},
-#line 2168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3381, 0},
-#line 2626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3382, 0},
-#line 1152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3383, 0},
-#line 6236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3384, 0},
-#line 5874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3385, 0},
-#line 4720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3386, 0},
-#line 5240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3387, 0},
-#line 4297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3388, 0},
-#line 1231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3389, 0},
-#line 5059 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3390, 0},
-#line 3395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3391, 0},
-#line 4116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3392, 0},
-#line 320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3393, 0},
-#line 4664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3394, 0},
-#line 5244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3395, 0},
-#line 1770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3396, 0},
-#line 2984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3397, 0},
-#line 1243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3398, 0},
-#line 867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3399, 0},
-#line 5164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3400, 0},
-#line 4296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3401, 0},
-#line 1501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3402, 0},
-#line 832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3403, 4},
-#line 4291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3404, 0},
-#line 5067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3405, 0},
-#line 5245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3406, 0},
-#line 1938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3407, 0},
-#line 5827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3408, 0},
-#line 6330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3409, 0},
-#line 199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3410, 0},
-#line 80 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3411, 0},
-#line 1075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3412, 0},
-#line 1388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3413, 0},
-#line 6346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3414, 0},
-#line 3990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3415, 0},
-#line 2022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3416, 0},
-#line 3653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3417, 0},
-#line 743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3418, 0},
-#line 6216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3419, 0},
-#line 5189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3420, 0},
-#line 4438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3421, 0},
-#line 1735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3422, 0},
-#line 6235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3423, 0},
-#line 3435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3424, 0},
-#line 561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3425, 0},
-#line 97 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3426, 0},
-#line 5838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3427, 0},
-#line 2625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3428, 0},
-#line 6208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3429, 0},
-#line 1192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3430, 0},
-#line 3886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3431, 0},
-#line 5785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3432, 0},
-#line 5931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3433, 0},
-#line 3277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3434, 0},
-#line 5985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3435, 0},
-#line 189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3436, 0},
-#line 6125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3437, 0},
-#line 6064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3438, 0},
-#line 6106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3439, 0},
-#line 4455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3440, 0},
-#line 6143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3441, 0},
-#line 3415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3442, 0},
-#line 1520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3443, 0},
-#line 471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3444, 0},
-#line 357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3445, 0},
-#line 1760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3446, 0},
-#line 5250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3447, 0},
-#line 3616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3448, 0},
-#line 2941 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3449, 0},
-#line 5175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3450, 0},
-#line 4387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3451, 0},
-#line 643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3452, 0},
-#line 3674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3453, 0},
-#line 3979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3454, 0},
-#line 4294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3455, 0},
-#line 5774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3456, 0},
-#line 2048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3457, 0},
-#line 2383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3458, 0},
-#line 2188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3459, 4},
-#line 2216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3460, 4},
-#line 6117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3461, 0},
-#line 2041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3462, 0},
-#line 5773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3463, 0},
-#line 4827 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3464, 0},
-#line 3719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3465, 0},
-#line 4967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3466, 0},
-#line 1764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3467, 0},
-#line 343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3468, 0},
-#line 1220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3469, 4},
-#line 5855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3470, 0},
-#line 5149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3471, 0},
-#line 1230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3472, 0},
-#line 577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3473, 0},
-#line 439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3474, 0},
-#line 389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3475, 0},
-#line 5079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3476, 0},
-#line 3584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3477, 0},
-#line 642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3478, 0},
-#line 4656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3479, 0},
-#line 745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3480, 0},
-#line 562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3481, 0},
-#line 171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3482, 0},
-#line 6215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3483, 0},
-#line 4845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3484, 0},
-#line 2885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3485, 0},
-#line 5039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3486, 0},
-#line 3072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3487, 0},
-#line 778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3488, 0},
-#line 1072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3489, 0},
-#line 3718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3490, 0},
-#line 656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3491, 0},
-#line 5088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3492, 0},
-#line 1559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3493, 0},
-#line 277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3494, 0},
-#line 1169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3495, 4},
-#line 5136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3496, 0},
-#line 413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3497, 0},
-#line 1146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3498, 0},
-#line 829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3499, 0},
-#line 1691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3500, 0},
-#line 3720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3501, 0},
-#line 2086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3502, 0},
-#line 1679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3503, 0},
-#line 5018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3504, 0},
-#line 2631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3505, 0},
-#line 1198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3506, 4},
-#line 6206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3507, 0},
-#line 2177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3508, 0},
-#line 1702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3509, 0},
-#line 5144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3510, 0},
-#line 2483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3511, 4},
-#line 6146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3512, 0},
-#line 376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3513, 0},
-#line 5091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3514, 0},
-#line 6122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3515, 0},
-#line 4849 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3516, 0},
-#line 1135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3517, 0},
-#line 3390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3518, 0},
-#line 3573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3519, 0},
-#line 2407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3520, 0},
-#line 4895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3521, 0},
-#line 1156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3522, 0},
-#line 2441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3523, 4},
-#line 1202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3524, 4},
-#line 4237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3525, 0},
-#line 1069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3526, 0},
-#line 4960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3527, 0},
-#line 5974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3528, 0},
-#line 4953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3529, 0},
-#line 3288 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3530, 0},
-#line 731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3531, 4},
-#line 2071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3532, 0},
-#line 1577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3533, 4},
-#line 1593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3534, 4},
-#line 2315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3535, 0},
-#line 1573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3536, 4},
-#line 2516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3537, 0},
-#line 156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3538, 0},
-#line 4185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3539, 0},
-#line 606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3540, 0},
-#line 663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3541, 0},
-#line 5046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3542, 0},
-#line 3739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3543, 0},
-#line 2713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3544, 0},
-#line 1036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3545, 0},
-#line 1097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3546, 0},
-#line 5749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3547, 0},
-#line 323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3548, 0},
-#line 809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3549, 0},
-#line 2556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3550, 0},
-#line 1460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3551, 0},
-#line 258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3552, 0},
-#line 6325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3553, 0},
-#line 6070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3554, 0},
-#line 3724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3555, 0},
-#line 5120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3556, 0},
-#line 3767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3557, 0},
-#line 207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3558, 0},
-#line 3312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3559, 0},
-#line 31 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3560, 0},
-#line 5794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3561, 0},
-#line 4875 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3562, 0},
-#line 4398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3563, 0},
-#line 6065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3564, 0},
-#line 4186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3565, 0},
-#line 4166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3566, 0},
-#line 6067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3567, 0},
-#line 2031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3568, 0},
-#line 1727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3569, 4},
-#line 1850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3570, 0},
-#line 1271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3571, 0},
-#line 4619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3572, 0},
-#line 940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3573, 0},
-#line 4575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3574, 0},
-#line 776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3575, 0},
-#line 6294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3576, 0},
-#line 2457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3577, 4},
-#line 5961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3578, 0},
-#line 3407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3579, 0},
-#line 4532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3580, 0},
-#line 608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3581, 0},
-#line 3056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3582, 0},
-#line 6217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3583, 0},
-#line 5993 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3584, 0},
-#line 3574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3585, 0},
-#line 4565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3586, 0},
-#line 3031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3587, 0},
-#line 256 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3588, 0},
-#line 2797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3589, 0},
-#line 4627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3590, 0},
-#line 2040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3591, 0},
-#line 4600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3592, 0},
-#line 279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3593, 0},
-#line 1027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3594, 0},
-#line 2164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3595, 0},
-#line 3334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3596, 0},
-#line 4219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3597, 0},
-#line 4607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3598, 0},
-#line 4456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3599, 0},
-#line 4543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3600, 0},
-#line 4457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3601, 0},
-#line 1949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3602, 4},
-#line 5935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3603, 0},
-#line 4488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3604, 0},
-#line 3814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3605, 0},
-#line 5926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3606, 0},
-#line 125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3607, 0},
-#line 2300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3608, 0},
-#line 196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3609, 0},
-#line 1160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3610, 0},
-#line 249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3611, 0},
-#line 1197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3612, 0},
-#line 4623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3613, 0},
-#line 101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3614, 0},
-#line 6259 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3615, 0},
-#line 2227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3616, 0},
-#line 184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3617, 0},
-#line 6278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3618, 0},
-#line 4018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3619, 0},
-#line 1033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3620, 0},
-#line 1840 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3621, 0},
-#line 1263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3622, 0},
-#line 1034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3623, 0},
-#line 931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3624, 0},
-#line 6234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3625, 0},
-#line 4603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3626, 0},
-#line 6105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3627, 0},
-#line 193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3628, 0},
-#line 4628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3629, 0},
-#line 3819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3630, 0},
-#line 6225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3631, 0},
-#line 3522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3632, 0},
-#line 3821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3633, 0},
-#line 5698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3634, 0},
-#line 4024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3635, 4},
-#line 5714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3636, 0},
-#line 2205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3637, 0},
-#line 1663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3638, 0},
-#line 4602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3639, 0},
-#line 2664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3640, 0},
-#line 563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3641, 0},
-#line 6296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3642, 0},
-#line 1121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3643, 0},
-#line 5130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3644, 0},
-#line 4637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3645, 0},
-#line 1536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3646, 4},
-#line 4088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3647, 0},
-#line 4089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3648, 0},
-#line 4493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3649, 0},
-#line 4085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3650, 0},
-#line 3292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3651, 0},
-#line 4606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3652, 0},
-#line 5738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3653, 0},
-#line 2287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3654, 0},
-#line 4592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3655, 0},
-#line 6044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3656, 0},
-#line 4761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3657, 0},
-#line 6207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3658, 0},
-#line 3326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3659, 0},
-#line 779 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3660, 0},
-#line 4542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3661, 0},
-#line 3046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3662, 0},
-#line 644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3663, 0},
-#line 4614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3664, 0},
-#line 2637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3665, 0},
-#line 1950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3666, 4},
-#line 659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3667, 0},
-#line 175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3668, 0},
-#line 5202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3669, 0},
-#line 6224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3670, 0},
-#line 5125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3671, 0},
-#line 6203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3672, 0},
-#line 1139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3673, 0},
-#line 4102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3674, 0},
-#line 2055 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3675, 0},
-#line 5988 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3676, 0},
-#line 4589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3677, 0},
-#line 5725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3678, 0},
-#line 816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3679, 1},
-#line 4220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3680, 0},
-#line 4892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3681, 0},
-#line 2145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3682, 0},
-#line 5013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3683, 0},
-#line 4458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3684, 0},
-#line 4014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3685, 0},
-#line 6016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3686, 0},
-#line 4732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3687, 0},
-#line 1829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3688, 0},
-#line 1253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3689, 0},
-#line 2851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3690, 4},
-#line 920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3691, 0},
-#line 4021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3692, 0},
-#line 4480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3693, 0},
-#line 579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3694, 0},
-#line 6068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3695, 0},
-#line 225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3696, 4},
-#line 2645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3697, 0},
-#line 5078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3698, 0},
-#line 6226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3699, 0},
-#line 566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3700, 0},
-#line 3817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3701, 0},
-#line 5724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3702, 0},
-#line 1756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3703, 4},
-#line 2093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3704, 0},
-#line 3736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3705, 0},
-#line 5681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3706, 0},
-#line 4525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3707, 0},
-#line 1196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3708, 0},
-#line 5939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3709, 0},
-#line 2388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3710, 0},
-#line 2729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3711, 0},
-#line 264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3712, 0},
-#line 4608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3713, 0},
-#line 3766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3714, 0},
-#line 4591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3715, 0},
-#line 6265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3716, 0},
-#line 3989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3717, 0},
-#line 4540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3718, 0},
-#line 2109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3719, 0},
-#line 4522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3720, 0},
-#line 2028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3721, 0},
-#line 3775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3722, 0},
-#line 3764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3723, 0},
-#line 5475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3724, 0},
-#line 5417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3725, 0},
-#line 4561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3726, 0},
-#line 5666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3727, 0},
-#line 2856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3728, 0},
-#line 1812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3729, 0},
-#line 4729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3730, 0},
-#line 5570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3731, 2},
-#line 4100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3732, 0},
-#line 5460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3733, 0},
-#line 4733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3734, 0},
-#line 1071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3735, 0},
-#line 4020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3736, 0},
-#line 6270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3737, 0},
-#line 1723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3738, 0},
-#line 5530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3739, 0},
-#line 3262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3740, 0},
-#line 3323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3741, 0},
-#line 4080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3742, 0},
-#line 5313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3743, 0},
-#line 502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3744, 4},
-#line 5472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3745, 0},
-#line 4620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3746, 0},
-#line 5314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3747, 0},
-#line 4138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3748, 0},
-#line 4507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3749, 0},
-#line 4521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3750, 0},
-#line 5011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3751, 0},
-#line 142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3752, 0},
-#line 4017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3753, 0},
-#line 4870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3754, 0},
-#line 6384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3755, 0},
-#line 4593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3756, 0},
-#line 4092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3757, 0},
-#line 4621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3758, 0},
-#line 2945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3759, 0},
-#line 4494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3760, 0},
-#line 4955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3761, 0},
-#line 5636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3762, 0},
-#line 6194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3763, 0},
-#line 151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3764, 0},
-#line 4515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3765, 0},
-#line 4518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3766, 0},
-#line 4951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3767, 0},
-#line 3287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3768, 0},
-#line 4429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3769, 0},
-#line 5975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3770, 0},
-#line 2404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3771, 0},
-#line 5601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3772, 0},
-#line 3013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3773, 0},
-#line 5872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3774, 0},
-#line 4479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3775, 0},
-#line 5870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3776, 0},
-#line 4292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3777, 0},
-#line 4086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3778, 0},
-#line 3965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3779, 0},
-#line 1776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3780, 0},
-#line 5025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3781, 0},
-#line 4574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3782, 0},
-#line 6219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3783, 0},
-#line 892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3784, 0},
-#line 848 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3785, 0},
-#line 5454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3786, 0},
-#line 6289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3787, 0},
-#line 2157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3788, 0},
-#line 2257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3789, 0},
-#line 786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3790, 0},
-#line 2870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3791, 0},
-#line 3768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3792, 0},
-#line 3811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3793, 0},
-#line 5640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3794, 0},
-#line 6155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3795, 0},
-#line 2878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3796, 0},
-#line 3464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3797, 0},
-#line 553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3798, 0},
-#line 858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3799, 0},
-#line 636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3800, 0},
-#line 5798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3801, 0},
-#line 4531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3802, 0},
-#line 1873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3803, 0},
-#line 1297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3804, 0},
-#line 968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3805, 0},
-#line 4868 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3806, 0},
-#line 5873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3807, 0},
-#line 6107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3808, 0},
-#line 5600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3809, 0},
-#line 4001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3810, 0},
-#line 5455 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3811, 0},
-#line 2944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3812, 0},
-#line 475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3813, 0},
-#line 5461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3814, 0},
-#line 5591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3815, 0},
-#line 903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3816, 0},
-#line 5793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3817, 0},
-#line 5433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3818, 0},
-#line 348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3819, 0},
-#line 5597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3820, 0},
-#line 5826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3821, 0},
-#line 2094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3822, 4},
-#line 5423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3823, 0},
-#line 1371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3824, 0},
-#line 6354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3825, 0},
-#line 2846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3826, 0},
-#line 5485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3827, 0},
-#line 5448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3828, 0},
-#line 4528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3829, 0},
-#line 666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3830, 0},
-#line 1374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3831, 4},
-#line 2317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3832, 0},
-#line 5411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3833, 0},
-#line 4581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3834, 0},
-#line 364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3835, 0},
-#line 2698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3836, 0},
-#line 2095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3837, 4},
-#line 4844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3838, 0},
-#line 4467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3839, 0},
-#line 4069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3840, 0},
-#line 6218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3841, 0},
-#line 1703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3842, 0},
-#line 5294 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3843, 0},
-#line 1409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3844, 0},
-#line 2542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3845, 0},
-#line 5864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3846, 0},
-#line 4230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3847, 0},
-#line 5639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3848, 0},
-#line 3812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3849, 0},
-#line 1578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3850, 4},
-#line 5646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3851, 0},
-#line 1847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3852, 0},
-#line 4036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3853, 0},
-#line 76 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3854, 0},
-#line 5471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3855, 0},
-#line 5663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3856, 0},
-#line 3421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3857, 0},
-#line 470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3858, 0},
-#line 5068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3859, 0},
-#line 2452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3860, 4},
-#line 4734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3861, 0},
-#line 1490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3862, 0},
-#line 6244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3863, 0},
-#line 5772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3864, 0},
-#line 4780 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3865, 0},
-#line 4503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3866, 0},
-#line 1222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3867, 4},
-#line 5568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3868, 0},
-#line 3857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3869, 0},
-#line 4161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3870, 0},
-#line 5574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3871, 0},
-#line 3500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3872, 4},
-#line 4680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3873, 0},
-#line 5899 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3874, 0},
-#line 4742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3875, 4},
-#line 4469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3876, 0},
-#line 4563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3877, 0},
-#line 2763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3878, 0},
-#line 5876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3879, 0},
-#line 5445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3880, 0},
-#line 5542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3881, 0},
-#line 5800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3882, 0},
-#line 3646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3883, 0},
-#line 5903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3884, 0},
-#line 4767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3885, 4},
-#line 789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3886, 0},
-#line 5263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3887, 0},
-#line 4858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3888, 0},
-#line 329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3889, 4},
-#line 4715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3890, 0},
-#line 4814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3891, 0},
-#line 5594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3892, 0},
-#line 4231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3893, 0},
-#line 3366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3894, 0},
-#line 4541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3895, 0},
-#line 3296 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3896, 0},
-#line 2050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3897, 0},
-#line 1612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3898, 4},
-#line 2829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3899, 0},
-#line 2847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3900, 0},
-#line 6116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3901, 0},
-#line 4954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3902, 0},
-#line 5884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3903, 0},
-#line 2346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3904, 0},
-#line 3859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3905, 0},
-#line 3533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3906, 0},
-#line 204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3907, 0},
-#line 6066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3908, 0},
-#line 6271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3909, 0},
-#line 5459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3910, 0},
-#line 5024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3911, 0},
-#line 763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3912, 0},
-#line 3855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3913, 0},
-#line 4677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3914, 0},
-#line 4147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3915, 0},
-#line 6387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3916, 0},
-#line 5943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3917, 0},
-#line 1494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3918, 0},
-#line 5909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3919, 0},
-#line 5786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3920, 0},
-#line 2708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3921, 0},
-#line 5783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3922, 0},
-#line 6052 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3923, 0},
-#line 4893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3924, 0},
-#line 5434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3925, 0},
-#line 4555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3926, 0},
-#line 3640 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3927, 0},
-#line 5662 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3928, 0},
-#line 6344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3929, 0},
-#line 5778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3930, 0},
-#line 3443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3931, 0},
-#line 4630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3932, 0},
-#line 3959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3933, 0},
-#line 4536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3934, 0},
-#line 2211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3935, 4},
-#line 4038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3936, 0},
-#line 5573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3937, 0},
-#line 5811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3938, 0},
-#line 5529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3939, 0},
-#line 6222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3940, 0},
-#line 4636 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3941, 0},
-#line 6266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3942, 0},
-#line 1126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3943, 0},
-#line 5546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3944, 0},
-#line 2262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3945, 0},
-#line 2210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3946, 4},
-#line 4481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3947, 0},
-#line 2070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3948, 0},
-#line 1157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3949, 0},
-#line 1233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3950, 0},
-#line 4464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3951, 0},
-#line 1140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3952, 0},
-#line 4584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3953, 0},
-#line 2867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3954, 0},
-#line 4860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3955, 0},
-#line 4822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3956, 0},
-#line 6084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3957, 0},
-#line 6366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3958, 0},
-#line 5452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3959, 0},
-#line 2876 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3960, 0},
-#line 4562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3961, 0},
-#line 3765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3962, 0},
-#line 5532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3963, 0},
-#line 4495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3964, 0},
-#line 5819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3965, 0},
-#line 4629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3966, 0},
-#line 2496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3967, 4},
-#line 6061 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3968, 0},
-#line 6001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3969, 0},
-#line 4831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3970, 0},
-#line 1406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3971, 0},
-#line 143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3972, 0},
-#line 757 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3973, 0},
-#line 5283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3974, 0},
-#line 2497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3975, 4},
-#line 4025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3976, 0},
-#line 2114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3977, 0},
-#line 2348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3978, 0},
-#line 4253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3979, 0},
-#line 4431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3980, 0},
-#line 4524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3981, 0},
-#line 4625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3982, 0},
-#line 4553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3983, 0},
-#line 4093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3984, 0},
-#line 6251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3985, 0},
-#line 1153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3986, 0},
-#line 550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3987, 0},
-#line 5221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3988, 0},
-#line 4189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3989, 0},
-#line 2349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3990, 0},
-#line 4564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3991, 0},
-#line 4765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3992, 0},
-#line 112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3993, 0},
-#line 4293 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3994, 0},
-#line 1450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3995, 0},
-#line 5082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3996, 0},
-#line 4772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3997, 0},
-#line 4952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3998, 0},
-#line 4583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str3999, 0},
-#line 4415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4000, 0},
-#line 2263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4001, 0},
-#line 3481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4002, 0},
-#line 5703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4003, 0},
-#line 6173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4004, 0},
-#line 2528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4005, 0},
-#line 4087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4006, 0},
-#line 5973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4007, 0},
-#line 4812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4008, 0},
-#line 5161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4009, 0},
-#line 5436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4010, 0},
-#line 2039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4011, 0},
-#line 4582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4012, 0},
-#line 5963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4013, 0},
-#line 4613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4014, 0},
-#line 5511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4015, 0},
-#line 5398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4016, 0},
-#line 5593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4017, 0},
-#line 2265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4018, 0},
-#line 4943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4019, 0},
-#line 4218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4020, 0},
-#line 434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4021, 0},
-#line 2894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4022, 2},
-#line 4818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4023, 0},
-#line 759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4024, 0},
-#line 4527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4025, 0},
-#line 2033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4026, 4},
-#line 5304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4027, 0},
-#line 284 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4028, 0},
-#line 4471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4029, 0},
-#line 3009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4030, 0},
-#line 2270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4031, 0},
-#line 5656 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4032, 0},
-#line 6264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4033, 0},
-#line 2924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4034, 0},
-#line 5830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4035, 0},
-#line 5846 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4036, 0},
-#line 4966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4037, 0},
-#line 2014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4038, 0},
-#line 5128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4039, 0},
-#line 3686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4040, 0},
-#line 4463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4041, 0},
-#line 5804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4042, 0},
-#line 5545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4043, 0},
-#line 4152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4044, 0},
-#line 5845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4045, 0},
-#line 6073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4046, 0},
-#line 3936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4047, 0},
-#line 5213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4048, 0},
-#line 6092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4049, 0},
-#line 2054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4050, 0},
-#line 6233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4051, 0},
-#line 4942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4052, 0},
-#line 5537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4053, 0},
-#line 3066 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4054, 0},
-#line 5541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4055, 0},
-#line 5998 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4056, 0},
-#line 4638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4057, 0},
-#line 4945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4058, 4},
-#line 3877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4059, 0},
-#line 1062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4060, 0},
-#line 6243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4061, 0},
-#line 5147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4062, 0},
-#line 4612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4063, 0},
-#line 1114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4064, 0},
-#line 4956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4065, 0},
-#line 6149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4066, 0},
-#line 5844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4067, 0},
-#line 3760 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4068, 0},
-#line 3368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4069, 0},
-#line 5609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4070, 0},
-#line 4826 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4071, 0},
-#line 5586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4072, 0},
-#line 3750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4073, 0},
-#line 5801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4074, 0},
-#line 99 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4075, 0},
-#line 6227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4076, 0},
-#line 447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4077, 0},
-#line 5621 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4078, 0},
-#line 1474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4079, 0},
-#line 2838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4080, 0},
-#line 4635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4081, 0},
-#line 3842 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4082, 0},
-#line 4805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4083, 0},
-#line 3791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4084, 0},
-#line 4682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4085, 0},
-#line 5543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4086, 0},
-#line 5021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4087, 0},
-#line 3069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4088, 0},
-#line 5184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4089, 0},
-#line 2442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4090, 4},
-#line 5807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4091, 0},
-#line 3915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4092, 0},
-#line 6168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4093, 0},
-#line 2624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4094, 0},
-#line 6079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4095, 0},
-#line 5484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4096, 0},
-#line 3946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4097, 0},
-#line 4806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4098, 4},
-#line 2774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4099, 0},
-#line 3858 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4100, 0},
-#line 4807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4101, 0},
-#line 6074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4102, 0},
-#line 6214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4103, 0},
-#line 4904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4104, 0},
-#line 5394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4105, 0},
-#line 4547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4106, 0},
-#line 4235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4107, 0},
-#line 1857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4108, 0},
-#line 1279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4109, 0},
-#line 6133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4110, 0},
-#line 949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4111, 0},
-#line 422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4112, 0},
-#line 5535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4113, 0},
-#line 2734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4114, 0},
-#line 6189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4115, 0},
-#line 5009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4116, 0},
-#line 3024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4117, 0},
-#line 4743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4118, 0},
-#line 2498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4119, 4},
-#line 3751 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4120, 0},
-#line 5847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4121, 0},
-#line 3873 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4122, 0},
-#line 4391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4123, 0},
-#line 3604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4124, 0},
-#line 4141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4125, 0},
-#line 5143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4126, 0},
-#line 2128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4127, 0},
-#line 4847 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4128, 0},
-#line 5671 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4129, 0},
-#line 4736 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4130, 0},
-#line 6276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4131, 0},
-#line 282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4132, 0},
-#line 5514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4133, 0},
-#line 4676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4134, 0},
-#line 3967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4135, 0},
-#line 2502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4136, 0},
-#line 5401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4137, 0},
-#line 4496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4138, 0},
-#line 5731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4139, 4},
-#line 6037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4140, 0},
-#line 4165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4141, 0},
-#line 4999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4142, 0},
-#line 2764 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4143, 0},
-#line 4470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4144, 0},
-#line 4714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4145, 0},
-#line 4395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4146, 0},
-#line 5424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4147, 1},
-#line 768 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4148, 0},
-#line 5538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4149, 0},
-#line 3885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4150, 0},
-#line 5524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4151, 0},
-#line 4535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4152, 0},
-#line 5489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4153, 0},
-#line 3520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4154, 0},
-#line 5002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4155, 0},
-#line 1609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4156, 4},
-#line 4617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4157, 0},
-#line 5431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4158, 0},
-#line 1587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4159, 4},
-#line 4905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4160, 0},
-#line 133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4161, 0},
-#line 4103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4162, 4},
-#line 4101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4163, 0},
-#line 4758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4164, 0},
-#line 5086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4165, 0},
-#line 1172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4166, 4},
-#line 4735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4167, 0},
-#line 6163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4168, 0},
-#line 5320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4169, 0},
-#line 77 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4170, 0},
-#line 6223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4171, 0},
-#line 6345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4172, 0},
-#line 5995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4173, 0},
-#line 6088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4174, 0},
-#line 4744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4175, 0},
-#line 5513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4176, 0},
-#line 5492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4177, 0},
-#line 4915 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4178, 0},
-#line 5491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4179, 0},
-#line 4654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4180, 0},
-#line 4759 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4181, 0},
-#line 5426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4182, 0},
-#line 4920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4183, 2},
-#line 4740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4184, 0},
-#line 4058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4185, 0},
-#line 558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4186, 0},
-#line 3871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4187, 0},
-#line 2531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4188, 0},
-#line 5987 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4189, 0},
-#line 2463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4190, 4},
-#line 2314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4191, 0},
-#line 6040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4192, 0},
-#line 4739 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4193, 0},
-#line 4919 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4194, 0},
-#line 5099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4195, 0},
-#line 3741 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4196, 0},
-#line 1580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4197, 4},
-#line 1125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4198, 0},
-#line 5526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4199, 0},
-#line 4913 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4200, 0},
-#line 3582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4201, 0},
-#line 5581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4202, 0},
-#line 4706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4203, 0},
-#line 6342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4204, 0},
-#line 4918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4205, 0},
-#line 5326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4206, 0},
-#line 2195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4207, 0},
-#line 2917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4208, 4},
-#line 5391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4209, 0},
-#line 2783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4210, 0},
-#line 2443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4211, 4},
-#line 5578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4212, 0},
-#line 4914 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4213, 0},
-#line 2206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4214, 4},
-#line 5388 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4215, 0},
-#line 5645 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4216, 0},
-#line 3816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4217, 0},
-#line 898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4218, 0},
-#line 4912 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4219, 0},
-#line 4516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4220, 0},
-#line 4667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4221, 0},
-#line 556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4222, 0},
-#line 5117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4223, 0},
-#line 2690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4224, 0},
-#line 5994 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4225, 0},
-#line 6310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4226, 0},
-#line 5536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4227, 0},
-#line 4958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4228, 0},
-#line 5654 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4229, 0},
-#line 3781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4230, 0},
-#line 5979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4231, 0},
-#line 5438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4232, 0},
-#line 2134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4233, 0},
-#line 3282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4234, 0},
-#line 6211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4235, 0},
-#line 5860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4236, 0},
-#line 1219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4237, 4},
-#line 5404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4238, 0},
-#line 4162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4239, 0},
-#line 5521 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4240, 0},
-#line 5508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4241, 0},
-#line 161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4242, 0},
-#line 1383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4243, 0},
-#line 6038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4244, 0},
-#line 501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4245, 4},
-#line 4911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4246, 0},
-#line 4655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4247, 0},
-#line 798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4248, 0},
-#line 2421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4249, 4},
-#line 5243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4250, 0},
-#line 4029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4251, 0},
-#line 4124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4252, 0},
-#line 5100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4253, 0},
-#line 6313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4254, 0},
-#line 4925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4255, 0},
-#line 6254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4256, 0},
-#line 4707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4257, 0},
-#line 3332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4258, 0},
-#line 1473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4259, 0},
-#line 6258 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4260, 0},
-#line 6308 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4261, 0},
-#line 5638 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4262, 0},
-#line 4834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4263, 0},
-#line 1384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4264, 0},
-#line 5894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4265, 0},
-#line 4856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4266, 0},
-#line 2127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4267, 0},
-#line 5540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4268, 0},
-#line 586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4269, 0},
-#line 4149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4270, 0},
-#line 6165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4271, 0},
-#line 6324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4272, 0},
-#line 4726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4273, 0},
-#line 6035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4274, 0},
-#line 5635 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4275, 0},
-#line 5155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4276, 0},
-#line 5323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4277, 0},
-#line 5210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4278, 0},
-#line 5208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4279, 0},
-#line 4618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4280, 0},
-#line 2084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4281, 0},
-#line 1543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4282, 0},
-#line 2224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4283, 0},
-#line 2174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4284, 0},
-#line 5576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4285, 4},
-#line 4450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4286, 0},
-#line 5051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4287, 0},
-#line 4369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4288, 0},
-#line 6126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4289, 0},
-#line 3688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4290, 0},
-#line 5655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4291, 0},
-#line 3021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4292, 0},
-#line 340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4293, 0},
-#line 4324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4294, 0},
-#line 5185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4295, 0},
-#line 1462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4296, 0},
-#line 4672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4297, 0},
-#line 4379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4298, 0},
-#line 2339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4299, 0},
-#line 5481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4300, 0},
-#line 1566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4301, 0},
-#line 5582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4302, 0},
-#line 1150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4303, 0},
-#line 2700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4304, 0},
-#line 3776 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4305, 0},
-#line 5103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4306, 0},
-#line 5065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4307, 0},
-#line 3398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4308, 0},
-#line 1120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4309, 0},
-#line 5408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4310, 0},
-#line 6327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4311, 0},
-#line 3824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4312, 0},
-#line 2172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4313, 0},
-#line 5674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4314, 0},
-#line 2722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4315, 0},
-#line 136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4316, 0},
-#line 3004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4317, 0},
-#line 4708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4318, 0},
-#line 1725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4319, 0},
-#line 3717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4320, 0},
-#line 4961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4321, 4},
-#line 4193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4322, 0},
-#line 3691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4323, 0},
-#line 3687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4324, 0},
-#line 1978 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4325, 0},
-#line 1971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4326, 0},
-#line 1967 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4327, 0},
-#line 5414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4328, 0},
-#line 3980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4329, 0},
-#line 5275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4330, 0},
-#line 1968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4331, 0},
-#line 1973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4332, 0},
-#line 3769 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4333, 0},
-#line 5632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4334, 0},
-#line 2589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4335, 0},
-#line 2591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4336, 0},
-#line 4337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4337, 0},
-#line 4820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4338, 0},
-#line 4810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4339, 0},
-#line 1975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4340, 0},
-#line 1917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4341, 0},
-#line 3862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4342, 0},
-#line 1341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4343, 0},
-#line 1820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4344, 0},
-#line 1015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4345, 0},
-#line 1819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4346, 0},
-#line 5834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4347, 0},
-#line 1879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4348, 0},
-#line 6103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4349, 0},
-#line 5197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4350, 0},
-#line 1969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4351, 0},
-#line 6269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4352, 0},
-#line 3942 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4353, 0},
-#line 1815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4354, 0},
-#line 2564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4355, 0},
-#line 619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4356, 0},
-#line 4938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4357, 0},
-#line 5222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4358, 0},
-#line 2267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4359, 0},
-#line 6101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4360, 0},
-#line 4808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4361, 0},
-#line 5292 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4362, 0},
-#line 2570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4363, 0},
-#line 5141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4364, 0},
-#line 1124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4365, 0},
-#line 4703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4366, 0},
-#line 5494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4367, 0},
-#line 2716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4368, 0},
-#line 5169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4369, 0},
-#line 1925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4370, 0},
-#line 2377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4371, 0},
-#line 1347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4372, 0},
-#line 4352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4373, 0},
-#line 216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4374, 0},
-#line 1022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4375, 0},
-#line 2535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4376, 0},
-#line 5969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4377, 0},
-#line 1821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4378, 0},
-#line 4502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4379, 0},
-#line 2189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4380, 4},
-#line 5407 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4381, 0},
-#line 1838 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4382, 0},
-#line 2598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4383, 0},
-#line 5425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4384, 0},
-#line 1262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4385, 0},
-#line 5266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4386, 0},
-#line 929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4387, 0},
-#line 5010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4388, 0},
-#line 2578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4389, 1},
-#line 3854 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4390, 0},
-#line 5457 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4391, 0},
-#line 4924 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4392, 0},
-#line 1865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4393, 0},
-#line 2279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4394, 0},
-#line 1289 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4395, 0},
-#line 1977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4396, 0},
-#line 2401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4397, 0},
-#line 2110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4398, 0},
-#line 4529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4399, 0},
-#line 2406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4400, 0},
-#line 4011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4401, 0},
-#line 2375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4402, 0},
-#line 5695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4403, 0},
-#line 5043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4404, 0},
-#line 5592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4405, 0},
-#line 367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4406, 0},
-#line 2615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4407, 0},
-#line 1959 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4408, 0},
-#line 4871 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4409, 0},
-#line 6104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4410, 0},
-#line 2588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4411, 2},
-#line 4517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4412, 0},
-#line 1311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4413, 0},
-#line 980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4414, 0},
-#line 6102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4415, 0},
-#line 1963 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4416, 0},
-#line 5246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4417, 0},
-#line 765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4418, 0},
-#line 2173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4419, 0},
-#line 1359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4420, 0},
-#line 5382 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4421, 0},
-#line 4691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4422, 0},
-#line 1814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4423, 0},
-#line 5178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4424, 0},
-#line 1961 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4425, 0},
-#line 1964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4426, 0},
-#line 3524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4427, 0},
-#line 4511 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4428, 0},
-#line 3845 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4429, 0},
-#line 2106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4430, 0},
-#line 38 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4431, 0},
-#line 6286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4432, 0},
-#line 5449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4433, 0},
-#line 5486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4434, 0},
-#line 2606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4435, 0},
-#line 3972 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4436, 0},
-#line 4689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4437, 0},
-#line 1696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4438, 0},
-#line 1904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4439, 0},
-#line 1328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4440, 0},
-#line 999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4441, 0},
-#line 5758 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4442, 0},
-#line 1818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4443, 0},
-#line 4104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4444, 0},
-#line 4969 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4445, 4},
-#line 837 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4446, 0},
-#line 4209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4447, 0},
-#line 4315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4448, 0},
-#line 1747 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4449, 0},
-#line 6311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4450, 0},
-#line 2614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4451, 0},
-#line 6353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4452, 0},
-#line 2397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4453, 0},
-#line 4690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4454, 0},
-#line 1960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4455, 0},
-#line 6307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4456, 0},
-#line 3619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4457, 0},
-#line 2597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4458, 0},
-#line 1816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4459, 0},
-#line 4505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4460, 2},
-#line 4917 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4461, 0},
-#line 5122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4462, 0},
-#line 1510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4463, 0},
-#line 486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4464, 0},
-#line 5096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4465, 0},
-#line 6249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4466, 0},
-#line 377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4467, 0},
-#line 1749 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4468, 0},
-#line 5679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4469, 0},
-#line 2025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4470, 0},
-#line 5608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4471, 0},
-#line 1285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4472, 0},
-#line 957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4473, 0},
-#line 5413 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4474, 0},
-#line 5333 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4475, 0},
-#line 1948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4476, 0},
-#line 4832 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4477, 0},
-#line 4679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4478, 0},
-#line 1965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4479, 0},
-#line 1699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4480, 0},
-#line 5098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4481, 0},
-#line 3458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4482, 0},
-#line 257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4483, 0},
-#line 5745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4484, 0},
-#line 4886 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4485, 0},
-#line 5182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4486, 0},
-#line 5670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4487, 0},
-#line 2067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4488, 0},
-#line 5520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4489, 0},
-#line 5534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4490, 0},
-#line 4590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4491, 0},
-#line 3820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4492, 0},
-#line 5839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4493, 0},
-#line 3649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4494, 0},
-#line 2107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4495, 0},
-#line 4989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4496, 0},
-#line 2350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4497, 0},
-#line 4421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4498, 0},
-#line 5525 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4499, 0},
-#line 3774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4500, 0},
-#line 3703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4501, 0},
-#line 3887 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4502, 0},
-#line 5302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4503, 0},
-#line 4881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4504, 2},
-#line 474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4505, 4},
-#line 5016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4506, 0},
-#line 5307 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4507, 0},
-#line 5316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4508, 0},
-#line 4405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4509, 0},
-#line 1358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4510, 0},
-#line 5392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4511, 0},
-#line 5402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4512, 0},
-#line 4127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4513, 0},
-#line 5145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4514, 0},
-#line 2935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4515, 0},
-#line 3793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4516, 0},
-#line 2064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4517, 0},
-#line 660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4518, 0},
-#line 5277 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4519, 0},
-#line 2604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4520, 0},
-#line 5622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4521, 0},
-#line 4851 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4522, 0},
-#line 5708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4523, 0},
-#line 2577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4524, 0},
-#line 4935 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4525, 0},
-#line 4927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4526, 0},
-#line 607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4527, 0},
-#line 6014 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4528, 0},
-#line 5381 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4529, 0},
-#line 4939 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4530, 4},
-#line 5610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4531, 0},
-#line 5050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4532, 0},
-#line 3310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4533, 0},
-#line 3351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4534, 0},
-#line 1355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4535, 0},
-#line 4634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4536, 0},
-#line 2261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4537, 0},
-#line 5443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4538, 0},
-#line 2100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4539, 0},
-#line 5384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4540, 0},
-#line 3280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4541, 0},
-#line 3721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4542, 0},
-#line 386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4543, 0},
-#line 5761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4544, 0},
-#line 2596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4545, 0},
-#line 5377 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4546, 0},
-#line 5602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4547, 0},
-#line 5892 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4548, 0},
-#line 6036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4549, 0},
-#line 5226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4550, 0},
-#line 796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4551, 0},
-#line 2580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4552, 0},
-#line 4250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4553, 0},
-#line 5112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4554, 4},
-#line 6318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4555, 0},
-#line 4252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4556, 0},
-#line 3986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4557, 0},
-#line 3597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4558, 0},
-#line 4452 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4559, 0},
-#line 2603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4560, 0},
-#line 1539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4561, 4},
-#line 4192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4562, 0},
-#line 3267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4563, 0},
-#line 4512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4564, 0},
-#line 5400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4565, 0},
-#line 4246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4566, 0},
-#line 6039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4567, 0},
-#line 4869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4568, 0},
-#line 5104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4569, 0},
-#line 4500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4570, 0},
-#line 4113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4571, 0},
-#line 1403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4572, 0},
-#line 4238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4573, 0},
-#line 571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4574, 0},
-#line 4241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4575, 0},
-#line 3036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4576, 0},
-#line 4125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4577, 0},
-#line 5162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4578, 0},
-#line 5279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4579, 0},
-#line 5085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4580, 0},
-#line 6263 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4581, 0},
-#line 4215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4582, 0},
-#line 648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4583, 0},
-#line 766 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4584, 0},
-#line 4424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4585, 0},
-#line 1651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4586, 0},
-#line 2731 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4587, 0},
-#line 2440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4588, 4},
-#line 5049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4589, 0},
-#line 1540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4590, 4},
-#line 4245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4591, 0},
-#line 4211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4592, 0},
-#line 4226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4593, 0},
-#line 4240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4594, 0},
-#line 1515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4595, 0},
-#line 5567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4596, 0},
-#line 5114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4597, 0},
-#line 5360 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4598, 0},
-#line 4418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4599, 0},
-#line 3659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4600, 0},
-#line 5633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4601, 0},
-#line 4123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4602, 0},
-#line 653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4603, 0},
-#line 2376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4604, 0},
-#line 2782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4605, 0},
-#line 4453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4606, 0},
-#line 2458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4607, 4},
-#line 5620 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4608, 0},
-#line 1954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4609, 0},
-#line 91 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4610, 0},
-#line 6156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4611, 0},
-#line 4247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4612, 0},
-#line 1516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4613, 0},
-#line 5910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4614, 0},
-#line 2572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4615, 0},
-#line 4817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4616, 0},
-#line 4442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4617, 0},
-#line 3641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4618, 0},
-#line 3576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4619, 0},
-#line 839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4620, 0},
-#line 4778 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4621, 0},
-#line 3773 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4622, 0},
-#line 1113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4623, 0},
-#line 5264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4624, 0},
-#line 2309 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4625, 0},
-#line 4217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4626, 0},
-#line 2575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4627, 0},
-#line 4171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4628, 0},
-#line 2568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4629, 0},
-#line 1646 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4630, 0},
-#line 5395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4631, 0},
-#line 108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4632, 0},
-#line 5368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4633, 0},
-#line 4427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4634, 0},
-#line 4155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4635, 0},
-#line 4239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4636, 0},
-#line 4249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4637, 0},
-#line 2234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4638, 0},
-#line 1189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4639, 0},
-#line 1753 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4640, 0},
-#line 4027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4641, 0},
-#line 5948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4642, 4},
-#line 4678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4643, 4},
-#line 1802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4644, 0},
-#line 2142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4645, 0},
-#line 2733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4646, 0},
-#line 2266 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4647, 0},
-#line 3782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4648, 0},
-#line 5495 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4649, 0},
-#line 4181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4650, 0},
-#line 2595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4651, 0},
-#line 5349 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4652, 0},
-#line 2424 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4653, 4},
-#line 4931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4654, 0},
-#line 2074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4655, 0},
-#line 1483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4656, 0},
-#line 2898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4657, 0},
-#line 2937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4658, 0},
-#line 1537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4659, 0},
-#line 4172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4660, 0},
-#line 4863 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4661, 0},
-#line 3971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4662, 0},
-#line 4422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4663, 0},
-#line 5298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4664, 0},
-#line 1105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4665, 0},
-#line 2691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4666, 0},
-#line 5707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4667, 0},
-#line 5523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4668, 0},
-#line 454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4669, 0},
-#line 4445 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4670, 0},
-#line 1106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4671, 0},
-#line 176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4672, 0},
-#line 5004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4673, 0},
-#line 5290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4674, 0},
-#line 2666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4675, 0},
-#line 5233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4676, 0},
-#line 1181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4677, 0},
-#line 4971 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4678, 4},
-#line 2712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4679, 0},
-#line 5353 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4680, 0},
-#line 5000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4681, 4},
-#line 324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4682, 0},
-#line 5356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4683, 0},
-#line 4117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4684, 0},
-#line 1223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4685, 0},
-#line 2141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4686, 0},
-#line 4663 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4687, 0},
-#line 4936 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4688, 0},
-#line 4839 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4689, 0},
-#line 3592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4690, 0},
-#line 5628 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4691, 0},
-#line 5945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4692, 0},
-#line 131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4693, 0},
-#line 4281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4694, 0},
-#line 1357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4695, 0},
-#line 5376 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4696, 0},
-#line 408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4697, 0},
-#line 4746 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4698, 0},
-#line 5716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4699, 0},
-#line 5512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4700, 0},
-#line 4934 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4701, 0},
-#line 5615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4702, 0},
-#line 6267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4703, 0},
-#line 4745 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4704, 0},
-#line 5393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4705, 0},
-#line 5198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4706, 4},
-#line 3227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4707, 0},
-#line 3226 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4708, 0},
-#line 1789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4709, 0},
-#line 1342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4710, 0},
-#line 5956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4711, 0},
-#line 1016 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4712, 0},
-#line 3991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4713, 0},
-#line 5084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4714, 0},
-#line 5519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4715, 0},
-#line 1880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4716, 0},
-#line 5179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4717, 0},
-#line 1303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4718, 0},
-#line 974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4719, 0},
-#line 3234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4720, 0},
-#line 2648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4721, 0},
-#line 3075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4722, 0},
-#line 3076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4723, 0},
-#line 3215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4724, 0},
-#line 300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4725, 0},
-#line 2600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4726, 0},
-#line 5709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4727, 0},
-#line 3228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4728, 0},
-#line 3300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4729, 0},
-#line 5350 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4730, 0},
-#line 4283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4731, 0},
-#line 1356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4732, 0},
-#line 2524 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4733, 0},
-#line 3104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4734, 0},
-#line 5389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4735, 0},
-#line 394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4736, 0},
-#line 6100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4737, 0},
-#line 4279 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4738, 0},
-#line 3132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4739, 0},
-#line 4425 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4740, 0},
-#line 5029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4741, 0},
-#line 3197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4742, 0},
-#line 2670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4743, 0},
-#line 5437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4744, 0},
-#line 3867 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4745, 0},
-#line 5983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4746, 0},
-#line 4950 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4747, 0},
-#line 2379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4748, 0},
-#line 3253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4749, 0},
-#line 4878 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4750, 0},
-#line 3244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4751, 0},
-#line 5338 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4752, 0},
-#line 4393 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4753, 0},
-#line 5421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4754, 0},
-#line 2601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4755, 0},
-#line 3242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4756, 0},
-#line 3027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4757, 0},
-#line 4190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4758, 0},
-#line 2047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4759, 0},
-#line 802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4760, 0},
-#line 3233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4761, 0},
-#line 1891 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4762, 0},
-#line 1312 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4763, 0},
-#line 4930 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4764, 0},
-#line 982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4765, 0},
-#line 410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4766, 0},
-#line 5297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4767, 0},
-#line 1447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4768, 0},
-#line 5364 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4769, 0},
-#line 3144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4770, 0},
-#line 6017 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4771, 0},
-#line 3803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4772, 0},
-#line 3111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4773, 0},
-#line 1461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4774, 0},
-#line 5037 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4775, 0},
-#line 5479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4776, 0},
-#line 604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4777, 0},
-#line 3077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4778, 0},
-#line 5257 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4779, 0},
-#line 2395 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4780, 0},
-#line 3115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4781, 0},
-#line 4242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4782, 0},
-#line 2571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4783, 0},
-#line 830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4784, 0},
-#line 5686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4785, 0},
-#line 4975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4786, 0},
-#line 794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4787, 0},
-#line 3089 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4788, 0},
-#line 3190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4789, 0},
-#line 5365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4790, 0},
-#line 3250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4791, 0},
-#line 5403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4792, 0},
-#line 3201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4793, 0},
-#line 275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4794, 0},
-#line 194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4795, 0},
-#line 2574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4796, 0},
-#line 4005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4797, 0},
-#line 3770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4798, 0},
-#line 5342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4799, 0},
-#line 3602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4800, 0},
-#line 3123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4801, 0},
-#line 4243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4802, 0},
-#line 5366 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4803, 0},
-#line 1713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4804, 0},
-#line 5505 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4805, 0},
-#line 3125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4806, 0},
-#line 5123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4807, 0},
-#line 3196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4808, 0},
-#line 4414 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4809, 0},
-#line 2995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4810, 0},
-#line 5080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4811, 0},
-#line 5989 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4812, 0},
-#line 3472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4813, 0},
-#line 3094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4814, 0},
-#line 6351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4815, 0},
-#line 3199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4816, 0},
-#line 3444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4817, 0},
-#line 337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4818, 0},
-#line 2855 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4819, 0},
-#line 3328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4820, 0},
-#line 5383 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4821, 0},
-#line 3174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4822, 0},
-#line 1185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4823, 4},
-#line 3239 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4824, 0},
-#line 3142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4825, 0},
-#line 2378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4826, 0},
-#line 2860 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4827, 0},
-#line 3173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4828, 0},
-#line 479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4829, 0},
-#line 3178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4830, 0},
-#line 2903 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4831, 0},
-#line 2384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4832, 0},
-#line 3143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4833, 0},
-#line 5617 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4834, 0},
-#line 4750 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4835, 0},
-#line 3207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4836, 0},
-#line 6336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4837, 0},
-#line 3168 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4838, 0},
-#line 3169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4839, 0},
-#line 804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4840, 0},
-#line 3164 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4841, 0},
-#line 5057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4842, 0},
-#line 4712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4843, 0},
-#line 4399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4844, 0},
-#line 5358 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4845, 0},
-#line 2562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4846, 0},
-#line 3166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4847, 0},
-#line 2831 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4848, 0},
-#line 3179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4849, 0},
-#line 5034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4850, 0},
-#line 4365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4851, 0},
-#line 4126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4852, 0},
-#line 3155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4853, 0},
-#line 431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4854, 0},
-#line 1659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4855, 0},
-#line 591 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4856, 0},
-#line 3110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4857, 0},
-#line 3096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4858, 0},
-#line 3575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4859, 0},
-#line 5422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4860, 0},
-#line 2931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4861, 0},
-#line 437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4862, 0},
-#line 5367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4863, 0},
-#line 4133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4864, 0},
-#line 5895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4865, 0},
-#line 3165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4866, 0},
-#line 2925 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4867, 0},
-#line 5234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4868, 0},
-#line 554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4869, 4},
-#line 781 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4870, 0},
-#line 3446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4871, 0},
-#line 1740 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4872, 0},
-#line 3151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4873, 0},
-#line 6081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4874, 0},
-#line 4373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4875, 0},
-#line 4578 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4876, 0},
-#line 2948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4877, 0},
-#line 3177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4878, 0},
-#line 2770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4879, 0},
-#line 5109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4880, 0},
-#line 4406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4881, 0},
-#line 3025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4882, 0},
-#line 3302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4883, 0},
-#line 3162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4884, 0},
-#line 3161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4885, 0},
-#line 3149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4886, 0},
-#line 3150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4887, 0},
-#line 3290 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4888, 0},
-#line 4304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4889, 0},
-#line 2754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4890, 0},
-#line 297 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4891, 0},
-#line 6033 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4892, 0},
-#line 4448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4893, 0},
-#line 5347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4894, 0},
-#line 3203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4895, 0},
-#line 2970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4896, 0},
-#line 4174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4897, 0},
-#line 3158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4898, 0},
-#line 5629 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4899, 0},
-#line 5856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4900, 0},
-#line 3137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4901, 0},
-#line 3136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4902, 0},
-#line 6331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4903, 0},
-#line 3134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4904, 0},
-#line 3254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4905, 0},
-#line 3156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4906, 0},
-#line 3361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4907, 0},
-#line 1688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4908, 0},
-#line 3157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4909, 0},
-#line 3176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4910, 0},
-#line 3135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4911, 0},
-#line 4227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4912, 0},
-#line 4330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4913, 0},
-#line 2140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4914, 0},
-#line 3065 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4915, 0},
-#line 2819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4916, 2},
-#line 4774 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4917, 0},
-#line 3493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4918, 0},
-#line 4264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4919, 0},
-#line 5748 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4920, 0},
-#line 3195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4921, 0},
-#line 5319 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4922, 0},
-#line 3723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4923, 0},
-#line 2911 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4924, 0},
-#line 2534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4925, 0},
-#line 2881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4926, 0},
-#line 3175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4927, 0},
-#line 342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4928, 0},
-#line 3159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4929, 0},
-#line 4146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4930, 0},
-#line 4348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4931, 0},
-#line 3183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4932, 0},
-#line 5344 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4933, 0},
-#line 3692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4934, 0},
-#line 4384 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4935, 0},
-#line 2147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4936, 0},
-#line 3087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4937, 0},
-#line 72 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4938, 0},
-#line 5341 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4939, 0},
-#line 4282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4940, 0},
-#line 6054 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4941, 0},
-#line 2004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4942, 0},
-#line 4498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4943, 0},
-#line 4898 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4944, 4},
-#line 3116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4945, 0},
-#line 5504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4946, 0},
-#line 166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4947, 0},
-#line 6253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4948, 0},
-#line 3184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4949, 0},
-#line 3722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4950, 0},
-#line 4586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4951, 0},
-#line 5300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4952, 0},
-#line 869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4953, 0},
-#line 4830 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4954, 0},
-#line 3246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4955, 0},
-#line 63 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4956, 0},
-#line 66 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4957, 0},
-#line 4300 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4958, 0},
-#line 5305 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4959, 0},
-#line 4789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4960, 4},
-#line 5345 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4961, 0},
-#line 3153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4962, 0},
-#line 2218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4963, 0},
-#line 1118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4964, 0},
-#line 59 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4965, 0},
-#line 2956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4966, 0},
-#line 55 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4967, 0},
-#line 4107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4968, 0},
-#line 4078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4969, 0},
-#line 60 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4970, 0},
-#line 62 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4971, 0},
-#line 6245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4972, 0},
-#line 40 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4973, 0},
-#line 4643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4974, 4},
-#line 5030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4975, 0},
-#line 3129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4976, 0},
-#line 3148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4977, 0},
-#line 41 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4978, 0},
-#line 54 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4979, 0},
-#line 46 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4980, 0},
-#line 4560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4981, 0},
-#line 45 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4982, 0},
-#line 3082 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4983, 0},
-#line 2804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4984, 0},
-#line 5630 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4985, 0},
-#line 4313 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4986, 0},
-#line 6260 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4987, 0},
-#line 3931 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4988, 0},
-#line 44 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4989, 0},
-#line 4476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4990, 0},
-#line 2799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4991, 0},
-#line 2543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4992, 0},
-#line 6378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4993, 0},
-#line 3590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4994, 0},
-#line 3761 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4995, 0},
-#line 3091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4996, 0},
-#line 5270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4997, 0},
-#line 3995 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4998, 0},
-#line 261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str4999, 0},
-#line 3363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5000, 0},
-#line 52 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5001, 0},
-#line 5012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5002, 0},
-#line 4497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5003, 0},
-#line 65 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5004, 0},
-#line 5435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5005, 0},
-#line 5451 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5006, 0},
-#line 5324 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5007, 0},
-#line 2579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5008, 0},
-#line 3938 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5009, 0},
-#line 4465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5010, 0},
-#line 43 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5011, 0},
-#line 3090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5012, 4},
-#line 5483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5013, 0},
-#line 5405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5014, 0},
-#line 1817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5015, 0},
-#line 3853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5016, 0},
-#line 6046 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5017, 0},
-#line 4119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5018, 0},
-#line 1193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5019, 0},
-#line 3834 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5020, 0},
-#line 3733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5021, 0},
-#line 6250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5022, 0},
-#line 2298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5023, 0},
-#line 701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5024, 0},
-#line 53 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5025, 0},
-#line 3191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5026, 0},
-#line 4437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5027, 0},
-#line 4142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5028, 0},
-#line 3001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5029, 0},
-#line 3603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5030, 0},
-#line 4530 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5031, 0},
-#line 192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5032, 0},
-#line 4130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5033, 0},
-#line 2586 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5034, 0},
-#line 2613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5035, 0},
-#line 6034 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5036, 0},
-#line 4244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5037, 0},
-#line 4229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5038, 0},
-#line 1708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5039, 0},
-#line 4642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5040, 0},
-#line 3900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5041, 0},
-#line 2877 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5042, 0},
-#line 5374 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5043, 0},
-#line 4254 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5044, 0},
-#line 415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5045, 0},
-#line 42 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5046, 0},
-#line 3078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5047, 0},
-#line 4423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5048, 0},
-#line 402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5049, 0},
-#line 1188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5050, 0},
-#line 4118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5051, 0},
-#line 5673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5052, 0},
-#line 3918 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5053, 0},
-#line 3119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5054, 0},
-#line 64 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5055, 0},
-#line 1419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5056, 0},
-#line 3544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5057, 0},
-#line 4404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5058, 0},
-#line 3642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5059, 0},
-#line 1670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5060, 0},
-#line 1700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5061, 0},
-#line 2655 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5062, 0},
-#line 2922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5063, 0},
-#line 401 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5064, 0},
-#line 1783 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5065, 0},
-#line 1282 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5066, 0},
-#line 5371 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5067, 0},
-#line 952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5068, 0},
-#line 1235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5069, 0},
-#line 3788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5070, 0},
-#line 2816 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5071, 0},
-#line 5518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5072, 0},
-#line 4420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5073, 0},
-#line 5330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5074, 0},
-#line 4234 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5075, 0},
-#line 2235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5076, 0},
-#line 3809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5077, 0},
-#line 2486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5078, 4},
-#line 3000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5079, 0},
-#line 4400 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5080, 0},
-#line 3874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5081, 0},
-#line 3754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5082, 0},
-#line 5660 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5083, 0},
-#line 5397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5084, 0},
-#line 5094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5085, 0},
-#line 2283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5086, 0},
-#line 772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5087, 0},
-#line 5458 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5088, 0},
-#line 4198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5089, 0},
-#line 3186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5090, 0},
-#line 3127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5091, 0},
-#line 4819 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5092, 0},
-#line 2342 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5093, 0},
-#line 4499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5094, 0},
-#line 879 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5095, 4},
-#line 5579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5096, 0},
-#line 4841 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5097, 0},
-#line 3725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5098, 0},
-#line 3734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5099, 0},
-#line 5064 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5100, 0},
-#line 2723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5101, 0},
-#line 880 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5102, 0},
-#line 4140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5103, 0},
-#line 3187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5104, 0},
-#line 4151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5105, 0},
-#line 1134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5106, 0},
-#line 1561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5107, 0},
-#line 5641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5108, 0},
-#line 5976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5109, 0},
-#line 5965 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5110, 0},
-#line 6169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5111, 0},
-#line 3100 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5112, 0},
-#line 5332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5113, 0},
-#line 4397 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5114, 0},
-#line 5843 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5115, 0},
-#line 5510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5116, 0},
-#line 2715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5117, 0},
-#line 5743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5118, 0},
-#line 4704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5119, 0},
-#line 1133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5120, 4},
-#line 6205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5121, 0},
-#line 3102 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5122, 0},
-#line 5522 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5123, 0},
-#line 2809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5124, 0},
-#line 2544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5125, 0},
-#line 3787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5126, 0},
-#line 5379 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5127, 0},
-#line 4738 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5128, 0},
-#line 6148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5129, 0},
-#line 1976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5130, 0},
-#line 4153 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5131, 0},
-#line 3534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5132, 0},
-#line 5369 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5133, 0},
-#line 572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5134, 0},
-#line 4121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5135, 0},
-#line 4232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5136, 0},
-#line 6091 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5137, 0},
-#line 6152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5138, 0},
-#line 5060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5139, 0},
-#line 2291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5140, 0},
-#line 5361 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5141, 0},
-#line 1191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5142, 0},
-#line 4409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5143, 0},
-#line 4286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5144, 0},
-#line 1162 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5145, 0},
-#line 3434 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5146, 0},
-#line 3551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5147, 0},
-#line 4370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5148, 0},
-#line 2996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5149, 0},
-#line 4380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5150, 0},
-#line 6131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5151, 0},
-#line 4114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5152, 0},
-#line 2743 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5153, 0},
-#line 2489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5154, 4},
-#line 4214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5155, 0},
-#line 47 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5156, 0},
-#line 1422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5157, 4},
-#line 5517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5158, 0},
-#line 3536 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5159, 0},
-#line 5642 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5160, 0},
-#line 4661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5161, 0},
-#line 50 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5162, 0},
-#line 4278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5163, 0},
-#line 6144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5164, 0},
-#line 56 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5165, 0},
-#line 6025 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5166, 0},
-#line 61 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5167, 0},
-#line 4176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5168, 0},
-#line 4436 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5169, 0},
-#line 5659 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5170, 0},
-#line 3999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5171, 0},
-#line 49 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5172, 0},
-#line 5643 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5173, 0},
-#line 2850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5174, 0},
-#line 6023 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5175, 0},
-#line 4224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5176, 0},
-#line 1464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5177, 0},
-#line 5035 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5178, 0},
-#line 6042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5179, 0},
-#line 4483 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5180, 0},
-#line 6024 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5181, 0},
-#line 4884 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5182, 0},
-#line 3299 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5183, 0},
-#line 5702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5184, 0},
-#line 4539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5185, 0},
-#line 6022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5186, 0},
-#line 1190 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5187, 0},
-#line 3193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5188, 4},
-#line 2695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5189, 0},
-#line 4267 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5190, 0},
-#line 4150 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5191, 0},
-#line 3975 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5192, 0},
-#line 2907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5193, 0},
-#line 4673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5194, 0},
-#line 5499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5195, 0},
-#line 3340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5196, 0},
-#line 48 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5197, 0},
-#line 1844 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5198, 0},
-#line 1265 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5199, 0},
-#line 933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5200, 0},
-#line 2947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5201, 0},
-#line 728 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5202, 0},
-#line 354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5203, 0},
-#line 405 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5204, 4},
-#line 2835 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5205, 0},
-#line 2808 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5206, 0},
-#line 3172 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5207, 0},
-#line 4083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5208, 0},
-#line 2807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5209, 0},
-#line 3212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5210, 0},
-#line 4157 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5211, 0},
-#line 1420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5212, 4},
-#line 4136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5213, 0},
-#line 4233 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5214, 4},
-#line 2083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5215, 0},
-#line 1421 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5216, 4},
-#line 5585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5217, 0},
-#line 5095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5218, 0},
-#line 3167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5219, 0},
-#line 4251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5220, 0},
-#line 5432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5221, 0},
-#line 3221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5222, 0},
-#line 5325 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5223, 0},
-#line 4694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5224, 0},
-#line 3949 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5225, 0},
-#line 6030 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5226, 0},
-#line 1142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5227, 0},
-#line 2814 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5228, 0},
-#line 3755 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5229, 0},
-#line 4713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5230, 4},
-#line 4075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5231, 0},
-#line 1624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5232, 4},
-#line 6005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5233, 0},
-#line 4897 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5234, 0},
-#line 4003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5235, 0},
-#line 6129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5236, 0},
-#line 4159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5237, 0},
-#line 2821 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5238, 0},
-#line 3247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5239, 0},
-#line 5488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5240, 0},
-#line 4122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5241, 0},
-#line 1423 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5242, 4},
-#line 5580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5243, 0},
-#line 2673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5244, 0},
-#line 1532 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5245, 4},
-#line 4316 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5246, 0},
-#line 2977 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5247, 0},
-#line 1199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5248, 4},
-#line 4426 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5249, 0},
-#line 3545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5250, 0},
-#line 5281 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5251, 0},
-#line 3807 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5252, 0},
-#line 2471 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5253, 4},
-#line 6314 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5254, 0},
-#line 4670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5255, 0},
-#line 6198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5256, 0},
-#line 6192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5257, 0},
-#line 6000 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5258, 0},
-#line 5981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5259, 0},
-#line 2448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5260, 4},
-#line 2446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5261, 4},
-#line 3359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5262, 0},
-#line 5506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5263, 0},
-#line 5036 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5264, 0},
-#line 4216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5265, 4},
-#line 4210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5266, 0},
-#line 306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5267, 0},
-#line 5406 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5268, 0},
-#line 3213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5269, 0},
-#line 5616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5270, 0},
-#line 317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5271, 0},
-#line 3546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5272, 0},
-#line 6184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5273, 0},
-#line 6132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5274, 0},
-#line 1772 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5275, 0},
-#line 1244 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5276, 0},
-#line 881 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5277, 0},
-#line 6026 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5278, 0},
-#line 1218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5279, 4},
-#line 3427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5280, 0},
-#line 4060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5281, 0},
-#line 6063 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5282, 0},
-#line 612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5283, 4},
-#line 4225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5284, 0},
-#line 2812 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5285, 0},
-#line 5626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5286, 0},
-#line 6062 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5287, 0},
-#line 3577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5288, 0},
-#line 4979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5289, 4},
-#line 6193 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5290, 0},
-#line 2573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5291, 0},
-#line 3893 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5292, 0},
-#line 1047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5293, 0},
-#line 5329 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5294, 0},
-#line 2468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5295, 4},
-#line 2459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5296, 4},
-#line 4255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5297, 0},
-#line 3093 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5298, 0},
-#line 1608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5299, 4},
-#line 4158 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5300, 0},
-#line 4813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5301, 0},
-#line 4980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5302, 4},
-#line 2125 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5303, 0},
-#line 5782 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5304, 0},
-#line 2449 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5305, 4},
-#line 5242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5306, 0},
-#line 6187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5307, 0},
-#line 1073 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5308, 0},
-#line 5788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5309, 0},
-#line 6161 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5310, 0},
-#line 2488 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5311, 4},
-#line 1574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5312, 4},
-#line 6213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5313, 0},
-#line 5343 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5314, 0},
-#line 4473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5315, 0},
-#line 2824 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5316, 0},
-#line 3122 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5317, 0},
-#line 5340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5318, 0},
-#line 443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5319, 4},
-#line 4145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5320, 0},
-#line 4187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5321, 0},
-#line 1534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5322, 4},
-#line 1799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5323, 4},
-#line 5355 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5324, 0},
-#line 3291 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5325, 0},
-#line 3902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5326, 0},
-#line 1171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5327, 4},
-#line 5076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5328, 0},
-#line 4022 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5329, 0},
-#line 2823 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5330, 0},
-#line 1213 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5331, 4},
-#line 6186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5332, 0},
-#line 5363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5333, 0},
-#line 1170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5334, 4},
-#line 6087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5335, 0},
-#line 4577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5336, 0},
-#line 104 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5337, 0},
-#line 6015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5338, 0},
-#line 2075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5339, 0},
-#line 6019 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5340, 0},
-#line 4622 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5341, 0},
-#line 5688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5342, 0},
-#line 71 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5343, 0},
-#line 3756 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5344, 0},
-#line 5625 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5345, 0},
-#line 2611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5346, 0},
-#line 3204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5347, 0},
-#line 1212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5348, 4},
-#line 2904 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5349, 0},
-#line 1601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5350, 4},
-#line 5287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5351, 0},
-#line 4788 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5352, 4},
-#line 4484 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5353, 0},
-#line 5337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5354, 0},
-#line 6075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5355, 0},
-#line 51 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5356, 0},
-#line 1208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5357, 4},
-#line 5354 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5358, 0},
-#line 472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5359, 0},
-#line 5359 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5360, 0},
-#line 4394 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5361, 0},
-#line 2490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5362, 4},
-#line 5228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5363, 0},
-#line 1637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5364, 0},
-#line 3392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5365, 0},
-#line 6181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5366, 0},
-#line 5509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5367, 0},
-#line 1389 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5368, 4},
-#line 411 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5369, 0},
-#line 5118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5370, 0},
-#line 1096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5371, 0},
-#line 2403 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5372, 0},
-#line 2518 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5373, 4},
-#line 1038 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5374, 0},
-#line 4885 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5375, 0},
-#line 1932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5376, 0},
-#line 3211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5377, 0},
-#line 6086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5378, 0},
-#line 2020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5379, 0},
-#line 4632 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5380, 0},
-#line 4491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5381, 0},
-#line 5996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5382, 0},
-#line 3181 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5383, 0},
-#line 286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5384, 0},
-#line 4461 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5385, 0},
-#line 1600 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5386, 4},
-#line 4533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5387, 0},
-#line 500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5388, 4},
-#line 4570 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5389, 0},
-#line 4182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5390, 0},
-#line 3666 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5391, 0},
-#line 5829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5392, 0},
-#line 5229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5393, 0},
-#line 1598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5394, 4},
-#line 378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5395, 0},
-#line 4985 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5396, 0},
-#line 5822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5397, 0},
-#line 499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5398, 4},
-#line 3320 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5399, 0},
-#line 1119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5400, 0},
-#line 1944 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5401, 0},
-#line 2765 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5402, 0},
-#line 5502 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5403, 0},
-#line 1178 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5404, 0},
-#line 5385 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5405, 0},
-#line 4571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5406, 0},
-#line 4572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5407, 0},
-#line 4992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5408, 0},
-#line 4948 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5409, 0},
-#line 3083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5410, 0},
-#line 2474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5411, 4},
-#line 2478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5412, 4},
-#line 4148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5413, 0},
-#line 5852 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5414, 0},
-#line 6352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5415, 0},
-#line 4402 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5416, 0},
-#line 1210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5417, 4},
-#line 1599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5418, 4},
-#line 5572 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5419, 0},
-#line 4489 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5420, 0},
-#line 4872 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5421, 0},
-#line 5474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5422, 0},
-#line 3275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5423, 0},
-#line 4981 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5424, 4},
-#line 2796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5425, 0},
-#line 5729 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5426, 0},
-#line 3232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5427, 0},
-#line 4137 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5428, 0},
-#line 4688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5429, 0},
-#line 5500 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5430, 0},
-#line 4194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5431, 0},
-#line 2908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5432, 0},
-#line 5999 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5433, 0},
-#line 1596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5434, 4},
-#line 4573 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5435, 0},
-#line 6121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5436, 0},
-#line 3790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5437, 0},
-#line 6108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5438, 0},
-#line 2435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5439, 4},
-#line 6111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5440, 0},
-#line 4443 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5441, 0},
-#line 5467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5442, 0},
-#line 5469 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5443, 0},
-#line 574 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5444, 0},
-#line 5493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5445, 0},
-#line 5470 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5446, 0},
-#line 1209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5447, 4},
-#line 1512 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5448, 0},
-#line 1945 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5449, 0},
-#line 6174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5450, 0},
-#line 1597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5451, 4},
-#line 5462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5452, 0},
-#line 641 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5453, 0},
-#line 5652 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5454, 0},
-#line 5653 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5455, 0},
-#line 3813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5456, 0},
-#line 2422 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5457, 4},
-#line 6090 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5458, 0},
-#line 5069 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5459, 0},
-#line 6283 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5460, 0},
-#line 5456 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5461, 0},
-#line 3146 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5462, 0},
-#line 5419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5463, 0},
-#line 6136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5464, 0},
-#line 5528 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5465, 0},
-#line 4295 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5466, 0},
-#line 6113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5467, 0},
-#line 6232 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5468, 0},
-#line 5430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5469, 0},
-#line 6175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5470, 0},
-#line 4084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5471, 0},
-#line 5372 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5472, 0},
-#line 4173 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5473, 0},
-#line 93 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5474, 0},
-#line 2464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5475, 4},
-#line 4447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5476, 0},
-#line 3549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5477, 0},
-#line 5223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5478, 0},
-#line 3539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5479, 0},
-#line 5214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5480, 0},
-#line 4197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5481, 0},
-#line 6268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5482, 0},
-#line 3205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5483, 0},
-#line 3542 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5484, 0},
-#line 2593 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5485, 0},
-#line 3529 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5486, 0},
-#line 2437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5487, 4},
-#line 92 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5488, 0},
-#line 1123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5489, 0},
-#line 5230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5490, 0},
-#line 3976 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5491, 0},
-#line 564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5492, 0},
-#line 4916 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5493, 0},
-#line 6080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5494, 0},
-#line 1143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5495, 0},
-#line 5463 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5496, 0},
-#line 6176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5497, 0},
-#line 2565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5498, 0},
-#line 4492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5499, 0},
-#line 2476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5500, 4},
-#line 4504 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5501, 0},
-#line 5464 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5502, 0},
-#line 1754 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5503, 0},
-#line 5041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5504, 0},
-#line 1204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5505, 4},
-#line 5647 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5506, 0},
-#line 5651 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5507, 0},
-#line 5744 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5508, 0},
-#line 5415 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5509, 0},
-#line 5648 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5510, 0},
-#line 5986 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5511, 0},
-#line 128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5512, 0},
-#line 6306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5513, 0},
-#line 6210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5514, 0},
-#line 5335 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5515, 0},
-#line 103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5516, 0},
-#line 2096 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5517, 0},
-#line 3220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5518, 0},
-#line 201 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5519, 0},
-#line 5490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5520, 0},
-#line 637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5521, 0},
-#line 4580 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5522, 0},
-#line 2243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5523, 0},
-#line 5003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5524, 0},
-#line 387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5525, 0},
-#line 6275 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5526, 0},
-#line 2822 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5527, 0},
-#line 5927 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5528, 0},
-#line 4982 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5529, 4},
-#line 6119 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5530, 0},
-#line 5199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5531, 0},
-#line 6029 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5532, 0},
-#line 5795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5533, 0},
-#line 6347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5534, 0},
-#line 5501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5535, 0},
-#line 5322 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5536, 0},
-#line 4546 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5537, 0},
-#line 4487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5538, 0},
-#line 4639 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5539, 0},
-#line 5032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5540, 0},
-#line 1179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5541, 0},
-#line 4576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5542, 0},
-#line 4853 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5543, 0},
-#line 67 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5544, 0},
-#line 5196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5545, 0},
-#line 5896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5546, 0},
-#line 4626 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5547, 0},
-#line 2920 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5548, 0},
-#line 2906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5549, 0},
-#line 4544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5550, 0},
-#line 1131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5551, 0},
-#line 4490 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5552, 0},
-#line 1966 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5553, 0},
-#line 4624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5554, 0},
-#line 1174 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5555, 4},
-#line 5328 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5556, 0},
-#line 2820 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5557, 0},
-#line 5133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5558, 0},
-#line 4850 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5559, 0},
-#line 4519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5560, 0},
-#line 5261 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5561, 0},
-#line 3870 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5562, 0},
-#line 5331 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5563, 0},
-#line 3365 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5564, 0},
-#line 5072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5565, 0},
-#line 2076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5566, 0},
-#line 4859 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5567, 0},
-#line 5160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5568, 0},
-#line 2431 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5569, 4},
-#line 5442 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5570, 0},
-#line 793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5571, 0},
-#line 3108 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5572, 0},
-#line 3960 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5573, 0},
-#line 3101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5574, 0},
-#line 5908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5575, 0},
-#line 4763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5576, 0},
-#line 6221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5577, 0},
-#line 4460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5578, 0},
-#line 4538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5579, 0},
-#line 2801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5580, 0},
-#line 2517 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5581, 4},
-#line 4212 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5582, 0},
-#line 4633 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5583, 0},
-#line 5362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5584, 0},
-#line 4548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5585, 0},
-#line 4501 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5586, 0},
-#line 3535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5587, 0},
-#line 3810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5588, 0},
-#line 2447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5589, 4},
-#line 129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5590, 0},
-#line 123 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5591, 0},
-#line 5476 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5592, 0},
-#line 5606 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5593, 0},
-#line 5444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5594, 0},
-#line 5571 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5595, 0},
-#line 1138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5596, 0},
-#line 6229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5597, 0},
-#line 5339 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5598, 0},
-#line 6179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5599, 0},
-#line 5657 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5600, 0},
-#line 649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5601, 0},
-#line 5278 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5602, 0},
-#line 5473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5603, 0},
-#line 5357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5604, 0},
-#line 5418 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5605, 0},
-#line 5047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5606, 0},
-#line 3147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5607, 0},
-#line 555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5608, 0},
-#line 2818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5609, 0},
-#line 4610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5610, 0},
-#line 5346 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5611, 0},
-#line 2810 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5612, 0},
-#line 2815 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5613, 0},
-#line 1215 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5614, 4},
-#line 4154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5615, 0},
-#line 2777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5616, 0},
-#line 5031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5617, 0},
-#line 5001 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5618, 0},
-#line 74 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5619, 0},
-#line 6043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5620, 0},
-#line 5637 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5621, 0},
-#line 5375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5622, 0},
-#line 4367 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5623, 0},
-#line 4983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5624, 4},
-#line 2439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5625, 4},
-#line 4557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5626, 0},
-#line 6228 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5627, 0},
-#line 3548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5628, 0},
-#line 5015 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5629, 0},
-#line 4432 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5630, 0},
-#line 6337 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5631, 0},
-#line 5315 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5632, 0},
-#line 2438 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5633, 4},
-#line 3955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5634, 0},
-#line 4375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5635, 0},
-#line 73 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5636, 0},
-#line 5569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5637, 0},
-#line 6304 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5638, 0},
-#line 5450 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5639, 0},
-#line 262 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5640, 0},
-#line 5650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5641, 0},
-#line 2454 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5642, 4},
-#line 4276 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5643, 0},
-#line 1149 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5644, 0},
-#line 4179 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5645, 0},
-#line 3306 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5646, 0},
-#line 3587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5647, 0},
-#line 5453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5648, 0},
-#line 6191 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5649, 0},
-#line 4569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5650, 0},
-#line 2791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5651, 0},
-#line 2802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5652, 0},
-#line 6340 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5653, 0},
-#line 1205 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5654, 4},
-#line 6357 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5655, 4},
-#line 2269 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5656, 0},
-#line 5929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5657, 0},
-#line 4332 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5658, 0},
-#line 6051 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5659, 0},
-#line 4466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5660, 0},
-#line 3953 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5661, 0},
-#line 5649 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5662, 0},
-#line 5387 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5663, 0},
-#line 2347 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5664, 0},
-#line 6130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5665, 0},
-#line 1216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5666, 4},
-#line 3057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5667, 0},
-#line 1141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5668, 0},
-#line 5665 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5669, 0},
-#line 4908 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5670, 0},
-#line 4351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5671, 0},
-#line 4910 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5672, 0},
-#line 3856 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5673, 0},
-#line 4520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5674, 0},
-#line 3068 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5675, 0},
-#line 3550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5676, 0},
-#line 4559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5677, 0},
-#line 6302 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5678, 0},
-#line 1203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5679, 4},
-#line 5070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5680, 0},
-#line 1237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5681, 0},
-#line 4301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5682, 0},
-#line 4906 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5683, 0},
-#line 5869 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5684, 0},
-#line 4111 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5685, 0},
-#line 6154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5686, 0},
-#line 1117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5687, 0},
-#line 4907 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5688, 0},
-#line 2491 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5689, 4},
-#line 5658 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5690, 0},
-#line 5477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5691, 0},
-#line 1217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5692, 4},
-#line 5992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5693, 0},
-#line 5722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5694, 0},
-#line 4510 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5695, 0},
-#line 5990 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5696, 0},
-#line 4996 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5697, 0},
-#line 3202 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5698, 0},
-#line 5390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5699, 0},
-#line 5183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5700, 0},
-#line 3222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5701, 0},
-#line 3485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5702, 0},
-#line 5126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5703, 0},
-#line 5599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5704, 0},
-#line 6301 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5705, 0},
-#line 5447 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5706, 4},
-#line 3114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5707, 0},
-#line 4074 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5708, 0},
-#line 5742 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5709, 0},
-#line 3818 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5710, 0},
-#line 4674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5711, 0},
-#line 592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5712, 0},
-#line 3540 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5713, 0},
-#line 3113 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5714, 0},
-#line 3243 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5715, 0},
-#line 4144 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5716, 0},
-#line 5327 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5717, 0},
-#line 5348 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5718, 0},
-#line 5020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5719, 0},
-#line 6083 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5720, 0},
-#line 5321 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5721, 0},
-#line 4514 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5722, 0},
-#line 1136 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5723, 0},
-#line 2171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5724, 0},
-#line 2817 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5725, 0},
-#line 5527 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5726, 0},
-#line 5429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5727, 0},
-#line 6003 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5728, 0},
-#line 5129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5729, 0},
-#line 3237 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5730, 0},
-#line 2268 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5731, 0},
-#line 1444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5732, 0},
-#line 3537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5733, 0},
-#line 4475 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5734, 1},
-#line 3800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5735, 0},
-#line 5006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5736, 0},
-#line 5008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5737, 0},
-#line 4616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5738, 0},
-#line 5516 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5739, 0},
-#line 5604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5740, 0},
-#line 4551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5741, 0},
-#line 895 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5742, 0},
-#line 1184 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5743, 4},
-#line 5446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5744, 0},
-#line 2806 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5745, 0},
-#line 1183 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5746, 4},
-#line 3789 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5747, 0},
-#line 2811 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5748, 0},
-#line 896 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5749, 0},
-#line 2242 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5750, 4},
-#line 2609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5751, 0},
-#line 6041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5752, 0},
-#line 2587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5753, 0},
-#line 6375 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5754, 0},
-#line 4509 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5755, 0},
-#line 4601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5756, 0},
-#line 6170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5757, 0},
-#line 3973 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5758, 0},
-#line 1707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5759, 4},
-#line 5584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5760, 0},
-#line 6199 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5761, 0},
-#line 4567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5762, 0},
-#line 4417 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5763, 0},
-#line 5428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5764, 0},
-#line 4833 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5765, 0},
-#line 5583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5766, 0},
-#line 5980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5767, 0},
-#line 4957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5768, 0},
-#line 3983 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5769, 0},
-#line 2285 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5770, 0},
-#line 5310 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5771, 0},
-#line 250 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5772, 0},
-#line 1390 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5773, 4},
-#line 6109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5774, 0},
-#line 3543 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5775, 0},
-#line 6209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5776, 0},
-#line 2444 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5777, 4},
-#line 2473 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5778, 4},
-#line 5045 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5779, 0},
-#line 4900 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5780, 0},
-#line 4477 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5781, 0},
-#line 5478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5782, 0},
-#line 6362 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5783, 4},
-#line 5619 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5784, 0},
-#line 1595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5785, 4},
-#line 5531 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5786, 0},
-#line 2430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5787, 4},
-#line 4430 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5788, 0},
-#line 5577 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5789, 0},
-#line 3236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5790, 0},
-#line 6147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5791, 0},
-#line 1151 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5792, 0},
-#line 4368 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5793, 0},
-#line 2857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5794, 0},
-#line 4323 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5795, 0},
-#line 4901 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5796, 0},
-#line 2271 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5797, 0},
-#line 5380 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5798, 0},
-#line 4156 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5799, 0},
-#line 4112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5800, 0},
-#line 5409 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5801, 0},
-#line 5317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5802, 0},
-#line 4334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5803, 0},
-#line 1187 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5804, 4},
-#line 5692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5805, 4},
-#line 6142 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5806, 0},
-#line 2427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5807, 4},
-#line 1974 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5808, 0},
-#line 1970 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5809, 0},
-#line 6049 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5810, 0},
-#line 221 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5811, 4},
-#line 4203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5812, 0},
-#line 5253 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5813, 4},
-#line 4031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5814, 0},
-#line 4556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5815, 0},
-#line 6110 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5816, 0},
-#line 3519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5817, 4},
-#line 6071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5818, 0},
-#line 5466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5819, 0},
-#line 4921 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5820, 0},
-#line 326 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5821, 4},
-#line 3171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5822, 0},
-#line 5991 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5823, 0},
-#line 6072 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5824, 0},
-#line 4894 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5825, 0},
-#line 270 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5826, 0},
-#line 85 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5827, 0},
-#line 5192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5828, 0},
-#line 2864 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5829, 0},
-#line 2560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5830, 0},
-#line 5441 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5831, 0},
-#line 1437 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5832, 0},
-#line 6252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5833, 0},
-#line 4968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5834, 4},
-#line 4984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5835, 4},
-#line 6195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5836, 0},
-#line 2585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5837, 0},
-#line 2608 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5838, 4},
-#line 5515 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5839, 0},
-#line 1398 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5840, 0},
-#line 1492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5841, 0},
-#line 4802 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5842, 4},
-#line 3680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5843, 4},
-#line 4874 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5844, 0},
-#line 5618 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5845, 0},
-#line 3670 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5846, 0},
-#line 6047 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5847, 0},
-#line 3248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5848, 0},
-#line 1611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5849, 4},
-#line 4506 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5850, 0},
-#line 4922 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5851, 0},
-#line 6141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5852, 0},
-#line 4195 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5853, 0},
-#line 3154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5854, 0},
-#line 5634 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5855, 0},
-#line 1610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5856, 4},
-#line 1435 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5857, 4},
-#line 3735 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5858, 0},
-#line 6220 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5859, 0},
-#line 5336 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5860, 0},
-#line 5627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5861, 0},
-#line 6160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5862, 0},
-#line 6095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5863, 0},
-#line 1147 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5864, 0},
-#line 4923 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5865, 0},
-#line 3245 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5866, 0},
-#line 5734 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5867, 4},
-#line 1115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5868, 0},
-#line 777 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5869, 0},
-#line 3126 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5870, 0},
-#line 5733 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5871, 4},
-#line 1041 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5872, 0},
-#line 2805 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5873, 0},
-#line 4932 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5874, 0},
-#line 3208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5875, 0},
-#line 4933 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5876, 0},
-#line 223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5877, 4},
-#line 4132 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5878, 0},
-#line 2602 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5879, 0},
-#line 2599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5880, 0},
-#line 222 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5881, 4},
-#line 2612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5882, 0},
-#line 1992 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5883, 0},
-#line 3079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5884, 0},
-#line 5311 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5885, 0},
-#line 2905 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5886, 0},
-#line 4929 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5887, 0},
-#line 2616 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5888, 0},
-#line 1182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5889, 0},
-#line 1128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5890, 0},
-#line 3230 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5891, 0},
-#line 6247 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5892, 0},
-#line 3188 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5893, 0},
-#line 2087 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5894, 0},
-#line 717 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5895, 0},
-#line 676 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5896, 0},
-#line 2627 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5897, 0},
-#line 675 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5898, 0},
-#line 674 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5899, 0},
-#line 4298 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5900, 0},
-#line 712 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5901, 0},
-#line 698 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5902, 0},
-#line 694 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5903, 0},
-#line 673 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5904, 0},
-#line 699 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5905, 0},
-#line 4615 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5906, 0},
-#line 4857 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5907, 0},
-#line 703 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5908, 0},
-#line 4175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5909, 0},
-#line 3958 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5910, 0},
-#line 718 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5911, 0},
-#line 5539 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5912, 0},
-#line 3086 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5913, 0},
-#line 5071 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5914, 0},
-#line 2610 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5915, 0},
-#line 684 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5916, 0},
-#line 697 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5917, 0},
-#line 678 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5918, 0},
-#line 681 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5919, 0},
-#line 720 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5920, 0},
-#line 695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5921, 0},
-#line 2264 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5922, 0},
-#line 1129 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5923, 0},
-#line 704 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5924, 0},
-#line 700 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5925, 0},
-#line 5503 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5926, 0},
-#line 682 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5927, 0},
-#line 6196 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5928, 0},
-#line 5701 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5929, 0},
-#line 330 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5930, 0},
-#line 3251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5931, 0},
-#line 4446 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5932, 0},
-#line 696 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5933, 0},
-#line 715 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5934, 0},
-#line 679 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5935, 0},
-#line 3105 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5936, 0},
-#line 1148 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5937, 0},
-#line 690 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5938, 0},
-#line 2466 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5939, 4},
-#line 5955 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5940, 1},
-#line 4131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5941, 0},
-#line 1042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5942, 0},
-#line 709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5943, 0},
-#line 3240 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5944, 0},
-#line 706 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5945, 0},
-#line 707 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5946, 0},
-#line 2576 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5947, 0},
-#line 5318 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5948, 0},
-#line 692 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5949, 0},
-#line 3216 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5950, 0},
-#line 693 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5951, 0},
-#line 1067 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5952, 0},
-#line 4926 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5953, 0},
-#line 683 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5954, 0},
-#line 4909 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5955, 0},
-#line 404 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5956, 4},
-#line 4861 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5957, 0},
-#line 208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5958, 0},
-#line 691 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5959, 0},
-#line 708 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5960, 0},
-#line 677 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5961, 0},
-#line 2520 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5962, 0},
-#line 4508 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5963, 0},
-#line 1767 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5964, 4},
-#line 710 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5965, 0},
-#line 685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5966, 0},
-#line 705 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5967, 0},
-#line 5005 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5968, 0},
-#line 2479 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5969, 4},
-#line 1650 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5970, 0},
-#line 5370 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5971, 0},
-#line 2408 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5972, 0},
-#line 672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5973, 0},
-#line 711 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5974, 0},
-#line 1145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5975, 0},
-#line 1177 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5976, 0},
-#line 5882 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5977, 0},
-#line 5204 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5978, 0},
-#line 2538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5979, 0},
-#line 5334 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5980, 0},
-#line 3209 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5981, 0},
-#line 3140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5982, 0},
-#line 4160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5983, 0},
-#line 5251 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5984, 0},
-#line 4609 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5985, 0},
-#line 3235 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5986, 0},
-#line 4791 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5987, 4},
-#line 4792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5988, 4},
-#line 274 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5989, 0},
-#line 3107 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5990, 0},
-#line 5351 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5991, 0},
-#line 3229 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5992, 0},
-#line 3139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5993, 0},
-#line 2541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5994, 0},
-#line 4787 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5995, 4},
-#line 4280 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5996, 0},
-#line 3231 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5997, 0},
-#line 3133 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5998, 0},
-#line 35 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str5999, 0},
-#line 3217 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6000, 0},
-#line 4947 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6001, 0},
-#line 2594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6002, 0},
-#line 2902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6003, 0},
-#line 3241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6004, 0},
-#line 4599 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6005, 0},
-#line 5763 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6006, 0},
-#line 5613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6007, 0},
-#line 5218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6008, 0},
-#line 3189 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6009, 0},
-#line 3668 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6010, 0},
-#line 1613 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6011, 4},
-#line 3145 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6012, 0},
-#line 4604 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6013, 0},
-#line 687 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6014, 0},
-#line 3112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6015, 4},
-#line 688 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6016, 0},
-#line 5480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6017, 0},
-#line 5075 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6018, 0},
-#line 4248 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6019, 0},
-#line 689 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6020, 0},
-#line 725 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6021, 0},
-#line 5588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6022, 0},
-#line 241 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6023, 0},
-#line 5598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6024, 0},
-#line 3050 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6025, 0},
-#line 5420 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6026, 4},
-#line 727 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6027, 0},
-#line 1043 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6028, 0},
-#line 3255 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6029, 0},
-#line 4552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6030, 0},
-#line 4412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6031, 0},
-#line 2566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6032, 0},
-#line 1116 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6033, 0},
-#line 5661 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6034, 0},
-#line 724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6035, 0},
-#line 5589 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6036, 0},
-#line 5410 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6037, 0},
-#line 2590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6038, 0},
-#line 5590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6039, 0},
-#line 5439 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6040, 0},
-#line 3206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6041, 0},
-#line 726 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6042, 0},
-#line 6166 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6043, 0},
-#line 686 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6044, 0},
-#line 3200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6045, 0},
-#line 4597 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6046, 0},
-#line 5101 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6047, 0},
-#line 3180 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6048, 0},
-#line 5595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6049, 0},
-#line 4585 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6050, 0},
-#line 4588 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6051, 0},
-#line 4940 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6052, 4},
-#line 3118 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6053, 0},
-#line 5664 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6054, 0},
-#line 3152 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6055, 0},
-#line 2060 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6056, 0},
-#line 3160 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6057, 0},
-#line 3095 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6058, 0},
-#line 5396 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6059, 0},
-#line 5623 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6060, 0},
-#line 3084 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6061, 0},
-#line 590 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6062, 0},
-#line 3218 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6063, 0},
-#line 5984 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6064, 0},
-#line 6167 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6065, 0},
-#line 4115 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6066, 0},
-#line 3120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6067, 4},
-#line 3198 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6068, 0},
-#line 4523 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6069, 0},
-#line 4478 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6070, 0},
-#line 3097 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6071, 0},
-#line 719 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6072, 0},
-#line 4587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6073, 0},
-#line 1130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6074, 0},
-#line 3249 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6075, 0},
-#line 4631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6076, 0},
-#line 58 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6077, 0},
-#line 5547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6078, 0},
-#line 4120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6079, 0},
-#line 3080 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6080, 0},
-#line 6356 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6081, 4},
-#line 3601 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6082, 0},
-#line 1533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6083, 4},
-#line 5252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6084, 4},
-#line 5607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6085, 0},
-#line 5596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6086, 0},
-#line 1535 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6087, 4},
-#line 5964 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6088, 0},
-#line 4595 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6089, 4},
-#line 5533 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6090, 0},
-#line 1614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6091, 4},
-#line 5644 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6092, 0},
-#line 57 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6093, 0},
-#line 4946 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6094, 4},
-#line 4534 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6095, 0},
-#line 3541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6096, 0},
-#line 2208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6097, 4},
-#line 4208 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6098, 0},
-#line 6021 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6099, 0},
-#line 3667 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6100, 0},
-#line 4784 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6101, 4},
-#line 4468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6102, 0},
-#line 5603 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6103, 0},
-#line 5487 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6104, 0},
-#line 6078 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6105, 0},
-#line 4134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6106, 4},
-#line 3088 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6107, 0},
-#line 4139 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6108, 0},
-#line 5624 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6109, 0},
-#line 4579 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6110, 0},
-#line 5775 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6111, 0},
-#line 4605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6112, 0},
-#line 4786 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6113, 4},
-#line 4785 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6114, 4},
-#line 5482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6115, 0},
-#line 5378 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6116, 0},
-#line 4143 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6117, 0},
-#line 2813 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6118, 0},
-#line 6020 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6119, 0},
-#line 5544 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6120, 0},
-#line 1155 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6121, 0},
-#line 4790 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6122, 4},
-#line 3121 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6123, 0},
-#line 3957 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6124, 0},
-#line 5605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6125, 0},
-#line 3085 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6126, 0},
-#line 2499 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6127, 4},
-#line 5631 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6128, 0},
-#line 4902 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6129, 0},
-#line 4554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6130, 0},
-#line 4550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6131, 4},
-#line 3131 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6132, 0},
-#line 3138 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6133, 0},
-#line 4804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6134, 4},
-#line 6002 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6135, 0},
-#line 3956 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6136, 0},
-#line 5685 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6137, 0},
-#line 3192 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6138, 4},
-#line 4474 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6139, 0},
-#line 5373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6140, 0},
-#line 3194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6141, 0},
-#line 428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6142, 0},
-#line 4079 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6143, 0},
-#line 5386 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6144, 0},
-#line 5440 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6145, 0},
-#line 2460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6146, 4},
-#line 3538 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6147, 0},
-#line 5412 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6148, 0},
-#line 1541 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6149, 0},
-#line 4513 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6150, 0},
-#line 4798 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6151, 4},
-#line 5399 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6152, 0},
-#line 4797 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6153, 4},
-#line 6004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6154, 0},
-#line 5171 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6155, 0},
-#line 1695 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6156, 4},
-#line 1176 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6157, 4},
-#line 6076 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6158, 0},
-#line 5548 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6159, 0},
-#line 4472 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6160, 0},
-#line 1979 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6161, 0},
-#line 5507 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6162, 0},
-#line 3951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6163, 0},
-#line 1519 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6164, 0},
-#line 5612 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6165, 0},
-#line 1594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6166, 4},
-#line 5498 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6167, 0},
-#line 3547 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6168, 0},
-#line 6140 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6169, 0},
-#line 6112 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6170, 0},
-#line 2169 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6171, 0},
-#line 4392 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6172, 0},
-#line 5154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6173, 0},
-#line 5551 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6174, 0},
-#line 5207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6175, 0},
-#line 1214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6176, 4},
-#line 2429 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6177, 4},
-#line 5465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6178, 0},
-#line 6009 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6179, 0},
-#line 3795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6180, 0},
-#line 6010 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6181, 0},
-#line 460 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6182, 0},
-#line 2428 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6183, 4},
-#line 6114 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6184, 0},
-#line 4526 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6185, 0},
-#line 1373 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6186, 4},
-#line 4795 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6187, 4},
-#line 6007 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6188, 0},
-#line 4163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6189, 0},
-#line 5552 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6190, 0},
-#line 5352 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6191, 0},
-#line 4794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6192, 4},
-#line 4809 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6193, 0},
-#line 6011 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6194, 0},
-#line 4793 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6195, 4},
-#line 5560 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6196, 0},
-#line 5558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6197, 0},
-#line 1186 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6198, 4},
-#line 5553 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6199, 0},
-#line 5614 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6200, 0},
-#line 4801 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6201, 4},
-#line 5058 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6202, 0},
-#line 6120 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6203, 0},
-#line 5496 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6204, 0},
-#line 5562 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6205, 0},
-#line 4800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6206, 4},
-#line 2493 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6207, 4},
-#line 6053 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6208, 0},
-#line 2800 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6209, 0},
-#line 5559 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6210, 0},
-#line 4008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6211, 0},
-#line 5497 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6212, 0},
-#line 4485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6213, 0},
-#line 6135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6214, 0},
-#line 4862 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6215, 0},
-#line 2592 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6216, 0},
-#line 6077 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6217, 0},
-#line 4762 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6218, 0},
-#line 5468 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6219, 0},
-#line 6018 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6220, 0},
-#line 4004 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6221, 0},
-#line 2943 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6222, 0},
-#line 6128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6223, 0},
-#line 4596 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6224, 0},
-#line 5286 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6225, 0},
-#line 5550 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6226, 0},
-#line 5557 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6227, 0},
-#line 5556 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6228, 0},
-#line 1363 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6229, 0},
-#line 6317 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6230, 0},
-#line 5564 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6231, 0},
-#line 3130 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6232, 0},
-#line 4537 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6233, 0},
-#line 5042 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6234, 0},
-#line 4594 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6235, 0},
-#line 2951 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6236, 0},
-#line 5566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6237, 0},
-#line 3952 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6238, 0},
-#line 4611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6239, 0},
-#line 5611 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6240, 0},
-#line 3124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6241, 0},
-#line 5159 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6242, 0},
-#line 2433 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6243, 4},
-#line 1194 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6244, 4},
-#line 6032 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6245, 0},
-#line 3098 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6246, 0},
-#line 3182 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6247, 0},
-#line 3103 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6248, 0},
-#line 1163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6249, 0},
-#line 3210 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6250, 0},
-#line 4013 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6251, 0},
-#line 3163 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6252, 0},
-#line 5672 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6253, 0},
-#line 4558 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6254, 0},
-#line 3185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6255, 0},
-#line 3223 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6256, 0},
-#line 5549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6257, 0},
-#line 1165 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6258, 0},
-#line 5427 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6259, 0},
-#line 5865 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6260, 0},
-#line 68 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6261, 0},
-#line 6185 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6262, 0},
-#line 6287 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6263, 0},
-#line 5416 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6264, 0},
-#line 5587 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6265, 0},
-#line 6028 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6266, 0},
-#line 2803 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6267, 0},
-#line 5127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6268, 0},
-#line 5575 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6269, 0},
-#line 6200 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6270, 0},
-#line 4829 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6271, 0},
-#line 3219 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6272, 0},
-#line 5203 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6273, 0},
-#line 3081 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6274, 0},
-#line 1206 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6275, 4},
-#line 2453 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6276, 4},
-#line 4889 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6277, 0},
-#line 6303 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6278, 0},
-#line 2605 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6279, 0},
-#line 3252 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6280, 0},
-#line 1207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6281, 4},
-#line 6012 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6282, 0},
-#line 4419 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6283, 0},
-#line 1211 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6284, 4},
-#line 4770 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6285, 0},
-#line 6094 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6286, 0},
-#line 2582 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6287, 0},
-#line 6246 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6288, 0},
-#line 3170 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6289, 0},
-#line 2583 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6290, 0},
-#line 3092 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6291, 0},
-#line 2567 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6292, 0},
-#line 2207 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6293, 4},
-#line 1154 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6294, 0},
-#line 3225 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6295, 0},
-#line 3804 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6296, 0},
-#line 2465 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6297, 4},
-#line 4799 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6298, 4},
-#line 2485 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6299, 4},
-#line 716 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6300, 0},
-#line 4928 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6301, 0},
-#line 680 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6302, 0},
-#line 702 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6303, 0},
-#line 2480 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6304, 4},
-#line 2481 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6305, 4},
-#line 4549 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6306, 4},
-#line 4135 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6307, 0},
-#line 1175 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6308, 4},
-#line 2467 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6309, 4},
-#line 3794 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6310, 0},
-#line 5561 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6311, 0},
-#line 714 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6312, 0},
-#line 3106 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6313, 0},
-#line 3792 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6314, 1},
-#line 713 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6315, 0},
-#line 2494 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6316, 4},
-#line 1127 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6317, 0},
-#line 4598 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6318, 0},
-#line 1057 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6319, 0},
-#line 6056 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6320, 0},
-#line 4545 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6321, 0},
-#line 4459 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6322, 0},
-#line 1040 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6323, 4},
-#line 1039 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6324, 4},
-#line 1448 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6325, 0},
-#line 3109 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6326, 0},
-#line 5968 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6327, 0},
-#line 2492 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6328, 4},
-#line 6027 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6329, 0},
-#line 2584 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6330, 0},
-#line 3117 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6331, 0},
-#line 3238 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6332, 0},
-#line 2607 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6333, 4},
-#line 6124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6334, 0},
-#line 1980 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6335, 0},
-#line 3099 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6336, 0},
-#line 2482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6337, 4},
-#line 4709 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6338, 0},
-#line 4486 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6339, 0},
-#line 4937 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6340, 0},
-#line 4796 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6341, 4},
-#line 6048 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6342, 0},
-#line 3954 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6343, 0},
-#line 5227 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6344, 0},
-#line 5554 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6345, 0},
-#line 2581 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6346, 0},
-#line 723 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6347, 0},
-#line 722 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6348, 0},
-#line 5563 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6349, 0},
-#line 721 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6350, 0},
-#line 2236 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6351, 0},
-#line 1070 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6352, 0},
-#line 5555 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6353, 0},
-#line 3141 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6354, 0},
-#line 124 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6355, 0},
-#line 5732 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6356, 4},
-#line 2569 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6357, 0},
-#line 4566 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6358, 0},
-#line 4568 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6359, 0},
-#line 4482 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6360, 0},
-#line 3214 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6361, 0},
-#line 3128 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6362, 0},
-#line 6197 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6363, 0},
-#line 5565 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6364, 0},
-#line 4462 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6365, 0},
-#line 6134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6366, 0},
-#line 5044 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6367, 0},
-#line 1724 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6368, 0},
-#line 1391 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6369, 4},
-#line 5134 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6370, 0},
-#line 6008 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6371, 0},
-#line 3224 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6372, 0},
-#line 6006 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6373, 0},
-#line 6031 "effective_tld_names.gperf"
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str6374, 0}
- };
-
- static const short lookup[] =
- {
- -1, -1, -1, -1, -1, -1, -1, -1,
- 0, -1, 1, 2, 3, -1, -1, -1,
- -1, -1, 4, 5, -1, 6, -1, 7,
- -1, 8, -1, -1, -1, 9, 10, 11,
- -1, -1, 12, -1, -1, 13, 14, -1,
- 15, 16, 17, 18, -1, -1, 19, -1,
- -1, 20, 21, 22, 23, 24, 25, -1,
- -1, -1, 26, -1, -1, -1, 27, -1,
- 28, -1, 29, -1, 30, -1, 31, -1,
- -1, -1, 32, -1, -1, -1, 33, -1,
- -1, 34, 35, -1, -1, -1, -1, -1,
- 36, -1, -1, 37, -1, 38, -1, -1,
- 39, 40, 41, 42, -1, 43, -1, -1,
- -1, -1, 44, 45, 46, 47, -1, 48,
- 49, -1, 50, -1, -1, 51, 52, -1,
- -1, 53, -1, -1, -1, -1, 54, 55,
- 56, 57, 58, -1, 59, 60, 61, 62,
- -1, -1, 63, 64, 65, 66, 67, -1,
- -1, -1, -1, -1, -1, -1, 68, 69,
- 70, 71, -1, -1, 72, -1, -1, -1,
- 73, 74, 75, -1, 76, -1, -1, -1,
- -1, 77, -1, -1, -1, 78, -1, -1,
- 79, -1, 80, 81, 82, 83, 84, 85,
- 86, 87, 88, 89, 90, 91, 92, 93,
- 94, 95, 96, 97, 98, 99, 100, 101,
- 102, 103, 104, -1, -1, -1, 105, 106,
- 107, 108, 109, 110, -1, -1, -1, -1,
- -1, -1, -1, 111, -1, -1, -1, -1,
- 112, -1, -1, -1, -1, -1, -1, 113,
- -1, -1, -1, 114, -1, -1, 115, -1,
- -1, -1, 116, 117, -1, 118, 119, 120,
- 121, 122, -1, 123, -1, -1, -1, 124,
- -1, -1, 125, 126, -1, -1, -1, 127,
- -1, -1, 128, -1, -1, 129, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 130,
- -1, 131, 132, 133, -1, -1, -1, -1,
- 134, -1, -1, 135, 136, 137, 138, 139,
- 140, -1, 141, 142, -1, -1, 143, -1,
- 144, -1, -1, -1, 145, 146, -1, -1,
- -1, 147, -1, 148, 149, 150, 151, -1,
- -1, 152, -1, 153, -1, -1, 154, -1,
- -1, 155, -1, -1, -1, 156, -1, 157,
- -1, -1, 158, 159, 160, 161, 162, -1,
- -1, 163, 164, -1, 165, -1, 166, -1,
- -1, 167, -1, -1, -1, -1, -1, -1,
- 168, -1, 169, 170, -1, -1, 171, -1,
- -1, -1, -1, -1, -1, -1, 172, 173,
- -1, -1, -1, 174, -1, -1, 175, 176,
- -1, -1, 177, -1, -1, -1, -1, 178,
- 179, -1, 180, -1, -1, -1, -1, -1,
- -1, -1, -1, 181, -1, 182, -1, 183,
- -1, -1, 184, -1, -1, 185, 186, 187,
- -1, 188, -1, -1, 189, 190, 191, 192,
- 193, -1, -1, -1, 194, -1, -1, 195,
- 196, 197, 198, 199, -1, 200, -1, -1,
- 201, -1, -1, -1, 202, -1, -1, -1,
- -1, -1, -1, -1, -1, 203, -1, -1,
- -1, -1, 204, -1, -1, 205, 206, -1,
- 207, 208, 209, -1, 210, 211, 212, -1,
- -1, -1, -1, 213, 214, 215, 216, -1,
- -1, -1, -1, 217, -1, -1, 218, -1,
- 219, 220, 221, 222, 223, -1, -1, 224,
- -1, -1, 225, -1, 226, -1, 227, -1,
- -1, -1, 228, 229, -1, -1, 230, 231,
- -1, 232, -1, -1, -1, -1, -1, 233,
- -1, -1, 234, 235, -1, -1, -1, -1,
- -1, 236, 237, 238, -1, 239, 240, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 241, -1, -1, -1, -1, -1, -1, -1,
- 242, -1, 243, -1, 244, -1, -1, 245,
- 246, 247, -1, 248, 249, -1, -1, 250,
- -1, -1, -1, -1, 251, 252, -1, 253,
- -1, -1, -1, -1, -1, -1, -1, 254,
- 255, 256, -1, 257, 258, 259, -1, 260,
- -1, -1, -1, 261, -1, 262, -1, -1,
- 263, -1, 264, -1, -1, -1, 265, -1,
- -1, -1, -1, -1, 266, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 267, -1, -1,
- -1, -1, -1, -1, -1, 268, 269, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 270, -1, 271, -1, -1,
- 272, 273, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 274, 275, -1, -1, -1, 276, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 277, 278, -1, 279, -1, -1, -1,
- -1, -1, 280, 281, 282, -1, -1, 283,
- -1, -1, -1, -1, -1, -1, 284, -1,
- 285, -1, 286, -1, 287, 288, 289, -1,
- -1, -1, -1, -1, 290, 291, -1, -1,
- 292, -1, -1, 293, 294, 295, 296, -1,
- -1, -1, -1, -1, -1, -1, 297, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 298, -1, -1, -1, -1, 299,
- -1, 300, -1, -1, -1, -1, 301, -1,
- 302, -1, 303, 304, -1, 305, -1, 306,
- 307, -1, -1, -1, -1, -1, -1, -1,
- 308, 309, 310, -1, 311, 312, 313, -1,
- -1, 314, -1, 315, 316, 317, -1, 318,
- -1, -1, -1, -1, -1, 319, -1, -1,
- 320, -1, -1, -1, -1, -1, -1, -1,
- -1, 321, -1, -1, -1, -1, 322, -1,
- -1, -1, 323, -1, -1, -1, -1, -1,
- 324, -1, 325, -1, -1, 326, -1, -1,
- 327, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 328, -1, 329, 330,
- -1, 331, -1, -1, -1, -1, 332, 333,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 334, -1, 335,
- 336, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 337, -1, -1, 338, 339, 340,
- -1, 341, -1, 342, -1, -1, -1, -1,
- -1, -1, 343, -1, 344, 345, -1, -1,
- 346, 347, -1, 348, -1, 349, -1, -1,
- -1, 350, 351, 352, -1, 353, 354, -1,
- 355, -1, -1, -1, -1, -1, 356, -1,
- -1, -1, 357, -1, -1, -1, -1, -1,
- -1, -1, 358, -1, -1, -1, -1, -1,
- -1, -1, -1, 359, 360, -1, -1, 361,
- 362, -1, -1, -1, -1, -1, -1, 363,
- 364, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 365, -1, -1, -1,
- 366, 367, -1, -1, 368, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 369, 370, -1, -1,
- -1, 371, 372, 373, 374, -1, -1, 375,
- 376, -1, -1, 377, 378, 379, 380, -1,
- -1, 381, -1, -1, 382, 383, 384, 385,
- -1, 386, -1, 387, -1, -1, 388, -1,
- -1, 389, 390, 391, -1, 392, -1, 393,
- -1, 394, -1, -1, -1, -1, -1, -1,
- -1, 395, -1, -1, 396, 397, -1, -1,
- -1, -1, -1, 398, -1, -1, -1, -1,
- -1, 399, -1, -1, -1, 400, -1, 401,
- -1, -1, -1, 402, -1, 403, -1, -1,
- 404, 405, -1, -1, -1, -1, -1, -1,
- -1, 406, -1, -1, 407, -1, 408, -1,
- -1, -1, -1, 409, -1, 410, -1, -1,
- -1, 411, -1, -1, -1, -1, -1, -1,
- -1, 412, 413, -1, -1, -1, 414, -1,
- 415, 416, -1, 417, -1, 418, -1, -1,
- 419, 420, 421, -1, -1, 422, -1, -1,
- -1, 423, -1, -1, 424, -1, -1, 425,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 426, 427, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 428, -1, -1, -1, -1, -1, -1,
- -1, -1, 429, -1, -1, -1, -1, -1,
- 430, -1, -1, 431, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 432, 433, -1, -1, -1, 434,
- -1, -1, 435, -1, 436, -1, -1, 437,
- 438, -1, -1, -1, -1, -1, 439, -1,
- -1, -1, -1, 440, 441, -1, -1, -1,
- -1, -1, 442, -1, -1, -1, -1, 443,
- 444, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 445, 446, -1, -1, -1, 447, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 448,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 449, -1, -1, -1, -1, -1,
- -1, -1, 450, -1, 451, -1, 452, -1,
- -1, -1, 453, -1, -1, -1, -1, 454,
- 455, -1, 456, -1, -1, 457, 458, -1,
- -1, -1, -1, 459, 460, -1, -1, 461,
- -1, -1, -1, 462, -1, -1, -1, -1,
- 463, -1, -1, -1, 464, -1, -1, -1,
- -1, -1, -1, 465, 466, -1, 467, -1,
- -1, -1, -1, 468, -1, -1, -1, 469,
- -1, -1, -1, -1, 470, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 471, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 472, -1, -1, -1,
- 473, -1, -1, -1, -1, 474, -1, 475,
- -1, -1, -1, -1, -1, -1, -1, 476,
- 477, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 478,
- 479, -1, -1, 480, 481, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 482, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 483, -1, -1, -1, 484, 485, -1,
- 486, -1, 487, -1, -1, -1, -1, -1,
- -1, -1, -1, 488, 489, 490, -1, 491,
- -1, -1, 492, -1, -1, 493, -1, 494,
- -1, -1, -1, -1, -1, 495, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 496, 497, -1,
- -1, -1, -1, 498, -1, 499, -1, -1,
- 500, -1, 501, -1, -1, 502, -1, -1,
- -1, 503, -1, 504, -1, -1, -1, -1,
- -1, 505, 506, -1, -1, -1, -1, -1,
- -1, -1, 507, -1, 508, -1, -1, -1,
- -1, -1, -1, -1, 509, -1, 510, -1,
- 511, -1, 512, 513, -1, -1, -1, -1,
- -1, 514, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 515, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 516, -1, -1, -1, -1, -1,
- 517, -1, -1, 518, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 519, -1,
- -1, -1, -1, -1, -1, -1, 520, -1,
- 521, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 522, -1, -1,
- -1, -1, -1, -1, -1, -1, 523, 524,
- 525, -1, -1, -1, -1, 526, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 527, -1, -1,
- 528, -1, 529, -1, 530, -1, 531, -1,
- -1, -1, -1, -1, -1, -1, 532, -1,
- -1, -1, -1, -1, 533, -1, -1, -1,
- -1, -1, 534, -1, -1, -1, 535, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 536, -1, -1, -1, -1, -1,
- -1, 537, -1, -1, 538, 539, -1, -1,
- -1, -1, -1, -1, 540, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 541, -1, 542, -1, -1, -1,
- -1, -1, -1, -1, 543, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 544, -1, 545, -1, -1, -1, 546,
- -1, -1, 547, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 548, -1, 549,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 550, 551, -1, -1, -1, -1, 552, 553,
- -1, -1, -1, 554, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 555, -1, -1,
- -1, -1, 556, 557, -1, -1, -1, -1,
- -1, -1, 558, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 559, 560, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 561, 562, 563, -1, -1, -1, -1,
- -1, -1, -1, -1, 564, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 565, -1, -1, -1, -1, 566, -1, 567,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 568, -1, -1, -1, 569, -1, 570, -1,
- 571, -1, 572, -1, 573, -1, -1, -1,
- -1, -1, -1, -1, -1, 574, -1, -1,
- 575, -1, -1, -1, -1, 576, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 577, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 578,
- 579, -1, -1, -1, 580, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 581,
- 582, -1, 583, -1, -1, -1, -1, -1,
- -1, -1, -1, 584, -1, -1, -1, -1,
- -1, 585, -1, 586, -1, 587, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 588, -1, -1, -1, 589, 590, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 591, -1, 592, 593, 594, -1, -1,
- 595, -1, -1, -1, -1, -1, 596, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 597, -1, -1, -1,
- -1, -1, 598, -1, -1, -1, 599, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 600, -1, -1,
- -1, 601, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 602, -1,
- 603, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 604, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 605, -1, -1, -1, 606,
- -1, 607, 608, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 609,
- -1, -1, -1, -1, -1, -1, -1, 610,
- -1, -1, -1, 611, -1, 612, -1, -1,
- -1, -1, -1, -1, -1, 613, 614, -1,
- -1, -1, -1, 615, -1, -1, 616, -1,
- -1, -1, -1, -1, 617, 618, -1, -1,
- -1, -1, 619, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 620, 621,
- -1, 622, 623, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 624, -1, -1, 625,
- 626, 627, -1, -1, -1, 628, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 629, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 630,
- 631, 632, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 633, -1, 634, -1, -1, -1, -1, -1,
- -1, -1, 635, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 636, -1,
- -1, -1, -1, -1, -1, -1, 637, -1,
- -1, 638, -1, 639, 640, -1, -1, -1,
- -1, -1, 641, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 642, -1, 643,
- -1, 644, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 645, -1, -1, -1, -1,
- -1, -1, 646, -1, -1, -1, -1, -1,
- -1, 647, -1, -1, -1, -1, -1, -1,
- -1, 648, -1, -1, -1, 649, -1, -1,
- -1, -1, 650, -1, 651, -1, -1, -1,
- -1, -1, 652, -1, -1, -1, -1, -1,
- -1, -1, -1, 653, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 654, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 655, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 656, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 657, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 658, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 659, 660, -1,
- -1, -1, -1, -1, 661, 662, 663, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 664, -1, -1, 665, -1, -1, -1, 666,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 667, -1, 668, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 669, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 670, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 671, -1, -1, -1, 672,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 673, -1, -1,
- -1, -1, -1, -1, -1, -1, 674, -1,
- -1, -1, -1, -1, -1, -1, 675, -1,
- 676, -1, -1, -1, -1, -1, -1, -1,
- -1, 677, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 678, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 679,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 680, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 681, -1, -1, -1, -1, -1, -1, -1,
- -1, 682, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 683, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 684, -1, -1,
- -1, -1, -1, 685, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 686, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 687, -1, -1, -1, -1,
- 688, 689, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 690, -1, -1, -1, -1, 691, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 692, -1, -1,
- -1, -1, -1, 693, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 694, -1, -1, 695, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 696, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 697, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 698, -1, -1, -1, -1, -1, -1,
- 699, -1, -1, -1, -1, 700, -1, -1,
- -1, -1, 701, -1, -1, -1, -1, -1,
- -1, -1, -1, 702, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 703, 704, -1, -1, -1, 705, -1,
- -1, -1, -1, 706, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 707, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 708, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 709, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 710, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 711, -1, -1, -1, -1, -1,
- 712, -1, -1, 713, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 714,
- -1, -1, -1, -1, -1, -1, 715, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 716, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 717,
- -1, -1, -1, 718, -1, -1, -1, -1,
- 719, -1, -1, -1, -1, 720, 721, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 722, -1, -1, -1,
- -1, -1, -1, -1, 723, -1, -1, -1,
- -1, -1, -1, -1, -1, 724, -1, -1,
- 725, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 726, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 727, -1,
- -1, -1, -1, -1, -1, 728, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 729,
- -1, -1, -1, -1, -1, -1, 730, -1,
- -1, -1, -1, -1, 731, 732, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 733, -1, -1, -1,
- -1, -1, -1, -1, -1, 734, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 735, -1, -1, -1, -1, 736, -1, -1,
- -1, 737, -1, -1, -1, -1, -1, 738,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 739, -1, -1, -1,
- -1, -1, 740, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 741, -1,
- 742, -1, 743, -1, -1, -1, -1, 744,
- -1, -1, 745, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 746, -1,
- -1, -1, -1, -1, 747, -1, -1, -1,
- -1, -1, -1, -1, -1, 748, -1, -1,
- -1, -1, -1, 749, -1, 750, -1, -1,
- 751, -1, -1, -1, -1, -1, -1, -1,
- -1, 752, 753, -1, -1, -1, -1, -1,
- -1, -1, -1, 754, 755, -1, -1, -1,
- -1, -1, -1, -1, -1, 756, -1, -1,
- -1, -1, -1, -1, -1, 757, -1, 758,
- 759, -1, -1, -1, 760, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 761,
- -1, -1, -1, 762, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 763, -1, -1,
- -1, -1, -1, -1, 764, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 765, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 766, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 767, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 768,
- -1, -1, -1, -1, -1, -1, -1, 769,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 770, -1, -1, 771, -1, -1, -1, -1,
- -1, -1, -1, 772, -1, 773, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 774, -1, -1, -1,
- -1, 775, 776, -1, -1, -1, -1, -1,
- -1, -1, 777, 778, 779, -1, -1, -1,
- -1, 780, -1, -1, -1, -1, 781, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 782,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 783, -1, -1, -1, -1,
- 784, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 785, -1, -1, -1, -1, -1, 786,
- 787, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 788,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 789, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 790, 791, -1, -1, 792,
- -1, 793, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 794, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 795, -1, -1,
- 796, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 797,
- 798, -1, -1, -1, -1, -1, -1, -1,
- 799, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 800, -1, -1, -1, -1, -1, 801,
- -1, 802, 803, 804, -1, -1, -1, -1,
- -1, -1, -1, -1, 805, -1, 806, 807,
- -1, -1, -1, 808, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 809, 810, 811, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 812, -1, -1, -1,
- 813, -1, -1, -1, -1, 814, -1, -1,
- -1, -1, -1, -1, -1, 815, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 816, -1, -1,
- -1, -1, -1, 817, -1, -1, -1, -1,
- -1, 818, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 819, -1,
- -1, 820, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 821, -1, -1,
- 822, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 823, -1, -1, -1, 824, -1,
- -1, -1, -1, -1, -1, 825, -1, -1,
- -1, -1, -1, -1, 826, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 827, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 828, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 829, -1, -1, 830,
- 831, -1, -1, -1, -1, 832, -1, 833,
- 834, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 835, -1, -1, -1, -1,
- -1, 836, -1, -1, -1, -1, -1, -1,
- -1, 837, -1, -1, -1, -1, -1, 838,
- -1, 839, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 840, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 841, -1, -1, -1, 842, -1,
- 843, -1, -1, -1, -1, -1, -1, -1,
- 844, -1, -1, -1, -1, -1, -1, -1,
- 845, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 846, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 847, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 848, -1, 849, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 850, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 851, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 852, -1, -1, -1, -1, -1,
- -1, -1, -1, 853, -1, -1, -1, -1,
- -1, 854, -1, -1, -1, -1, 855, -1,
- -1, -1, -1, -1, -1, 856, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 857, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 858,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 859, 860, -1, -1, 861, 862,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 863, 864, -1, 865, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 866,
- 867, -1, -1, -1, -1, -1, -1, -1,
- -1, 868, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 869, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 870, -1, 871, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 872, -1, 873, -1, -1, -1, -1, -1,
- 874, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 875, -1, -1, -1, -1, -1,
- -1, -1, -1, 876, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 877, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 878, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 879, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 880, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 881, -1, -1, -1, 882,
- -1, -1, 883, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 884, -1, -1, -1, -1, -1, -1, 885,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 886, -1,
- -1, -1, -1, 887, -1, -1, -1, -1,
- -1, 888, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 889, -1, -1, -1, -1, -1,
- 890, -1, 891, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 892, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 893, 894, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 895, -1, -1, 896,
- -1, -1, -1, -1, -1, -1, -1, 897,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 898, -1, -1,
- 899, -1, -1, -1, 900, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 901, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 902, -1, -1, 903, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 904, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 905, 906, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 907, -1,
- -1, -1, -1, -1, 908, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 909, -1, -1, 910, -1,
- -1, -1, -1, -1, -1, 911, -1, -1,
- 912, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 913, -1, -1, -1, -1, 914,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 915, -1, -1, 916, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 917,
- -1, -1, 918, -1, 919, 920, -1, 921,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 922, -1, 923, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 924,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 925, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 926,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 927, 928, -1, -1, 929, 930,
- -1, -1, -1, -1, -1, 931, -1, -1,
- -1, -1, -1, -1, 932, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 933, 934, -1, 935, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 936, -1, -1,
- -1, 937, -1, -1, -1, 938, 939, -1,
- -1, 940, -1, -1, -1, -1, -1, -1,
- -1, -1, 941, -1, -1, -1, 942, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 943, -1, -1, -1, -1, 944,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 945, -1, -1, -1, -1, 946,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 947, -1, -1, 948, 949, 950, -1,
- -1, -1, -1, -1, 951, -1, -1, -1,
- -1, -1, 952, -1, -1, -1, -1, -1,
- 953, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 954, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 955, -1, -1, -1,
- 956, 957, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 958, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 959, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 960, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 961, 962, -1, -1,
- 963, -1, -1, -1, -1, -1, -1, 964,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 965, -1,
- -1, -1, -1, 966, -1, 967, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 968, -1, -1, -1, -1, -1, -1,
- -1, -1, 969, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 970, -1, -1,
- -1, -1, 971, -1, -1, -1, -1, 972,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 973, 974, -1, -1, 975, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 976, -1, -1, -1,
- -1, -1, -1, -1, -1, 977, -1, -1,
- -1, 978, -1, -1, -1, -1, -1, -1,
- -1, -1, 979, -1, -1, -1, -1, 980,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 981, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 982, 983, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 984, -1, -1, -1, -1, 985,
- 986, -1, -1, -1, -1, -1, 987, -1,
- -1, -1, -1, -1, -1, -1, 988, -1,
- -1, -1, -1, 989, -1, -1, 990, -1,
- 991, -1, -1, -1, 992, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 993, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 994,
- 995, -1, 996, -1, -1, -1, -1, -1,
- -1, 997, -1, -1, -1, 998, -1, -1,
- 999, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1000, 1001, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1002, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1003, -1,
- -1, -1, -1, -1, -1, -1, -1, 1004,
- 1005, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1006, -1,
- -1, -1, -1, 1007, 1008, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1009, -1,
- 1010, 1011, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1012, 1013, -1, -1, -1,
- -1, -1, 1014, -1, -1, -1, 1015, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1016, -1, -1, -1, -1,
- -1, -1, 1017, -1, -1, -1, -1, -1,
- 1018, -1, -1, 1019, -1, -1, -1, -1,
- -1, -1, -1, -1, 1020, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1021, -1, -1, -1, -1, -1, -1, 1022,
- -1, -1, -1, -1, -1, -1, 1023, -1,
- -1, -1, -1, 1024, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1025, -1, -1, -1, 1026, -1, 1027,
- -1, -1, -1, -1, -1, 1028, -1, -1,
- -1, -1, 1029, -1, -1, -1, -1, -1,
- -1, -1, -1, 1030, -1, -1, 1031, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1032, -1, 1033, -1, -1, -1, -1,
- 1034, -1, -1, 1035, -1, -1, -1, 1036,
- 1037, -1, -1, -1, -1, -1, -1, -1,
- -1, 1038, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1039, -1, -1, -1, -1, -1, 1040,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1041, 1042, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1043, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1044, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1045, -1, -1,
- -1, -1, -1, -1, -1, 1046, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1047,
- -1, -1, 1048, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1049, -1, -1,
- -1, -1, 1050, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1051, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1052,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1053, -1,
- 1054, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1055, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1056, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1057, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1058, -1, -1, -1,
- -1, -1, -1, 1059, 1060, -1, 1061, -1,
- 1062, -1, -1, 1063, -1, -1, -1, -1,
- 1064, -1, -1, -1, -1, 1065, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1066, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1067, -1,
- -1, -1, -1, -1, -1, -1, 1068, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1069, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1070,
- -1, -1, -1, -1, -1, -1, 1071, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1072, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1073,
- -1, -1, -1, 1074, -1, -1, -1, -1,
- -1, -1, 1075, -1, -1, -1, -1, 1076,
- -1, -1, -1, -1, 1077, -1, -1, -1,
- -1, -1, -1, -1, 1078, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1079,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1080, -1, -1, 1081, -1,
- 1082, -1, 1083, -1, -1, 1084, -1, -1,
- -1, -1, 1085, -1, -1, -1, -1, -1,
- -1, -1, -1, 1086, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1087, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1088, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1089, -1, 1090,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1091, -1, 1092, -1, -1, -1, 1093,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1094, -1, -1, -1, 1095, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1096, 1097,
- -1, -1, 1098, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1099, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1100, -1,
- 1101, -1, -1, -1, -1, -1, 1102, -1,
- 1103, -1, -1, -1, -1, -1, -1, -1,
- 1104, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1105, -1, -1, -1, -1,
- -1, 1106, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1107, 1108, 1109, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1110, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1111, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1112, 1113, 1114, -1,
- -1, -1, -1, -1, 1115, -1, -1, -1,
- -1, -1, -1, -1, 1116, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1117, -1, -1, -1, -1, -1,
- 1118, -1, -1, 1119, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1120,
- -1, -1, -1, -1, -1, -1, 1121, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1122, 1123, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1124, -1, -1,
- -1, -1, 1125, -1, -1, -1, -1, -1,
- -1, -1, 1126, -1, 1127, -1, 1128, -1,
- -1, -1, 1129, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1130, -1, -1, -1, -1, -1,
- -1, 1131, -1, -1, 1132, -1, -1, -1,
- 1133, -1, -1, -1, 1134, -1, -1, 1135,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1136, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1137, -1, 1138, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1139,
- 1140, -1, -1, -1, 1141, -1, 1142, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1143, -1, 1144, -1, -1, -1,
- -1, 1145, 1146, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1147, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1148,
- -1, -1, -1, -1, -1, 1149, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1150, -1, -1, -1,
- -1, -1, 1151, -1, -1, -1, -1, -1,
- -1, 1152, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1153, -1, -1, -1,
- -1, -1, -1, -1, -1, 1154, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1155,
- -1, -1, -1, -1, 1156, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1157, -1, -1, -1, -1, -1, 1158, -1,
- -1, 1159, -1, -1, -1, -1, -1, -1,
- -1, -1, 1160, -1, -1, -1, -1, 1161,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1162, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1163, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1164, 1165, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1166, -1, 1167, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1168, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1169, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1170,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1171, -1, -1, -1, -1, -1, 1172,
- 1173, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1174, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1175,
- -1, -1, 1176, 1177, -1, -1, 1178, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1179, -1, -1, -1, -1, 1180, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1181, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1182, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1183, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1184, -1, -1, -1, 1185, -1,
- -1, 1186, -1, -1, 1187, -1, -1, -1,
- 1188, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1189, -1,
- -1, -1, -1, -1, 1190, -1, 1191, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1192, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1193, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1194,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1195, 1196, -1,
- 1197, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1198,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1199, -1,
- -1, -1, -1, -1, -1, 1200, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1201, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1202, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1203, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1204, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1205, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1206, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1207, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1208,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1209, 1210, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1211, -1, 1212, -1,
- -1, -1, -1, -1, 1213, -1, -1, -1,
- 1214, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1215, -1, -1, -1,
- -1, -1, -1, 1216, -1, -1, -1, -1,
- -1, -1, -1, -1, 1217, -1, -1, -1,
- -1, -1, -1, -1, 1218, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1219, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1220,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1221, -1, 1222,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1223, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1224, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1225, 1226, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1227, -1, 1228, -1,
- 1229, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1230, -1, -1,
- -1, -1, -1, -1, -1, 1231, 1232, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1233, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1234, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1235, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1236, -1, -1, 1237, -1,
- -1, -1, -1, -1, 1238, -1, 1239, -1,
- -1, 1240, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1241, -1, -1, -1,
- 1242, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1243, -1, -1, -1, -1, -1, 1244, 1245,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1246, -1, -1, -1, -1, 1247, -1, -1,
- 1248, 1249, -1, 1250, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1251, -1, -1, -1, -1, -1, -1, 1252,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1253, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1254, -1, -1,
- -1, -1, 1255, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1256, 1257, -1, -1, -1, -1, -1, 1258,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1259, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1260, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1261, -1, -1,
- -1, -1, -1, 1262, -1, -1, -1, -1,
- -1, -1, -1, 1263, -1, -1, 1264, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1265, -1, -1, -1, -1,
- -1, -1, 1266, -1, -1, -1, 1267, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1268,
- 1269, -1, -1, -1, -1, -1, -1, -1,
- 1270, -1, -1, 1271, -1, -1, -1, -1,
- -1, -1, -1, 1272, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1273, -1, -1,
- -1, -1, -1, -1, -1, 1274, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1275, -1, -1, -1, -1, -1, -1, -1,
- 1276, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1277, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1278, -1, -1, -1,
- -1, -1, -1, -1, -1, 1279, -1, 1280,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1281, 1282, -1, -1, -1,
- 1283, 1284, 1285, -1, -1, -1, 1286, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1287, -1, -1, -1,
- -1, 1288, -1, -1, -1, -1, -1, -1,
- -1, 1289, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1290, -1,
- -1, -1, -1, -1, 1291, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1292, -1, 1293, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1294, -1, -1, -1, -1, 1295, 1296,
- -1, -1, -1, 1297, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1298, -1, -1, -1, -1,
- 1299, -1, -1, -1, 1300, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1301,
- -1, -1, 1302, -1, -1, -1, -1, -1,
- -1, -1, -1, 1303, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1304, -1, -1, -1, -1, 1305, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1306, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1307, -1, -1,
- -1, -1, -1, -1, 1308, -1, -1, -1,
- -1, 1309, -1, -1, -1, -1, -1, -1,
- 1310, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1311, -1, 1312, -1, -1,
- -1, 1313, 1314, -1, -1, -1, -1, 1315,
- -1, -1, -1, 1316, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1317,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1318, -1, 1319, 1320,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1321, -1, -1, -1,
- -1, 1322, -1, -1, 1323, -1, 1324, -1,
- -1, -1, 1325, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1326, -1,
- -1, -1, -1, -1, -1, -1, -1, 1327,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1328, -1, 1329,
- -1, -1, -1, -1, -1, -1, 1330, -1,
- -1, 1331, -1, -1, -1, -1, -1, -1,
- -1, 1332, 1333, -1, -1, -1, -1, 1334,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1335, -1, 1336, -1, -1, -1, 1337, -1,
- -1, -1, 1338, -1, -1, 1339, -1, -1,
- -1, -1, -1, -1, -1, 1340, -1, -1,
- 1341, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1342, -1, -1, -1,
- -1, -1, -1, -1, -1, 1343, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1344, 1345, -1, -1, 1346,
- -1, -1, -1, -1, 1347, -1, -1, -1,
- 1348, -1, -1, -1, 1349, -1, -1, -1,
- -1, 1350, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1351,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1352,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1353, -1, -1, 1354, 1355, -1, -1, -1,
- -1, -1, 1356, -1, -1, -1, -1, 1357,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1358, -1, -1, -1,
- 1359, -1, -1, -1, -1, -1, 1360, -1,
- -1, 1361, -1, -1, -1, -1, -1, 1362,
- -1, -1, 1363, -1, -1, -1, 1364, -1,
- -1, 1365, -1, -1, -1, -1, -1, -1,
- -1, 1366, -1, -1, -1, -1, -1, -1,
- -1, -1, 1367, -1, -1, -1, 1368, -1,
- -1, -1, -1, 1369, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1370, -1, -1, -1, -1, 1371,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1372,
- -1, -1, -1, -1, -1, -1, -1, 1373,
- -1, 1374, 1375, 1376, -1, -1, -1, -1,
- 1377, -1, -1, -1, 1378, -1, -1, -1,
- -1, -1, -1, -1, -1, 1379, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1380, -1,
- -1, -1, -1, 1381, -1, -1, -1, -1,
- 1382, 1383, 1384, -1, -1, -1, 1385, -1,
- -1, -1, -1, -1, 1386, 1387, -1, -1,
- -1, -1, 1388, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1389, -1, -1, -1,
- -1, -1, 1390, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1391, -1, 1392, -1,
- 1393, -1, -1, 1394, -1, -1, -1, -1,
- -1, -1, -1, 1395, -1, -1, -1, -1,
- -1, -1, 1396, -1, -1, -1, 1397, -1,
- -1, -1, -1, -1, 1398, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1399, -1, -1, -1, -1,
- -1, 1400, -1, -1, -1, -1, -1, 1401,
- -1, -1, -1, 1402, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1403,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1404, -1,
- -1, -1, -1, -1, -1, -1, -1, 1405,
- -1, 1406, -1, -1, 1407, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1408, 1409, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1410, -1, -1, -1, -1,
- -1, -1, -1, -1, 1411, -1, -1, -1,
- -1, -1, -1, -1, 1412, -1, -1, -1,
- -1, -1, -1, -1, 1413, -1, 1414, -1,
- 1415, -1, -1, -1, -1, 1416, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1417, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1418, -1, -1,
- 1419, -1, -1, 1420, -1, -1, -1, -1,
- -1, -1, 1421, -1, 1422, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1423,
- -1, -1, -1, 1424, -1, -1, -1, -1,
- -1, -1, -1, -1, 1425, -1, -1, -1,
- -1, -1, -1, -1, -1, 1426, 1427, -1,
- -1, -1, -1, 1428, -1, 1429, -1, -1,
- 1430, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1431, -1, -1, 1432, -1, -1, 1433, -1,
- -1, -1, -1, -1, -1, -1, 1434, 1435,
- -1, 1436, -1, -1, 1437, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1438, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1439,
- 1440, -1, -1, -1, -1, -1, -1, -1,
- 1441, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1442, -1,
- 1443, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1444, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1445, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1446, 1447, -1, -1, -1, -1, -1, -1,
- -1, -1, 1448, -1, -1, -1, -1, -1,
- -1, -1, -1, 1449, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1450, -1, -1, -1, -1, 1451, -1, -1,
- 1452, 1453, -1, -1, -1, -1, -1, -1,
- -1, 1454, 1455, -1, -1, 1456, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1457, 1458, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1459, -1, -1, -1, -1, -1, -1,
- -1, 1460, -1, -1, -1, -1, -1, -1,
- -1, 1461, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1462, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1463, 1464,
- -1, -1, -1, -1, -1, 1465, -1, -1,
- -1, -1, -1, -1, 1466, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1467, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1468,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1469, -1, 1470, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1471, -1,
- -1, -1, -1, 1472, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1473,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1474, -1, -1, 1475, -1, -1, -1, -1,
- -1, 1476, -1, -1, 1477, -1, -1, 1478,
- -1, -1, -1, -1, -1, 1479, -1, 1480,
- -1, -1, 1481, -1, -1, -1, -1, -1,
- -1, 1482, 1483, -1, -1, -1, 1484, -1,
- -1, -1, -1, -1, -1, -1, 1485, -1,
- -1, -1, -1, -1, -1, -1, 1486, -1,
- -1, -1, -1, 1487, 1488, -1, -1, -1,
- -1, -1, 1489, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1490, -1,
- -1, -1, 1491, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1492,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1493, 1494, 1495,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1496, -1, -1, 1497, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1498, -1,
- -1, -1, -1, 1499, -1, -1, -1, -1,
- -1, -1, 1500, 1501, -1, -1, 1502, -1,
- -1, -1, -1, -1, 1503, -1, -1, 1504,
- 1505, -1, -1, -1, -1, -1, -1, -1,
- 1506, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1507, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1508, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1509, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1510, -1, -1, -1, -1, -1, -1, -1,
- 1511, -1, -1, -1, 1512, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1513, -1, -1, -1, -1, -1, -1, -1,
- -1, 1514, -1, -1, -1, -1, -1, -1,
- 1515, 1516, -1, -1, -1, -1, -1, -1,
- -1, 1517, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1518,
- -1, -1, -1, 1519, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1520, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1521, 1522, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1523, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1524, 1525, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1526, 1527, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1528, -1, -1,
- 1529, -1, -1, -1, 1530, -1, -1, -1,
- -1, -1, -1, -1, -1, 1531, -1, -1,
- -1, -1, -1, 1532, -1, -1, -1, 1533,
- 1534, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1535,
- -1, -1, -1, 1536, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1537, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1538, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1539, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1540, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1541, -1, -1, 1542, -1, -1, -1, -1,
- 1543, -1, -1, 1544, -1, -1, -1, -1,
- -1, -1, -1, -1, 1545, -1, -1, -1,
- 1546, -1, -1, -1, 1547, -1, -1, -1,
- -1, 1548, -1, 1549, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1550, 1551, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1552, -1, -1, 1553, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1554, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1555, -1, -1,
- -1, 1556, -1, -1, 1557, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1558,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1559, -1, -1, -1, -1, -1, 1560, 1561,
- -1, 1562, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1563, -1, -1, 1564,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1565,
- -1, -1, -1, 1566, -1, -1, -1, -1,
- -1, -1, 1567, -1, -1, -1, -1, 1568,
- -1, -1, -1, -1, -1, -1, 1569, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1570, -1, -1, -1, -1,
- -1, -1, 1571, -1, -1, -1, 1572, 1573,
- -1, -1, 1574, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1575, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1576,
- -1, -1, -1, -1, -1, 1577, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1578,
- 1579, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1580, -1, 1581, -1, -1, -1, 1582, -1,
- -1, 1583, -1, -1, -1, 1584, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1585, -1, -1, -1, -1, -1,
- 1586, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1587, -1, 1588, -1, -1, -1,
- -1, -1, 1589, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1590, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1591, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1592, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1593, -1, -1, -1, -1, 1594,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1595, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1596, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1597, -1, -1,
- -1, -1, -1, 1598, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1599, -1, -1, -1, 1600, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1601,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1602, -1, 1603, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1604, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1605, 1606, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1607, -1, -1, -1, 1608, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1609, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1610, -1, 1611, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1612, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1613,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1614,
- -1, -1, -1, 1615, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1616, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1617,
- 1618, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1619, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1620, -1, -1, -1, -1, 1621, 1622, -1,
- 1623, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1624, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1625,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1626, -1, 1627, 1628, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1629, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1630, -1, -1, -1, -1,
- 1631, -1, 1632, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1633, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1634, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1635, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1636,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1637, -1, -1, -1, -1,
- -1, 1638, -1, -1, -1, -1, -1, -1,
- -1, 1639, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1640, 1641, -1,
- -1, -1, -1, -1, -1, -1, -1, 1642,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1643, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1644, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1645, -1, -1, -1, -1,
- 1646, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1647, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1648, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1649,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1650, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1651, -1, -1,
- -1, -1, -1, -1, -1, 1652, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1653, -1, -1,
- 1654, -1, -1, -1, 1655, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1656,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1657, -1, -1, 1658, -1,
- 1659, 1660, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1661, -1, -1,
- -1, 1662, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1663, -1, -1, -1,
- -1, -1, -1, 1664, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1665, -1,
- -1, -1, -1, -1, -1, -1, -1, 1666,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1667, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1668, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1669,
- -1, -1, -1, -1, 1670, -1, 1671, -1,
- -1, 1672, -1, -1, 1673, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1674, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1675, -1, -1, -1, -1,
- -1, -1, -1, -1, 1676, -1, -1, -1,
- -1, -1, 1677, 1678, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1679, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1680, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1681, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1682, -1, -1, -1, 1683,
- -1, -1, -1, -1, -1, 1684, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1685, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1686, 1687, -1,
- -1, -1, 1688, -1, 1689, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1690, -1,
- -1, -1, -1, 1691, -1, 1692, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1693,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1694, -1, 1695, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1696,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1697, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1698, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1699, -1, 1700,
- 1701, -1, -1, -1, -1, -1, -1, -1,
- 1702, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1703, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1704, -1, -1,
- -1, -1, -1, -1, -1, 1705, -1, -1,
- -1, -1, 1706, -1, -1, -1, -1, -1,
- -1, -1, 1707, -1, -1, 1708, 1709, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1710, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1711, -1, -1, -1, -1, -1,
- -1, -1, 1712, -1, -1, -1, -1, -1,
- -1, -1, -1, 1713, -1, -1, -1, 1714,
- -1, 1715, -1, -1, -1, -1, -1, -1,
- -1, 1716, -1, 1717, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1718, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1719, -1, -1, 1720,
- -1, -1, -1, -1, -1, 1721, -1, -1,
- -1, -1, -1, -1, -1, -1, 1722, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1723, -1, -1, -1,
- -1, 1724, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1725,
- -1, -1, -1, 1726, -1, -1, -1, -1,
- 1727, -1, -1, 1728, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1729, -1, -1, -1, -1, -1, -1,
- 1730, -1, -1, -1, 1731, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1732, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1733, 1734, -1,
- -1, 1735, 1736, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1737, -1, 1738,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1739, -1, -1, -1, -1, -1, -1, -1,
- -1, 1740, -1, -1, -1, -1, -1, -1,
- -1, 1741, -1, -1, -1, 1742, 1743, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1744, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1745,
- 1746, 1747, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1748,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1749, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1750,
- 1751, -1, -1, 1752, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1753, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1754, -1, -1,
- -1, -1, -1, -1, -1, -1, 1755, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1756, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1757, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1758, -1, -1, -1, 1759, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1760,
- -1, -1, 1761, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1762, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1763, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1764, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1765, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1766,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1767, 1768, -1, -1, -1, -1, -1, -1,
- 1769, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1770, 1771, -1, -1, -1, -1, -1, -1,
- -1, -1, 1772, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1773, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1774, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1775, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1776, -1, -1,
- 1777, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1778, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1779, -1, -1,
- -1, -1, -1, 1780, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1781, -1,
- -1, -1, -1, -1, -1, -1, 1782, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1783, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1784, -1, -1, -1, 1785,
- -1, 1786, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1787, -1, -1,
- -1, -1, -1, -1, -1, -1, 1788, -1,
- -1, -1, -1, -1, -1, -1, 1789, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1790, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1791, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1792, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1793,
- 1794, -1, -1, -1, -1, -1, -1, -1,
- -1, 1795, -1, -1, -1, -1, -1, -1,
- -1, -1, 1796, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1797, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1798, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1799, -1, -1, 1800, -1, -1,
- -1, -1, -1, -1, 1801, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1802, -1,
- -1, 1803, -1, -1, -1, -1, -1, -1,
- -1, 1804, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1805, -1, -1, -1, -1, -1, 1806, 1807,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1808, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1809, -1, -1, -1, 1810, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1811, -1, -1, -1, -1, -1, -1,
- -1, -1, 1812, -1, -1, -1, -1, -1,
- -1, -1, 1813, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1814, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1815, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1816, 1817, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1818, -1, -1, -1, -1, -1, 1819,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1820, 1821, 1822, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1823, -1, -1, -1, -1, -1, -1, -1,
- 1824, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1825, -1, -1, -1, -1,
- -1, -1, 1826, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1827, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1828,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1829, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1830, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1831, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1832, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1833,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1834, 1835,
- -1, -1, -1, -1, -1, -1, 1836, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1837, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1838, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1839, -1, -1, -1,
- -1, -1, -1, -1, -1, 1840, -1, -1,
- -1, -1, -1, -1, -1, 1841, -1, -1,
- -1, -1, -1, -1, -1, 1842, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1843,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1844, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1845, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1846, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1847, -1, -1, -1, -1, -1, 1848,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1849, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1850, -1, -1, -1,
- -1, -1, -1, 1851, -1, -1, -1, 1852,
- -1, -1, -1, -1, 1853, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1854, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1855, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1856, 1857, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1858, -1, -1, 1859, -1, -1, -1, -1,
- 1860, 1861, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1862, -1, -1,
- -1, -1, -1, -1, 1863, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1864, -1, -1, -1, -1, -1, -1,
- 1865, -1, -1, -1, -1, -1, -1, -1,
- -1, 1866, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1867, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1868, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1869, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1870, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1871, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1872, 1873, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1874, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1875,
- -1, -1, -1, -1, -1, -1, -1, 1876,
- 1877, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1878, -1, -1, -1, -1,
- -1, -1, -1, 1879, -1, -1, -1, -1,
- -1, 1880, -1, -1, -1, -1, -1, -1,
- -1, 1881, -1, -1, -1, 1882, -1, -1,
- -1, -1, -1, -1, -1, -1, 1883, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1884,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1885, 1886, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1887, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1888, -1, -1,
- -1, -1, -1, -1, -1, -1, 1889, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1890,
- -1, 1891, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1892, -1, -1,
- -1, -1, -1, -1, -1, 1893, -1, 1894,
- -1, -1, -1, -1, -1, 1895, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1896, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1897,
- -1, 1898, -1, -1, -1, -1, -1, -1,
- -1, 1899, -1, -1, -1, -1, -1, -1,
- -1, -1, 1900, -1, -1, -1, -1, -1,
- -1, -1, 1901, -1, -1, -1, 1902, -1,
- -1, 1903, -1, 1904, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1905, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1906, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1907, -1,
- -1, -1, -1, 1908, -1, -1, -1, 1909,
- -1, 1910, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1911, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1912, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1913, -1, -1, -1, -1, -1, -1, -1,
- 1914, -1, -1, 1915, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1916, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1917,
- -1, -1, -1, -1, -1, -1, -1, 1918,
- -1, -1, -1, -1, -1, -1, -1, 1919,
- -1, -1, -1, 1920, -1, -1, -1, 1921,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1922, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1923, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1924, -1, -1, -1, 1925, -1, -1,
- -1, -1, -1, -1, -1, -1, 1926, -1,
- -1, -1, 1927, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1928, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 1929, -1,
- -1, -1, 1930, -1, -1, -1, -1, -1,
- -1, 1931, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1932, -1, -1, -1, -1, -1, -1,
- 1933, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1934, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 1935, -1, 1936, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1937, -1, -1, -1, -1, -1, -1,
- -1, -1, 1938, -1, -1, 1939, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1940,
- -1, -1, -1, -1, -1, -1, 1941, -1,
- 1942, -1, 1943, -1, 1944, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1945, -1, -1, -1, -1, -1, -1, -1,
- 1946, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1947, -1, -1, -1,
- -1, -1, 1948, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1949, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1950,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1951, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1952, -1, 1953, 1954,
- -1, -1, -1, -1, 1955, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1956, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1957, -1, -1, 1958, -1, -1, -1,
- 1959, -1, -1, -1, -1, -1, -1, 1960,
- -1, -1, 1961, 1962, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1963, -1, -1, -1, -1, -1, -1,
- 1964, -1, -1, -1, -1, -1, -1, -1,
- 1965, -1, -1, -1, -1, -1, 1966, -1,
- -1, -1, -1, 1967, 1968, 1969, -1, -1,
- -1, -1, 1970, 1971, -1, -1, -1, -1,
- 1972, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1973, -1, -1, -1,
- 1974, -1, -1, -1, -1, -1, 1975, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1976, 1977, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1978, -1, -1,
- -1, -1, 1979, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1980, 1981, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 1982,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1983, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1984, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1985, 1986, -1, -1, -1,
- 1987, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1988, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 1989, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 1990, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 1991, -1, -1,
- -1, 1992, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1993, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 1994, -1, -1, -1, -1,
- -1, 1995, -1, -1, -1, -1, 1996, -1,
- -1, -1, 1997, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 1998, -1, 1999, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2000, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2001, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2002, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2003, -1, -1,
- 2004, -1, -1, -1, 2005, -1, -1, -1,
- -1, 2006, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2007, -1, -1, -1, -1, 2008, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2009, -1,
- 2010, 2011, 2012, -1, -1, -1, -1, 2013,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2014, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2015, -1, -1,
- 2016, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2017, -1, -1, -1, -1, -1,
- 2018, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2019, -1, -1, -1, -1, -1, -1, -1,
- -1, 2020, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2021, 2022, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2023, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2024,
- -1, -1, -1, -1, -1, 2025, -1, -1,
- -1, -1, -1, 2026, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2027, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2028, -1, 2029,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2030,
- -1, 2031, 2032, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2033,
- -1, -1, -1, 2034, -1, -1, -1, 2035,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2036, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2037, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2038, -1, -1,
- -1, -1, -1, -1, 2039, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2040, -1, -1, -1, -1, -1,
- 2041, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2042, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2043, 2044, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2045, 2046, -1, -1,
- 2047, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2048, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2049, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2050, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2051, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2052,
- -1, 2053, -1, 2054, -1, 2055, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2056, -1, -1, -1, -1, -1,
- -1, -1, 2057, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2058, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2059, -1, -1, -1,
- -1, 2060, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2061, -1,
- -1, -1, -1, 2062, -1, -1, -1, -1,
- 2063, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2064, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2065, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2066,
- 2067, -1, -1, -1, -1, -1, 2068, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2069, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2070, -1, -1, 2071, -1,
- -1, -1, -1, -1, -1, 2072, -1, -1,
- -1, -1, -1, 2073, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2074, -1,
- 2075, -1, -1, 2076, -1, 2077, -1, -1,
- -1, -1, -1, -1, 2078, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2079, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2080, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2081, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2082, 2083, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2084, 2085, -1, -1, -1, -1,
- 2086, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2087, -1, 2088, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2089, 2090, -1, -1, 2091, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2092, -1, -1, -1, 2093, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2094, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2095, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2096, -1, -1, 2097, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2098,
- -1, -1, -1, 2099, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2100,
- -1, 2101, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2102, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2103, -1, -1, -1, 2104, -1, -1,
- -1, -1, -1, -1, -1, 2105, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2106, -1, -1, 2107, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2108, -1, -1,
- -1, -1, -1, -1, -1, 2109, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2110, 2111, -1, -1, -1, -1, -1,
- -1, -1, -1, 2112, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2113, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2114, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2115, -1, -1,
- -1, -1, -1, 2116, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2117, -1,
- -1, -1, -1, -1, -1, -1, 2118, -1,
- 2119, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2120, -1, 2121, -1, -1, -1,
- -1, -1, -1, -1, 2122, -1, -1, -1,
- 2123, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2124, -1, -1, -1,
- -1, -1, -1, -1, -1, 2125, 2126, -1,
- -1, 2127, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2128, -1, -1, -1, 2129,
- -1, 2130, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2131, -1, -1, -1,
- 2132, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2133, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2134, -1, -1, -1, 2135, -1, -1, -1,
- -1, -1, -1, -1, 2136, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2137, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2138, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2139,
- -1, -1, -1, -1, -1, -1, 2140, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2141, -1, -1, -1,
- -1, -1, -1, -1, 2142, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2143, 2144, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2145, -1, -1, -1,
- 2146, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2147, -1,
- 2148, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2149, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2150, -1, -1,
- -1, -1, -1, -1, 2151, -1, 2152, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2153, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2154, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2155,
- -1, -1, 2156, -1, -1, -1, -1, -1,
- -1, -1, 2157, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2158, -1,
- -1, -1, 2159, -1, -1, -1, -1, -1,
- -1, -1, -1, 2160, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2161, -1, -1, -1, 2162, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2163, -1, -1, -1, -1, 2164, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2165, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2166, 2167, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2168, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2169, -1, -1, -1, -1,
- 2170, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2171, -1, -1,
- -1, 2172, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2173, -1,
- -1, -1, -1, -1, -1, 2174, -1, -1,
- -1, -1, -1, -1, 2175, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2176, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2177, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2178, -1, -1, -1,
- -1, 2179, 2180, -1, -1, -1, -1, 2181,
- -1, -1, -1, -1, 2182, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2183, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2184, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2185, -1, 2186,
- -1, -1, -1, -1, -1, 2187, -1, -1,
- 2188, -1, -1, -1, -1, -1, -1, -1,
- 2189, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2190, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2191, 2192, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2193, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2194, -1, -1, -1, -1,
- 2195, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2196, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2197, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2198, -1,
- -1, -1, -1, 2199, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2200,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2201, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2202, -1, 2203, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2204, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2205, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2206, -1,
- -1, -1, -1, -1, -1, -1, 2207, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2208, -1, -1,
- -1, -1, -1, -1, -1, 2209, 2210, -1,
- -1, -1, 2211, -1, 2212, -1, -1, -1,
- 2213, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2214, -1, -1,
- -1, -1, -1, -1, 2215, 2216, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2217, -1, -1, 2218, -1, -1,
- -1, -1, -1, -1, -1, -1, 2219, 2220,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2221, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2222, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2223, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2224,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2225, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2226, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2227, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2228, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2229, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2230, -1, 2231,
- -1, 2232, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2233, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2234, -1, -1,
- -1, -1, -1, -1, 2235, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2236, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2237, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2238, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2239, -1, -1, -1, -1, -1,
- -1, -1, 2240, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2241, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2242, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2243, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2244, -1, -1, -1, -1, -1, 2245,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2246, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2247, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2248,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2249, -1, -1, -1, -1, -1, -1, -1,
- 2250, -1, 2251, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2252, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2253, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2254, -1, -1, -1, -1, -1,
- -1, -1, -1, 2255, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2256, 2257,
- -1, 2258, -1, -1, -1, -1, 2259, -1,
- -1, -1, 2260, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2261, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2262, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2263, 2264, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2265,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2266, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2267, -1, -1, -1, -1, -1,
- -1, -1, 2268, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2269, -1,
- -1, 2270, 2271, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2272, -1, -1, -1, -1,
- -1, 2273, -1, 2274, -1, -1, -1, 2275,
- -1, -1, -1, -1, -1, -1, -1, 2276,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2277, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2278, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2279, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2280, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2281,
- -1, 2282, 2283, -1, -1, -1, -1, -1,
- -1, -1, -1, 2284, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2285,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2286, 2287, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2288, -1, 2289, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2290,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2291, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2292, -1, -1, -1, -1, 2293, -1,
- 2294, -1, 2295, -1, -1, -1, -1, -1,
- -1, -1, 2296, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2297, -1,
- -1, -1, -1, -1, -1, -1, 2298, -1,
- 2299, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2300, -1, -1, -1, -1, 2301,
- -1, -1, -1, -1, 2302, -1, -1, 2303,
- -1, -1, -1, -1, -1, 2304, 2305, -1,
- -1, 2306, -1, 2307, 2308, -1, -1, -1,
- -1, -1, 2309, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2310,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2311, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2312, -1, -1, -1, -1, -1,
- -1, 2313, -1, -1, -1, 2314, -1, -1,
- 2315, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2316, -1, -1, -1, -1, -1,
- -1, 2317, -1, -1, -1, -1, 2318, -1,
- 2319, -1, -1, 2320, -1, -1, -1, -1,
- -1, 2321, -1, -1, 2322, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2323,
- -1, -1, 2324, -1, -1, -1, 2325, -1,
- -1, -1, -1, 2326, -1, -1, 2327, -1,
- -1, -1, 2328, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2329,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2330, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2331, -1, -1, -1, 2332, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2333, 2334, -1, -1, -1, -1, -1, 2335,
- -1, -1, 2336, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2337, -1, -1, -1, -1, -1, -1,
- -1, -1, 2338, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2339, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2340, -1,
- 2341, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2342, -1, -1, -1, -1, -1,
- -1, -1, -1, 2343, -1, -1, 2344, 2345,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2346, 2347, -1,
- -1, -1, -1, -1, -1, -1, 2348, -1,
- -1, 2349, 2350, 2351, -1, -1, 2352, -1,
- 2353, -1, 2354, -1, 2355, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2356, -1, -1, -1, -1,
- 2357, -1, -1, -1, -1, -1, -1, 2358,
- -1, -1, -1, 2359, -1, -1, 2360, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2361, 2362, 2363, -1, -1, 2364, -1, -1,
- -1, -1, -1, -1, 2365, -1, -1, -1,
- 2366, -1, -1, 2367, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2368, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2369, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2370, -1, -1, 2371, -1,
- -1, -1, -1, 2372, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2373, -1,
- -1, -1, -1, 2374, -1, -1, -1, -1,
- 2375, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2376, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2377, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2378, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2379, 2380,
- 2381, -1, -1, -1, -1, -1, -1, 2382,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2383, -1, -1, -1, -1,
- -1, -1, 2384, -1, -1, -1, -1, -1,
- -1, 2385, -1, -1, -1, -1, -1, -1,
- -1, 2386, 2387, -1, -1, -1, -1, -1,
- 2388, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2389, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2390, -1, 2391,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2392, -1, -1,
- 2393, -1, -1, 2394, -1, -1, 2395, -1,
- 2396, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2397,
- 2398, -1, -1, -1, -1, -1, 2399, -1,
- -1, -1, 2400, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2401, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2402, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2403, -1, -1,
- -1, 2404, -1, -1, -1, -1, -1, -1,
- -1, -1, 2405, -1, -1, -1, -1, 2406,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2407, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2408, -1, -1, -1, -1, -1,
- -1, -1, 2409, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2410, -1, -1, -1,
- -1, -1, -1, -1, 2411, -1, -1, -1,
- -1, 2412, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2413, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2414, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2415, 2416, -1, -1, 2417, -1, -1,
- -1, -1, -1, 2418, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2419, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2420,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2421, -1, -1, -1, -1, -1, -1,
- -1, 2422, -1, 2423, -1, -1, -1, -1,
- -1, -1, -1, -1, 2424, 2425, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2426, -1, -1,
- -1, 2427, -1, -1, 2428, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2429, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2430,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2431, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2432, -1, 2433, -1,
- -1, -1, -1, 2434, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2435, -1, 2436, 2437, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2438, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2439, -1, -1, -1, -1, -1,
- 2440, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2441, -1, -1, -1,
- -1, -1, -1, -1, 2442, -1, 2443, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2444, -1, -1, -1, -1,
- -1, -1, -1, -1, 2445, 2446, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2447, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2448, -1, -1, -1, -1,
- -1, -1, 2449, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2450, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2451,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2452, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2453, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2454, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2455,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2456, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2457, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2458, -1, -1,
- 2459, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2460, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2461, -1, -1, -1, -1, -1, -1,
- -1, 2462, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2463, 2464, -1, -1,
- -1, 2465, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2466, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2467, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2468, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2469, -1, 2470, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2471, 2472, -1, 2473, -1,
- 2474, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2475, -1, -1, -1, -1, 2476,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2477, -1, -1, -1,
- -1, -1, 2478, -1, -1, -1, 2479, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2480, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2481,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2482, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2483, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2484, -1, -1,
- -1, -1, -1, -1, -1, -1, 2485, -1,
- -1, -1, -1, -1, -1, 2486, -1, -1,
- -1, -1, -1, -1, 2487, -1, -1, -1,
- -1, -1, -1, 2488, -1, -1, -1, -1,
- -1, -1, 2489, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2490,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2491, -1, -1, -1, -1, -1, -1, -1,
- -1, 2492, -1, -1, -1, -1, -1, -1,
- -1, 2493, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2494, 2495, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2496, -1, 2497, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2498, -1, -1, -1, -1, -1, -1, 2499,
- 2500, -1, 2501, -1, -1, -1, -1, -1,
- -1, -1, -1, 2502, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2503, -1, -1,
- -1, 2504, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2505, -1, -1, 2506,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2507, -1, -1, -1, -1, -1, 2508,
- -1, -1, -1, -1, -1, 2509, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2510,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2511, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2512, -1,
- -1, -1, -1, -1, -1, -1, -1, 2513,
- -1, -1, -1, -1, -1, -1, -1, 2514,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2515, 2516, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2517, -1, 2518, 2519, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2520, -1, -1,
- 2521, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2522, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2523, -1,
- -1, -1, -1, 2524, -1, -1, -1, -1,
- 2525, -1, -1, -1, -1, -1, -1, -1,
- 2526, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2527, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2528, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2529, -1,
- -1, -1, -1, -1, 2530, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2531, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2532, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2533, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2534, 2535, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2536, -1, 2537, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2538, -1, -1, -1,
- -1, -1, -1, -1, -1, 2539, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2540,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2541,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2542, -1, -1,
- -1, 2543, -1, -1, -1, -1, -1, -1,
- -1, -1, 2544, 2545, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2546,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2547, -1, -1, 2548, -1,
- -1, 2549, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2550,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2551, 2552, -1, -1, -1, -1, -1,
- -1, 2553, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2554, -1, -1,
- -1, -1, 2555, -1, 2556, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2557, -1, -1, -1,
- 2558, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2559, -1, -1, -1, -1, -1, -1, 2560,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2561, 2562, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2563, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2564,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2565, -1, -1, -1, -1,
- -1, -1, -1, 2566, 2567, 2568, -1, 2569,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2570,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2571, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2572, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2573, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2574, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2575,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2576, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2577, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2578, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2579, -1, 2580, 2581,
- -1, -1, -1, -1, -1, -1, -1, 2582,
- 2583, 2584, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2585, -1,
- -1, -1, 2586, -1, -1, -1, -1, -1,
- 2587, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2588, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2589, -1, -1, -1, -1, 2590,
- -1, -1, -1, -1, 2591, -1, -1, -1,
- -1, 2592, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2593, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2594, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2595, -1, -1, 2596, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2597, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2598, -1, -1, -1, -1, -1, -1,
- -1, 2599, -1, -1, -1, -1, -1, -1,
- -1, 2600, 2601, -1, 2602, -1, -1, 2603,
- -1, 2604, -1, -1, 2605, 2606, 2607, -1,
- -1, -1, 2608, -1, -1, -1, -1, -1,
- -1, -1, 2609, -1, -1, -1, -1, -1,
- -1, 2610, -1, -1, -1, -1, -1, -1,
- 2611, -1, -1, -1, -1, -1, -1, -1,
- 2612, -1, 2613, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2614, -1, -1,
- -1, -1, 2615, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2616, -1,
- 2617, -1, -1, 2618, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2619, -1, 2620,
- -1, -1, -1, 2621, -1, -1, 2622, -1,
- -1, -1, -1, -1, -1, -1, 2623, -1,
- -1, -1, 2624, -1, -1, -1, -1, -1,
- 2625, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2626, -1, -1,
- -1, -1, 2627, -1, 2628, 2629, -1, -1,
- -1, -1, -1, 2630, -1, -1, -1, 2631,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2632, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2633, -1, 2634,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2635, -1,
- -1, -1, -1, -1, 2636, -1, -1, 2637,
- 2638, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2639, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2640,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2641, -1, -1, -1, -1, -1,
- -1, -1, 2642, -1, 2643, 2644, -1, -1,
- -1, -1, -1, 2645, -1, 2646, -1, -1,
- -1, -1, -1, -1, -1, -1, 2647, -1,
- -1, -1, -1, 2648, 2649, -1, -1, -1,
- -1, -1, 2650, -1, 2651, 2652, -1, 2653,
- -1, 2654, -1, -1, -1, -1, -1, -1,
- -1, 2655, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2656, -1, -1, -1, 2657, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2658, -1, 2659, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2660, 2661, -1, -1, -1, 2662, -1, 2663,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2664, 2665,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2666, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2667,
- -1, 2668, 2669, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2670, -1, -1, -1,
- -1, -1, -1, -1, -1, 2671, -1, -1,
- -1, -1, -1, 2672, 2673, -1, -1, 2674,
- -1, 2675, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2676, -1, -1, -1, 2677,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2678, -1, -1, -1, -1, -1, -1, 2679,
- -1, -1, -1, -1, 2680, -1, -1, -1,
- -1, 2681, 2682, -1, -1, -1, 2683, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2684, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2685, -1,
- -1, -1, -1, -1, -1, -1, -1, 2686,
- -1, 2687, -1, -1, -1, 2688, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2689, -1, -1, 2690, -1, -1, -1,
- -1, -1, -1, 2691, -1, -1, -1, -1,
- 2692, -1, 2693, -1, -1, -1, -1, -1,
- 2694, 2695, -1, -1, -1, -1, -1, 2696,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2697, 2698, 2699, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2700,
- -1, -1, -1, -1, -1, -1, 2701, -1,
- -1, -1, -1, 2702, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2703, -1,
- -1, -1, -1, -1, -1, 2704, 2705, 2706,
- -1, 2707, -1, -1, -1, 2708, -1, 2709,
- -1, -1, 2710, -1, 2711, -1, 2712, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2713, 2714, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2715,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2716, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2717, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2718, -1, -1, 2719, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2720, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2721, -1, -1, -1, -1, 2722, 2723,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2724, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2725, -1, 2726, -1, 2727,
- -1, -1, 2728, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2729, -1, 2730, 2731, -1, 2732, 2733, -1,
- -1, -1, 2734, -1, -1, -1, 2735, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2736, 2737, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2738, 2739, 2740,
- -1, -1, -1, 2741, -1, -1, 2742, -1,
- -1, -1, 2743, -1, -1, -1, -1, 2744,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2745,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2746, -1, -1, 2747, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2748, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2749, -1, 2750, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2751, -1, -1, 2752, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2753, -1,
- -1, -1, -1, -1, -1, 2754, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2755, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2756, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2757,
- -1, -1, -1, -1, -1, 2758, -1, 2759,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2760, -1, -1,
- -1, -1, 2761, 2762, -1, -1, -1, 2763,
- -1, 2764, 2765, 2766, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2767,
- -1, 2768, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2769, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2770, -1, -1, 2771, -1,
- -1, -1, -1, -1, 2772, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2773, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2774, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2775, -1, 2776, -1, -1, -1,
- 2777, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2778, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2779,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2780, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2781,
- -1, -1, -1, 2782, -1, -1, 2783, -1,
- -1, 2784, -1, -1, -1, -1, -1, -1,
- -1, -1, 2785, -1, -1, 2786, -1, -1,
- -1, -1, -1, -1, -1, -1, 2787, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2788, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2789, 2790, -1,
- -1, -1, -1, 2791, -1, -1, -1, 2792,
- -1, 2793, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2794, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2795, -1, -1, -1, -1, -1,
- -1, 2796, -1, -1, -1, -1, 2797, -1,
- -1, -1, -1, -1, -1, -1, -1, 2798,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2799, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2800, -1, -1, -1,
- 2801, -1, 2802, -1, -1, -1, -1, -1,
- 2803, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2804, 2805,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2806,
- -1, -1, -1, -1, -1, 2807, -1, -1,
- -1, -1, -1, -1, -1, -1, 2808, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2809, -1, -1, -1, 2810, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2811, -1, 2812,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2813, -1, -1, -1, -1, 2814, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2815,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2816, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2817, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2818, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2819, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2820, -1, -1, -1, -1, 2821, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2822, -1, 2823, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2824, -1, -1, -1, -1,
- -1, 2825, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2826, -1, -1, -1, -1, -1, 2827,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2828, 2829, -1, -1,
- -1, -1, -1, 2830, -1, 2831, -1, 2832,
- -1, -1, -1, -1, 2833, -1, 2834, -1,
- -1, -1, -1, -1, -1, -1, 2835, -1,
- -1, -1, -1, -1, -1, 2836, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2837, -1, 2838, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2839, -1, 2840, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2841, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2842, -1,
- 2843, -1, 2844, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2845, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2846, -1, 2847, -1, -1, -1, -1,
- -1, 2848, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2849, -1, -1, -1, -1, -1, 2850,
- -1, -1, 2851, -1, -1, -1, -1, -1,
- 2852, -1, -1, -1, -1, -1, 2853, -1,
- -1, -1, -1, -1, -1, -1, -1, 2854,
- -1, -1, -1, -1, -1, 2855, -1, -1,
- -1, -1, -1, -1, -1, 2856, 2857, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2858, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2859, -1, 2860, -1, -1, -1,
- -1, -1, 2861, -1, -1, -1, -1, 2862,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2863, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2864,
- -1, 2865, 2866, -1, 2867, -1, -1, -1,
- -1, -1, -1, 2868, 2869, 2870, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2871, -1, -1, 2872, -1, 2873, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2874, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2875, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2876,
- 2877, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2878, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2879, -1, -1,
- 2880, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2881,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2882,
- 2883, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2884, -1, -1, -1,
- 2885, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2886, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2887,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2888, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2889, 2890, 2891, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2892, -1, 2893, 2894, -1, 2895, -1,
- -1, 2896, -1, -1, 2897, 2898, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2899,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2900,
- -1, -1, -1, -1, -1, 2901, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2902, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2903, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2904, 2905, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2906, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2907, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2908, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2909, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2910, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2911, 2912, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2913, -1, -1, -1, -1, -1, 2914, -1,
- -1, -1, -1, -1, -1, 2915, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2916, -1,
- 2917, 2918, -1, -1, -1, -1, -1, -1,
- 2919, -1, -1, -1, 2920, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2921, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2922, 2923, 2924, -1,
- -1, -1, -1, -1, 2925, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2926, -1, -1,
- 2927, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2928, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2929,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2930,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2931, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2932, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2933, -1, -1, -1, -1,
- -1, -1, -1, -1, 2934, -1, -1, -1,
- -1, -1, -1, 2935, -1, -1, -1, -1,
- 2936, -1, -1, -1, -1, 2937, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2938, -1, -1, -1, -1, -1, -1, -1,
- -1, 2939, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2940, -1, -1,
- -1, -1, -1, -1, 2941, -1, -1, 2942,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2943, -1, -1, -1, -1, -1, -1,
- -1, -1, 2944, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2945, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2946, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2947, 2948,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 2949, -1,
- -1, 2950, -1, -1, 2951, -1, 2952, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2953, -1, -1,
- -1, -1, -1, -1, -1, 2954, -1, -1,
- -1, -1, -1, 2955, 2956, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2957,
- -1, -1, -1, -1, 2958, -1, 2959, -1,
- -1, -1, -1, -1, -1, -1, -1, 2960,
- 2961, -1, 2962, -1, -1, 2963, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2964, -1, -1, 2965, 2966, -1, -1, 2967,
- -1, 2968, -1, -1, -1, -1, -1, -1,
- -1, -1, 2969, -1, -1, -1, -1, -1,
- 2970, -1, 2971, -1, -1, -1, 2972, -1,
- -1, -1, 2973, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2974, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2975, -1, -1, -1,
- -1, -1, -1, -1, 2976, -1, -1, 2977,
- -1, -1, -1, -1, -1, 2978, -1, 2979,
- 2980, 2981, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2982, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 2983, -1, -1, -1, 2984, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 2985, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2986,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2987, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 2988, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 2989,
- -1, 2990, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 2991, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 2992, -1, -1, -1, -1, 2993, 2994, -1,
- 2995, -1, -1, -1, 2996, -1, -1, -1,
- -1, -1, 2997, -1, 2998, -1, 2999, 3000,
- -1, -1, 3001, -1, -1, -1, -1, 3002,
- -1, 3003, -1, -1, -1, -1, -1, -1,
- 3004, -1, -1, 3005, -1, 3006, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3007, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3008, -1, 3009, -1, -1, -1,
- 3010, -1, -1, -1, -1, -1, -1, -1,
- 3011, 3012, -1, 3013, 3014, -1, -1, 3015,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3016, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3017,
- -1, -1, 3018, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3019, -1, -1, -1,
- -1, -1, -1, -1, -1, 3020, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3021, -1, 3022, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3023, -1, -1, 3024, -1, -1,
- -1, -1, -1, 3025, -1, -1, 3026, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3027, -1, -1, -1,
- -1, -1, -1, 3028, -1, -1, -1, -1,
- -1, -1, 3029, -1, -1, -1, -1, -1,
- 3030, -1, -1, 3031, -1, -1, 3032, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3033, -1, -1, -1, -1,
- 3034, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3035, -1, -1, -1,
- -1, -1, -1, -1, 3036, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3037, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3038, -1, -1, 3039, -1, -1, -1, -1,
- -1, 3040, -1, -1, -1, -1, 3041, -1,
- 3042, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3043, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3044, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3045, -1, -1, -1, -1, -1, 3046, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3047, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3048, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3049,
- 3050, 3051, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3052, 3053,
- -1, -1, -1, -1, 3054, 3055, 3056, -1,
- -1, -1, -1, -1, -1, 3057, -1, -1,
- -1, 3058, 3059, -1, -1, -1, 3060, -1,
- 3061, -1, -1, 3062, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3063, -1,
- -1, -1, -1, -1, 3064, -1, -1, -1,
- -1, -1, -1, 3065, -1, -1, -1, -1,
- -1, -1, 3066, -1, 3067, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3068, -1,
- -1, -1, -1, -1, -1, -1, -1, 3069,
- -1, -1, -1, 3070, -1, -1, -1, -1,
- 3071, -1, -1, -1, -1, 3072, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3073, -1, 3074, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3075,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3076, -1, -1, -1, -1, -1, -1,
- 3077, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3078, -1, 3079,
- -1, -1, 3080, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3081, -1, -1, -1,
- -1, -1, -1, -1, -1, 3082, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3083,
- -1, -1, -1, -1, -1, -1, 3084, 3085,
- -1, 3086, -1, 3087, -1, -1, 3088, -1,
- -1, -1, -1, 3089, -1, -1, -1, 3090,
- 3091, -1, -1, -1, -1, -1, -1, -1,
- 3092, -1, -1, -1, -1, -1, 3093, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3094, -1, -1, -1,
- 3095, -1, 3096, 3097, -1, -1, -1, -1,
- 3098, 3099, -1, -1, -1, -1, -1, -1,
- -1, -1, 3100, -1, -1, -1, 3101, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3102, -1, 3103, -1, -1, -1, -1, 3104,
- -1, -1, -1, 3105, -1, 3106, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3107, -1, -1, -1, -1, -1, 3108, -1,
- -1, 3109, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3110, -1, -1, -1, -1, 3111,
- -1, -1, -1, -1, -1, -1, -1, 3112,
- -1, -1, -1, -1, -1, -1, -1, 3113,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3114, -1, -1, 3115,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3116, -1, -1, -1, 3117, -1,
- -1, 3118, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3119,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3120, -1, -1, -1,
- -1, -1, -1, -1, -1, 3121, 3122, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3123, 3124, -1, -1, -1, -1, -1, 3125,
- -1, -1, -1, -1, -1, -1, -1, 3126,
- -1, -1, -1, -1, -1, 3127, -1, -1,
- -1, 3128, 3129, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3130, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3131, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3132, -1, -1, -1, -1, -1, 3133,
- 3134, -1, -1, -1, 3135, -1, -1, -1,
- -1, 3136, -1, -1, 3137, -1, -1, -1,
- 3138, -1, -1, -1, -1, -1, -1, 3139,
- -1, 3140, -1, -1, -1, -1, -1, 3141,
- -1, 3142, -1, -1, -1, -1, -1, -1,
- -1, -1, 3143, -1, -1, -1, -1, -1,
- 3144, -1, -1, 3145, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3146, 3147, -1, -1, -1, 3148, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3149, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3150, -1, -1,
- -1, -1, -1, 3151, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3152, -1, -1,
- -1, 3153, -1, -1, -1, -1, -1, -1,
- -1, -1, 3154, -1, -1, -1, -1, -1,
- -1, -1, -1, 3155, -1, 3156, -1, -1,
- -1, -1, -1, 3157, -1, -1, -1, 3158,
- -1, -1, -1, -1, 3159, -1, -1, 3160,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3161, -1, 3162, -1,
- -1, -1, -1, 3163, -1, -1, 3164, -1,
- -1, -1, -1, -1, -1, 3165, -1, -1,
- -1, -1, 3166, -1, -1, 3167, -1, -1,
- -1, -1, -1, -1, -1, 3168, -1, -1,
- -1, -1, -1, -1, -1, 3169, 3170, -1,
- -1, 3171, 3172, -1, -1, 3173, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3174, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3175, -1, -1,
- -1, 3176, 3177, -1, -1, -1, -1, -1,
- -1, -1, -1, 3178, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3179,
- -1, 3180, -1, -1, 3181, 3182, -1, 3183,
- -1, -1, -1, -1, -1, -1, -1, 3184,
- 3185, -1, -1, -1, -1, -1, 3186, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3187, -1, -1, -1, -1, -1, -1,
- 3188, -1, 3189, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3190, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3191, -1, -1, -1, 3192,
- 3193, 3194, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3195, -1, -1, 3196, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3197, 3198, -1, -1, -1, -1, -1,
- -1, -1, 3199, 3200, -1, -1, -1, -1,
- -1, -1, -1, 3201, -1, -1, -1, -1,
- -1, 3202, -1, 3203, 3204, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3205, -1,
- -1, -1, 3206, -1, -1, -1, -1, -1,
- -1, 3207, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3208, -1, -1, -1, 3209,
- -1, -1, -1, -1, 3210, -1, -1, 3211,
- -1, -1, -1, -1, 3212, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3213, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3214, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3215,
- -1, -1, -1, -1, -1, -1, -1, 3216,
- -1, -1, -1, -1, -1, -1, -1, 3217,
- -1, 3218, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3219, 3220, -1, -1, -1,
- -1, -1, -1, 3221, -1, 3222, -1, -1,
- -1, 3223, -1, -1, -1, -1, 3224, -1,
- -1, -1, -1, 3225, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3226, -1, -1,
- -1, -1, -1, -1, -1, 3227, -1, -1,
- -1, -1, 3228, -1, -1, -1, -1, 3229,
- -1, 3230, -1, 3231, -1, -1, -1, -1,
- -1, -1, 3232, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3233, -1, -1, -1,
- -1, 3234, -1, -1, -1, -1, -1, -1,
- 3235, -1, -1, -1, -1, -1, 3236, -1,
- -1, -1, 3237, -1, -1, -1, -1, -1,
- 3238, -1, -1, -1, -1, 3239, 3240, -1,
- 3241, -1, -1, 3242, -1, -1, -1, -1,
- -1, -1, -1, 3243, -1, -1, -1, 3244,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3245, -1, -1, -1, -1, 3246,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3247, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3248, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3249, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3250, -1, -1,
- 3251, -1, -1, -1, -1, -1, 3252, -1,
- -1, -1, -1, -1, -1, 3253, -1, -1,
- -1, 3254, -1, -1, -1, -1, -1, 3255,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3256, -1, -1, -1,
- -1, -1, 3257, -1, -1, -1, -1, 3258,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3259, -1, -1, -1, -1, 3260, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3261, -1, -1, -1, -1, -1,
- -1, 3262, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3263, -1,
- -1, -1, -1, -1, 3264, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3265,
- -1, -1, -1, -1, -1, -1, -1, 3266,
- 3267, -1, -1, -1, -1, 3268, -1, -1,
- -1, 3269, -1, -1, -1, -1, -1, 3270,
- -1, -1, -1, -1, -1, -1, -1, 3271,
- -1, -1, -1, 3272, -1, 3273, 3274, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3275, -1, -1, -1, -1, -1,
- -1, -1, 3276, -1, -1, -1, 3277, -1,
- -1, -1, 3278, -1, -1, -1, -1, -1,
- -1, 3279, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3280, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3281, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3282, -1, -1, -1, 3283, -1, 3284, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3285, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3286, -1, -1,
- 3287, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3288, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3289, -1, 3290, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3291, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3292, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3293, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3294, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3295, -1, -1, -1, -1, -1, -1,
- -1, 3296, -1, -1, -1, 3297, -1, -1,
- -1, -1, -1, -1, -1, -1, 3298, -1,
- 3299, -1, -1, -1, -1, -1, -1, 3300,
- -1, 3301, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3302, -1, -1, 3303,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3304, -1, 3305, -1, -1, -1, -1, -1,
- -1, -1, 3306, -1, 3307, -1, 3308, -1,
- -1, 3309, -1, -1, -1, -1, -1, -1,
- -1, 3310, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3311, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3312, -1, -1, -1, -1, 3313, -1,
- -1, -1, 3314, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3315,
- -1, -1, -1, -1, -1, 3316, 3317, -1,
- -1, 3318, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3319, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3320,
- -1, -1, -1, 3321, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3322, 3323, -1,
- -1, -1, -1, -1, 3324, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3325, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3326, -1, -1, -1, 3327, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3328, -1, -1, -1,
- -1, -1, -1, -1, 3329, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3330, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3331, -1, -1, -1,
- -1, -1, -1, 3332, 3333, -1, -1, -1,
- -1, 3334, -1, -1, -1, -1, -1, -1,
- 3335, -1, -1, -1, -1, -1, -1, 3336,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3337,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3338, -1, -1, -1, -1, 3339, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3340, 3341, -1, -1, -1,
- -1, 3342, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3343, -1, -1,
- 3344, -1, -1, -1, -1, 3345, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3346, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3347, -1, -1, -1, -1, -1, -1, -1,
- -1, 3348, 3349, -1, -1, -1, -1, -1,
- 3350, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3351, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3352, 3353, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3354, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3355, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3356, -1, 3357,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3358,
- -1, -1, -1, -1, -1, 3359, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3360, 3361,
- -1, -1, -1, -1, -1, -1, -1, 3362,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3363, 3364, 3365, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3366, -1, -1, -1,
- -1, 3367, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3368, -1, -1,
- -1, 3369, -1, 3370, -1, 3371, -1, -1,
- -1, -1, -1, 3372, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3373, -1,
- -1, -1, -1, -1, -1, -1, 3374, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3375, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3376, -1, -1, -1, -1,
- -1, 3377, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3378, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3379, -1, -1,
- -1, -1, -1, 3380, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3381, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3382, -1, -1, 3383,
- -1, -1, -1, 3384, 3385, 3386, -1, -1,
- 3387, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3388, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3389, -1, 3390,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3391, -1, -1, -1, 3392,
- 3393, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3394, -1, -1, -1, 3395,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3396, -1, 3397, -1, -1, -1, -1, 3398,
- -1, -1, -1, -1, 3399, -1, -1, 3400,
- -1, -1, 3401, -1, -1, -1, 3402, -1,
- 3403, -1, -1, -1, -1, -1, -1, 3404,
- 3405, -1, -1, -1, -1, -1, -1, 3406,
- -1, -1, -1, 3407, -1, -1, 3408, 3409,
- -1, 3410, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3411, -1, 3412,
- 3413, 3414, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3415, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3416,
- -1, 3417, -1, 3418, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3419, 3420, -1,
- -1, -1, -1, 3421, -1, -1, -1, -1,
- -1, 3422, -1, -1, -1, 3423, -1, -1,
- -1, -1, -1, 3424, -1, -1, -1, -1,
- -1, -1, -1, 3425, -1, -1, -1, -1,
- -1, 3426, -1, -1, -1, -1, 3427, -1,
- -1, -1, -1, -1, -1, -1, -1, 3428,
- -1, 3429, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3430, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3431, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3432,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3433, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3434, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3435, -1, -1, -1, -1, -1, -1,
- -1, -1, 3436, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3437, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3438, -1, -1, -1, -1,
- 3439, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3440, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3441,
- 3442, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3443, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3444, -1, -1,
- -1, 3445, -1, 3446, -1, -1, 3447, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3448, 3449, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3450, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3451, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3452, 3453, -1, -1, -1, -1, -1, 3454,
- -1, -1, -1, -1, 3455, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3456, -1,
- -1, -1, -1, -1, 3457, -1, -1, -1,
- -1, -1, -1, -1, 3458, -1, -1, -1,
- -1, -1, 3459, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3460, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3461,
- -1, -1, -1, -1, 3462, -1, -1, -1,
- -1, 3463, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3464, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3465, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3466, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3467, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3468, 3469, -1,
- 3470, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3471, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3472, -1,
- -1, -1, -1, -1, -1, -1, 3473, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3474, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3475, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3476, -1, -1, -1, -1,
- -1, -1, 3477, -1, -1, -1, -1, -1,
- 3478, -1, -1, -1, -1, -1, 3479, 3480,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3481, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3482, -1, -1, -1, -1, 3483,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3484, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3485, -1, -1, 3486, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3487, -1, -1, -1, -1, -1,
- 3488, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3489, -1, -1, 3490,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3491, -1, -1, -1, -1, -1,
- -1, -1, 3492, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3493, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3494, -1,
- -1, 3495, 3496, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3497, -1, -1, -1, -1, -1,
- 3498, -1, -1, -1, 3499, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3500,
- -1, -1, -1, -1, -1, -1, 3501, -1,
- -1, 3502, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3503, -1, 3504, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3505,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3506, -1, -1, 3507, -1, -1, -1,
- -1, -1, 3508, -1, -1, -1, -1, -1,
- -1, -1, -1, 3509, -1, -1, 3510, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3511, -1, -1, -1, -1, 3512, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3513, -1, 3514, -1, -1, -1, -1,
- -1, 3515, 3516, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3517, -1,
- 3518, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3519,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3520, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3521, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3522, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3523, -1, -1, -1, -1, 3524, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3525, 3526, -1, -1, -1,
- -1, -1, -1, -1, 3527, -1, -1, -1,
- -1, -1, 3528, -1, -1, -1, -1, -1,
- 3529, -1, -1, -1, -1, -1, -1, -1,
- 3530, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3531, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3532, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3533,
- -1, -1, -1, -1, 3534, -1, -1, -1,
- -1, -1, -1, -1, 3535, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3536, -1, -1, 3537,
- -1, -1, -1, -1, -1, -1, 3538, 3539,
- 3540, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3541, -1, -1, -1,
- 3542, -1, -1, -1, 3543, -1, -1, 3544,
- -1, 3545, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3546, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3547, -1, -1, -1, -1, -1, -1, 3548,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3549, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3550, -1, -1,
- -1, -1, -1, -1, 3551, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3552, -1, -1, -1, -1,
- 3553, -1, 3554, -1, 3555, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3556,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3557, 3558, -1, -1,
- -1, -1, -1, -1, -1, 3559, -1, -1,
- -1, 3560, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3561,
- -1, -1, -1, -1, -1, 3562, -1, -1,
- -1, -1, -1, -1, -1, 3563, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3564, -1, -1, -1,
- -1, -1, -1, -1, 3565, -1, -1, -1,
- -1, -1, -1, -1, 3566, -1, -1, -1,
- 3567, -1, -1, -1, -1, -1, -1, -1,
- -1, 3568, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3569, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3570, -1, -1, -1, -1, -1, -1, -1,
- 3571, -1, -1, -1, -1, -1, 3572, -1,
- -1, -1, -1, -1, 3573, -1, -1, -1,
- 3574, -1, -1, -1, -1, -1, -1, 3575,
- -1, -1, -1, -1, -1, -1, -1, 3576,
- 3577, 3578, -1, -1, -1, -1, 3579, -1,
- -1, -1, -1, -1, -1, -1, -1, 3580,
- -1, -1, 3581, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3582, -1,
- -1, -1, -1, -1, -1, -1, -1, 3583,
- -1, -1, -1, -1, -1, 3584, 3585, -1,
- -1, 3586, 3587, 3588, -1, 3589, -1, -1,
- -1, -1, 3590, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3591, -1, 3592,
- -1, -1, -1, 3593, -1, -1, -1, -1,
- 3594, 3595, -1, 3596, -1, -1, 3597, -1,
- -1, -1, -1, -1, -1, -1, -1, 3598,
- 3599, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3600, -1, -1, -1, 3601, -1, -1, -1,
- -1, -1, -1, -1, 3602, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3603, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3604, -1, -1, -1, 3605,
- -1, -1, -1, -1, -1, 3606, -1, -1,
- -1, 3607, -1, -1, -1, -1, -1, -1,
- -1, -1, 3608, -1, -1, -1, -1, -1,
- -1, 3609, -1, -1, -1, -1, 3610, -1,
- -1, 3611, -1, -1, 3612, -1, -1, -1,
- -1, 3613, -1, -1, -1, -1, 3614, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3615, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3616, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3617, -1, 3618, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3619, -1, -1, -1, -1, -1, 3620, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3621, -1, -1, -1, -1, -1,
- -1, -1, 3622, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3623, -1, 3624, 3625,
- -1, -1, -1, -1, -1, 3626, -1, -1,
- -1, -1, -1, -1, 3627, -1, -1, -1,
- -1, -1, -1, -1, 3628, 3629, -1, 3630,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3631, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3632, -1, -1, -1, -1, -1,
- -1, 3633, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3634, -1, -1,
- -1, -1, -1, -1, -1, 3635, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3636, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3637, -1, -1, -1, -1, -1, -1,
- -1, -1, 3638, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3639, -1, -1, -1, -1,
- -1, -1, -1, -1, 3640, -1, 3641, -1,
- -1, -1, 3642, -1, 3643, -1, -1, -1,
- -1, -1, -1, -1, 3644, -1, -1, -1,
- -1, -1, 3645, -1, -1, 3646, -1, -1,
- -1, -1, 3647, -1, -1, -1, -1, -1,
- -1, -1, -1, 3648, -1, -1, -1, -1,
- -1, -1, -1, -1, 3649, -1, -1, -1,
- 3650, -1, -1, -1, 3651, -1, -1, -1,
- -1, -1, -1, -1, 3652, -1, -1, -1,
- -1, 3653, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3654, -1, 3655,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3656, -1, -1, 3657, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3658, 3659, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3660, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3661, -1, -1, -1, -1, -1,
- 3662, -1, 3663, 3664, -1, -1, 3665, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3666, -1, -1, -1, 3667, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3668, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3669,
- -1, 3670, -1, -1, -1, 3671, 3672, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3673, -1, -1,
- 3674, -1, -1, 3675, -1, -1, 3676, -1,
- -1, -1, -1, -1, 3677, -1, -1, -1,
- -1, -1, -1, 3678, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3679, -1, -1, -1,
- 3680, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3681, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3682, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3683, -1, -1, -1,
- -1, -1, -1, 3684, -1, -1, 3685, -1,
- -1, -1, -1, -1, -1, 3686, -1, -1,
- 3687, -1, 3688, -1, -1, -1, -1, -1,
- -1, -1, 3689, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3690, -1, 3691, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3692, -1, 3693, -1, -1, -1, -1, -1,
- 3694, 3695, -1, -1, -1, -1, -1, 3696,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3697, -1, -1,
- -1, 3698, -1, -1, -1, -1, -1, -1,
- 3699, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3700, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3701, -1, -1, 3702, 3703, -1,
- -1, -1, -1, -1, -1, 3704, -1, -1,
- -1, 3705, -1, -1, -1, -1, -1, 3706,
- 3707, -1, -1, -1, 3708, -1, -1, 3709,
- -1, -1, -1, -1, -1, 3710, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3711, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3712, -1,
- -1, -1, -1, 3713, -1, 3714, -1, -1,
- -1, 3715, -1, -1, -1, -1, 3716, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3717, -1,
- -1, 3718, -1, -1, -1, 3719, -1, -1,
- -1, -1, -1, -1, -1, 3720, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3721, -1, -1, -1,
- -1, -1, -1, -1, 3722, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3723, -1, -1, -1, 3724,
- -1, -1, -1, 3725, 3726, 3727, 3728, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3729, 3730, -1, -1, -1, -1, -1, -1,
- -1, -1, 3731, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3732, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3733, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3734, -1, -1, 3735, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3736,
- -1, -1, -1, 3737, -1, -1, -1, -1,
- -1, -1, 3738, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3739,
- -1, -1, 3740, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3741, 3742, -1,
- -1, -1, 3743, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3744, -1, 3745, -1, -1, 3746, 3747, -1,
- -1, 3748, -1, 3749, -1, -1, -1, -1,
- 3750, 3751, -1, -1, -1, 3752, -1, 3753,
- 3754, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3755, -1, -1, -1, -1, -1, -1,
- 3756, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3757, -1, -1,
- -1, -1, -1, -1, -1, 3758, 3759, -1,
- 3760, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3761, -1, -1, 3762, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3763, -1,
- -1, -1, -1, 3764, -1, -1, -1, -1,
- -1, -1, -1, 3765, -1, -1, 3766, -1,
- -1, -1, -1, -1, -1, -1, -1, 3767,
- -1, -1, 3768, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3769, -1, -1,
- -1, -1, -1, 3770, -1, -1, -1, -1,
- -1, -1, 3771, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3772, 3773, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3774, -1, -1, -1, -1, -1, 3775,
- -1, -1, -1, 3776, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3777, -1, -1, -1, -1, -1,
- -1, 3778, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3779, -1, -1, -1, 3780, 3781, -1,
- 3782, -1, -1, -1, -1, -1, 3783, -1,
- -1, 3784, -1, 3785, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3786, -1, 3787,
- -1, 3788, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3789,
- 3790, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3791, -1, -1, -1, 3792,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3793, -1, -1, -1, -1,
- 3794, -1, -1, -1, -1, -1, -1, 3795,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3796, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3797, -1, -1, -1,
- 3798, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3799, -1, 3800, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3801, -1,
- -1, -1, -1, -1, -1, 3802, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3803, -1, -1, -1,
- -1, -1, -1, -1, 3804, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3805, -1, -1, 3806, 3807, -1, -1, 3808,
- -1, -1, -1, -1, -1, -1, -1, 3809,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3810,
- -1, -1, 3811, -1, -1, -1, -1, -1,
- -1, -1, -1, 3812, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3813, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3814, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3815, -1,
- -1, 3816, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3817, -1,
- 3818, -1, -1, -1, -1, -1, -1, 3819,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3820, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3821, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3822, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3823,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3824, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3825, -1, 3826, -1, -1, -1, 3827, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3828, -1, -1,
- -1, -1, 3829, -1, -1, -1, -1, 3830,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3831, -1,
- -1, -1, 3832, -1, -1, 3833, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3834, -1, -1, -1, 3835, -1, -1, -1,
- 3836, -1, -1, -1, -1, -1, -1, -1,
- 3837, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3838, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3839, -1, -1, -1, -1, -1, -1, -1,
- 3840, -1, 3841, 3842, -1, -1, -1, 3843,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3844, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3845, -1, -1,
- 3846, -1, -1, 3847, -1, -1, -1, -1,
- 3848, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3849, -1, -1, -1, -1, 3850,
- -1, -1, -1, -1, -1, 3851, -1, -1,
- -1, 3852, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3853, -1, -1, -1,
- -1, -1, 3854, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3855, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3856, -1,
- -1, -1, -1, -1, -1, -1, 3857, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3858, 3859, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3860, -1, -1,
- -1, -1, -1, -1, -1, -1, 3861, -1,
- -1, -1, 3862, -1, -1, 3863, -1, -1,
- -1, -1, -1, -1, -1, -1, 3864, -1,
- -1, 3865, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3866, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3867, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3868, -1, -1, -1, -1,
- -1, 3869, -1, -1, 3870, -1, -1, -1,
- -1, -1, -1, -1, -1, 3871, -1, -1,
- -1, -1, 3872, -1, -1, -1, 3873, -1,
- -1, -1, -1, -1, -1, -1, 3874, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3875, -1, -1, -1, -1,
- -1, -1, -1, -1, 3876, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3877, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3878, -1, -1, -1, -1, 3879, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3880, -1, -1,
- 3881, -1, 3882, -1, -1, -1, -1, -1,
- 3883, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3884, -1, -1, -1,
- -1, -1, -1, 3885, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3886, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3887,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3888, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3889, -1, -1, -1, -1, -1, -1, 3890,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3891,
- -1, -1, -1, 3892, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3893, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3894, -1, -1, 3895, -1,
- -1, -1, -1, -1, -1, -1, -1, 3896,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3897, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3898, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3899,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3900, 3901,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3902, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3903, -1, 3904, 3905, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3906, -1, -1,
- -1, -1, -1, -1, 3907, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3908, -1, 3909, -1, -1, -1, -1, 3910,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3911, -1, -1, -1, -1, -1, -1,
- -1, 3912, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3913, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3914, -1, -1, 3915, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3916, -1, -1, -1,
- 3917, -1, -1, -1, 3918, -1, 3919, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3920, 3921, -1, -1,
- -1, -1, -1, 3922, -1, -1, -1, -1,
- -1, -1, 3923, -1, -1, 3924, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3925, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3926, -1, 3927, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3928, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3929, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3930, -1, -1, -1, -1, -1,
- 3931, -1, -1, -1, -1, -1, -1, 3932,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3933, -1, -1, -1, -1,
- -1, -1, -1, 3934, -1, -1, 3935, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3936, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3937, -1, -1, -1,
- -1, -1, -1, 3938, 3939, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3940, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3941, -1, 3942, -1, -1, -1, -1, -1,
- -1, -1, 3943, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3944, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3945, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3946, -1, -1, -1, -1, -1, 3947,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3948, -1, -1, -1, 3949,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3950,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3951, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3952,
- -1, -1, -1, -1, -1, -1, 3953, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3954,
- 3955, 3956, -1, -1, -1, -1, 3957, -1,
- -1, -1, -1, -1, 3958, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3959, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 3960, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3961, -1, -1, -1, -1, 3962, -1,
- 3963, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 3964, -1, -1, -1, 3965,
- -1, -1, -1, 3966, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3967,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3968, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3969, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3970, -1, 3971, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3972, -1, -1, -1, -1, -1,
- 3973, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3974, -1, -1, -1, -1, 3975, -1,
- -1, -1, -1, 3976, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3977, -1, -1,
- -1, -1, -1, -1, 3978, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3979, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3980,
- 3981, -1, -1, -1, 3982, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3983, 3984, -1, -1, -1, 3985,
- -1, -1, -1, -1, -1, 3986, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 3987, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 3988, -1,
- -1, -1, -1, -1, -1, -1, -1, 3989,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 3990, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 3991, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3992,
- -1, -1, -1, -1, -1, -1, 3993, -1,
- -1, -1, -1, -1, 3994, -1, -1, -1,
- -1, 3995, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3996, -1, -1, -1,
- -1, -1, -1, -1, 3997, -1, -1, -1,
- -1, 3998, -1, -1, -1, -1, 3999, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4000, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4001, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4002, -1, -1, -1, -1, 4003, -1,
- -1, -1, -1, -1, -1, 4004, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4005, -1, -1, -1, -1, 4006, -1, -1,
- -1, -1, -1, -1, -1, 4007, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4008, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4009, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4010, 4011, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4012,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4013, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4014, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4015, -1, -1, -1,
- -1, 4016, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4017, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4018, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4019, -1, -1, -1,
- -1, -1, -1, -1, 4020, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4021, -1, -1, -1, -1, -1, -1, -1,
- 4022, -1, -1, -1, -1, -1, -1, 4023,
- -1, -1, -1, -1, -1, -1, 4024, -1,
- 4025, -1, -1, -1, -1, -1, -1, 4026,
- 4027, -1, -1, -1, -1, 4028, 4029, -1,
- 4030, -1, 4031, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4032, -1, -1, -1, -1, -1,
- 4033, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4034, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4035, -1, -1, -1, -1,
- 4036, 4037, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4038,
- -1, -1, -1, -1, -1, -1, 4039, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4040, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4041,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4042, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4043, -1, -1, -1,
- -1, -1, -1, 4044, -1, -1, -1, -1,
- -1, -1, -1, 4045, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4046,
- -1, 4047, -1, -1, -1, -1, -1, 4048,
- -1, -1, 4049, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4050, -1, -1, 4051,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4052, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4053, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4054, -1, 4055, -1, -1,
- 4056, -1, -1, -1, -1, 4057, -1, -1,
- 4058, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4059, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4060,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4061, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4062, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4063, -1, -1,
- 4064, -1, -1, -1, -1, -1, -1, 4065,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4066, -1, 4067, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4068, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4069,
- -1, -1, -1, 4070, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4071, -1, -1, -1, 4072, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4073, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4074, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4075,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4076, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4077, -1, 4078, -1, -1,
- -1, -1, -1, -1, 4079, -1, -1, -1,
- -1, -1, 4080, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4081, -1, -1, -1,
- -1, -1, -1, 4082, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4083,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4084, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4085, -1, -1, 4086, 4087, -1, 4088, -1,
- -1, -1, -1, -1, -1, 4089, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4090, -1, -1, -1, -1, -1,
- -1, -1, 4091, 4092, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4093, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4094, -1, 4095, -1, -1, -1, -1,
- 4096, -1, -1, 4097, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4098, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4099, -1, -1, -1, -1, -1, -1,
- -1, 4100, -1, -1, -1, 4101, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4102, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4103, -1,
- -1, -1, -1, 4104, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4105, -1, -1, 4106, -1, -1,
- -1, -1, -1, 4107, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4108, -1, -1, -1, -1,
- -1, -1, -1, 4109, -1, -1, -1, -1,
- -1, -1, 4110, -1, -1, -1, -1, 4111,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4112, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4113, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4114, -1, 4115, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4116, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4117, -1, -1,
- -1, 4118, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4119,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4120, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4121, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4122, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4123, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4124, -1, -1, -1, -1, -1,
- 4125, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4126, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4127, 4128,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4129, -1,
- -1, -1, -1, -1, 4130, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4131,
- -1, -1, 4132, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4133, -1, 4134,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4135, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4136,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4137, -1, -1, -1, -1, 4138,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4139, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4140, -1, -1, 4141, -1, -1, -1,
- -1, -1, -1, -1, -1, 4142, -1, -1,
- -1, -1, -1, -1, -1, 4143, 4144, -1,
- -1, -1, -1, -1, -1, -1, -1, 4145,
- 4146, -1, 4147, -1, 4148, -1, -1, 4149,
- -1, -1, -1, -1, -1, 4150, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4151, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4152, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4153, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4154, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4155, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4156, -1, -1, 4157, -1, -1, -1, -1,
- -1, 4158, -1, 4159, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4160,
- -1, -1, -1, -1, -1, -1, -1, 4161,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4162, -1, -1, -1, -1, 4163,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4164, -1, -1, -1, -1,
- -1, 4165, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4166, -1, -1, -1, 4167, -1,
- -1, -1, -1, -1, -1, -1, -1, 4168,
- -1, -1, 4169, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4170, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4171, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4172, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4173, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4174, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4175, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4176, -1, 4177,
- -1, -1, -1, 4178, -1, 4179, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4180,
- -1, -1, 4181, -1, -1, -1, -1, -1,
- -1, -1, 4182, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4183, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4184, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4185,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4186, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4187,
- 4188, -1, -1, -1, 4189, 4190, -1, -1,
- -1, -1, -1, -1, 4191, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4192, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4193, -1, -1, -1, -1, 4194,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4195, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4196, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4197,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4198, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4199,
- 4200, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4201, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4202, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4203,
- -1, -1, -1, -1, 4204, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4205, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4206, -1, 4207,
- -1, -1, 4208, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4209, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4210, -1, -1, -1, 4211, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4212,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4213, -1,
- 4214, 4215, -1, 4216, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4217, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4218, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4219, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4220, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4221, -1, -1,
- -1, -1, -1, -1, 4222, -1, -1, -1,
- -1, -1, 4223, -1, -1, -1, -1, -1,
- 4224, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4225, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4226, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4227, -1, -1, -1, -1, -1,
- -1, -1, -1, 4228, -1, -1, -1, -1,
- -1, -1, 4229, -1, -1, -1, 4230, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4231, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4232, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4233, -1, -1, -1,
- -1, -1, 4234, -1, -1, -1, -1, 4235,
- -1, -1, 4236, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4237, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4238, -1, -1, -1, -1, -1, -1, -1,
- -1, 4239, -1, -1, -1, 4240, -1, 4241,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4242, -1, -1, -1, -1,
- -1, -1, 4243, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4244, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4245, 4246, -1, 4247, -1, -1, 4248, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4249, -1, -1,
- -1, -1, -1, -1, -1, -1, 4250, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4251, -1, 4252, -1, -1, -1,
- -1, -1, -1, -1, -1, 4253, -1, -1,
- -1, -1, -1, -1, -1, 4254, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4255, 4256, 4257, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4258, -1, -1,
- -1, -1, -1, -1, -1, -1, 4259, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4260, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4261,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4262, -1, -1, 4263, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4264, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4265, -1, -1, -1, 4266,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4267, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4268, -1, -1, -1, -1, -1, 4269,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4270, -1, -1, -1, 4271, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4272, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4273, -1, -1, -1, -1, -1, 4274, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4275, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4276, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4277, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4278, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4279, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4280,
- -1, -1, -1, -1, -1, -1, -1, 4281,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4282, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4283, -1, -1, 4284,
- -1, -1, 4285, -1, -1, 4286, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4287, 4288, -1,
- -1, -1, 4289, -1, -1, -1, 4290, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4291, -1, -1, -1, -1, -1, -1,
- -1, -1, 4292, -1, -1, 4293, -1, 4294,
- -1, -1, -1, -1, -1, -1, 4295, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4296, -1, -1, 4297, 4298, -1, 4299,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4300, -1, -1, -1, -1, 4301,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4302, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4303, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4304, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4305, -1, -1, -1, -1, 4306, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4307, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4308, -1, -1,
- -1, 4309, -1, -1, -1, -1, -1, -1,
- -1, 4310, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4311, 4312, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4313, -1, -1, 4314, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4315, -1, -1, -1, -1, -1,
- -1, -1, 4316, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4317, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4318, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4319, -1, -1,
- -1, -1, -1, -1, 4320, -1, -1, -1,
- -1, 4321, -1, -1, -1, -1, -1, -1,
- -1, -1, 4322, -1, -1, -1, -1, -1,
- -1, 4323, -1, 4324, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4325, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4326, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4327, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4328, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4329, -1,
- -1, 4330, -1, 4331, -1, -1, -1, -1,
- -1, -1, -1, -1, 4332, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4333, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4334, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4335, -1, -1, -1, -1,
- -1, -1, 4336, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4337, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4338, -1, -1, 4339, -1,
- 4340, -1, -1, 4341, 4342, -1, -1, -1,
- -1, -1, -1, 4343, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4344, 4345,
- -1, -1, 4346, -1, -1, -1, 4347, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4348, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4349,
- -1, -1, -1, -1, -1, -1, 4350, -1,
- -1, -1, -1, -1, -1, 4351, 4352, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4353, -1, -1, -1, 4354, -1, 4355, -1,
- -1, -1, 4356, -1, -1, -1, -1, -1,
- -1, -1, 4357, -1, -1, -1, 4358, -1,
- -1, -1, -1, -1, -1, 4359, -1, -1,
- -1, -1, -1, -1, 4360, -1, -1, -1,
- -1, -1, -1, -1, 4361, -1, 4362, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4363, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4364, -1, -1, -1, -1, -1, -1,
- -1, 4365, -1, 4366, -1, -1, -1, -1,
- -1, -1, -1, -1, 4367, -1, -1, -1,
- -1, -1, -1, -1, 4368, -1, -1, -1,
- -1, -1, -1, -1, -1, 4369, 4370, -1,
- -1, 4371, -1, -1, -1, -1, 4372, -1,
- -1, -1, -1, 4373, 4374, -1, -1, -1,
- -1, -1, 4375, -1, -1, -1, 4376, -1,
- -1, -1, 4377, -1, -1, -1, -1, -1,
- -1, -1, 4378, -1, -1, 4379, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4380, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4381, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4382, -1, -1, -1, 4383, -1, -1, 4384,
- 4385, -1, -1, -1, -1, 4386, -1, -1,
- -1, -1, -1, -1, 4387, -1, -1, 4388,
- 4389, -1, -1, -1, -1, -1, 4390, -1,
- -1, -1, -1, -1, -1, -1, -1, 4391,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4392,
- 4393, -1, -1, 4394, -1, -1, -1, -1,
- 4395, -1, -1, 4396, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4397, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4398, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4399,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4400, -1, -1, -1,
- -1, 4401, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4402, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4403, -1, -1,
- -1, -1, -1, -1, 4404, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4405,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4406, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4407,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4408, -1, -1,
- -1, -1, -1, 4409, -1, -1, -1, -1,
- 4410, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4411, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4412, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4413,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4414, -1, 4415, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4416, -1, -1, -1, -1, -1, -1, -1,
- 4417, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4418, -1, -1, -1, 4419, -1, -1,
- -1, -1, 4420, 4421, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4422, 4423, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4424, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4425, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4426,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4427, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4428, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4429, -1,
- -1, -1, 4430, -1, -1, -1, -1, -1,
- -1, 4431, -1, -1, -1, -1, -1, 4432,
- -1, -1, -1, -1, -1, 4433, -1, -1,
- -1, -1, -1, -1, -1, -1, 4434, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4435, -1, -1, -1, -1, -1, 4436,
- -1, -1, -1, -1, 4437, 4438, -1, -1,
- 4439, -1, -1, -1, -1, -1, -1, -1,
- 4440, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4441, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4442, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4443, -1, -1,
- -1, -1, -1, -1, 4444, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4445, -1, -1, -1, -1,
- -1, -1, -1, 4446, -1, -1, -1, -1,
- -1, -1, 4447, -1, -1, 4448, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4449, -1, -1, -1, -1, -1,
- -1, 4450, -1, -1, 4451, -1, -1, -1,
- -1, -1, -1, -1, 4452, -1, -1, -1,
- -1, -1, -1, 4453, -1, -1, -1, -1,
- -1, -1, 4454, -1, -1, 4455, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4456,
- -1, -1, -1, -1, -1, -1, -1, 4457,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4458, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4459,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4460,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4461, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4462, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4463, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4464,
- 4465, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4466, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4467, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4468, -1, -1, 4469, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4470, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4471, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4472, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4473, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4474, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4475,
- 4476, -1, -1, -1, -1, -1, -1, -1,
- 4477, -1, -1, -1, -1, -1, 4478, -1,
- -1, -1, 4479, -1, -1, -1, -1, -1,
- -1, 4480, -1, -1, -1, -1, 4481, 4482,
- -1, -1, -1, 4483, -1, 4484, -1, -1,
- -1, -1, -1, 4485, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4486, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4487,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4488, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4489,
- -1, -1, -1, -1, -1, -1, -1, 4490,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4491,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4492, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4493, -1, 4494, -1, -1,
- -1, -1, -1, -1, -1, -1, 4495, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4496, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4497, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4498, -1,
- -1, -1, -1, -1, -1, 4499, -1, 4500,
- -1, -1, -1, -1, 4501, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4502, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4503, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4504, 4505, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4506, 4507,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4508, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4509, -1, -1, -1, -1, -1, -1, -1,
- 4510, -1, -1, -1, -1, -1, -1, -1,
- -1, 4511, -1, -1, -1, -1, -1, -1,
- -1, 4512, -1, -1, -1, -1, 4513, -1,
- -1, -1, -1, -1, -1, 4514, -1, -1,
- 4515, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4516, -1, -1, 4517, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4518, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4519, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4520, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4521, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4522, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4523,
- -1, -1, 4524, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4525, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4526, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4527, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4528,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4529, 4530, -1,
- -1, -1, -1, -1, 4531, 4532, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4533, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4534, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4535, -1, -1,
- -1, -1, -1, -1, -1, -1, 4536, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4537, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4538, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4539, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4540, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4541,
- -1, -1, -1, -1, -1, 4542, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4543, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4544, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4545, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4546, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4547, -1,
- -1, -1, -1, -1, -1, -1, -1, 4548,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4549, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4550, -1, -1, -1, 4551, -1,
- -1, -1, 4552, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4553, -1, 4554, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4555, -1, -1, -1,
- -1, -1, 4556, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4557,
- -1, -1, -1, -1, 4558, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4559, -1,
- -1, -1, -1, -1, -1, 4560, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4561, -1, -1, -1,
- -1, -1, -1, -1, 4562, -1, -1, -1,
- -1, 4563, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4564, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4565, -1, -1, -1,
- -1, 4566, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4567, -1, -1, -1, -1,
- -1, -1, -1, 4568, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4569, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4570, -1, -1, 4571, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4572, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4573,
- -1, -1, 4574, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4575, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4576, -1, -1, 4577,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4578, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4579,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4580, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4581, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4582, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4583, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4584, -1, -1, -1, 4585,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4586, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4587, -1, -1, 4588, -1, -1, 4589, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4590,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4591, -1, -1, -1, -1,
- -1, 4592, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4593, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4594, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4595, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4596, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4597, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4598,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4599, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4600, 4601,
- 4602, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4603, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4604, -1, -1, -1, 4605,
- -1, -1, -1, -1, -1, -1, 4606, 4607,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4608, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4609, -1, 4610, 4611, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4612, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4613, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4614, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4615, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4616, 4617,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4618, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4619, -1, -1,
- 4620, -1, -1, -1, -1, -1, -1, -1,
- -1, 4621, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4622,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4623, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4624,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4625, -1, -1, -1, -1,
- -1, -1, -1, -1, 4626, -1, -1, -1,
- 4627, -1, -1, -1, 4628, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4629, -1, -1, -1, -1, 4630, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4631, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4632,
- -1, -1, -1, -1, -1, 4633, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4634,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4635, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4636,
- 4637, -1, -1, 4638, -1, -1, -1, -1,
- -1, 4639, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4640, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4641, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4642, -1, -1, 4643, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4644, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4645, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4646, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4647,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4648, -1, -1, -1, -1, 4649, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4650,
- -1, -1, -1, -1, 4651, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4652, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4653, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4654, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4655, -1, -1, -1, -1, -1, -1, -1,
- 4656, -1, -1, 4657, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4658, 4659, 4660,
- -1, -1, -1, -1, 4661, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4662, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4663, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4664, -1, -1,
- -1, -1, -1, -1, 4665, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4666, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4667, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4668, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4669, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4670, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4671, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4672, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4673, -1, -1,
- -1, -1, 4674, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4675, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4676, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4677, -1, -1, -1, -1, -1, -1, -1,
- -1, 4678, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4679, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4680,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4681, -1, -1, 4682, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4683, -1, -1, -1, -1,
- -1, -1, 4684, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4685, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4686, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4687, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4688, -1, -1, -1, 4689, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4690, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4691,
- -1, -1, -1, -1, -1, -1, 4692, -1,
- -1, -1, 4693, -1, -1, -1, -1, -1,
- -1, 4694, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4695, 4696, -1, -1, -1, -1, -1,
- 4697, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4698,
- -1, 4699, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4700, -1, -1, -1, -1, -1,
- -1, -1, -1, 4701, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4702, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4703, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4704, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4705, -1, -1, -1, -1, -1,
- -1, -1, 4706, -1, -1, 4707, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4708,
- -1, -1, -1, -1, -1, -1, 4709, -1,
- -1, -1, -1, -1, 4710, -1, -1, -1,
- 4711, -1, -1, -1, -1, -1, -1, -1,
- 4712, -1, -1, -1, -1, 4713, 4714, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4715, 4716, 4717, -1,
- -1, -1, -1, -1, -1, 4718, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4719, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4720, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4721, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4722,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4723, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4724, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4725,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4726, -1, -1, -1, -1, -1, -1, 4727,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4728, -1, -1, -1, -1, -1, -1, -1,
- -1, 4729, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4730, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4731, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4732, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4733, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4734, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4735, -1, -1, -1, 4736,
- -1, -1, -1, -1, -1, 4737, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4738, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4739, -1, -1, -1, 4740, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4741, -1, -1,
- -1, -1, -1, 4742, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4743,
- -1, -1, -1, -1, -1, 4744, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4745,
- 4746, -1, -1, -1, -1, -1, -1, 4747,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4748, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4749, -1, -1, -1, -1, -1, -1,
- 4750, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4751, -1, -1, 4752,
- -1, -1, -1, 4753, -1, -1, -1, -1,
- -1, -1, 4754, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4755, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4756, -1, -1, 4757, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4758, -1, -1, -1, -1, -1,
- -1, 4759, -1, -1, -1, -1, -1, -1,
- -1, 4760, -1, -1, -1, -1, -1, 4761,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4762, -1, -1, -1, -1, -1, -1, -1,
- 4763, -1, -1, -1, -1, 4764, -1, -1,
- -1, -1, -1, -1, 4765, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4766, 4767,
- -1, 4768, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4769, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4770, 4771, -1, 4772,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4773, -1, -1, -1, 4774, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4775, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4776, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4777, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4778, -1, -1, -1, -1, 4779,
- -1, -1, -1, 4780, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4781,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4782, -1, -1, 4783, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4784, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4785, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4786, -1, -1, 4787, -1,
- -1, -1, -1, -1, -1, 4788, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4789, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4790, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4791, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4792, -1, -1, -1, -1, -1, 4793,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4794, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4795, -1, -1, -1,
- -1, -1, -1, -1, -1, 4796, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4797, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4798, -1,
- -1, 4799, -1, -1, -1, -1, -1, -1,
- -1, -1, 4800, -1, -1, -1, -1, -1,
- 4801, -1, 4802, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4803, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4804, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4805, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4806, -1,
- -1, -1, -1, -1, 4807, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4808, 4809, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4810, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4811, -1, -1,
- -1, -1, -1, -1, -1, 4812, 4813, -1,
- -1, -1, -1, -1, 4814, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4815, -1, -1, -1, -1,
- 4816, -1, -1, -1, -1, -1, -1, -1,
- -1, 4817, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4818,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4819, -1, -1, -1, -1, -1,
- -1, -1, 4820, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4821, 4822, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4823, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4824, -1, -1, -1, -1, -1, -1, 4825,
- -1, -1, -1, -1, -1, -1, -1, 4826,
- -1, -1, -1, -1, -1, 4827, -1, -1,
- 4828, 4829, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4830, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4831, -1,
- -1, -1, 4832, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4833, -1,
- -1, -1, 4834, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4835, -1, -1, -1,
- -1, -1, -1, -1, 4836, 4837, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4838, -1, -1, 4839, 4840,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4841, -1,
- -1, -1, -1, -1, -1, -1, 4842, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4843, -1, 4844, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4845, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4846, -1, -1, 4847, -1, -1,
- -1, -1, -1, -1, 4848, -1, -1, 4849,
- -1, -1, -1, -1, 4850, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4851, -1,
- -1, -1, -1, -1, -1, -1, 4852, 4853,
- -1, -1, 4854, -1, -1, -1, -1, -1,
- 4855, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4856, -1, -1, -1,
- 4857, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4858, -1,
- -1, -1, -1, -1, -1, -1, -1, 4859,
- -1, -1, -1, -1, -1, 4860, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4861,
- 4862, 4863, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4864,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4865, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4866, -1, -1, -1, -1, 4867, -1,
- -1, -1, -1, -1, -1, -1, 4868, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4869, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4870,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4871, -1, 4872, -1,
- -1, -1, 4873, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4874, 4875, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4876, -1, -1, 4877, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4878, -1, -1, -1, -1, 4879,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4880, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4881, -1,
- -1, 4882, 4883, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4884, -1, 4885,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4886, -1, -1, -1, -1,
- -1, -1, -1, -1, 4887, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4888, -1, -1, -1, -1,
- -1, -1, -1, 4889, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4890, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4891, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4892,
- -1, -1, 4893, -1, -1, -1, -1, -1,
- -1, -1, 4894, -1, -1, -1, -1, -1,
- 4895, -1, -1, -1, -1, -1, -1, -1,
- 4896, -1, -1, -1, -1, -1, 4897, 4898,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4899, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4900, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4901, 4902,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4903, -1, -1, -1, 4904, -1,
- -1, -1, -1, -1, -1, 4905, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4906, 4907, -1, -1, 4908, -1,
- -1, -1, -1, 4909, -1, -1, -1, 4910,
- -1, -1, 4911, -1, -1, -1, -1, 4912,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4913, -1, -1, 4914, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4915, -1, -1, 4916,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4917, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4918, -1, -1, -1, -1,
- -1, -1, -1, 4919, 4920, -1, -1, -1,
- -1, -1, 4921, -1, -1, -1, -1, -1,
- -1, -1, -1, 4922, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4923, -1, -1, -1, -1, 4924, -1, 4925,
- -1, -1, -1, -1, -1, 4926, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4927, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4928, -1, -1,
- -1, -1, -1, -1, 4929, -1, 4930, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4931, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4932, -1, 4933, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4934, -1, -1, -1, -1, -1,
- -1, -1, -1, 4935, -1, -1, -1, -1,
- -1, 4936, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4937, -1, -1, -1, 4938, -1,
- -1, -1, -1, -1, -1, -1, -1, 4939,
- -1, 4940, -1, -1, -1, -1, -1, -1,
- -1, 4941, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4942, -1,
- -1, -1, -1, -1, -1, 4943, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4944, 4945, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4946,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4947, -1, -1, -1,
- -1, -1, 4948, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4949, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4950, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4951, -1, 4952, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4953, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 4954, -1, -1, -1, -1, 4955,
- -1, -1, -1, -1, -1, 4956, -1, 4957,
- -1, -1, -1, 4958, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4959,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4960, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4961, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4962, -1, -1, -1, -1, 4963, -1, -1,
- -1, -1, -1, -1, -1, -1, 4964, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4965, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4966, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4967, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4968,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4969,
- -1, -1, -1, -1, -1, 4970, -1, -1,
- -1, -1, -1, -1, -1, 4971, -1, 4972,
- 4973, -1, -1, -1, -1, -1, -1, 4974,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4975, -1, -1, 4976, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4977, 4978, -1,
- -1, -1, -1, -1, -1, -1, 4979, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4980, -1, -1,
- -1, -1, -1, -1, 4981, -1, -1, -1,
- 4982, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4983,
- -1, -1, -1, -1, -1, 4984, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4985,
- -1, -1, -1, -1, -1, 4986, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4987, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4988,
- 4989, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4990, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4991, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 4992, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4993, 4994, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 4995, -1, -1, -1, 4996, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4997, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 4998, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4999, -1, -1, -1, -1,
- 5000, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5001, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5002, -1, -1, -1, -1, 5003,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5004, -1, 5005, -1,
- -1, -1, -1, -1, 5006, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5007, -1, -1, -1, 5008, -1, -1,
- -1, -1, 5009, 5010, -1, -1, -1, -1,
- -1, -1, -1, 5011, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5012, -1, -1, -1, 5013,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5014, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5015, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5016, -1, -1, -1,
- -1, -1, 5017, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5018, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5019,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5020, -1, -1, -1,
- 5021, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5022, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5023, -1, -1, -1,
- -1, -1, -1, -1, -1, 5024, 5025, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5026, -1, -1, 5027,
- -1, -1, 5028, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5029, -1, -1, -1, 5030, -1, -1, -1,
- -1, -1, -1, -1, 5031, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5032, -1, 5033, -1, 5034, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5035, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5036, -1, 5037, -1,
- -1, -1, -1, -1, -1, 5038, -1, -1,
- -1, -1, 5039, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5040,
- -1, -1, -1, 5041, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5042, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5043, -1,
- -1, -1, -1, -1, -1, 5044, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5045,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5046, -1,
- -1, -1, -1, 5047, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5048, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5049, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5050, -1, -1,
- -1, 5051, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5052, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5053, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5054, -1, -1, -1, -1, -1, -1, 5055,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5056, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5057, -1, -1, -1, -1, -1, -1,
- -1, -1, 5058, -1, -1, -1, -1, -1,
- -1, 5059, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5060, 5061, -1,
- -1, -1, -1, -1, -1, 5062, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5063, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5064, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5065, -1,
- -1, -1, -1, -1, 5066, -1, -1, -1,
- -1, 5067, -1, -1, -1, -1, -1, -1,
- 5068, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5069, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5070, -1, -1, -1, -1, -1,
- 5071, -1, -1, -1, 5072, -1, -1, -1,
- -1, -1, 5073, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5074, 5075,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5076, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5077, -1, 5078, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5079, 5080, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5081, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5082, -1, -1, -1, -1,
- -1, 5083, -1, -1, -1, -1, -1, -1,
- 5084, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5085, -1, 5086,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5087,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5088, -1, -1, 5089, -1, -1, -1, -1,
- 5090, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5091, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5092, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5093, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5094, 5095, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5096, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5097, -1, -1, -1, 5098, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5099, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5100, -1, -1,
- 5101, -1, -1, -1, 5102, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5103, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5104, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5105, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5106, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5107, 5108, 5109, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5110, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5111, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5112, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5113, 5114, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5115, -1, -1, -1,
- -1, -1, 5116, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5117, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5118, -1, -1, 5119, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5120,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5121, -1, -1, -1,
- -1, -1, 5122, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5123,
- -1, -1, -1, 5124, -1, -1, -1, 5125,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5126,
- 5127, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5128, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5129, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5130, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5131, 5132, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5133, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5134,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5135, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5136, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5137, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5138, 5139, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5140, -1, -1, -1, -1, -1, -1, -1,
- -1, 5141, -1, 5142, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5143, -1, -1, 5144, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5145,
- 5146, -1, -1, -1, -1, -1, 5147, -1,
- -1, 5148, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5149, -1, -1, -1, -1,
- 5150, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5151, -1,
- -1, -1, -1, 5152, -1, -1, -1, -1,
- -1, -1, 5153, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5154, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5155, -1, -1, -1, -1,
- -1, 5156, -1, -1, -1, -1, -1, -1,
- 5157, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5158, -1, 5159, -1, -1, -1,
- -1, -1, -1, -1, -1, 5160, -1, 5161,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5162, -1, -1, -1, -1, -1, 5163, 5164,
- -1, -1, -1, 5165, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5166, -1, -1, -1, -1, -1,
- 5167, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5168, -1, 5169,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5170, -1, -1, -1, -1, 5171,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5172, -1, -1, -1, -1, -1, 5173, -1,
- -1, 5174, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5175, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5176, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5177, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5178, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5179, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5180, -1, -1, -1, -1,
- -1, -1, -1, 5181, -1, -1, -1, -1,
- -1, -1, -1, -1, 5182, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5183,
- -1, -1, 5184, -1, -1, -1, -1, -1,
- -1, -1, -1, 5185, -1, -1, -1, 5186,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5187, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5188, -1,
- -1, -1, -1, -1, -1, -1, -1, 5189,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5190, -1,
- -1, -1, 5191, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5192, 5193, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5194,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5195, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5196,
- -1, -1, 5197, -1, -1, 5198, -1, -1,
- -1, -1, -1, -1, -1, 5199, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5200, -1, -1, -1, -1, -1, -1,
- 5201, -1, -1, -1, -1, 5202, -1, -1,
- 5203, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5204, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5205, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5206, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5207,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5208, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5209,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5210, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5211, -1, -1, 5212, -1,
- -1, -1, 5213, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5214, 5215, -1, 5216, -1, -1, -1,
- 5217, -1, -1, -1, -1, -1, -1, 5218,
- -1, -1, 5219, 5220, -1, 5221, -1, -1,
- -1, -1, -1, -1, -1, 5222, -1, -1,
- -1, -1, 5223, -1, -1, -1, -1, -1,
- -1, -1, 5224, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5225, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5226, -1, 5227, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5228, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5229, -1, -1, -1, -1, -1, -1,
- -1, -1, 5230, -1, -1, -1, -1, 5231,
- -1, -1, 5232, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5233,
- -1, -1, -1, -1, -1, -1, -1, 5234,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5235, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5236, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5237, -1, -1, -1,
- -1, -1, -1, -1, -1, 5238, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5239, -1, -1, -1, 5240, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5241, -1,
- -1, -1, -1, -1, -1, -1, 5242, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5243, -1,
- -1, -1, -1, -1, -1, 5244, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5245, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5246, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5247, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5248, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5249, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5250, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5251, -1,
- -1, -1, -1, -1, 5252, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5253,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5254, -1, -1, -1, -1, 5255,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5256, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5257, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5258, -1,
- -1, 5259, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5260,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5261, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5262, 5263, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5264, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5265, -1, 5266, -1, -1, 5267,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5268, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5269, -1, -1, -1,
- -1, -1, 5270, -1, 5271, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5272, -1, -1, -1,
- 5273, -1, -1, -1, 5274, -1, -1, -1,
- 5275, -1, -1, -1, -1, -1, -1, 5276,
- -1, -1, -1, -1, 5277, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5278, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5279,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5280, -1, 5281, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5282,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5283, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5284, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5285, -1, -1, -1, -1, 5286, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5287, -1, -1, -1, -1, -1,
- -1, 5288, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5289,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5290, -1, 5291,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5292, -1, -1, -1, -1, -1, -1,
- -1, -1, 5293, -1, 5294, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5295,
- -1, -1, -1, 5296, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5297, -1,
- -1, -1, -1, -1, -1, -1, 5298, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5299, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5300, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5301, -1, -1, -1, -1, -1, 5302, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5303, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5304, -1, -1, -1, 5305, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5306, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5307, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5308, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5309, -1, -1, -1, -1,
- -1, -1, 5310, -1, -1, -1, -1, 5311,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5312, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5313, -1, -1, 5314,
- -1, -1, 5315, -1, -1, -1, -1, -1,
- 5316, -1, -1, -1, -1, -1, -1, -1,
- -1, 5317, -1, -1, -1, -1, -1, -1,
- -1, 5318, -1, -1, -1, -1, -1, -1,
- 5319, -1, -1, -1, -1, -1, -1, 5320,
- -1, -1, 5321, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5322, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5323, -1, -1,
- 5324, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5325, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5326, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5327, -1, -1, -1, -1,
- -1, -1, -1, -1, 5328, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5329, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5330, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5331, -1, -1, -1, -1,
- -1, -1, -1, 5332, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5333, -1, -1,
- -1, -1, -1, -1, -1, -1, 5334, -1,
- -1, -1, -1, -1, 5335, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5336, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5337,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5338, -1, 5339, -1, -1,
- -1, -1, 5340, -1, -1, -1, -1, 5341,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5342, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5343, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5344, -1, -1, 5345, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5346, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5347, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5348, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5349, -1, -1, -1, -1, -1, 5350,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5351,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5352, -1, -1, -1, -1,
- -1, 5353, -1, -1, 5354, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5355, -1, -1, 5356, -1, -1,
- -1, -1, -1, -1, -1, 5357, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5358, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5359, -1, -1, -1,
- -1, -1, -1, -1, 5360, 5361, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5362, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5363, 5364, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5365, -1, -1, -1,
- -1, -1, -1, -1, -1, 5366, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5367,
- -1, -1, 5368, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5369, -1, -1, -1, -1, -1,
- 5370, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5371, -1, -1,
- -1, -1, -1, 5372, -1, -1, -1, -1,
- -1, 5373, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5374, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5375, -1,
- 5376, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5377, 5378, -1, -1, 5379, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5380, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5381, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5382, -1, -1, -1, -1, -1,
- -1, -1, 5383, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5384, -1, -1, -1, -1, -1, -1,
- 5385, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5386, -1, -1,
- -1, 5387, -1, -1, -1, -1, -1, -1,
- -1, 5388, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5389,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5390, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5391, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5392,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5393, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5394, -1, 5395,
- 5396, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5397, -1, -1, -1, 5398, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5399, -1, -1,
- -1, -1, -1, 5400, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5401,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5402,
- -1, -1, -1, -1, -1, -1, -1, 5403,
- -1, -1, -1, -1, -1, 5404, -1, 5405,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5406, -1, -1, -1,
- -1, -1, -1, -1, 5407, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5408, -1, 5409, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5410,
- -1, -1, -1, 5411, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5412,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5413, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5414, -1, 5415, -1, -1, 5416, -1,
- -1, -1, -1, -1, 5417, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5418, -1,
- -1, -1, 5419, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5420, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5421, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5422,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5423, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5424,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5425, -1, 5426, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5427, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5428, -1, -1, 5429, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5430, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5431, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5432, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5433, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5434, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5435, -1, -1,
- -1, -1, 5436, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5437, -1, -1, -1, -1, -1, -1, 5438,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5439,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5440, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5441, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5442, -1, -1, -1,
- -1, -1, -1, -1, -1, 5443, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5444, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5445, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5446, -1, -1,
- -1, -1, -1, 5447, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5448, -1, -1, 5449, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5450, -1, -1, -1, -1, -1,
- 5451, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5452, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5453, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5454, -1, -1, -1,
- 5455, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5456,
- -1, -1, -1, -1, 5457, -1, -1, -1,
- -1, -1, 5458, -1, 5459, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5460, -1, -1, -1, -1, 5461, 5462,
- -1, -1, -1, -1, -1, -1, -1, 5463,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5464,
- -1, 5465, -1, -1, -1, -1, -1, 5466,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5467, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5468, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5469,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5470, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5471, 5472, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5473, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5474, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5475, -1, -1, -1, -1, -1, -1, 5476,
- -1, -1, -1, -1, -1, 5477, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5478,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5479, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5480, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5481, -1, -1, -1, 5482, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5483,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5484, 5485, -1, -1, -1,
- 5486, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5487, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5488, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5489, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5490,
- -1, -1, -1, -1, -1, -1, -1, 5491,
- -1, -1, -1, 5492, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5493, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5494, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5495, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5496, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5497,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5498, -1,
- 5499, -1, -1, -1, -1, -1, -1, -1,
- -1, 5500, 5501, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5502, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5503,
- -1, -1, -1, 5504, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5505, -1, -1, -1, -1, -1,
- -1, 5506, -1, 5507, 5508, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5509, -1, -1, -1, -1,
- -1, -1, -1, -1, 5510, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5511, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5512, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5513, -1,
- 5514, -1, -1, -1, -1, 5515, -1, -1,
- -1, -1, -1, -1, -1, -1, 5516, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5517, -1, -1,
- -1, -1, 5518, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5519, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5520, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5521, 5522, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5523, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5524, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5525,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5526, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5527, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5528, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5529, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5530, -1, -1, -1, 5531, -1, 5532,
- -1, -1, -1, -1, 5533, -1, -1, -1,
- -1, 5534, 5535, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5536, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5537, -1, -1, -1, 5538, -1,
- 5539, 5540, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5541, 5542, -1, -1,
- -1, -1, -1, 5543, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5544, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5545, -1, -1,
- -1, 5546, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5547, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5548,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5549, -1, -1,
- -1, -1, -1, -1, -1, 5550, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5551, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5552, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5553, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5554, 5555,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5556, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5557, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5558, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5559, -1, -1, -1,
- 5560, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5561, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5562, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5563,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5564, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5565, -1, 5566, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5567, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5568, -1, -1, -1,
- -1, -1, 5569, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5570, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5571,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5572,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5573, -1, 5574, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5575, -1, -1, 5576, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5577, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5578, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5579, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5580, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5581, -1, -1, -1, -1, 5582,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5583, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5584, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5585, -1, -1, -1,
- -1, -1, 5586, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5587, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5588, -1, -1, 5589, -1, -1,
- -1, -1, -1, -1, 5590, -1, -1, -1,
- -1, -1, -1, 5591, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5592, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5593, -1, -1,
- 5594, -1, -1, -1, -1, -1, -1, 5595,
- -1, -1, -1, -1, 5596, -1, -1, -1,
- -1, -1, -1, -1, 5597, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5598, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5599, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5600, -1,
- 5601, -1, 5602, -1, -1, -1, -1, 5603,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5604, -1, 5605, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5606, -1, -1, -1, -1,
- -1, -1, -1, -1, 5607, -1, -1, 5608,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5609, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5610, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5611, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5612, -1, -1, -1, -1,
- -1, 5613, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5614, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5615, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5616,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5617, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5618, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5619, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5620, -1, -1, -1,
- -1, 5621, -1, -1, -1, -1, 5622, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5623,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5624, -1,
- -1, -1, -1, -1, 5625, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5626, -1, -1,
- -1, -1, -1, -1, 5627, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5628, -1, -1, -1, -1,
- 5629, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5630,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5631, 5632, -1, -1, -1, -1, 5633,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5634, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5635, -1, -1, -1, -1, 5636,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5637, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5638, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5639, -1, -1, -1, -1, -1, 5640,
- -1, -1, 5641, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5642,
- -1, -1, -1, -1, 5643, -1, -1, -1,
- 5644, -1, -1, -1, -1, -1, 5645, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5646, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5647, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5648, -1, 5649, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5650,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5651, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5652,
- -1, -1, -1, -1, 5653, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5654,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5655, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5656, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5657, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5658, -1, -1, -1, -1,
- -1, -1, 5659, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5660, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5661, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5662, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5663, -1, -1, 5664, -1, -1, -1, -1,
- -1, -1, -1, -1, 5665, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5666, -1, -1, -1, -1, -1, 5667,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5668, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5669, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5670, -1, 5671, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5672, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5673, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5674, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5675, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5676, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5677,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5678, -1,
- -1, -1, -1, 5679, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5680, -1, -1, -1, -1, 5681,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5682, -1, -1, -1,
- -1, -1, -1, 5683, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5684, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5685, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5686, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5687, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5688, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5689, -1, -1, -1, -1, -1,
- -1, 5690, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5691, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5692, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5693, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5694, -1, -1,
- 5695, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5696, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5697, -1, -1, -1, -1, 5698, -1, -1,
- -1, 5699, -1, -1, -1, -1, -1, 5700,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5701, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5702,
- -1, -1, 5703, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5704, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5705, -1,
- -1, -1, -1, -1, -1, -1, -1, 5706,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5707, 5708, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5709,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5710, -1, -1,
- -1, -1, -1, -1, -1, 5711, -1, -1,
- -1, 5712, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5713, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5714, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5715, -1, -1, -1, -1, -1,
- -1, -1, -1, 5716, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5717, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5718, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5719, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5720, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5721, -1,
- -1, -1, -1, -1, 5722, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5723, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5724, -1, -1, -1,
- -1, -1, 5725, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5726,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5727, -1, -1, -1, -1, -1, -1,
- -1, 5728, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5729, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5730, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5731, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5732, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5733, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5734, -1,
- -1, -1, -1, -1, -1, -1, -1, 5735,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5736, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5737, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5738,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5739, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5740, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5741, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5742, -1, -1, -1, 5743, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5744, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5745, -1, -1, -1, -1, -1, -1, 5746,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5747,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5748, -1, -1, -1, -1, -1, -1, -1,
- 5749, -1, -1, -1, -1, 5750, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5751, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5752, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5753, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5754, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5755,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5756,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5757, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5758, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5759, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5760, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5761, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5762, -1, -1, -1, -1, -1, -1,
- -1, 5763, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5764, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5765, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5766,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5767, -1, -1, -1, -1, -1, -1,
- -1, 5768, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5769, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5770, -1,
- -1, -1, -1, -1, -1, 5771, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5772,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5773, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5774, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5775,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5776, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5777, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5778, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5779, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5780,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5781, -1, -1, 5782, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5783, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5784, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5785, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5786,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5787, 5788, -1, -1, -1, -1, -1, 5789,
- -1, -1, -1, -1, -1, -1, -1, 5790,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5791, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5792, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5793, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5794, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5795, -1, -1, -1, -1, -1, -1,
- 5796, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5797, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5798, -1, -1, -1, -1, -1,
- -1, 5799, -1, -1, -1, -1, -1, -1,
- -1, -1, 5800, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5801, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5802, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5803, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5804, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5805, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5806, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5807, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5808, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5809, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5810, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5811, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5812, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5813, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5814,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5815, 5816, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5817, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5818, -1, -1, -1,
- -1, -1, 5819, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5820, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5821,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5822, -1, -1,
- -1, -1, -1, -1, -1, -1, 5823, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5824, -1, -1,
- -1, 5825, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5826, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5827,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5828, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5829, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5830,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5831, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5832, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5833,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5834, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5835, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5836, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5837, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5838, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5839, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5840,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5841,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5842, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5843, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5844, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5845, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5846, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5847, -1, -1, -1, -1, -1, -1,
- -1, 5848, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5849,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5850, -1, -1, 5851,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5852, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5853, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5854, -1, -1, -1, -1, 5855,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5856, -1, -1, -1,
- -1, -1, -1, 5857, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5858, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5859, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5860, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5861, -1,
- -1, -1, 5862, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5863,
- 5864, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5865, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5866, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5867, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5868, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5869,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5870, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5871, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5872,
- -1, -1, -1, -1, -1, -1, 5873, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5874, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5875, -1, -1, -1, -1,
- 5876, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5877, -1, -1, -1, -1, -1, -1, 5878,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5879,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5880, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5881, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5882, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5883, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5884,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5885, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5886, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5887, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5888, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5889,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5890, -1, -1, -1, -1, -1,
- -1, -1, 5891, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5892, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5893, -1, -1,
- 5894, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5895,
- -1, -1, -1, -1, 5896, -1, -1, -1,
- -1, -1, -1, -1, -1, 5897, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5898,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5899, -1, -1, -1, -1, -1, -1,
- 5900, -1, 5901, -1, 5902, -1, -1, -1,
- 5903, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5904, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5905, -1, -1,
- -1, 5906, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5907, 5908, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5909, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5910, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5911, -1, -1, 5912, -1, 5913, -1, -1,
- -1, 5914, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5915, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5916, 5917,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5918, -1, -1, -1, -1, -1, 5919,
- 5920, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5921, -1, -1, -1, 5922, -1,
- -1, -1, -1, -1, 5923, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5924, -1,
- 5925, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5926, -1, -1, -1, 5927, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5928, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5929, -1,
- -1, 5930, -1, -1, 5931, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5932, -1, -1, -1,
- -1, -1, 5933, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5934,
- -1, -1, -1, -1, -1, 5935, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5936, -1, -1, -1, -1, 5937, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5938, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5939,
- -1, -1, -1, -1, -1, -1, -1, 5940,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5941, -1, -1, -1, -1, 5942, -1, -1,
- -1, -1, -1, -1, -1, 5943, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5944, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5945, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5946, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5947, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5948, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5949, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5950, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5951, -1, -1, -1, -1, -1,
- -1, -1, -1, 5952, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5953, 5954, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5955, -1, -1,
- -1, -1, 5956, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5957, -1, -1, -1, -1, -1, -1, -1,
- 5958, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5959, -1, -1, -1, -1, -1, 5960, -1,
- -1, -1, -1, -1, -1, -1, -1, 5961,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5962, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5963, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5964, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5965, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5966, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5967, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5968, -1,
- -1, -1, -1, -1, 5969, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5970, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5971, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 5972, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5973, -1,
- -1, -1, -1, -1, -1, -1, -1, 5974,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5975, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5976, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5977, -1, -1, -1, -1,
- -1, 5978, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 5979, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5980, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5981, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 5982, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5983, -1, -1,
- -1, -1, -1, -1, -1, -1, 5984, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5985, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5986, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 5987, -1, -1, -1, 5988,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5989,
- -1, -1, -1, -1, -1, -1, -1, 5990,
- -1, -1, -1, -1, -1, -1, -1, 5991,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5992, -1, -1,
- -1, 5993, -1, -1, -1, -1, -1, 5994,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 5995, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 5996, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5997, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 5998,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 5999, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6000, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6001, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6002, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6003, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6004, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6005, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6006, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6007, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6008, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6009, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6010, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6011, -1,
- -1, 6012, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6013, -1, -1,
- -1, -1, -1, -1, -1, 6014, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6015, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6016, 6017, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6018, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6019, -1,
- -1, -1, -1, -1, 6020, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6021, -1, -1, -1,
- -1, -1, 6022, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6023, -1, -1, -1, -1, 6024, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6025, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6026, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6027, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6028, -1, -1, -1, -1, 6029, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6030, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6031, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6032, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6033, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6034,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6035, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6036, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6037, -1, -1, -1, -1, -1, -1,
- -1, -1, 6038, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6039, -1, -1, -1, 6040,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6041, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6042, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6043,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6044,
- -1, 6045, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6046, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6047, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6048, -1,
- -1, 6049, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6050, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6051, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6052, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6053,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6054, -1,
- 6055, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6056, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6057,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6058, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6059, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6060, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6061, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6062, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6063, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6064, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6065, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6066, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6067, -1, -1, -1, -1, -1, -1, -1,
- 6068, -1, -1, -1, -1, -1, -1, -1,
- 6069, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6070, -1, -1, -1, -1, -1,
- -1, -1, -1, 6071, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6072, -1, -1, -1, -1, -1,
- -1, 6073, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6074, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6075, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6076, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6077, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6078, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6079, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6080, -1, -1, -1, -1, -1,
- -1, 6081, -1, -1, -1, -1, 6082, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6083, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6084, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6085, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6086, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6087, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6088, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6089,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6090, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6091, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6092, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6093, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6094, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6095, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6096, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6097, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6098, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6099, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6100, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6101,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6102, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6103, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6104, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6105, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6106,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6107, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6108, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6109, -1,
- -1, -1, 6110, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6111, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6112, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6113, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6114,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6115, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6116, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6117, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6118, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6119, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6120,
- -1, -1, -1, -1, -1, -1, -1, 6121,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6122, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6123, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6124, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6125, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6126, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6127, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6128, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6129, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6130, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6131, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6132, -1, -1, 6133, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6134, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6135, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6136,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6137, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6138, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6139,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6140,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6141, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6142, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6143, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6144, 6145, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6146, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6147, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6148, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6149, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6150, -1, -1, -1, -1, -1, 6151,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6152, -1, -1, -1, -1, -1, -1,
- -1, -1, 6153, -1, -1, -1, -1, -1,
- 6154, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6155, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6156,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6157, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6158, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6159, -1, -1, -1, -1,
- -1, -1, -1, -1, 6160, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6161, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6162,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6163,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6164, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6165, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6166, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6167, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6168, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6169,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6170, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6171, 6172,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6173, -1, -1, -1, -1, -1,
- 6174, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6175, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6176, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6177, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6178, -1, -1, -1,
- -1, 6179, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6180, -1, -1, -1, -1,
- -1, -1, -1, -1, 6181, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6182, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6183, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6184, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6185,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6186, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6187, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6188, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6189, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6190, -1, -1, 6191,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6192, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6193, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6194,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6195, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6196, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6197,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6198, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6199,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6200, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6201, -1,
- -1, -1, -1, -1, -1, 6202, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6203, -1,
- -1, -1, -1, -1, -1, 6204, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6205, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6206, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6207, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6208, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6209, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6210, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6211,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6212, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6213, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6214, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6215, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6216, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6217, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6218, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6219, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6220, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6221, -1,
- 6222, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6223, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6224, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6225, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6226, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6227, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6228, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6229,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6230, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6231, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6232,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6233, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6234, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6235, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6236, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6237, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6238, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6239, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6240,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6241, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6242, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6243, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6244, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6245, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6246, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6247, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6248, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6249, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6250, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6251,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6252, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6253, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6254, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6255, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6256, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6257, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6258,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6259, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6260, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6261, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6262, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6263, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6264, -1, -1, -1,
- -1, 6265, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6266, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6267, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6268, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6269, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6270,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6271, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6272, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6273, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6274, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6275, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6276,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6277, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6278, -1, 6279, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6280,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6281, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6282, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6283, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6284, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6285,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6286, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6287, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6288, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6289,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6290,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6291, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6292, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6293, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6294, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6295,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6296, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6297, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6298, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6299, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6300,
- -1, -1, -1, -1, 6301, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6302, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6303, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6304, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6305,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6306, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6307,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6308,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6309, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6310,
- -1, -1, 6311, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6312, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6313, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6314, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6315, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6316,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6317, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6318, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6319, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6320, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6321, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6322, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6323, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6324, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6325,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6326,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6327, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6328, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6329, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6330, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6331,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6332, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6333, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6334, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6335, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6336, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6337, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6338, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6339, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6340, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6341, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6342,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6343,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6344, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6345, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6346, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6347, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6348, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6349, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6350, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6351, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6352,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6353, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 6354,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6355, -1, -1, -1, -1, -1, -1,
- 6356, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6357, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6358, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 6359, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6360, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6361, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6362, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6363, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6364, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6365, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- 6366, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 6367, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6368, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 6369, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6370, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6371, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 6372, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 6373, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 6374
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register int index = lookup[key];
-
- if (index >= 0)
- {
- register const char *s = wordlist[index].name_offset + stringpool;
-
- if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
- return &wordlist[index];
- }
- }
- }
- return 0;
-}
-#line 6389 "effective_tld_names.gperf"
-
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names.dat b/chromium/net/base/registry_controlled_domains/effective_tld_names.dat
index 038e25d4e64..4a8664322b8 100644
--- a/chromium/net/base/registry_controlled_domains/effective_tld_names.dat
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names.dat
@@ -183,6 +183,7 @@ ar
com.ar
edu.ar
gob.ar
+gov.ar
int.ar
mil.ar
net.ar
@@ -191,6 +192,7 @@ tur.ar
// arpa : http://en.wikipedia.org/wiki/.arpa
// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+arpa
e164.arpa
in-addr.arpa
ip6.arpa
@@ -215,6 +217,7 @@ or.at
// au : http://en.wikipedia.org/wiki/.au
// http://www.auda.org.au/
+au
// 2LDs
com.au
net.au
@@ -223,8 +226,6 @@ edu.au
gov.au
asn.au
id.au
-// Chromium - Carve a hole for CSIRO. Requested by Kalman Dee <kalman.dee@csiro.au>
-!csiro.au
// Historic 2LDs (closed to new registration, but sites still exist)
info.au
conf.au
@@ -247,7 +248,7 @@ sa.edu.au
tas.edu.au
vic.edu.au
wa.edu.au
-act.gov.au
+// act.gov.au Bug 984824 - Removed at request of Greg Tankard
// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
qld.gov.au
@@ -294,6 +295,7 @@ rs.ba
// bb : http://en.wikipedia.org/wiki/.bb
bb
biz.bb
+co.bb
com.bb
edu.bb
gov.bb
@@ -301,6 +303,7 @@ info.bb
net.bb
org.bb
store.bb
+tv.bb
// bd : http://en.wikipedia.org/wiki/.bd
*.bd
@@ -403,8 +406,8 @@ net.bo
mil.bo
tv.bo
-// br : http://registro.br/dominio/dpn.html
-// Submitted by registry <fneves@registro.br> 2011-03-01
+// br : http://registro.br/dominio/categoria.html
+// Submitted by registry <fneves@registro.br> 2014-03-04
br
adm.br
adv.br
@@ -449,6 +452,7 @@ lel.br
mat.br
med.br
mil.br
+mp.br
mus.br
net.br
nom.br
@@ -494,6 +498,7 @@ org.bt
// bv : No registrations at this time.
// Submitted by registry <jarle@uninett.no> 2006-06-16
+bv
// bw : http://en.wikipedia.org/wiki/.bw
// http://www.gobin.info/domainname/bw.doc
@@ -596,9 +601,12 @@ gob.cl
co.cl
mil.cl
-// cm : http://en.wikipedia.org/wiki/.cm
+// cm : http://en.wikipedia.org/wiki/.cm plus bug 981927
cm
+co.cm
+com.cm
gov.cm
+net.cm
// cn : http://en.wikipedia.org/wiki/.cn
// Submitted by registry <tanyaling@cnnic.cn> 2008-06-11
@@ -872,6 +880,7 @@ ga
// gb : This registry is effectively dormant
// Submitted by registry <Damien.Shaw@ja.net> 2008-06-12
+gb
// gd : http://en.wikipedia.org/wiki/.gd
gd
@@ -925,6 +934,7 @@ gm
// gn : http://psg.com/dns/gn/gn.txt
// Submitted by registry <randy@psg.com> 2008-06-17
+gn
ac.gn
com.gn
edu.gn
@@ -1086,6 +1096,7 @@ id
ac.id
biz.id
co.id
+desa.id
go.id
mil.id
my.id
@@ -1184,286 +1195,378 @@ int.is
it
gov.it
edu.it
-// list of reserved geo-names :
+// Reserved geo-names:
// http://www.nic.it/documenti/regolamenti-e-linee-guida/regolamento-assegnazione-versione-6.0.pdf
-// (There is also a list of reserved geo-names corresponding to Italian
-// municipalities : http://www.nic.it/documenti/appendice-c.pdf , but it is
-// not included here.)
-agrigento.it
+// There is also a list of reserved geo-names corresponding to Italian municipalities
+// http://www.nic.it/documenti/appendice-c.pdf, but it is not included here.
+// Regions
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
+sar.it
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-sudtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosudtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+valleeaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
+// Provinces
ag.it
-alessandria.it
+agrigento.it
al.it
-ancona.it
+alessandria.it
+alto-adige.it
+altoadige.it
an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
aosta.it
aoste.it
-ao.it
-arezzo.it
+ap.it
+aq.it
+aquila.it
ar.it
+arezzo.it
ascoli-piceno.it
ascolipiceno.it
-ap.it
asti.it
at.it
-avellino.it
av.it
-bari.it
+avellino.it
ba.it
-andria-barletta-trani.it
-andriabarlettatrani.it
-trani-barletta-andria.it
-tranibarlettaandria.it
+balsan.it
+bari.it
barletta-trani-andria.it
barlettatraniandria.it
-andria-trani-barletta.it
-andriatranibarletta.it
-trani-andria-barletta.it
-traniandriabarletta.it
-bt.it
belluno.it
-bl.it
benevento.it
-bn.it
bergamo.it
bg.it
-biella.it
bi.it
-bologna.it
+biella.it
+bl.it
+bn.it
bo.it
+bologna.it
bolzano.it
bozen.it
-balsan.it
-alto-adige.it
-altoadige.it
-suedtirol.it
-bz.it
+br.it
brescia.it
-bs.it
brindisi.it
-br.it
-cagliari.it
+bs.it
+bt.it
+bz.it
ca.it
+cagliari.it
caltanissetta.it
-cl.it
+campidano-medio.it
+campidanomedio.it
campobasso.it
-cb.it
-carboniaiglesias.it
carbonia-iglesias.it
-iglesias-carbonia.it
-iglesiascarbonia.it
-ci.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
caserta.it
-ce.it
catania.it
-ct.it
catanzaro.it
-cz.it
-chieti.it
+cb.it
+ce.it
+cesena-forli.it
+cesenaforli.it
ch.it
-como.it
+chieti.it
+ci.it
+cl.it
+cn.it
co.it
+como.it
cosenza.it
-cs.it
-cremona.it
cr.it
+cremona.it
crotone.it
-kr.it
+cs.it
+ct.it
cuneo.it
-cn.it
+cz.it
dell-ogliastra.it
dellogliastra.it
-ogliastra.it
-og.it
-enna.it
en.it
-ferrara.it
+enna.it
+fc.it
fe.it
fermo.it
-fm.it
+ferrara.it
+fg.it
+fi.it
firenze.it
florence.it
-fi.it
+fm.it
foggia.it
-fg.it
forli-cesena.it
forlicesena.it
-cesena-forli.it
-cesenaforli.it
-fc.it
-frosinone.it
fr.it
-genova.it
-genoa.it
+frosinone.it
ge.it
-gorizia.it
+genoa.it
+genova.it
go.it
-grosseto.it
+gorizia.it
gr.it
-imperia.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
im.it
-isernia.it
+imperia.it
is.it
-laquila.it
-aquila.it
-aq.it
+isernia.it
+kr.it
la-spezia.it
+laquila.it
laspezia.it
-sp.it
latina.it
-lt.it
-lecce.it
+lc.it
le.it
+lecce.it
lecco.it
-lc.it
-livorno.it
li.it
-lodi.it
+livorno.it
lo.it
-lucca.it
+lodi.it
+lt.it
lu.it
+lucca.it
macerata.it
-mc.it
mantova.it
-mn.it
massa-carrara.it
massacarrara.it
-carrara-massa.it
-carraramassa.it
-ms.it
matera.it
-mt.it
+mb.it
+mc.it
+me.it
medio-campidano.it
mediocampidano.it
-campidano-medio.it
-campidanomedio.it
-vs.it
messina.it
-me.it
-milano.it
-milan.it
mi.it
-modena.it
+milan.it
+milano.it
+mn.it
mo.it
-monza.it
+modena.it
monza-brianza.it
+monza-e-della-brianza.it
+monza.it
monzabrianza.it
monzaebrianza.it
monzaedellabrianza.it
-monza-e-della-brianza.it
-mb.it
-napoli.it
-naples.it
+ms.it
+mt.it
na.it
-novara.it
+naples.it
+napoli.it
no.it
-nuoro.it
+novara.it
nu.it
-oristano.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
or.it
+oristano.it
+ot.it
+pa.it
padova.it
padua.it
-pd.it
palermo.it
-pa.it
parma.it
-pr.it
pavia.it
-pv.it
-perugia.it
-pg.it
-pescara.it
+pc.it
+pd.it
pe.it
+perugia.it
pesaro-urbino.it
pesarourbino.it
-urbino-pesaro.it
-urbinopesaro.it
-pu.it
+pescara.it
+pg.it
+pi.it
piacenza.it
-pc.it
pisa.it
-pi.it
pistoia.it
-pt.it
-pordenone.it
pn.it
+po.it
+pordenone.it
potenza.it
-pz.it
+pr.it
prato.it
-po.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
ragusa.it
-rg.it
ravenna.it
-ra.it
-reggio-calabria.it
-reggiocalabria.it
rc.it
+re.it
+reggio-calabria.it
reggio-emilia.it
+reggiocalabria.it
reggioemilia.it
-re.it
-rieti.it
+rg.it
ri.it
+rieti.it
rimini.it
+rm.it
rn.it
+ro.it
roma.it
rome.it
-rm.it
rovigo.it
-ro.it
-salerno.it
sa.it
+salerno.it
sassari.it
-ss.it
savona.it
-sv.it
-siena.it
si.it
+siena.it
siracusa.it
-sr.it
-sondrio.it
so.it
-taranto.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+suedtirol.it
+sv.it
ta.it
+taranto.it
+te.it
tempio-olbia.it
tempioolbia.it
-olbia-tempio.it
-olbiatempio.it
-ot.it
teramo.it
-te.it
terni.it
-tr.it
-torino.it
-turin.it
+tn.it
to.it
-trapani.it
+torino.it
tp.it
-trento.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
trentino.it
-tn.it
+trento.it
treviso.it
-tv.it
trieste.it
ts.it
-udine.it
+turin.it
+tv.it
ud.it
-varese.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
va.it
+varese.it
+vb.it
+vc.it
+ve.it
venezia.it
venice.it
-ve.it
verbania.it
-vb.it
vercelli.it
-vc.it
verona.it
-vr.it
+vi.it
vibo-valentia.it
vibovalentia.it
-vv.it
vicenza.it
-vi.it
viterbo.it
+vr.it
+vs.it
vt.it
+vv.it
// je : http://www.channelisles.net/register-domains/
// Confirmed by registry <nigel@channelisles.net> 2013-11-28
@@ -1491,7 +1594,7 @@ jobs
// jp : http://en.wikipedia.org/wiki/.jp
// http://jprs.co.jp/en/jpdomain.html
-// Submitted by registry <info@jprs.jp> 2012-05-28
+// Submitted by registry <info@jprs.jp> 2014-02-28
jp
// jp organizational type names
ac.jp
@@ -1595,7 +1698,6 @@ konan.aichi.jp
kota.aichi.jp
mihama.aichi.jp
miyoshi.aichi.jp
-nagakute.aichi.jp
nishio.aichi.jp
nisshin.aichi.jp
obu.aichi.jp
@@ -2267,7 +2369,6 @@ rikuzentakata.iwate.jp
shiwa.iwate.jp
shizukuishi.iwate.jp
sumita.iwate.jp
-takizawa.iwate.jp
tanohata.iwate.jp
tono.iwate.jp
yahaba.iwate.jp
@@ -3305,6 +3406,7 @@ edu.kn
gov.kn
// kp : http://www.kcce.kp/en_index.php
+kp
com.kp
edu.kp
gov.kp
@@ -3382,6 +3484,7 @@ org.la
// lb : http://en.wikipedia.org/wiki/.lb
// Submitted by registry <randy@psg.com> 2008-06-17
+lb
com.lb
edu.lb
gov.lb
@@ -3420,6 +3523,7 @@ hotel.lk
// lr : http://psg.com/dns/lr/lr.txt
// Submitted by registry <randy@psg.com> 2008-06-17
+lr
com.lr
edu.lr
gov.lr
@@ -3562,8 +3666,13 @@ mq
mr
gov.mr
-// ms : http://en.wikipedia.org/wiki/.ms
+// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf
ms
+com.ms
+edu.ms
+gov.ms
+net.ms
+org.ms
// mt : https://www.nic.org.mt/go/policy
// Submitted by registry <help@nic.org.mt> 2013-11-19
@@ -5045,7 +5154,24 @@ com.nr
nu
// nz : http://en.wikipedia.org/wiki/.nz
-*.nz
+// Confirmed by registry <jay@nzrs.net.nz> 2014-05-19
+nz
+ac.nz
+co.nz
+cri.nz
+geek.nz
+gen.nz
+govt.nz
+health.nz
+iwi.nz
+kiwi.nz
+maori.nz
+mil.nz
+māori.nz
+net.nz
+org.nz
+parliament.nz
+school.nz
// om : http://en.wikipedia.org/wiki/.om
om
@@ -5512,7 +5638,6 @@ oryol.ru
palana.ru
penza.ru
perm.ru
-pskov.ru
ptz.ru
rnd.ru
ryazan.ru
@@ -5632,7 +5757,7 @@ gov.sd
info.sd
// se : http://en.wikipedia.org/wiki/.se
-// Submitted by registry <Patrik.Wallstrom@iis.se> 2008-06-24
+// Submitted by registry <patrik.wallstrom@iis.se> 2014-03-18
se
a.se
ac.se
@@ -5666,7 +5791,6 @@ pp.se
press.se
r.se
s.se
-sshn.se
t.se
tm.se
u.se
@@ -5697,6 +5821,7 @@ si
// sj : No registrations at this time.
// Submitted by registry <jarle@uninett.no> 2008-06-16
+sj
// sk : http://en.wikipedia.org/wiki/.sk
// list of 2nd level domains ?
@@ -5879,6 +6004,10 @@ org.to
edu.to
mil.to
+// tp : No registrations at this time.
+// Submitted by Ryan Sleevi <ryan.sleevi@gmail.com> 2014-01-03
+tp
+
// tr : http://en.wikipedia.org/wiki/.tr
*.tr
!nic.tr
@@ -5932,6 +6061,7 @@ club.tw
// tz : http://www.tznic.or.tz/index.php/domains
// Confirmed by registry <manager@tznic.or.tz> 2013-01-22
+tz
ac.tz
co.tz
go.tz
@@ -6044,19 +6174,19 @@ com.ug
org.ug
// uk : http://en.wikipedia.org/wiki/.uk
-// Submitted by registry <noc@nominet.org.uk> 2012-10-02
-// and tweaked by us pending further consultation.
-*.uk
+// Submitted by registry <Michael.Daly@nominet.org.uk>
+uk
+ac.uk
+co.uk
+gov.uk
+ltd.uk
+me.uk
+net.uk
+nhs.uk
+org.uk
+plc.uk
+police.uk
*.sch.uk
-!bl.uk
-!british-library.uk
-!jet.uk
-!mod.uk
-!national-library-scotland.uk
-!nel.uk
-!nic.uk
-!nls.uk
-!parliament.uk
// us : http://en.wikipedia.org/wiki/.us
us
@@ -6180,7 +6310,7 @@ k12.vt.us
k12.va.us
k12.wa.us
k12.wi.us
-k12.wv.us
+// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu>
k12.wy.us
cc.ak.us
@@ -6334,16 +6464,24 @@ edu.vc
// ve : https://registro.nic.ve/
// Confirmed by registry 2012-10-04
+// Updated 2014-05-20 - Bug 940478
ve
+arts.ve
co.ve
com.ve
e12.ve
edu.ve
+firm.ve
+gob.ve
gov.ve
info.ve
+int.ve
mil.ve
net.ve
org.ve
+rec.ve
+store.ve
+tec.ve
web.ve
// vg : http://en.wikipedia.org/wiki/.vg
@@ -6376,8 +6514,12 @@ pro.vn
health.vn
// vu : http://en.wikipedia.org/wiki/.vu
-// list of 2nd level tlds ?
+// http://www.vunic.vu/
vu
+com.vu
+edu.vu
+net.vu
+org.vu
// wf : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf
wf
@@ -6408,12 +6550,12 @@ yt
// xn--54b7fta0cc ("Bangla" Bangla) : BD
বাংলা
-// xn--fiqs8s ("China" Chinese-Han-Simplified <.Zhonggou>) : CN
+// xn--fiqs8s ("China" Chinese-Han-Simplified <.Zhongguo>) : CN
// CNNIC
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
中国
-// xn--fiqz9s ("China" Chinese-Han-Traditional <.Zhonggou>) : CN
+// xn--fiqz9s ("China" Chinese-Han-Traditional <.Zhongguo>) : CN
// CNNIC
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
中國
@@ -6474,6 +6616,9 @@ yt
// xn--3e0b707e ("Republic of Korea" Hangul) : KR
한국
+// xn--80ao21a ("Kaz" Kazakh) : KZ
+қаз
+
// xn--fzc2c9e2c ("Lanka" Sinhalese-Sinhala) : LK
// http://nic.lk
ලංකා
@@ -6485,6 +6630,12 @@ yt
// xn--mgbc0a9azcg ("Morocco / al-Maghrib" Arabic) : MA
المغرب
+// xn--l1acc ("mon" Mongolian) : MN
+мон
+
+// xn--mgbx4cd0ab ("Malaysia" Malay) : MY
+مليسيا
+
// xn--mgb9awbf ("Oman" Arabic) : OM
عمان
@@ -6494,7 +6645,14 @@ yt
فلسطين
// xn--90a3ac ("srb" Cyrillic) : RS
+// http://www.rnids.rs/en/the-.срб-domain
срб
+пр.срб
+орг.срб
+обр.срб
+од.срб
+упр.срб
+ак.срб
// xn--p1ai ("rf" Russian-Cyrillic) : RU
// http://www.cctld.ru/en/docs/rulesrf.php
@@ -6919,7 +7077,7 @@ club
voting
// TOKYO : 2013-11-13 GMO Registry, Inc.
-TOKYO
+tokyo
// moe : 2013-11-13 Interlink Co., Ltd.
moe
@@ -7299,6 +7457,540 @@ vegas
// black : 2014-01-16 Afilias Limited
black
+// soy : 2014-01-23 Charleston Road Registry Inc.
+soy
+
+// trade : 2014-01-23 Elite Registry Limited
+trade
+
+// gent : 2014-01-23 COMBELL GROUP NV/SA
+gent
+
+// ing : 2014-01-23 Charleston Road Registry Inc.
+ing
+
+// dad : 2014-01-23 Charleston Road Registry Inc.
+dad
+
+// shriram : 2014-01-23 Shriram Capital Ltd.
+shriram
+
+// bayern : 2014-01-23 Bayern Connect GmbH
+bayern
+
+// scot : 2014-01-23 Dot Scot Registry Limited
+scot
+
+// webcam : 2014-01-23 dot Webcam Limited
+webcam
+
+// foo : 2014-01-23 Charleston Road Registry Inc.
+foo
+
+// eat : 2014-01-23 Charleston Road Registry Inc.
+eat
+
+// nyc : 2014-01-23 The City of New York
+nyc
+
+// prod : 2014-01-23 Charleston Road Registry Inc.
+prod
+
+// how : 2014-01-23 Charleston Road Registry Inc.
+how
+
+// day : 2014-01-30 Charleston Road Registry Inc.
+day
+
+// meme : 2014-01-30 Charleston Road Registry Inc.
+meme
+
+// mov : 2014-01-30 Charleston Road Registry Inc.
+mov
+
+// paris : 2014-01-30 City of Paris
+paris
+
+// boo : 2014-01-30 Charleston Road Registry Inc.
+boo
+
+// new : 2014-01-30 Charleston Road Registry Inc.
+new
+
+// ifm : 2014-01-30 ifm electronic gmbh
+ifm
+
+// life : 2014-02-06 Trixy Oaks, LLC
+life
+
+// archi : 2014-02-06 STARTING DOT LIMITED
+archi
+
+// spiegel : 2014-02-06 SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG
+spiegel
+
+// brussels : 2014-02-06 DNS.be vzw
+brussels
+
+// church : 2014-02-06 Holly Fileds, LLC
+church
+
+// here : 2014-02-06 Charleston Road Registry Inc.
+here
+
+// dabur : 2014-02-06 Dabur India Limited
+dabur
+
+// vlaanderen : 2014-02-06 DNS.be vzw
+vlaanderen
+
+// cologne : 2014-02-06 NetCologne Gesellschaft für Telekommunikation mbH
+cologne
+
+// xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
+手机
+
+// wme : 2014-02-13 William Morris Endeavor Entertainment, LLC
+wme
+
+// nhk : 2014-02-13 Japan Broadcasting Corporation (NHK)
+nhk
+
+// suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
+suzuki
+
+// whoswho : 2014-02-20 Who's Who Registry
+whoswho
+
+// scb : 2014-02-20 The Siam Commercial Bank Public Company Limited ("SCB""\)
+scb
+
+// hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH
+hamburg
+
+// services : 2014-02-27 Fox Castle, LLC
+services
+
+// bzh : 2014-02-27 Association www.bzh
+bzh
+
+// rio : 2014-02-27 Empresa Municipal de Informática SA - IPLANRIO
+rio
+
+// cash : 2014-03-07 Delta Lake, LLC
+cash
+
+// gives : 2014-03-07 United TLD Holdco Ltd.
+gives
+
+// hiphop : 2014-03-07 Uniregistry, Corp.
+hiphop
+
+// degree : 2014-03-07 Puff House, LLC
+degree
+
+// digital : 2014-03-07 Dash Park, LLC
+digital
+
+// rehab : 2014-03-07 United TLD Holdco Ltd.
+rehab
+
+// wtf : 2014-03-07 Hidden Way, LLC
+wtf
+
+// financial : 2014-03-07 Just Cover, LLC
+financial
+
+// limited : 2014-03-07 Big Fest, LLC
+limited
+
+// discount : 2014-03-07 Holly Hill, LLC
+discount
+
+// fail : 2014-03-07 Atomic Pipe, LLC
+fail
+
+// vet : 2014-03-07 Wild Dale, LLC
+vet
+
+// ngo : 2014-03-07 Public Interest Registry
+ngo
+
+// fitness : 2014-03-07 Brice Orchard, LLC
+fitness
+
+// schule : 2014-03-07 Outer Moon, LLC
+schule
+
+// navy : 2014-03-07 United TLD Holdco Ltd.
+navy
+
+// bio : 2014-03-07 STARTING DOT LIMITED
+bio
+
+// ong : 2014-03-07 Public Interest Registry
+ong
+
+// town : 2014-03-07 Koko Moon, LLC
+town
+
+// toys : 2014-03-07 Pioneer Orchard, LLC
+toys
+
+// army : 2014-03-07 United TLD Holdco Ltd.
+army
+
+// engineering : 2014-03-07 Romeo Canyon
+engineering
+
+// capital : 2014-03-07 Delta Mill, LLC
+capital
+
+// exchange : 2014-03-07 Spring Falls, LLC
+exchange
+
+// fan : 2014-03-07 Goose Glen, LLC
+fan
+
+// market : 2014-03-07 Victor Way, LLC
+market
+
+// media : 2014-03-07 Grand Glen, LLC
+media
+
+// lease : 2014-03-07 Victor Trail, LLC
+lease
+
+// university : 2014-03-07 Little Station, LLC
+university
+
+// reisen : 2014-03-07 New Cypress, LLC
+reisen
+
+// airforce : 2014-03-07 United TLD Holdco Ltd.
+airforce
+
+// pictures : 2014-03-07 Foggy Sky, LLC
+pictures
+
+// gripe : 2014-03-07 Corn Sunset, LLC
+gripe
+
+// engineer : 2014-03-07 United TLD Holdco Ltd.
+// CHROMIUM: Filed as bug http://bugzil.la/1024740
+engineer
+
+// associates : 2014-03-07 Baxter Hill, LLC
+associates
+
+// xn--mxtq1m : 2014-03-07 Net-Chinese Co., Ltd.
+政府
+
+// williamhill : 2014-03-13 William Hill Organization Limited
+williamhill
+
+// hiv : 2014-03-13 dotHIV gemeinnuetziger e.V.
+hiv
+
+// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
+sca
+
+// reise : 2014-03-13 dotreise GmbH
+reise
+
+// accountants : 2014-03-20 Knob Town, LLC
+accountants
+
+// clinic : 2014-03-20 Goose Park, LLC
+clinic
+
+// versicherung : 2014-03-20 dotversicherung-registry GmbH
+versicherung
+
+// top : 2014-03-20 Jiangsu Bangning Science & Technology Co.,Ltd.
+top
+
+// furniture : 2014-03-20 Lone Fields, LLC
+furniture
+
+// dental : 2014-03-20 Tin Birch, LLC
+dental
+
+// fund : 2014-03-20 John Castle, LLC
+fund
+
+// creditcard : 2014-03-20 Binky Frostbite, LLC
+creditcard
+
+// insure : 2014-03-20 Pioneer Willow, LLC
+insure
+
+// audio : 2014-03-20 Uniregistry, Corp.
+audio
+
+// claims : 2014-03-20 Black Corner, LLC
+claims
+
+// loans : 2014-03-20 June Woods, LLC
+loans
+
+// auction : 2014-03-20 Sand Galley, LLC
+auction
+
+// attorney : 2014-03-20 Victor North, LLC
+attorney
+
+// finance : 2014-03-20 Cotton Cypress, LLC
+finance
+
+// investments : 2014-03-20 Holly Glen, LLC
+investments
+
+// juegos : 2014-03-20 Uniregistry, Corp.
+juegos
+
+// dentist : 2014-03-20 Outer Lake, LLC
+dentist
+
+// lds : 2014-03-20 IRI Domain Management, LLC
+lds
+
+// lawyer : 2014-03-20 Atomic Station, LLC
+lawyer
+
+// surgery : 2014-03-20 Tin Avenue, LLC
+surgery
+
+// gratis : 2014-03-20 Pioneer Tigers, LLC
+gratis
+
+// software : 2014-03-20 Over Birch, LLC
+software
+
+// mortgage : 2014-03-20 Outer Gardens, LLC
+mortgage
+
+// republican : 2014-03-20 United TLD Holdco Ltd.
+republican
+
+// credit : 2014-03-20 Snow Shadow, LLC
+credit
+
+// tax : 2014-03-20 Storm Orchard, LLC
+tax
+
+// africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa
+africa
+
+// joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+joburg
+
+// durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+durban
+
+// capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+capetown
+
+// sap : 2014-03-27 SAP AG
+sap
+
+// datsun : 2014-03-27 NISSAN MOTOR CO., LTD.
+datsun
+
+// infiniti : 2014-03-27 NISSAN MOTOR CO., LTD.
+infiniti
+
+// firmdale : 2014-03-27 Firmdale Holdings Limited
+firmdale
+
+// organic : 2014-03-27 Afilias Limited
+organic
+
+// nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
+nissan
+
+// website : 2014-04-03 DotWebsite Inc.
+website
+
+// space : 2014-04-03 DotSpace Inc.
+space
+
+// schmidt : 2014-04-03 SALM S.A.S.
+schmidt
+
+// cuisinella : 2014-04-03 SALM S.A.S.
+cuisinella
+
+// samsung : 2014-04-03 SAMSUNG SDS CO., LTD
+samsung
+
+// crs : 2014-04-03 Federated Co operatives Limited
+crs
+
+// doosan : 2014-04-03 Doosan Corporation
+doosan
+
+// press : 2014-04-03 DotPress Inc.
+press
+
+// emerck : 2014-04-03 Merck KGaA
+emerck
+
+// erni : 2014-04-03 ERNI Group Holding AG
+erni
+
+// direct : 2014-04-10 Half Trail, LLC
+direct
+
+// yandex : 2014-04-10 YANDEX, LLC
+yandex
+
+// lotto : 2014-04-10 Afilias Limited
+lotto
+
+// toshiba : 2014-04-10 TOSHIBA Corporation
+toshiba
+
+// bauhaus : 2014-04-17 Werkhaus GmbH
+bauhaus
+
+// host : 2014-04-17 DotHost Inc.
+host
+
+// ltda : 2014-04-17 DOMAIN ROBOT SERVICOS DE HOSPEDAGEM NA INTERNET LTDA
+ltda
+
+// global : 2014-04-17 Dot GLOBAL AS
+global
+
+// abogado : 2014-04-24 Top Level Domain Holdings Limited
+abogado
+
+// place : 2014-04-24 Snow Galley, LLC
+place
+
+// tirol : 2014-04-24 punkt Tirol GmbH
+tirol
+
+// gmx : 2014-04-24 1&1 Mail & Media GmbH
+gmx
+
+// tatar : 2014-04-24 Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
+tatar
+
+// scholarships : 2014-04-24 Scholarships.com, LLC
+scholarships
+
+// eurovision : 2014-04-24 European Broadcasting Union (EBU)
+eurovision
+
+// wedding : 2014-04-24 Top Level Domain Holdings Limited
+wedding
+
+// active : 2014-05-01 The Active Network, Inc
+active
+
+// madrid : 2014-05-01 Comunidad de Madrid
+madrid
+
+// youtube : 2014-05-01 Charleston Road Registry Inc.
+youtube
+
+// sharp : 2014-05-01 Sharp Corporation
+sharp
+
+// uol : 2014-05-01 UBN INTERNET LTDA.
+uol
+
+// physio : 2014-05-01 PhysBiz Pty Ltd
+physio
+
+// gmail : 2014-05-01 Charleston Road Registry Inc.
+gmail
+
+// channel : 2014-05-08 Charleston Road Registry Inc.
+channel
+
+// fly : 2014-05-08 Charleston Road Registry Inc.
+fly
+
+// zip : 2014-05-08 Charleston Road Registry Inc.
+zip
+
+// esq : 2014-05-08 Charleston Road Registry Inc.
+esq
+
+// rsvp : 2014-05-08 Charleston Road Registry Inc.
+rsvp
+
+// wales : 2014-05-08 Nominet UK
+wales
+
+// cymru : 2014-05-08 Nominet UK
+cymru
+
+// green : 2014-05-08 Afilias Limited
+green
+
+// lgbt : 2014-05-08 Afilias Limited
+lgbt
+
+// xn--hxt814e : 2014-05-15 Zodiac Libra Limited
+网店
+
+// cancerresearch : 2014-05-15 Australian Cancer Research Foundation
+cancerresearch
+
+// everbank : 2014-05-15 EverBank
+everbank
+
+// frl : 2014-05-15 FRLregistry B.V.
+frl
+
+// property : 2014-05-22 Uniregistry, Corp.
+property
+
+// forsale : 2014-05-22 Sea Oaks, LLC
+forsale
+
+// seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal)
+seat
+
+// deals : 2014-05-22 Sand Sunset, LLC
+deals
+
+// nra : 2014-05-22 NRA Holdings Company, INC.
+nra
+
+// xn--fjq720a : 2014-05-22 Will Bloom, LLC
+娱乐
+
+// realtor : 2014-05-29 Real Estate Domains LLC
+realtor
+
+// bnpparibas : 2014-05-29 BNP Paribas
+bnpparibas
+
+// melbourne : 2014-05-29 The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+melbourne
+
+// hosting : 2014-05-29 Uniregistry, Corp.
+hosting
+
+// yoga : 2014-05-29 Top Level Domain Holdings Limited
+yoga
+
+// city : 2014-05-29 Snow Sky, LLC
+city
+
+// bond : 2014-06-05 Bond University Limited
+bond
+
+// click : 2014-06-05 Uniregistry, Corp.
+click
+
+// cern : 2014-06-05 European Organization for Nuclear Research ("CERN")
+cern
// ===END ICANN DOMAINS===
// ===BEGIN PRIVATE DOMAINS===
@@ -7308,20 +8000,22 @@ black
cloudfront.net
// Amazon Elastic Compute Cloud: https://aws.amazon.com/ec2/
-// Submitted by Osman Surkatty <osmans@amazon.com> 2013-04-02
-compute.amazonaws.com
-us-east-1.amazonaws.com
-compute-1.amazonaws.com
-z-1.compute-1.amazonaws.com
-z-2.compute-1.amazonaws.com
+// Submitted by Osman Surkatty <osmans@amazon.com> 2014-05-20
ap-northeast-1.compute.amazonaws.com
ap-southeast-1.compute.amazonaws.com
ap-southeast-2.compute.amazonaws.com
+cn-north-1.compute.amazonaws.cn
+compute.amazonaws.cn
+compute.amazonaws.com
+compute-1.amazonaws.com
eu-west-1.compute.amazonaws.com
sa-east-1.compute.amazonaws.com
+us-east-1.amazonaws.com
us-gov-west-1.compute.amazonaws.com
us-west-1.compute.amazonaws.com
us-west-2.compute.amazonaws.com
+z-1.compute-1.amazonaws.com
+z-2.compute-1.amazonaws.com
// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/
// Submitted by Adam Stein <astein@amazon.com> 2013-04-02
@@ -7364,16 +8058,17 @@ ar.com
br.com
cn.com
com.de
+com.se
de.com
eu.com
gb.com
gb.net
-gr.com
hu.com
hu.net
jp.net
jpn.com
kr.com
+mex.com
no.com
qc.com
ru.com
@@ -7383,10 +8078,30 @@ se.net
uk.com
uk.net
us.com
-us.org
uy.com
+za.bz
za.com
+// Africa.com Web Solutions Ltd : https://registry.africa.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+africa.com
+
+// iDOT Services Limited : http://www.domain.gr.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+gr.com
+
+// Radix FZC : http://domains.in.net
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+in.net
+
+// US REGISTRY LLC : http://us.org
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+us.org
+
+// co.com Registry, LLC : https://registry.co.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+co.com
+
// c.la : http://www.c.la/
c.la
@@ -7699,9 +8414,14 @@ global.ssl.fastly.net
a.prod.fastly.net
global.prod.fastly.net
+// Firebase, Inc.
+// Submitted by Chris Raynor <chris@firebase.com> 2014-01-21
+firebaseapp.com
+
// GitHub, Inc.
-// Submitted by Ben Toews <btoews@github.com> 2013-04-18
+// Submitted by Ben Toews <btoews@github.com> 2014-02-06
github.io
+githubusercontent.com
// GlobeHosting, Inc.
// Submitted by Zoltan Egresi <egresi@globehosting.com> 2013-07-12
@@ -7753,6 +8473,7 @@ blogspot.tw
codespot.com
googleapis.com
googlecode.com
+withgoogle.com
// Heroku : https://www.heroku.com/
// Submitted by Tom Maher <tmaher@heroku.com> 2013-05-02
@@ -7770,14 +8491,32 @@ info.at
// Michau Enterprises Limited : http://www.co.pl/
co.pl
+// Microsoft : http://microsoft.com
+// Submitted by Barry Dorrans <bdorrans@microsoft.com> 2014-01-24
+azurewebsites.net
+azure-mobile.net
+cloudapp.net
+
+// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
+// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net> 2014-02-02
+nfshost.com
+
// NYC.mn : http://www.information.nyc.mn
// Submitted by Matthew Brown <mattbrown@nyc.mn> 2013-03-11
nyc.mn
+// One Fold Media : http://www.onefoldmedia.com/
+// Submitted by Eddie Jones <eddie@onefoldmedia.com> 2014-06-10
+nid.io
+
// Opera Software, A.S.A.
// Submitted by Yngve Pettersen <yngve@opera.com> 2009-11-26
operaunite.com
+// OutSystems
+// Submitted by Duarte Santos <domain-admin@outsystemscloud.com> 2014-03-11
+outsystemscloud.com
+
// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
// Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24
rhcloud.com
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names.gperf b/chromium/net/base/registry_controlled_domains/effective_tld_names.gperf
index 890f814d844..6bee195507e 100644
--- a/chromium/net/base/registry_controlled_domains/effective_tld_names.gperf
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names.gperf
@@ -35,6 +35,9 @@ abeno.osaka.jp, 0
abiko.chiba.jp, 0
abira.hokkaido.jp, 0
abo.pa, 0
+abogado, 0
+abr.it, 0
+abruzzo.it, 0
abu.yamaguchi.jp, 0
ac, 0
ac.ae, 0
@@ -54,6 +57,7 @@ ac.ma, 0
ac.me, 0
ac.mu, 0
ac.mw, 0
+ac.nz, 0
ac.pa, 0
ac.pr, 0
ac.rs, 0
@@ -65,16 +69,18 @@ ac.th, 0
ac.tj, 0
ac.tz, 0
ac.ug, 0
+ac.uk, 0
ac.vn, 0
aca.pro, 0
academy, 0
academy.museum, 0
accident-investigation.aero, 0
accident-prevention.aero, 0
+accountants, 0
achi.nagano.jp, 0
act.au, 0
act.edu.au, 0
-act.gov.au, 0
+active, 0
actor, 0
ad, 0
ad.jp, 0
@@ -95,6 +101,8 @@ aerodrome.aero, 0
aeroport.fr, 0
af, 0
afjord.no, 0
+africa, 0
+africa.com, 4
ag, 0
ag.it, 0
aga.niigata.jp, 0
@@ -124,6 +132,7 @@ air-surveillance.aero, 0
air-traffic-control.aero, 0
air.museum, 0
aircraft.aero, 0
+airforce, 0
airguard.museum, 0
airline.aero, 0
airport.aero, 0
@@ -216,7 +225,9 @@ aogashima.tokyo.jp, 0
aoki.nagano.jp, 0
aomori.aomori.jp, 0
aomori.jp, 0
+aosta-valley.it, 0
aosta.it, 0
+aostavalley.it, 0
aoste.it, 0
ap-northeast-1.compute.amazonaws.com, 4
ap-southeast-1.compute.amazonaws.com, 4
@@ -238,6 +249,7 @@ arao.kumamoto.jp, 0
arboretum.museum, 0
archaeological.museum, 0
archaeology.museum, 0
+archi, 0
architecture.museum, 0
ardal.no, 0
aremark.no, 0
@@ -248,6 +260,7 @@ arida.wakayama.jp, 0
aridagawa.wakayama.jp, 0
arita.saga.jp, 0
arkhangelsk.ru, 0
+army, 0
arna.no, 0
arpa, 0
arq.br, 0
@@ -267,6 +280,7 @@ arts.co, 0
arts.museum, 0
arts.nf, 0
arts.ro, 0
+arts.ve, 0
artsandcrafts.museum, 0
as, 0
as.us, 0
@@ -316,6 +330,7 @@ asso.km, 0
asso.mc, 0
asso.nc, 0
asso.re, 0
+associates, 0
association.aero, 0
association.museum, 0
asti.it, 0
@@ -332,7 +347,10 @@ atm.pl, 0
ato.br, 0
atsugi.kanagawa.jp, 0
atsuma.hokkaido.jp, 0
+attorney, 0
au, 0
+auction, 0
+audio, 0
audnedaln.no, 0
augustow.pl, 0
aukra.no, 0
@@ -365,6 +383,8 @@ ayase.kanagawa.jp, 0
az, 0
az.us, 0
azumino.nagano.jp, 0
+azure-mobile.net, 4
+azurewebsites.net, 4
b.bg, 0
b.br, 0
b.se, 0
@@ -404,13 +424,17 @@ barreau.bj, 0
barrel-of-knowledge.info, 4
barrell-of-knowledge.info, 4
barum.no, 0
+bas.it, 0
baseball.museum, 0
basel.museum, 0
bashkiria.ru, 0
+basilicata.it, 0
baths.museum, 0
bato.tochigi.jp, 0
batsfjord.no, 0
bauern.museum, 0
+bauhaus, 0
+bayern, 0
bb, 0
bc.ca, 0
bd, 2
@@ -464,6 +488,7 @@ bike, 0
bilbao.museum, 0
bill.museum, 0
bindal.no, 0
+bio, 0
bio.br, 0
bir.ru, 0
biratori.hokkaido.jp, 0
@@ -492,7 +517,6 @@ bjarkoy.no, 0
bjerkreim.no, 0
bjugn.no, 0
bl.it, 0
-bl.uk, 1
black, 0
blackfriday, 0
blog.br, 0
@@ -545,6 +569,7 @@ bm, 0
bmd.br, 0
bn, 2
bn.it, 0
+bnpparibas, 0
bo, 0
bo.it, 0
bo.nordland.no, 0
@@ -557,7 +582,9 @@ bologna.it, 0
bolt.hu, 0
bolzano.it, 0
bomlo.no, 0
+bond, 0
bonn.museum, 0
+boo, 0
boston.museum, 0
botanical.museum, 0
botanicalgarden.museum, 0
@@ -575,7 +602,6 @@ bremanger.no, 0
brescia.it, 0
brindisi.it, 0
bristol.museum, 0
-british-library.uk, 1
british.museum, 0
britishcolumbia.museum, 0
broadcast.museum, 0
@@ -586,6 +612,7 @@ bronnoysund.no, 0
brumunddal.no, 0
brunel.museum, 0
brussel.museum, 0
+brussels, 0
brussels.museum, 0
bruxelles.museum, 0
bryansk.ru, 0
@@ -612,6 +639,7 @@ business, 0
buyshouses.net, 4
buzen.fukuoka.jp, 0
buzz, 0
+bv, 0
bv.nl, 0
bw, 0
by, 0
@@ -621,6 +649,7 @@ bykle.no, 0
bytom.pl, 0
bz, 0
bz.it, 0
+bzh, 0
c.bg, 0
c.la, 4
c.se, 0
@@ -633,17 +662,24 @@ cab, 0
cadaques.museum, 0
cagliari.it, 0
cahcesuolo.no, 0
+cal.it, 0
+calabria.it, 0
california.museum, 0
caltanissetta.it, 0
+cam.it, 0
cambridge.museum, 0
camera, 0
camp, 0
+campania.it, 0
campidano-medio.it, 0
campidanomedio.it, 0
campobasso.it, 0
can.museum, 0
canada.museum, 0
+cancerresearch, 0
capebreton.museum, 0
+capetown, 0
+capital, 0
caravan, 0
carbonia-iglesias.it, 0
carboniaiglesias.it, 0
@@ -658,6 +694,7 @@ cartoonart.museum, 0
casa, 0
casadelamoneda.museum, 0
caserta.it, 0
+cash, 0
casino.hu, 0
castle.museum, 0
castres.museum, 0
@@ -733,6 +770,7 @@ celtic.museum, 0
center, 0
center.museum, 0
ceo, 0
+cern, 0
certification.aero, 0
cesena-forli.it, 0
cesenaforli.it, 0
@@ -742,6 +780,7 @@ ch, 0
ch.it, 0
chambagri.fr, 0
championship.aero, 0
+channel, 0
charter.aero, 0
chattanooga.museum, 0
cheap, 0
@@ -801,6 +840,7 @@ chuo.fukuoka.jp, 0
chuo.osaka.jp, 0
chuo.tokyo.jp, 0
chuo.yamanashi.jp, 0
+church, 0
chuvashia.ru, 0
ci, 0
ci.it, 0
@@ -809,6 +849,7 @@ cim.br, 0
cincinnati.museum, 0
cinema.museum, 0
circus.museum, 0
+city, 0
city.hu, 0
city.kawasaki.jp, 1
city.kitakyushu.jp, 1
@@ -825,10 +866,14 @@ ck, 2
ck.ua, 0
cl, 0
cl.it, 0
+claims, 0
cleaning, 0
+click, 0
+clinic, 0
clinton.museum, 0
clock.museum, 0
clothing, 0
+cloudapp.net, 4
cloudcontrolapp.com, 4
cloudcontrolled.com, 4
cloudfront.net, 4
@@ -838,6 +883,7 @@ club.tw, 0
cm, 0
cmw.ru, 0
cn, 0
+cn-north-1.compute.amazonaws.cn, 4
cn.com, 4
cn.it, 0
cn.ua, 0
@@ -849,11 +895,14 @@ co.ag, 0
co.ao, 0
co.at, 0
co.ba, 0
+co.bb, 0
co.bi, 0
co.bw, 0
co.ca, 4
co.ci, 0
co.cl, 0
+co.cm, 0
+co.com, 4
co.cr, 0
co.gg, 0
co.gy, 0
@@ -875,6 +924,7 @@ co.mw, 0
co.na, 0
co.nl, 4
co.no, 4
+co.nz, 0
co.om, 0
co.pl, 4
co.pn, 0
@@ -890,6 +940,7 @@ co.tt, 0
co.tz, 0
co.ua, 0
co.ug, 0
+co.uk, 0
co.us, 0
co.uz, 0
co.ve, 0
@@ -903,6 +954,7 @@ coffee, 0
coldwar.museum, 0
collection.museum, 0
college, 0
+cologne, 0
colonialwilliamsburg.museum, 0
coloradoplateau.museum, 0
columbia.museum, 0
@@ -930,6 +982,7 @@ com.bt, 0
com.by, 0
com.bz, 0
com.ci, 0
+com.cm, 0
com.cn, 0
com.co, 0
com.cu, 0
@@ -977,6 +1030,7 @@ com.mg, 0
com.mk, 0
com.ml, 0
com.mo, 0
+com.ms, 0
com.mt, 0
com.mu, 0
com.mv, 0
@@ -1007,6 +1061,7 @@ com.sa, 0
com.sb, 0
com.sc, 0
com.sd, 0
+com.se, 4
com.sg, 0
com.sh, 0
com.sl, 0
@@ -1029,6 +1084,7 @@ com.vc, 0
com.ve, 0
com.vi, 0
com.vn, 0
+com.vu, 0
com.ws, 0
communication.museum, 0
communications.museum, 0
@@ -1037,6 +1093,7 @@ community.museum, 0
como.it, 0
company, 0
compute-1.amazonaws.com, 4
+compute.amazonaws.cn, 4
compute.amazonaws.com, 4
computer, 0
computer.museum, 0
@@ -1082,16 +1139,20 @@ cr.ua, 0
crafts.museum, 0
cranbrook.museum, 0
creation.museum, 0
+credit, 0
+creditcard, 0
cremona.it, 0
crew.aero, 0
+cri.nz, 0
crimea.ua, 0
crotone.it, 0
+crs, 0
cruises, 0
cs.it, 0
-csiro.au, 1
ct.it, 0
ct.us, 0
cu, 0
+cuisinella, 0
cultural.museum, 0
culturalcenter.museum, 0
culture.museum, 0
@@ -1103,6 +1164,7 @@ cw, 0
cx, 0
cy, 2
cyber.museum, 0
+cymru, 0
cymru.museum, 0
cz, 0
cz.it, 0
@@ -1110,6 +1172,8 @@ czeladz.pl, 0
czest.pl, 0
d.bg, 0
d.se, 0
+dabur, 0
+dad, 0
daegu.kr, 0
daejeon.kr, 0
dagestan.ru, 0
@@ -1124,25 +1188,32 @@ database.museum, 0
date.fukushima.jp, 0
date.hokkaido.jp, 0
dating, 0
+datsun, 0
davvenjarga.no, 0
davvesiida.no, 0
+day, 0
dazaifu.fukuoka.jp, 0
dc.us, 0
ddr.museum, 0
de, 0
de.com, 4
de.us, 0
+deals, 0
deatnu.no, 0
decorativearts.museum, 0
defense.tn, 0
+degree, 0
delaware.museum, 0
dell-ogliastra.it, 0
dellogliastra.it, 0
delmenhorst.museum, 0
democrat, 0
denmark.museum, 0
+dental, 0
+dentist, 0
dep.no, 0
depot.museum, 0
+desa.id, 0
desi, 0
design.aero, 0
design.museum, 0
@@ -1150,8 +1221,11 @@ detroit.museum, 0
dgca.aero, 0
diamonds, 0
dielddanuorri.no, 0
+digital, 0
dinosaur.museum, 0
+direct, 0
directory, 0
+discount, 0
discovery.museum, 0
divtasvuodna.no, 0
divttasvuotna.no, 0
@@ -1185,6 +1259,7 @@ dontexist.net, 4
dontexist.org, 4
doomdns.com, 4
doomdns.org, 4
+doosan, 0
doshi.yamanashi.jp, 0
dovre.no, 0
dp.ua, 0
@@ -1194,6 +1269,7 @@ drangedal.no, 0
dreamhosters.com, 4
drobak.no, 0
dudinka.ru, 0
+durban, 0
durham.museum, 0
dvrdns.org, 4
dyn-o-saur.com, 4
@@ -1229,6 +1305,7 @@ e12.ve, 0
e164.arpa, 0
eastafrica.museum, 0
eastcoast.museum, 0
+eat, 0
ebetsu.hokkaido.jp, 0
ebina.kanagawa.jp, 0
ebino.miyazaki.jp, 0
@@ -1308,6 +1385,7 @@ edu.mk, 0
edu.ml, 0
edu.mn, 0
edu.mo, 0
+edu.ms, 0
edu.mt, 0
edu.mv, 0
edu.mw, 0
@@ -1351,6 +1429,7 @@ edu.uy, 0
edu.vc, 0
edu.ve, 0
edu.vn, 0
+edu.vu, 0
edu.ws, 0
education, 0
education.museum, 0
@@ -1381,8 +1460,12 @@ email, 0
embaixada.st, 0
embetsu.hokkaido.jp, 0
embroidery.museum, 0
+emerck, 0
emergency.aero, 0
+emilia-romagna.it, 0
+emiliaromagna.it, 0
emp.br, 0
+emr.it, 0
en.it, 0
ena.gifu.jp, 0
encyclopedic.museum, 0
@@ -1394,7 +1477,9 @@ eng.br, 0
eng.pro, 0
engerdal.no, 0
engine.aero, 0
+engineer, 0
engineer.aero, 0
+engineering, 0
england.museum, 0
eniwa.hokkaido.jp, 0
enna.it, 0
@@ -1409,6 +1494,7 @@ equipment, 0
equipment.aero, 0
er, 2
erimo.hokkaido.jp, 0
+erni, 0
erotica.hu, 0
erotika.hu, 0
es, 0
@@ -1416,6 +1502,7 @@ es.kr, 0
esan.hokkaido.jp, 0
esashi.hokkaido.jp, 0
esp.br, 0
+esq, 0
essex.museum, 0
est-a-la-maison.com, 4
est-a-la-masion.com, 4
@@ -1436,11 +1523,14 @@ eu-west-1.compute.amazonaws.com, 4
eu.com, 4
eu.int, 0
eun.eg, 0
+eurovision, 0
eus, 0
evenassi.no, 0
evenes.no, 0
events, 0
+everbank, 0
evje-og-hornnes.no, 0
+exchange, 0
exchange.aero, 0
exeter.museum, 0
exhibition.museum, 0
@@ -1450,8 +1540,10 @@ exposed, 0
express.aero, 0
f.bg, 0
f.se, 0
+fail, 0
fam.pk, 0
family.museum, 0
+fan, 0
far.br, 0
fareast.ru, 0
farm, 0
@@ -1487,19 +1579,25 @@ film.hu, 0
film.museum, 0
fin.ec, 0
fin.tn, 0
+finance, 0
+financial, 0
fineart.museum, 0
finearts.museum, 0
finland.museum, 0
finnoy.no, 0
+firebaseapp.com, 4
firenze.it, 0
firm.co, 0
firm.ht, 0
firm.in, 0
firm.nf, 0
firm.ro, 0
+firm.ve, 0
+firmdale, 0
fish, 0
fishing, 0
fitjar.no, 0
+fitness, 0
fj, 2
fj.cn, 0
fjaler.no, 0
@@ -1520,6 +1618,7 @@ florence.it, 0
florida.museum, 0
florist, 0
floro.no, 0
+fly, 0
fm, 0
fm.br, 0
fm.it, 0
@@ -1529,6 +1628,7 @@ fo, 0
foggia.it, 0
folkebibl.no, 0
folldal.no, 0
+foo, 0
for-better.biz, 4
for-more.biz, 4
for-our.info, 4
@@ -1540,6 +1640,7 @@ forgot.her.name, 4
forgot.his.name, 4
forli-cesena.it, 0
forlicesena.it, 0
+forsale, 0
forsand.no, 0
fortmissoula.museum, 0
fortworth.museum, 0
@@ -1560,6 +1661,19 @@ frei.no, 0
freiburg.museum, 0
freight.aero, 0
fribourg.museum, 0
+friuli-v-giulia.it, 0
+friuli-ve-giulia.it, 0
+friuli-vegiulia.it, 0
+friuli-venezia-giulia.it, 0
+friuli-veneziagiulia.it, 0
+friuli-vgiulia.it, 0
+friuliv-giulia.it, 0
+friulive-giulia.it, 0
+friulivegiulia.it, 0
+friulivenezia-giulia.it, 0
+friuliveneziagiulia.it, 0
+friulivgiulia.it, 0
+frl, 0
frog.museum, 0
frogans, 0
frogn.no, 0
@@ -1663,10 +1777,12 @@ fukuyama.hiroshima.jp, 0
funabashi.chiba.jp, 0
funagata.yamagata.jp, 0
funahashi.toyama.jp, 0
+fund, 0
fundacio.museum, 0
fuoisku.no, 0
fuossko.no, 0
furano.hokkaido.jp, 0
+furniture, 0
furniture.museum, 0
furubira.hokkaido.jp, 0
furudono.fukushima.jp, 0
@@ -1678,6 +1794,7 @@ futaba.fukushima.jp, 0
futbol, 0
futsu.nagasaki.jp, 0
futtsu.chiba.jp, 0
+fvg.it, 0
fylkesbibl.no, 0
fyresdal.no, 0
g.bg, 0
@@ -1703,6 +1820,7 @@ garden.museum, 0
gateway.museum, 0
gaular.no, 0
gausdal.no, 0
+gb, 0
gb.com, 4
gb.net, 4
gc.ca, 0
@@ -1713,13 +1831,16 @@ gdansk.pl, 0
gdynia.pl, 0
ge, 0
ge.it, 0
+geek.nz, 0
geelvinck.museum, 0
geisei.kochi.jp, 0
gemological.museum, 0
gen.in, 0
+gen.nz, 0
genkai.saga.jp, 0
genoa.it, 0
genova.it, 0
+gent, 0
geology.museum, 0
geometre-expert.fr, 0
georgia.museum, 0
@@ -1742,6 +1863,8 @@ ginowan.okinawa.jp, 0
ginoza.okinawa.jp, 0
giske.no, 0
github.io, 4
+githubusercontent.com, 4
+gives, 0
gjemnes.no, 0
gjerdrum.no, 0
gjerstad.no, 0
@@ -1753,13 +1876,16 @@ glass, 0
glass.museum, 0
gliding.aero, 0
gliwice.pl, 0
+global, 0
global.prod.fastly.net, 4
global.ssl.fastly.net, 4
globo, 0
glogow.pl, 0
gloppen.no, 0
gm, 0
+gmail, 0
gmina.pl, 0
+gmx, 0
gn, 0
gniezno.pl, 0
go.ci, 0
@@ -1787,6 +1913,7 @@ gob.pa, 0
gob.pe, 0
gob.pk, 0
gob.sv, 0
+gob.ve, 0
gobo.wakayama.jp, 0
godo.gifu.jp, 0
gojome.akita.jp, 0
@@ -1824,6 +1951,7 @@ gov.ac, 0
gov.ae, 0
gov.af, 0
gov.al, 0
+gov.ar, 0
gov.as, 0
gov.au, 0
gov.az, 0
@@ -1887,6 +2015,7 @@ gov.ml, 0
gov.mn, 0
gov.mo, 0
gov.mr, 0
+gov.ms, 0
gov.mu, 0
gov.mv, 0
gov.mw, 0
@@ -1925,11 +2054,13 @@ gov.to, 0
gov.tt, 0
gov.tw, 0
gov.ua, 0
+gov.uk, 0
gov.vc, 0
gov.ve, 0
gov.vn, 0
gov.ws, 0
government.aero, 0
+govt.nz, 0
gp, 0
gq, 0
gr, 0
@@ -1943,9 +2074,12 @@ grane.no, 0
granvin.no, 0
graphics, 0
gratangen.no, 0
+gratis, 0
graz.museum, 0
+green, 0
greta.fr, 0
grimstad.no, 0
+gripe, 0
groks-the.info, 4
groks-this.info, 4
grong.no, 0
@@ -2037,6 +2171,7 @@ hamar.no, 0
hamaroy.no, 0
hamatama.saga.jp, 0
hamatonbetsu.hokkaido.jp, 0
+hamburg, 0
hamburg.museum, 0
hammarfeasta.no, 0
hammerfest.no, 0
@@ -2078,6 +2213,7 @@ hazu.aichi.jp, 0
hb.cn, 0
he.cn, 0
health.museum, 0
+health.nz, 0
health.vn, 0
heguri.nara.jp, 0
heimatunduhren.museum, 0
@@ -2089,6 +2225,7 @@ hemne.no, 0
hemnes.no, 0
hemsedal.no, 0
herad.no, 0
+here, 0
here-for-more.info, 4
heritage.museum, 0
herokuapp.com, 4
@@ -2143,6 +2280,7 @@ hino.tottori.jp, 0
hinode.tokyo.jp, 0
hinohara.tokyo.jp, 0
hioki.kagoshima.jp, 0
+hiphop, 0
hirado.nagasaki.jp, 0
hiraizumi.iwate.jp, 0
hirakata.osaka.jp, 0
@@ -2174,6 +2312,7 @@ hitachiomiya.ibaraki.jp, 0
hitachiota.ibaraki.jp, 0
hitoyoshi.kumamoto.jp, 0
hitra.no, 0
+hiv, 0
hizen.saga.jp, 0
hjartdal.no, 0
hjelmeland.no, 0
@@ -2227,11 +2366,14 @@ horology.museum, 0
horonobe.hokkaido.jp, 0
horse, 0
horten.no, 0
+host, 0
+hosting, 0
hotel.hu, 0
hotel.lk, 0
hotel.tz, 0
house, 0
house.museum, 0
+how, 0
hoyanger.no, 0
hoylandet.no, 0
hr, 0
@@ -2281,6 +2423,7 @@ idv.hk, 0
idv.tw, 0
ie, 0
if.ua, 0
+ifm, 0
iglesias-carbonia.it, 0
iglesiascarbonia.it, 0
iheya.okinawa.jp, 0
@@ -2321,6 +2464,7 @@ in, 0
in-addr.arpa, 0
in-the-band.net, 4
in.na, 0
+in.net, 4
in.rs, 0
in.th, 0
in.ua, 0
@@ -2352,6 +2496,7 @@ ine.kyoto.jp, 0
inf.br, 0
inf.cu, 0
inf.mk, 0
+infiniti, 0
info, 0
info.at, 4
info.au, 0
@@ -2377,12 +2522,14 @@ info.tt, 0
info.tz, 0
info.ve, 0
info.vn, 0
+ing, 0
ing.pa, 0
ingatlan.hu, 0
ink, 0
ino.kochi.jp, 0
institute, 0
insurance.aero, 0
+insure, 0
int, 0
int.ar, 0
int.az, 0
@@ -2399,12 +2546,14 @@ int.ru, 0
int.rw, 0
int.tj, 0
int.tt, 0
+int.ve, 0
int.vn, 0
intelligence.museum, 0
interactive.museum, 0
international, 0
intl.tn, 0
inuyama.aichi.jp, 0
+investments, 0
inzai.chiba.jp, 0
io, 0
ip6.arpa, 0
@@ -2548,6 +2697,7 @@ iwata.shizuoka.jp, 0
iwate.iwate.jp, 0
iwate.jp, 0
iwatsuki.saitama.jp, 0
+iwi.nz, 0
iyo.ehime.jp, 0
iz.hr, 0
izena.okinawa.jp, 0
@@ -2575,7 +2725,6 @@ jeonbuk.kr, 0
jeonnam.kr, 0
jerusalem.museum, 0
jessheim.no, 0
-jet.uk, 1
jetzt, 0
jevnaker.no, 0
jewelry.museum, 0
@@ -2590,6 +2739,7 @@ jo, 0
joboji.iwate.jp, 0
jobs, 0
jobs.tt, 0
+joburg, 0
joetsu.niigata.jp, 0
jogasz.hu, 0
johana.toyama.jp, 0
@@ -2610,6 +2760,7 @@ js.cn, 0
judaica.museum, 0
judygarland.museum, 0
juedisches.museum, 0
+juegos, 0
juif.museum, 0
jur.pro, 0
jus.br, 0
@@ -2670,7 +2821,6 @@ k12.vi.us, 0
k12.vt.us, 0
k12.wa.us, 0
k12.wi.us, 0
-k12.wv.us, 0
k12.wy.us, 0
kadena.okinawa.jp, 0
kadogawa.miyazaki.jp, 0
@@ -2901,6 +3051,7 @@ kitayama.wakayama.jp, 0
kitchen, 0
kiwa.mie.jp, 0
kiwi, 0
+kiwi.nz, 0
kiyama.saga.jp, 0
kiyokawa.kanagawa.jp, 0
kiyosato.hokkaido.jp, 0
@@ -3101,11 +3252,16 @@ latina.it, 0
lavagis.no, 0
lavangen.no, 0
law.pro, 0
+lawyer, 0
+laz.it, 0
+lazio.it, 0
lb, 0
lc, 0
lc.it, 0
+lds, 0
le.it, 0
leangaviika.no, 0
+lease, 0
leasing.aero, 0
lebesby.no, 0
lebork.pl, 0
@@ -3129,6 +3285,7 @@ lewismiller.museum, 0
lezajsk.pl, 0
lg.jp, 0
lg.ua, 0
+lgbt, 0
li, 0
li.it, 0
lib.ak.us, 0
@@ -3188,12 +3345,16 @@ lib.wi.us, 0
lib.wy.us, 0
lier.no, 0
lierne.no, 0
+life, 0
+lig.it, 0
lighting, 0
+liguria.it, 0
likes-pie.com, 4
likescandy.com, 4
lillehammer.no, 0
lillesand.no, 0
limanowa.pl, 0
+limited, 0
limo, 0
lincoln.museum, 0
lindas.no, 0
@@ -3208,11 +3369,15 @@ lk, 0
ln.cn, 0
lo.it, 0
loabat.no, 0
+loans, 0
localhistory.museum, 0
lodi.it, 0
lodingen.no, 0
logistics.aero, 0
+lom.it, 0
lom.no, 0
+lombardia.it, 0
+lombardy.it, 0
lomza.pl, 0
london, 0
london.museum, 0
@@ -3220,6 +3385,7 @@ loppa.no, 0
lorenskog.no, 0
losangeles.museum, 0
loten.no, 0
+lotto, 0
louvre.museum, 0
lowicz.pl, 0
loyalist.museum, 0
@@ -3231,9 +3397,12 @@ lt.ua, 0
ltd.co.im, 0
ltd.gi, 0
ltd.lk, 0
+ltd.uk, 0
+ltda, 0
lu, 0
lu.it, 0
lubin.pl, 0
+lucania.it, 0
lucca.it, 0
lucerne.museum, 0
lugansk.ua, 0
@@ -3260,6 +3429,7 @@ ma.us, 0
macerata.it, 0
machida.tokyo.jp, 0
mad.museum, 0
+madrid, 0
madrid.museum, 0
maebashi.gunma.jp, 0
magadan.ru, 0
@@ -3289,13 +3459,17 @@ mansion.museum, 0
mansions.museum, 0
mantova.it, 0
manx.museum, 0
+maori.nz, 0
+mar.it, 0
marburg.museum, 0
+marche.it, 0
mari-el.ru, 0
mari.ru, 0
marine.ru, 0
maritime.museum, 0
maritimo.museum, 0
marker.no, 0
+market, 0
marketing, 0
marketplace.aero, 0
marnardal.no, 0
@@ -3344,6 +3518,7 @@ md.us, 0
me, 0
me.it, 0
me.tz, 0
+me.uk, 0
me.us, 0
med.br, 0
med.ec, 0
@@ -3358,6 +3533,7 @@ med.sa, 0
med.sd, 0
medecin.fr, 0
medecin.km, 0
+media, 0
media.aero, 0
media.hu, 0
media.museum, 0
@@ -3372,15 +3548,18 @@ meguro.tokyo.jp, 0
meiwa.gunma.jp, 0
meiwa.mie.jp, 0
meland.no, 0
+melbourne, 0
meldal.no, 0
melhus.no, 0
meloy.no, 0
+meme, 0
memorial.museum, 0
menu, 0
meraker.no, 0
merseine.nu, 4
mesaverde.museum, 0
messina.it, 0
+mex.com, 4
mg, 0
mh, 0
mi.it, 0
@@ -3447,6 +3626,7 @@ mil.mv, 0
mil.my, 0
mil.ng, 0
mil.no, 0
+mil.nz, 0
mil.pe, 0
mil.ph, 0
mil.pl, 0
@@ -3576,7 +3756,6 @@ mobi.tt, 0
mobi.tz, 0
mochizuki.nagano.jp, 0
mod.gi, 0
-mod.uk, 1
moda, 0
modalen.no, 0
modelling.aero, 0
@@ -3585,7 +3764,9 @@ modern.museum, 0
modum.no, 0
moe, 0
moka.tochigi.jp, 0
+mol.it, 0
molde.no, 0
+molise.it, 0
moma.museum, 0
mombetsu.hokkaido.jp, 0
monash, 0
@@ -3609,6 +3790,7 @@ moriyoshi.akita.jp, 0
mormon, 0
morotsuka.miyazaki.jp, 0
moroyama.saitama.jp, 0
+mortgage, 0
moscow, 0
moscow.museum, 0
moseushi.hokkaido.jp, 0
@@ -3623,7 +3805,9 @@ motorcycle.museum, 0
motorcycles, 0
motosu.gifu.jp, 0
motoyama.kochi.jp, 0
+mov, 0
mp, 0
+mp.br, 0
mq, 0
mr, 0
mr.no, 0
@@ -3689,7 +3873,6 @@ nabari.mie.jp, 0
nachikatsuura.wakayama.jp, 0
nagahama.shiga.jp, 0
nagai.yamagata.jp, 0
-nagakute.aichi.jp, 0
nagano.jp, 0
nagano.nagano.jp, 0
naganohara.gunma.jp, 0
@@ -3789,7 +3972,6 @@ narvik.no, 0
nasu.tochigi.jp, 0
nasushiobara.tochigi.jp, 0
nat.tn, 0
-national-library-scotland.uk, 1
national.museum, 0
nationalfirearms.museum, 0
nationalheritage.museum, 0
@@ -3807,6 +3989,7 @@ naustdal.no, 0
naval.museum, 0
navigation.aero, 0
navuotna.no, 0
+navy, 0
nayoro.hokkaido.jp, 0
nb.ca, 0
nc, 0
@@ -3822,7 +4005,6 @@ ne.us, 0
neat-url.com, 4
nebraska.museum, 0
nedre-eiker.no, 0
-nel.uk, 1
nemuro.hokkaido.jp, 0
nerima.tokyo.jp, 0
nes.akershus.no, 0
@@ -3853,6 +4035,7 @@ net.bs, 0
net.bt, 0
net.bz, 0
net.ci, 0
+net.cm, 0
net.cn, 0
net.co, 0
net.cu, 0
@@ -3897,6 +4080,7 @@ net.me, 0
net.mk, 0
net.ml, 0
net.mo, 0
+net.ms, 0
net.mt, 0
net.mu, 0
net.mv, 0
@@ -3906,6 +4090,7 @@ net.my, 0
net.nf, 0
net.ng, 0
net.nr, 0
+net.nz, 0
net.om, 0
net.pa, 0
net.pe, 0
@@ -3938,16 +4123,19 @@ net.to, 0
net.tt, 0
net.tw, 0
net.ua, 0
+net.uk, 0
net.uy, 0
net.uz, 0
net.vc, 0
net.ve, 0
net.vi, 0
net.vn, 0
+net.vu, 0
net.ws, 0
network, 0
neues.museum, 0
neustar, 0
+new, 0
newhampshire.museum, 0
newjersey.museum, 0
newmexico.museum, 0
@@ -3958,18 +4146,22 @@ newyork.museum, 0
neyagawa.osaka.jp, 0
nf, 0
nf.ca, 0
+nfshost.com, 4
ng, 0
+ngo, 0
ngo.lk, 0
ngo.ph, 0
ngo.pl, 0
nh.us, 0
+nhk, 0
+nhs.uk, 0
ni, 2
nic.in, 0
nic.tj, 0
nic.tr, 1
-nic.uk, 1
nichinan.miyazaki.jp, 0
nichinan.tottori.jp, 0
+nid.io, 4
niepce.museum, 0
nieruchomosci.pl, 0
niigata.jp, 0
@@ -4007,6 +4199,7 @@ nishio.aichi.jp, 0
nishiokoppe.hokkaido.jp, 0
nishitosa.kochi.jp, 0
nishiwaki.hyogo.jp, 0
+nissan, 0
nissedal.no, 0
nisshin.aichi.jp, 0
nittedal.no, 0
@@ -4016,7 +4209,6 @@ nkz.ru, 0
nl, 0
nl.ca, 0
nl.no, 0
-nls.uk, 1
nm.cn, 0
nm.us, 0
nnov.ru, 0
@@ -4075,6 +4267,7 @@ nowaruda.pl, 0
nozawaonsen.nagano.jp, 0
np, 2
nr, 0
+nra, 0
nrw, 0
nrw.museum, 0
ns.ca, 0
@@ -4100,12 +4293,13 @@ nuremberg.museum, 0
nv.us, 0
nx.cn, 0
ny.us, 0
+nyc, 0
nyc.mn, 4
nyc.museum, 0
nyny.museum, 0
nysa.pl, 0
nyuzen.toyama.jp, 0
-nz, 2
+nz, 0
o.bg, 0
o.se, 0
oamishirasato.chiba.jp, 0
@@ -4216,6 +4410,7 @@ omuta.fukuoka.jp, 0
on-the-web.tv, 4
on.ca, 0
onagawa.miyagi.jp, 0
+ong, 0
onga.fukuoka.jp, 0
onjuku.chiba.jp, 0
onl, 0
@@ -4329,6 +4524,7 @@ org.mk, 0
org.ml, 0
org.mn, 0
org.mo, 0
+org.ms, 0
org.mt, 0
org.mu, 0
org.mv, 0
@@ -4338,6 +4534,7 @@ org.my, 0
org.na, 0
org.ng, 0
org.nr, 0
+org.nz, 0
org.om, 0
org.pa, 0
org.pe, 0
@@ -4376,13 +4573,16 @@ org.tt, 0
org.tw, 0
org.ua, 0
org.ug, 0
+org.uk, 0
org.uy, 0
org.uz, 0
org.vc, 0
org.ve, 0
org.vi, 0
org.vn, 0
+org.vu, 0
org.ws, 0
+organic, 0
oristano.it, 0
orkanger.no, 0
orkdal.no, 0
@@ -4436,6 +4636,7 @@ otsuki.yamanashi.jp, 0
ouchi.saga.jp, 0
ouda.nara.jp, 0
oumu.hokkaido.jp, 0
+outsystemscloud.com, 4
overhalla.no, 0
ovh, 0
ovre-eiker.no, 0
@@ -4471,8 +4672,9 @@ palmsprings.museum, 0
panama.museum, 0
parachuting.aero, 0
paragliding.aero, 0
+paris, 0
paris.museum, 0
-parliament.uk, 1
+parliament.nz, 0
parma.it, 0
paroch.k12.ma.us, 0
parti.se, 0
@@ -4516,9 +4718,13 @@ photo, 0
photography, 0
photography.museum, 0
photos, 0
+physio, 0
pi.it, 0
piacenza.it, 0
pics, 0
+pictures, 0
+piedmont.it, 0
+piemonte.it, 0
pila.pl, 0
pilot.aero, 0
pilots.museum, 0
@@ -4531,15 +4737,18 @@ pittsburgh.museum, 0
pk, 0
pl, 0
pl.ua, 0
+place, 0
planetarium.museum, 0
plantation.museum, 0
plants.museum, 0
plaza.museum, 0
plc.co.im, 0
plc.ly, 0
+plc.uk, 0
plo.ps, 0
plumbing, 0
pm, 0
+pmn.it, 0
pn, 0
pn.it, 0
po.gov.pl, 0
@@ -4550,6 +4759,7 @@ podzone.net, 4
podzone.org, 4
pol.dz, 0
pol.ht, 0
+police.uk, 0
polkowice.pl, 0
poltava.ua, 0
pomorskie.pl, 0
@@ -4582,6 +4792,7 @@ prd.km, 0
prd.mg, 0
preservation.museum, 0
presidio.museum, 0
+press, 0
press.aero, 0
press.ma, 0
press.museum, 0
@@ -4609,17 +4820,18 @@ pro.pr, 0
pro.tt, 0
pro.vn, 0
prochowice.pl, 0
+prod, 0
production.aero, 0
productions, 0
prof.pr, 0
project.museum, 0
properties, 0
+property, 0
pruszkow.pl, 0
przeworsk.pl, 0
ps, 0
psc.br, 0
psi.br, 0
-pskov.ru, 0
pt, 0
pt.it, 0
ptz.ru, 0
@@ -4629,6 +4841,8 @@ pub.sa, 0
publ.pt, 0
public.museum, 0
pubol.museum, 0
+pug.it, 0
+puglia.it, 0
pulawy.pl, 0
pv.it, 0
pvt.ge, 0
@@ -4677,11 +4891,13 @@ re.it, 0
re.kr, 0
readmyblog.org, 4
realestate.pl, 0
+realtor, 0
rebun.hokkaido.jp, 0
rec.br, 0
rec.co, 0
rec.nf, 0
rec.ro, 0
+rec.ve, 0
recipes, 0
recreation.aero, 0
red, 0
@@ -4690,6 +4906,9 @@ reggio-calabria.it, 0
reggio-emilia.it, 0
reggiocalabria.it, 0
reggioemilia.it, 0
+rehab, 0
+reise, 0
+reisen, 0
reklam.hu, 0
rel.ht, 0
rel.pl, 0
@@ -4702,6 +4921,7 @@ rep.kp, 0
repair, 0
repbody.aero, 0
report, 0
+republican, 0
res.aero, 0
res.in, 0
research.aero, 0
@@ -4724,6 +4944,7 @@ rindal.no, 0
ringebu.no, 0
ringerike.no, 0
ringsaker.no, 0
+rio, 0
riodejaneiro.museum, 0
rishiri.hokkaido.jp, 0
rishirifuji.hokkaido.jp, 0
@@ -4763,6 +4984,7 @@ royken.no, 0
royrvik.no, 0
rs, 0
rs.ba, 0
+rsvp, 0
ru, 0
ru.com, 4
rubtsovsk.ru, 0
@@ -4855,6 +5077,7 @@ salzburg.museum, 0
samara.ru, 0
samegawa.fukushima.jp, 0
samnanger.no, 0
+samsung, 0
samukawa.kanagawa.jp, 0
sanagochi.tokushima.jp, 0
sanda.hyogo.jp, 0
@@ -4878,8 +5101,12 @@ santacruz.museum, 0
santafe.museum, 0
sanuki.kagawa.jp, 0
saotome.st, 0
+sap, 0
sapporo.jp, 2
+sar.it, 0
saratov.ru, 0
+sardegna.it, 0
+sardinia.it, 0
saroma.hokkaido.jp, 0
sarpsborg.no, 0
sarufutsu.hokkaido.jp, 0
@@ -4908,6 +5135,8 @@ sc.kr, 0
sc.tz, 0
sc.ug, 0
sc.us, 0
+sca, 0
+scb, 0
sch.ae, 0
sch.id, 0
sch.ir, 0
@@ -4919,10 +5148,14 @@ sch.qa, 0
sch.sa, 0
sch.uk, 2
schlesisches.museum, 0
+schmidt, 0
schoenbrunn.museum, 0
schokoladen.museum, 0
+scholarships, 0
school.museum, 0
school.na, 0
+school.nz, 0
+schule, 0
schweiz.museum, 0
sci.eg, 0
science-fiction.museum, 0
@@ -4935,6 +5168,7 @@ sciencehistory.museum, 0
sciences.museum, 0
sciencesnaturelles.museum, 0
scientist.aero, 0
+scot, 0
scotland.museum, 0
scrapper-site.net, 4
scrapping.cc, 4
@@ -4945,6 +5179,7 @@ se, 0
se.com, 4
se.net, 4
seaport.museum, 0
+seat, 0
sebastopol.ua, 0
sec.ps, 0
seihi.nagasaki.jp, 0
@@ -4982,6 +5217,7 @@ servebbs.org, 4
serveftp.net, 4
serveftp.org, 4
servegame.org, 4
+services, 0
services.aero, 0
setagaya.tokyo.jp, 0
seto.aichi.jp, 0
@@ -5000,6 +5236,7 @@ sh.cn, 0
shacknet.nu, 4
shakotan.hokkaido.jp, 0
shari.hokkaido.jp, 0
+sharp, 0
shell.museum, 0
sherbrooke.museum, 0
shibata.miyagi.jp, 0
@@ -5098,10 +5335,14 @@ show.aero, 0
showa.fukushima.jp, 0
showa.gunma.jp, 0
showa.yamanashi.jp, 0
+shriram, 0
shunan.yamaguchi.jp, 0
si, 0
si.it, 0
sibenik.museum, 0
+sic.it, 0
+sicilia.it, 0
+sicily.it, 0
siedlce.pl, 0
siellak.no, 0
siena.it, 0
@@ -5113,6 +5354,7 @@ simple-url.com, 4
singles, 0
siracusa.it, 0
sirdal.no, 0
+sj, 0
sk, 0
sk.ca, 0
skanit.no, 0
@@ -5159,6 +5401,7 @@ social, 0
society.museum, 0
sodegaura.chiba.jp, 0
soeda.fukuoka.jp, 0
+software, 0
software.aero, 0
sogndal.no, 0
sogne.no, 0
@@ -5194,10 +5437,13 @@ soundandvision.museum, 0
southcarolina.museum, 0
southwest.museum, 0
sowa.ibaraki.jp, 0
+soy, 0
sp.it, 0
+space, 0
space-to-rent.com, 4
space.museum, 0
spb.ru, 0
+spiegel, 0
spjelkavik.no, 0
sport.hu, 0
spy.museum, 0
@@ -5208,7 +5454,6 @@ sr.gov.pl, 0
sr.it, 0
srv.br, 0
ss.it, 0
-sshn.se, 0
st, 0
st.no, 0
stadt.museum, 0
@@ -5243,6 +5488,7 @@ store.bb, 0
store.nf, 0
store.ro, 0
store.st, 0
+store.ve, 0
storfjord.no, 0
stpetersburg.museum, 0
strand.no, 0
@@ -5278,6 +5524,7 @@ supplies, 0
supply, 0
support, 0
surgeonshall.museum, 0
+surgery, 0
surgut.ru, 0
surnadal.no, 0
surrey.museum, 0
@@ -5288,6 +5535,7 @@ suwalki.pl, 0
suzaka.nagano.jp, 0
suzu.ishikawa.jp, 0
suzuka.mie.jp, 0
+suzuki, 0
sv, 0
sv.it, 0
svalbard.no, 0
@@ -5313,6 +5561,7 @@ szkola.pl, 0
t.bg, 0
t.se, 0
ta.it, 0
+taa.it, 0
tabayama.yamanashi.jp, 0
tabuse.yamaguchi.jp, 0
tachiarai.fukuoka.jp, 0
@@ -5371,7 +5620,6 @@ taki.mie.jp, 0
takikawa.hokkaido.jp, 0
takino.hyogo.jp, 0
takinoue.hokkaido.jp, 0
-takizawa.iwate.jp, 0
takko.aomori.jp, 0
tako.chiba.jp, 0
taku.saga.jp, 0
@@ -5401,6 +5649,7 @@ tarumizu.kagoshima.jp, 0
tas.au, 0
tas.edu.au, 0
tas.gov.au, 0
+tatar, 0
tatarstan.ru, 0
tatebayashi.gunma.jp, 0
tateshina.nagano.jp, 0
@@ -5410,6 +5659,7 @@ tatsuno.hyogo.jp, 0
tatsuno.nagano.jp, 0
tattoo, 0
tawaramoto.nara.jp, 0
+tax, 0
taxi.aero, 0
taxi.br, 0
tc, 0
@@ -5418,6 +5668,7 @@ td, 0
te.it, 0
te.ua, 0
teaches-yoga.com, 4
+tec.ve, 0
technology, 0
technology.museum, 0
tel, 0
@@ -5452,6 +5703,7 @@ timekeeping.museum, 0
tingvoll.no, 0
tinn.no, 0
tips, 0
+tirol, 0
tj, 0
tj.cn, 0
tjeldsund.no, 0
@@ -5529,14 +5781,18 @@ tonosho.kagawa.jp, 0
tonsberg.no, 0
tools, 0
toon.ehime.jp, 0
+top, 0
topology.museum, 0
torahime.shiga.jp, 0
toride.ibaraki.jp, 0
torino.it, 0
torino.museum, 0
torsken.no, 0
+tos.it, 0
tosa.kochi.jp, 0
tosashimizu.kochi.jp, 0
+toscana.it, 0
+toshiba, 0
toshima.tokyo.jp, 0
tosu.saga.jp, 0
tottori.jp, 0
@@ -5545,6 +5801,7 @@ touch.museum, 0
tourism.pl, 0
tourism.tn, 0
towada.aomori.jp, 0
+town, 0
town.museum, 0
toya.hokkaido.jp, 0
toyako.hokkaido.jp, 0
@@ -5564,13 +5821,16 @@ toyota.yamaguchi.jp, 0
toyotomi.hokkaido.jp, 0
toyotsu.fukuoka.jp, 0
toyoura.hokkaido.jp, 0
+toys, 0
tozawa.yamagata.jp, 0
tozsde.hu, 0
+tp, 0
tp.it, 0
tr, 2
tr.it, 0
tr.no, 0
tra.kp, 0
+trade, 0
trader.aero, 0
trading.aero, 0
traeumtgerade.de, 4
@@ -5590,7 +5850,27 @@ travel.pl, 0
travel.tt, 0
trd.br, 0
tree.museum, 0
+trentino-a-adige.it, 0
+trentino-aadige.it, 0
+trentino-alto-adige.it, 0
+trentino-altoadige.it, 0
+trentino-s-tirol.it, 0
+trentino-stirol.it, 0
+trentino-sud-tirol.it, 0
+trentino-sudtirol.it, 0
+trentino-sued-tirol.it, 0
+trentino-suedtirol.it, 0
trentino.it, 0
+trentinoa-adige.it, 0
+trentinoaadige.it, 0
+trentinoalto-adige.it, 0
+trentinoaltoadige.it, 0
+trentinos-tirol.it, 0
+trentinostirol.it, 0
+trentinosud-tirol.it, 0
+trentinosudtirol.it, 0
+trentinosued-tirol.it, 0
+trentinosuedtirol.it, 0
trento.it, 0
treviso.it, 0
trieste.it, 0
@@ -5642,8 +5922,10 @@ turek.pl, 0
turen.tn, 0
turin.it, 0
turystyka.pl, 0
+tuscany.it, 0
tuva.ru, 0
tv, 0
+tv.bb, 0
tv.bo, 0
tv.br, 0
tv.im, 0
@@ -5687,7 +5969,7 @@ uhren.museum, 0
uji.kyoto.jp, 0
ujiie.tochigi.jp, 0
ujitawara.kyoto.jp, 0
-uk, 2
+uk, 0
uk.com, 4
uk.net, 4
uki.kumamoto.jp, 0
@@ -5700,18 +5982,22 @@ ulsan.kr, 0
ulvik.no, 0
um.gov.pl, 0
umaji.kochi.jp, 0
+umb.it, 0
+umbria.it, 0
umi.fukuoka.jp, 0
unazuki.toyama.jp, 0
unbi.ba, 0
undersea.museum, 0
union.aero, 0
univ.sn, 0
+university, 0
university.museum, 0
unjarga.no, 0
unnan.shimane.jp, 0
uno, 0
unsa.ba, 0
unzen.nagasaki.jp, 0
+uol, 0
uonuma.niigata.jp, 0
uozu.toyama.jp, 0
upow.gov.pl, 0
@@ -5779,13 +6065,26 @@ vaga.no, 0
vagan.no, 0
vagsoy.no, 0
vaksdal.no, 0
+val-d-aosta.it, 0
+val-daosta.it, 0
+vald-aosta.it, 0
+valdaosta.it, 0
valer.hedmark.no, 0
valer.ostfold.no, 0
+valle-aosta.it, 0
+valle-d-aosta.it, 0
+valle-daosta.it, 0
valle.no, 0
+valleaosta.it, 0
+valled-aosta.it, 0
+valledaosta.it, 0
+vallee-aoste.it, 0
+valleeaoste.it, 0
valley.museum, 0
vang.no, 0
vantaa.museum, 0
vanylven.no, 0
+vao.it, 0
vardo.no, 0
varese.it, 0
varggat.no, 0
@@ -5793,6 +6092,7 @@ varoy.no, 0
vb.it, 0
vc, 0
vc.it, 0
+vda.it, 0
vdonsk.ru, 0
ve, 0
ve.it, 0
@@ -5800,6 +6100,8 @@ vefsn.no, 0
vega.no, 0
vegarshei.no, 0
vegas, 0
+ven.it, 0
+veneto.it, 0
venezia.it, 0
venice.it, 0
vennesla.no, 0
@@ -5810,11 +6112,13 @@ verdal.no, 0
verona.it, 0
verran.no, 0
versailles.museum, 0
+versicherung, 0
vestby.no, 0
vestnes.no, 0
vestre-slidre.no, 0
vestre-toten.no, 0
vestvagoy.no, 0
+vet, 0
vet.br, 0
veterinaire.fr, 0
veterinaire.km, 0
@@ -5846,6 +6150,7 @@ virtual.museum, 0
virtuel.museum, 0
vision, 0
viterbo.it, 0
+vlaanderen, 0
vlaanderen.museum, 0
vladikavkaz.ru, 0
vladimir.ru, 0
@@ -5892,6 +6197,7 @@ wake.okayama.jp, 0
wakkanai.hokkaido.jp, 0
wakuya.miyagi.jp, 0
walbrzych.pl, 0
+wales, 0
wales.museum, 0
wallonie.museum, 0
wang, 0
@@ -5917,26 +6223,33 @@ web.nf, 0
web.pk, 0
web.tj, 0
web.ve, 0
+webcam, 0
webhop.biz, 4
webhop.info, 4
webhop.net, 4
webhop.org, 4
+website, 0
wed, 0
+wedding, 0
wegrow.pl, 0
western.museum, 0
westfalen.museum, 0
wf, 0
whaling.museum, 0
+whoswho, 0
wi.us, 0
wielun.pl, 0
wien, 0
wiki, 0
wiki.br, 0
wildlife.museum, 0
+williamhill, 0
williamsburg.museum, 0
windmill.museum, 0
+withgoogle.com, 4
wlocl.pl, 0
wloclawek.pl, 0
+wme, 0
wodzislaw.pl, 0
wolomin.pl, 0
work, 0
@@ -5951,6 +6264,7 @@ wroclaw.pl, 0
ws, 0
ws.na, 0
wtc, 0
+wtf, 0
wv.us, 0
www.ck, 1
www.ro, 0
@@ -5973,9 +6287,12 @@ xn--55qx5d.hk, 0
xn--6frz82g, 0
xn--6qq986b3xl, 0
xn--80adxhks, 0
+xn--80ao21a, 0
xn--80asehdb, 0
xn--80aswg, 0
+xn--80au.xn--90a3ac, 0
xn--90a3ac, 0
+xn--90azh.xn--90a3ac, 0
xn--9dbhblg6di.museum, 0
xn--andy-ira.no, 0
xn--aroport-bya.ci, 0
@@ -6001,6 +6318,7 @@ xn--brnnysund-m8ac.no, 0
xn--brum-voa.no, 0
xn--btsfjord-9za.no, 0
xn--c1avg, 0
+xn--c1avg.xn--90a3ac, 0
xn--cg4bki, 0
xn--ciqpn.hk, 0
xn--clchc0ea0b2g2a9gcd, 0
@@ -6011,6 +6329,7 @@ xn--czrs0t, 0
xn--czru2d, 0
xn--czrw28b.tw, 0
xn--d1acj3b, 0
+xn--d1at.xn--90a3ac, 0
xn--davvenjrga-y4a.no, 0
xn--dnna-gra.no, 0
xn--drbak-wua.no, 0
@@ -6022,6 +6341,7 @@ xn--fiq64b, 0
xn--fiqs8s, 0
xn--fiqz9s, 0
xn--fjord-lra.no, 0
+xn--fjq720a, 0
xn--fl-zia.no, 0
xn--flor-jra.no, 0
xn--fpcrj9c3d, 0
@@ -6050,6 +6370,7 @@ xn--hnefoss-q1a.no, 0
xn--hobl-ira.no, 0
xn--holtlen-hxa.no, 0
xn--hpmir-xqa.no, 0
+xn--hxt814e, 0
xn--hyanger-q1a.no, 0
xn--hylandet-54a.no, 0
xn--i1b6b1a6a2e, 0
@@ -6067,6 +6388,7 @@ xn--klbu-woa.no, 0
xn--koluokta-7ya57h.no, 0
xn--kprw13d, 0
xn--kpry57d, 0
+xn--kput3i, 0
xn--krager-gya.no, 0
xn--kranghke-b0a.no, 0
xn--krdsherad-m8a.no, 0
@@ -6077,6 +6399,7 @@ xn--kvfjord-nxa.no, 0
xn--kvitsy-fya.no, 0
xn--kvnangen-k0a.no, 0
xn--l-1fa.no, 0
+xn--l1acc, 0
xn--laheadju-7ya.no, 0
xn--langevg-jxa.no, 0
xn--lcvr32d.hk, 0
@@ -6112,17 +6435,20 @@ xn--mgberp4a5d4ar, 0
xn--mgbqly7c0a67fbc, 0
xn--mgbqly7cvafr, 0
xn--mgbtf8fl, 0
+xn--mgbx4cd0ab, 0
xn--mjndalen-64a.no, 0
xn--mk0axi.hk, 0
xn--mlatvuopmi-s4a.no, 0
xn--mli-tla.no, 0
xn--mlselv-iua.no, 0
xn--moreke-jua.no, 0
+xn--mori-qsa.nz, 0
xn--mosjen-eya.no, 0
xn--mot-tla.no, 0
xn--msy-ula0h.no, 0
xn--mtta-vrjjat-k7af.no, 0
xn--muost-0qa.no, 0
+xn--mxtq1m, 0
xn--mxtq1m.hk, 0
xn--ngbc5azd, 0
xn--nmesjevuemie-tcba.no, 0
@@ -6133,6 +6459,8 @@ xn--nqv7fs00ema, 0
xn--nry-yla5g.no, 0
xn--nttery-byae.no, 0
xn--nvuotna-hwa.no, 0
+xn--o1ac.xn--90a3ac, 0
+xn--o1ach.xn--90a3ac, 0
xn--o3cw4h, 0
xn--od0alg.cn, 0
xn--od0alg.hk, 0
@@ -6285,6 +6613,7 @@ yamazoe.nara.jp, 0
yame.fukuoka.jp, 0
yanagawa.fukuoka.jp, 0
yanaizu.fukushima.jp, 0
+yandex, 0
yao.osaka.jp, 0
yaotsu.gifu.jp, 0
yaroslavl.ru, 0
@@ -6306,6 +6635,7 @@ ye, 2
yekaterinburg.ru, 0
yk.ca, 0
yn.cn, 0
+yoga, 0
yoichi.hokkaido.jp, 0
yoita.niigata.jp, 0
yoka.hyogo.jp, 0
@@ -6338,6 +6668,7 @@ yoshinogari.saga.jp, 0
yoshioka.gunma.jp, 0
yotsukaido.chiba.jp, 0
youth.museum, 0
+youtube, 0
yt, 0
yuasa.wakayama.jp, 0
yufu.oita.jp, 0
@@ -6358,6 +6689,7 @@ z-2.compute-1.amazonaws.com, 4
z.bg, 0
z.se, 0
za, 2
+za.bz, 4
za.com, 4
za.net, 4
za.org, 4
@@ -6376,6 +6708,7 @@ zgorzelec.pl, 0
zgrad.ru, 0
zhitomir.ua, 0
zhytomyr.ua, 0
+zip, 0
zj.cn, 0
zlg.br, 0
zm, 2
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest1.cc b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest1.cc
deleted file mode 100644
index b1c5d26c506..00000000000
--- a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest1.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-/* C++ code produced by gperf version 3.0.3 */
-/* Command-line: gperf -a -L C++ -C -c -o -t -k '*' -NFindDomain -ZPerfect_Hash_Test1 -P -K name_offset -Q stringpool1 -D effective_tld_names_unittest1.gperf */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-#line 1 "effective_tld_names_unittest1.gperf"
-
-// 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.
-// Test file used by registry_controlled_domain_unittest.
-// We edit this file manually, then run
-// gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -ZPerfect_Hash_Test1 -P -K name_offset -Q stringpool1 -D effective_tld_names_unittest1.gperf > effective_tld_names_unittest1.cc
-// to generate the perfect hashmap.
-#line 10 "effective_tld_names_unittest1.gperf"
-struct DomainRule {
- int name_offset;
- int type; // 1: exception, 2: wildcard, 4: private
-};
-
-#define TOTAL_KEYWORDS 11
-#define MIN_WORD_LENGTH 1
-#define MAX_WORD_LENGTH 11
-#define MIN_HASH_VALUE 1
-#define MAX_HASH_VALUE 17
-/* maximum key range = 17, duplicates = 0 */
-
-class Perfect_Hash_Test1
-{
-private:
- static inline unsigned int hash (const char *str, unsigned int len);
-public:
- static const struct DomainRule *FindDomain (const char *str, unsigned int len);
-};
-
-inline unsigned int
-Perfect_Hash_Test1::hash (register const char *str, register unsigned int len)
-{
- static const unsigned char asso_values[] =
- {
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 0, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 0, 0, 0,
- 18, 5, 0, 18, 18, 0, 0, 18, 18, 0,
- 5, 0, 0, 18, 0, 18, 5, 18, 0, 18,
- 18, 18, 0, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18
- };
- register int hval = len;
-
- switch (hval)
- {
- default:
- hval += asso_values[(unsigned char)str[10]];
- /*FALLTHROUGH*/
- case 10:
- hval += asso_values[(unsigned char)str[9]];
- /*FALLTHROUGH*/
- case 9:
- hval += asso_values[(unsigned char)str[8]];
- /*FALLTHROUGH*/
- case 8:
- hval += asso_values[(unsigned char)str[7]];
- /*FALLTHROUGH*/
- case 7:
- hval += asso_values[(unsigned char)str[6]];
- /*FALLTHROUGH*/
- case 6:
- hval += asso_values[(unsigned char)str[5]];
- /*FALLTHROUGH*/
- case 5:
- hval += asso_values[(unsigned char)str[4]];
- /*FALLTHROUGH*/
- case 4:
- hval += asso_values[(unsigned char)str[3]];
- /*FALLTHROUGH*/
- case 3:
- hval += asso_values[(unsigned char)str[2]];
- /*FALLTHROUGH*/
- case 2:
- hval += asso_values[(unsigned char)str[1]];
- /*FALLTHROUGH*/
- case 1:
- hval += asso_values[(unsigned char)str[0]];
- break;
- }
- return hval;
-}
-
-struct stringpool1_t
- {
- char stringpool1_str0[sizeof("c")];
- char stringpool1_str1[sizeof("jp")];
- char stringpool1_str2[sizeof("b.c")];
- char stringpool1_str3[sizeof("ac.jp")];
- char stringpool1_str4[sizeof("bar.jp")];
- char stringpool1_str5[sizeof("no")];
- char stringpool1_str6[sizeof("baz.bar.jp")];
- char stringpool1_str7[sizeof("bar.baz.com")];
- char stringpool1_str8[sizeof("priv.no")];
- char stringpool1_str9[sizeof("pref.bar.jp")];
- char stringpool1_str10[sizeof("private")];
- };
-static const struct stringpool1_t stringpool1_contents =
- {
- "c",
- "jp",
- "b.c",
- "ac.jp",
- "bar.jp",
- "no",
- "baz.bar.jp",
- "bar.baz.com",
- "priv.no",
- "pref.bar.jp",
- "private"
- };
-#define stringpool1 ((const char *) &stringpool1_contents)
-const struct DomainRule *
-Perfect_Hash_Test1::FindDomain (register const char *str, register unsigned int len)
-{
- static const struct DomainRule wordlist[] =
- {
-#line 21 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str0, 2},
-#line 15 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str1, 0},
-#line 22 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str2, 1},
-#line 16 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str3, 0},
-#line 17 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str4, 2},
-#line 23 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str5, 0},
-#line 18 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str6, 2},
-#line 20 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str7, 0},
-#line 24 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str8, 4},
-#line 19 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str9, 1},
-#line 25 "effective_tld_names_unittest1.gperf"
- {(int)(long)&((struct stringpool1_t *)0)->stringpool1_str10, 4}
- };
-
- static const signed char lookup[] =
- {
- -1, 0, 1, 2, -1, 3, 4, 5, -1, -1, 6, 7, 8, -1,
- -1, -1, 9, 10
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register int index = lookup[key];
-
- if (index >= 0)
- {
- register const char *s = wordlist[index].name_offset + stringpool1;
-
- if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
- return &wordlist[index];
- }
- }
- }
- return 0;
-}
-#line 26 "effective_tld_names_unittest1.gperf"
-
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest2.cc b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest2.cc
deleted file mode 100644
index a57151e0c23..00000000000
--- a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest2.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-/* C++ code produced by gperf version 3.0.3 */
-/* Command-line: gperf -a -L C++ -C -c -o -t -k '*' -NFindDomain -ZPerfect_Hash_Test2 -P -K name_offset -Q stringpool2 -D -T effective_tld_names_unittest2.gperf */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
- && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
- && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
- && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
- && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
- && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
- && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
- && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
- && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
- && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
- && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
- && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
- && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
- && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
- && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
- && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
- && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
- && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
- && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
- && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
- && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
- && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
- && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646. */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-#line 1 "effective_tld_names_unittest2.gperf"
-
-// 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.
-// Test file used by registry_controlled_domain_unittest.
-// We edit this file manually, then run
-// gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -ZPerfect_Hash_Test2 -P -K name_offset -Q stringpool2 -D -T effective_tld_names_unittest2.gperf > effective_tld_names_unittest2.cc
-// to generate the perfect hashmap.
-
-#define TOTAL_KEYWORDS 2
-#define MIN_WORD_LENGTH 2
-#define MAX_WORD_LENGTH 6
-#define MIN_HASH_VALUE 2
-#define MAX_HASH_VALUE 6
-/* maximum key range = 5, duplicates = 0 */
-
-class Perfect_Hash_Test2
-{
-private:
- static inline unsigned int hash (const char *str, unsigned int len);
-public:
- static const struct DomainRule *FindDomain (const char *str, unsigned int len);
-};
-
-inline unsigned int
-Perfect_Hash_Test2::hash (register const char *str, register unsigned int len)
-{
- static const unsigned char asso_values[] =
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 0, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 0, 0, 7,
- 7, 7, 7, 7, 7, 7, 0, 7, 7, 7,
- 7, 7, 0, 7, 0, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7
- };
- register int hval = len;
-
- switch (hval)
- {
- default:
- hval += asso_values[(unsigned char)str[5]];
- /*FALLTHROUGH*/
- case 5:
- hval += asso_values[(unsigned char)str[4]];
- /*FALLTHROUGH*/
- case 4:
- hval += asso_values[(unsigned char)str[3]];
- /*FALLTHROUGH*/
- case 3:
- hval += asso_values[(unsigned char)str[2]];
- /*FALLTHROUGH*/
- case 2:
- hval += asso_values[(unsigned char)str[1]];
- /*FALLTHROUGH*/
- case 1:
- hval += asso_values[(unsigned char)str[0]];
- break;
- }
- return hval;
-}
-
-struct stringpool2_t
- {
- char stringpool2_str0[sizeof("jp")];
- char stringpool2_str1[sizeof("bar.jp")];
- };
-static const struct stringpool2_t stringpool2_contents =
- {
- "jp",
- "bar.jp"
- };
-#define stringpool2 ((const char *) &stringpool2_contents)
-const struct DomainRule *
-Perfect_Hash_Test2::FindDomain (register const char *str, register unsigned int len)
-{
- static const struct DomainRule wordlist[] =
- {
-#line 15 "effective_tld_names_unittest2.gperf"
- {(int)(long)&((struct stringpool2_t *)0)->stringpool2_str0, 0},
-#line 16 "effective_tld_names_unittest2.gperf"
- {(int)(long)&((struct stringpool2_t *)0)->stringpool2_str1, 0}
- };
-
- static const signed char lookup[] =
- {
- -1, -1, 0, -1, -1, -1, 1
- };
-
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = hash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register int index = lookup[key];
-
- if (index >= 0)
- {
- register const char *s = wordlist[index].name_offset + stringpool2;
-
- if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
- return &wordlist[index];
- }
- }
- }
- return 0;
-}
-#line 17 "effective_tld_names_unittest2.gperf"
-
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf
new file mode 100644
index 00000000000..aed5838da73
--- /dev/null
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf
@@ -0,0 +1,13 @@
+This file is for testing 2 byte offsets
+%%
+0____________________________________________________________________________________________________0, 0
+1____________________________________________________________________________________________________1, 4
+2____________________________________________________________________________________________________2, 0
+3____________________________________________________________________________________________________3, 4
+4____________________________________________________________________________________________________4, 0
+5____________________________________________________________________________________________________5, 4
+6____________________________________________________________________________________________________6, 0
+7____________________________________________________________________________________________________7, 4
+8____________________________________________________________________________________________________8, 0
+9____________________________________________________________________________________________________9, 4
+%%
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf
new file mode 100644
index 00000000000..b89e6c02423
--- /dev/null
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf
@@ -0,0 +1,523 @@
+This file is for testing 3 byte offsets
+%%
+a0____________________________________________________________________________________________________a0, 0
+a1____________________________________________________________________________________________________a1, 4
+a2____________________________________________________________________________________________________a2, 0
+a3____________________________________________________________________________________________________a3, 4
+a4____________________________________________________________________________________________________a4, 0
+a5____________________________________________________________________________________________________a5, 4
+a6____________________________________________________________________________________________________a6, 0
+a7____________________________________________________________________________________________________a7, 4
+a8____________________________________________________________________________________________________a8, 0
+a9____________________________________________________________________________________________________a9, 4
+b0____________________________________________________________________________________________________b0, 0
+b1____________________________________________________________________________________________________b1, 4
+b2____________________________________________________________________________________________________b2, 0
+b3____________________________________________________________________________________________________b3, 4
+b4____________________________________________________________________________________________________b4, 0
+b5____________________________________________________________________________________________________b5, 4
+b6____________________________________________________________________________________________________b6, 0
+b7____________________________________________________________________________________________________b7, 4
+b8____________________________________________________________________________________________________b8, 0
+b9____________________________________________________________________________________________________b9, 4
+c0____________________________________________________________________________________________________c0, 0
+c1____________________________________________________________________________________________________c1, 4
+c2____________________________________________________________________________________________________c2, 0
+c3____________________________________________________________________________________________________c3, 4
+c4____________________________________________________________________________________________________c4, 0
+c5____________________________________________________________________________________________________c5, 4
+c6____________________________________________________________________________________________________c6, 0
+c7____________________________________________________________________________________________________c7, 4
+c8____________________________________________________________________________________________________c8, 0
+c9____________________________________________________________________________________________________c9, 4
+d0____________________________________________________________________________________________________d0, 0
+d1____________________________________________________________________________________________________d1, 4
+d2____________________________________________________________________________________________________d2, 0
+d3____________________________________________________________________________________________________d3, 4
+d4____________________________________________________________________________________________________d4, 0
+d5____________________________________________________________________________________________________d5, 4
+d6____________________________________________________________________________________________________d6, 0
+d7____________________________________________________________________________________________________d7, 4
+d8____________________________________________________________________________________________________d8, 0
+d9____________________________________________________________________________________________________d9, 4
+e0____________________________________________________________________________________________________e0, 0
+e1____________________________________________________________________________________________________e1, 4
+e2____________________________________________________________________________________________________e2, 0
+e3____________________________________________________________________________________________________e3, 4
+e4____________________________________________________________________________________________________e4, 0
+e5____________________________________________________________________________________________________e5, 4
+e6____________________________________________________________________________________________________e6, 0
+e7____________________________________________________________________________________________________e7, 4
+e8____________________________________________________________________________________________________e8, 0
+e9____________________________________________________________________________________________________e9, 4
+f0____________________________________________________________________________________________________f0, 0
+f1____________________________________________________________________________________________________f1, 4
+f2____________________________________________________________________________________________________f2, 0
+f3____________________________________________________________________________________________________f3, 4
+f4____________________________________________________________________________________________________f4, 0
+f5____________________________________________________________________________________________________f5, 4
+f6____________________________________________________________________________________________________f6, 0
+f7____________________________________________________________________________________________________f7, 4
+f8____________________________________________________________________________________________________f8, 0
+f9____________________________________________________________________________________________________f9, 4
+g0____________________________________________________________________________________________________g0, 0
+g1____________________________________________________________________________________________________g1, 4
+g2____________________________________________________________________________________________________g2, 0
+g3____________________________________________________________________________________________________g3, 4
+g4____________________________________________________________________________________________________g4, 0
+g5____________________________________________________________________________________________________g5, 4
+g6____________________________________________________________________________________________________g6, 0
+g7____________________________________________________________________________________________________g7, 4
+g8____________________________________________________________________________________________________g8, 0
+g9____________________________________________________________________________________________________g9, 4
+h0____________________________________________________________________________________________________h0, 0
+h1____________________________________________________________________________________________________h1, 4
+h2____________________________________________________________________________________________________h2, 0
+h3____________________________________________________________________________________________________h3, 4
+h4____________________________________________________________________________________________________h4, 0
+h5____________________________________________________________________________________________________h5, 4
+h6____________________________________________________________________________________________________h6, 0
+h7____________________________________________________________________________________________________h7, 4
+h8____________________________________________________________________________________________________h8, 0
+h9____________________________________________________________________________________________________h9, 4
+i0____________________________________________________________________________________________________i0, 0
+i1____________________________________________________________________________________________________i1, 4
+i2____________________________________________________________________________________________________i2, 0
+i3____________________________________________________________________________________________________i3, 4
+i4____________________________________________________________________________________________________i4, 0
+i5____________________________________________________________________________________________________i5, 4
+i6____________________________________________________________________________________________________i6, 0
+i7____________________________________________________________________________________________________i7, 4
+i8____________________________________________________________________________________________________i8, 0
+i9____________________________________________________________________________________________________i9, 4
+j0____________________________________________________________________________________________________j0, 0
+j1____________________________________________________________________________________________________j1, 4
+j2____________________________________________________________________________________________________j2, 0
+j3____________________________________________________________________________________________________j3, 4
+j4____________________________________________________________________________________________________j4, 0
+j5____________________________________________________________________________________________________j5, 4
+j6____________________________________________________________________________________________________j6, 0
+j7____________________________________________________________________________________________________j7, 4
+j8____________________________________________________________________________________________________j8, 0
+j9____________________________________________________________________________________________________j9, 4
+k0____________________________________________________________________________________________________k0, 0
+k1____________________________________________________________________________________________________k1, 4
+k2____________________________________________________________________________________________________k2, 0
+k3____________________________________________________________________________________________________k3, 4
+k4____________________________________________________________________________________________________k4, 0
+k5____________________________________________________________________________________________________k5, 4
+k6____________________________________________________________________________________________________k6, 0
+k7____________________________________________________________________________________________________k7, 4
+k8____________________________________________________________________________________________________k8, 0
+k9____________________________________________________________________________________________________k9, 4
+l0____________________________________________________________________________________________________l0, 0
+l1____________________________________________________________________________________________________l1, 4
+l2____________________________________________________________________________________________________l2, 0
+l3____________________________________________________________________________________________________l3, 4
+l4____________________________________________________________________________________________________l4, 0
+l5____________________________________________________________________________________________________l5, 4
+l6____________________________________________________________________________________________________l6, 0
+l7____________________________________________________________________________________________________l7, 4
+l8____________________________________________________________________________________________________l8, 0
+l9____________________________________________________________________________________________________l9, 4
+m0____________________________________________________________________________________________________m0, 0
+m1____________________________________________________________________________________________________m1, 4
+m2____________________________________________________________________________________________________m2, 0
+m3____________________________________________________________________________________________________m3, 4
+m4____________________________________________________________________________________________________m4, 0
+m5____________________________________________________________________________________________________m5, 4
+m6____________________________________________________________________________________________________m6, 0
+m7____________________________________________________________________________________________________m7, 4
+m8____________________________________________________________________________________________________m8, 0
+m9____________________________________________________________________________________________________m9, 4
+n0____________________________________________________________________________________________________n0, 0
+n1____________________________________________________________________________________________________n1, 4
+n2____________________________________________________________________________________________________n2, 0
+n3____________________________________________________________________________________________________n3, 4
+n4____________________________________________________________________________________________________n4, 0
+n5____________________________________________________________________________________________________n5, 4
+n6____________________________________________________________________________________________________n6, 0
+n7____________________________________________________________________________________________________n7, 4
+n8____________________________________________________________________________________________________n8, 0
+n9____________________________________________________________________________________________________n9, 4
+o0____________________________________________________________________________________________________o0, 0
+o1____________________________________________________________________________________________________o1, 4
+o2____________________________________________________________________________________________________o2, 0
+o3____________________________________________________________________________________________________o3, 4
+o4____________________________________________________________________________________________________o4, 0
+o5____________________________________________________________________________________________________o5, 4
+o6____________________________________________________________________________________________________o6, 0
+o7____________________________________________________________________________________________________o7, 4
+o8____________________________________________________________________________________________________o8, 0
+o9____________________________________________________________________________________________________o9, 4
+p0____________________________________________________________________________________________________p0, 0
+p1____________________________________________________________________________________________________p1, 4
+p2____________________________________________________________________________________________________p2, 0
+p3____________________________________________________________________________________________________p3, 4
+p4____________________________________________________________________________________________________p4, 0
+p5____________________________________________________________________________________________________p5, 4
+p6____________________________________________________________________________________________________p6, 0
+p7____________________________________________________________________________________________________p7, 4
+p8____________________________________________________________________________________________________p8, 0
+p9____________________________________________________________________________________________________p9, 4
+q0____________________________________________________________________________________________________q0, 0
+q1____________________________________________________________________________________________________q1, 4
+q2____________________________________________________________________________________________________q2, 0
+q3____________________________________________________________________________________________________q3, 4
+q4____________________________________________________________________________________________________q4, 0
+q5____________________________________________________________________________________________________q5, 4
+q6____________________________________________________________________________________________________q6, 0
+q7____________________________________________________________________________________________________q7, 4
+q8____________________________________________________________________________________________________q8, 0
+q9____________________________________________________________________________________________________q9, 4
+r0____________________________________________________________________________________________________r0, 0
+r1____________________________________________________________________________________________________r1, 4
+r2____________________________________________________________________________________________________r2, 0
+r3____________________________________________________________________________________________________r3, 4
+r4____________________________________________________________________________________________________r4, 0
+r5____________________________________________________________________________________________________r5, 4
+r6____________________________________________________________________________________________________r6, 0
+r7____________________________________________________________________________________________________r7, 4
+r8____________________________________________________________________________________________________r8, 0
+r9____________________________________________________________________________________________________r9, 4
+s0____________________________________________________________________________________________________s0, 0
+s1____________________________________________________________________________________________________s1, 4
+s2____________________________________________________________________________________________________s2, 0
+s3____________________________________________________________________________________________________s3, 4
+s4____________________________________________________________________________________________________s4, 0
+s5____________________________________________________________________________________________________s5, 4
+s6____________________________________________________________________________________________________s6, 0
+s7____________________________________________________________________________________________________s7, 4
+s8____________________________________________________________________________________________________s8, 0
+s9____________________________________________________________________________________________________s9, 4
+t0____________________________________________________________________________________________________t0, 0
+t1____________________________________________________________________________________________________t1, 4
+t2____________________________________________________________________________________________________t2, 0
+t3____________________________________________________________________________________________________t3, 4
+t4____________________________________________________________________________________________________t4, 0
+t5____________________________________________________________________________________________________t5, 4
+t6____________________________________________________________________________________________________t6, 0
+t7____________________________________________________________________________________________________t7, 4
+t8____________________________________________________________________________________________________t8, 0
+t9____________________________________________________________________________________________________t9, 4
+u0____________________________________________________________________________________________________u0, 0
+u1____________________________________________________________________________________________________u1, 4
+u2____________________________________________________________________________________________________u2, 0
+u3____________________________________________________________________________________________________u3, 4
+u4____________________________________________________________________________________________________u4, 0
+u5____________________________________________________________________________________________________u5, 4
+u6____________________________________________________________________________________________________u6, 0
+u7____________________________________________________________________________________________________u7, 4
+u8____________________________________________________________________________________________________u8, 0
+u9____________________________________________________________________________________________________u9, 4
+v0____________________________________________________________________________________________________v0, 0
+v1____________________________________________________________________________________________________v1, 4
+v2____________________________________________________________________________________________________v2, 0
+v3____________________________________________________________________________________________________v3, 4
+v4____________________________________________________________________________________________________v4, 0
+v5____________________________________________________________________________________________________v5, 4
+v6____________________________________________________________________________________________________v6, 0
+v7____________________________________________________________________________________________________v7, 4
+v8____________________________________________________________________________________________________v8, 0
+v9____________________________________________________________________________________________________v9, 4
+w0____________________________________________________________________________________________________w0, 0
+w1____________________________________________________________________________________________________w1, 4
+w2____________________________________________________________________________________________________w2, 0
+w3____________________________________________________________________________________________________w3, 4
+w4____________________________________________________________________________________________________w4, 0
+w5____________________________________________________________________________________________________w5, 4
+w6____________________________________________________________________________________________________w6, 0
+w7____________________________________________________________________________________________________w7, 4
+w8____________________________________________________________________________________________________w8, 0
+w9____________________________________________________________________________________________________w9, 4
+x0____________________________________________________________________________________________________x0, 0
+x1____________________________________________________________________________________________________x1, 4
+x2____________________________________________________________________________________________________x2, 0
+x3____________________________________________________________________________________________________x3, 4
+x4____________________________________________________________________________________________________x4, 0
+x5____________________________________________________________________________________________________x5, 4
+x6____________________________________________________________________________________________________x6, 0
+x7____________________________________________________________________________________________________x7, 4
+x8____________________________________________________________________________________________________x8, 0
+x9____________________________________________________________________________________________________x9, 4
+y0____________________________________________________________________________________________________y0, 0
+y1____________________________________________________________________________________________________y1, 4
+y2____________________________________________________________________________________________________y2, 0
+y3____________________________________________________________________________________________________y3, 4
+y4____________________________________________________________________________________________________y4, 0
+y5____________________________________________________________________________________________________y5, 4
+y6____________________________________________________________________________________________________y6, 0
+y7____________________________________________________________________________________________________y7, 4
+y8____________________________________________________________________________________________________y8, 0
+y9____________________________________________________________________________________________________y9, 4
+z0____________________________________________________________________________________________________z0, 0
+z1____________________________________________________________________________________________________z1, 4
+z2____________________________________________________________________________________________________z2, 0
+z3____________________________________________________________________________________________________z3, 4
+z4____________________________________________________________________________________________________z4, 0
+z5____________________________________________________________________________________________________z5, 4
+z6____________________________________________________________________________________________________z6, 0
+z7____________________________________________________________________________________________________z7, 4
+z8____________________________________________________________________________________________________z8, 0
+z9____________________________________________________________________________________________________z9, 4
+A0____________________________________________________________________________________________________A0, 0
+A1____________________________________________________________________________________________________A1, 4
+A2____________________________________________________________________________________________________A2, 0
+A3____________________________________________________________________________________________________A3, 4
+A4____________________________________________________________________________________________________A4, 0
+A5____________________________________________________________________________________________________A5, 4
+A6____________________________________________________________________________________________________A6, 0
+A7____________________________________________________________________________________________________A7, 4
+A8____________________________________________________________________________________________________A8, 0
+A9____________________________________________________________________________________________________A9, 4
+B0____________________________________________________________________________________________________B0, 0
+B1____________________________________________________________________________________________________B1, 4
+B2____________________________________________________________________________________________________B2, 0
+B3____________________________________________________________________________________________________B3, 4
+B4____________________________________________________________________________________________________B4, 0
+B5____________________________________________________________________________________________________B5, 4
+B6____________________________________________________________________________________________________B6, 0
+B7____________________________________________________________________________________________________B7, 4
+B8____________________________________________________________________________________________________B8, 0
+B9____________________________________________________________________________________________________B9, 4
+C0____________________________________________________________________________________________________C0, 0
+C1____________________________________________________________________________________________________C1, 4
+C2____________________________________________________________________________________________________C2, 0
+C3____________________________________________________________________________________________________C3, 4
+C4____________________________________________________________________________________________________C4, 0
+C5____________________________________________________________________________________________________C5, 4
+C6____________________________________________________________________________________________________C6, 0
+C7____________________________________________________________________________________________________C7, 4
+C8____________________________________________________________________________________________________C8, 0
+C9____________________________________________________________________________________________________C9, 4
+D0____________________________________________________________________________________________________D0, 0
+D1____________________________________________________________________________________________________D1, 4
+D2____________________________________________________________________________________________________D2, 0
+D3____________________________________________________________________________________________________D3, 4
+D4____________________________________________________________________________________________________D4, 0
+D5____________________________________________________________________________________________________D5, 4
+D6____________________________________________________________________________________________________D6, 0
+D7____________________________________________________________________________________________________D7, 4
+D8____________________________________________________________________________________________________D8, 0
+D9____________________________________________________________________________________________________D9, 4
+E0____________________________________________________________________________________________________E0, 0
+E1____________________________________________________________________________________________________E1, 4
+E2____________________________________________________________________________________________________E2, 0
+E3____________________________________________________________________________________________________E3, 4
+E4____________________________________________________________________________________________________E4, 0
+E5____________________________________________________________________________________________________E5, 4
+E6____________________________________________________________________________________________________E6, 0
+E7____________________________________________________________________________________________________E7, 4
+E8____________________________________________________________________________________________________E8, 0
+E9____________________________________________________________________________________________________E9, 4
+F0____________________________________________________________________________________________________F0, 0
+F1____________________________________________________________________________________________________F1, 4
+F2____________________________________________________________________________________________________F2, 0
+F3____________________________________________________________________________________________________F3, 4
+F4____________________________________________________________________________________________________F4, 0
+F5____________________________________________________________________________________________________F5, 4
+F6____________________________________________________________________________________________________F6, 0
+F7____________________________________________________________________________________________________F7, 4
+F8____________________________________________________________________________________________________F8, 0
+F9____________________________________________________________________________________________________F9, 4
+G0____________________________________________________________________________________________________G0, 0
+G1____________________________________________________________________________________________________G1, 4
+G2____________________________________________________________________________________________________G2, 0
+G3____________________________________________________________________________________________________G3, 4
+G4____________________________________________________________________________________________________G4, 0
+G5____________________________________________________________________________________________________G5, 4
+G6____________________________________________________________________________________________________G6, 0
+G7____________________________________________________________________________________________________G7, 4
+G8____________________________________________________________________________________________________G8, 0
+G9____________________________________________________________________________________________________G9, 4
+H0____________________________________________________________________________________________________H0, 0
+H1____________________________________________________________________________________________________H1, 4
+H2____________________________________________________________________________________________________H2, 0
+H3____________________________________________________________________________________________________H3, 4
+H4____________________________________________________________________________________________________H4, 0
+H5____________________________________________________________________________________________________H5, 4
+H6____________________________________________________________________________________________________H6, 0
+H7____________________________________________________________________________________________________H7, 4
+H8____________________________________________________________________________________________________H8, 0
+H9____________________________________________________________________________________________________H9, 4
+I0____________________________________________________________________________________________________I0, 0
+I1____________________________________________________________________________________________________I1, 4
+I2____________________________________________________________________________________________________I2, 0
+I3____________________________________________________________________________________________________I3, 4
+I4____________________________________________________________________________________________________I4, 0
+I5____________________________________________________________________________________________________I5, 4
+I6____________________________________________________________________________________________________I6, 0
+I7____________________________________________________________________________________________________I7, 4
+I8____________________________________________________________________________________________________I8, 0
+I9____________________________________________________________________________________________________I9, 4
+J0____________________________________________________________________________________________________J0, 0
+J1____________________________________________________________________________________________________J1, 4
+J2____________________________________________________________________________________________________J2, 0
+J3____________________________________________________________________________________________________J3, 4
+J4____________________________________________________________________________________________________J4, 0
+J5____________________________________________________________________________________________________J5, 4
+J6____________________________________________________________________________________________________J6, 0
+J7____________________________________________________________________________________________________J7, 4
+J8____________________________________________________________________________________________________J8, 0
+J9____________________________________________________________________________________________________J9, 4
+K0____________________________________________________________________________________________________K0, 0
+K1____________________________________________________________________________________________________K1, 4
+K2____________________________________________________________________________________________________K2, 0
+K3____________________________________________________________________________________________________K3, 4
+K4____________________________________________________________________________________________________K4, 0
+K5____________________________________________________________________________________________________K5, 4
+K6____________________________________________________________________________________________________K6, 0
+K7____________________________________________________________________________________________________K7, 4
+K8____________________________________________________________________________________________________K8, 0
+K9____________________________________________________________________________________________________K9, 4
+L0____________________________________________________________________________________________________L0, 0
+L1____________________________________________________________________________________________________L1, 4
+L2____________________________________________________________________________________________________L2, 0
+L3____________________________________________________________________________________________________L3, 4
+L4____________________________________________________________________________________________________L4, 0
+L5____________________________________________________________________________________________________L5, 4
+L6____________________________________________________________________________________________________L6, 0
+L7____________________________________________________________________________________________________L7, 4
+L8____________________________________________________________________________________________________L8, 0
+L9____________________________________________________________________________________________________L9, 4
+M0____________________________________________________________________________________________________M0, 0
+M1____________________________________________________________________________________________________M1, 4
+M2____________________________________________________________________________________________________M2, 0
+M3____________________________________________________________________________________________________M3, 4
+M4____________________________________________________________________________________________________M4, 0
+M5____________________________________________________________________________________________________M5, 4
+M6____________________________________________________________________________________________________M6, 0
+M7____________________________________________________________________________________________________M7, 4
+M8____________________________________________________________________________________________________M8, 0
+M9____________________________________________________________________________________________________M9, 4
+N0____________________________________________________________________________________________________N0, 0
+N1____________________________________________________________________________________________________N1, 4
+N2____________________________________________________________________________________________________N2, 0
+N3____________________________________________________________________________________________________N3, 4
+N4____________________________________________________________________________________________________N4, 0
+N5____________________________________________________________________________________________________N5, 4
+N6____________________________________________________________________________________________________N6, 0
+N7____________________________________________________________________________________________________N7, 4
+N8____________________________________________________________________________________________________N8, 0
+N9____________________________________________________________________________________________________N9, 4
+O0____________________________________________________________________________________________________O0, 0
+O1____________________________________________________________________________________________________O1, 4
+O2____________________________________________________________________________________________________O2, 0
+O3____________________________________________________________________________________________________O3, 4
+O4____________________________________________________________________________________________________O4, 0
+O5____________________________________________________________________________________________________O5, 4
+O6____________________________________________________________________________________________________O6, 0
+O7____________________________________________________________________________________________________O7, 4
+O8____________________________________________________________________________________________________O8, 0
+O9____________________________________________________________________________________________________O9, 4
+P0____________________________________________________________________________________________________P0, 0
+P1____________________________________________________________________________________________________P1, 4
+P2____________________________________________________________________________________________________P2, 0
+P3____________________________________________________________________________________________________P3, 4
+P4____________________________________________________________________________________________________P4, 0
+P5____________________________________________________________________________________________________P5, 4
+P6____________________________________________________________________________________________________P6, 0
+P7____________________________________________________________________________________________________P7, 4
+P8____________________________________________________________________________________________________P8, 0
+P9____________________________________________________________________________________________________P9, 4
+Q0____________________________________________________________________________________________________Q0, 0
+Q1____________________________________________________________________________________________________Q1, 4
+Q2____________________________________________________________________________________________________Q2, 0
+Q3____________________________________________________________________________________________________Q3, 4
+Q4____________________________________________________________________________________________________Q4, 0
+Q5____________________________________________________________________________________________________Q5, 4
+Q6____________________________________________________________________________________________________Q6, 0
+Q7____________________________________________________________________________________________________Q7, 4
+Q8____________________________________________________________________________________________________Q8, 0
+Q9____________________________________________________________________________________________________Q9, 4
+R0____________________________________________________________________________________________________R0, 0
+R1____________________________________________________________________________________________________R1, 4
+R2____________________________________________________________________________________________________R2, 0
+R3____________________________________________________________________________________________________R3, 4
+R4____________________________________________________________________________________________________R4, 0
+R5____________________________________________________________________________________________________R5, 4
+R6____________________________________________________________________________________________________R6, 0
+R7____________________________________________________________________________________________________R7, 4
+R8____________________________________________________________________________________________________R8, 0
+R9____________________________________________________________________________________________________R9, 4
+S0____________________________________________________________________________________________________S0, 0
+S1____________________________________________________________________________________________________S1, 4
+S2____________________________________________________________________________________________________S2, 0
+S3____________________________________________________________________________________________________S3, 4
+S4____________________________________________________________________________________________________S4, 0
+S5____________________________________________________________________________________________________S5, 4
+S6____________________________________________________________________________________________________S6, 0
+S7____________________________________________________________________________________________________S7, 4
+S8____________________________________________________________________________________________________S8, 0
+S9____________________________________________________________________________________________________S9, 4
+T0____________________________________________________________________________________________________T0, 0
+T1____________________________________________________________________________________________________T1, 4
+T2____________________________________________________________________________________________________T2, 0
+T3____________________________________________________________________________________________________T3, 4
+T4____________________________________________________________________________________________________T4, 0
+T5____________________________________________________________________________________________________T5, 4
+T6____________________________________________________________________________________________________T6, 0
+T7____________________________________________________________________________________________________T7, 4
+T8____________________________________________________________________________________________________T8, 0
+T9____________________________________________________________________________________________________T9, 4
+U0____________________________________________________________________________________________________U0, 0
+U1____________________________________________________________________________________________________U1, 4
+U2____________________________________________________________________________________________________U2, 0
+U3____________________________________________________________________________________________________U3, 4
+U4____________________________________________________________________________________________________U4, 0
+U5____________________________________________________________________________________________________U5, 4
+U6____________________________________________________________________________________________________U6, 0
+U7____________________________________________________________________________________________________U7, 4
+U8____________________________________________________________________________________________________U8, 0
+U9____________________________________________________________________________________________________U9, 4
+V0____________________________________________________________________________________________________V0, 0
+V1____________________________________________________________________________________________________V1, 4
+V2____________________________________________________________________________________________________V2, 0
+V3____________________________________________________________________________________________________V3, 4
+V4____________________________________________________________________________________________________V4, 0
+V5____________________________________________________________________________________________________V5, 4
+V6____________________________________________________________________________________________________V6, 0
+V7____________________________________________________________________________________________________V7, 4
+V8____________________________________________________________________________________________________V8, 0
+V9____________________________________________________________________________________________________V9, 4
+W0____________________________________________________________________________________________________W0, 0
+W1____________________________________________________________________________________________________W1, 4
+W2____________________________________________________________________________________________________W2, 0
+W3____________________________________________________________________________________________________W3, 4
+W4____________________________________________________________________________________________________W4, 0
+W5____________________________________________________________________________________________________W5, 4
+W6____________________________________________________________________________________________________W6, 0
+W7____________________________________________________________________________________________________W7, 4
+W8____________________________________________________________________________________________________W8, 0
+W9____________________________________________________________________________________________________W9, 4
+X0____________________________________________________________________________________________________X0, 0
+X1____________________________________________________________________________________________________X1, 4
+X2____________________________________________________________________________________________________X2, 0
+X3____________________________________________________________________________________________________X3, 4
+X4____________________________________________________________________________________________________X4, 0
+X5____________________________________________________________________________________________________X5, 4
+X6____________________________________________________________________________________________________X6, 0
+X7____________________________________________________________________________________________________X7, 4
+X8____________________________________________________________________________________________________X8, 0
+X9____________________________________________________________________________________________________X9, 4
+Y0____________________________________________________________________________________________________Y0, 0
+Y1____________________________________________________________________________________________________Y1, 4
+Y2____________________________________________________________________________________________________Y2, 0
+Y3____________________________________________________________________________________________________Y3, 4
+Y4____________________________________________________________________________________________________Y4, 0
+Y5____________________________________________________________________________________________________Y5, 4
+Y6____________________________________________________________________________________________________Y6, 0
+Y7____________________________________________________________________________________________________Y7, 4
+Y8____________________________________________________________________________________________________Y8, 0
+Y9____________________________________________________________________________________________________Y9, 4
+Z0____________________________________________________________________________________________________Z0, 0
+Z1____________________________________________________________________________________________________Z1, 4
+Z2____________________________________________________________________________________________________Z2, 0
+Z3____________________________________________________________________________________________________Z3, 4
+Z4____________________________________________________________________________________________________Z4, 0
+Z5____________________________________________________________________________________________________Z5, 4
+Z6____________________________________________________________________________________________________Z6, 0
+Z7____________________________________________________________________________________________________Z7, 4
+Z8____________________________________________________________________________________________________Z8, 0
+Z9____________________________________________________________________________________________________Z9, 4
+%%
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf
new file mode 100644
index 00000000000..ea7e604acfb
--- /dev/null
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf
@@ -0,0 +1,9 @@
+This file is for testing joined prefixes
+%%
+ai, 0
+bj, 4
+aak, 0
+bbl, 4
+aaaam, 0
+bbbbn, 0
+%%
diff --git a/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf
new file mode 100644
index 00000000000..667d798c617
--- /dev/null
+++ b/chromium/net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf
@@ -0,0 +1,9 @@
+This file is for testing joined suffixes
+%%
+ia, 0
+jb, 4
+kaa, 0
+lbb, 4
+maaaa, 0
+nbbbb, 0
+%%
diff --git a/chromium/net/base/registry_controlled_domains/registry_controlled_domain.cc b/chromium/net/base/registry_controlled_domains/registry_controlled_domain.cc
index 56d5ed9d6a1..e5b8e4cb8a1 100644
--- a/chromium/net/base/registry_controlled_domains/registry_controlled_domain.cc
+++ b/chromium/net/base/registry_controlled_domains/registry_controlled_domain.cc
@@ -53,26 +53,151 @@
#include "url/gurl.h"
#include "url/url_parse.h"
-#include "effective_tld_names.cc"
-
namespace net {
namespace registry_controlled_domains {
namespace {
+#include "net/base/registry_controlled_domains/effective_tld_names-inc.cc"
+
+// See make_dafsa.py for documentation of the generated dafsa byte array.
+const unsigned char* g_graph = kDafsa;
+size_t g_graph_length = sizeof(kDafsa);
+
+const int kNotFound = -1;
const int kExceptionRule = 1;
const int kWildcardRule = 2;
const int kPrivateRule = 4;
-const FindDomainPtr kDefaultFindDomainFunction = Perfect_Hash::FindDomain;
+// Read next offset from pos.
+// Returns true if an offset could be read, false otherwise.
+bool GetNextOffset(const unsigned char** pos, const unsigned char* end,
+ const unsigned char** offset) {
+ if (*pos == end)
+ return false;
+
+ // When reading an offset the byte array must always contain at least
+ // three more bytes to consume. First the offset to read, then a node
+ // to skip over and finally a destination node. No object can be smaller
+ // than one byte.
+ CHECK_LT(*pos + 2, end);
+ size_t bytes_consumed;
+ switch (**pos & 0x60) {
+ case 0x60: // Read three byte offset
+ *offset += (((*pos)[0] & 0x1F) << 16) | ((*pos)[1] << 8) | (*pos)[2];
+ bytes_consumed = 3;
+ break;
+ case 0x40: // Read two byte offset
+ *offset += (((*pos)[0] & 0x1F) << 8) | (*pos)[1];
+ bytes_consumed = 2;
+ break;
+ default:
+ *offset += (*pos)[0] & 0x3F;
+ bytes_consumed = 1;
+ }
+ if ((**pos & 0x80) != 0) {
+ *pos = end;
+ } else {
+ *pos += bytes_consumed;
+ }
+ return true;
+}
+
+// Check if byte at offset is last in label.
+bool IsEOL(const unsigned char* offset, const unsigned char* end) {
+ CHECK_LT(offset, end);
+ return (*offset & 0x80) != 0;
+}
+
+// Check if byte at offset matches first character in key.
+// This version matches characters not last in label.
+bool IsMatch(const unsigned char* offset, const unsigned char* end,
+ const char* key) {
+ CHECK_LT(offset, end);
+ return *offset == *key;
+}
+
+// Check if byte at offset matches first character in key.
+// This version matches characters last in label.
+bool IsEndCharMatch(const unsigned char* offset, const unsigned char* end,
+ const char* key) {
+ CHECK_LT(offset, end);
+ return *offset == (*key | 0x80);
+}
-// 'stringpool' is defined as a macro by the gperf-generated
-// "effective_tld_names.cc". Provide a real constant value for it instead.
-const char* const kDefaultStringPool = stringpool;
-#undef stringpool
+// Read return value at offset.
+// Returns true if a return value could be read, false otherwise.
+bool GetReturnValue(const unsigned char* offset, const unsigned char* end,
+ int* return_value) {
+ CHECK_LT(offset, end);
+ if ((*offset & 0xE0) == 0x80) {
+ *return_value = *offset & 0x0F;
+ return true;
+ }
+ return false;
+}
-FindDomainPtr g_find_domain_function = kDefaultFindDomainFunction;
-const char* g_stringpool = kDefaultStringPool;
+// Lookup a domain key in a byte array generated by make_dafsa.py.
+// The rule type is returned if key is found, otherwise kNotFound is returned.
+int LookupString(const unsigned char* graph, size_t length, const char* key,
+ size_t key_length) {
+ const unsigned char* pos = graph;
+ const unsigned char* end = graph + length;
+ const unsigned char* offset = pos;
+ const char* key_end = key + key_length;
+ while (GetNextOffset(&pos, end, &offset)) {
+ // char <char>+ end_char offsets
+ // char <char>+ return value
+ // char end_char offsets
+ // char return value
+ // end_char offsets
+ // return_value
+ bool did_consume = false;
+ if (key != key_end && !IsEOL(offset, end)) {
+ // Leading <char> is not a match. Don't dive into this child
+ if (!IsMatch(offset, end, key))
+ continue;
+ did_consume = true;
+ ++offset;
+ ++key;
+ // Possible matches at this point:
+ // <char>+ end_char offsets
+ // <char>+ return value
+ // end_char offsets
+ // return value
+ // Remove all remaining <char> nodes possible
+ while (!IsEOL(offset, end) && key != key_end) {
+ if (!IsMatch(offset, end, key))
+ return kNotFound;
+ ++key;
+ ++offset;
+ }
+ }
+ // Possible matches at this point:
+ // end_char offsets
+ // return_value
+ // If one or more <char> elements were consumed, a failure
+ // to match is terminal. Otherwise, try the next node.
+ if (key == key_end) {
+ int return_value;
+ if (GetReturnValue(offset, end, &return_value))
+ return return_value;
+ // The DAFSA guarantees that if the first char is a match, all
+ // remaining char elements MUST match if the key is truly present.
+ if (did_consume)
+ return kNotFound;
+ continue;
+ }
+ if (!IsEndCharMatch(offset, end, key)) {
+ if (did_consume)
+ return kNotFound; // Unexpected
+ continue;
+ }
+ ++key;
+ pos = ++offset; // Dive into child
+ }
+ return kNotFound; // No match
+}
size_t GetRegistryLengthImpl(
const std::string& host,
@@ -105,46 +230,40 @@ size_t GetRegistryLengthImpl(
return 0; // This can't have a registry + domain.
while (1) {
const char* domain_str = host.data() + curr_start;
- int domain_length = host_check_len - curr_start;
- const DomainRule* rule = g_find_domain_function(domain_str, domain_length);
-
- // We need to compare the string after finding a match because the
- // no-collisions of perfect hashing only refers to items in the set. Since
- // we're searching for arbitrary domains, there could be collisions.
- // Furthermore, if the apparent match is a private registry and we're not
- // including those, it can't be an actual match.
- if (rule) {
- bool do_check = !(rule->type & kPrivateRule) ||
- private_filter == INCLUDE_PRIVATE_REGISTRIES;
- if (do_check && base::strncasecmp(domain_str,
- g_stringpool + rule->name_offset,
- domain_length) == 0) {
- // Exception rules override wildcard rules when the domain is an exact
- // match, but wildcards take precedence when there's a subdomain.
- if (rule->type & kWildcardRule && (prev_start != std::string::npos)) {
- // If prev_start == host_check_begin, then the host is the registry
- // itself, so return 0.
- return (prev_start == host_check_begin) ?
- 0 : (host.length() - prev_start);
- }
+ size_t domain_length = host_check_len - curr_start;
+ int type = LookupString(g_graph, g_graph_length, domain_str, domain_length);
+ bool do_check =
+ type != kNotFound && (!(type & kPrivateRule) ||
+ private_filter == INCLUDE_PRIVATE_REGISTRIES);
+
+ // If the apparent match is a private registry and we're not including
+ // those, it can't be an actual match.
+ if (do_check) {
+ // Exception rules override wildcard rules when the domain is an exact
+ // match, but wildcards take precedence when there's a subdomain.
+ if (type & kWildcardRule && (prev_start != std::string::npos)) {
+ // If prev_start == host_check_begin, then the host is the registry
+ // itself, so return 0.
+ return (prev_start == host_check_begin) ? 0
+ : (host.length() - prev_start);
+ }
- if (rule->type & kExceptionRule) {
- if (next_dot == std::string::npos) {
- // If we get here, we had an exception rule with no dots (e.g.
- // "!foo"). This would only be valid if we had a corresponding
- // wildcard rule, which would have to be "*". But we explicitly
- // disallow that case, so this kind of rule is invalid.
- NOTREACHED() << "Invalid exception rule";
- return 0;
- }
- return host.length() - next_dot - 1;
+ if (type & kExceptionRule) {
+ if (next_dot == std::string::npos) {
+ // If we get here, we had an exception rule with no dots (e.g.
+ // "!foo"). This would only be valid if we had a corresponding
+ // wildcard rule, which would have to be "*". But we explicitly
+ // disallow that case, so this kind of rule is invalid.
+ NOTREACHED() << "Invalid exception rule";
+ return 0;
}
-
- // If curr_start == host_check_begin, then the host is the registry
- // itself, so return 0.
- return (curr_start == host_check_begin) ?
- 0 : (host.length() - curr_start);
+ return host.length() - next_dot - 1;
}
+
+ // If curr_start == host_check_begin, then the host is the registry
+ // itself, so return 0.
+ return (curr_start == host_check_begin) ? 0
+ : (host.length() - curr_start);
}
if (next_dot >= host_check_len) // Catches std::string::npos as well.
@@ -194,8 +313,7 @@ std::string GetDomainAndRegistryImpl(
std::string GetDomainAndRegistry(
const GURL& gurl,
PrivateRegistryFilter filter) {
- const url_parse::Component host =
- gurl.parsed_for_possibly_invalid_spec().host;
+ const url::Component host = gurl.parsed_for_possibly_invalid_spec().host;
if ((host.len <= 0) || gurl.HostIsIPAddress())
return std::string();
return GetDomainAndRegistryImpl(std::string(
@@ -205,7 +323,7 @@ std::string GetDomainAndRegistry(
std::string GetDomainAndRegistry(
const std::string& host,
PrivateRegistryFilter filter) {
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
const std::string canon_host(CanonicalizeHost(host, &host_info));
if (canon_host.empty() || host_info.IsIPAddress())
return std::string();
@@ -224,10 +342,8 @@ bool SameDomainOrHost(
return domain1 == domain2;
// No domains. See if the hosts are identical.
- const url_parse::Component host1 =
- gurl1.parsed_for_possibly_invalid_spec().host;
- const url_parse::Component host2 =
- gurl2.parsed_for_possibly_invalid_spec().host;
+ const url::Component host1 = gurl1.parsed_for_possibly_invalid_spec().host;
+ const url::Component host2 = gurl2.parsed_for_possibly_invalid_spec().host;
if ((host1.len <= 0) || (host1.len != host2.len))
return false;
return !strncmp(gurl1.possibly_invalid_spec().data() + host1.begin,
@@ -239,8 +355,7 @@ size_t GetRegistryLength(
const GURL& gurl,
UnknownRegistryFilter unknown_filter,
PrivateRegistryFilter private_filter) {
- const url_parse::Component host =
- gurl.parsed_for_possibly_invalid_spec().host;
+ const url::Component host = gurl.parsed_for_possibly_invalid_spec().host;
if (host.len <= 0)
return std::string::npos;
if (gurl.HostIsIPAddress())
@@ -255,7 +370,7 @@ size_t GetRegistryLength(
const std::string& host,
UnknownRegistryFilter unknown_filter,
PrivateRegistryFilter private_filter) {
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
const std::string canon_host(CanonicalizeHost(host, &host_info));
if (canon_host.empty())
return std::string::npos;
@@ -264,10 +379,16 @@ size_t GetRegistryLength(
return GetRegistryLengthImpl(canon_host, unknown_filter, private_filter);
}
-void SetFindDomainFunctionAndStringPoolForTesting(FindDomainPtr function,
- const char* stringpool) {
- g_find_domain_function = function ? function : kDefaultFindDomainFunction;
- g_stringpool = stringpool ? stringpool : kDefaultStringPool;
+void SetFindDomainGraph() {
+ g_graph = kDafsa;
+ g_graph_length = sizeof(kDafsa);
+}
+
+void SetFindDomainGraph(const unsigned char* domains, size_t length) {
+ CHECK(domains);
+ CHECK_NE(length, 0u);
+ g_graph = domains;
+ g_graph_length = length;
}
} // namespace registry_controlled_domains
diff --git a/chromium/net/base/registry_controlled_domains/registry_controlled_domain.h b/chromium/net/base/registry_controlled_domains/registry_controlled_domain.h
index b30f55ea6c2..639f264047b 100644
--- a/chromium/net/base/registry_controlled_domains/registry_controlled_domain.h
+++ b/chromium/net/base/registry_controlled_domains/registry_controlled_domain.h
@@ -226,11 +226,12 @@ NET_EXPORT size_t GetRegistryLength(const std::string& host,
typedef const struct DomainRule* (*FindDomainPtr)(const char *, unsigned int);
-// Used for unit tests, so that a different perfect hash map from the full
-// list is used. Set to NULL to use the Default function.
-NET_EXPORT_PRIVATE void SetFindDomainFunctionAndStringPoolForTesting(
- FindDomainPtr fn, const char* stringpool);
+// Used for unit tests. Use default domains.
+NET_EXPORT_PRIVATE void SetFindDomainGraph();
+// Used for unit tests, so that a frozen list of domains is used.
+NET_EXPORT_PRIVATE void SetFindDomainGraph(const unsigned char* domains,
+ size_t length);
} // namespace registry_controlled_domains
} // namespace net
diff --git a/chromium/net/base/registry_controlled_domains/registry_controlled_domain_unittest.cc b/chromium/net/base/registry_controlled_domains/registry_controlled_domain_unittest.cc
index d1d96f9caa4..7a4a5283a81 100644
--- a/chromium/net/base/registry_controlled_domains/registry_controlled_domain_unittest.cc
+++ b/chromium/net/base/registry_controlled_domains/registry_controlled_domain_unittest.cc
@@ -6,15 +6,26 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-#include "effective_tld_names_unittest1.cc"
-static const char* const Perfect_Hash_Test1_stringpool = stringpool1;
-#undef TOTAL_KEYWORDS
-#undef MIN_WORD_LENGTH
-#undef MAX_WORD_LENGTH
-#undef MIN_HASH_VALUE
-#undef MAX_HASH_VALUE
-#include "effective_tld_names_unittest2.cc"
-static const char* const Perfect_Hash_Test2_stringpool = stringpool2;
+namespace {
+namespace test1 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest1-inc.cc"
+}
+namespace test2 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest2-inc.cc"
+}
+namespace test3 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest3-inc.cc"
+}
+namespace test4 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest4-inc.cc"
+}
+namespace test5 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest5-inc.cc"
+}
+namespace test6 {
+#include "net/base/registry_controlled_domains/effective_tld_names_unittest6-inc.cc"
+}
+} // namespace
namespace net {
namespace registry_controlled_domains {
@@ -50,6 +61,12 @@ size_t GetRegistryLengthFromHost(
return GetRegistryLength(host, unknown_filter, EXCLUDE_PRIVATE_REGISTRIES);
}
+size_t GetRegistryLengthFromHostIncludingPrivate(
+ const std::string& host,
+ UnknownRegistryFilter unknown_filter) {
+ return GetRegistryLength(host, unknown_filter, INCLUDE_PRIVATE_REGISTRIES);
+}
+
bool CompareDomains(const std::string& url1, const std::string& url2) {
GURL g1 = GURL(url1);
GURL g2 = GURL(url2);
@@ -60,17 +77,16 @@ bool CompareDomains(const std::string& url1, const std::string& url2) {
class RegistryControlledDomainTest : public testing::Test {
protected:
- void UseDomainData(FindDomainPtr function, const char* const stringpool) {
- SetFindDomainFunctionAndStringPoolForTesting(function, stringpool);
+ template <typename Graph>
+ void UseDomainData(const Graph& graph) {
+ SetFindDomainGraph(graph, sizeof(Graph));
}
- virtual void TearDown() {
- SetFindDomainFunctionAndStringPoolForTesting(NULL, NULL);
- }
+ virtual void TearDown() { SetFindDomainGraph(); }
};
TEST_F(RegistryControlledDomainTest, TestGetDomainAndRegistry) {
- UseDomainData(Perfect_Hash_Test1::FindDomain, Perfect_Hash_Test1_stringpool);
+ UseDomainData(test1::kDafsa);
// Test GURL version of GetDomainAndRegistry().
EXPECT_EQ("baz.jp", GetDomainFromURL("http://a.baz.jp/file.html")); // 1
@@ -129,7 +145,7 @@ TEST_F(RegistryControlledDomainTest, TestGetDomainAndRegistry) {
}
TEST_F(RegistryControlledDomainTest, TestGetRegistryLength) {
- UseDomainData(Perfect_Hash_Test1::FindDomain, Perfect_Hash_Test1_stringpool);
+ UseDomainData(test1::kDafsa);
// Test GURL version of GetRegistryLength().
EXPECT_EQ(2U, GetRegistryLengthFromURL("http://a.baz.jp/file.html",
@@ -248,7 +264,7 @@ TEST_F(RegistryControlledDomainTest, TestGetRegistryLength) {
}
TEST_F(RegistryControlledDomainTest, TestSameDomainOrHost) {
- UseDomainData(Perfect_Hash_Test2::FindDomain, Perfect_Hash_Test2_stringpool);
+ UseDomainData(test2::kDafsa);
EXPECT_TRUE(CompareDomains("http://a.b.bar.jp/file.html",
"http://a.b.bar.jp/file.html")); // b.bar.jp
@@ -288,14 +304,14 @@ TEST_F(RegistryControlledDomainTest, TestDefaultData) {
EXCLUDE_UNKNOWN_REGISTRIES));
EXPECT_EQ(3U, GetRegistryLengthFromURL("http://ferretcentral.org",
EXCLUDE_UNKNOWN_REGISTRIES));
- EXPECT_EQ(0U, GetRegistryLengthFromURL("http://nowhere.foo",
+ EXPECT_EQ(0U, GetRegistryLengthFromURL("http://nowhere.notavaliddomain",
EXCLUDE_UNKNOWN_REGISTRIES));
- EXPECT_EQ(3U, GetRegistryLengthFromURL("http://nowhere.foo",
+ EXPECT_EQ(15U, GetRegistryLengthFromURL("http://nowhere.notavaliddomain",
INCLUDE_UNKNOWN_REGISTRIES));
}
TEST_F(RegistryControlledDomainTest, TestPrivateRegistryHandling) {
- UseDomainData(Perfect_Hash_Test1::FindDomain, Perfect_Hash_Test1_stringpool);
+ UseDomainData(test1::kDafsa);
// Testing the same dataset for INCLUDE_PRIVATE_REGISTRIES and
// EXCLUDE_PRIVATE_REGISTRIES arguments.
@@ -347,6 +363,138 @@ TEST_F(RegistryControlledDomainTest, TestPrivateRegistryHandling) {
INCLUDE_UNKNOWN_REGISTRIES));
}
+TEST_F(RegistryControlledDomainTest, TestDafsaTwoByteOffsets) {
+ UseDomainData(test3::kDafsa);
+
+ // Testing to lookup keys in a DAFSA with two byte offsets.
+ // This DAFSA is constructed so that labels begin and end with unique
+ // characters, which makes it impossible to merge labels. Each inner node
+ // is about 100 bytes and a one byte offset can at most add 64 bytes to
+ // previous offset. Thus the paths must go over two byte offsets.
+
+ const char* key0 =
+ "a.b.6____________________________________________________"
+ "________________________________________________6";
+ const char* key1 =
+ "a.b.7____________________________________________________"
+ "________________________________________________7";
+ const char* key2 =
+ "a.b.a____________________________________________________"
+ "________________________________________________8";
+
+ EXPECT_EQ(102U, GetRegistryLengthFromHost(key0, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(102U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key2, EXCLUDE_UNKNOWN_REGISTRIES));
+}
+
+TEST_F(RegistryControlledDomainTest, TestDafsaThreeByteOffsets) {
+ UseDomainData(test4::kDafsa);
+
+ // Testing to lookup keys in a DAFSA with three byte offsets.
+ // This DAFSA is constructed so that labels begin and end with unique
+ // characters, which makes it impossible to merge labels. The byte array
+ // has a size of ~54k. A two byte offset can add at most add 8k to the
+ // previous offset. Since we can skip only forward in memory, the nodes
+ // representing the return values must be located near the end of the byte
+ // array. The probability that we can reach from an arbitrary inner node to
+ // a return value without using a three byte offset is small (but not zero).
+ // The test is repeated with some different keys and with a reasonable
+ // probability at least one of the tested paths has go over a three byte
+ // offset.
+ const char* key0 =
+ "a.b.Z6___________________________________________________"
+ "_________________________________________________Z6";
+ const char* key1 =
+ "a.b.Z7___________________________________________________"
+ "_________________________________________________Z7";
+ const char* key2 =
+ "a.b.Za___________________________________________________"
+ "_________________________________________________Z8";
+
+ EXPECT_EQ(104U, GetRegistryLengthFromHost(key0, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(104U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key2, EXCLUDE_UNKNOWN_REGISTRIES));
+}
+
+TEST_F(RegistryControlledDomainTest, TestDafsaJoinedPrefixes) {
+ UseDomainData(test5::kDafsa);
+
+ // Testing to lookup keys in a DAFSA with compressed prefixes.
+ // This DAFSA is constructed from words with similar prefixes but distinct
+ // suffixes. The DAFSA will then form a trie with the implicit source node
+ // as root.
+
+ const char* key0 = "a.b.ai";
+ const char* key1 = "a.b.bj";
+ const char* key2 = "a.b.aak";
+ const char* key3 = "a.b.bbl";
+ const char* key4 = "a.b.aaa";
+ const char* key5 = "a.b.bbb";
+ const char* key6 = "a.b.aaaam";
+ const char* key7 = "a.b.bbbbn";
+
+ EXPECT_EQ(2U, GetRegistryLengthFromHost(key0, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(2U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(3U, GetRegistryLengthFromHost(key2, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key3, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(3U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key3, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key4, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key5, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(5U, GetRegistryLengthFromHost(key6, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(5U, GetRegistryLengthFromHost(key7, EXCLUDE_UNKNOWN_REGISTRIES));
+}
+
+TEST_F(RegistryControlledDomainTest, TestDafsaJoinedSuffixes) {
+ UseDomainData(test6::kDafsa);
+
+ // Testing to lookup keys in a DAFSA with compressed suffixes.
+ // This DAFSA is constructed from words with similar suffixes but distinct
+ // prefixes. The DAFSA will then form a trie with the implicit sink node as
+ // root.
+
+ const char* key0 = "a.b.ia";
+ const char* key1 = "a.b.jb";
+ const char* key2 = "a.b.kaa";
+ const char* key3 = "a.b.lbb";
+ const char* key4 = "a.b.aaa";
+ const char* key5 = "a.b.bbb";
+ const char* key6 = "a.b.maaaa";
+ const char* key7 = "a.b.nbbbb";
+
+ EXPECT_EQ(2U, GetRegistryLengthFromHost(key0, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(2U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key1, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(3U, GetRegistryLengthFromHost(key2, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U, GetRegistryLengthFromHost(key3, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(3U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key3, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key4, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(0U,
+ GetRegistryLengthFromHostIncludingPrivate(
+ key5, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(5U, GetRegistryLengthFromHost(key6, EXCLUDE_UNKNOWN_REGISTRIES));
+ EXPECT_EQ(5U, GetRegistryLengthFromHost(key7, EXCLUDE_UNKNOWN_REGISTRIES));
+}
} // namespace registry_controlled_domains
} // namespace net
diff --git a/chromium/net/base/request_priority.h b/chromium/net/base/request_priority.h
index f6ff43e3655..e663610fff4 100644
--- a/chromium/net/base/request_priority.h
+++ b/chromium/net/base/request_priority.h
@@ -5,6 +5,8 @@
#ifndef NET_BASE_REQUEST_PRIORITY_H_
#define NET_BASE_REQUEST_PRIORITY_H_
+#include "net/base/net_export.h"
+
namespace net {
// Prioritization used in various parts of the networking code such
@@ -27,7 +29,7 @@ enum RequestPrioritySize {
NUM_PRIORITIES = MAXIMUM_PRIORITY + 1,
};
-const char* RequestPriorityToString(RequestPriority priority);
+NET_EXPORT const char* RequestPriorityToString(RequestPriority priority);
} // namespace net
diff --git a/chromium/net/base/sdch_dictionary_fetcher.cc b/chromium/net/base/sdch_dictionary_fetcher.cc
new file mode 100644
index 00000000000..aa2061eba8e
--- /dev/null
+++ b/chromium/net/base/sdch_dictionary_fetcher.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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/base/sdch_dictionary_fetcher.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+
+namespace net {
+
+SdchDictionaryFetcher::SdchDictionaryFetcher(
+ SdchManager* manager,
+ URLRequestContextGetter* context)
+ : manager_(manager),
+ weak_factory_(this),
+ task_is_pending_(false),
+ context_(context) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(manager);
+}
+
+SdchDictionaryFetcher::~SdchDictionaryFetcher() {
+ DCHECK(CalledOnValidThread());
+}
+
+void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url) {
+ DCHECK(CalledOnValidThread());
+
+ // Avoid pushing duplicate copy onto queue. We may fetch this url again later
+ // and get a different dictionary, but there is no reason to have it in the
+ // queue twice at one time.
+ if (!fetch_queue_.empty() && fetch_queue_.back() == dictionary_url) {
+ SdchManager::SdchErrorRecovery(
+ SdchManager::DICTIONARY_ALREADY_SCHEDULED_TO_DOWNLOAD);
+ return;
+ }
+ if (attempted_load_.find(dictionary_url) != attempted_load_.end()) {
+ SdchManager::SdchErrorRecovery(
+ SdchManager::DICTIONARY_ALREADY_TRIED_TO_DOWNLOAD);
+ return;
+ }
+ attempted_load_.insert(dictionary_url);
+ fetch_queue_.push(dictionary_url);
+ ScheduleDelayedRun();
+}
+
+void SdchDictionaryFetcher::Cancel() {
+ DCHECK(CalledOnValidThread());
+
+ while (!fetch_queue_.empty())
+ fetch_queue_.pop();
+ attempted_load_.clear();
+ weak_factory_.InvalidateWeakPtrs();
+ current_fetch_.reset(NULL);
+}
+
+void SdchDictionaryFetcher::ScheduleDelayedRun() {
+ if (fetch_queue_.empty() || current_fetch_.get() || task_is_pending_)
+ return;
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&SdchDictionaryFetcher::StartFetching,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(kMsDelayFromRequestTillDownload));
+ task_is_pending_ = true;
+}
+
+void SdchDictionaryFetcher::StartFetching() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(task_is_pending_);
+ task_is_pending_ = false;
+
+ // Handle losing the race against Cancel().
+ if (fetch_queue_.empty())
+ return;
+
+ DCHECK(context_.get());
+ current_fetch_.reset(URLFetcher::Create(
+ fetch_queue_.front(), URLFetcher::GET, this));
+ fetch_queue_.pop();
+ current_fetch_->SetRequestContext(context_.get());
+ current_fetch_->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES |
+ LOAD_DO_NOT_SAVE_COOKIES);
+ current_fetch_->Start();
+}
+
+void SdchDictionaryFetcher::OnURLFetchComplete(
+ const URLFetcher* source) {
+ DCHECK(CalledOnValidThread());
+ if ((200 == source->GetResponseCode()) &&
+ (source->GetStatus().status() == URLRequestStatus::SUCCESS)) {
+ std::string data;
+ source->GetResponseAsString(&data);
+ manager_->AddSdchDictionary(data, source->GetURL());
+ }
+ current_fetch_.reset(NULL);
+ ScheduleDelayedRun();
+}
+
+} // namespace net
diff --git a/chromium/net/base/sdch_dictionary_fetcher.h b/chromium/net/base/sdch_dictionary_fetcher.h
new file mode 100644
index 00000000000..b657b8f80c3
--- /dev/null
+++ b/chromium/net/base/sdch_dictionary_fetcher.h
@@ -0,0 +1,99 @@
+// Copyright 2014 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.
+
+// Support modularity by calling to load a new SDCH filter dictionary.
+// Note that this sort of calling can't be done in the /net directory, as it has
+// no concept of the HTTP cache (which is only visible at the browser level).
+
+#ifndef NET_BASE_SDCH_DICTIONARY_FETCHER_H_
+#define NET_BASE_SDCH_DICTIONARY_FETCHER_H_
+
+#include <queue>
+#include <set>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/base/sdch_manager.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace net {
+
+class URLFetcher;
+class URLRequestContextGetter;
+
+class NET_EXPORT SdchDictionaryFetcher
+ : public URLFetcherDelegate,
+ public SdchFetcher,
+ public base::NonThreadSafe {
+ public:
+ // Consumer must guarantee that the SdchManager pointer outlives
+ // this object. The current implementation guarantees this by
+ // the SdchManager owning this object.
+ SdchDictionaryFetcher(SdchManager* manager,
+ URLRequestContextGetter* context);
+ virtual ~SdchDictionaryFetcher();
+
+ // Implementation of SdchFetcher class.
+ virtual void Schedule(const GURL& dictionary_url) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ private:
+ // Delay in ms between Schedule and actual download.
+ // This leaves the URL in a queue, which is de-duped, so that there is less
+ // chance we'll try to load the same URL multiple times when a pile of
+ // page subresources (or tabs opened in parallel) all suggest the dictionary.
+ static const int kMsDelayFromRequestTillDownload = 100;
+
+ // Ensure the download after the above delay.
+ void ScheduleDelayedRun();
+
+ // Make sure we're processing (or waiting for) the the arrival of the next URL
+ // in the |fetch_queue_|.
+ void StartFetching();
+
+ // Implementation of URLFetcherDelegate. Called after transmission
+ // completes (either successfully or with failure).
+ virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE;
+
+ SdchManager* const manager_;
+
+ // A queue of URLs that are being used to download dictionaries.
+ std::queue<GURL> fetch_queue_;
+ // The currently outstanding URL fetch of a dicitonary.
+ // If this is null, then there is no outstanding request.
+ scoped_ptr<URLFetcher> current_fetch_;
+
+ // Always spread out the dictionary fetches, so that they don't steal
+ // bandwidth from the actual page load. Create delayed tasks to spread out
+ // the download.
+ base::WeakPtrFactory<SdchDictionaryFetcher> weak_factory_;
+ bool task_is_pending_;
+
+ // Althought the SDCH spec does not preclude a server from using a single URL
+ // to load several distinct dictionaries (by telling a client to load a
+ // dictionary from an URL several times), current implementations seem to have
+ // that 1-1 relationship (i.e., each URL points at a single dictionary, and
+ // the dictionary content does not change over time, and hence is not worth
+ // trying to load more than once). In addition, some dictionaries prove
+ // unloadable only after downloading them (because they are too large? ...or
+ // malformed?). As a protective element, Chromium will *only* load a
+ // dictionary at most once from a given URL (so that it doesn't waste
+ // bandwidth trying repeatedly).
+ // The following set lists all the dictionary URLs that we've tried to load,
+ // so that we won't try to load from an URL more than once.
+ // TODO(jar): Try to augment the SDCH proposal to include this restiction.
+ std::set<GURL> attempted_load_;
+
+ // Store the system_url_request_context_getter to use it when we start
+ // fetching.
+ scoped_refptr<URLRequestContextGetter> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(SdchDictionaryFetcher);
+};
+
+} // namespace net
+
+#endif // NET_BASE_SDCH_DICTIONARY_FETCHER_H_
diff --git a/chromium/net/base/sdch_manager.cc b/chromium/net/base/sdch_manager.cc
index 17883b11613..8892c13a7c4 100644
--- a/chromium/net/base/sdch_manager.cc
+++ b/chromium/net/base/sdch_manager.cc
@@ -17,16 +17,23 @@ namespace net {
//------------------------------------------------------------------------------
// static
-const size_t SdchManager::kMaxDictionarySize = 1000000;
+// Adjust SDCH limits downwards for mobile.
+#if defined(OS_ANDROID) || defined(OS_IOS)
+// static
+const size_t SdchManager::kMaxDictionaryCount = 1;
+const size_t SdchManager::kMaxDictionarySize = 150 * 1000;
+#else
// static
const size_t SdchManager::kMaxDictionaryCount = 20;
+const size_t SdchManager::kMaxDictionarySize = 1000 * 1000;
+#endif
// static
-SdchManager* SdchManager::global_ = NULL;
+bool SdchManager::g_sdch_enabled_ = true;
// static
-bool SdchManager::g_sdch_enabled_ = true;
+bool SdchManager::g_secure_scheme_supported_ = false;
//------------------------------------------------------------------------------
SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
@@ -50,8 +57,6 @@ SdchManager::Dictionary::~Dictionary() {
}
bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
- if (!SdchManager::Global()->IsInSupportedDomain(target_url))
- return false;
/* The specific rules of when a dictionary should be advertised in an
Avail-Dictionary header are modeled after the rules for cookie scoping. The
terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
@@ -63,6 +68,8 @@ bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
ports listed in the Port attribute.
3. The request URI path-matches the path header of the dictionary.
4. The request is not an HTTPS request.
+ We can override (ignore) item (4) only when we have explicitly enabled
+ HTTPS support AND dictionary has been acquired over HTTPS.
*/
if (!DomainMatch(target_url, domain_))
return false;
@@ -70,7 +77,9 @@ bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
return false;
if (path_.size() && !PathMatch(target_url.path(), path_))
return false;
- if (target_url.SchemeIsSecure())
+ if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure())
+ return false;
+ if (target_url.SchemeIsSecure() && !url_.SchemeIsSecure())
return false;
if (base::Time::Now() > expiration_)
return false;
@@ -85,8 +94,6 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain,
const std::string& path,
const std::set<int>& ports,
const GURL& dictionary_url) {
- if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
- return false;
/*
A dictionary is invalid and must not be stored if any of the following are
true:
@@ -111,7 +118,7 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain,
}
if (registry_controlled_domains::GetDomainAndRegistry(
domain,
- registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES).empty()) {
+ registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) {
SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
return false; // domain was a TLD.
}
@@ -143,8 +150,6 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain,
// static
bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
- if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
- return false;
/*
1. The request URL's host name domain-matches the Domain attribute of the
dictionary.
@@ -152,6 +157,8 @@ bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
ports listed in the Port attribute.
3. The request URL path-matches the path attribute of the dictionary.
4. The request is not an HTTPS request.
+ We can override (ignore) item (4) only when we have explicitly enabled
+ HTTPS support AND dictionary has been acquired over HTTPS.
*/
if (!DomainMatch(referring_url, domain_)) {
SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
@@ -166,14 +173,19 @@ bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
return false;
}
- if (referring_url.SchemeIsSecure()) {
+ if (!SdchManager::secure_scheme_supported() &&
+ referring_url.SchemeIsSecure()) {
+ SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
+ return false;
+ }
+ if (referring_url.SchemeIsSecure() && !url_.SchemeIsSecure()) {
SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
return false;
}
// TODO(jar): Remove overly restrictive failsafe test (added per security
// review) when we have a need to be more general.
- if (!referring_url.SchemeIs("http")) {
+ if (!referring_url.SchemeIsHTTPOrHTTPS()) {
SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
return false;
}
@@ -207,33 +219,29 @@ bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
//------------------------------------------------------------------------------
SdchManager::SdchManager() {
- DCHECK(!global_);
DCHECK(CalledOnValidThread());
- global_ = this;
}
SdchManager::~SdchManager() {
- DCHECK_EQ(this, global_);
DCHECK(CalledOnValidThread());
while (!dictionaries_.empty()) {
DictionaryMap::iterator it = dictionaries_.begin();
- it->second->Release();
dictionaries_.erase(it->first);
}
- global_ = NULL;
}
-// static
-void SdchManager::Shutdown() {
- EnableSdchSupport(false);
- if (!global_ )
- return;
- global_->set_sdch_fetcher(NULL);
-}
-
-// static
-SdchManager* SdchManager::Global() {
- return global_;
+void SdchManager::ClearData() {
+ blacklisted_domains_.clear();
+ exponential_blacklist_count_.clear();
+ allow_latency_experiment_.clear();
+ if (fetcher_.get())
+ fetcher_->Cancel();
+
+ // Note that this may result in not having dictionaries we've advertised
+ // for incoming responses. The window is relatively small (as ClearData()
+ // is not expected to be called frequently), so we rely on meta-refresh
+ // to handle this case.
+ dictionaries_.clear();
}
// static
@@ -252,61 +260,55 @@ void SdchManager::EnableSdchSupport(bool enabled) {
}
// static
+void SdchManager::EnableSecureSchemeSupport(bool enabled) {
+ g_secure_scheme_supported_ = enabled;
+}
+
void SdchManager::BlacklistDomain(const GURL& url) {
- if (!global_ )
- return;
- global_->SetAllowLatencyExperiment(url, false);
+ SetAllowLatencyExperiment(url, false);
std::string domain(StringToLowerASCII(url.host()));
- int count = global_->blacklisted_domains_[domain];
+ int count = blacklisted_domains_[domain];
if (count > 0)
return; // Domain is already blacklisted.
- count = 1 + 2 * global_->exponential_blacklist_count[domain];
+ count = 1 + 2 * exponential_blacklist_count_[domain];
if (count > 0)
- global_->exponential_blacklist_count[domain] = count;
+ exponential_blacklist_count_[domain] = count;
else
count = INT_MAX;
- global_->blacklisted_domains_[domain] = count;
+ blacklisted_domains_[domain] = count;
}
-// static
void SdchManager::BlacklistDomainForever(const GURL& url) {
- if (!global_ )
- return;
- global_->SetAllowLatencyExperiment(url, false);
+ SetAllowLatencyExperiment(url, false);
std::string domain(StringToLowerASCII(url.host()));
- global_->exponential_blacklist_count[domain] = INT_MAX;
- global_->blacklisted_domains_[domain] = INT_MAX;
+ exponential_blacklist_count_[domain] = INT_MAX;
+ blacklisted_domains_[domain] = INT_MAX;
}
-// static
void SdchManager::ClearBlacklistings() {
- Global()->blacklisted_domains_.clear();
- Global()->exponential_blacklist_count.clear();
+ blacklisted_domains_.clear();
+ exponential_blacklist_count_.clear();
}
-// static
void SdchManager::ClearDomainBlacklisting(const std::string& domain) {
- Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
+ blacklisted_domains_.erase(StringToLowerASCII(domain));
}
-// static
int SdchManager::BlackListDomainCount(const std::string& domain) {
- if (Global()->blacklisted_domains_.end() ==
- Global()->blacklisted_domains_.find(domain))
+ if (blacklisted_domains_.end() == blacklisted_domains_.find(domain))
return 0;
- return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
+ return blacklisted_domains_[StringToLowerASCII(domain)];
}
-// static
int SdchManager::BlacklistDomainExponential(const std::string& domain) {
- if (Global()->exponential_blacklist_count.end() ==
- Global()->exponential_blacklist_count.find(domain))
+ if (exponential_blacklist_count_.end() ==
+ exponential_blacklist_count_.find(domain))
return 0;
- return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
+ return exponential_blacklist_count_[StringToLowerASCII(domain)];
}
bool SdchManager::IsInSupportedDomain(const GURL& url) {
@@ -314,6 +316,9 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) {
if (!g_sdch_enabled_ )
return false;
+ if (!secure_scheme_supported() && url.SchemeIsSecure())
+ return false;
+
if (blacklisted_domains_.empty())
return true;
@@ -334,8 +339,7 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) {
void SdchManager::FetchDictionary(const GURL& request_url,
const GURL& dictionary_url) {
DCHECK(CalledOnValidThread());
- if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
- fetcher_.get())
+ if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get())
fetcher_->Schedule(dictionary_url);
}
@@ -344,7 +348,8 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url,
DCHECK(CalledOnValidThread());
/* The user agent may retrieve a dictionary from the dictionary URL if all of
the following are true:
- 1 The dictionary URL host name matches the referrer URL host name
+ 1 The dictionary URL host name matches the referrer URL host name and
+ scheme.
2 The dictionary URL host name domain matches the parent domain of the
referrer URL host name
3 The parent domain of the referrer URL host name is not a top level
@@ -353,18 +358,19 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url,
*/
// Item (1) above implies item (2). Spec should be updated.
// I take "host name match" to be "is identical to"
- if (referring_url.host() != dictionary_url.host()) {
+ if (referring_url.host() != dictionary_url.host() ||
+ referring_url.scheme() != dictionary_url.scheme()) {
SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST);
return false;
}
- if (referring_url.SchemeIs("https")) {
+ if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) {
SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL);
return false;
}
// TODO(jar): Remove this failsafe conservative hack which is more restrictive
// than current SDCH spec when needed, and justified by security audit.
- if (!referring_url.SchemeIs("http")) {
+ if (!referring_url.SchemeIsHTTPOrHTTPS()) {
SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP);
return false;
}
@@ -444,6 +450,9 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
line_start = line_end + 1;
}
+ if (!IsInSupportedDomain(dictionary_url))
+ return false;
+
if (!Dictionary::CanSet(domain, path, ports, dictionary_url))
return false;
@@ -466,20 +475,23 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
Dictionary* dictionary =
new Dictionary(dictionary_text, header_end + 2, client_hash,
dictionary_url, domain, path, expiration, ports);
- dictionary->AddRef();
dictionaries_[server_hash] = dictionary;
return true;
}
-void SdchManager::GetVcdiffDictionary(const std::string& server_hash,
- const GURL& referring_url, Dictionary** dictionary) {
+void SdchManager::GetVcdiffDictionary(
+ const std::string& server_hash,
+ const GURL& referring_url,
+ scoped_refptr<Dictionary>* dictionary) {
DCHECK(CalledOnValidThread());
*dictionary = NULL;
DictionaryMap::iterator it = dictionaries_.find(server_hash);
if (it == dictionaries_.end()) {
return;
}
- Dictionary* matching_dictionary = it->second;
+ scoped_refptr<Dictionary> matching_dictionary = it->second;
+ if (!IsInSupportedDomain(referring_url))
+ return;
if (!matching_dictionary->CanUse(referring_url))
return;
*dictionary = matching_dictionary;
@@ -494,6 +506,8 @@ void SdchManager::GetAvailDictionaryList(const GURL& target_url,
int count = 0;
for (DictionaryMap::iterator it = dictionaries_.begin();
it != dictionaries_.end(); ++it) {
+ if (!IsInSupportedDomain(target_url))
+ continue;
if (!it->second->CanAdvertise(target_url))
continue;
++count;
diff --git a/chromium/net/base/sdch_manager.h b/chromium/net/base/sdch_manager.h
index 4f45966dd5b..6f2ea5af6de 100644
--- a/chromium/net/base/sdch_manager.h
+++ b/chromium/net/base/sdch_manager.h
@@ -40,7 +40,7 @@ namespace net {
// A browser may register a fetcher that is used by the dictionary managers to
// get data from a specified URL. This allows us to use very high level browser
// functionality in this base (when the functionaity can be provided).
-class SdchFetcher {
+class NET_EXPORT SdchFetcher {
public:
SdchFetcher() {}
virtual ~SdchFetcher() {}
@@ -49,6 +49,11 @@ class SdchFetcher {
// from a server. The callee is responsible for getting that dictionary_text,
// and then calling back to AddSdchDictionary() to the SdchManager instance.
virtual void Schedule(const GURL& dictionary_url) = 0;
+
+ // The Cancel() method is called to cancel all pending dictionary fetches.
+ // This is used for implementation of ClearData() below.
+ virtual void Cancel() = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(SdchFetcher);
};
@@ -170,7 +175,7 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
private:
friend class base::RefCounted<Dictionary>;
friend class SdchManager; // Only manager can construct an instance.
- FRIEND_TEST_ALL_PREFIXES(SdchFilterTest, PathMatch);
+ FRIEND_TEST_ALL_PREFIXES(SdchManagerTest, PathMatch);
// Construct a vc-diff usable dictionary from the dictionary_text starting
// at the given offset. The supplied client_hash should be used to
@@ -234,11 +239,8 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
SdchManager();
~SdchManager();
- // Discontinue fetching of dictionaries, as we're now shutting down.
- static void Shutdown();
-
- // Provide access to the single instance of this class.
- static SdchManager* Global();
+ // Clear data (for browser data removal).
+ void ClearData();
// Record stats on various errors.
static void SdchErrorRecovery(ProblemCodes problem);
@@ -251,6 +253,11 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
static bool sdch_enabled() { return g_sdch_enabled_; }
+ // Enables or disables SDCH compression over secure connection.
+ static void EnableSecureSchemeSupport(bool enabled);
+
+ static bool secure_scheme_supported() { return g_secure_scheme_supported_; }
+
// Briefly prevent further advertising of SDCH on this domain (if SDCH is
// enabled). After enough calls to IsInSupportedDomain() the blacklisting
// will be removed. Additional blacklists take exponentially more calls
@@ -258,24 +265,24 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
// Used when filter errors are found from a given domain, but it is plausible
// that the cause is temporary (such as application startup, where cached
// entries are used, but a dictionary is not yet loaded).
- static void BlacklistDomain(const GURL& url);
+ void BlacklistDomain(const GURL& url);
// Used when SEVERE filter errors are found from a given domain, to prevent
// further use of SDCH on that domain.
- static void BlacklistDomainForever(const GURL& url);
+ void BlacklistDomainForever(const GURL& url);
// Unit test only, this function resets enabling of sdch, and clears the
// blacklist.
- static void ClearBlacklistings();
+ void ClearBlacklistings();
// Unit test only, this function resets the blacklisting count for a domain.
- static void ClearDomainBlacklisting(const std::string& domain);
+ void ClearDomainBlacklisting(const std::string& domain);
// Unit test only: indicate how many more times a domain will be blacklisted.
- static int BlackListDomainCount(const std::string& domain);
+ int BlackListDomainCount(const std::string& domain);
// Unit test only: Indicate what current blacklist increment is for a domain.
- static int BlacklistDomainExponential(const std::string& domain);
+ int BlacklistDomainExponential(const std::string& domain);
// Check to see if SDCH is enabled (globally), and the given URL is in a
// supported domain (i.e., not blacklisted, and either the specific supported
@@ -305,12 +312,10 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
// to use to decompreses data that arrived as SDCH encoded content. Check to
// be sure the returned |dictionary| can be used for decoding content supplied
// in response to a request for |referring_url|.
- // Caller is responsible for AddRef()ing the dictionary, and Release()ing it
- // when done.
// Return null in |dictionary| if there is no matching legal dictionary.
void GetVcdiffDictionary(const std::string& server_hash,
const GURL& referring_url,
- Dictionary** dictionary);
+ scoped_refptr<Dictionary>* dictionary);
// Get list of available (pre-cached) dictionaries that we have already loaded
// into memory. The list is a comma separated list of (client) hashes per
@@ -336,14 +341,15 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
typedef std::set<std::string> ExperimentSet;
// A map of dictionaries info indexed by the hash that the server provides.
- typedef std::map<std::string, Dictionary*> DictionaryMap;
-
- // The one global instance of that holds all the data.
- static SdchManager* global_;
+ typedef std::map<std::string, scoped_refptr<Dictionary> > DictionaryMap;
// Support SDCH compression, by advertising in headers.
static bool g_sdch_enabled_;
+ // Support SDCH compression for HTTPS requests and responses. When supported,
+ // HTTPS applicable dictionaries MUST have been acquired securely via HTTPS.
+ static bool g_secure_scheme_supported_;
+
// A simple implementation of a RFC 3548 "URL safe" base64 encoder.
static void UrlSafeBase64Encode(const std::string& input,
std::string* output);
@@ -358,7 +364,7 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
// Support exponential backoff in number of domain accesses before
// blacklisting expires.
- DomainCounter exponential_blacklist_count;
+ DomainCounter exponential_blacklist_count_;
// List of hostnames for which a latency experiment is allowed (because a
// round trip test has recently passed).
diff --git a/chromium/net/base/sdch_manager_unittest.cc b/chromium/net/base/sdch_manager_unittest.cc
new file mode 100644
index 00000000000..87b01f4f7ae
--- /dev/null
+++ b/chromium/net/base/sdch_manager_unittest.cc
@@ -0,0 +1,478 @@
+// Copyright 2014 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 <limits.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/sdch_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+//------------------------------------------------------------------------------
+// Provide sample data and compression results with a sample VCDIFF dictionary.
+// Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
+static const char kTestVcdiffDictionary[] = "DictionaryFor"
+ "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
+
+//------------------------------------------------------------------------------
+
+class SdchManagerTest : public testing::Test {
+ protected:
+ SdchManagerTest()
+ : sdch_manager_(new SdchManager) {
+ }
+
+ SdchManager* sdch_manager() { return sdch_manager_.get(); }
+
+ // Reset globals back to default state.
+ virtual void TearDown() {
+ SdchManager::EnableSdchSupport(true);
+ SdchManager::EnableSecureSchemeSupport(false);
+ }
+
+ private:
+ scoped_ptr<SdchManager> sdch_manager_;
+};
+
+//------------------------------------------------------------------------------
+static std::string NewSdchDictionary(const std::string& domain) {
+ std::string dictionary;
+ if (!domain.empty()) {
+ dictionary.append("Domain: ");
+ dictionary.append(domain);
+ dictionary.append("\n");
+ }
+ dictionary.append("\n");
+ dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1);
+ return dictionary;
+}
+
+TEST_F(SdchManagerTest, DomainSupported) {
+ GURL google_url("http://www.google.com");
+
+ SdchManager::EnableSdchSupport(false);
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(google_url));
+ SdchManager::EnableSdchSupport(true);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(google_url));
+}
+
+TEST_F(SdchManagerTest, DomainBlacklisting) {
+ GURL test_url("http://www.test.com");
+ GURL google_url("http://www.google.com");
+
+ sdch_manager()->BlacklistDomain(test_url);
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(test_url));
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(google_url));
+
+ sdch_manager()->BlacklistDomain(google_url);
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(google_url));
+}
+
+TEST_F(SdchManagerTest, DomainBlacklistingCaseSensitivity) {
+ GURL test_url("http://www.TesT.com");
+ GURL test2_url("http://www.tEst.com");
+
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(test_url));
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(test2_url));
+ sdch_manager()->BlacklistDomain(test_url);
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(test2_url));
+}
+
+TEST_F(SdchManagerTest, BlacklistingReset) {
+ GURL gurl("http://mytest.DoMain.com");
+ std::string domain(gurl.host());
+
+ sdch_manager()->ClearBlacklistings();
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0);
+ EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), 0);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(gurl));
+}
+
+TEST_F(SdchManagerTest, BlacklistingSingleBlacklist) {
+ GURL gurl("http://mytest.DoMain.com");
+ std::string domain(gurl.host());
+ sdch_manager()->ClearBlacklistings();
+
+ sdch_manager()->BlacklistDomain(gurl);
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 1);
+ EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), 1);
+
+ // Check that any domain lookup reduces the blacklist counter.
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(gurl));
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(gurl));
+}
+
+TEST_F(SdchManagerTest, BlacklistingExponential) {
+ GURL gurl("http://mytest.DoMain.com");
+ std::string domain(gurl.host());
+ sdch_manager()->ClearBlacklistings();
+
+ int exponential = 1;
+ for (int i = 1; i < 100; ++i) {
+ sdch_manager()->BlacklistDomain(gurl);
+ EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), exponential);
+
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), exponential);
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(gurl));
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), exponential - 1);
+
+ // Simulate a large number of domain checks (which eventually remove the
+ // blacklisting).
+ sdch_manager()->ClearDomainBlacklisting(domain);
+ EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(gurl));
+
+ // Predict what exponential backoff will be.
+ exponential = 1 + 2 * exponential;
+ if (exponential < 0)
+ exponential = INT_MAX; // We don't wrap.
+ }
+}
+
+TEST_F(SdchManagerTest, CanSetExactMatchDictionary) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Perfect match should work.
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+}
+
+TEST_F(SdchManagerTest, CanAdvertiseDictionaryOverHTTP) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+
+ std::string dictionary_list;
+ // HTTP target URL can advertise dictionary.
+ sdch_manager()->GetAvailDictionaryList(
+ GURL("http://" + dictionary_domain + "/test"),
+ &dictionary_list);
+ EXPECT_FALSE(dictionary_list.empty());
+}
+
+TEST_F(SdchManagerTest, CanNotAdvertiseDictionaryOverHTTPS) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+
+ std::string dictionary_list;
+ // HTTPS target URL should NOT advertise dictionary.
+ sdch_manager()->GetAvailDictionaryList(
+ GURL("https://" + dictionary_domain + "/test"),
+ &dictionary_list);
+ EXPECT_TRUE(dictionary_list.empty());
+}
+
+TEST_F(SdchManagerTest, CanUseHTTPSDictionaryOverHTTPSIfEnabled) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ EXPECT_FALSE(sdch_manager()->AddSdchDictionary(
+ dictionary_text, GURL("https://" + dictionary_domain)));
+ SdchManager::EnableSecureSchemeSupport(true);
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(
+ dictionary_text, GURL("https://" + dictionary_domain)));
+
+ GURL target_url("https://" + dictionary_domain + "/test");
+ std::string dictionary_list;
+ // HTTPS target URL should advertise dictionary if secure scheme support is
+ // enabled.
+ sdch_manager()->GetAvailDictionaryList(target_url, &dictionary_list);
+ EXPECT_FALSE(dictionary_list.empty());
+
+ // Dictionary should be available.
+ scoped_refptr<SdchManager::Dictionary> dictionary;
+ std::string client_hash;
+ std::string server_hash;
+ sdch_manager()->GenerateHash(dictionary_text, &client_hash, &server_hash);
+ sdch_manager()->GetVcdiffDictionary(server_hash, target_url, &dictionary);
+ EXPECT_TRUE(dictionary != NULL);
+}
+
+TEST_F(SdchManagerTest, CanNotUseHTTPDictionaryOverHTTPS) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+
+ GURL target_url("https://" + dictionary_domain + "/test");
+ std::string dictionary_list;
+ // HTTPS target URL should not advertise dictionary acquired over HTTP even if
+ // secure scheme support is enabled.
+ SdchManager::EnableSecureSchemeSupport(true);
+ sdch_manager()->GetAvailDictionaryList(target_url, &dictionary_list);
+ EXPECT_TRUE(dictionary_list.empty());
+
+ scoped_refptr<SdchManager::Dictionary> dictionary;
+ std::string client_hash;
+ std::string server_hash;
+ sdch_manager()->GenerateHash(dictionary_text, &client_hash, &server_hash);
+ sdch_manager()->GetVcdiffDictionary(server_hash, target_url, &dictionary);
+ EXPECT_TRUE(dictionary == NULL);
+}
+
+TEST_F(SdchManagerTest, FailToSetDomainMismatchDictionary) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Fail the "domain match" requirement.
+ EXPECT_FALSE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://y.z.google.com")));
+}
+
+TEST_F(SdchManagerTest, FailToSetDotHostPrefixDomainDictionary) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Fail the HD with D being the domain and H having a dot requirement.
+ EXPECT_FALSE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://w.x.y.z.google.com")));
+}
+
+TEST_F(SdchManagerTest, FailToSetRepeatPrefixWithDotDictionary) {
+ // Make sure that a prefix that matches the domain postfix won't confuse
+ // the validation checks.
+ std::string dictionary_domain("www.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Fail the HD with D being the domain and H having a dot requirement.
+ EXPECT_FALSE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://www.google.com.www.google.com")));
+}
+
+TEST_F(SdchManagerTest, CanSetLeadingDotDomainDictionary) {
+ // Make sure that a prefix that matches the domain postfix won't confuse
+ // the validation checks.
+ std::string dictionary_domain(".google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Verify that a leading dot in the domain is acceptable, as long as the host
+ // name does not contain any dots preceding the matched domain name.
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://www.google.com")));
+}
+
+// Make sure the order of the tests is not helping us or confusing things.
+// See test CanSetExactMatchDictionary above for first try.
+TEST_F(SdchManagerTest, CanStillSetExactMatchDictionary) {
+ std::string dictionary_domain("x.y.z.google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ // Perfect match should *STILL* work.
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+}
+
+// Make sure the DOS protection precludes the addition of too many dictionaries.
+TEST_F(SdchManagerTest, TooManyDictionaries) {
+ std::string dictionary_domain(".google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ size_t count = 0;
+ while (count <= SdchManager::kMaxDictionaryCount + 1) {
+ if (!sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://www.google.com")))
+ break;
+
+ dictionary_text += " "; // Create dictionary with different SHA signature.
+ ++count;
+ }
+ EXPECT_EQ(SdchManager::kMaxDictionaryCount, count);
+}
+
+TEST_F(SdchManagerTest, DictionaryNotTooLarge) {
+ std::string dictionary_domain(".google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ dictionary_text.append(
+ SdchManager::kMaxDictionarySize - dictionary_text.size(), ' ');
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+}
+
+TEST_F(SdchManagerTest, DictionaryTooLarge) {
+ std::string dictionary_domain(".google.com");
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+
+ dictionary_text.append(
+ SdchManager::kMaxDictionarySize + 1 - dictionary_text.size(), ' ');
+ EXPECT_FALSE(sdch_manager()->AddSdchDictionary(dictionary_text,
+ GURL("http://" + dictionary_domain)));
+}
+
+TEST_F(SdchManagerTest, PathMatch) {
+ bool (*PathMatch)(const std::string& path, const std::string& restriction) =
+ SdchManager::Dictionary::PathMatch;
+ // Perfect match is supported.
+ EXPECT_TRUE(PathMatch("/search", "/search"));
+ EXPECT_TRUE(PathMatch("/search/", "/search/"));
+
+ // Prefix only works if last character of restriction is a slash, or first
+ // character in path after a match is a slash. Validate each case separately.
+
+ // Rely on the slash in the path (not at the end of the restriction).
+ EXPECT_TRUE(PathMatch("/search/something", "/search"));
+ EXPECT_TRUE(PathMatch("/search/s", "/search"));
+ EXPECT_TRUE(PathMatch("/search/other", "/search"));
+ EXPECT_TRUE(PathMatch("/search/something", "/search"));
+
+ // Rely on the slash at the end of the restriction.
+ EXPECT_TRUE(PathMatch("/search/something", "/search/"));
+ EXPECT_TRUE(PathMatch("/search/s", "/search/"));
+ EXPECT_TRUE(PathMatch("/search/other", "/search/"));
+ EXPECT_TRUE(PathMatch("/search/something", "/search/"));
+
+ // Make sure less that sufficient prefix match is false.
+ EXPECT_FALSE(PathMatch("/sear", "/search"));
+ EXPECT_FALSE(PathMatch("/", "/search"));
+ EXPECT_FALSE(PathMatch(std::string(), "/search"));
+
+ // Add examples with several levels of direcories in the restriction.
+ EXPECT_FALSE(PathMatch("/search/something", "search/s"));
+ EXPECT_FALSE(PathMatch("/search/", "/search/s"));
+
+ // Make sure adding characters to path will also fail.
+ EXPECT_FALSE(PathMatch("/searching", "/search/"));
+ EXPECT_FALSE(PathMatch("/searching", "/search"));
+
+ // Make sure we're case sensitive.
+ EXPECT_FALSE(PathMatch("/ABC", "/abc"));
+ EXPECT_FALSE(PathMatch("/abc", "/ABC"));
+}
+
+// The following are only applicable while we have a latency test in the code,
+// and can be removed when that functionality is stripped.
+TEST_F(SdchManagerTest, LatencyTestControls) {
+ GURL url("http://www.google.com");
+ GURL url2("http://www.google2.com");
+
+ // First make sure we default to false.
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url));
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2));
+
+ // That we can set each to true.
+ sdch_manager()->SetAllowLatencyExperiment(url, true);
+ EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url));
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2));
+
+ sdch_manager()->SetAllowLatencyExperiment(url2, true);
+ EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url));
+ EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url2));
+
+ // And can reset them to false.
+ sdch_manager()->SetAllowLatencyExperiment(url, false);
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url));
+ EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url2));
+
+ sdch_manager()->SetAllowLatencyExperiment(url2, false);
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url));
+ EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2));
+}
+
+TEST_F(SdchManagerTest, CanUseMultipleManagers) {
+ SdchManager second_manager;
+
+ std::string dictionary_domain_1("x.y.z.google.com");
+ std::string dictionary_domain_2("x.y.z.chromium.org");
+
+ std::string dictionary_text_1(NewSdchDictionary(dictionary_domain_1));
+ std::string dictionary_text_2(NewSdchDictionary(dictionary_domain_2));
+
+ std::string tmp_hash;
+ std::string server_hash_1;
+ std::string server_hash_2;
+
+ SdchManager::GenerateHash(dictionary_text_1, &tmp_hash, &server_hash_1);
+ SdchManager::GenerateHash(dictionary_text_2, &tmp_hash, &server_hash_2);
+
+ // Confirm that if you add directories to one manager, you
+ // can't get them from the other.
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(
+ dictionary_text_1, GURL("http://" + dictionary_domain_1)));
+ scoped_refptr<SdchManager::Dictionary> dictionary;
+ sdch_manager()->GetVcdiffDictionary(
+ server_hash_1,
+ GURL("http://" + dictionary_domain_1 + "/random_url"),
+ &dictionary);
+ EXPECT_TRUE(dictionary);
+
+ EXPECT_TRUE(second_manager.AddSdchDictionary(
+ dictionary_text_2, GURL("http://" + dictionary_domain_2)));
+ second_manager.GetVcdiffDictionary(
+ server_hash_2,
+ GURL("http://" + dictionary_domain_2 + "/random_url"),
+ &dictionary);
+ EXPECT_TRUE(dictionary);
+
+ sdch_manager()->GetVcdiffDictionary(
+ server_hash_2,
+ GURL("http://" + dictionary_domain_2 + "/random_url"),
+ &dictionary);
+ EXPECT_FALSE(dictionary);
+
+ second_manager.GetVcdiffDictionary(
+ server_hash_1,
+ GURL("http://" + dictionary_domain_1 + "/random_url"),
+ &dictionary);
+ EXPECT_FALSE(dictionary);
+}
+
+TEST_F(SdchManagerTest, HttpsCorrectlySupported) {
+ GURL url("http://www.google.com");
+ GURL secure_url("https://www.google.com");
+
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(url));
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(secure_url));
+
+ SdchManager::EnableSecureSchemeSupport(true);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(url));
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(secure_url));
+}
+
+TEST_F(SdchManagerTest, ClearDictionaryData) {
+ std::string dictionary_domain("x.y.z.google.com");
+ GURL blacklist_url("http://bad.chromium.org");
+
+ std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+ std::string tmp_hash;
+ std::string server_hash;
+
+ SdchManager::GenerateHash(dictionary_text, &tmp_hash, &server_hash);
+
+ EXPECT_TRUE(sdch_manager()->AddSdchDictionary(
+ dictionary_text, GURL("http://" + dictionary_domain)));
+ scoped_refptr<SdchManager::Dictionary> dictionary;
+ sdch_manager()->GetVcdiffDictionary(
+ server_hash,
+ GURL("http://" + dictionary_domain + "/random_url"),
+ &dictionary);
+ EXPECT_TRUE(dictionary);
+
+ sdch_manager()->BlacklistDomain(GURL(blacklist_url));
+ EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(blacklist_url));
+
+ sdch_manager()->ClearData();
+
+ dictionary = NULL;
+ sdch_manager()->GetVcdiffDictionary(
+ server_hash,
+ GURL("http://" + dictionary_domain + "/random_url"),
+ &dictionary);
+ EXPECT_FALSE(dictionary);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(blacklist_url));
+}
+
+} // namespace net
+
diff --git a/chromium/net/base/static_cookie_policy.cc b/chromium/net/base/static_cookie_policy.cc
index c1acb669736..021cacef4ca 100644
--- a/chromium/net/base/static_cookie_policy.cc
+++ b/chromium/net/base/static_cookie_policy.cc
@@ -16,7 +16,6 @@ int StaticCookiePolicy::CanGetCookies(
const GURL& first_party_for_cookies) const {
switch (type_) {
case StaticCookiePolicy::ALLOW_ALL_COOKIES:
- case StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES:
return OK;
case StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES:
if (first_party_for_cookies.is_empty())
@@ -24,7 +23,7 @@ int StaticCookiePolicy::CanGetCookies(
return registry_controlled_domains::SameDomainOrHost(
url,
first_party_for_cookies,
- registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES) ?
+ registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) ?
OK : ERR_ACCESS_DENIED;
case StaticCookiePolicy::BLOCK_ALL_COOKIES:
return ERR_ACCESS_DENIED;
@@ -40,14 +39,13 @@ int StaticCookiePolicy::CanSetCookie(
switch (type_) {
case StaticCookiePolicy::ALLOW_ALL_COOKIES:
return OK;
- case StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES:
case StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES:
if (first_party_for_cookies.is_empty())
return OK; // Empty first-party URL indicates a first-party request.
return registry_controlled_domains::SameDomainOrHost(
url,
first_party_for_cookies,
- registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES) ?
+ registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) ?
OK : ERR_ACCESS_DENIED;
case StaticCookiePolicy::BLOCK_ALL_COOKIES:
return ERR_ACCESS_DENIED;
diff --git a/chromium/net/base/static_cookie_policy.h b/chromium/net/base/static_cookie_policy.h
index eaf2f97fc36..023153199db 100644
--- a/chromium/net/base/static_cookie_policy.h
+++ b/chromium/net/base/static_cookie_policy.h
@@ -21,8 +21,6 @@ class NET_EXPORT StaticCookiePolicy {
enum Type {
// Do not perform any cookie blocking.
ALLOW_ALL_COOKIES = 0,
- // Prevent only third-party cookies from being set.
- BLOCK_SETTING_THIRD_PARTY_COOKIES,
// Block all cookies (third-party or not) from begin set or read.
BLOCK_ALL_COOKIES,
// Prevent only third-party cookies from being set or read.
diff --git a/chromium/net/base/static_cookie_policy_unittest.cc b/chromium/net/base/static_cookie_policy_unittest.cc
index eb1d40b816b..7749411addf 100644
--- a/chromium/net/base/static_cookie_policy_unittest.cc
+++ b/chromium/net/base/static_cookie_policy_unittest.cc
@@ -64,22 +64,6 @@ TEST_F(StaticCookiePolicyTest, AllowAllCookiesTest) {
EXPECT_EQ(OK, CanSetCookie(url_google_, GURL()));
}
-TEST_F(StaticCookiePolicyTest, BlockSettingThirdPartyCookiesTest) {
- SetPolicyType(StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES);
-
- EXPECT_EQ(OK, CanGetCookies(url_google_, url_google_));
- EXPECT_EQ(OK, CanGetCookies(url_google_, url_google_secure_));
- EXPECT_EQ(OK, CanGetCookies(url_google_, url_google_mail_));
- EXPECT_EQ(OK, CanGetCookies(url_google_, url_google_analytics_));
- EXPECT_EQ(OK, CanGetCookies(url_google_, GURL()));
-
- EXPECT_EQ(OK, CanSetCookie(url_google_, url_google_));
- EXPECT_EQ(OK, CanSetCookie(url_google_, url_google_secure_));
- EXPECT_EQ(OK, CanSetCookie(url_google_, url_google_mail_));
- EXPECT_NE(OK, CanSetCookie(url_google_, url_google_analytics_));
- EXPECT_EQ(OK, CanSetCookie(url_google_, GURL()));
-}
-
TEST_F(StaticCookiePolicyTest, BlockAllThirdPartyCookiesTest) {
SetPolicyType(StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
diff --git a/chromium/net/base/upload_data.cc b/chromium/net/base/upload_data.cc
deleted file mode 100644
index 48cb2020f98..00000000000
--- a/chromium/net/base/upload_data.cc
+++ /dev/null
@@ -1,37 +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/base/upload_data.h"
-
-#include "base/logging.h"
-
-namespace net {
-
-UploadData::UploadData()
- : identifier_(0),
- is_chunked_(false),
- last_chunk_appended_(false) {
-}
-
-void UploadData::AppendBytes(const char* bytes, int bytes_len) {
- DCHECK(!is_chunked_);
- if (bytes_len > 0) {
- elements_.push_back(new UploadElement());
- elements_.back()->SetToBytes(bytes, bytes_len);
- }
-}
-
-void UploadData::AppendFileRange(const base::FilePath& file_path,
- uint64 offset, uint64 length,
- const base::Time& expected_modification_time) {
- DCHECK(!is_chunked_);
- elements_.push_back(new UploadElement());
- elements_.back()->SetToFilePathRange(file_path, offset, length,
- expected_modification_time);
-}
-
-UploadData::~UploadData() {
-}
-
-} // namespace net
diff --git a/chromium/net/base/upload_data.h b/chromium/net/base/upload_data.h
deleted file mode 100644
index b782ab4e4de..00000000000
--- a/chromium/net/base/upload_data.h
+++ /dev/null
@@ -1,83 +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_BASE_UPLOAD_DATA_H_
-#define NET_BASE_UPLOAD_DATA_H_
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
-#include "base/supports_user_data.h"
-#include "net/base/net_export.h"
-#include "net/base/upload_element.h"
-
-namespace base {
-class FilePath;
-class Time;
-} // namespace base
-
-namespace net {
-
-//-----------------------------------------------------------------------------
-// A very concrete class representing the data to be uploaded as part of a
-// URLRequest.
-//
-// Until there is a more abstract class for this, this one derives from
-// SupportsUserData to allow users to stash random data by
-// key and ensure its destruction when UploadData is finally deleted.
-class NET_EXPORT UploadData
- : public base::RefCounted<UploadData>,
- public base::SupportsUserData {
- public:
- UploadData();
-
- void AppendBytes(const char* bytes, int bytes_len);
-
- void AppendFileRange(const base::FilePath& file_path,
- uint64 offset, uint64 length,
- const base::Time& expected_modification_time);
-
- // Initializes the object to send chunks of upload data over time rather
- // than all at once. Chunked data may only contain bytes, not files.
- void set_is_chunked(bool set) { is_chunked_ = set; }
- bool is_chunked() const { return is_chunked_; }
-
- // set_last_chunk_appended() is only used for serialization.
- void set_last_chunk_appended(bool set) { last_chunk_appended_ = set; }
- bool last_chunk_appended() const { return last_chunk_appended_; }
-
- const ScopedVector<UploadElement>& elements() const {
- return elements_;
- }
-
- ScopedVector<UploadElement>* elements_mutable() {
- return &elements_;
- }
-
- void swap_elements(ScopedVector<UploadElement>* elements) {
- elements_.swap(*elements);
- }
-
- // Identifies a particular upload instance, which is used by the cache to
- // formulate a cache key. This value should be unique across browser
- // sessions. A value of 0 is used to indicate an unspecified identifier.
- void set_identifier(int64 id) { identifier_ = id; }
- int64 identifier() const { return identifier_; }
-
- private:
- friend class base::RefCounted<UploadData>;
-
- virtual ~UploadData();
-
- ScopedVector<UploadElement> elements_;
- int64 identifier_;
- bool is_chunked_;
- bool last_chunk_appended_;
-
- DISALLOW_COPY_AND_ASSIGN(UploadData);
-};
-
-} // namespace net
-
-#endif // NET_BASE_UPLOAD_DATA_H_
diff --git a/chromium/net/base/upload_data_stream_unittest.cc b/chromium/net/base/upload_data_stream_unittest.cc
index 24855a85ea4..90a78d24b52 100644
--- a/chromium/net/base/upload_data_stream_unittest.cc
+++ b/chromium/net/base/upload_data_stream_unittest.cc
@@ -174,7 +174,7 @@ TEST_F(UploadDataStreamTest, File) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
element_readers_.push_back(
new UploadFileElementReader(base::MessageLoopProxy::current().get(),
@@ -208,7 +208,7 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
const uint64 kFakeSize = kTestDataSize*2;
UploadFileElementReader::ScopedOverridingContentLengthForTests
@@ -326,7 +326,7 @@ TEST_F(UploadDataStreamTest, FileAndBytes) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
const uint64 kFileRangeOffset = 1;
const uint64 kFileRangeLength = 4;
@@ -552,9 +552,9 @@ TEST_F(UploadDataStreamTest, FileChanged) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
ASSERT_TRUE(base::GetFileInfo(temp_file_path, &file_info));
// Test file not changed.
@@ -571,7 +571,7 @@ TEST_F(UploadDataStreamTest, MultipleInit) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
// Prepare data.
element_readers_.push_back(new UploadBytesElementReader(
@@ -615,7 +615,7 @@ TEST_F(UploadDataStreamTest, MultipleInitAsync) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
TestCompletionCallback test_callback;
// Prepare data.
@@ -658,7 +658,7 @@ TEST_F(UploadDataStreamTest, InitToReset) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
// Prepare data.
element_readers_.push_back(new UploadBytesElementReader(
@@ -715,7 +715,7 @@ TEST_F(UploadDataStreamTest, InitDuringAsyncInit) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
// Prepare data.
element_readers_.push_back(new UploadBytesElementReader(
@@ -763,7 +763,7 @@ TEST_F(UploadDataStreamTest, InitDuringAsyncRead) {
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
&temp_file_path));
ASSERT_EQ(static_cast<int>(kTestDataSize),
- file_util::WriteFile(temp_file_path, kTestData, kTestDataSize));
+ base::WriteFile(temp_file_path, kTestData, kTestDataSize));
// Prepare data.
element_readers_.push_back(new UploadBytesElementReader(
diff --git a/chromium/net/base/upload_file_element_reader.cc b/chromium/net/base/upload_file_element_reader.cc
index ab7dd9eb1a1..f8775ee567b 100644
--- a/chromium/net/base/upload_file_element_reader.cc
+++ b/chromium/net/base/upload_file_element_reader.cc
@@ -20,102 +20,8 @@ namespace {
// UploadFileElementReader::GetContentLength() when set to non-zero.
uint64 overriding_content_length = 0;
-// This function is used to implement Init().
-template<typename FileStreamDeleter>
-int InitInternal(const base::FilePath& path,
- uint64 range_offset,
- uint64 range_length,
- const base::Time& expected_modification_time,
- scoped_ptr<FileStream, FileStreamDeleter>* out_file_stream,
- uint64* out_content_length) {
- scoped_ptr<FileStream> file_stream(new FileStream(NULL));
- int64 rv = file_stream->OpenSync(
- path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
- if (rv != OK) {
- // If the file can't be opened, the upload should fail.
- DLOG(WARNING) << "Failed to open \"" << path.value()
- << "\" for reading: " << rv;
- return rv;
- } else if (range_offset) {
- rv = file_stream->SeekSync(FROM_BEGIN, range_offset);
- if (rv < 0) {
- DLOG(WARNING) << "Failed to seek \"" << path.value()
- << "\" to offset: " << range_offset << " (" << rv << ")";
- return rv;
- }
- }
-
- int64 length = 0;
- if (!base::GetFileSize(path, &length)) {
- DLOG(WARNING) << "Failed to get file size of \"" << path.value() << "\"";
- return ERR_FILE_NOT_FOUND;
- }
-
- if (range_offset < static_cast<uint64>(length)) {
- // Compensate for the offset.
- length = std::min(length - range_offset, range_length);
- }
-
- // If the underlying file has been changed and the expected file modification
- // time is set, treat it as error. Note that the expected modification time
- // from WebKit is based on time_t precision. So we have to convert both to
- // time_t to compare. This check is used for sliced files.
- if (!expected_modification_time.is_null()) {
- base::PlatformFileInfo info;
- if (!base::GetFileInfo(path, &info)) {
- DLOG(WARNING) << "Failed to get file info of \"" << path.value() << "\"";
- return ERR_FILE_NOT_FOUND;
- }
-
- if (expected_modification_time.ToTimeT() != info.last_modified.ToTimeT()) {
- return ERR_UPLOAD_FILE_CHANGED;
- }
- }
-
- *out_content_length = length;
- out_file_stream->reset(file_stream.release());
-
- return OK;
-}
-
-// This function is used to implement Read().
-int ReadInternal(scoped_refptr<IOBuffer> buf,
- int buf_length,
- uint64 bytes_remaining,
- FileStream* file_stream) {
- DCHECK_LT(0, buf_length);
-
- const uint64 num_bytes_to_read =
- std::min(bytes_remaining, static_cast<uint64>(buf_length));
-
- int result = 0;
- if (num_bytes_to_read > 0) {
- DCHECK(file_stream); // file_stream is non-null if content_length_ > 0.
- result = file_stream->ReadSync(buf->data(), num_bytes_to_read);
- if (result == 0) // Reached end-of-file earlier than expected.
- result = ERR_UPLOAD_FILE_CHANGED;
- }
- return result;
-}
-
} // namespace
-UploadFileElementReader::FileStreamDeleter::FileStreamDeleter(
- base::TaskRunner* task_runner) : task_runner_(task_runner) {
- DCHECK(task_runner_.get());
-}
-
-UploadFileElementReader::FileStreamDeleter::~FileStreamDeleter() {}
-
-void UploadFileElementReader::FileStreamDeleter::operator() (
- FileStream* file_stream) const {
- if (file_stream) {
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&base::DeletePointer<FileStream>,
- file_stream));
- }
-}
-
UploadFileElementReader::UploadFileElementReader(
base::TaskRunner* task_runner,
const base::FilePath& path,
@@ -127,7 +33,6 @@ UploadFileElementReader::UploadFileElementReader(
range_offset_(range_offset),
range_length_(range_length),
expected_modification_time_(expected_modification_time),
- file_stream_(NULL, FileStreamDeleter(task_runner_.get())),
content_length_(0),
bytes_remaining_(0),
weak_ptr_factory_(this) {
@@ -145,26 +50,16 @@ int UploadFileElementReader::Init(const CompletionCallback& callback) {
DCHECK(!callback.is_null());
Reset();
- ScopedFileStreamPtr* file_stream =
- new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get()));
- uint64* content_length = new uint64;
- const bool posted = base::PostTaskAndReplyWithResult(
- task_runner_.get(),
- FROM_HERE,
- base::Bind(&InitInternal<FileStreamDeleter>,
- path_,
- range_offset_,
- range_length_,
- expected_modification_time_,
- file_stream,
- content_length),
- base::Bind(&UploadFileElementReader::OnInitCompleted,
+ file_stream_.reset(new FileStream(task_runner_.get()));
+ int result = file_stream_->Open(
+ path_,
+ base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_ASYNC,
+ base::Bind(&UploadFileElementReader::OnOpenCompleted,
weak_ptr_factory_.GetWeakPtr(),
- base::Owned(file_stream),
- base::Owned(content_length),
callback));
- DCHECK(posted);
- return ERR_IO_PENDING;
+ DCHECK_GT(0, result);
+ return result;
}
uint64 UploadFileElementReader::GetContentLength() const {
@@ -182,26 +77,19 @@ int UploadFileElementReader::Read(IOBuffer* buf,
const CompletionCallback& callback) {
DCHECK(!callback.is_null());
- if (BytesRemaining() == 0)
+ uint64 num_bytes_to_read =
+ std::min(BytesRemaining(), static_cast<uint64>(buf_length));
+ if (num_bytes_to_read == 0)
return 0;
- // Save the value of file_stream_.get() before base::Passed() invalidates it.
- FileStream* file_stream_ptr = file_stream_.get();
- // Pass the ownership of file_stream_ to the worker pool to safely perform
- // operation even when |this| is destructed before the read completes.
- const bool posted = base::PostTaskAndReplyWithResult(
- task_runner_.get(),
- FROM_HERE,
- base::Bind(&ReadInternal,
- scoped_refptr<IOBuffer>(buf),
- buf_length,
- BytesRemaining(),
- file_stream_ptr),
- base::Bind(&UploadFileElementReader::OnReadCompleted,
+ int result = file_stream_->Read(
+ buf, num_bytes_to_read,
+ base::Bind(base::IgnoreResult(&UploadFileElementReader::OnReadCompleted),
weak_ptr_factory_.GetWeakPtr(),
- base::Passed(&file_stream_),
callback));
- DCHECK(posted);
+ // Even in async mode, FileStream::Read() may return the result synchronously.
+ if (result != ERR_IO_PENDING)
+ return OnReadCompleted(CompletionCallback(), result);
return ERR_IO_PENDING;
}
@@ -212,29 +100,105 @@ void UploadFileElementReader::Reset() {
file_stream_.reset();
}
-void UploadFileElementReader::OnInitCompleted(
- ScopedFileStreamPtr* file_stream,
- uint64* content_length,
+void UploadFileElementReader::OnOpenCompleted(
const CompletionCallback& callback,
int result) {
- file_stream_.swap(*file_stream);
- content_length_ = *content_length;
- bytes_remaining_ = GetContentLength();
- if (!callback.is_null())
+ DCHECK(!callback.is_null());
+
+ if (result < 0) {
+ DLOG(WARNING) << "Failed to open \"" << path_.value()
+ << "\" for reading: " << result;
+ callback.Run(result);
+ return;
+ }
+
+ if (range_offset_) {
+ int result = file_stream_->Seek(
+ FROM_BEGIN, range_offset_,
+ base::Bind(&UploadFileElementReader::OnSeekCompleted,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+ DCHECK_GT(0, result);
+ if (result != ERR_IO_PENDING)
+ callback.Run(result);
+ } else {
+ OnSeekCompleted(callback, OK);
+ }
+}
+
+void UploadFileElementReader::OnSeekCompleted(
+ const CompletionCallback& callback,
+ int64 result) {
+ DCHECK(!callback.is_null());
+
+ if (result < 0) {
+ DLOG(WARNING) << "Failed to seek \"" << path_.value()
+ << "\" to offset: " << range_offset_ << " (" << result << ")";
callback.Run(result);
+ return;
+ }
+
+ base::File::Info* file_info = new base::File::Info;
+ bool posted = base::PostTaskAndReplyWithResult(
+ task_runner_,
+ FROM_HERE,
+ base::Bind(&base::GetFileInfo,
+ path_,
+ file_info),
+ base::Bind(&UploadFileElementReader::OnGetFileInfoCompleted,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ base::Owned(file_info)));
+ DCHECK(posted);
}
-void UploadFileElementReader::OnReadCompleted(
- ScopedFileStreamPtr file_stream,
+void UploadFileElementReader::OnGetFileInfoCompleted(
+ const CompletionCallback& callback,
+ base::File::Info* file_info,
+ bool result) {
+ DCHECK(!callback.is_null());
+ if (!result) {
+ DLOG(WARNING) << "Failed to get file info of \"" << path_.value() << "\"";
+ callback.Run(ERR_FILE_NOT_FOUND);
+ return;
+ }
+
+ int64 length = file_info->size;
+ if (range_offset_ < static_cast<uint64>(length)) {
+ // Compensate for the offset.
+ length = std::min(length - range_offset_, range_length_);
+ }
+
+ // If the underlying file has been changed and the expected file modification
+ // time is set, treat it as error. Note that the expected modification time
+ // from WebKit is based on time_t precision. So we have to convert both to
+ // time_t to compare. This check is used for sliced files.
+ if (!expected_modification_time_.is_null() &&
+ expected_modification_time_.ToTimeT() !=
+ file_info->last_modified.ToTimeT()) {
+ callback.Run(ERR_UPLOAD_FILE_CHANGED);
+ return;
+ }
+
+ content_length_ = length;
+ bytes_remaining_ = GetContentLength();
+ callback.Run(OK);
+}
+
+int UploadFileElementReader::OnReadCompleted(
const CompletionCallback& callback,
int result) {
- file_stream_.swap(file_stream);
+ if (result == 0) // Reached end-of-file earlier than expected.
+ result = ERR_UPLOAD_FILE_CHANGED;
+
if (result > 0) {
DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
bytes_remaining_ -= result;
}
+
if (!callback.is_null())
callback.Run(result);
+ return result;
}
UploadFileElementReader::ScopedOverridingContentLengthForTests::
@@ -247,52 +211,4 @@ UploadFileElementReader::ScopedOverridingContentLengthForTests::
overriding_content_length = 0;
}
-UploadFileElementReaderSync::UploadFileElementReaderSync(
- const base::FilePath& path,
- uint64 range_offset,
- uint64 range_length,
- const base::Time& expected_modification_time)
- : path_(path),
- range_offset_(range_offset),
- range_length_(range_length),
- expected_modification_time_(expected_modification_time),
- content_length_(0),
- bytes_remaining_(0) {
-}
-
-UploadFileElementReaderSync::~UploadFileElementReaderSync() {
-}
-
-int UploadFileElementReaderSync::Init(const CompletionCallback& callback) {
- bytes_remaining_ = 0;
- content_length_ = 0;
- file_stream_.reset();
-
- const int result = InitInternal(path_, range_offset_, range_length_,
- expected_modification_time_,
- &file_stream_, &content_length_);
- bytes_remaining_ = GetContentLength();
- return result;
-}
-
-uint64 UploadFileElementReaderSync::GetContentLength() const {
- return content_length_;
-}
-
-uint64 UploadFileElementReaderSync::BytesRemaining() const {
- return bytes_remaining_;
-}
-
-int UploadFileElementReaderSync::Read(IOBuffer* buf,
- int buf_length,
- const CompletionCallback& callback) {
- const int result = ReadInternal(buf, buf_length, BytesRemaining(),
- file_stream_.get());
- if (result > 0) {
- DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
- bytes_remaining_ -= result;
- }
- return result;
-}
-
} // namespace net
diff --git a/chromium/net/base/upload_file_element_reader.h b/chromium/net/base/upload_file_element_reader.h
index a805c7a74ee..12427174377 100644
--- a/chromium/net/base/upload_file_element_reader.h
+++ b/chromium/net/base/upload_file_element_reader.h
@@ -6,6 +6,7 @@
#define NET_BASE_UPLOAD_FILE_ELEMENT_READER_H_
#include "base/compiler_specific.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
@@ -50,20 +51,6 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader {
const CompletionCallback& callback) OVERRIDE;
private:
- // Deletes FileStream with |task_runner| to avoid blocking the IO thread.
- // This class is used as a template argument of scoped_ptr.
- class FileStreamDeleter {
- public:
- explicit FileStreamDeleter(base::TaskRunner* task_runner);
- ~FileStreamDeleter();
- void operator() (FileStream* file_stream) const;
-
- private:
- scoped_refptr<base::TaskRunner> task_runner_;
- };
-
- typedef scoped_ptr<FileStream, FileStreamDeleter> ScopedFileStreamPtr;
-
FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, FileSmallerThanLength);
FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest,
UploadFileSmallerThanLength);
@@ -75,16 +62,15 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader {
// Resets this instance to the uninitialized state.
void Reset();
- // This method is used to implement Init().
- void OnInitCompleted(ScopedFileStreamPtr* file_stream,
- uint64* content_length,
- const CompletionCallback& callback,
- int result);
+ // These methods are used to implement Init().
+ void OnOpenCompleted(const CompletionCallback& callback, int result);
+ void OnSeekCompleted(const CompletionCallback& callback, int64 result);
+ void OnGetFileInfoCompleted(const CompletionCallback& callback,
+ base::File::Info* file_info,
+ bool result);
// This method is used to implement Read().
- void OnReadCompleted(ScopedFileStreamPtr file_stream,
- const CompletionCallback& callback,
- int result);
+ int OnReadCompleted(const CompletionCallback& callback, int result);
// Sets an value to override the result for GetContentLength().
// Used for tests.
@@ -98,7 +84,7 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader {
const uint64 range_offset_;
const uint64 range_length_;
const base::Time expected_modification_time_;
- ScopedFileStreamPtr file_stream_;
+ scoped_ptr<FileStream> file_stream_;
uint64 content_length_;
uint64 bytes_remaining_;
base::WeakPtrFactory<UploadFileElementReader> weak_ptr_factory_;
@@ -106,37 +92,6 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader {
DISALLOW_COPY_AND_ASSIGN(UploadFileElementReader);
};
-// An UploadElementReader implementation for file which performs file operation
-// synchronously.
-// Use this class only if the thread is IO allowed.
-class NET_EXPORT UploadFileElementReaderSync : public UploadElementReader {
- public:
- UploadFileElementReaderSync(const base::FilePath& path,
- uint64 range_offset,
- uint64 range_length,
- const base::Time& expected_modification_time);
- virtual ~UploadFileElementReaderSync();
-
- // UploadElementReader overrides:
- virtual int Init(const CompletionCallback& callback) OVERRIDE;
- virtual uint64 GetContentLength() const OVERRIDE;
- virtual uint64 BytesRemaining() const OVERRIDE;
- virtual int Read(IOBuffer* buf,
- int buf_length,
- const CompletionCallback& callback) OVERRIDE;
-
- private:
- const base::FilePath path_;
- const uint64 range_offset_;
- const uint64 range_length_;
- const base::Time expected_modification_time_;
- scoped_ptr<FileStream> file_stream_;
- uint64 content_length_;
- uint64 bytes_remaining_;
-
- DISALLOW_COPY_AND_ASSIGN(UploadFileElementReaderSync);
-};
-
} // namespace net
#endif // NET_BASE_UPLOAD_FILE_ELEMENT_READER_H_
diff --git a/chromium/net/base/upload_file_element_reader_unittest.cc b/chromium/net/base/upload_file_element_reader_unittest.cc
index f3ccb4180f9..395b8673724 100644
--- a/chromium/net/base/upload_file_element_reader_unittest.cc
+++ b/chromium/net/base/upload_file_element_reader_unittest.cc
@@ -30,7 +30,7 @@ class UploadFileElementReaderTest : public PlatformTest {
&temp_file_path_));
ASSERT_EQ(
static_cast<int>(bytes_.size()),
- file_util::WriteFile(temp_file_path_, &bytes_[0], bytes_.size()));
+ base::WriteFile(temp_file_path_, &bytes_[0], bytes_.size()));
reader_.reset(
new UploadFileElementReader(base::MessageLoopProxy::current().get(),
@@ -204,7 +204,7 @@ TEST_F(UploadFileElementReaderTest, Range) {
}
TEST_F(UploadFileElementReaderTest, FileChanged) {
- base::PlatformFileInfo info;
+ base::File::Info info;
ASSERT_TRUE(base::GetFileInfo(temp_file_path_, &info));
// Expect one second before the actual modification time to simulate change.
@@ -234,137 +234,4 @@ TEST_F(UploadFileElementReaderTest, WrongPath) {
EXPECT_EQ(ERR_FILE_NOT_FOUND, init_callback.WaitForResult());
}
-
-class UploadFileElementReaderSyncTest : public PlatformTest {
- protected:
- virtual void SetUp() OVERRIDE {
- // Some tests (*.ReadPartially) rely on bytes_.size() being even.
- const char kData[] = "123456789abcdefghi";
- bytes_.assign(kData, kData + arraysize(kData) - 1);
-
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-
- ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
- &temp_file_path_));
- ASSERT_EQ(
- static_cast<int>(bytes_.size()),
- file_util::WriteFile(temp_file_path_, &bytes_[0], bytes_.size()));
-
- reader_.reset(new UploadFileElementReaderSync(
- temp_file_path_, 0, kuint64max, base::Time()));
- ASSERT_EQ(OK, reader_->Init(CompletionCallback()));
- EXPECT_EQ(bytes_.size(), reader_->GetContentLength());
- EXPECT_EQ(bytes_.size(), reader_->BytesRemaining());
- EXPECT_FALSE(reader_->IsInMemory());
- }
-
- std::vector<char> bytes_;
- scoped_ptr<UploadElementReader> reader_;
- base::ScopedTempDir temp_dir_;
- base::FilePath temp_file_path_;
-};
-
-TEST_F(UploadFileElementReaderSyncTest, ReadPartially) {
- const size_t kHalfSize = bytes_.size() / 2;
- ASSERT_EQ(bytes_.size(), kHalfSize * 2);
- std::vector<char> buf(kHalfSize);
- scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]);
- EXPECT_EQ(
- static_cast<int>(buf.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(bytes_.size() - buf.size(), reader_->BytesRemaining());
- EXPECT_EQ(std::vector<char>(bytes_.begin(), bytes_.begin() + kHalfSize), buf);
-
- EXPECT_EQ(
- static_cast<int>(buf.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(0U, reader_->BytesRemaining());
- EXPECT_EQ(std::vector<char>(bytes_.begin() + kHalfSize, bytes_.end()), buf);
-}
-
-TEST_F(UploadFileElementReaderSyncTest, ReadAll) {
- std::vector<char> buf(bytes_.size());
- scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]);
- EXPECT_EQ(
- static_cast<int>(buf.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(0U, reader_->BytesRemaining());
- EXPECT_EQ(bytes_, buf);
- // Try to read again.
- EXPECT_EQ(
- 0, reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
-}
-
-TEST_F(UploadFileElementReaderSyncTest, ReadTooMuch) {
- const size_t kTooLargeSize = bytes_.size() * 2;
- std::vector<char> buf(kTooLargeSize);
- scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]);
- EXPECT_EQ(
- static_cast<int>(bytes_.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(0U, reader_->BytesRemaining());
- buf.resize(bytes_.size()); // Resize to compare.
- EXPECT_EQ(bytes_, buf);
-}
-
-TEST_F(UploadFileElementReaderSyncTest, MultipleInit) {
- std::vector<char> buf(bytes_.size());
- scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]);
-
- // Read all.
- EXPECT_EQ(
- static_cast<int>(buf.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(0U, reader_->BytesRemaining());
- EXPECT_EQ(bytes_, buf);
-
- // Call Init() again to reset the state.
- ASSERT_EQ(OK, reader_->Init(CompletionCallback()));
- EXPECT_EQ(bytes_.size(), reader_->GetContentLength());
- EXPECT_EQ(bytes_.size(), reader_->BytesRemaining());
-
- // Read again.
- EXPECT_EQ(
- static_cast<int>(buf.size()),
- reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback()));
- EXPECT_EQ(0U, reader_->BytesRemaining());
- EXPECT_EQ(bytes_, buf);
-}
-
-TEST_F(UploadFileElementReaderSyncTest, Range) {
- const uint64 kOffset = 2;
- const uint64 kLength = bytes_.size() - kOffset * 3;
- reader_.reset(new UploadFileElementReaderSync(
- temp_file_path_, kOffset, kLength, base::Time()));
- ASSERT_EQ(OK, reader_->Init(CompletionCallback()));
- EXPECT_EQ(kLength, reader_->GetContentLength());
- EXPECT_EQ(kLength, reader_->BytesRemaining());
- std::vector<char> buf(kLength);
- scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]);
- EXPECT_EQ(static_cast<int>(kLength),
- reader_->Read(wrapped_buffer.get(), kLength, CompletionCallback()));
- const std::vector<char> expected(bytes_.begin() + kOffset,
- bytes_.begin() + kOffset + kLength);
- EXPECT_EQ(expected, buf);
-}
-
-TEST_F(UploadFileElementReaderSyncTest, FileChanged) {
- base::PlatformFileInfo info;
- ASSERT_TRUE(base::GetFileInfo(temp_file_path_, &info));
-
- // Expect one second before the actual modification time to simulate change.
- const base::Time expected_modification_time =
- info.last_modified - base::TimeDelta::FromSeconds(1);
- reader_.reset(new UploadFileElementReaderSync(
- temp_file_path_, 0, kuint64max, expected_modification_time));
- EXPECT_EQ(ERR_UPLOAD_FILE_CHANGED, reader_->Init(CompletionCallback()));
-}
-
-TEST_F(UploadFileElementReaderSyncTest, WrongPath) {
- const base::FilePath wrong_path(FILE_PATH_LITERAL("wrong_path"));
- reader_.reset(new UploadFileElementReaderSync(
- wrong_path, 0, kuint64max, base::Time()));
- ASSERT_EQ(ERR_FILE_NOT_FOUND, reader_->Init(CompletionCallback()));
-}
-
} // namespace net
diff --git a/chromium/net/base/url_util.cc b/chromium/net/base/url_util.cc
index a7c89e9047d..8540e193d3c 100644
--- a/chromium/net/base/url_util.cc
+++ b/chromium/net/base/url_util.cc
@@ -36,11 +36,11 @@ GURL AppendOrReplaceQueryParameter(const GURL& url,
std::string param_value = EscapeQueryParamValue(value, true);
const std::string input = url.query();
- url_parse::Component cursor(0, input.size());
+ url::Component cursor(0, input.size());
std::string output;
- url_parse::Component key_range, value_range;
- while (url_parse::ExtractQueryKeyValue(
- input.data(), &cursor, &key_range, &value_range)) {
+ url::Component key_range, value_range;
+ while (url::ExtractQueryKeyValue(input.data(), &cursor, &key_range,
+ &value_range)) {
const base::StringPiece key(
input.data() + key_range.begin, key_range.len);
const base::StringPiece value(
@@ -118,10 +118,8 @@ void QueryIterator::Advance() {
key_.reset();
value_.reset();
unescaped_value_.clear();
- at_end_ = !url_parse::ExtractQueryKeyValue(url_.spec().c_str(),
- &query_,
- &key_,
- &value_);
+ at_end_ =
+ !url::ExtractQueryKeyValue(url_.spec().c_str(), &query_, &key_, &value_);
}
bool GetValueForKeyInQuery(const GURL& url,
diff --git a/chromium/net/base/url_util.h b/chromium/net/base/url_util.h
index 83d03562f86..362be873c11 100644
--- a/chromium/net/base/url_util.h
+++ b/chromium/net/base/url_util.h
@@ -61,10 +61,10 @@ class NET_EXPORT QueryIterator {
private:
const GURL& url_;
- url_parse::Component query_;
+ url::Component query_;
bool at_end_;
- url_parse::Component key_;
- url_parse::Component value_;
+ url::Component key_;
+ url::Component value_;
std::string unescaped_value_;
DISALLOW_COPY_AND_ASSIGN(QueryIterator);
diff --git a/chromium/net/cert/cert_database.cc b/chromium/net/cert/cert_database.cc
index f36562aadf7..d6a9b1b77fc 100644
--- a/chromium/net/cert/cert_database.cc
+++ b/chromium/net/cert/cert_database.cc
@@ -11,7 +11,9 @@ namespace net {
// static
CertDatabase* CertDatabase::GetInstance() {
- return Singleton<CertDatabase>::get();
+ // Leaky so it can be initialized on worker threads, and because there is no
+ // useful cleanup to do.
+ return Singleton<CertDatabase, LeakySingletonTraits<CertDatabase> >::get();
}
void CertDatabase::AddObserver(Observer* observer) {
diff --git a/chromium/net/cert/cert_database.h b/chromium/net/cert/cert_database.h
index feadf4c1d08..865d685097d 100644
--- a/chromium/net/cert/cert_database.h
+++ b/chromium/net/cert/cert_database.h
@@ -16,6 +16,8 @@ template <class ObserverType> class ObserverListThreadSafe;
namespace net {
+class NSSCertDatabase;
+
// This class provides cross-platform functions to verify and add user
// certificates, and to observe changes to the underlying certificate stores.
@@ -80,11 +82,23 @@ class NET_EXPORT CertDatabase {
#endif
#if defined(OS_ANDROID)
- // On android, the system database is used. When the system notifies the
+ // On Android, the system key store may be replaced with a device-specific
+ // KeyStore used for storing client certificates. When the Java side replaces
+ // the KeyStore used for client certificates, notifies the observers as if a
+ // new client certificate was added.
+ void OnAndroidKeyStoreChanged();
+
+ // On Android, the system database is used. When the system notifies the
// application that the certificates changed, the observers must be notified.
void OnAndroidKeyChainChanged();
#endif
+#if defined(USE_NSS)
+ // Observe events from the |source| and forward them to observers of this
+ // CertDatabase.
+ void ObserveNSSCertDatabase(NSSCertDatabase* source);
+#endif
+
private:
friend struct DefaultSingletonTraits<CertDatabase>;
diff --git a/chromium/net/cert/cert_database_android.cc b/chromium/net/cert/cert_database_android.cc
index 350028fc753..890f4717db2 100644
--- a/chromium/net/cert/cert_database_android.cc
+++ b/chromium/net/cert/cert_database_android.cc
@@ -36,6 +36,10 @@ int CertDatabase::AddUserCert(X509Certificate* cert) {
return ERR_NOT_IMPLEMENTED;
}
+void CertDatabase::OnAndroidKeyStoreChanged() {
+ NotifyObserversOfCertAdded(NULL);
+}
+
void CertDatabase::OnAndroidKeyChainChanged() {
observer_list_->Notify(&Observer::OnCACertChanged,
scoped_refptr<X509Certificate>());
diff --git a/chromium/net/cert/cert_database_mac.cc b/chromium/net/cert/cert_database_mac.cc
index 9427be316d2..3f405f33a69 100644
--- a/chromium/net/cert/cert_database_mac.cc
+++ b/chromium/net/cert/cert_database_mac.cc
@@ -31,7 +31,7 @@ class CertDatabase::Notifier {
registered_(false),
called_shutdown_(false) {
// Ensure an associated CFRunLoop.
- DCHECK(message_loop->IsType(base::MessageLoop::TYPE_UI));
+ DCHECK(base::MessageLoopForUI::IsCurrent());
task_runner_ = message_loop->message_loop_proxy();
task_runner_->PostTask(FROM_HERE,
base::Bind(&Notifier::Init,
diff --git a/chromium/net/cert/cert_database_nss.cc b/chromium/net/cert/cert_database_nss.cc
index a1677fe85a1..9819dded6cb 100644
--- a/chromium/net/cert/cert_database_nss.cc
+++ b/chromium/net/cert/cert_database_nss.cc
@@ -23,13 +23,9 @@ namespace net {
// the given CertDatabase.
class CertDatabase::Notifier : public NSSCertDatabase::Observer {
public:
- explicit Notifier(CertDatabase* cert_db) : cert_db_(cert_db) {
- NSSCertDatabase::GetInstance()->AddObserver(this);
- }
+ explicit Notifier(CertDatabase* cert_db) : cert_db_(cert_db) {}
- virtual ~Notifier() {
- NSSCertDatabase::GetInstance()->RemoveObserver(this);
- }
+ virtual ~Notifier() {}
// NSSCertDatabase::Observer implementation:
virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
@@ -51,10 +47,9 @@ class CertDatabase::Notifier : public NSSCertDatabase::Observer {
};
CertDatabase::CertDatabase()
- : observer_list_(new ObserverListThreadSafe<Observer>) {
- // Observe NSSCertDatabase events and forward them to observers of
- // CertDatabase. This also makes sure that NSS has been initialized.
- notifier_.reset(new Notifier(this));
+ : observer_list_(new ObserverListThreadSafe<Observer>),
+ notifier_(new Notifier(this)) {
+ crypto::EnsureNSSInit();
}
CertDatabase::~CertDatabase() {}
@@ -109,4 +104,8 @@ int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
return OK;
}
+void CertDatabase::ObserveNSSCertDatabase(NSSCertDatabase* source) {
+ source->AddObserver(this->notifier_.get());
+}
+
} // namespace net
diff --git a/chromium/net/cert/cert_status_flags.cc b/chromium/net/cert/cert_status_flags.cc
index 417a1833fd7..d278ea47b45 100644
--- a/chromium/net/cert/cert_status_flags.cc
+++ b/chromium/net/cert/cert_status_flags.cc
@@ -41,12 +41,12 @@ CertStatus MapNetErrorToCertStatus(int error) {
return CERT_STATUS_INVALID;
case ERR_CERT_WEAK_SIGNATURE_ALGORITHM:
return CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
+ case ERR_CERT_NON_UNIQUE_NAME:
+ return CERT_STATUS_NON_UNIQUE_NAME;
case ERR_CERT_WEAK_KEY:
return CERT_STATUS_WEAK_KEY;
case ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN:
return CERT_STATUS_PINNED_KEY_MISSING;
- case ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY:
- return CERT_STATUS_WEAK_DH_KEY;
case ERR_CERT_NAME_CONSTRAINT_VIOLATION:
return CERT_STATUS_NAME_CONSTRAINT_VIOLATION;
default:
@@ -65,14 +65,14 @@ int MapCertStatusToNetError(CertStatus cert_status) {
return ERR_CERT_INVALID;
if (cert_status & CERT_STATUS_PINNED_KEY_MISSING)
return ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
- if (cert_status & CERT_STATUS_WEAK_DH_KEY)
- return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
// Recoverable errors
if (cert_status & CERT_STATUS_AUTHORITY_INVALID)
return ERR_CERT_AUTHORITY_INVALID;
if (cert_status & CERT_STATUS_COMMON_NAME_INVALID)
return ERR_CERT_COMMON_NAME_INVALID;
+ // CERT_STATUS_NON_UNIQUE_NAME is intentionally not mapped to an error.
+ // It is treated as just a warning and used to degrade the SSL UI.
if (cert_status & CERT_STATUS_NAME_CONSTRAINT_VIOLATION)
return ERR_CERT_NAME_CONSTRAINT_VIOLATION;
if (cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM)
diff --git a/chromium/net/cert/cert_status_flags.h b/chromium/net/cert/cert_status_flags.h
index 711474ff2b0..e66856beae1 100644
--- a/chromium/net/cert/cert_status_flags.h
+++ b/chromium/net/cert/cert_status_flags.h
@@ -14,32 +14,15 @@ namespace net {
// other non-error status information such as whether the certificate is EV.
typedef uint32 CertStatus;
-// The possible status bits for CertStatus.
// NOTE: Because these names have appeared in bug reports, we preserve them as
// MACRO_STYLE for continuity, instead of renaming them to kConstantStyle as
// befits most static consts.
-// Bits 0 to 15 are for errors.
-static const CertStatus CERT_STATUS_ALL_ERRORS = 0xFFFF;
-static const CertStatus CERT_STATUS_COMMON_NAME_INVALID = 1 << 0;
-static const CertStatus CERT_STATUS_DATE_INVALID = 1 << 1;
-static const CertStatus CERT_STATUS_AUTHORITY_INVALID = 1 << 2;
-// 1 << 3 is reserved for ERR_CERT_CONTAINS_ERRORS (not useful with WinHTTP).
-static const CertStatus CERT_STATUS_NO_REVOCATION_MECHANISM = 1 << 4;
-static const CertStatus CERT_STATUS_UNABLE_TO_CHECK_REVOCATION = 1 << 5;
-static const CertStatus CERT_STATUS_REVOKED = 1 << 6;
-static const CertStatus CERT_STATUS_INVALID = 1 << 7;
-static const CertStatus CERT_STATUS_WEAK_SIGNATURE_ALGORITHM = 1 << 8;
-// 1 << 9 was used for CERT_STATUS_NOT_IN_DNS
-static const CertStatus CERT_STATUS_NON_UNIQUE_NAME = 1 << 10;
-static const CertStatus CERT_STATUS_WEAK_KEY = 1 << 11;
-static const CertStatus CERT_STATUS_WEAK_DH_KEY = 1 << 12;
-static const CertStatus CERT_STATUS_PINNED_KEY_MISSING = 1 << 13;
-static const CertStatus CERT_STATUS_NAME_CONSTRAINT_VIOLATION = 1 << 14;
+#define CERT_STATUS_FLAG(label, value) \
+ CertStatus static const CERT_STATUS_##label = value;
+#include "net/cert/cert_status_flags_list.h"
+#undef CERT_STATUS_FLAG
-// Bits 16 to 31 are for non-error statuses.
-static const CertStatus CERT_STATUS_IS_EV = 1 << 16;
-static const CertStatus CERT_STATUS_REV_CHECKING_ENABLED = 1 << 17;
-// bit 18 was CERT_STATUS_IS_DNSSEC.
+static const CertStatus CERT_STATUS_ALL_ERRORS = 0xFFFF;
// Returns true if the specified cert status has an error set.
static inline bool IsCertStatusError(CertStatus status) {
diff --git a/chromium/net/cert/cert_status_flags_list.h b/chromium/net/cert/cert_status_flags_list.h
new file mode 100644
index 00000000000..a0fda43aeb5
--- /dev/null
+++ b/chromium/net/cert/cert_status_flags_list.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+// This is the list of CertStatus flags and their values.
+//
+// Defines the values using a macro CERT_STATUS_FLAG,
+// so it can be expanded differently in some places
+
+// The possible status bits for CertStatus.
+// Bits 0 to 15 are for errors.
+CERT_STATUS_FLAG(COMMON_NAME_INVALID, 1 << 0);
+CERT_STATUS_FLAG(DATE_INVALID, 1 << 1);
+CERT_STATUS_FLAG(AUTHORITY_INVALID, 1 << 2);
+// 1 << 3 is reserved for ERR_CERT_CONTAINS_ERRORS (not useful with WinHTTP).
+CERT_STATUS_FLAG(NO_REVOCATION_MECHANISM, 1 << 4);
+CERT_STATUS_FLAG(UNABLE_TO_CHECK_REVOCATION, 1 << 5);
+CERT_STATUS_FLAG(REVOKED, 1 << 6);
+CERT_STATUS_FLAG(INVALID, 1 << 7);
+CERT_STATUS_FLAG(WEAK_SIGNATURE_ALGORITHM, 1 << 8);
+// 1 << 9 was used for CERT_STATUS_NOT_IN_DNS
+CERT_STATUS_FLAG(NON_UNIQUE_NAME, 1 << 10);
+CERT_STATUS_FLAG(WEAK_KEY, 1 << 11);
+// 1 << 12 was used for CERT_STATUS_WEAK_DH_KEY
+CERT_STATUS_FLAG(PINNED_KEY_MISSING, 1 << 13);
+CERT_STATUS_FLAG(NAME_CONSTRAINT_VIOLATION, 1 << 14);
+
+// Bits 16 to 31 are for non-error statuses.
+CERT_STATUS_FLAG(IS_EV, 1 << 16);
+CERT_STATUS_FLAG(REV_CHECKING_ENABLED, 1 << 17);
+// Bit 18 was CERT_STATUS_IS_DNSSEC
diff --git a/chromium/net/cert/cert_verify_proc.cc b/chromium/net/cert/cert_verify_proc.cc
index 6c2d5d91c4d..f598acc6998 100644
--- a/chromium/net/cert/cert_verify_proc.cc
+++ b/chromium/net/cert/cert_verify_proc.cc
@@ -4,6 +4,7 @@
#include "net/cert/cert_verify_proc.h"
+#include "base/basictypes.h"
#include "base/metrics/histogram.h"
#include "base/sha1.h"
#include "base/strings/stringprintf.h"
@@ -20,7 +21,7 @@
#if defined(USE_NSS) || defined(OS_IOS)
#include "net/cert/cert_verify_proc_nss.h"
-#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
#include "net/cert/cert_verify_proc_openssl.h"
#elif defined(OS_ANDROID)
#include "net/cert/cert_verify_proc_android.h"
@@ -167,7 +168,7 @@ bool ExaminePublicKeys(const scoped_refptr<X509Certificate>& cert,
CertVerifyProc* CertVerifyProc::CreateDefault() {
#if defined(USE_NSS) || defined(OS_IOS)
return new CertVerifyProcNSS();
-#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
return new CertVerifyProcOpenSSL();
#elif defined(OS_ANDROID)
return new CertVerifyProcAndroid();
@@ -261,19 +262,16 @@ int CertVerifyProc::Verify(X509Certificate* cert,
rv = MapCertStatusToNetError(verify_result->cert_status);
}
-#if !defined(OS_ANDROID)
// Flag certificates from publicly-trusted CAs that are issued to intranet
// hosts. While the CA/Browser Forum Baseline Requirements (v1.1) permit
// these to be issued until 1 November 2015, they represent a real risk for
// the deployment of gTLDs and are being phased out ahead of the hard
// deadline.
- //
- // TODO(ppi): is_issued_by_known_root is incorrect on Android. Once this is
- // fixed, re-enable this check for Android. crbug.com/116838
if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) {
verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME;
+ // CERT_STATUS_NON_UNIQUE_NAME will eventually become a hard error. For
+ // now treat it as a warning and do not map it to an error return value.
}
-#endif
return rv;
}
@@ -344,6 +342,25 @@ bool CertVerifyProc::IsBlacklisted(X509Certificate* cert) {
}
}
+ // CloudFlare revoked all certificates issued prior to April 2nd, 2014. Thus
+ // all certificates where the CN ends with ".cloudflare.com" with a prior
+ // issuance date are rejected.
+ //
+ // The old certs had a lifetime of five years, so this can be removed April
+ // 2nd, 2019.
+ const std::string& cn = cert->subject().common_name;
+ static const char kCloudFlareCNSuffix[] = ".cloudflare.com";
+ // kCloudFlareEpoch is the base::Time internal value for midnight at the
+ // beginning of April 2nd, 2014, UTC.
+ static const int64 kCloudFlareEpoch = INT64_C(13040870400000000);
+ if (cn.size() > arraysize(kCloudFlareCNSuffix) - 1 &&
+ cn.compare(cn.size() - (arraysize(kCloudFlareCNSuffix) - 1),
+ arraysize(kCloudFlareCNSuffix) - 1,
+ kCloudFlareCNSuffix) == 0 &&
+ cert->valid_start() < base::Time::FromInternalValue(kCloudFlareEpoch)) {
+ return true;
+ }
+
return false;
}
@@ -351,7 +368,7 @@ bool CertVerifyProc::IsBlacklisted(X509Certificate* cert) {
// NOTE: This implementation assumes and enforces that the hashes are SHA1.
bool CertVerifyProc::IsPublicKeyBlacklisted(
const HashValueVector& public_key_hashes) {
- static const unsigned kNumHashes = 11;
+ static const unsigned kNumHashes = 17;
static const uint8 kHashes[kNumHashes][base::kSHA1Length] = {
// Subject: CN=DigiNotar Root CA
// Issuer: CN=Entrust.net x2 and self-signed
@@ -400,6 +417,30 @@ bool CertVerifyProc::IsPublicKeyBlacklisted(
// Expires: Jul 18 10:05:28 2014 GMT
{0x3e, 0xcf, 0x4b, 0xbb, 0xe4, 0x60, 0x96, 0xd5, 0x14, 0xbb,
0x53, 0x9b, 0xb9, 0x13, 0xd7, 0x7a, 0xa4, 0xef, 0x31, 0xbf},
+ // Three retired intermediate certificates from Symantec. No compromise;
+ // just for robustness. All expire May 17 23:59:59 2018.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=966060
+ {0x68, 0x5e, 0xec, 0x0a, 0x39, 0xf6, 0x68, 0xae, 0x8f, 0xd8,
+ 0x96, 0x4f, 0x98, 0x74, 0x76, 0xb4, 0x50, 0x4f, 0xd2, 0xbe},
+ {0x0e, 0x50, 0x2d, 0x4d, 0xd1, 0xe1, 0x60, 0x36, 0x8a, 0x31,
+ 0xf0, 0x6a, 0x81, 0x04, 0x31, 0xba, 0x6f, 0x72, 0xc0, 0x41},
+ {0x93, 0xd1, 0x53, 0x22, 0x29, 0xcc, 0x2a, 0xbd, 0x21, 0xdf,
+ 0xf5, 0x97, 0xee, 0x32, 0x0f, 0xe4, 0x24, 0x6f, 0x3d, 0x0c},
+ // C=IN, O=National Informatics Centre, OU=NICCA, CN=NIC Certifying
+ // Authority. Issued by C=IN, O=India PKI, CN=CCA India 2007.
+ // Expires July 4th, 2015.
+ {0xf5, 0x71, 0x79, 0xfa, 0xea, 0x10, 0xc5, 0x43, 0x8c, 0xb0,
+ 0xc6, 0xe1, 0xcc, 0x27, 0x7b, 0x6e, 0x0d, 0xb2, 0xff, 0x54},
+ // C=IN, O=National Informatics Centre, CN=NIC CA 2011. Issued by
+ // C=IN, O=India PKI, CN=CCA India 2011.
+ // Expires March 11th 2016.
+ {0x07, 0x7a, 0xc7, 0xde, 0x8d, 0xa5, 0x58, 0x64, 0x3a, 0x06,
+ 0xc5, 0x36, 0x9e, 0x55, 0x4f, 0xae, 0xb3, 0xdf, 0xa1, 0x66},
+ // C=IN, O=National Informatics Centre, CN=NIC CA 2014. Issued by
+ // C=IN, O=India PKI, CN=CCA India 2014.
+ // Expires: March 5th, 2024.
+ {0xe5, 0x8e, 0x31, 0x5b, 0xaa, 0xee, 0xaa, 0xc6, 0xe7, 0x2e,
+ 0xc9, 0x57, 0x36, 0x70, 0xca, 0x2f, 0x25, 0x4e, 0xc3, 0x47},
};
for (unsigned i = 0; i < kNumHashes; i++) {
@@ -425,7 +466,7 @@ static bool CheckNameConstraints(const std::vector<std::string>& dns_names,
for (std::vector<std::string>::const_iterator i = dns_names.begin();
i != dns_names.end(); ++i) {
bool ok = false;
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
const std::string dns_name = CanonicalizeHost(*i, &host_info);
if (host_info.IsIPAddress())
continue;
diff --git a/chromium/net/cert/cert_verify_proc_android.cc b/chromium/net/cert/cert_verify_proc_android.cc
index bca62bc70c4..bd747267a2e 100644
--- a/chromium/net/cert/cert_verify_proc_android.cc
+++ b/chromium/net/cert/cert_verify_proc_android.cc
@@ -8,9 +8,13 @@
#include <vector>
#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+#include "crypto/sha2.h"
#include "net/android/cert_verify_result_android.h"
#include "net/android/network_library.h"
#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/x509_certificate.h"
@@ -22,11 +26,16 @@ namespace {
// Returns true if the certificate verification call was successful (regardless
// of its result), i.e. if |verify_result| was set. Otherwise returns false.
bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
+ const std::string& hostname,
CertVerifyResult* verify_result) {
+ android::CertVerifyStatusAndroid status;
+ std::vector<std::string> verified_chain;
+
// TODO(joth): Fetch the authentication type from SSL rather than hardcode.
- android::CertVerifyResultAndroid android_result =
- android::VerifyX509CertChain(cert_bytes, "RSA");
- switch (android_result) {
+ android::VerifyX509CertChain(cert_bytes, "RSA", hostname,
+ &status, &verify_result->is_issued_by_known_root,
+ &verified_chain);
+ switch (status) {
case android::VERIFY_FAILED:
return false;
case android::VERIFY_OK:
@@ -49,6 +58,35 @@ bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
verify_result->cert_status |= CERT_STATUS_INVALID;
break;
}
+
+ // Save the verified chain.
+ if (!verified_chain.empty()) {
+ std::vector<base::StringPiece> verified_chain_pieces(verified_chain.size());
+ for (size_t i = 0; i < verified_chain.size(); i++) {
+ verified_chain_pieces[i] = base::StringPiece(verified_chain[i]);
+ }
+ scoped_refptr<X509Certificate> verified_cert =
+ X509Certificate::CreateFromDERCertChain(verified_chain_pieces);
+ if (verified_cert)
+ verify_result->verified_cert = verified_cert;
+ }
+
+ // Extract the public key hashes.
+ for (size_t i = 0; i < verified_chain.size(); i++) {
+ base::StringPiece spki_bytes;
+ if (!asn1::ExtractSPKIFromDERCert(verified_chain[i], &spki_bytes))
+ continue;
+
+ HashValue sha1(HASH_VALUE_SHA1);
+ base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()),
+ spki_bytes.size(), sha1.data());
+ verify_result->public_key_hashes.push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
+ verify_result->public_key_hashes.push_back(sha256);
+ }
+
return true;
}
@@ -99,23 +137,13 @@ int CertVerifyProcAndroid::VerifyInternal(
std::vector<std::string> cert_bytes;
if (!GetChainDEREncodedBytes(cert, &cert_bytes))
return ERR_CERT_INVALID;
- if (!VerifyFromAndroidTrustManager(cert_bytes, verify_result)) {
+ if (!VerifyFromAndroidTrustManager(cert_bytes, hostname, verify_result)) {
NOTREACHED();
return ERR_FAILED;
}
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
- // TODO(ppi): Implement missing functionality: yielding the constructed trust
- // chain, public key hashes of its certificates and |is_issued_by_known_root|
- // flag. All of the above require specific support from the platform, missing
- // in the Java APIs. See also: http://crbug.com/116838
-
- // Until the required support is available in the platform, we don't know if
- // the trust root at the end of the chain was standard or user-added, so we
- // mark all correctly verified certificates as issued by a known root.
- verify_result->is_issued_by_known_root = true;
-
return OK;
}
diff --git a/chromium/net/cert/cert_verify_proc_mac.cc b/chromium/net/cert/cert_verify_proc_mac.cc
index 542fba11514..97c0bf13de6 100644
--- a/chromium/net/cert/cert_verify_proc_mac.cc
+++ b/chromium/net/cert/cert_verify_proc_mac.cc
@@ -18,7 +18,6 @@
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "crypto/mac_security_services_lock.h"
-#include "crypto/nss_util.h"
#include "crypto/sha2.h"
#include "net/base/net_errors.h"
#include "net/cert/asn1_util.h"
@@ -578,7 +577,7 @@ int CertVerifyProcMac::VerifyInternal(
// the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only
// error was due to an unsupported key size.
bool policy_failed = false;
- bool weak_key = false;
+ bool weak_key_or_signature_algorithm = false;
// Evaluate the results
OSStatus cssm_result;
@@ -622,14 +621,31 @@ int CertVerifyProcMac::VerifyInternal(
for (uint32 status_code_index = 0;
status_code_index < chain_info[index].NumStatusCodes;
++status_code_index) {
- CertStatus mapped_status = CertStatusFromOSStatus(
- chain_info[index].StatusCodes[status_code_index]);
- if (mapped_status == CERT_STATUS_WEAK_KEY)
- weak_key = true;
+ // As of OS X 10.9, attempting to verify a certificate chain that
+ // contains a weak signature algorithm (MD2, MD5) in an intermediate
+ // or leaf cert will be treated as a (recoverable) policy validation
+ // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE
+ // added to the Status Codes. Don't treat this code as an invalid
+ // certificate; instead, map it to a weak key. Any truly invalid
+ // certificates will have the major error (cssm_result) set to
+ // CSSMERR_TP_INVALID_CERTIFICATE, rather than
+ // CSSMERR_TP_VERIFY_ACTION_FAILED.
+ CertStatus mapped_status = 0;
+ if (policy_failed &&
+ chain_info[index].StatusCodes[status_code_index] ==
+ CSSMERR_TP_INVALID_CERTIFICATE) {
+ mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
+ weak_key_or_signature_algorithm = true;
+ } else {
+ mapped_status = CertStatusFromOSStatus(
+ chain_info[index].StatusCodes[status_code_index]);
+ if (mapped_status == CERT_STATUS_WEAK_KEY)
+ weak_key_or_signature_algorithm = true;
+ }
verify_result->cert_status |= mapped_status;
}
}
- if (policy_failed && !weak_key) {
+ if (policy_failed && !weak_key_or_signature_algorithm) {
// If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak
// key, map it back to an appropriate error code.
verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
diff --git a/chromium/net/cert/cert_verify_proc_nss.cc b/chromium/net/cert/cert_verify_proc_nss.cc
index c401eba7837..9d3652b8c59 100644
--- a/chromium/net/cert/cert_verify_proc_nss.cc
+++ b/chromium/net/cert/cert_verify_proc_nss.cc
@@ -33,28 +33,17 @@
#include "net/cert/x509_util_ios.h"
#endif // defined(OS_IOS)
-#define NSS_VERSION_NUM (NSS_VMAJOR * 10000 + NSS_VMINOR * 100 + NSS_VPATCH)
-#if NSS_VERSION_NUM < 31305
-// Added in NSS 3.13.5.
-#define SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED -8016
-#endif
-
-#if NSS_VERSION_NUM < 31402
-// Added in NSS 3.14.2.
-#define cert_pi_useOnlyTrustAnchors static_cast<CERTValParamInType>(14)
-#endif
-
namespace net {
namespace {
-typedef scoped_ptr_malloc<
+typedef scoped_ptr<
CERTCertificatePolicies,
crypto::NSSDestroyer<CERTCertificatePolicies,
CERT_DestroyCertificatePoliciesExtension> >
ScopedCERTCertificatePolicies;
-typedef scoped_ptr_malloc<
+typedef scoped_ptr<
CERTCertList,
crypto::NSSDestroyer<CERTCertList, CERT_DestroyCertList> >
ScopedCERTCertList;
@@ -119,6 +108,8 @@ int MapSecurityError(int err) {
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_UNTRUSTED_ISSUER:
case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_APPLICATION_CALLBACK_ERROR: // Rejected by
+ // chain_verify_callback.
return ERR_CERT_AUTHORITY_INVALID;
// TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE:
@@ -360,6 +351,7 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle,
const SECOidTag* policy_oids,
int num_policy_oids,
CERTCertList* additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
CERTValOutParam* cvout) {
bool use_crl = check_revocation;
bool use_ocsp = check_revocation;
@@ -449,6 +441,11 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle,
in_param.value.scalar.b = PR_FALSE;
cvin.push_back(in_param);
}
+ if (chain_verify_callback) {
+ in_param.type = cert_pi_chainVerifyCallback;
+ in_param.value.pointer.chainVerifyCallback = chain_verify_callback;
+ cvin.push_back(in_param);
+ }
in_param.type = cert_pi_end;
cvin.push_back(in_param);
@@ -669,7 +666,8 @@ bool VerifyEV(CERTCertificate* cert_handle,
bool rev_checking_enabled,
EVRootCAMetadata* metadata,
SECOidTag ev_policy_oid,
- CERTCertList* additional_trust_anchors) {
+ CERTCertList* additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback) {
CERTValOutParam cvout[3];
int cvout_index = 0;
cvout[cvout_index].type = cert_po_certList;
@@ -691,6 +689,7 @@ bool VerifyEV(CERTCertificate* cert_handle,
&ev_policy_oid,
1,
additional_trust_anchors,
+ chain_verify_callback,
cvout);
if (status != SECSuccess)
return false;
@@ -744,16 +743,16 @@ CertVerifyProcNSS::CertVerifyProcNSS() {}
CertVerifyProcNSS::~CertVerifyProcNSS() {}
bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
- // This requires APIs introduced in 3.14.2.
- return NSS_VersionCheck("3.14.2");
+ return true;
}
-int CertVerifyProcNSS::VerifyInternal(
+int CertVerifyProcNSS::VerifyInternalImpl(
X509Certificate* cert,
const std::string& hostname,
int flags,
CRLSet* crl_set,
const CertificateList& additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
CertVerifyResult* verify_result) {
#if defined(OS_IOS)
// For iOS, the entire chain must be loaded into NSS's in-memory certificate
@@ -801,14 +800,20 @@ int CertVerifyProcNSS::VerifyInternal(
verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
ScopedCERTCertList trust_anchors;
- if (SupportsAdditionalTrustAnchors() && !additional_trust_anchors.empty()) {
+ if (!additional_trust_anchors.empty()) {
trust_anchors.reset(
CertificateListToCERTCertList(additional_trust_anchors));
}
- SECStatus status = PKIXVerifyCert(cert_handle, check_revocation, false,
- cert_io_enabled, NULL, 0,
- trust_anchors.get(), cvout);
+ SECStatus status = PKIXVerifyCert(cert_handle,
+ check_revocation,
+ false,
+ cert_io_enabled,
+ NULL,
+ 0,
+ trust_anchors.get(),
+ chain_verify_callback,
+ cvout);
if (status == SECSuccess &&
(flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) &&
@@ -818,8 +823,14 @@ int CertVerifyProcNSS::VerifyInternal(
// NSS tests for that feature.
scoped_cvout.Clear();
verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
- status = PKIXVerifyCert(cert_handle, true, true,
- cert_io_enabled, NULL, 0, trust_anchors.get(),
+ status = PKIXVerifyCert(cert_handle,
+ true,
+ true,
+ cert_io_enabled,
+ NULL,
+ 0,
+ trust_anchors.get(),
+ chain_verify_callback,
cvout);
}
@@ -881,8 +892,14 @@ int CertVerifyProcNSS::VerifyInternal(
if (check_revocation)
verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
- if (VerifyEV(cert_handle, flags, crl_set, check_revocation, metadata,
- ev_policy_oid, trust_anchors.get())) {
+ if (VerifyEV(cert_handle,
+ flags,
+ crl_set,
+ check_revocation,
+ metadata,
+ ev_policy_oid,
+ trust_anchors.get(),
+ chain_verify_callback)) {
verify_result->cert_status |= CERT_STATUS_IS_EV;
}
}
@@ -890,4 +907,20 @@ int CertVerifyProcNSS::VerifyInternal(
return OK;
}
+int CertVerifyProcNSS::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ return VerifyInternalImpl(cert,
+ hostname,
+ flags,
+ crl_set,
+ additional_trust_anchors,
+ NULL, // chain_verify_callback
+ verify_result);
+}
+
} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_nss.h b/chromium/net/cert/cert_verify_proc_nss.h
index f8bb853544e..d9971cf34c8 100644
--- a/chromium/net/cert/cert_verify_proc_nss.h
+++ b/chromium/net/cert/cert_verify_proc_nss.h
@@ -5,6 +5,8 @@
#ifndef NET_CERT_CERT_VERIFY_PROC_NSS_H_
#define NET_CERT_CERT_VERIFY_PROC_NSS_H_
+#include <certt.h>
+
#include "net/base/net_export.h"
#include "net/cert/cert_verify_proc.h"
@@ -20,6 +22,17 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc {
protected:
virtual ~CertVerifyProcNSS();
+ // Like VerifyInternal, but adds a |chain_verify_callback| to override trust
+ // decisions. See the documentation for CERTChainVerifyCallback and
+ // CERTChainVerifyCallbackFunc in NSS's lib/certdb/certt.h.
+ int VerifyInternalImpl(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
+ CertVerifyResult* verify_result);
+
private:
virtual int VerifyInternal(X509Certificate* cert,
const std::string& hostname,
diff --git a/chromium/net/cert/cert_verify_proc_openssl.cc b/chromium/net/cert/cert_verify_proc_openssl.cc
index 906e4cb484e..0f21daa13f3 100644
--- a/chromium/net/cert/cert_verify_proc_openssl.cc
+++ b/chromium/net/cert/cert_verify_proc_openssl.cc
@@ -18,6 +18,7 @@
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
+#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
namespace net {
@@ -121,9 +122,28 @@ void GetCertChainInfo(X509_STORE_CTX* store_ctx,
}
}
+ // Set verify_result->verified_cert and
+ // verify_result->is_issued_by_known_root.
if (verified_cert) {
verify_result->verified_cert =
X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+
+ // For OpenSSL builds, only certificates used for unit tests are treated
+ // as not issued by known roots. The only way to determine whether a
+ // certificate is issued by a known root using OpenSSL is to examine
+ // distro-and-release specific hardcoded lists.
+ verify_result->is_issued_by_known_root = true;
+ if (TestRootCerts::HasInstance()) {
+ X509* root = NULL;
+ if (verified_chain.empty()) {
+ root = verified_cert;
+ } else {
+ root = verified_chain.back();
+ }
+ TestRootCerts* root_certs = TestRootCerts::GetInstance();
+ if (root_certs->Contains(root))
+ verify_result->is_issued_by_known_root = false;
+ }
}
}
@@ -214,14 +234,6 @@ int CertVerifyProcOpenSSL::VerifyInternal(
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
- // Currently we only ues OpenSSL's default root CA paths, so treat all
- // correctly verified certs as being from a known root.
- // TODO(joth): if the motivations described in
- // http://src.chromium.org/viewvc/chrome?view=rev&revision=80778 become an
- // issue on OpenSSL builds, we will need to embed a hardcoded list of well
- // known root CAs, as per the _mac and _win versions.
- verify_result->is_issued_by_known_root = true;
-
return OK;
}
diff --git a/chromium/net/cert/cert_verify_proc_unittest.cc b/chromium/net/cert/cert_verify_proc_unittest.cc
index 71a0c0ee990..6567d1656fc 100644
--- a/chromium/net/cert/cert_verify_proc_unittest.cc
+++ b/chromium/net/cert/cert_verify_proc_unittest.cc
@@ -29,6 +29,8 @@
#include "base/win/windows_version.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/mac/mac_util.h"
+#elif defined(OS_ANDROID)
+#include "base/android/build_info.h"
#endif
using base::HexEncode;
@@ -83,6 +85,26 @@ int WellKnownCaCertVerifyProc::VerifyInternal(
return OK;
}
+bool SupportsReturningVerifiedChain() {
+#if defined(OS_ANDROID)
+ // Before API level 17, Android does not expose the APIs necessary to get at
+ // the verified certificate chain.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < 17)
+ return false;
+#endif
+ return true;
+}
+
+bool SupportsDetectingKnownRoots() {
+#if defined(OS_ANDROID)
+ // Before API level 17, Android does not expose the APIs necessary to get at
+ // the verified certificate chain and detect known roots.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < 17)
+ return false;
+#endif
+ return true;
+}
+
} // namespace
class CertVerifyProcTest : public testing::Test {
@@ -135,7 +157,7 @@ TEST_F(CertVerifyProcTest, DISABLED_WithoutRevocationChecking) {
&verify_result));
}
-#if defined(OS_ANDROID) || defined(USE_OPENSSL)
+#if defined(OS_ANDROID) || defined(USE_OPENSSL_CERTS)
// TODO(jnd): http://crbug.com/117478 - EV verification is not yet supported.
#define MAYBE_EVVerification DISABLED_EVVerification
#else
@@ -398,14 +420,15 @@ TEST_F(CertVerifyProcTest, RejectWeakKeys) {
// provided by servers. See CertVerifyProcTest.CybertrustGTERoot for further
// details.
#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
-#elif defined(USE_OPENSSL) || defined(OS_ANDROID)
-// Disabled for OpenSSL / Android - Android and OpenSSL do not attempt to find
-// a minimal certificate chain, thus prefer the MD5 root over the SHA-1 root.
-#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
#else
#define MAYBE_ExtraneousMD5RootCert ExtraneousMD5RootCert
#endif
TEST_F(CertVerifyProcTest, MAYBE_ExtraneousMD5RootCert) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
scoped_refptr<X509Certificate> server_cert =
@@ -554,13 +577,12 @@ TEST_F(CertVerifyProcTest, NameConstraintsOk) {
EXPECT_EQ(0U, verify_result.cert_status);
}
-#if defined(OS_ANDROID)
-// Disabled because Android isn't filling in SPKI hashes: crbug.com/116838.
-#define MAYBE_NameConstraintsFailure DISABLED_NameConstraintsFailure
-#else
-#define MAYBE_NameConstraintsFailure NameConstraintsFailure
-#endif
-TEST_F(CertVerifyProcTest, MAYBE_NameConstraintsFailure) {
+TEST_F(CertVerifyProcTest, NameConstraintsFailure) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
CertificateList ca_cert_list =
CreateCertificateListFromFile(GetTestCertsDirectory(),
"root_ca_cert.pem",
@@ -591,8 +613,12 @@ TEST_F(CertVerifyProcTest, MAYBE_NameConstraintsFailure) {
verify_result.cert_status & CERT_STATUS_NAME_CONSTRAINT_VIOLATION);
}
-// The certse.pem certificate has been revoked. crbug.com/259723.
TEST_F(CertVerifyProcTest, TestKnownRoot) {
+ if (!SupportsDetectingKnownRoots()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile(
certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
@@ -622,6 +648,11 @@ TEST_F(CertVerifyProcTest, TestKnownRoot) {
// The certse.pem certificate has been revoked. crbug.com/259723.
TEST_F(CertVerifyProcTest, PublicKeyHashes) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile(
certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
@@ -693,7 +724,7 @@ TEST_F(CertVerifyProcTest, InvalidKeyUsage) {
NULL,
empty_cert_list_,
&verify_result);
-#if defined(USE_OPENSSL) && !defined(OS_ANDROID)
+#if defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
// This certificate has two errors: "invalid key usage" and "untrusted CA".
// However, OpenSSL returns only one (the latter), and we can't detect
// the other errors.
@@ -717,6 +748,11 @@ TEST_F(CertVerifyProcTest, InvalidKeyUsage) {
// used to ensure that the actual, verified chain is being returned by
// Verify().
TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem",
@@ -759,19 +795,16 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
certs[2]->os_cert_handle()));
}
-#if defined(OS_ANDROID)
-// TODO(ppi): Disabled because is_issued_by_known_root is incorrect on Android.
-// Once this is fixed, re-enable this check for android. crbug.com/116838
-#define MAYBE_IntranetHostsRejected DISABLED_IntranetHostsRejected
-#else
-#define MAYBE_IntranetHostsRejected IntranetHostsRejected
-#endif
-
// Test that certificates issued for 'intranet' names (that is, containing no
// known public registry controlled domain information) issued by well-known
// CAs are flagged appropriately, while certificates that are issued by
// internal CAs are not flagged.
-TEST_F(CertVerifyProcTest, MAYBE_IntranetHostsRejected) {
+TEST_F(CertVerifyProcTest, IntranetHostsRejected) {
+ if (!SupportsDetectingKnownRoots()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
CertificateList cert_list = CreateCertificateListFromFile(
GetTestCertsDirectory(), "ok_cert.pem",
X509Certificate::FORMAT_AUTO);
@@ -802,6 +835,11 @@ TEST_F(CertVerifyProcTest, MAYBE_IntranetHostsRejected) {
// of intermediate certificates are combined, it's possible that order may
// not be maintained.
TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem",
@@ -848,6 +886,11 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) {
// Test that Verify() filters out certificates which are not related to
// or part of the certificate chain being verified.
TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) {
+ if (!SupportsReturningVerifiedChain()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem",
@@ -946,6 +989,35 @@ TEST_F(CertVerifyProcTest, AdditionalTrustAnchors) {
EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
}
+// Tests that certificates issued by user-supplied roots are not flagged as
+// issued by a known root. This should pass whether or not the platform supports
+// detecting known roots.
+TEST_F(CertVerifyProcTest, IsIssuedByKnownRootIgnoresTestRoots) {
+ // Load root_ca_cert.pem into the test root store.
+ TestRootCerts* root_certs = TestRootCerts::GetInstance();
+ root_certs->AddFromFile(
+ GetTestCertsDirectory().AppendASCII("root_ca_cert.pem"));
+
+ CertificateList cert_list = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ // Verification should pass.
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(
+ cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_, &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+ // But should not be marked as a known root.
+ EXPECT_FALSE(verify_result.is_issued_by_known_root);
+
+ root_certs->Clear();
+ EXPECT_TRUE(root_certs->IsEmpty());
+}
+
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Tests that, on OS X, issues with a cross-certified Baltimore CyberTrust
// Root can be successfully worked around once Apple completes removing the
@@ -1064,6 +1136,8 @@ TEST_F(CertVerifyProcTest, CybertrustGTERoot) {
EXPECT_EQ(OK, error);
EXPECT_EQ(0U, verify_result.cert_status);
+ TestRootCerts::GetInstance()->Clear();
+ EXPECT_TRUE(TestRootCerts::GetInstance()->IsEmpty());
}
#endif
@@ -1333,7 +1407,7 @@ TEST_P(CertVerifyProcWeakDigestTest, Verify) {
const WeakDigestTestData kVerifyRootCATestData[] = {
{ "weak_digest_md5_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, false },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_md4_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, false },
@@ -1348,7 +1422,7 @@ INSTANTIATE_TEST_CASE_P(VerifyRoot, CertVerifyProcWeakDigestTest,
const WeakDigestTestData kVerifyIntermediateCATestData[] = {
{ "weak_digest_sha1_root.pem", "weak_digest_md5_intermediate.pem",
"weak_digest_sha1_ee.pem", true, false, false },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
"weak_digest_sha1_ee.pem", false, true, false },
@@ -1371,7 +1445,7 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
const WeakDigestTestData kVerifyEndEntityTestData[] = {
{ "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_md5_ee.pem", true, false, false },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_md4_ee.pem", false, true, false },
@@ -1395,7 +1469,7 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(MAYBE_VerifyEndEntity,
const WeakDigestTestData kVerifyIncompleteIntermediateTestData[] = {
{ NULL, "weak_digest_md5_intermediate.pem", "weak_digest_sha1_ee.pem",
true, false, false },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ NULL, "weak_digest_md4_intermediate.pem", "weak_digest_sha1_ee.pem",
false, true, false },
@@ -1420,7 +1494,7 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
const WeakDigestTestData kVerifyIncompleteEETestData[] = {
{ NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md5_ee.pem",
true, false, false },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md4_ee.pem",
false, true, false },
@@ -1447,7 +1521,7 @@ const WeakDigestTestData kVerifyMixedTestData[] = {
"weak_digest_md2_ee.pem", true, false, true },
{ "weak_digest_sha1_root.pem", "weak_digest_md2_intermediate.pem",
"weak_digest_md5_ee.pem", true, false, true },
-#if defined(USE_OPENSSL) || defined(OS_WIN)
+#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
"weak_digest_md2_ee.pem", false, true, true },
diff --git a/chromium/net/cert/cert_verify_proc_win.cc b/chromium/net/cert/cert_verify_proc_win.cc
index 29885eac18f..b6ab2b60ad1 100644
--- a/chromium/net/cert/cert_verify_proc_win.cc
+++ b/chromium/net/cert/cert_verify_proc_win.cc
@@ -61,12 +61,11 @@ struct FreeCertContextFunctor {
typedef crypto::ScopedCAPIHandle<HCERTCHAINENGINE, FreeChainEngineFunctor>
ScopedHCERTCHAINENGINE;
-typedef scoped_ptr_malloc<const CERT_CHAIN_CONTEXT,
- FreeCertChainContextFunctor>
+typedef scoped_ptr<const CERT_CHAIN_CONTEXT, FreeCertChainContextFunctor>
ScopedPCCERT_CHAIN_CONTEXT;
-typedef scoped_ptr_malloc<const CERT_CONTEXT,
- FreeCertContextFunctor> ScopedPCCERT_CONTEXT;
+typedef scoped_ptr<const CERT_CONTEXT, FreeCertContextFunctor>
+ ScopedPCCERT_CONTEXT;
//-----------------------------------------------------------------------------
@@ -200,7 +199,7 @@ bool CertSubjectCommonNameHasNull(PCCERT_CONTEXT cert) {
&name_info,
&name_info_size);
if (rv) {
- scoped_ptr_malloc<CERT_NAME_INFO> scoped_name_info(name_info);
+ scoped_ptr<CERT_NAME_INFO, base::FreeDeleter> scoped_name_info(name_info);
// The Subject field may have multiple common names. According to the
// "PKI Layer Cake" paper, CryptoAPI uses every common name in the
@@ -349,8 +348,9 @@ void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
// Decodes the cert's certificatePolicies extension into a CERT_POLICIES_INFO
// structure and stores it in *output.
-void GetCertPoliciesInfo(PCCERT_CONTEXT cert,
- scoped_ptr_malloc<CERT_POLICIES_INFO>* output) {
+void GetCertPoliciesInfo(
+ PCCERT_CONTEXT cert,
+ scoped_ptr<CERT_POLICIES_INFO, base::FreeDeleter>* output) {
PCERT_EXTENSION extension = CertFindExtension(szOID_CERT_POLICIES,
cert->pCertInfo->cExtension,
cert->pCertInfo->rgExtension);
@@ -570,7 +570,7 @@ int CertVerifyProcWin::VerifyInternal(
const_cast<LPSTR*>(usage);
// Get the certificatePolicies extension of the certificate.
- scoped_ptr_malloc<CERT_POLICIES_INFO> policies_info;
+ scoped_ptr<CERT_POLICIES_INFO, base::FreeDeleter> policies_info;
LPSTR ev_policy_oid = NULL;
if (flags & CertVerifier::VERIFY_EV_CERT) {
GetCertPoliciesInfo(cert_handle, &policies_info);
@@ -722,7 +722,7 @@ int CertVerifyProcWin::VerifyInternal(
if (CertSubjectCommonNameHasNull(cert_handle))
verify_result->cert_status |= CERT_STATUS_INVALID;
- std::wstring wstr_hostname = ASCIIToWide(hostname);
+ std::wstring wstr_hostname = base::ASCIIToWide(hostname);
SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para;
memset(&extra_policy_para, 0, sizeof(extra_policy_para));
diff --git a/chromium/net/cert/crl_set.cc b/chromium/net/cert/crl_set.cc
index de0651666d8..2505d299fc4 100644
--- a/chromium/net/cert/crl_set.cc
+++ b/chromium/net/cert/crl_set.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/base64.h"
+#include "base/debug/trace_event.h"
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
@@ -151,27 +152,32 @@ static bool ReadCRL(base::StringPiece* data, std::string* out_parent_spki_hash,
std::vector<std::string>* out_serials) {
if (data->size() < crypto::kSHA256Length)
return false;
- *out_parent_spki_hash = std::string(data->data(), crypto::kSHA256Length);
+ out_parent_spki_hash->assign(data->data(), crypto::kSHA256Length);
data->remove_prefix(crypto::kSHA256Length);
if (data->size() < sizeof(uint32))
return false;
uint32 num_serials;
memcpy(&num_serials, data->data(), sizeof(uint32)); // assumes little endian
+ if (num_serials > 32 * 1024 * 1024) // Sanity check.
+ return false;
+
+ out_serials->reserve(num_serials);
data->remove_prefix(sizeof(uint32));
for (uint32 i = 0; i < num_serials; ++i) {
- uint8 serial_length;
if (data->size() < sizeof(uint8))
return false;
- memcpy(&serial_length, data->data(), sizeof(uint8));
+
+ uint8 serial_length = data->data()[0];
data->remove_prefix(sizeof(uint8));
if (data->size() < serial_length)
return false;
- std::string serial(data->data(), serial_length);
+
+ out_serials->push_back(std::string());
+ out_serials->back().assign(data->data(), serial_length);
data->remove_prefix(serial_length);
- out_serials->push_back(serial);
}
return true;
@@ -185,14 +191,21 @@ bool CRLSet::CopyBlockedSPKIsFromHeader(base::DictionaryValue* header_dict) {
}
blocked_spkis_.clear();
+ blocked_spkis_.reserve(blocked_spkis_list->GetSize());
+
+ std::string spki_sha256_base64;
for (size_t i = 0; i < blocked_spkis_list->GetSize(); ++i) {
- std::string spki_sha256_base64, spki_sha256;
+ spki_sha256_base64.clear();
+
if (!blocked_spkis_list->GetString(i, &spki_sha256_base64))
return false;
- if (!base::Base64Decode(spki_sha256_base64, &spki_sha256))
+
+ blocked_spkis_.push_back(std::string());
+ if (!base::Base64Decode(spki_sha256_base64, &blocked_spkis_.back())) {
+ blocked_spkis_.pop_back();
return false;
- blocked_spkis_.push_back(spki_sha256);
+ }
}
return true;
@@ -200,6 +213,7 @@ bool CRLSet::CopyBlockedSPKIsFromHeader(base::DictionaryValue* header_dict) {
// static
bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
+ TRACE_EVENT0("CRLSet", "Parse");
// Other parts of Chrome assume that we're little endian, so we don't lose
// anything by doing this.
#if defined(__BYTE_ORDER)
@@ -238,18 +252,26 @@ bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
if (not_after < 0)
return false;
- scoped_refptr<CRLSet> crl_set(new CRLSet);
+ scoped_refptr<CRLSet> crl_set(new CRLSet());
crl_set->sequence_ = static_cast<uint32>(sequence);
crl_set->not_after_ = static_cast<uint64>(not_after);
+ crl_set->crls_.reserve(64); // Value observed experimentally.
for (size_t crl_index = 0; !data.empty(); crl_index++) {
- std::string parent_spki_sha256;
- std::vector<std::string> serials;
- if (!ReadCRL(&data, &parent_spki_sha256, &serials))
+ // Speculatively push back a pair and pass it to ReadCRL() to avoid
+ // unnecessary copies.
+ crl_set->crls_.push_back(
+ std::make_pair(std::string(), std::vector<std::string>()));
+ std::pair<std::string, std::vector<std::string> >* const back_pair =
+ &crl_set->crls_.back();
+
+ if (!ReadCRL(&data, &back_pair->first, &back_pair->second)) {
+ // Undo the speculative push_back() performed above.
+ crl_set->crls_.pop_back();
return false;
+ }
- crl_set->crls_.push_back(std::make_pair(parent_spki_sha256, serials));
- crl_set->crls_index_by_issuer_[parent_spki_sha256] = crl_index;
+ crl_set->crls_index_by_issuer_[back_pair->first] = crl_index;
}
if (!crl_set->CopyBlockedSPKIsFromHeader(header_dict.get()))
@@ -548,7 +570,7 @@ CRLSet::Result CRLSet::CheckSerial(
while (serial.size() > 1 && serial[0] == 0x00)
serial.remove_prefix(1);
- std::map<std::string, size_t>::const_iterator i =
+ base::hash_map<std::string, size_t>::const_iterator i =
crls_index_by_issuer_.find(issuer_spki_hash.as_string());
if (i == crls_index_by_issuer_.end())
return UNKNOWN;
diff --git a/chromium/net/cert/crl_set.h b/chromium/net/cert/crl_set.h
index 348005f097b..6ef4bb740b2 100644
--- a/chromium/net/cert/crl_set.h
+++ b/chromium/net/cert/crl_set.h
@@ -5,11 +5,11 @@
#ifndef NET_CERT_CRL_SET_H_
#define NET_CERT_CRL_SET_H_
-#include <map>
#include <string>
#include <utility>
#include <vector>
+#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
@@ -117,7 +117,7 @@ class NET_EXPORT CRLSet : public base::RefCountedThreadSafe<CRLSet> {
// where the information for that issuer can be found. We have both |crls_|
// and |crls_index_by_issuer_| because, when applying a delta update, we need
// to identify a CRL by index.
- std::map<std::string, size_t> crls_index_by_issuer_;
+ base::hash_map<std::string, size_t> crls_index_by_issuer_;
// blocked_spkis_ contains the SHA256 hashes of SPKIs which are to be blocked
// no matter where in a certificate chain they might appear.
std::vector<std::string> blocked_spkis_;
diff --git a/chromium/net/cert/ct_log_response_parser.cc b/chromium/net/cert/ct_log_response_parser.cc
new file mode 100644
index 00000000000..bd59a9eed12
--- /dev/null
+++ b/chromium/net/cert/ct_log_response_parser.cc
@@ -0,0 +1,140 @@
+// Copyright 2014 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/cert/ct_log_response_parser.h"
+
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/signed_tree_head.h"
+
+namespace net {
+
+namespace ct {
+
+namespace {
+
+// Structure for making JSON decoding easier. The string fields
+// are base64-encoded so will require further decoding.
+struct JsonSignedTreeHead {
+ int tree_size;
+ double timestamp;
+ std::string sha256_root_hash;
+ DigitallySigned signature;
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<JsonSignedTreeHead>* converted);
+};
+
+bool ConvertSHA256RootHash(const base::StringPiece& s, std::string* result) {
+ if (!base::Base64Decode(s, result)) {
+ DVLOG(1) << "Failed decoding sha256_root_hash";
+ return false;
+ }
+
+ if (result->length() != kSthRootHashLength) {
+ DVLOG(1) << "sha256_root_hash is expected to be 32 bytes, but is "
+ << result->length() << " bytes.";
+ return false;
+ }
+
+ return true;
+}
+
+bool ConvertTreeHeadSignature(const base::StringPiece& s,
+ DigitallySigned* result) {
+ std::string tree_head_signature;
+ if (!base::Base64Decode(s, &tree_head_signature)) {
+ DVLOG(1) << "Failed decoding tree_head_signature";
+ return false;
+ }
+
+ base::StringPiece sp(tree_head_signature);
+ if (!DecodeDigitallySigned(&sp, result)) {
+ DVLOG(1) << "Failed decoding signature to DigitallySigned";
+ return false;
+ }
+ return true;
+}
+
+void JsonSignedTreeHead::RegisterJSONConverter(
+ base::JSONValueConverter<JsonSignedTreeHead>* converter) {
+ converter->RegisterIntField("tree_size", &JsonSignedTreeHead::tree_size);
+ converter->RegisterDoubleField("timestamp", &JsonSignedTreeHead::timestamp);
+ converter->RegisterCustomField("sha256_root_hash",
+ &JsonSignedTreeHead::sha256_root_hash,
+ &ConvertSHA256RootHash);
+ converter->RegisterCustomField<DigitallySigned>(
+ "tree_head_signature",
+ &JsonSignedTreeHead::signature,
+ &ConvertTreeHeadSignature);
+}
+
+bool IsJsonSTHStructurallyValid(const JsonSignedTreeHead& sth) {
+ if (sth.tree_size < 0) {
+ DVLOG(1) << "Tree size in Signed Tree Head JSON is negative: "
+ << sth.tree_size;
+ return false;
+ }
+
+ if (sth.timestamp < 0) {
+ DVLOG(1) << "Timestamp in Signed Tree Head JSON is negative: "
+ << sth.timestamp;
+ return false;
+ }
+
+ if (sth.sha256_root_hash.empty()) {
+ DVLOG(1) << "Missing SHA256 root hash from Signed Tree Head JSON.";
+ return false;
+ }
+
+ if (sth.signature.signature_data.empty()) {
+ DVLOG(1) << "Missing SHA256 root hash from Signed Tree Head JSON.";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool FillSignedTreeHead(const base::StringPiece& json_signed_tree_head,
+ SignedTreeHead* signed_tree_head) {
+ base::JSONReader json_reader;
+ scoped_ptr<base::Value> json(json_reader.Read(json_signed_tree_head));
+ if (json.get() == NULL) {
+ DVLOG(1) << "Empty Signed Tree Head JSON.";
+ return false;
+ }
+
+ JsonSignedTreeHead parsed_sth;
+ base::JSONValueConverter<JsonSignedTreeHead> converter;
+ if (!converter.Convert(*json.get(), &parsed_sth)) {
+ DVLOG(1) << "Invalid Signed Tree Head JSON.";
+ return false;
+ }
+
+ if (!IsJsonSTHStructurallyValid(parsed_sth))
+ return false;
+
+ signed_tree_head->version = SignedTreeHead::V1;
+ signed_tree_head->tree_size = parsed_sth.tree_size;
+ signed_tree_head->timestamp =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(parsed_sth.timestamp);
+ signed_tree_head->signature = parsed_sth.signature;
+ memcpy(signed_tree_head->sha256_root_hash,
+ parsed_sth.sha256_root_hash.c_str(),
+ kSthRootHashLength);
+ return true;
+}
+
+} // namespace ct
+
+} // namespace net
diff --git a/chromium/net/cert/ct_log_response_parser.h b/chromium/net/cert/ct_log_response_parser.h
new file mode 100644
index 00000000000..e458670324f
--- /dev/null
+++ b/chromium/net/cert/ct_log_response_parser.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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_CERT_CT_LOG_RESPONSE_PARSER_H_
+#define NET_CERT_CT_LOG_RESPONSE_PARSER_H_
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace ct {
+struct SignedTreeHead;
+
+// Fills in |signed_tree_head| from its JSON representation in
+// |json_signed_tree_head|.
+// Returns true and fills in |signed_tree_head| if all fields are present and
+// valid.Otherwise, returns false and does not modify |signed_tree_head|.
+NET_EXPORT bool FillSignedTreeHead(
+ const base::StringPiece& json_signed_tree_head,
+ SignedTreeHead* signed_tree_head);
+
+} // namespace ct
+
+} // namespace net
+#endif // NET_CERT_CT_LOG_RESPONSE_PARSER_H_
diff --git a/chromium/net/cert/ct_log_response_parser_unittest.cc b/chromium/net/cert/ct_log_response_parser_unittest.cc
new file mode 100644
index 00000000000..7d6140a60e9
--- /dev/null
+++ b/chromium/net/cert/ct_log_response_parser_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright 2014 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/cert/ct_log_response_parser.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/signed_tree_head.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace ct {
+
+std::string CreateSignedTreeHeadJsonString(std::string sha256_root_hash,
+ std::string tree_head_signature) {
+ std::string sth_json = "{\"tree_size\":2903698,\"timestamp\":1395761621447";
+
+ if (!sha256_root_hash.empty()) {
+ sth_json += base::StringPrintf(",\"sha256_root_hash\":\"%s\"",
+ sha256_root_hash.c_str());
+ }
+ if (!tree_head_signature.empty()) {
+ sth_json += base::StringPrintf(",\"tree_head_signature\":\"%s\"",
+ tree_head_signature.c_str());
+ }
+
+ sth_json += "}";
+ return sth_json;
+}
+
+const char kSHA256RootHash[] = "/WHFMgXtI/umKKuACJIN0Bb73TcILm9WkeU6qszvoAo=";
+
+const char kTreeHeadSignature[] =
+ "BAMARzBFAiAB+IIYrkRsZDW0/6TzPgR+aJ26twCQ1JDTwq/"
+ "mpinCjAIhAKDXdXMtqbvQ42r9dBIwV5RM/KpEzNQdIhXHesd9HPv3";
+
+TEST(CTLogResponseParserTest, ParsesValidJsonSTH) {
+ std::string sample_sth =
+ CreateSignedTreeHeadJsonString(kSHA256RootHash, kTreeHeadSignature);
+ SignedTreeHead tree_head;
+ EXPECT_TRUE(FillSignedTreeHead(sample_sth, &tree_head));
+
+ base::Time expected_timestamp =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(1395761621447);
+
+ ASSERT_EQ(SignedTreeHead::V1, tree_head.version);
+ ASSERT_EQ(expected_timestamp, tree_head.timestamp);
+ ASSERT_EQ(2903698u, tree_head.tree_size);
+
+ // Copy the field from the SignedTreeHead because it's not null terminated
+ // there and ASSERT_STREQ expects null-terminated strings.
+ char actual_hash[kSthRootHashLength + 1];
+ memcpy(actual_hash, tree_head.sha256_root_hash, kSthRootHashLength);
+ actual_hash[kSthRootHashLength] = '\0';
+ std::string expected_sha256_root_hash;
+ base::Base64Decode(kSHA256RootHash, &expected_sha256_root_hash);
+ ASSERT_STREQ(expected_sha256_root_hash.c_str(), actual_hash);
+
+ std::string tree_head_signature;
+ base::Base64Decode(kTreeHeadSignature, &tree_head_signature);
+ base::StringPiece sp(tree_head_signature);
+ DigitallySigned expected_signature;
+ ASSERT_TRUE(DecodeDigitallySigned(&sp, &expected_signature));
+
+ ASSERT_EQ(tree_head.signature.hash_algorithm,
+ expected_signature.hash_algorithm);
+ ASSERT_EQ(tree_head.signature.signature_algorithm,
+ expected_signature.signature_algorithm);
+ ASSERT_EQ(tree_head.signature.signature_data,
+ expected_signature.signature_data);
+}
+
+TEST(CTLogResponseParserTest, FailsToParseMissingFields) {
+ std::string missing_signature_sth =
+ CreateSignedTreeHeadJsonString(kSHA256RootHash, "");
+
+ SignedTreeHead tree_head;
+ ASSERT_FALSE(FillSignedTreeHead(missing_signature_sth, &tree_head));
+
+ std::string missing_root_hash_sth =
+ CreateSignedTreeHeadJsonString("", kTreeHeadSignature);
+ ASSERT_FALSE(FillSignedTreeHead(missing_root_hash_sth, &tree_head));
+}
+
+TEST(CTLogResponseParserTest, FailsToParseIncorrectLengthRootHash) {
+ SignedTreeHead tree_head;
+
+ std::string too_long_hash = CreateSignedTreeHeadJsonString(
+ kSHA256RootHash, "/WHFMgXtI/umKKuACJIN0Bb73TcILm9WkeU6qszvoArK\n");
+ ASSERT_FALSE(FillSignedTreeHead(too_long_hash, &tree_head));
+
+ std::string too_short_hash = CreateSignedTreeHeadJsonString(
+ kSHA256RootHash, "/WHFMgXtI/umKKuACJIN0Bb73TcILm9WkeU6qszvoA==\n");
+ ASSERT_FALSE(FillSignedTreeHead(too_short_hash, &tree_head));
+}
+
+} // namespace ct
+
+} // namespace net
diff --git a/chromium/net/cert/ct_log_verifier.cc b/chromium/net/cert/ct_log_verifier.cc
index 1c9374dfd94..6efb96a9319 100644
--- a/chromium/net/cert/ct_log_verifier.cc
+++ b/chromium/net/cert/ct_log_verifier.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "net/cert/ct_serialization.h"
+#include "net/cert/signed_tree_head.h"
namespace net {
@@ -26,17 +27,8 @@ bool CTLogVerifier::Verify(const ct::LogEntry& entry,
return false;
}
- if (sct.signature.hash_algorithm != hash_algorithm_) {
- DVLOG(1) << "Mismatched hash algorithm. Expected " << hash_algorithm_
- << ", got " << sct.signature.hash_algorithm << ".";
+ if (!SignatureParametersMatch(sct.signature))
return false;
- }
-
- if (sct.signature.signature_algorithm != signature_algorithm_) {
- DVLOG(1) << "Mismatched sig algorithm. Expected " << signature_algorithm_
- << ", got " << sct.signature.signature_algorithm << ".";
- return false;
- }
std::string serialized_log_entry;
if (!ct::EncodeLogEntry(entry, &serialized_log_entry)) {
@@ -53,4 +45,33 @@ bool CTLogVerifier::Verify(const ct::LogEntry& entry,
return VerifySignature(serialized_data, sct.signature.signature_data);
}
+bool CTLogVerifier::SetSignedTreeHead(
+ scoped_ptr<ct::SignedTreeHead> signed_tree_head) {
+ if (!SignatureParametersMatch(signed_tree_head->signature))
+ return false;
+
+ std::string serialized_data;
+ ct::EncodeTreeHeadSignature(*signed_tree_head.get(), &serialized_data);
+ if (VerifySignature(serialized_data,
+ signed_tree_head->signature.signature_data)) {
+ signed_tree_head_.reset(signed_tree_head.release());
+ return true;
+ }
+ return false;
+}
+
+bool CTLogVerifier::SignatureParametersMatch(
+ const ct::DigitallySigned& signature) {
+ if (!signature.SignatureParametersMatch(hash_algorithm_,
+ signature_algorithm_)) {
+ DVLOG(1) << "Mismatched hash or signature algorithm. Hash: "
+ << hash_algorithm_ << " vs " << signature.hash_algorithm
+ << " Signature: " << signature_algorithm_ << " vs "
+ << signature.signature_algorithm << ".";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace net
diff --git a/chromium/net/cert/ct_log_verifier.h b/chromium/net/cert/ct_log_verifier.h
index b4ee5202674..38e0930eb65 100644
--- a/chromium/net/cert/ct_log_verifier.h
+++ b/chromium/net/cert/ct_log_verifier.h
@@ -23,6 +23,10 @@ typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
namespace net {
+namespace ct {
+struct SignedTreeHead;
+} // namespace ct
+
// Class for verifying Signed Certificate Timestamps (SCTs) provided by a
// specific log (whose identity is provided during construction).
class NET_EXPORT CTLogVerifier {
@@ -46,6 +50,11 @@ class NET_EXPORT CTLogVerifier {
bool Verify(const ct::LogEntry& entry,
const ct::SignedCertificateTimestamp& sct);
+ // Verifies and sets |signed_tree_head|. If |signed_tree_head|'s signature is
+ // valid, stores it and returns true. Otherwise, discards the sth and
+ // returns false.
+ bool SetSignedTreeHead(scoped_ptr<ct::SignedTreeHead> signed_tree_head);
+
private:
FRIEND_TEST_ALL_PREFIXES(CTLogVerifierTest, VerifySignature);
@@ -61,10 +70,15 @@ class NET_EXPORT CTLogVerifier {
bool VerifySignature(const base::StringPiece& data_to_sign,
const base::StringPiece& signature);
+ // Returns true if the signature and hash algorithms in |signature|
+ // match those of the log
+ bool SignatureParametersMatch(const ct::DigitallySigned& signature);
+
std::string key_id_;
std::string description_;
ct::DigitallySigned::HashAlgorithm hash_algorithm_;
ct::DigitallySigned::SignatureAlgorithm signature_algorithm_;
+ scoped_ptr<ct::SignedTreeHead> signed_tree_head_;
#if defined(USE_OPENSSL)
EVP_PKEY* public_key_;
diff --git a/chromium/net/cert/ct_log_verifier_nss.cc b/chromium/net/cert/ct_log_verifier_nss.cc
index 49b6f7339ba..02ef99a7a60 100644
--- a/chromium/net/cert/ct_log_verifier_nss.cc
+++ b/chromium/net/cert/ct_log_verifier_nss.cc
@@ -14,7 +14,7 @@
#include "base/logging.h"
#include "crypto/nss_util.h"
#include "crypto/sha2.h"
-#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
namespace net {
diff --git a/chromium/net/cert/ct_log_verifier_openssl.cc b/chromium/net/cert/ct_log_verifier_openssl.cc
index fca4182e126..0b05d96de83 100644
--- a/chromium/net/cert/ct_log_verifier_openssl.cc
+++ b/chromium/net/cert/ct_log_verifier_openssl.cc
@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "crypto/openssl_util.h"
#include "crypto/sha2.h"
+#include "net/cert/signed_tree_head.h"
namespace net {
diff --git a/chromium/net/cert/ct_log_verifier_unittest.cc b/chromium/net/cert/ct_log_verifier_unittest.cc
index b94f14a11c2..9b3211df2e0 100644
--- a/chromium/net/cert/ct_log_verifier_unittest.cc
+++ b/chromium/net/cert/ct_log_verifier_unittest.cc
@@ -8,6 +8,7 @@
#include "base/time/time.h"
#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
#include "net/test/ct_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -75,4 +76,17 @@ TEST_F(CTLogVerifierTest, FailsInvalidLogID) {
EXPECT_FALSE(log_->Verify(cert_entry, *cert_sct));
}
+TEST_F(CTLogVerifierTest, SetsValidSTH) {
+ scoped_ptr<ct::SignedTreeHead> sth(new ct::SignedTreeHead());
+ ct::GetSignedTreeHead(sth.get());
+ ASSERT_TRUE(log_->SetSignedTreeHead(sth.Pass()));
+}
+
+TEST_F(CTLogVerifierTest, DoesNotSetInvalidSTH) {
+ scoped_ptr<ct::SignedTreeHead> sth(new ct::SignedTreeHead());
+ ct::GetSignedTreeHead(sth.get());
+ sth->sha256_root_hash[0] = '\x0';
+ ASSERT_FALSE(log_->SetSignedTreeHead(sth.Pass()));
+}
+
} // namespace net
diff --git a/chromium/net/cert/ct_serialization.cc b/chromium/net/cert/ct_serialization.cc
index 3de512c8181..489717a9686 100644
--- a/chromium/net/cert/ct_serialization.cc
+++ b/chromium/net/cert/ct_serialization.cc
@@ -34,6 +34,9 @@ const size_t kTbsCertificateLengthBytes = 3;
const size_t kSCTListLengthBytes = 2;
const size_t kSerializedSCTLengthBytes = 2;
+// Members of digitally-signed struct of a STH
+const size_t kTreeSizeLength = 8;
+
enum SignatureType {
SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
TREE_HASH = 1,
@@ -285,6 +288,12 @@ bool EncodeLogEntry(const LogEntry& input, std::string* output) {
return false;
}
+static void WriteTimeSinceEpoch(const base::Time& timestamp,
+ std::string* output) {
+ base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
+ WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output);
+}
+
bool EncodeV1SCTSignedData(const base::Time& timestamp,
const std::string& serialized_log_entry,
const std::string& extensions,
@@ -293,15 +302,24 @@ bool EncodeV1SCTSignedData(const base::Time& timestamp,
output);
WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
output);
- base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
- WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(),
- output);
+ WriteTimeSinceEpoch(timestamp, output);
// NOTE: serialized_log_entry must already be serialized and contain the
// length as the prefix.
WriteEncodedBytes(serialized_log_entry, output);
return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
}
+void EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head,
+ std::string* output) {
+ WriteUint(kVersionLength, signed_tree_head.version, output);
+ WriteUint(kSignatureTypeLength, TREE_HASH, output);
+ WriteTimeSinceEpoch(signed_tree_head.timestamp, output);
+ WriteUint(kTreeSizeLength, signed_tree_head.tree_size, output);
+ WriteEncodedBytes(
+ base::StringPiece(signed_tree_head.sha256_root_hash, kSthRootHashLength),
+ output);
+}
+
bool DecodeSCTList(base::StringPiece* input,
std::vector<base::StringPiece>* output) {
std::vector<base::StringPiece> result;
diff --git a/chromium/net/cert/ct_serialization.h b/chromium/net/cert/ct_serialization.h
index 19dc0873f4f..f183247469f 100644
--- a/chromium/net/cert/ct_serialization.h
+++ b/chromium/net/cert/ct_serialization.h
@@ -11,6 +11,7 @@
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
namespace net {
@@ -48,6 +49,13 @@ NET_EXPORT_PRIVATE bool EncodeV1SCTSignedData(
const std::string& extensions,
std::string* output);
+// Encodes the data signed by a Signed Tree Head (STH) |signed_tree_head| into
+// |output|. The signature included in the |signed_tree_head| can then be
+// verified over these bytes.
+NET_EXPORT_PRIVATE void EncodeTreeHeadSignature(
+ const SignedTreeHead& signed_tree_head,
+ std::string* output);
+
// Decode a list of Signed Certificate Timestamps
// (SignedCertificateTimestampList as defined in RFC6962): from a single
// string in |input| to a vector of individually-encoded SCTs |output|.
diff --git a/chromium/net/cert/ct_serialization_unittest.cc b/chromium/net/cert/ct_serialization_unittest.cc
index d832c1d8ef1..606bdd1d2d7 100644
--- a/chromium/net/cert/ct_serialization_unittest.cc
+++ b/chromium/net/cert/ct_serialization_unittest.cc
@@ -94,7 +94,7 @@ TEST_F(CtSerializationTest, EncodesV1SCTSignedData) {
base::Time timestamp = base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(1348589665525);
std::string dummy_entry("abc");
- std::string empty_extensions("");
+ std::string empty_extensions;
// For now, no known failure cases.
std::string encoded;
ASSERT_TRUE(ct::EncodeV1SCTSignedData(
@@ -145,7 +145,7 @@ TEST_F(CtSerializationTest, DecodesSignedCertificateTimestamp) {
// Subtracting 4 bytes for signature data (hash & sig algs),
// actual signature data should be 71 bytes.
EXPECT_EQ((size_t) 71, sct->signature.signature_data.size());
- EXPECT_EQ(std::string(""), sct->extensions);
+ EXPECT_TRUE(sct->extensions.empty());
}
TEST_F(CtSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) {
@@ -162,5 +162,23 @@ TEST_F(CtSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) {
ct::DecodeSignedCertificateTimestamp(&invalid_length_sct, &sct));
}
-} // namespace net
+TEST_F(CtSerializationTest, EncodesValidSignedTreeHead) {
+ ct::SignedTreeHead signed_tree_head;
+ GetSignedTreeHead(&signed_tree_head);
+
+ std::string encoded;
+ ct::EncodeTreeHeadSignature(signed_tree_head, &encoded);
+ // Expected size is 50 bytes:
+ // Byte 0 is version, byte 1 is signature type
+ // Bytes 2-9 are timestamp
+ // Bytes 10-17 are tree size
+ // Bytes 18-49 are sha256 root hash
+ ASSERT_EQ(50u, encoded.length());
+ std::string expected_buffer(
+ "\x0\x1\x0\x0\x1\x45\x3c\x5f\xb8\x35\x0\x0\x0\x0\x0\x0\x0\x15", 18);
+ expected_buffer.append(ct::GetSampleSTHSHA256RootHash());
+ ASSERT_EQ(expected_buffer, encoded);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/ct_signed_certificate_timestamp_log_param.cc b/chromium/net/cert/ct_signed_certificate_timestamp_log_param.cc
index 75190871a55..e7bb97c4c72 100644
--- a/chromium/net/cert/ct_signed_certificate_timestamp_log_param.cc
+++ b/chromium/net/cert/ct_signed_certificate_timestamp_log_param.cc
@@ -27,6 +27,8 @@ const char* OriginToString(ct::SignedCertificateTimestamp::Origin origin) {
return "tls_extension";
case ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE:
return "ocsp";
+ case ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX:
+ break;
}
return "unknown";
diff --git a/chromium/net/cert/jwk_serializer_nss.cc b/chromium/net/cert/jwk_serializer_nss.cc
index 3da0a81f25a..1db65f705ba 100644
--- a/chromium/net/cert/jwk_serializer_nss.cc
+++ b/chromium/net/cert/jwk_serializer_nss.cc
@@ -69,7 +69,7 @@ bool ConvertEcPublicKeyInfoToJwk(
return false;
}
-typedef scoped_ptr_malloc<
+typedef scoped_ptr<
CERTSubjectPublicKeyInfo,
crypto::NSSDestroyer<CERTSubjectPublicKeyInfo,
SECKEY_DestroySubjectPublicKeyInfo> >
diff --git a/chromium/net/cert/multi_log_ct_verifier.cc b/chromium/net/cert/multi_log_ct_verifier.cc
index 05a32da81eb..5094da026d0 100644
--- a/chromium/net/cert/multi_log_ct_verifier.cc
+++ b/chromium/net/cert/multi_log_ct_verifier.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/metrics/histogram.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/cert/ct_log_verifier.h"
@@ -13,10 +14,45 @@
#include "net/cert/ct_serialization.h"
#include "net/cert/ct_signed_certificate_timestamp_log_param.h"
#include "net/cert/ct_verify_result.h"
+#include "net/cert/sct_status_flags.h"
#include "net/cert/x509_certificate.h"
namespace net {
+namespace {
+
+// Record SCT verification status. This metric would help detecting presence
+// of unknown CT logs as well as bad deployments (invalid SCTs).
+void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.CertificateTransparency.SCTStatus", status, ct::SCT_STATUS_MAX);
+}
+
+// Record SCT origin enum. This metric measure the popularity
+// of the various channels of providing SCTs for a certificate.
+void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
+ UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
+ origin,
+ ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
+}
+
+// Count the number of SCTs that were available for each SSL connection
+// (including SCTs embedded in the certificate).
+// This metric would allow measuring:
+// * Of all SSL connections, how many had SCTs available for validation.
+// * When SCTs are available, how many are available per connection.
+void LogNumSCTsToUMA(const ct::CTVerifyResult& result) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection",
+ result.invalid_scts.size() +
+ result.verified_scts.size() +
+ result.unknown_logs_scts.size(),
+ 1,
+ 10,
+ 11);
+}
+
+} // namespace
+
MultiLogCTVerifier::MultiLogCTVerifier() { }
MultiLogCTVerifier::~MultiLogCTVerifier() { }
@@ -104,6 +140,8 @@ int MultiLogCTVerifier::Verify(
NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
net_log_checked_callback);
+ LogNumSCTsToUMA(*result);
+
if (has_verified_scts)
return OK;
@@ -128,9 +166,11 @@ bool MultiLogCTVerifier::VerifySCTs(
for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
it != sct_list.end(); ++it) {
base::StringPiece encoded_sct(*it);
+ LogSCTOriginToUMA(origin);
scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
+ LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
// XXX(rsleevi): Should we really just skip over bad SCTs?
continue;
}
@@ -152,6 +192,7 @@ bool MultiLogCTVerifier::VerifySingleSCT(
if (it == logs_.end()) {
DVLOG(1) << "SCT does not match any known log.";
result->unknown_logs_scts.push_back(sct);
+ LogSCTStatusToUMA(ct::SCT_STATUS_LOG_UNKNOWN);
return false;
}
@@ -160,6 +201,7 @@ bool MultiLogCTVerifier::VerifySingleSCT(
if (!it->second->Verify(expected_entry, *sct)) {
DVLOG(1) << "Unable to verify SCT signature.";
result->invalid_scts.push_back(sct);
+ LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
return false;
}
@@ -167,9 +209,11 @@ bool MultiLogCTVerifier::VerifySingleSCT(
if (sct->timestamp > base::Time::Now()) {
DVLOG(1) << "SCT is from the future!";
result->invalid_scts.push_back(sct);
+ LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
return false;
}
+ LogSCTStatusToUMA(ct::SCT_STATUS_OK);
result->verified_scts.push_back(sct);
return true;
}
diff --git a/chromium/net/cert/multi_log_ct_verifier_unittest.cc b/chromium/net/cert/multi_log_ct_verifier_unittest.cc
index b1d1aa889e8..c2ae25e3073 100644
--- a/chromium/net/cert/multi_log_ct_verifier_unittest.cc
+++ b/chromium/net/cert/multi_log_ct_verifier_unittest.cc
@@ -8,6 +8,10 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/values.h"
#include "net/base/capturing_net_log.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
@@ -16,6 +20,7 @@
#include "net/cert/ct_serialization.h"
#include "net/cert/ct_verify_result.h"
#include "net/cert/pem_tokenizer.h"
+#include "net/cert/sct_status_flags.h"
#include "net/cert/signed_certificate_timestamp.h"
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
@@ -27,6 +32,8 @@ namespace net {
namespace {
const char kLogDescription[] = "somelog";
+const char kSCTCountHistogram[] =
+ "Net.CertificateTransparency.SCTsPerConnection";
class MultiLogCTVerifierTest : public ::testing::Test {
public:
@@ -42,6 +49,12 @@ class MultiLogCTVerifierTest : public ::testing::Test {
der_test_cert.data(),
der_test_cert.length());
ASSERT_TRUE(chain_);
+
+ embedded_sct_chain_ =
+ CreateCertificateChainFromFile(GetTestCertsDirectory(),
+ "ct-test-embedded-cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_TRUE(embedded_sct_chain_);
}
bool CheckForSingleVerifiedSCTInResult(const ct::CTVerifyResult& result) {
@@ -64,45 +77,121 @@ class MultiLogCTVerifierTest : public ::testing::Test {
if (entries.size() != 2)
return false;
- const CapturingNetLog::CapturedEntry& received(entries[0]);
+ const CapturingNetLog::CapturedEntry& received = entries[0];
std::string embedded_scts;
if (!received.GetStringValue("embedded_scts", &embedded_scts))
return false;
if (embedded_scts.empty())
return false;
- //XXX(eranm): entries[1] is the NetLog message with the checked SCTs.
- //When CapturedEntry has methods to get a dictionary, rather than just
- //a string, add more checks here.
+ const CapturingNetLog::CapturedEntry& parsed = entries[1];
+ base::ListValue* verified_scts;
+ if (!parsed.GetListValue("verified_scts", &verified_scts) ||
+ verified_scts->GetSize() != 1) {
+ return false;
+ }
+
+ base::DictionaryValue* the_sct;
+ if (!verified_scts->GetDictionary(0, &the_sct))
+ return false;
+
+ std::string origin;
+ if (!the_sct->GetString("origin", &origin))
+ return false;
+ if (origin != "embedded_in_certificate")
+ return false;
+
+ base::ListValue* other_scts;
+ if (!parsed.GetListValue("invalid_scts", &other_scts) ||
+ !other_scts->empty()) {
+ return false;
+ }
+
+ if (!parsed.GetListValue("unknown_logs_scts", &other_scts) ||
+ !other_scts->empty()) {
+ return false;
+ }
return true;
}
+ std::string GetSCTListWithInvalidSCT() {
+ std::string sct(ct::GetTestSignedCertificateTimestamp());
+
+ // Change a byte inside the Log ID part of the SCT so it does
+ // not match the log used in the tests
+ sct[15] = 't';
+
+ std::string sct_list;
+ ct::EncodeSCTListForTesting(sct, &sct_list);
+ return sct_list;
+ }
+
+ bool VerifySinglePrecertificateChain(scoped_refptr<X509Certificate> chain,
+ const BoundNetLog& bound_net_log,
+ ct::CTVerifyResult* result) {
+ return verifier_->Verify(
+ chain, std::string(), std::string(), result, bound_net_log) ==
+ OK;
+ }
+
+ bool VerifySinglePrecertificateChain(scoped_refptr<X509Certificate> chain) {
+ ct::CTVerifyResult result;
+ CapturingNetLog net_log;
+ BoundNetLog bound_net_log =
+ BoundNetLog::Make(&net_log, NetLog::SOURCE_CONNECT_JOB);
+
+ return verifier_->Verify(
+ chain, std::string(), std::string(), &result, bound_net_log) ==
+ OK;
+ }
+
bool CheckPrecertificateVerification(scoped_refptr<X509Certificate> chain) {
ct::CTVerifyResult result;
CapturingNetLog net_log;
BoundNetLog bound_net_log =
BoundNetLog::Make(&net_log, NetLog::SOURCE_CONNECT_JOB);
- return (verifier_->Verify(chain, std::string(), std::string(), &result,
- bound_net_log) == OK) &&
- CheckForSingleVerifiedSCTInResult(result) &&
- CheckForSCTOrigin(
- result, ct::SignedCertificateTimestamp::SCT_EMBEDDED) &&
- CheckForEmbeddedSCTInNetLog(net_log);
+ return (VerifySinglePrecertificateChain(chain, bound_net_log, &result) &&
+ CheckForSingleVerifiedSCTInResult(result) &&
+ CheckForSCTOrigin(result,
+ ct::SignedCertificateTimestamp::SCT_EMBEDDED) &&
+ CheckForEmbeddedSCTInNetLog(net_log));
+ }
+
+ // Histogram-related helper methods
+ int GetValueFromHistogram(std::string histogram_name, int sample_index) {
+ base::Histogram* histogram = static_cast<base::Histogram*>(
+ base::StatisticsRecorder::FindHistogram(histogram_name));
+
+ if (histogram == NULL)
+ return 0;
+
+ scoped_ptr<base::HistogramSamples> samples = histogram->SnapshotSamples();
+ return samples->GetCount(sample_index);
+ }
+
+ int NumConnectionsWithSingleSCT() {
+ return GetValueFromHistogram(kSCTCountHistogram, 1);
+ }
+
+ int NumEmbeddedSCTsInHistogram() {
+ return GetValueFromHistogram("Net.CertificateTransparency.SCTOrigin",
+ ct::SignedCertificateTimestamp::SCT_EMBEDDED);
+ }
+
+ int NumValidSCTsInStatusHistogram() {
+ return GetValueFromHistogram("Net.CertificateTransparency.SCTStatus",
+ ct::SCT_STATUS_OK);
}
protected:
scoped_ptr<MultiLogCTVerifier> verifier_;
scoped_refptr<X509Certificate> chain_;
+ scoped_refptr<X509Certificate> embedded_sct_chain_;
};
TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT) {
- scoped_refptr<X509Certificate> chain(
- CreateCertificateChainFromFile(GetTestCertsDirectory(),
- "ct-test-embedded-cert.pem",
- X509Certificate::FORMAT_AUTO));
- ASSERT_TRUE(chain);
- ASSERT_TRUE(CheckPrecertificateVerification(chain));
+ ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
}
TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA) {
@@ -151,21 +240,58 @@ TEST_F(MultiLogCTVerifierTest,
TEST_F(MultiLogCTVerifierTest,
IdentifiesSCTFromUnknownLog) {
- std::string sct(ct::GetTestSignedCertificateTimestamp());
+ std::string sct_list = GetSCTListWithInvalidSCT();
+ ct::CTVerifyResult result;
- // Change a byte inside the Log ID part of the SCT so it does
- // not match the log used in the tests
- sct[15] = 't';
+ EXPECT_NE(OK,
+ verifier_->Verify(
+ chain_, std::string(), sct_list, &result, BoundNetLog()));
+ EXPECT_EQ(1U, result.unknown_logs_scts.size());
+ EXPECT_EQ("", result.unknown_logs_scts[0]->log_description);
+}
- std::string sct_list;
- ASSERT_TRUE(ct::EncodeSCTListForTesting(sct, &sct_list));
+TEST_F(MultiLogCTVerifierTest, CountsValidSCTsInStatusHistogram) {
+ int num_valid_scts = NumValidSCTsInStatusHistogram();
+
+ ASSERT_TRUE(VerifySinglePrecertificateChain(embedded_sct_chain_));
+
+ EXPECT_EQ(num_valid_scts + 1, NumValidSCTsInStatusHistogram());
+}
+TEST_F(MultiLogCTVerifierTest, CountsInvalidSCTsInStatusHistogram) {
+ std::string sct_list = GetSCTListWithInvalidSCT();
ct::CTVerifyResult result;
+ int num_valid_scts = NumValidSCTsInStatusHistogram();
+ int num_invalid_scts = GetValueFromHistogram(
+ "Net.CertificateTransparency.SCTStatus", ct::SCT_STATUS_LOG_UNKNOWN);
+
EXPECT_NE(OK,
verifier_->Verify(chain_, std::string(), sct_list, &result,
BoundNetLog()));
- EXPECT_EQ(1U, result.unknown_logs_scts.size());
- EXPECT_EQ("", result.unknown_logs_scts[0]->log_description);
+
+ ASSERT_EQ(num_valid_scts, NumValidSCTsInStatusHistogram());
+ ASSERT_EQ(num_invalid_scts + 1,
+ GetValueFromHistogram("Net.CertificateTransparency.SCTStatus",
+ ct::SCT_STATUS_LOG_UNKNOWN));
+}
+
+TEST_F(MultiLogCTVerifierTest, CountsSingleEmbeddedSCTInConnectionsHistogram) {
+ int old_sct_count = NumConnectionsWithSingleSCT();
+ ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
+ EXPECT_EQ(old_sct_count + 1, NumConnectionsWithSingleSCT());
+}
+
+TEST_F(MultiLogCTVerifierTest, CountsSingleEmbeddedSCTInOriginsHistogram) {
+ int old_embedded_count = NumEmbeddedSCTsInHistogram();
+ ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
+ EXPECT_EQ(old_embedded_count + 1, NumEmbeddedSCTsInHistogram());
+}
+
+TEST_F(MultiLogCTVerifierTest, CountsZeroSCTsCorrectly) {
+ int connections_without_scts = GetValueFromHistogram(kSCTCountHistogram, 0);
+ EXPECT_FALSE(VerifySinglePrecertificateChain(chain_));
+ ASSERT_EQ(connections_without_scts + 1,
+ GetValueFromHistogram(kSCTCountHistogram, 0));
}
} // namespace
diff --git a/chromium/net/cert/multi_threaded_cert_verifier.cc b/chromium/net/cert/multi_threaded_cert_verifier.cc
index 4b2f37fbc55..3d1b048616e 100644
--- a/chromium/net/cert/multi_threaded_cert_verifier.cc
+++ b/chromium/net/cert/multi_threaded_cert_verifier.cc
@@ -15,6 +15,8 @@
#include "base/synchronization/lock.h"
#include "base/threading/worker_pool.h"
#include "base/time/time.h"
+#include "base/values.h"
+#include "net/base/hash_value.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/cert/cert_trust_anchor_provider.h"
@@ -78,6 +80,35 @@ const unsigned kMaxCacheEntries = 256;
// The number of seconds for which we'll cache a cache entry.
const unsigned kTTLSecs = 1800; // 30 minutes.
+base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* results = new base::DictionaryValue();
+ results->SetBoolean("has_md5", verify_result.has_md5);
+ results->SetBoolean("has_md2", verify_result.has_md2);
+ results->SetBoolean("has_md4", verify_result.has_md4);
+ results->SetBoolean("is_issued_by_known_root",
+ verify_result.is_issued_by_known_root);
+ results->SetBoolean("is_issued_by_additional_trust_anchor",
+ verify_result.is_issued_by_additional_trust_anchor);
+ results->SetBoolean("common_name_fallback_used",
+ verify_result.common_name_fallback_used);
+ results->SetInteger("cert_status", verify_result.cert_status);
+ results->Set(
+ "verified_cert",
+ NetLogX509CertificateCallback(verify_result.verified_cert, log_level));
+
+ base::ListValue* hashes = new base::ListValue();
+ for (std::vector<HashValue>::const_iterator it =
+ verify_result.public_key_hashes.begin();
+ it != verify_result.public_key_hashes.end();
+ ++it) {
+ hashes->AppendString(it->ToString());
+ }
+ results->Set("public_key_hashes", hashes);
+
+ return results;
+}
+
} // namespace
MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
@@ -348,14 +379,25 @@ class CertVerifierJob {
}
void HandleResult(
- const MultiThreadedCertVerifier::CachedResult& verify_result) {
+ const MultiThreadedCertVerifier::CachedResult& verify_result,
+ bool is_first_job) {
worker_ = NULL;
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
+ net_log_.EndEvent(
+ NetLog::TYPE_CERT_VERIFIER_JOB,
+ base::Bind(&CertVerifyResultCallback, verify_result.result));
+ base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
- base::TimeTicks::Now() - start_time_,
+ latency,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromMinutes(10),
100);
+ if (is_first_job) {
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency",
+ latency,
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMinutes(10),
+ 100);
+ }
PostAll(verify_result);
}
@@ -391,6 +433,7 @@ class CertVerifierJob {
MultiThreadedCertVerifier::MultiThreadedCertVerifier(
CertVerifyProc* verify_proc)
: cache_(kMaxCacheEntries),
+ first_job_(NULL),
requests_(0),
cache_hits_(0),
inflight_joins_(0),
@@ -474,6 +517,10 @@ int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
}
inflight_.insert(std::make_pair(key, job));
+ if (requests_ == 1) {
+ // Cleared in HandleResult.
+ first_job_ = job;
+ }
}
CertVerifierRequest* request =
@@ -551,8 +598,13 @@ void MultiThreadedCertVerifier::HandleResult(
}
CertVerifierJob* job = j->second;
inflight_.erase(j);
+ bool is_first_job = false;
+ if (first_job_ == job) {
+ is_first_job = true;
+ first_job_ = NULL;
+ }
- job->HandleResult(cached_result);
+ job->HandleResult(cached_result, is_first_job);
delete job;
}
@@ -564,3 +616,4 @@ void MultiThreadedCertVerifier::OnCACertChanged(
}
} // namespace net
+
diff --git a/chromium/net/cert/multi_threaded_cert_verifier.h b/chromium/net/cert/multi_threaded_cert_verifier.h
index f4e278793f1..8f00ebe301f 100644
--- a/chromium/net/cert/multi_threaded_cert_verifier.h
+++ b/chromium/net/cert/multi_threaded_cert_verifier.h
@@ -153,6 +153,9 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier
// place.
std::map<RequestParams, CertVerifierJob*> inflight_;
+ // A non-owning pointer to the first job for histogramming.
+ CertVerifierJob* first_job_;
+
uint64 requests_;
uint64 cache_hits_;
uint64 inflight_joins_;
diff --git a/chromium/net/cert/nss_cert_database.cc b/chromium/net/cert/nss_cert_database.cc
index d4ece105745..c1fc4f9d0cc 100644
--- a/chromium/net/cert/nss_cert_database.cc
+++ b/chromium/net/cert/nss_cert_database.cc
@@ -10,10 +10,15 @@
#include <pk11pub.h>
#include <secmod.h>
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/singleton.h"
#include "base/observer_list_threadsafe.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/threading/worker_pool.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_nss_types.h"
@@ -35,6 +40,13 @@ namespace psm = mozilla_security_manager;
namespace net {
+namespace {
+
+base::LazyInstance<NSSCertDatabase>::Leaky
+ g_nss_cert_database = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
NSSCertDatabase::ImportCertFailure::ImportCertFailure(
const scoped_refptr<X509Certificate>& cert,
int err)
@@ -44,30 +56,57 @@ NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
// static
NSSCertDatabase* NSSCertDatabase::GetInstance() {
- return Singleton<NSSCertDatabase,
- LeakySingletonTraits<NSSCertDatabase> >::get();
+ // TODO(mattm): Remove this ifdef guard once the linux impl of
+ // GetNSSCertDatabaseForResourceContext does not call GetInstance.
+#if defined(OS_CHROMEOS)
+ LOG(ERROR) << "NSSCertDatabase::GetInstance() is deprecated."
+ << "See http://crbug.com/329735.";
+#endif
+ return &g_nss_cert_database.Get();
}
NSSCertDatabase::NSSCertDatabase()
- : observer_list_(new ObserverListThreadSafe<Observer>) {
- crypto::EnsureNSSInit();
+ : observer_list_(new ObserverListThreadSafe<Observer>),
+ weak_factory_(this) {
+ // This also makes sure that NSS has been initialized.
+ CertDatabase::GetInstance()->ObserveNSSCertDatabase(this);
+
psm::EnsurePKCS12Init();
}
NSSCertDatabase::~NSSCertDatabase() {}
-void NSSCertDatabase::ListCerts(CertificateList* certs) {
- certs->clear();
+void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
+ ListCertsImpl(crypto::ScopedPK11Slot(), certs);
+}
- CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
- CERTCertListNode* node;
- for (node = CERT_LIST_HEAD(cert_list);
- !CERT_LIST_END(node, cert_list);
- node = CERT_LIST_NEXT(node)) {
- certs->push_back(X509Certificate::CreateFromHandle(
- node->cert, X509Certificate::OSCertHandles()));
- }
- CERT_DestroyCertList(cert_list);
+void NSSCertDatabase::ListCerts(
+ const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
+ scoped_ptr<CertificateList> certs(new CertificateList());
+
+ // base::Passed will NULL out |certs|, so cache the underlying pointer here.
+ CertificateList* raw_certs = certs.get();
+ GetSlowTaskRunner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&NSSCertDatabase::ListCertsImpl,
+ base::Passed(crypto::ScopedPK11Slot()),
+ base::Unretained(raw_certs)),
+ base::Bind(callback, base::Passed(&certs)));
+}
+
+void NSSCertDatabase::ListCertsInSlot(const ListCertsCallback& callback,
+ PK11SlotInfo* slot) {
+ DCHECK(slot);
+ scoped_ptr<CertificateList> certs(new CertificateList());
+
+ // base::Passed will NULL out |certs|, so cache the underlying pointer here.
+ CertificateList* raw_certs = certs.get();
+ GetSlowTaskRunner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&NSSCertDatabase::ListCertsImpl,
+ base::Passed(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
+ base::Unretained(raw_certs)),
+ base::Bind(callback, base::Passed(&certs)));
}
crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
@@ -117,6 +156,9 @@ int NSSCertDatabase::ImportFromPKCS12(
const base::string16& password,
bool is_extractable,
net::CertificateList* imported_certs) {
+ DVLOG(1) << __func__ << " "
+ << PK11_GetModuleID(module->os_module_handle()) << ":"
+ << PK11_GetSlotID(module->os_module_handle());
int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
data.data(), data.size(),
password,
@@ -154,7 +196,7 @@ X509Certificate* NSSCertDatabase::FindRootInList(
&certn_1->os_cert_handle()->subject) == SECEqual)
return certn_1;
- VLOG(1) << "certificate list is not a hierarchy";
+ LOG(WARNING) << "certificate list is not a hierarchy";
return cert0;
}
@@ -289,31 +331,26 @@ bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
return success;
}
-bool NSSCertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
- // For some reason, PK11_DeleteTokenCertAndKey only calls
- // SEC_DeletePermCertificate if the private key is found. So, we check
- // whether a private key exists before deciding which function to call to
- // delete the cert.
- SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
- NULL);
- if (privKey) {
- SECKEY_DestroyPrivateKey(privKey);
- if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
- LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
- return false;
- }
- } else {
- if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
- LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
- return false;
- }
- }
-
+bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
+ if (!DeleteCertAndKeyImpl(cert))
+ return false;
NotifyObserversOfCertRemoved(cert);
-
return true;
}
+void NSSCertDatabase::DeleteCertAndKeyAsync(
+ const scoped_refptr<X509Certificate>& cert,
+ const DeleteCertCallback& callback) {
+ base::PostTaskAndReplyWithResult(
+ GetSlowTaskRunner().get(),
+ FROM_HERE,
+ base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
+ base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
+ weak_factory_.GetWeakPtr(),
+ cert,
+ callback));
+}
+
bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
PK11SlotInfo* slot = cert->os_cert_handle()->slot;
return slot && PK11_IsReadOnly(slot);
@@ -332,6 +369,46 @@ void NSSCertDatabase::RemoveObserver(Observer* observer) {
observer_list_->RemoveObserver(observer);
}
+void NSSCertDatabase::SetSlowTaskRunnerForTest(
+ const scoped_refptr<base::TaskRunner>& task_runner) {
+ slow_task_runner_for_test_ = task_runner;
+}
+
+// static
+void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
+ CertificateList* certs) {
+ certs->clear();
+
+ CERTCertList* cert_list = NULL;
+ if (slot)
+ cert_list = PK11_ListCertsInSlot(slot.get());
+ else
+ cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
+
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ certs->push_back(X509Certificate::CreateFromHandle(
+ node->cert, X509Certificate::OSCertHandles()));
+ }
+ CERT_DestroyCertList(cert_list);
+}
+
+scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
+ if (slow_task_runner_for_test_)
+ return slow_task_runner_for_test_;
+ return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
+}
+
+void NSSCertDatabase::NotifyCertRemovalAndCallBack(
+ scoped_refptr<X509Certificate> cert,
+ const DeleteCertCallback& callback,
+ bool success) {
+ if (success)
+ NotifyObserversOfCertRemoved(cert);
+ callback.Run(success);
+}
+
void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
}
@@ -347,4 +424,28 @@ void NSSCertDatabase::NotifyObserversOfCACertChanged(
&Observer::OnCACertChanged, make_scoped_refptr(cert));
}
+// static
+bool NSSCertDatabase::DeleteCertAndKeyImpl(
+ scoped_refptr<X509Certificate> cert) {
+ // For some reason, PK11_DeleteTokenCertAndKey only calls
+ // SEC_DeletePermCertificate if the private key is found. So, we check
+ // whether a private key exists before deciding which function to call to
+ // delete the cert.
+ SECKEYPrivateKey* privKey =
+ PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
+ if (privKey) {
+ SECKEY_DestroyPrivateKey(privKey);
+ if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
+ LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
+ return false;
+ }
+ } else {
+ if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
+ LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace net
diff --git a/chromium/net/cert/nss_cert_database.h b/chromium/net/cert/nss_cert_database.h
index 28751a46d01..1c4daf81793 100644
--- a/chromium/net/cert/nss_cert_database.h
+++ b/chromium/net/cert/nss_cert_database.h
@@ -9,14 +9,20 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "crypto/scoped_nss_types.h"
+#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/cert/cert_type.h"
#include "net/cert/x509_certificate.h"
-template <typename T> struct DefaultSingletonTraits;
+namespace base {
+template <typename T> struct DefaultLazyInstanceTraits;
+class TaskRunner;
+}
template <class ObserverType> class ObserverListThreadSafe;
namespace net {
@@ -89,17 +95,37 @@ class NET_EXPORT NSSCertDatabase {
DISTRUSTED_OBJ_SIGN = 1 << 5,
};
+ typedef base::Callback<void(scoped_ptr<CertificateList> certs)>
+ ListCertsCallback;
+
+ typedef base::Callback<void(bool)> DeleteCertCallback;
+
+ // DEPRECATED: See http://crbug.com/329735.
static NSSCertDatabase* GetInstance();
// Get a list of unique certificates in the certificate database (one
// instance of all certificates).
- void ListCerts(CertificateList* certs);
+ // DEPRECATED by |ListCerts|. See http://crbug.com/340460.
+ virtual void ListCertsSync(CertificateList* certs);
+
+ // Asynchronously get a list of unique certificates in the certificate
+ // database (one instance of all certificates). Note that the callback may be
+ // run even after the database is deleted.
+ virtual void ListCerts(const ListCertsCallback& callback);
+
+ // Get a list of certificates in the certificate database of the given slot.
+ // Note that the callback may be run even after the database is deleted.
+ // Must be called on the IO thread and it calls |callback| on the IO thread.
+ // This does not block by retrieving the certs asynchronously on a worker
+ // thread. Never calls |callback| synchronously.
+ virtual void ListCertsInSlot(const ListCertsCallback& callback,
+ PK11SlotInfo* slot);
// Get the default slot for public key data.
- crypto::ScopedPK11Slot GetPublicSlot() const;
+ virtual crypto::ScopedPK11Slot GetPublicSlot() const;
// Get the default slot for private key or mixed private/public key data.
- crypto::ScopedPK11Slot GetPrivateSlot() const;
+ virtual crypto::ScopedPK11Slot GetPrivateSlot() const;
// Get the default module for public key data.
// The returned pointer must be stored in a scoped_refptr<CryptoModule>.
@@ -116,7 +142,7 @@ class NET_EXPORT NSSCertDatabase {
// Get all modules.
// If |need_rw| is true, only writable modules will be returned.
// TODO(mattm): come up with better alternative to CryptoModuleList.
- void ListModules(CryptoModuleList* modules, bool need_rw) const;
+ virtual void ListModules(CryptoModuleList* modules, bool need_rw) const;
// Import certificates and private keys from PKCS #12 blob into the module.
// If |is_extractable| is false, mark the private key as being unextractable
@@ -185,7 +211,13 @@ class NET_EXPORT NSSCertDatabase {
// Delete certificate and associated private key (if one exists).
// |cert| is still valid when this function returns. Returns true on
// success.
- bool DeleteCertAndKey(const X509Certificate* cert);
+ bool DeleteCertAndKey(X509Certificate* cert);
+
+ // Like DeleteCertAndKey but does not block by running the removal on a worker
+ // thread. This must be called on IO thread and it will run |callback| on IO
+ // thread. Never calls |callback| synchronously.
+ void DeleteCertAndKeyAsync(const scoped_refptr<X509Certificate>& cert,
+ const DeleteCertCallback& callback);
// Check whether cert is stored in a readonly slot.
bool IsReadOnly(const X509Certificate* cert) const;
@@ -196,25 +228,61 @@ class NET_EXPORT NSSCertDatabase {
// Registers |observer| to receive notifications of certificate changes. The
// thread on which this is called is the thread on which |observer| will be
// called back with notifications.
+ // NOTE: CertDatabase::AddObserver should be preferred. Observers registered
+ // here will only receive notifications generated directly through the
+ // NSSCertDatabase, but not those from the CertDatabase. The CertDatabase
+ // observers will receive both.
void AddObserver(Observer* observer);
// Unregisters |observer| from receiving notifications. This must be called
// on the same thread on which AddObserver() was called.
void RemoveObserver(Observer* observer);
- private:
- friend struct DefaultSingletonTraits<NSSCertDatabase>;
+ // Overrides task runner that's used for running slow tasks.
+ void SetSlowTaskRunnerForTest(
+ const scoped_refptr<base::TaskRunner>& task_runner);
+ protected:
NSSCertDatabase();
- ~NSSCertDatabase();
+ virtual ~NSSCertDatabase();
+
+ // Certificate listing implementation used by |ListCerts*| and
+ // |ListCertsSync|. Static so it may safely be used on the worker thread.
+ // If |slot| is NULL, obtains the certs of all slots, otherwise only of
+ // |slot|.
+ static void ListCertsImpl(crypto::ScopedPK11Slot slot,
+ CertificateList* certs);
+
+ // Gets task runner that should be used for slow tasks like certificate
+ // listing. Defaults to a base::WorkerPool runner, but may be overriden
+ // in tests (see SetSlowTaskRunnerForTest).
+ scoped_refptr<base::TaskRunner> GetSlowTaskRunner() const;
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<NSSCertDatabase>;
+
+ // Notifies observers of the removal of |cert| and calls |callback| with
+ // |success| as argument.
+ void NotifyCertRemovalAndCallBack(scoped_refptr<X509Certificate> cert,
+ const DeleteCertCallback& callback,
+ bool success);
// Broadcasts notifications to all registered observers.
void NotifyObserversOfCertAdded(const X509Certificate* cert);
void NotifyObserversOfCertRemoved(const X509Certificate* cert);
void NotifyObserversOfCACertChanged(const X509Certificate* cert);
+ // Certificate removal implementation used by |DeleteCertAndKey*|. Static so
+ // it may safely be used on the worker thread.
+ static bool DeleteCertAndKeyImpl(scoped_refptr<X509Certificate> cert);
+
+ // Task runner that should be used in tests if set.
+ scoped_refptr<base::TaskRunner> slow_task_runner_for_test_;
+
const scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+ base::WeakPtrFactory<NSSCertDatabase> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(NSSCertDatabase);
};
diff --git a/chromium/net/cert/nss_cert_database_chromeos.cc b/chromium/net/cert/nss_cert_database_chromeos.cc
new file mode 100644
index 00000000000..41852a742a7
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database_chromeos.cc
@@ -0,0 +1,91 @@
+// 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/cert/nss_cert_database_chromeos.h"
+
+#include <cert.h>
+#include <pk11pub.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/task_runner.h"
+#include "net/base/crypto_module.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+NSSCertDatabaseChromeOS::NSSCertDatabaseChromeOS(
+ crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot)
+ : public_slot_(public_slot.Pass()),
+ private_slot_(private_slot.Pass()) {
+ profile_filter_.Init(GetPublicSlot(), GetPrivateSlot());
+}
+
+NSSCertDatabaseChromeOS::~NSSCertDatabaseChromeOS() {}
+
+void NSSCertDatabaseChromeOS::ListCertsSync(CertificateList* certs) {
+ ListCertsImpl(profile_filter_, certs);
+}
+
+void NSSCertDatabaseChromeOS::ListCerts(
+ const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
+ scoped_ptr<CertificateList> certs(new CertificateList());
+
+ // base::Pased will NULL out |certs|, so cache the underlying pointer here.
+ CertificateList* raw_certs = certs.get();
+ GetSlowTaskRunner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&NSSCertDatabaseChromeOS::ListCertsImpl,
+ profile_filter_,
+ base::Unretained(raw_certs)),
+ base::Bind(callback, base::Passed(&certs)));
+}
+
+crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPublicSlot() const {
+ return crypto::ScopedPK11Slot(
+ public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL);
+}
+
+crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetPrivateSlot() const {
+ return crypto::ScopedPK11Slot(
+ private_slot_ ? PK11_ReferenceSlot(private_slot_.get()) : NULL);
+}
+
+void NSSCertDatabaseChromeOS::ListModules(CryptoModuleList* modules,
+ bool need_rw) const {
+ NSSCertDatabase::ListModules(modules, need_rw);
+
+ size_t pre_size = modules->size();
+ modules->erase(
+ std::remove_if(
+ modules->begin(),
+ modules->end(),
+ NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate(
+ profile_filter_)),
+ modules->end());
+ DVLOG(1) << "filtered " << pre_size - modules->size() << " of " << pre_size
+ << " modules";
+}
+
+void NSSCertDatabaseChromeOS::ListCertsImpl(
+ const NSSProfileFilterChromeOS& profile_filter,
+ CertificateList* certs) {
+ NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot(), certs);
+
+ size_t pre_size = certs->size();
+ certs->erase(std::remove_if(
+ certs->begin(),
+ certs->end(),
+ NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate(
+ profile_filter)),
+ certs->end());
+ DVLOG(1) << "filtered " << pre_size - certs->size() << " of " << pre_size
+ << " certs";
+}
+
+} // namespace net
diff --git a/chromium/net/cert/nss_cert_database_chromeos.h b/chromium/net/cert/nss_cert_database_chromeos.h
new file mode 100644
index 00000000000..07a1e67f31a
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database_chromeos.h
@@ -0,0 +1,53 @@
+// 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_CERT_NSS_CERT_DATABASE_CHROMEOS_
+#define NET_CERT_NSS_CERT_DATABASE_CHROMEOS_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/net_export.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/nss_profile_filter_chromeos.h"
+
+namespace net {
+
+class NET_EXPORT NSSCertDatabaseChromeOS : public NSSCertDatabase {
+ public:
+ NSSCertDatabaseChromeOS(crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot);
+ virtual ~NSSCertDatabaseChromeOS();
+
+ // NSSCertDatabase implementation.
+ virtual void ListCertsSync(CertificateList* certs) OVERRIDE;
+ virtual void ListCerts(const NSSCertDatabase::ListCertsCallback& callback)
+ OVERRIDE;
+ virtual crypto::ScopedPK11Slot GetPublicSlot() const OVERRIDE;
+ virtual crypto::ScopedPK11Slot GetPrivateSlot() const OVERRIDE;
+ virtual void ListModules(CryptoModuleList* modules, bool need_rw) const
+ OVERRIDE;
+
+ // TODO(mattm): handle trust setting, deletion, etc correctly when certs exist
+ // in multiple slots.
+ // TODO(mattm): handle trust setting correctly for certs in read-only slots.
+
+ private:
+ // Certificate listing implementation used by |ListCerts| and |ListCertsSync|.
+ // The certificate list normally returned by NSSCertDatabase::ListCertsImpl
+ // is additionally filtered by |profile_filter|.
+ // Static so it may safely be used on the worker thread.
+ static void ListCertsImpl(const NSSProfileFilterChromeOS& profile_filter,
+ CertificateList* certs);
+
+ crypto::ScopedPK11Slot public_slot_;
+ crypto::ScopedPK11Slot private_slot_;
+ NSSProfileFilterChromeOS profile_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(NSSCertDatabaseChromeOS);
+};
+
+} // namespace net
+
+#endif // NET_CERT_NSS_CERT_DATABASE_CHROMEOS_
diff --git a/chromium/net/cert/nss_cert_database_chromeos_unittest.cc b/chromium/net/cert/nss_cert_database_chromeos_unittest.cc
new file mode 100644
index 00000000000..324575d0b1e
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database_chromeos_unittest.cc
@@ -0,0 +1,279 @@
+// 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/cert/nss_cert_database_chromeos.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_database.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+bool IsCertInCertificateList(const X509Certificate* cert,
+ const CertificateList& cert_list) {
+ for (CertificateList::const_iterator it = cert_list.begin();
+ it != cert_list.end();
+ ++it) {
+ if (X509Certificate::IsSameOSCert((*it)->os_cert_handle(),
+ cert->os_cert_handle()))
+ return true;
+ }
+ return false;
+}
+
+void SwapCertLists(CertificateList* destination,
+ scoped_ptr<CertificateList> source) {
+ ASSERT_TRUE(destination);
+ ASSERT_TRUE(source);
+
+ destination->swap(*source);
+}
+
+} // namespace
+
+class NSSCertDatabaseChromeOSTest : public testing::Test,
+ public CertDatabase::Observer {
+ public:
+ NSSCertDatabaseChromeOSTest()
+ : observer_added_(false), user_1_("user1"), user_2_("user2") {}
+
+ virtual void SetUp() OVERRIDE {
+ // Initialize nss_util slots.
+ ASSERT_TRUE(user_1_.constructed_successfully());
+ ASSERT_TRUE(user_2_.constructed_successfully());
+ user_1_.FinishInit();
+ user_2_.FinishInit();
+
+ // Create NSSCertDatabaseChromeOS for each user.
+ db_1_.reset(new NSSCertDatabaseChromeOS(
+ crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
+ crypto::GetPrivateSlotForChromeOSUser(
+ user_1_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>())));
+ db_1_->SetSlowTaskRunnerForTest(base::MessageLoopProxy::current());
+ db_2_.reset(new NSSCertDatabaseChromeOS(
+ crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
+ crypto::GetPrivateSlotForChromeOSUser(
+ user_2_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>())));
+ db_2_->SetSlowTaskRunnerForTest(base::MessageLoopProxy::current());
+
+ // Add observer to CertDatabase for checking that notifications from
+ // NSSCertDatabaseChromeOS are proxied to the CertDatabase.
+ CertDatabase::GetInstance()->AddObserver(this);
+ observer_added_ = true;
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (observer_added_)
+ CertDatabase::GetInstance()->RemoveObserver(this);
+ }
+
+ // CertDatabase::Observer:
+ virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
+ added_.push_back(cert ? cert->os_cert_handle() : NULL);
+ }
+
+ virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {}
+
+ virtual void OnCACertChanged(const X509Certificate* cert) OVERRIDE {
+ added_ca_.push_back(cert ? cert->os_cert_handle() : NULL);
+ }
+
+ protected:
+ bool observer_added_;
+ // Certificates that were passed to the CertDatabase observers.
+ std::vector<CERTCertificate*> added_ca_;
+ std::vector<CERTCertificate*> added_;
+
+ crypto::ScopedTestNSSChromeOSUser user_1_;
+ crypto::ScopedTestNSSChromeOSUser user_2_;
+ scoped_ptr<NSSCertDatabaseChromeOS> db_1_;
+ scoped_ptr<NSSCertDatabaseChromeOS> db_2_;
+};
+
+// Test that ListModules() on each user includes that user's NSS software slot,
+// and does not include the software slot of the other user. (Does not check the
+// private slot, since it is the same as the public slot in tests.)
+TEST_F(NSSCertDatabaseChromeOSTest, ListModules) {
+ CryptoModuleList modules_1;
+ CryptoModuleList modules_2;
+
+ db_1_->ListModules(&modules_1, false /* need_rw */);
+ db_2_->ListModules(&modules_2, false /* need_rw */);
+
+ bool found_1 = false;
+ for (CryptoModuleList::iterator it = modules_1.begin(); it != modules_1.end();
+ ++it) {
+ EXPECT_NE(db_2_->GetPublicSlot().get(), (*it)->os_module_handle());
+ if ((*it)->os_module_handle() == db_1_->GetPublicSlot().get())
+ found_1 = true;
+ }
+ EXPECT_TRUE(found_1);
+
+ bool found_2 = false;
+ for (CryptoModuleList::iterator it = modules_2.begin(); it != modules_2.end();
+ ++it) {
+ EXPECT_NE(db_1_->GetPublicSlot().get(), (*it)->os_module_handle());
+ if ((*it)->os_module_handle() == db_2_->GetPublicSlot().get())
+ found_2 = true;
+ }
+ EXPECT_TRUE(found_2);
+}
+
+// Test that ImportCACerts imports the cert to the correct slot, and that
+// ListCerts includes the added cert for the correct user, and does not include
+// it for the other user.
+TEST_F(NSSCertDatabaseChromeOSTest, ImportCACerts) {
+ // Load test certs from disk.
+ CertificateList certs_1 =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_1.size());
+
+ CertificateList certs_2 =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "2048-rsa-root.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_2.size());
+
+ // Import one cert for each user.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(
+ db_1_->ImportCACerts(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+ failed.clear();
+ EXPECT_TRUE(
+ db_2_->ImportCACerts(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Get cert list for each user.
+ CertificateList user_1_certlist;
+ CertificateList user_2_certlist;
+ db_1_->ListCertsSync(&user_1_certlist);
+ db_2_->ListCertsSync(&user_2_certlist);
+
+ // Check that the imported certs only shows up in the list for the user that
+ // imported them.
+ EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist));
+ EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist));
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist));
+ EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist));
+
+ // Run the message loop so the observer notifications get processed.
+ base::RunLoop().RunUntilIdle();
+ // Should have gotten two OnCACertChanged notifications.
+ ASSERT_EQ(2U, added_ca_.size());
+ // TODO(mattm): make NSSCertDatabase actually pass the cert to the callback,
+ // and enable these checks:
+ // EXPECT_EQ(certs_1[0]->os_cert_handle(), added_ca_[0]);
+ // EXPECT_EQ(certs_2[0]->os_cert_handle(), added_ca_[1]);
+ EXPECT_EQ(0U, added_.size());
+
+ // Tests that the new certs are loaded by async ListCerts method.
+ CertificateList user_1_certlist_async;
+ CertificateList user_2_certlist_async;
+ db_1_->ListCerts(
+ base::Bind(&SwapCertLists, base::Unretained(&user_1_certlist_async)));
+ db_2_->ListCerts(
+ base::Bind(&SwapCertLists, base::Unretained(&user_2_certlist_async)));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist_async));
+ EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist_async));
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist_async));
+ EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist_async));
+}
+
+// Test that ImportServerCerts imports the cert to the correct slot, and that
+// ListCerts includes the added cert for the correct user, and does not include
+// it for the other user.
+TEST_F(NSSCertDatabaseChromeOSTest, ImportServerCert) {
+ // Load test certs from disk.
+ CertificateList certs_1 = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem", X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_1.size());
+
+ CertificateList certs_2 =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "2048-rsa-ee-by-2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_2.size());
+
+ // Import one cert for each user.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(
+ db_1_->ImportServerCert(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+ failed.clear();
+ EXPECT_TRUE(
+ db_2_->ImportServerCert(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Get cert list for each user.
+ CertificateList user_1_certlist;
+ CertificateList user_2_certlist;
+ db_1_->ListCertsSync(&user_1_certlist);
+ db_2_->ListCertsSync(&user_2_certlist);
+
+ // Check that the imported certs only shows up in the list for the user that
+ // imported them.
+ EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist));
+ EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist));
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist));
+ EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist));
+
+ // Run the message loop so the observer notifications get processed.
+ base::RunLoop().RunUntilIdle();
+ // TODO(mattm): ImportServerCert doesn't actually cause any observers to
+ // fire. Is that correct?
+ EXPECT_EQ(0U, added_ca_.size());
+ EXPECT_EQ(0U, added_.size());
+
+ // Tests that the new certs are loaded by async ListCerts method.
+ CertificateList user_1_certlist_async;
+ CertificateList user_2_certlist_async;
+ db_1_->ListCerts(
+ base::Bind(&SwapCertLists, base::Unretained(&user_1_certlist_async)));
+ db_2_->ListCerts(
+ base::Bind(&SwapCertLists, base::Unretained(&user_2_certlist_async)));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_1[0], user_1_certlist_async));
+ EXPECT_FALSE(IsCertInCertificateList(certs_1[0], user_2_certlist_async));
+
+ EXPECT_TRUE(IsCertInCertificateList(certs_2[0], user_2_certlist_async));
+ EXPECT_FALSE(IsCertInCertificateList(certs_2[0], user_1_certlist_async));
+}
+
+// Tests that There is no crash if the database is deleted while ListCerts
+// is being processed on the worker pool.
+TEST_F(NSSCertDatabaseChromeOSTest, NoCrashIfShutdownBeforeDoneOnWorkerPool) {
+ CertificateList certlist;
+ db_1_->ListCerts(base::Bind(&SwapCertLists, base::Unretained(&certlist)));
+ EXPECT_EQ(0U, certlist.size());
+
+ db_1_.reset();
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_LT(0U, certlist.size());
+}
+
+} // namespace net
diff --git a/chromium/net/cert/nss_cert_database_unittest.cc b/chromium/net/cert/nss_cert_database_unittest.cc
index 4ad1192e7af..342e0b9701a 100644
--- a/chromium/net/cert/nss_cert_database_unittest.cc
+++ b/chromium/net/cert/nss_cert_database_unittest.cc
@@ -8,11 +8,14 @@
#include <algorithm>
+#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/path_service.h"
+#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -37,8 +40,20 @@
#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
#endif
+using base::ASCIIToUTF16;
+
namespace net {
+namespace {
+
+void SwapCertList(CertificateList* destination,
+ scoped_ptr<CertificateList> source) {
+ ASSERT_TRUE(destination);
+ destination->swap(*source);
+}
+
+} // namespace
+
class CertDatabaseNSSTest : public testing::Test {
public:
virtual void SetUp() {
@@ -125,11 +140,26 @@ class CertDatabaseNSSTest : public testing::Test {
crypto::ScopedTestNSSDB test_nssdb_;
};
+TEST_F(CertDatabaseNSSTest, ListCertsSync) {
+ // This test isn't terribly useful, though it will at least let valgrind test
+ // for leaks.
+ CertificateList certs;
+ cert_db_->ListCertsSync(&certs);
+ // The test DB is empty, but let's assume there will always be something in
+ // the other slots.
+ EXPECT_LT(0U, certs.size());
+}
+
TEST_F(CertDatabaseNSSTest, ListCerts) {
// This test isn't terribly useful, though it will at least let valgrind test
// for leaks.
CertificateList certs;
- cert_db_->ListCerts(&certs);
+ cert_db_->SetSlowTaskRunnerForTest(base::MessageLoopProxy::current());
+ cert_db_->ListCerts(base::Bind(&SwapCertList, base::Unretained(&certs)));
+ EXPECT_EQ(0U, certs.size());
+
+ base::RunLoop().RunUntilIdle();
+
// The test DB is empty, but let's assume there will always be something in
// the other slots.
EXPECT_LT(0U, certs.size());
@@ -589,13 +619,6 @@ TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned) {
}
TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned_Trusted) {
- // When using CERT_PKIXVerifyCert (which we do), server trust only works from
- // 3.13.4 onwards. See https://bugzilla.mozilla.org/show_bug.cgi?id=647364.
- if (!NSS_VersionCheck("3.13.4")) {
- LOG(INFO) << "test skipped on NSS < 3.13.4";
- return;
- }
-
CertificateList certs;
ASSERT_TRUE(ReadCertIntoList("punycodetest.der", &certs));
@@ -664,12 +687,6 @@ TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert) {
}
TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert_DistrustServer) {
- // Explicit distrust only works starting in NSS 3.13.
- if (!NSS_VersionCheck("3.13")) {
- LOG(INFO) << "test skipped on NSS < 3.13";
- return;
- }
-
CertificateList ca_certs = CreateCertificateListFromFile(
GetTestCertsDirectory(), "root_ca_cert.pem",
X509Certificate::FORMAT_AUTO);
@@ -758,12 +775,6 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa) {
EXPECT_EQ(OK, error);
EXPECT_EQ(0U, verify_result.cert_status);
- // Explicit distrust only works starting in NSS 3.13.
- if (!NSS_VersionCheck("3.13")) {
- LOG(INFO) << "test partially skipped on NSS < 3.13";
- return;
- }
-
// Trust the root cert and distrust the intermediate.
EXPECT_TRUE(cert_db_->SetCertTrust(
ca_certs[0].get(), CA_CERT, NSSCertDatabase::TRUSTED_SSL));
@@ -927,12 +938,6 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa3) {
}
TEST_F(CertDatabaseNSSTest, TrustIntermediateCa4) {
- // Explicit distrust only works starting in NSS 3.13.
- if (!NSS_VersionCheck("3.13")) {
- LOG(INFO) << "test skipped on NSS < 3.13";
- return;
- }
-
NSSCertDatabase::ImportCertFailureList failed;
CertificateList ca_certs = CreateCertificateListFromFile(
diff --git a/chromium/net/cert/nss_profile_filter_chromeos.cc b/chromium/net/cert/nss_profile_filter_chromeos.cc
new file mode 100644
index 00000000000..e555750b89c
--- /dev/null
+++ b/chromium/net/cert/nss_profile_filter_chromeos.cc
@@ -0,0 +1,141 @@
+// 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/cert/nss_profile_filter_chromeos.h"
+
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+std::string CertSlotsString(CERTCertificate* cert) {
+ std::string result;
+ crypto::ScopedPK11SlotList slots_for_cert(
+ PK11_GetAllSlotsForCert(cert, NULL));
+ for (PK11SlotListElement* slot_element =
+ PK11_GetFirstSafe(slots_for_cert.get());
+ slot_element;
+ slot_element =
+ PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) {
+ if (!result.empty())
+ result += ',';
+ base::StringAppendF(&result,
+ "%lu:%lu",
+ PK11_GetModuleID(slot_element->slot),
+ PK11_GetSlotID(slot_element->slot));
+ }
+ return result;
+}
+
+} // namespace
+
+NSSProfileFilterChromeOS::NSSProfileFilterChromeOS() {}
+
+NSSProfileFilterChromeOS::NSSProfileFilterChromeOS(
+ const NSSProfileFilterChromeOS& other) {
+ public_slot_.reset(other.public_slot_ ?
+ PK11_ReferenceSlot(other.public_slot_.get()) :
+ NULL);
+ private_slot_.reset(other.private_slot_ ?
+ PK11_ReferenceSlot(other.private_slot_.get()) :
+ NULL);
+}
+
+NSSProfileFilterChromeOS::~NSSProfileFilterChromeOS() {}
+
+NSSProfileFilterChromeOS& NSSProfileFilterChromeOS::operator=(
+ const NSSProfileFilterChromeOS& other) {
+ public_slot_.reset(other.public_slot_ ?
+ PK11_ReferenceSlot(other.public_slot_.get()) :
+ NULL);
+ private_slot_.reset(other.private_slot_ ?
+ PK11_ReferenceSlot(other.private_slot_.get()) :
+ NULL);
+ return *this;
+}
+
+void NSSProfileFilterChromeOS::Init(crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot) {
+ // crypto::ScopedPK11Slot actually holds a reference counted object.
+ // Because scoped_ptr<T> assignment is a no-op if it already points to
+ // the same pointer, a reference would be leaked because .Pass() does
+ // not release its reference, and the receiving object won't free
+ // its copy.
+ if (public_slot_.get() != public_slot.get())
+ public_slot_ = public_slot.Pass();
+ if (private_slot_.get() != private_slot.get())
+ private_slot_ = private_slot.Pass();
+}
+
+bool NSSProfileFilterChromeOS::IsModuleAllowed(PK11SlotInfo* slot) const {
+ // If this is one of the public/private slots for this profile, allow it.
+ if (slot == public_slot_.get() || slot == private_slot_.get())
+ return true;
+ // Allow the root certs module.
+ if (PK11_HasRootCerts(slot))
+ return true;
+ // If it's from the read-only slots, allow it.
+ if (PK11_IsInternal(slot) && !PK11_IsRemovable(slot))
+ return true;
+ // If |public_slot_| or |private_slot_| is null, there isn't a way to get the
+ // modules to use in the final test.
+ if (!public_slot_.get() || !private_slot_.get())
+ return false;
+ // If this is not the internal (file-system) module or the TPM module, allow
+ // it.
+ SECMODModule* module_for_slot = PK11_GetModule(slot);
+ if (module_for_slot != PK11_GetModule(public_slot_.get()) &&
+ module_for_slot != PK11_GetModule(private_slot_.get()))
+ return true;
+ return false;
+}
+
+bool NSSProfileFilterChromeOS::IsCertAllowed(CERTCertificate* cert) const {
+ crypto::ScopedPK11SlotList slots_for_cert(
+ PK11_GetAllSlotsForCert(cert, NULL));
+ if (!slots_for_cert) {
+ DVLOG(2) << "cert no slots: " << base::StringPiece(cert->nickname);
+ return false;
+ }
+
+ for (PK11SlotListElement* slot_element =
+ PK11_GetFirstSafe(slots_for_cert.get());
+ slot_element;
+ slot_element =
+ PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) {
+ if (IsModuleAllowed(slot_element->slot)) {
+ DVLOG(3) << "cert from " << CertSlotsString(cert)
+ << " allowed: " << base::StringPiece(cert->nickname);
+ PK11_FreeSlotListElement(slots_for_cert.get(), slot_element);
+ return true;
+ }
+ }
+ DVLOG(2) << "cert from " << CertSlotsString(cert)
+ << " filtered: " << base::StringPiece(cert->nickname);
+ return false;
+}
+
+NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::
+ CertNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter)
+ : filter_(filter) {}
+
+bool NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::operator()(
+ const scoped_refptr<X509Certificate>& cert) const {
+ return !filter_.IsCertAllowed(cert->os_cert_handle());
+}
+
+NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::
+ ModuleNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter)
+ : filter_(filter) {}
+
+bool NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::operator()(
+ const scoped_refptr<CryptoModule>& module) const {
+ return !filter_.IsModuleAllowed(module->os_module_handle());
+}
+
+} // namespace net
+
diff --git a/chromium/net/cert/nss_profile_filter_chromeos.h b/chromium/net/cert/nss_profile_filter_chromeos.h
new file mode 100644
index 00000000000..46986198707
--- /dev/null
+++ b/chromium/net/cert/nss_profile_filter_chromeos.h
@@ -0,0 +1,71 @@
+// 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_CERT_NSS_PROFILE_FILTER_CHROMEOS_H_
+#define NET_CERT_NSS_PROFILE_FILTER_CHROMEOS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/crypto_module.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class X509Certificate;
+
+// On ChromeOS each user has separate NSS databases, which are loaded
+// simultaneously when multiple users are logged in at the same time. NSS
+// doesn't have built-in support to partition databases into separate groups, so
+// NSSProfileFilterChromeOS can be used to check if a given slot or certificate
+// should be used for a given user.
+//
+// Objects of this class are thread-safe except for the Init function, which if
+// called must not be called while other threads could access the object.
+class NET_EXPORT NSSProfileFilterChromeOS {
+ public:
+ // Create a filter. Until Init is called (or if Init is called with NULL
+ // slot handles), the filter will allow only certs/slots from the read-only
+ // slots and the root CA module.
+ NSSProfileFilterChromeOS();
+ NSSProfileFilterChromeOS(const NSSProfileFilterChromeOS& other);
+ ~NSSProfileFilterChromeOS();
+
+ NSSProfileFilterChromeOS& operator=(const NSSProfileFilterChromeOS& other);
+
+ // Initialize the filter with the slot handles to allow. This method is not
+ // thread-safe.
+ void Init(crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot);
+
+ bool IsModuleAllowed(PK11SlotInfo* slot) const;
+ bool IsCertAllowed(CERTCertificate* cert) const;
+
+ class CertNotAllowedForProfilePredicate {
+ public:
+ explicit CertNotAllowedForProfilePredicate(
+ const NSSProfileFilterChromeOS& filter);
+ bool operator()(const scoped_refptr<X509Certificate>& cert) const;
+
+ private:
+ const NSSProfileFilterChromeOS& filter_;
+ };
+
+ class ModuleNotAllowedForProfilePredicate {
+ public:
+ explicit ModuleNotAllowedForProfilePredicate(
+ const NSSProfileFilterChromeOS& filter);
+ bool operator()(const scoped_refptr<CryptoModule>& module) const;
+
+ private:
+ const NSSProfileFilterChromeOS& filter_;
+ };
+
+ private:
+ crypto::ScopedPK11Slot public_slot_;
+ crypto::ScopedPK11Slot private_slot_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_NSS_PROFILE_FILTER_CHROMEOS_H_
diff --git a/chromium/net/cert/nss_profile_filter_chromeos_unittest.cc b/chromium/net/cert/nss_profile_filter_chromeos_unittest.cc
new file mode 100644
index 00000000000..60ceeb1d24f
--- /dev/null
+++ b/chromium/net/cert/nss_profile_filter_chromeos_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 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/cert/nss_profile_filter_chromeos.h"
+
+#include <cert.h>
+#include <pk11pub.h>
+#include <secmod.h>
+
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/test_data_directory.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+crypto::ScopedPK11Slot GetRootCertsSlot() {
+ crypto::AutoSECMODListReadLock auto_lock;
+ SECMODModuleList* head = SECMOD_GetDefaultModuleList();
+ for (SECMODModuleList* item = head; item != NULL; item = item->next) {
+ int slot_count = item->module->loaded ? item->module->slotCount : 0;
+ for (int i = 0; i < slot_count; i++) {
+ PK11SlotInfo* slot = item->module->slots[i];
+ if (!PK11_IsPresent(slot))
+ continue;
+ if (PK11_HasRootCerts(slot))
+ return crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot));
+ }
+ }
+ return crypto::ScopedPK11Slot();
+}
+
+CertificateList ListCertsInSlot(PK11SlotInfo* slot) {
+ CertificateList result;
+ CERTCertList* cert_list = PK11_ListCertsInSlot(slot);
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ result.push_back(X509Certificate::CreateFromHandle(
+ node->cert, X509Certificate::OSCertHandles()));
+ }
+ CERT_DestroyCertList(cert_list);
+
+ // Sort the result so that test comparisons can be deterministic.
+ std::sort(result.begin(), result.end(), X509Certificate::LessThan());
+ return result;
+}
+
+}
+
+class NSSProfileFilterChromeOSTest : public testing::Test {
+ public:
+ NSSProfileFilterChromeOSTest() : user_1_("user1"), user_2_("user2") {}
+
+ virtual void SetUp() OVERRIDE {
+ // Initialize nss_util slots.
+ ASSERT_TRUE(user_1_.constructed_successfully());
+ ASSERT_TRUE(user_2_.constructed_successfully());
+ user_1_.FinishInit();
+ user_2_.FinishInit();
+
+ // TODO(mattm): more accurately test public/private slot filtering somehow.
+ // (The slots used to initialize a profile filter should be separate slots
+ // in separate modules, while ScopedTestNSSChromeOSUser uses the same slot
+ // for both.)
+ crypto::ScopedPK11Slot private_slot_1(crypto::GetPrivateSlotForChromeOSUser(
+ user_1_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>()));
+ ASSERT_TRUE(private_slot_1.get());
+ profile_filter_1_.Init(
+ crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
+ private_slot_1.Pass());
+
+ profile_filter_1_copy_ = profile_filter_1_;
+
+ crypto::ScopedPK11Slot private_slot_2(crypto::GetPrivateSlotForChromeOSUser(
+ user_2_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>()));
+ ASSERT_TRUE(private_slot_2.get());
+ profile_filter_2_.Init(
+ crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
+ private_slot_2.Pass());
+
+ certs_ = CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_.size());
+ }
+
+ protected:
+ CertificateList certs_;
+ crypto::ScopedTestNSSChromeOSUser user_1_;
+ crypto::ScopedTestNSSChromeOSUser user_2_;
+ NSSProfileFilterChromeOS no_slots_profile_filter_;
+ NSSProfileFilterChromeOS profile_filter_1_;
+ NSSProfileFilterChromeOS profile_filter_2_;
+ NSSProfileFilterChromeOS profile_filter_1_copy_;
+};
+
+TEST_F(NSSProfileFilterChromeOSTest, TempCertNotAllowed) {
+ EXPECT_EQ(NULL, certs_[0]->os_cert_handle()->slot);
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(
+ profile_filter_1_copy_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_2_.IsCertAllowed(certs_[0]->os_cert_handle()));
+}
+
+TEST_F(NSSProfileFilterChromeOSTest, InternalSlotAllowed) {
+ crypto::ScopedPK11Slot internal_slot(PK11_GetInternalSlot());
+ ASSERT_TRUE(internal_slot.get());
+ EXPECT_TRUE(no_slots_profile_filter_.IsModuleAllowed(internal_slot.get()));
+ EXPECT_TRUE(profile_filter_1_.IsModuleAllowed(internal_slot.get()));
+ EXPECT_TRUE(profile_filter_1_copy_.IsModuleAllowed(internal_slot.get()));
+ EXPECT_TRUE(profile_filter_2_.IsModuleAllowed(internal_slot.get()));
+
+ crypto::ScopedPK11Slot internal_key_slot(PK11_GetInternalKeySlot());
+ ASSERT_TRUE(internal_key_slot.get());
+ EXPECT_TRUE(
+ no_slots_profile_filter_.IsModuleAllowed(internal_key_slot.get()));
+ EXPECT_TRUE(profile_filter_1_.IsModuleAllowed(internal_key_slot.get()));
+ EXPECT_TRUE(profile_filter_1_copy_.IsModuleAllowed(internal_key_slot.get()));
+ EXPECT_TRUE(profile_filter_2_.IsModuleAllowed(internal_key_slot.get()));
+}
+
+TEST_F(NSSProfileFilterChromeOSTest, RootCertsAllowed) {
+ crypto::ScopedPK11Slot root_certs_slot(GetRootCertsSlot());
+ ASSERT_TRUE(root_certs_slot.get());
+ EXPECT_TRUE(no_slots_profile_filter_.IsModuleAllowed(root_certs_slot.get()));
+ EXPECT_TRUE(profile_filter_1_.IsModuleAllowed(root_certs_slot.get()));
+ EXPECT_TRUE(profile_filter_1_copy_.IsModuleAllowed(root_certs_slot.get()));
+ EXPECT_TRUE(profile_filter_2_.IsModuleAllowed(root_certs_slot.get()));
+
+ CertificateList root_certs(ListCertsInSlot(root_certs_slot.get()));
+ ASSERT_FALSE(root_certs.empty());
+ EXPECT_TRUE(
+ no_slots_profile_filter_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_1_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(
+ profile_filter_1_copy_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_2_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+}
+
+TEST_F(NSSProfileFilterChromeOSTest, SoftwareSlots) {
+ crypto::ScopedPK11Slot slot_1(
+ crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()));
+ ASSERT_TRUE(slot_1);
+ crypto::ScopedPK11Slot slot_2(
+ crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()));
+ ASSERT_TRUE(slot_2);
+
+ scoped_refptr<X509Certificate> cert_1 = certs_[0];
+ CertificateList certs_2 = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem", X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs_2.size());
+ scoped_refptr<X509Certificate> cert_2 = certs_2[0];
+
+ ASSERT_EQ(SECSuccess,
+ PK11_ImportCert(slot_1.get(),
+ cert_1->os_cert_handle(),
+ CK_INVALID_HANDLE,
+ "cert1",
+ PR_FALSE /* includeTrust (unused) */));
+
+ ASSERT_EQ(SECSuccess,
+ PK11_ImportCert(slot_2.get(),
+ cert_2->os_cert_handle(),
+ CK_INVALID_HANDLE,
+ "cert2",
+ PR_FALSE /* includeTrust (unused) */));
+
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(cert_2->os_cert_handle()));
+
+ EXPECT_TRUE(profile_filter_1_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_1_copy_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_.IsCertAllowed(cert_2->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_copy_.IsCertAllowed(cert_2->os_cert_handle()));
+
+ EXPECT_FALSE(profile_filter_2_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_2_.IsCertAllowed(cert_2->os_cert_handle()));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pem_tokenizer.cc b/chromium/net/cert/pem_tokenizer.cc
index d9c152875c6..6721462e413 100644
--- a/chromium/net/cert/pem_tokenizer.cc
+++ b/chromium/net/cert/pem_tokenizer.cc
@@ -63,8 +63,8 @@ bool PEMTokenizer::GetNext() {
StringPiece encoded = str_.substr(data_begin,
footer_pos - data_begin);
- if (!base::Base64Decode(CollapseWhitespaceASCII(encoded.as_string(),
- true), &data_)) {
+ if (!base::Base64Decode(base::CollapseWhitespaceASCII(encoded.as_string(),
+ true), &data_)) {
// The most likely cause for a decode failure is a datatype that
// includes PEM headers, which are not supported.
break;
diff --git a/chromium/net/cert/scoped_nss_types.h b/chromium/net/cert/scoped_nss_types.h
index 3e6d57d998d..0eb8d844f9d 100644
--- a/chromium/net/cert/scoped_nss_types.h
+++ b/chromium/net/cert/scoped_nss_types.h
@@ -1,7 +1,7 @@
// 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.
-//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
#ifndef NET_CERT_SCOPED_NSS_TYPES_H_
#define NET_CERT_SCOPED_NSS_TYPES_H_
@@ -12,14 +12,12 @@
namespace net {
struct FreeCERTCertificate {
- public:
void operator()(CERTCertificate* x) const {
CERT_DestroyCertificate(x);
}
};
-typedef scoped_ptr_malloc<CERTCertificate, FreeCERTCertificate>
- ScopedCERTCertificate;
+typedef scoped_ptr<CERTCertificate, FreeCERTCertificate> ScopedCERTCertificate;
} // namespace net
diff --git a/chromium/net/cert/sct_status_flags.h b/chromium/net/cert/sct_status_flags.h
index 1bcb42272b0..123c25c8905 100644
--- a/chromium/net/cert/sct_status_flags.h
+++ b/chromium/net/cert/sct_status_flags.h
@@ -10,9 +10,12 @@ namespace net {
namespace ct {
// The possible verification statuses for a SignedCertificateTimestamp.
+// Note: The numeric values are used within histograms and should not change
+// or be re-assigned.
enum SCTVerifyStatus {
// Not a real status, this just prevents a default int value from being
// mis-interpreseted as a valid status.
+ // Also used to count SCTs that cannot be decoded in the histogram.
SCT_STATUS_NONE = 0,
// The SCT is from an unknown log, so we cannot verify its signature.
@@ -23,6 +26,9 @@ enum SCTVerifyStatus {
// The SCT is from a known log, and the signature is valid.
SCT_STATUS_OK = 3,
+
+ // Used to bound the enum values.
+ SCT_STATUS_MAX,
};
} // namespace ct
diff --git a/chromium/net/cert/signed_certificate_timestamp.cc b/chromium/net/cert/signed_certificate_timestamp.cc
index 0a72cd1f9d2..371eea67b30 100644
--- a/chromium/net/cert/signed_certificate_timestamp.cc
+++ b/chromium/net/cert/signed_certificate_timestamp.cc
@@ -89,6 +89,12 @@ DigitallySigned::DigitallySigned() {}
DigitallySigned::~DigitallySigned() {}
+bool DigitallySigned::SignatureParametersMatch(
+ HashAlgorithm other_hash_algorithm,
+ SignatureAlgorithm other_signature_algorithm) const {
+ return (hash_algorithm == other_hash_algorithm) &&
+ (signature_algorithm == other_signature_algorithm);
+}
} // namespace ct
} // namespace net
diff --git a/chromium/net/cert/signed_certificate_timestamp.h b/chromium/net/cert/signed_certificate_timestamp.h
index f065a9472a1..9f80144d1d6 100644
--- a/chromium/net/cert/signed_certificate_timestamp.h
+++ b/chromium/net/cert/signed_certificate_timestamp.h
@@ -66,6 +66,12 @@ struct NET_EXPORT_PRIVATE DigitallySigned {
DigitallySigned();
~DigitallySigned();
+ // Returns true if |other_hash_algorithm| and |other_signature_algorithm|
+ // match this DigitallySigned hash and signature algorithms.
+ bool SignatureParametersMatch(
+ HashAlgorithm other_hash_algorithm,
+ SignatureAlgorithm other_signature_algorithm) const;
+
HashAlgorithm hash_algorithm;
SignatureAlgorithm signature_algorithm;
// 'signature' field.
@@ -88,10 +94,13 @@ struct NET_EXPORT SignedCertificateTimestamp
};
// Source of the SCT - supplementary, not defined in CT RFC.
+ // Note: The numeric values are used within histograms and should not change
+ // or be re-assigned.
enum Origin {
SCT_EMBEDDED = 0,
SCT_FROM_TLS_EXTENSION = 1,
SCT_FROM_OCSP_RESPONSE = 2,
+ SCT_ORIGIN_MAX,
};
SignedCertificateTimestamp();
diff --git a/chromium/net/cert/signed_tree_head.h b/chromium/net/cert/signed_tree_head.h
new file mode 100644
index 00000000000..fdeef4aae50
--- /dev/null
+++ b/chromium/net/cert/signed_tree_head.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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_CERT_SIGNED_TREE_HEAD_H_
+#define NET_CERT_SIGNED_TREE_HEAD_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "net/base/hash_value.h"
+#include "net/base/net_export.h"
+#include "net/cert/signed_certificate_timestamp.h"
+
+namespace net {
+
+namespace ct {
+
+static const uint8 kSthRootHashLength = 32;
+
+// Signed Tree Head as defined in section 3.5. of RFC6962
+struct NET_EXPORT SignedTreeHead {
+ // Version enum in RFC 6962, Section 3.2. Note that while in the current
+ // RFC the STH and SCT share the versioning scheme, there are plans in
+ // RFC6962-bis to use separate versions, so using a separate scheme here.
+ enum Version { V1 = 0, };
+
+ Version version;
+ base::Time timestamp;
+ uint64 tree_size;
+ char sha256_root_hash[kSthRootHashLength];
+ DigitallySigned signature;
+};
+
+} // namespace ct
+
+} // namespace net
+
+#endif
diff --git a/chromium/net/cert/test_root_certs.h b/chromium/net/cert/test_root_certs.h
index 22c635f9dbb..84c163a5bb2 100644
--- a/chromium/net/cert/test_root_certs.h
+++ b/chromium/net/cert/test_root_certs.h
@@ -12,6 +12,8 @@
#if defined(USE_NSS) || defined(OS_IOS)
#include <list>
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
+#include <vector>
#elif defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
@@ -21,6 +23,12 @@
#include "base/mac/scoped_cftyperef.h"
#endif
+#if defined(USE_NSS)
+typedef struct CERTCertificateStr CERTCertificate;
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
+typedef struct x509_st X509;
+#endif
+
namespace base {
class FilePath;
}
@@ -32,7 +40,7 @@ class X509Certificate;
// TestRootCerts is a helper class for unit tests that is used to
// artificially mark a certificate as trusted, independent of the local
// machine configuration.
-class NET_EXPORT_PRIVATE TestRootCerts {
+class NET_EXPORT TestRootCerts {
public:
// Obtains the Singleton instance to the trusted certificates.
static TestRootCerts* GetInstance();
@@ -56,7 +64,9 @@ class NET_EXPORT_PRIVATE TestRootCerts {
// Returns true if there are no certificates that have been marked trusted.
bool IsEmpty() const;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(USE_NSS)
+ bool Contains(CERTCertificate* cert) const;
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
CFArrayRef temporary_roots() const { return temporary_roots_; }
// Modifies the root certificates of |trust_ref| to include the
@@ -68,7 +78,10 @@ class NET_EXPORT_PRIVATE TestRootCerts {
// be trusted. By default, this is true, indicating that the TestRootCerts
// are used in addition to OS trust store.
void SetAllowSystemTrust(bool allow_system_trust);
-
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
+ const std::vector<scoped_refptr<X509Certificate> >&
+ temporary_roots() const { return temporary_roots_; }
+ bool Contains(X509* cert) const;
#elif defined(OS_WIN)
HCERTSTORE temporary_roots() const { return temporary_roots_; }
@@ -93,6 +106,8 @@ class NET_EXPORT_PRIVATE TestRootCerts {
// settings, in order to restore them when Clear() is called.
class TrustEntry;
std::list<TrustEntry*> trust_cache_;
+#elif defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
+ std::vector<scoped_refptr<X509Certificate> > temporary_roots_;
#elif defined(OS_WIN)
HCERTSTORE temporary_roots_;
#elif defined(OS_MACOSX)
@@ -100,7 +115,7 @@ class NET_EXPORT_PRIVATE TestRootCerts {
bool allow_system_trust_;
#endif
-#if defined(OS_WIN) || defined(USE_OPENSSL)
+#if defined(OS_WIN) || defined(OS_ANDROID)
// True if there are no temporarily trusted root certificates.
bool empty_;
#endif
diff --git a/chromium/net/cert/test_root_certs_nss.cc b/chromium/net/cert/test_root_certs_nss.cc
index 3a2f88a7968..3c271452faf 100644
--- a/chromium/net/cert/test_root_certs_nss.cc
+++ b/chromium/net/cert/test_root_certs_nss.cc
@@ -114,6 +114,17 @@ bool TestRootCerts::IsEmpty() const {
return trust_cache_.empty();
}
+#if defined(USE_NSS)
+bool TestRootCerts::Contains(CERTCertificate* cert) const {
+ for (std::list<TrustEntry*>::const_iterator it = trust_cache_.begin();
+ it != trust_cache_.end(); ++it) {
+ if (X509Certificate::IsSameOSCert(cert, (*it)->certificate()))
+ return true;
+ }
+ return false;
+}
+#endif
+
TestRootCerts::~TestRootCerts() {
Clear();
}
diff --git a/chromium/net/cert/test_root_certs_openssl.cc b/chromium/net/cert/test_root_certs_openssl.cc
index 3d5cf3d72d1..1b194bc646f 100644
--- a/chromium/net/cert/test_root_certs_openssl.cc
+++ b/chromium/net/cert/test_root_certs_openssl.cc
@@ -26,26 +26,34 @@ bool TestRootCerts::Add(X509Certificate* certificate) {
ERR_clear_error();
}
- empty_ = false;
+ temporary_roots_.push_back(certificate);
return true;
}
void TestRootCerts::Clear() {
- if (empty_)
+ if (temporary_roots_.empty())
return;
+ temporary_roots_.clear();
X509Certificate::ResetCertStore();
- empty_ = true;
}
bool TestRootCerts::IsEmpty() const {
- return empty_;
+ return temporary_roots_.empty();
+}
+
+bool TestRootCerts::Contains(X509* cert) const {
+ for (std::vector<scoped_refptr<X509Certificate> >::const_iterator it =
+ temporary_roots_.begin();
+ it != temporary_roots_.end(); ++it) {
+ if (X509Certificate::IsSameOSCert(cert, (*it)->os_cert_handle()))
+ return true;
+ }
+ return false;
}
TestRootCerts::~TestRootCerts() {}
-void TestRootCerts::Init() {
- empty_ = true;
-}
+void TestRootCerts::Init() {}
} // namespace net
diff --git a/chromium/net/cert/test_root_certs_unittest.cc b/chromium/net/cert/test_root_certs_unittest.cc
index 74c3551862b..a2cf695f95f 100644
--- a/chromium/net/cert/test_root_certs_unittest.cc
+++ b/chromium/net/cert/test_root_certs_unittest.cc
@@ -135,6 +135,39 @@ TEST(TestRootCertsTest, OverrideTrust) {
EXPECT_EQ(bad_verify_result.cert_status, restored_verify_result.cert_status);
}
+#if defined(USE_NSS) || (defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID))
+TEST(TestRootCertsTest, Contains) {
+ // Another test root certificate.
+ const char kRootCertificateFile2[] = "2048-rsa-root.pem";
+
+ TestRootCerts* test_roots = TestRootCerts::GetInstance();
+ ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+
+ scoped_refptr<X509Certificate> root_cert_1 =
+ ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_1.get());
+
+ scoped_refptr<X509Certificate> root_cert_2 =
+ ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile2);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_2.get());
+
+ EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ EXPECT_TRUE(test_roots->Add(root_cert_1.get()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ EXPECT_TRUE(test_roots->Add(root_cert_2.get()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ test_roots->Clear();
+ EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+}
+#endif
+
// TODO(rsleevi): Add tests for revocation checking via CRLs, ensuring that
// TestRootCerts properly injects itself into the validation process. See
// http://crbug.com/63958
diff --git a/chromium/net/cert/x509_cert_types_mac.cc b/chromium/net/cert/x509_cert_types_mac.cc
index 6439c7f3e77..d5b2ea35ff2 100644
--- a/chromium/net/cert/x509_cert_types_mac.cc
+++ b/chromium/net/cert/x509_cert_types_mac.cc
@@ -1,17 +1,19 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 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/cert/x509_cert_types.h"
+#include <stdint.h>
+
#include <CoreServices/CoreServices.h>
#include <Security/SecAsn1Coder.h>
#include <Security/Security.h>
-#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "net/base/net_string_util.h"
namespace net {
@@ -113,10 +115,9 @@ std::string DataToString(CSSM_DATA data) {
// Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
std::string Latin1DataToUTF8String(CSSM_DATA data) {
base::string16 utf16;
- if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1,
- base::OnStringConversionError::FAIL, &utf16))
+ if (!ConvertToUTF16(DataToString(data), kCharsetLatin1, &utf16))
return "";
- return UTF16ToUTF8(utf16);
+ return base::UTF16ToUTF8(utf16);
}
// Converts big-endian UTF-16 to UTF-8 in a std::string.
@@ -125,18 +126,18 @@ bool UTF16BigEndianToUTF8(base::char16* chars, size_t length,
std::string* out_string) {
for (size_t i = 0; i < length; i++)
chars[i] = EndianU16_BtoN(chars[i]);
- return UTF16ToUTF8(chars, length, out_string);
+ return base::UTF16ToUTF8(chars, length, out_string);
}
// Converts big-endian UTF-32 to UTF-8 in a std::string.
// Note: The byte-order flipping is done in place on the input buffer!
-bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+bool UTF32BigEndianToUTF8(int32_t* chars, size_t length,
std::string* out_string) {
for (size_t i = 0; i < length; ++i)
chars[i] = EndianS32_BtoN(chars[i]);
#if defined(WCHAR_T_IS_UTF32)
- return WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
- length, out_string);
+ return base::WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
+ length, out_string);
#else
#error This code doesn't handle 16-bit wchar_t.
#endif
@@ -250,8 +251,8 @@ bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
}
case BER_TAG_PKIX_UNIVERSAL_STRING: { // UTF-32, big-endian
std::string value;
- UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data),
- pair->value.Length / sizeof(char32),
+ UTF32BigEndianToUTF8(reinterpret_cast<int32_t*>(pair->value.Data),
+ pair->value.Length / sizeof(int32_t),
&value);
AddTypeValuePair(pair->type, value, values);
break;
diff --git a/chromium/net/cert/x509_cert_types_win.cc b/chromium/net/cert/x509_cert_types_win.cc
index d99362cb921..570f5fc8013 100644
--- a/chromium/net/cert/x509_cert_types_win.cc
+++ b/chromium/net/cert/x509_cert_types_win.cc
@@ -54,7 +54,7 @@ bool GetAttributeValue(PCERT_RDN_ATTR attribute,
if (chars_written <= 1)
return false;
wide_name.resize(chars_written - 1);
- *value = WideToUTF8(wide_name);
+ *value = base::WideToUTF8(wide_name);
return true;
}
@@ -105,7 +105,7 @@ bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
&name_info, &name_info_size);
if (!rv)
return false;
- scoped_ptr_malloc<CERT_NAME_INFO> scoped_name_info(name_info);
+ scoped_ptr<CERT_NAME_INFO, base::FreeDeleter> scoped_name_info(name_info);
std::vector<std::string> common_names, locality_names, state_names,
country_names;
diff --git a/chromium/net/cert/x509_certificate.cc b/chromium/net/cert/x509_certificate.cc
index a095fdf83e9..052b7b7ecc1 100644
--- a/chromium/net/cert/x509_certificate.cc
+++ b/chromium/net/cert/x509_certificate.cc
@@ -516,7 +516,7 @@ bool X509Certificate::VerifyHostname(
// CanonicalizeHost requires surrounding brackets to parse an IPv6 address.
const std::string host_or_ip = hostname.find(':') != std::string::npos ?
"[" + hostname + "]" : hostname;
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
std::string reference_name = CanonicalizeHost(host_or_ip, &host_info);
// CanonicalizeHost does not normalize absolute vs relative DNS names. If
// the input name was absolute (included trailing .), normalize it as if it
@@ -533,8 +533,7 @@ bool X509Certificate::VerifyHostname(
// Fully handle all cases where |hostname| contains an IP address.
if (host_info.IsIPAddress()) {
- if (common_name_fallback &&
- host_info.family == url_canon::CanonHostInfo::IPV4) {
+ if (common_name_fallback && host_info.family == url::CanonHostInfo::IPV4) {
// Fallback to Common name matching. As this is deprecated and only
// supported for compatibility refuse it for IPv6 addresses.
return reference_name == cert_common_name;
diff --git a/chromium/net/cert/x509_certificate.h b/chromium/net/cert/x509_certificate.h
index 43ed01414e5..7aa48f068fa 100644
--- a/chromium/net/cert/x509_certificate.h
+++ b/chromium/net/cert/x509_certificate.h
@@ -25,7 +25,7 @@
#include <CoreFoundation/CFArray.h>
#include <Security/SecBase.h>
-#elif defined(USE_OPENSSL)
+#elif defined(USE_OPENSSL_CERTS)
// Forward declaration; real one in <x509.h>
typedef struct x509_st X509;
typedef struct x509_store_st X509_STORE;
@@ -58,7 +58,7 @@ class NET_EXPORT X509Certificate
typedef PCCERT_CONTEXT OSCertHandle;
#elif defined(OS_MACOSX)
typedef SecCertificateRef OSCertHandle;
-#elif defined(USE_OPENSSL)
+#elif defined(USE_OPENSSL_CERTS)
typedef X509* OSCertHandle;
#elif defined(USE_NSS)
typedef struct CERTCertificateStr* OSCertHandle;
@@ -304,7 +304,7 @@ class NET_EXPORT X509Certificate
PCCERT_CONTEXT CreateOSCertChainForCert() const;
#endif
-#if defined(USE_OPENSSL)
+#if defined(USE_OPENSSL_CERTS)
// Returns a handle to a global, in-memory certificate store. We
// use it for test code, e.g. importing the test server's certificate.
static X509_STORE* cert_store();
@@ -413,7 +413,7 @@ class NET_EXPORT X509Certificate
// Common object initialization code. Called by the constructors only.
void Initialize();
-#if defined(USE_OPENSSL)
+#if defined(USE_OPENSSL_CERTS)
// Resets the store returned by cert_store() to default state. Used by
// TestRootCerts to undo modifications.
static void ResetCertStore();
diff --git a/chromium/net/cert/x509_certificate_mac.cc b/chromium/net/cert/x509_certificate_mac.cc
index 1d874c4b023..ab479384a17 100644
--- a/chromium/net/cert/x509_certificate_mac.cc
+++ b/chromium/net/cert/x509_certificate_mac.cc
@@ -8,8 +8,6 @@
#include <CoreServices/CoreServices.h>
#include <Security/Security.h>
-#include <cert.h>
-
#include <vector>
#include "base/lazy_instance.h"
@@ -24,7 +22,6 @@
#include "base/synchronization/lock.h"
#include "crypto/cssm_init.h"
#include "crypto/mac_security_services_lock.h"
-#include "crypto/nss_util.h"
#include "net/cert/x509_util_mac.h"
using base::ScopedCFTypeRef;
@@ -199,45 +196,6 @@ void AddCertificatesFromBytes(const char* data, size_t length,
}
}
-struct CSSMOIDString {
- const CSSM_OID* oid_;
- std::string string_;
-};
-
-typedef std::vector<CSSMOIDString> CSSMOIDStringVector;
-
-class ScopedCertName {
- public:
- explicit ScopedCertName(CERTName* name) : name_(name) { }
- ~ScopedCertName() {
- if (name_) CERT_DestroyName(name_);
- }
- operator CERTName*() { return name_; }
-
- private:
- CERTName* name_;
-};
-
-class ScopedEncodedCertResults {
- public:
- explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results)
- : results_(results) { }
- ~ScopedEncodedCertResults() {
- if (results_) {
- CSSM_ENCODED_CERT* encCert =
- reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results);
- for (uint32 i = 0; i < results_->NumberOfResults; i++) {
- crypto::CSSMFree(encCert[i].CertBlob.Data);
- }
- crypto::CSSMFree(results_->Results);
- crypto::CSSMFree(results_);
- }
- }
-
- private:
- CSSM_TP_RESULT_SET* results_;
-};
-
} // namespace
void X509Certificate::Initialize() {
diff --git a/chromium/net/cert/x509_certificate_win.cc b/chromium/net/cert/x509_certificate_win.cc
index f0c9e502e46..c679107371a 100644
--- a/chromium/net/cert/x509_certificate_win.cc
+++ b/chromium/net/cert/x509_certificate_win.cc
@@ -45,8 +45,9 @@ void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
// Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
// structure and stores it in *output.
-void GetCertSubjectAltName(PCCERT_CONTEXT cert,
- scoped_ptr_malloc<CERT_ALT_NAME_INFO>* output) {
+void GetCertSubjectAltName(
+ PCCERT_CONTEXT cert,
+ scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter>* output) {
PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
cert->pCertInfo->cExtension,
cert->pCertInfo->rgExtension);
@@ -175,18 +176,18 @@ void X509Certificate::GetSubjectAltName(
if (!cert_handle_)
return;
- scoped_ptr_malloc<CERT_ALT_NAME_INFO> alt_name_info;
+ scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter> alt_name_info;
GetCertSubjectAltName(cert_handle_, &alt_name_info);
CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
if (alt_name) {
int num_entries = alt_name->cAltEntry;
for (int i = 0; i < num_entries; i++) {
// dNSName is an ASN.1 IA5String representing a string of ASCII
- // characters, so we can use WideToASCII here.
+ // characters, so we can use UTF16ToASCII here.
const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
- dns_names->push_back(WideToASCII(entry.pwszDNSName));
+ dns_names->push_back(base::UTF16ToASCII(entry.pwszDNSName));
} else if (ip_addrs &&
entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
ip_addrs->push_back(std::string(
diff --git a/chromium/net/cert/x509_util_android.cc b/chromium/net/cert/x509_util_android.cc
index 1f6c3c696d2..128ea850ac0 100644
--- a/chromium/net/cert/x509_util_android.cc
+++ b/chromium/net/cert/x509_util_android.cc
@@ -4,7 +4,9 @@
#include "net/cert/x509_util_android.h"
+#include "base/android/build_info.h"
#include "base/android/jni_android.h"
+#include "base/metrics/histogram.h"
#include "jni/X509Util_jni.h"
#include "net/cert/cert_database.h"
@@ -14,6 +16,17 @@ void NotifyKeyChainChanged(JNIEnv* env, jclass clazz) {
CertDatabase::GetInstance()->OnAndroidKeyChainChanged();
}
+void RecordCertVerifyCapabilitiesHistogram(JNIEnv* env,
+ jclass clazz,
+ jboolean found_system_trust_roots) {
+ // Only record the histogram for 4.2 and up. Before 4.2, the platform doesn't
+ // return the certificate chain anyway.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >= 17) {
+ UMA_HISTOGRAM_BOOLEAN("Net.FoundSystemTrustRootsAndroid",
+ found_system_trust_roots);
+ }
+}
+
jobject GetApplicationContext(JNIEnv* env, jclass clazz) {
return base::android::GetApplicationContext();
}
diff --git a/chromium/net/cert/x509_util_nss.cc b/chromium/net/cert/x509_util_nss.cc
index 67ad467f5c5..beed9a1a984 100644
--- a/chromium/net/cert/x509_util_nss.cc
+++ b/chromium/net/cert/x509_util_nss.cc
@@ -214,7 +214,7 @@ SECStatus PR_CALLBACK CollectCertsCallback(void* arg,
return SECSuccess;
}
-typedef scoped_ptr_malloc<
+typedef scoped_ptr<
CERTName,
crypto::NSSDestroyer<CERTName, CERT_DestroyName> > ScopedCERTName;
diff --git a/chromium/net/cookies/canonical_cookie.cc b/chromium/net/cookies/canonical_cookie.cc
index 15b77c7add5..54a9565a9c6 100644
--- a/chromium/net/cookies/canonical_cookie.cc
+++ b/chromium/net/cookies/canonical_cookie.cc
@@ -162,10 +162,10 @@ std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) {
if (url.SchemeIsFile())
return url.spec();
- url_canon::Replacements<char> replacements;
+ url::Replacements<char> replacements;
replacements.ClearPort();
if (url.SchemeIsSecure())
- replacements.SetScheme("http", url_parse::Component(0, 4));
+ replacements.SetScheme("http", url::Component(0, 4));
return url.GetOrigin().ReplaceComponents(replacements).spec();
}
@@ -283,11 +283,11 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url,
if (!parsed_path.empty() && cookie_path != parsed_path)
return NULL;
// Canonicalize path again to make sure it escapes characters as needed.
- url_parse::Component path_component(0, cookie_path.length());
- url_canon::RawCanonOutputT<char> canon_path;
- url_parse::Component canon_path_component;
- url_canon::CanonicalizePath(cookie_path.data(), path_component,
- &canon_path, &canon_path_component);
+ url::Component path_component(0, cookie_path.length());
+ url::RawCanonOutputT<char> canon_path;
+ url::Component canon_path_component;
+ url::CanonicalizePath(cookie_path.data(), path_component, &canon_path,
+ &canon_path_component);
cookie_path = std::string(canon_path.data() + canon_path_component.begin,
canon_path_component.len);
@@ -394,4 +394,20 @@ std::string CanonicalCookie::DebugString() const {
static_cast<int64>(creation_date_.ToTimeT()));
}
+CanonicalCookie* CanonicalCookie::Duplicate() {
+ CanonicalCookie* cc = new CanonicalCookie();
+ cc->source_ = source_;
+ cc->name_ = name_;
+ cc->value_ = value_;
+ cc->domain_ = domain_;
+ cc->path_ = path_;
+ cc->creation_date_ = creation_date_;
+ cc->expiry_date_ = expiry_date_;
+ cc->last_access_date_ = last_access_date_;
+ cc->secure_ = secure_;
+ cc->httponly_ = httponly_;
+ cc->priority_ = priority_;
+ return cc;
+}
+
} // namespace net
diff --git a/chromium/net/cookies/canonical_cookie.h b/chromium/net/cookies/canonical_cookie.h
index a78eece4198..a5567405242 100644
--- a/chromium/net/cookies/canonical_cookie.h
+++ b/chromium/net/cookies/canonical_cookie.h
@@ -125,6 +125,9 @@ class NET_EXPORT CanonicalCookie {
std::string DebugString() const;
+ // Returns a duplicate of this cookie.
+ CanonicalCookie* Duplicate();
+
// Returns the cookie source when cookies are set for |url|. This function
// is public for unit test purposes only.
static std::string GetCookieSourceFromURL(const GURL& url);
@@ -134,6 +137,9 @@ class NET_EXPORT CanonicalCookie {
const base::Time& server_time);
private:
+ // NOTE: When any new members are added below, the implementation of
+ // Duplicate() must be updated to copy the new member accordingly.
+
// The source member of a canonical cookie is the origin of the URL that tried
// to set this cookie, minus the port number if any. This field is not
// persistent though; its only used in the in-tab cookies dialog to show the
@@ -153,6 +159,9 @@ class NET_EXPORT CanonicalCookie {
bool secure_;
bool httponly_;
CookiePriority priority_;
+ // NOTE: When any new members are added above this comment, the
+ // implementation of Duplicate() must be updated to copy the new member
+ // accordingly.
};
typedef std::vector<CanonicalCookie> CookieList;
diff --git a/chromium/net/cookies/cookie_monster.cc b/chromium/net/cookies/cookie_monster.cc
index 15c7143a608..7aed8f8d0f7 100644
--- a/chromium/net/cookies/cookie_monster.cc
+++ b/chromium/net/cookies/cookie_monster.cc
@@ -53,6 +53,7 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
@@ -254,40 +255,40 @@ CookieMonster::CookieItVector::iterator LowerBoundAccessDate(
LowerBoundAccessDateComparator);
}
-// Mapping between DeletionCause and Delegate::ChangeCause; the mapping also
-// provides a boolean that specifies whether or not an OnCookieChanged
-// notification ought to be generated.
+// Mapping between DeletionCause and CookieMonsterDelegate::ChangeCause; the
+// mapping also provides a boolean that specifies whether or not an
+// OnCookieChanged notification ought to be generated.
typedef struct ChangeCausePair_struct {
- CookieMonster::Delegate::ChangeCause cause;
+ CookieMonsterDelegate::ChangeCause cause;
bool notify;
} ChangeCausePair;
ChangeCausePair ChangeCauseMapping[] = {
// DELETE_COOKIE_EXPLICIT
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, true },
// DELETE_COOKIE_OVERWRITE
- { CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_OVERWRITE, true },
// DELETE_COOKIE_EXPIRED
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED, true },
// DELETE_COOKIE_EVICTED
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
// DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false },
// DELETE_COOKIE_DONT_RECORD
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false },
// DELETE_COOKIE_EVICTED_DOMAIN
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
// DELETE_COOKIE_EVICTED_GLOBAL
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
// DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
// DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
// DELETE_COOKIE_EXPIRED_OVERWRITE
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true },
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true },
// DELETE_COOKIE_CONTROL_CHAR
- { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true},
+ { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
// DELETE_COOKIE_LAST_ENTRY
- { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }
+ { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false }
};
std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
@@ -308,12 +309,10 @@ std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
} // namespace
-// static
-bool CookieMonster::default_enable_file_scheme_ = false;
-
-CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
+CookieMonster::CookieMonster(PersistentCookieStore* store,
+ CookieMonsterDelegate* delegate)
: initialized_(false),
- loaded_(false),
+ loaded_(store == NULL),
store_(store),
last_access_threshold_(
TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
@@ -326,10 +325,10 @@ CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
}
CookieMonster::CookieMonster(PersistentCookieStore* store,
- Delegate* delegate,
+ CookieMonsterDelegate* delegate,
int last_access_threshold_milliseconds)
: initialized_(false),
- loaded_(false),
+ loaded_(store == NULL),
store_(store),
last_access_threshold_(base::TimeDelta::FromMilliseconds(
last_access_threshold_milliseconds)),
@@ -1289,11 +1288,6 @@ void CookieMonster::SetKeepExpiredCookies() {
keep_expired_cookies_ = true;
}
-// static
-void CookieMonster::EnableFileScheme() {
- default_enable_file_scheme_ = true;
-}
-
void CookieMonster::FlushStore(const base::Closure& callback) {
base::AutoLock autolock(lock_);
if (initialized_ && store_.get())
@@ -1440,6 +1434,11 @@ void CookieMonster::InitStore() {
store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now()));
}
+void CookieMonster::ReportLoaded() {
+ if (delegate_.get())
+ delegate_->OnLoaded();
+}
+
void CookieMonster::OnLoaded(TimeTicks beginning_time,
const std::vector<CanonicalCookie*>& cookies) {
StoreLoadedCookies(cookies);
@@ -1447,6 +1446,8 @@ void CookieMonster::OnLoaded(TimeTicks beginning_time,
// Invoke the task queue of cookie request.
InvokeQueue();
+
+ ReportLoaded();
}
void CookieMonster::OnKeyLoaded(const std::string& key,
@@ -1673,9 +1674,9 @@ const int CookieMonster::kDefaultCookieableSchemesCount =
arraysize(kDefaultCookieableSchemes);
void CookieMonster::SetDefaultCookieableSchemes() {
- int num_schemes = default_enable_file_scheme_ ?
- kDefaultCookieableSchemesCount : kDefaultCookieableSchemesCount - 1;
- SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
+ // Always disable file scheme unless SetEnableFileScheme(true) is called.
+ SetCookieableSchemes(kDefaultCookieableSchemes,
+ kDefaultCookieableSchemesCount - 1);
}
void CookieMonster::FindCookiesForHostAndDomain(
@@ -1777,7 +1778,7 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
cookies_.insert(CookieMap::value_type(key, cc));
if (delegate_.get()) {
delegate_->OnCookieChanged(
- *cc, false, Delegate::CHANGE_COOKIE_EXPLICIT);
+ *cc, false, CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT);
}
return inserted;
@@ -2083,7 +2084,7 @@ int CookieMonster::GarbageCollectDeleteRange(
std::string CookieMonster::GetKey(const std::string& domain) const {
std::string effective_domain(
registry_controlled_domains::GetDomainAndRegistry(
- domain, registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES));
+ domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
if (effective_domain.empty())
effective_domain = domain;
@@ -2252,4 +2253,55 @@ Time CookieMonster::CurrentTime() {
Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1));
}
+bool CookieMonster::CopyCookiesForKeyToOtherCookieMonster(
+ std::string key,
+ CookieMonster* other) {
+ ScopedVector<CanonicalCookie> duplicated_cookies;
+
+ {
+ base::AutoLock autolock(lock_);
+ DCHECK(other);
+ if (!loaded_)
+ return false;
+
+ for (CookieMapItPair its = cookies_.equal_range(key);
+ its.first != its.second;
+ ++its.first) {
+ CookieMap::iterator curit = its.first;
+ CanonicalCookie* cc = curit->second;
+
+ duplicated_cookies.push_back(cc->Duplicate());
+ }
+ }
+
+ {
+ base::AutoLock autolock(other->lock_);
+ if (!other->loaded_)
+ return false;
+
+ // There must not exist any entries for the key to be copied in |other|.
+ CookieMapItPair its = other->cookies_.equal_range(key);
+ if (its.first != its.second)
+ return false;
+
+ // Store the copied cookies in |other|.
+ for (ScopedVector<CanonicalCookie>::const_iterator it =
+ duplicated_cookies.begin();
+ it != duplicated_cookies.end();
+ ++it) {
+ other->InternalInsertCookie(key, *it, true);
+ }
+
+ // Since the cookies are owned by |other| now, weak clear must be used.
+ duplicated_cookies.weak_clear();
+ }
+
+ return true;
+}
+
+bool CookieMonster::loaded() {
+ base::AutoLock autolock(lock_);
+ return loaded_;
+}
+
} // namespace net
diff --git a/chromium/net/cookies/cookie_monster.h b/chromium/net/cookies/cookie_monster.h
index de00319fb4b..b719c32b595 100644
--- a/chromium/net/cookies/cookie_monster.h
+++ b/chromium/net/cookies/cookie_monster.h
@@ -37,6 +37,7 @@ class TimeTicks;
namespace net {
+class CookieMonsterDelegate;
class ParsedCookie;
// The cookie monster is the system for storing and retrieving cookies. It has
@@ -65,8 +66,8 @@ class ParsedCookie;
// - Verify that our domain enforcement and non-dotted handling is correct
class NET_EXPORT CookieMonster : public CookieStore {
public:
- class Delegate;
class PersistentCookieStore;
+ typedef CookieMonsterDelegate Delegate;
// Terminology:
// * The 'top level domain' (TLD) of an internet domain name is
@@ -138,11 +139,11 @@ class NET_EXPORT CookieMonster : public CookieStore {
// monster's existence. If |store| is NULL, then no backing store will be
// updated. If |delegate| is non-NULL, it will be notified on
// creation/deletion of cookies.
- CookieMonster(PersistentCookieStore* store, Delegate* delegate);
+ CookieMonster(PersistentCookieStore* store, CookieMonsterDelegate* delegate);
// Only used during unit testing.
CookieMonster(PersistentCookieStore* store,
- Delegate* delegate,
+ CookieMonsterDelegate* delegate,
int last_access_threshold_milliseconds);
// Helper function that adds all cookies from |list| into this instance.
@@ -186,11 +187,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
const CookieOptions& options,
const GetCookieListCallback& callback);
- // Invokes GetAllCookiesForURLWithOptions with options set to include HTTP
- // only cookies.
- void GetAllCookiesForURLAsync(const GURL& url,
- const GetCookieListCallback& callback);
-
// Deletes all of the cookies.
void DeleteAllAsync(const DeleteCallback& callback);
@@ -201,14 +197,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
void DeleteAllForHostAsync(const GURL& url,
const DeleteCallback& callback);
- // Same as DeleteAllForHostAsync, except it deletes cookies between
- // [|delete_begin|, |delete_end|).
- // Returns the number of cookies deleted.
- void DeleteAllCreatedBetweenForHostAsync(const base::Time delete_begin,
- const base::Time delete_end,
- const GURL& url,
- const DeleteCallback& callback);
-
// Deletes one specific cookie.
void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
const DeleteCookieCallback& callback);
@@ -224,6 +212,10 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Resets the list of cookieable schemes to kDefaultCookieableSchemes with or
// without 'file' being included.
+ //
+ // There are some unknowns about how to correctly handle file:// cookies,
+ // and our implementation for this is not robust enough. This allows you
+ // to enable support, but it should only be used for testing. Bug 1157243.
void SetEnableFileScheme(bool accept);
// Instructs the cookie monster to not delete expired cookies. This is used
@@ -234,12 +226,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Protects session cookies from deletion on shutdown.
void SetForceKeepSessionState();
- // There are some unknowns about how to correctly handle file:// cookies,
- // and our implementation for this is not robust enough. This allows you
- // to enable support, but it should only be used for testing. Bug 1157243.
- // Must be called before creating a CookieMonster instance.
- static void EnableFileScheme();
-
// Flush the backing store (if any) to disk and post the given callback when
// done.
// WARNING: THE CALLBACK WILL RUN ON A RANDOM THREAD. IT MUST BE THREAD SAFE.
@@ -266,19 +252,37 @@ class NET_EXPORT CookieMonster : public CookieStore {
const CookieOptions& options,
const GetCookiesCallback& callback) OVERRIDE;
+ // Invokes GetAllCookiesForURLWithOptions with options set to include HTTP
+ // only cookies.
+ virtual void GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) OVERRIDE;
+
// Deletes all cookies with that might apply to |url| that has |cookie_name|.
virtual void DeleteCookieAsync(
const GURL& url, const std::string& cookie_name,
const base::Closure& callback) OVERRIDE;
// Deletes all of the cookies that have a creation_date greater than or equal
- // to |delete_begin| and less than |delete_end|
+ // to |delete_begin| and less than |delete_end|.
// Returns the number of cookies that have been deleted.
virtual void DeleteAllCreatedBetweenAsync(
const base::Time& delete_begin,
const base::Time& delete_end,
const DeleteCallback& callback) OVERRIDE;
+ // Deletes all of the cookies that match the host of the given URL
+ // regardless of path and that have a creation_date greater than or
+ // equal to |delete_begin| and less then |delete_end|. This includes
+ // all http_only and secure cookies, but does not include any domain
+ // cookies that may apply to this host.
+ // Returns the number of cookies deleted.
+ virtual void DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) OVERRIDE;
+
virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE;
virtual CookieMonster* GetCookieMonster() OVERRIDE;
@@ -303,6 +307,19 @@ class NET_EXPORT CookieMonster : public CookieStore {
static const char* kDefaultCookieableSchemes[];
static const int kDefaultCookieableSchemesCount;
+ // Copies all keys for the given |key| to another cookie monster |other|.
+ // Both |other| and |this| must be loaded for this operation to succeed.
+ // Furthermore, there may not be any cookies stored in |other| for |key|.
+ // Returns false if any of these conditions is not met.
+ bool CopyCookiesForKeyToOtherCookieMonster(std::string key,
+ CookieMonster* other);
+
+ // Find the key (for lookup in cookies_) based on the given domain.
+ // See comment on keys before the CookieMap typedef.
+ std::string GetKey(const std::string& domain) const;
+
+ bool loaded();
+
private:
// For queueing the cookie monster calls.
class CookieMonsterTask;
@@ -348,8 +365,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
// and to provide a public cause for onCookieChange notifications.
//
// If you add or remove causes from this list, please be sure to also update
- // the Delegate::ChangeCause mapping inside ChangeCauseMapping. Moreover,
- // these are used as array indexes, so avoid reordering to keep the
+ // the CookieMonsterDelegate::ChangeCause mapping inside ChangeCauseMapping.
+ // Moreover, these are used as array indexes, so avoid reordering to keep the
// histogram buckets consistent. New items (if necessary) should be added
// at the end of the list, just before DELETE_COOKIE_LAST_ENTRY.
enum DeletionCause {
@@ -451,6 +468,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
InitStore();
} else {
loaded_ = true;
+ ReportLoaded();
}
initialized_ = true;
}
@@ -460,6 +478,9 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Should only be called by InitIfNecessary().
void InitStore();
+ // Reports to the delegate that the cookie monster was loaded.
+ void ReportLoaded();
+
// Stores cookies loaded from the backing store and invokes any deferred
// calls. |beginning_time| should be the moment PersistentCookieStore::Load
// was invoked and is used for reporting histogram_time_blocked_on_load_.
@@ -542,9 +563,9 @@ class NET_EXPORT CookieMonster : public CookieStore {
const base::Time& current_time);
// |deletion_cause| argument is used for collecting statistics and choosing
- // the correct Delegate::ChangeCause for OnCookieChanged notifications.
- // Guarantee: All iterators to cookies_ except to the deleted entry remain
- // vaild.
+ // the correct CookieMonsterDelegate::ChangeCause for OnCookieChanged
+ // notifications. Guarantee: All iterators to cookies_ except to the
+ // deleted entry remain vaild.
void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store,
DeletionCause deletion_cause);
@@ -572,10 +593,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
CookieItVector::iterator cookie_its_begin,
CookieItVector::iterator cookie_its_end);
- // Find the key (for lookup in cookies_) based on the given domain.
- // See comment on keys before the CookieMap typedef.
- std::string GetKey(const std::string& domain) const;
-
bool HasCookieableScheme(const GURL& url);
// Statistics support
@@ -666,7 +683,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
std::vector<std::string> cookieable_schemes_;
- scoped_refptr<Delegate> delegate_;
+ scoped_refptr<CookieMonsterDelegate> delegate_;
// Lock for thread-safety
base::Lock lock_;
@@ -684,8 +701,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
DISALLOW_COPY_AND_ASSIGN(CookieMonster);
};
-class NET_EXPORT CookieMonster::Delegate
- : public base::RefCountedThreadSafe<CookieMonster::Delegate> {
+class NET_EXPORT CookieMonsterDelegate
+ : public base::RefCountedThreadSafe<CookieMonsterDelegate> {
public:
// The publicly relevant reasons a cookie might be changed.
enum ChangeCause {
@@ -716,9 +733,12 @@ class NET_EXPORT CookieMonster::Delegate
virtual void OnCookieChanged(const CanonicalCookie& cookie,
bool removed,
ChangeCause cause) = 0;
+ // Indicates that the cookie store has fully loaded.
+ virtual void OnLoaded() = 0;
+
protected:
- friend class base::RefCountedThreadSafe<CookieMonster::Delegate>;
- virtual ~Delegate() {}
+ friend class base::RefCountedThreadSafe<CookieMonsterDelegate>;
+ virtual ~CookieMonsterDelegate() {}
};
typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore>
diff --git a/chromium/net/cookies/cookie_monster_store_test.cc b/chromium/net/cookies/cookie_monster_store_test.cc
index 350a0159b90..226242a6e50 100644
--- a/chromium/net/cookies/cookie_monster_store_test.cc
+++ b/chromium/net/cookies/cookie_monster_store_test.cc
@@ -97,6 +97,8 @@ void MockCookieMonsterDelegate::OnCookieChanged(
changes_.push_back(notification);
}
+void MockCookieMonsterDelegate::OnLoaded() {}
+
MockCookieMonsterDelegate::~MockCookieMonsterDelegate() {}
CanonicalCookie BuildCanonicalCookie(const std::string& key,
diff --git a/chromium/net/cookies/cookie_monster_store_test.h b/chromium/net/cookies/cookie_monster_store_test.h
index d7da52104be..efbcbe5bd56 100644
--- a/chromium/net/cookies/cookie_monster_store_test.h
+++ b/chromium/net/cookies/cookie_monster_store_test.h
@@ -115,8 +115,8 @@ class MockPersistentCookieStore
DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore);
};
-// Mock for CookieMonster::Delegate
-class MockCookieMonsterDelegate : public CookieMonster::Delegate {
+// Mock for CookieMonsterDelegate
+class MockCookieMonsterDelegate : public CookieMonsterDelegate {
public:
typedef std::pair<CanonicalCookie, bool>
CookieNotification;
@@ -130,7 +130,9 @@ class MockCookieMonsterDelegate : public CookieMonster::Delegate {
virtual void OnCookieChanged(
const CanonicalCookie& cookie,
bool removed,
- CookieMonster::Delegate::ChangeCause cause) OVERRIDE;
+ CookieMonsterDelegate::ChangeCause cause) OVERRIDE;
+
+ virtual void OnLoaded() OVERRIDE;
private:
virtual ~MockCookieMonsterDelegate();
diff --git a/chromium/net/cookies/cookie_monster_unittest.cc b/chromium/net/cookies/cookie_monster_unittest.cc
index 69d98a1862a..e7fb6b0875c 100644
--- a/chromium/net/cookies/cookie_monster_unittest.cc
+++ b/chromium/net/cookies/cookie_monster_unittest.cc
@@ -157,12 +157,13 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
bool http_only,
CookiePriority priority) {
DCHECK(cm);
- BoolResultCookieCallback callback;
+ ResultSavingCookieCallback<bool> callback;
cm->SetCookieWithDetailsAsync(
url, name, value, domain, path, expiration_time, secure, http_only,
priority,
- base::Bind(&BoolResultCookieCallback::Run,
- base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -170,9 +171,11 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
int DeleteAll(CookieMonster*cm) {
DCHECK(cm);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cm->DeleteAllAsync(
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -182,10 +185,12 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
const base::Time& delete_begin,
const base::Time& delete_end) {
DCHECK(cm);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cm->DeleteAllCreatedBetweenAsync(
delete_begin, delete_end,
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -196,10 +201,12 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
const base::Time delete_end,
const GURL& url) {
DCHECK(cm);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cm->DeleteAllCreatedBetweenForHostAsync(
delete_begin, delete_end, url,
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -208,9 +215,9 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
int DeleteAllForHost(CookieMonster* cm,
const GURL& url) {
DCHECK(cm);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cm->DeleteAllForHostAsync(
- url, base::Bind(&IntResultCookieCallback::Run,
+ url, base::Bind(&ResultSavingCookieCallback<int>::Run,
base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
@@ -219,10 +226,10 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
bool DeleteCanonicalCookie(CookieMonster* cm, const CanonicalCookie& cookie) {
DCHECK(cm);
- BoolResultCookieCallback callback;
+ ResultSavingCookieCallback<bool> callback;
cm->DeleteCanonicalCookieAsync(
cookie,
- base::Bind(&BoolResultCookieCallback::Run,
+ base::Bind(&ResultSavingCookieCallback<bool>::Run,
base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
@@ -1546,7 +1553,7 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCreationTimes) {
EXPECT_NE(name1, name2);
}
-TEST_F(CookieMonsterTest, Delegate) {
+TEST_F(CookieMonsterTest, CookieMonsterDelegate) {
scoped_refptr<MockPersistentCookieStore> store(
new MockPersistentCookieStore);
scoped_refptr<MockCookieMonsterDelegate> delegate(
@@ -2336,7 +2343,7 @@ class MultiThreadedCookieMonsterTest : public CookieMonsterTest {
}
void SetCookieWithDetailsTask(CookieMonster* cm, const GURL& url,
- BoolResultCookieCallback* callback) {
+ ResultSavingCookieCallback<bool>* callback) {
// Define the parameters here instead of in the calling fucntion.
// The maximum number of parameters for Bind function is 6.
std::string name = "A";
@@ -2350,43 +2357,51 @@ class MultiThreadedCookieMonsterTest : public CookieMonsterTest {
cm->SetCookieWithDetailsAsync(
url, name, value, domain, path, expiration_time, secure, http_only,
priority,
- base::Bind(&BoolResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(callback)));
}
void DeleteAllCreatedBetweenTask(CookieMonster* cm,
const base::Time& delete_begin,
const base::Time& delete_end,
- IntResultCookieCallback* callback) {
+ ResultSavingCookieCallback<int>* callback) {
cm->DeleteAllCreatedBetweenAsync(
delete_begin, delete_end,
- base::Bind(&IntResultCookieCallback::Run,
- base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run, base::Unretained(callback)));
}
void DeleteAllForHostTask(CookieMonster* cm,
const GURL& url,
- IntResultCookieCallback* callback) {
+ ResultSavingCookieCallback<int>* callback) {
cm->DeleteAllForHostAsync(
url,
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run, base::Unretained(callback)));
}
- void DeleteAllCreatedBetweenForHostTask(CookieMonster* cm,
- const base::Time delete_begin,
- const base::Time delete_end,
- const GURL& url,
- IntResultCookieCallback* callback) {
+ void DeleteAllCreatedBetweenForHostTask(
+ CookieMonster* cm,
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ ResultSavingCookieCallback<int>* callback) {
cm->DeleteAllCreatedBetweenForHostAsync(
delete_begin, delete_end, url,
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(callback)));
}
void DeleteCanonicalCookieTask(CookieMonster* cm,
const CanonicalCookie& cookie,
- BoolResultCookieCallback* callback) {
+ ResultSavingCookieCallback<bool>* callback) {
cm->DeleteCanonicalCookieAsync(
cookie,
- base::Bind(&BoolResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(callback)));
}
protected:
@@ -2485,7 +2500,7 @@ TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckSetCookieWithDetails) {
false,
false,
COOKIE_PRIORITY_DEFAULT));
- BoolResultCookieCallback callback(&other_thread_);
+ ResultSavingCookieCallback<bool> callback(&other_thread_);
base::Closure task = base::Bind(
&net::MultiThreadedCookieMonsterTest::SetCookieWithDetailsTask,
base::Unretained(this),
@@ -2504,7 +2519,7 @@ TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteAllCreatedBetween) {
1,
DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99), Time()));
EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
- IntResultCookieCallback callback(&other_thread_);
+ ResultSavingCookieCallback<int> callback(&other_thread_);
base::Closure task = base::Bind(
&net::MultiThreadedCookieMonsterTest::DeleteAllCreatedBetweenTask,
base::Unretained(this),
@@ -2521,7 +2536,7 @@ TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteAllForHost) {
EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
EXPECT_EQ(1, DeleteAllForHost(cm.get(), url_google_));
EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
- IntResultCookieCallback callback(&other_thread_);
+ ResultSavingCookieCallback<int> callback(&other_thread_);
base::Closure task = base::Bind(
&net::MultiThreadedCookieMonsterTest::DeleteAllForHostTask,
base::Unretained(this),
@@ -2562,7 +2577,7 @@ TEST_F(MultiThreadedCookieMonsterTest,
cm.get(), ago3, Time::Max(), url_google_));
EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
- IntResultCookieCallback callback(&other_thread_);
+ ResultSavingCookieCallback<int> callback(&other_thread_);
// 2. Second set of deletions.
base::Closure task = base::Bind(
@@ -2584,7 +2599,7 @@ TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteCanonicalCookie) {
EXPECT_TRUE(DeleteCanonicalCookie(cm.get(), *it));
EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
- BoolResultCookieCallback callback(&other_thread_);
+ ResultSavingCookieCallback<bool> callback(&other_thread_);
cookies = GetAllCookies(cm.get());
it = cookies.begin();
base::Closure task = base::Bind(
diff --git a/chromium/net/cookies/cookie_store.h b/chromium/net/cookies/cookie_store.h
index af996378893..b2552a6cf0e 100644
--- a/chromium/net/cookies/cookie_store.h
+++ b/chromium/net/cookies/cookie_store.h
@@ -15,6 +15,7 @@
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "net/base/net_export.h"
+#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
class GURL;
@@ -28,12 +29,11 @@ class CookieMonster;
class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> {
public:
// Callback definitions.
- typedef base::Callback<void(const std::string& cookie)>
- GetCookiesCallback;
+ typedef base::Callback<void(const CookieList& cookies)> GetCookieListCallback;
+ typedef base::Callback<void(const std::string& cookie)> GetCookiesCallback;
typedef base::Callback<void(bool success)> SetCookiesCallback;
typedef base::Callback<void(int num_deleted)> DeleteCallback;
-
// Sets a single cookie. Expects a cookie line, like "a=1; domain=b.com".
//
// Fails either if the cookie is invalid or if this is a non-HTTPONLY cookie
@@ -56,6 +56,12 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> {
const CookieOptions& options,
const GetCookiesCallback& callback) = 0;
+ // Returns all matching cookies without marking them as accessed,
+ // including HTTP only cookies.
+ virtual void GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) = 0;
+
// Deletes the passed in cookie for the specified URL.
virtual void DeleteCookieAsync(const GURL& url,
const std::string& cookie_name,
@@ -68,6 +74,18 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> {
const base::Time& delete_end,
const DeleteCallback& callback) = 0;
+ // Deletes all of the cookies that match the host of the given URL
+ // regardless of path and that have a creation_date greater than or
+ // equal to |delete_begin| and less then |delete_end|. This includes
+ // all http_only and secure cookies, but does not include any domain
+ // cookies that may apply to this host.
+ // Returns the number of cookies deleted.
+ virtual void DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) = 0;
+
virtual void DeleteSessionCookiesAsync(const DeleteCallback&) = 0;
// Returns the underlying CookieMonster.
diff --git a/chromium/net/cookies/cookie_store_test_callbacks.cc b/chromium/net/cookies/cookie_store_test_callbacks.cc
index 8d09f9ea516..8ba1c940637 100644
--- a/chromium/net/cookies/cookie_store_test_callbacks.cc
+++ b/chromium/net/cookies/cookie_store_test_callbacks.cc
@@ -39,21 +39,11 @@ void CookieCallback::CallbackEpilogue() {
loop_to_quit_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}
-BoolResultCookieCallback::BoolResultCookieCallback() : result_(false) {}
-BoolResultCookieCallback::BoolResultCookieCallback(base::Thread* run_in_thread)
- : CookieCallback(run_in_thread),
- result_(false) {}
-
StringResultCookieCallback::StringResultCookieCallback() {}
StringResultCookieCallback::StringResultCookieCallback(
base::Thread* run_in_thread)
: CookieCallback(run_in_thread) {}
-IntResultCookieCallback::IntResultCookieCallback() : result_(0) {}
-IntResultCookieCallback::IntResultCookieCallback(base::Thread* run_in_thread)
- : CookieCallback(run_in_thread),
- result_(0) {}
-
NoResultCookieCallback::NoResultCookieCallback() {}
NoResultCookieCallback::NoResultCookieCallback(base::Thread* run_in_thread)
: CookieCallback(run_in_thread) {}
diff --git a/chromium/net/cookies/cookie_store_test_callbacks.h b/chromium/net/cookies/cookie_store_test_callbacks.h
index c8308e1bc39..eccdf1bff26 100644
--- a/chromium/net/cookies/cookie_store_test_callbacks.h
+++ b/chromium/net/cookies/cookie_store_test_callbacks.h
@@ -48,20 +48,24 @@ class CookieCallback {
// Callback implementations for the asynchronous CookieStore methods.
-class BoolResultCookieCallback : public CookieCallback {
+template <typename T>
+class ResultSavingCookieCallback : public CookieCallback {
public:
- BoolResultCookieCallback();
- explicit BoolResultCookieCallback(base::Thread* run_in_thread);
+ ResultSavingCookieCallback() {
+ }
+ explicit ResultSavingCookieCallback(base::Thread* run_in_thread)
+ : CookieCallback(run_in_thread) {
+ }
- void Run(bool result) {
+ void Run(T result) {
result_ = result;
CallbackEpilogue();
}
- bool result() { return result_; }
+ const T& result() { return result_; }
private:
- bool result_;
+ T result_;
};
class StringResultCookieCallback : public CookieCallback {
@@ -80,22 +84,6 @@ class StringResultCookieCallback : public CookieCallback {
std::string result_;
};
-class IntResultCookieCallback : public CookieCallback {
- public:
- IntResultCookieCallback();
- explicit IntResultCookieCallback(base::Thread* run_in_thread);
-
- void Run(int result) {
- result_ = result;
- CallbackEpilogue();
- }
-
- int result() { return result_; }
-
- private:
- int result_;
-};
-
class NoResultCookieCallback : public CookieCallback {
public:
NoResultCookieCallback();
diff --git a/chromium/net/cookies/cookie_store_test_helpers.cc b/chromium/net/cookies/cookie_store_test_helpers.cc
index 22992f63ec5..fdf2c1f1ee8 100644
--- a/chromium/net/cookies/cookie_store_test_helpers.cc
+++ b/chromium/net/cookies/cookie_store_test_helpers.cc
@@ -68,6 +68,12 @@ void DelayedCookieMonster::GetCookiesWithOptionsAsync(
base::TimeDelta::FromMilliseconds(kDelayedTime));
}
+void DelayedCookieMonster::GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) {
+ cookie_monster_->GetAllCookiesForURLAsync(url, callback);
+}
+
void DelayedCookieMonster::InvokeSetCookiesCallback(
const CookieMonster::SetCookiesCallback& callback) {
if (!callback.is_null())
@@ -113,6 +119,14 @@ void DelayedCookieMonster::DeleteAllCreatedBetweenAsync(
ADD_FAILURE();
}
+void DelayedCookieMonster::DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) {
+ ADD_FAILURE();
+}
+
void DelayedCookieMonster::DeleteSessionCookiesAsync(const DeleteCallback&) {
ADD_FAILURE();
}
diff --git a/chromium/net/cookies/cookie_store_test_helpers.h b/chromium/net/cookies/cookie_store_test_helpers.h
index 86b572afdd9..84b83bc062c 100644
--- a/chromium/net/cookies/cookie_store_test_helpers.h
+++ b/chromium/net/cookies/cookie_store_test_helpers.h
@@ -34,6 +34,10 @@ class DelayedCookieMonster : public CookieStore {
const CookieOptions& options,
const CookieMonster::GetCookiesCallback& callback) OVERRIDE;
+ virtual void GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) OVERRIDE;
+
virtual bool SetCookieWithOptions(const GURL& url,
const std::string& cookie_line,
const CookieOptions& options);
@@ -53,6 +57,12 @@ class DelayedCookieMonster : public CookieStore {
const base::Time& delete_end,
const DeleteCallback& callback) OVERRIDE;
+ virtual void DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) OVERRIDE;
+
virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE;
virtual CookieMonster* GetCookieMonster() OVERRIDE;
diff --git a/chromium/net/cookies/cookie_store_unittest.h b/chromium/net/cookies/cookie_store_unittest.h
index 90999ecdc31..f0fcd71fd97 100644
--- a/chromium/net/cookies/cookie_store_unittest.h
+++ b/chromium/net/cookies/cookie_store_unittest.h
@@ -118,11 +118,12 @@ class CookieStoreTest : public testing::Test {
const std::string& cookie_line,
const CookieOptions& options) {
DCHECK(cs);
- BoolResultCookieCallback callback;
+ ResultSavingCookieCallback<bool> callback;
cs->SetCookieWithOptionsAsync(
url, cookie_line, options,
- base::Bind(&BoolResultCookieCallback::Run,
- base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -164,10 +165,28 @@ class CookieStoreTest : public testing::Test {
const base::Time& delete_begin,
const base::Time& delete_end) {
DCHECK(cs);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cs->DeleteAllCreatedBetweenAsync(
delete_begin, delete_end,
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
+ RunFor(kTimeout);
+ EXPECT_TRUE(callback.did_run());
+ return callback.result();
+ }
+
+ int DeleteAllCreatedBetweenForHost(CookieStore* cs,
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url) {
+ DCHECK(cs);
+ ResultSavingCookieCallback<int> callback;
+ cs->DeleteAllCreatedBetweenForHostAsync(
+ delete_begin, delete_end, url,
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -175,9 +194,11 @@ class CookieStoreTest : public testing::Test {
int DeleteSessionCookies(CookieStore* cs) {
DCHECK(cs);
- IntResultCookieCallback callback;
+ ResultSavingCookieCallback<int> callback;
cs->DeleteSessionCookiesAsync(
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(&callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(&callback)));
RunFor(kTimeout);
EXPECT_TRUE(callback.did_run());
return callback.result();
@@ -795,6 +816,26 @@ TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) {
this->GetCookies(cs.get(), this->url_google_));
}
+TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetweenForHost) {
+ scoped_refptr<CookieStore> cs(this->GetCookieStore());
+ GURL url_not_google("http://www.notgoogle.com");
+ base::Time now = base::Time::Now();
+
+ // These 3 cookies match the time range and host.
+ EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "A=B"));
+ EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "C=D"));
+ EXPECT_TRUE(this->SetCookie(cs.get(), this->url_google_, "Y=Z"));
+
+ // This cookie does not match host.
+ EXPECT_TRUE(this->SetCookie(cs.get(), url_not_google, "E=F"));
+
+ // Delete cookies.
+ EXPECT_EQ(
+ 3, // Deletes A=B, C=D, Y=Z
+ this->DeleteAllCreatedBetweenForHost(
+ cs.get(), now, base::Time::Max(), this->url_google_));
+}
+
TYPED_TEST_P(CookieStoreTest, TestSecure) {
scoped_refptr<CookieStore> cs(this->GetCookieStore());
@@ -976,6 +1017,7 @@ REGISTER_TYPED_TEST_CASE_P(CookieStoreTest,
HttpOnlyTest,
TestCookieDeletion,
TestDeleteAllCreatedBetween,
+ TestDeleteAllCreatedBetweenForHost,
TestSecure,
NetUtilCookieTest,
OverwritePersistentCookie,
@@ -1017,10 +1059,12 @@ class MultiThreadedCookieStoreTest :
const GURL& url,
const std::string& cookie_line,
const CookieOptions& options,
- BoolResultCookieCallback* callback) {
+ ResultSavingCookieCallback<bool>* callback) {
cs->SetCookieWithOptionsAsync(
url, cookie_line, options,
- base::Bind(&BoolResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<bool>::Run,
+ base::Unretained(callback)));
}
void DeleteCookieTask(CookieStore* cs,
@@ -1033,9 +1077,11 @@ class MultiThreadedCookieStoreTest :
}
void DeleteSessionCookiesTask(CookieStore* cs,
- IntResultCookieCallback* callback) {
+ ResultSavingCookieCallback<int>* callback) {
cs->DeleteSessionCookiesAsync(
- base::Bind(&IntResultCookieCallback::Run, base::Unretained(callback)));
+ base::Bind(
+ &ResultSavingCookieCallback<int>::Run,
+ base::Unretained(callback)));
}
protected:
@@ -1093,7 +1139,7 @@ TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckSetCookieWithOptions) {
options.set_include_httponly();
EXPECT_TRUE(
this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
- BoolResultCookieCallback callback(&this->other_thread_);
+ ResultSavingCookieCallback<bool> callback(&this->other_thread_);
base::Closure task = base::Bind(
&net::MultiThreadedCookieStoreTest<TypeParam>::SetCookieWithOptionsTask,
base::Unretained(this),
@@ -1138,7 +1184,7 @@ TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteSessionCookies) {
EXPECT_EQ(0, this->DeleteSessionCookies(cs.get()));
EXPECT_TRUE(
this->SetCookieWithOptions(cs.get(), this->url_google_, "A=B", options));
- IntResultCookieCallback callback(&this->other_thread_);
+ ResultSavingCookieCallback<int> callback(&this->other_thread_);
base::Closure task = base::Bind(
&net::MultiThreadedCookieStoreTest<TypeParam>::DeleteSessionCookiesTask,
base::Unretained(this),
diff --git a/chromium/net/cookies/cookie_util.cc b/chromium/net/cookies/cookie_util.cc
index a4200f5c1d6..3c80566e7a3 100644
--- a/chromium/net/cookies/cookie_util.cc
+++ b/chromium/net/cookies/cookie_util.cc
@@ -26,7 +26,8 @@ std::string GetEffectiveDomain(const std::string& scheme,
const std::string& host) {
if (scheme == "http" || scheme == "https") {
return registry_controlled_domains::GetDomainAndRegistry(
- host, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ host,
+ registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}
if (!DomainIsHostOnly(host))
@@ -50,7 +51,7 @@ bool GetCookieDomainWithString(const GURL& url,
}
// Get the normalized domain specified in cookie line.
- url_canon::CanonHostInfo ignored;
+ url::CanonHostInfo ignored;
std::string cookie_domain(CanonicalizeHost(domain_string, &ignored));
if (cookie_domain.empty())
return false;
diff --git a/chromium/net/data/ftp/dir-listing-ls-32.expected b/chromium/net/data/ftp/dir-listing-ls-32.expected
index 4626652da42..ac54545ef06 100644
--- a/chromium/net/data/ftp/dir-listing-ls-32.expected
+++ b/chromium/net/data/ftp/dir-listing-ls-32.expected
@@ -1,98 +1,98 @@
-d
-bin
--1
-1994
-1
-1
-0
-0
-
-d
-dev
--1
-1994
-1
-1
-0
-0
-
-d
-etc
--1
-1994
-1
-1
-0
-0
-
-d
-mnt
--1
-1994
-1
-1
-0
-0
-
-d
-nfs
--1
-1994
-1
-1
-0
-0
-
-d
-proc
--1
-1994
-1
-1
-0
-0
-
-d
-ram
--1
-1994
-1
-1
-0
-0
-
-d
-sbin
--1
-1994
-1
-1
-0
-0
-
-l
-tmp
--1
-1994
-1
-1
-0
-0
-
-d
-usr
--1
-1994
-1
-1
-0
-0
-
-l
-var
--1
-1994
-1
-1
-0
-0
+d
+bin
+-1
+1994
+1
+1
+0
+0
+
+d
+dev
+-1
+1994
+1
+1
+0
+0
+
+d
+etc
+-1
+1994
+1
+1
+0
+0
+
+d
+mnt
+-1
+1994
+1
+1
+0
+0
+
+d
+nfs
+-1
+1994
+1
+1
+0
+0
+
+d
+proc
+-1
+1994
+1
+1
+0
+0
+
+d
+ram
+-1
+1994
+1
+1
+0
+0
+
+d
+sbin
+-1
+1994
+1
+1
+0
+0
+
+l
+tmp
+-1
+1994
+1
+1
+0
+0
+
+d
+usr
+-1
+1994
+1
+1
+0
+0
+
+l
+var
+-1
+1994
+1
+1
+0
+0
diff --git a/chromium/net/data/spdy_tests/examples_07.hpack b/chromium/net/data/spdy_tests/examples_07.hpack
new file mode 100644
index 00000000000..ee573345d30
--- /dev/null
+++ b/chromium/net/data/spdy_tests/examples_07.hpack
Binary files differ
diff --git a/chromium/net/data/ssl/certificates/1024-rsa-ee-by-secp256k1-ecdsa-intermediate.pem b/chromium/net/data/ssl/certificates/1024-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
deleted file mode 100644
index 2d47aced565..00000000000
--- a/chromium/net/data/ssl/certificates/1024-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
+++ /dev/null
@@ -1,44 +0,0 @@
-Certificate:
- Data:
- Version: 1 (0x0)
- Serial Number: 237 (0xed)
- Signature Algorithm: ecdsa-with-SHA1
- Issuer: CN=secp256k1 ecdsa Test intermediate CA
- Validity
- Not Before: Dec 10 01:51:17 2011 GMT
- Not After : Dec 9 01:51:17 2012 GMT
- Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (1024 bit)
- Modulus (1024 bit):
- 00:9b:1b:ad:af:0e:61:db:3f:dc:b7:91:5d:bf:1f:
- 0a:70:6a:fa:89:b7:6e:fc:aa:ef:ce:9e:db:6c:4c:
- 9a:2d:81:7b:59:96:20:eb:11:ef:e4:85:c6:ca:33:
- 41:22:4a:20:86:9c:01:02:f9:63:13:9b:3b:1e:f5:
- a9:3e:40:98:8e:78:1f:99:32:64:2f:4c:dc:ae:3a:
- e7:cf:00:22:2f:77:f2:be:7b:64:9c:a0:92:27:b1:
- 35:4d:44:de:7b:cd:75:4a:a7:9b:27:e0:3c:0b:13:
- ee:57:5a:f7:c2:81:c0:b8:ea:0b:39:b5:6f:17:57:
- 24:f0:c0:c5:4b:b3:0d:92:6f
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Subject Alternative Name:
- IP Address:127.0.0.1
- Signature Algorithm: ecdsa-with-SHA1
- 30:46:02:21:00:83:ef:77:11:e7:67:3c:53:20:88:b6:03:10:
- e8:e5:9b:a1:12:48:3a:1e:a8:3b:31:fa:1b:56:95:28:d3:6e:
- 6b:02:21:00:cd:e3:2c:6e:41:59:e2:6a:d4:ec:de:11:99:99:
- e6:b7:7e:90:89:91:e5:35:d1:2c:c7:15:e7:46:94:ab:11:6f
------BEGIN CERTIFICATE-----
-MIIB0zCCAXkCAgDtMAkGByqGSM49BAEwLzEtMCsGA1UEAwwkc2VjcDI1NmsxIGVj
-ZHNhIFRlc3QgaW50ZXJtZWRpYXRlIENBMB4XDTExMTIxMDAxNTExN1oXDTEyMTIw
-OTAxNTExN1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
-BgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExEjAQBgNVBAMM
-CTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmxutrw5h2z/c
-t5Fdvx8KcGr6ibdu/Krvzp7bbEyaLYF7WZYg6xHv5IXGyjNBIkoghpwBAvljE5s7
-HvWpPkCYjngfmTJkL0zcrjrnzwAiL3fyvntknKCSJ7E1TUTee811SqebJ+A8CxPu
-V1r3woHAuOoLObVvF1ck8MDFS7MNkm8CAwEAAaMTMBEwDwYDVR0RBAgwBocEfwAA
-ATAJBgcqhkjOPQQBA0kAMEYCIQCD73cR52c8UyCItgMQ6OWboRJIOh6oOzH6G1aV
-KNNuawIhAM3jLG5BWeJq1OzeEZmZ5rd+kImR5TXRLMcV50aUqxFv
------END CERTIFICATE-----
diff --git a/chromium/net/data/ssl/certificates/2048-rsa-ee-by-secp256k1-ecdsa-intermediate.pem b/chromium/net/data/ssl/certificates/2048-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
deleted file mode 100644
index d23179db870..00000000000
--- a/chromium/net/data/ssl/certificates/2048-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
+++ /dev/null
@@ -1,56 +0,0 @@
-Certificate:
- Data:
- Version: 1 (0x0)
- Serial Number: 238 (0xee)
- Signature Algorithm: ecdsa-with-SHA1
- Issuer: CN=secp256k1 ecdsa Test intermediate CA
- Validity
- Not Before: Dec 10 01:51:17 2011 GMT
- Not After : Dec 9 01:51:17 2012 GMT
- Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (2048 bit)
- Modulus (2048 bit):
- 00:e7:10:f2:68:0c:18:a5:e5:dd:a8:4b:2f:6b:f5:
- 71:f4:bf:dd:ef:39:69:04:38:3d:52:c5:e7:cc:b3:
- eb:98:57:13:4e:3e:79:cf:80:4b:d7:9d:7e:88:f3:
- a9:02:47:b8:d9:ec:8a:8c:34:20:aa:29:3b:a1:d6:
- 45:23:b5:6d:36:56:3c:a4:64:13:ee:23:70:09:fa:
- 75:83:c6:b7:be:b5:b3:3f:80:cb:ce:7b:18:1f:ac:
- 7c:25:b6:58:bc:07:b7:35:77:2b:64:1e:ca:14:0b:
- d0:bb:6c:6e:1d:2f:ee:10:90:a1:ce:a9:ab:88:0a:
- 28:74:ae:ae:ca:fc:da:c3:3a:ba:39:de:c8:1b:46:
- bf:93:98:a2:5b:ba:b2:a6:d8:bd:54:52:be:52:31:
- fa:07:3a:6d:8f:42:c2:92:80:31:5c:ae:cb:15:f0:
- 72:cf:f6:5c:b9:f2:6b:91:b0:03:48:08:ae:a6:8d:
- e4:bd:a1:f6:05:38:1c:70:43:b6:7d:34:b5:c1:b9:
- 0b:f7:ec:71:0c:a4:20:92:2b:0f:c0:41:80:16:84:
- 64:98:6d:13:38:df:ce:82:98:8c:ac:97:56:10:6d:
- f8:e1:d5:19:ed:b7:60:44:c8:9e:72:61:1f:16:3b:
- 81:13:a8:c3:99:99:47:ba:81:68:af:2a:39:80:c5:
- 88:1b
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Subject Alternative Name:
- IP Address:127.0.0.1
- Signature Algorithm: ecdsa-with-SHA1
- 30:45:02:21:00:b4:35:4d:5d:8b:9c:bd:ea:be:86:13:75:ab:
- d0:af:cc:fb:39:85:20:0b:2d:a7:84:d7:ce:8f:44:54:b6:6a:
- 7c:02:20:62:49:3e:32:da:e2:fe:bb:f3:db:8d:f6:78:de:0a:
- 46:e3:93:87:1c:e7:b1:6f:81:9e:1d:b2:3b:5e:a2:2c:7b
------BEGIN CERTIFICATE-----
-MIICVjCCAf0CAgDuMAkGByqGSM49BAEwLzEtMCsGA1UEAwwkc2VjcDI1NmsxIGVj
-ZHNhIFRlc3QgaW50ZXJtZWRpYXRlIENBMB4XDTExMTIxMDAxNTExN1oXDTEyMTIw
-OTAxNTExN1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
-BgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExEjAQBgNVBAMM
-CTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcQ8mgM
-GKXl3ahLL2v1cfS/3e85aQQ4PVLF58yz65hXE04+ec+AS9edfojzqQJHuNnsiow0
-IKopO6HWRSO1bTZWPKRkE+4jcAn6dYPGt761sz+Ay857GB+sfCW2WLwHtzV3K2Qe
-yhQL0Ltsbh0v7hCQoc6pq4gKKHSursr82sM6ujneyBtGv5OYolu6sqbYvVRSvlIx
-+gc6bY9CwpKAMVyuyxXwcs/2XLnya5GwA0gIrqaN5L2h9gU4HHBDtn00tcG5C/fs
-cQykIJIrD8BBgBaEZJhtEzjfzoKYjKyXVhBt+OHVGe23YETInnJhHxY7gROow5mZ
-R7qBaK8qOYDFiBsCAwEAAaMTMBEwDwYDVR0RBAgwBocEfwAAATAJBgcqhkjOPQQB
-A0gAMEUCIQC0NU1di5y96r6GE3Wr0K/M+zmFIAstp4TXzo9EVLZqfAIgYkk+Mtri
-/rvz2432eN4KRuOThxznsW+Bnh2yO16iLHs=
------END CERTIFICATE-----
diff --git a/chromium/net/data/ssl/certificates/768-rsa-ee-by-secp256k1-ecdsa-intermediate.pem b/chromium/net/data/ssl/certificates/768-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
deleted file mode 100644
index 5825106a1c7..00000000000
--- a/chromium/net/data/ssl/certificates/768-rsa-ee-by-secp256k1-ecdsa-intermediate.pem
+++ /dev/null
@@ -1,42 +0,0 @@
-Certificate:
- Data:
- Version: 1 (0x0)
- Serial Number: 236 (0xec)
- Signature Algorithm: ecdsa-with-SHA1
- Issuer: CN=secp256k1 ecdsa Test intermediate CA
- Validity
- Not Before: Dec 10 01:51:16 2011 GMT
- Not After : Dec 9 01:51:16 2012 GMT
- Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (768 bit)
- Modulus (768 bit):
- 00:cb:5e:34:c8:77:3c:55:25:ee:1c:68:96:0c:2c:
- 48:2b:ed:83:ca:91:12:37:ea:71:ff:bc:c8:de:16:
- 03:0c:cf:b8:40:ff:3c:43:1f:10:ab:bf:d8:e4:8f:
- c1:82:cf:66:7d:c0:aa:c6:e6:3d:74:65:2d:df:f3:
- f7:e1:f0:c0:4a:f8:eb:b8:5d:63:ff:78:67:b7:c6:
- 1b:24:33:6d:0f:9c:39:86:72:41:03:26:8f:e0:55:
- 1d:1c:72:a5:38:15:8f
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Subject Alternative Name:
- IP Address:127.0.0.1
- Signature Algorithm: ecdsa-with-SHA1
- 30:44:02:20:72:f6:48:3b:5d:88:f4:fc:50:c8:74:21:a6:f2:
- c4:f7:d0:40:69:a1:48:93:98:36:fe:36:16:ec:95:a6:28:12:
- 02:20:48:e4:7e:32:a0:4b:c0:4d:08:5f:c8:63:f9:67:7f:2d:
- dc:78:77:78:ec:0e:a2:ee:78:60:d9:07:7d:b3:0a:d3
------BEGIN CERTIFICATE-----
-MIIBrTCCAVUCAgDsMAkGByqGSM49BAEwLzEtMCsGA1UEAwwkc2VjcDI1NmsxIGVj
-ZHNhIFRlc3QgaW50ZXJtZWRpYXRlIENBMB4XDTExMTIxMDAxNTExNloXDTEyMTIw
-OTAxNTExNlowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
-BgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExEjAQBgNVBAMM
-CTEyNy4wLjAuMTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDLXjTIdzxVJe4caJYM
-LEgr7YPKkRI36nH/vMjeFgMMz7hA/zxDHxCrv9jkj8GCz2Z9wKrG5j10ZS3f8/fh
-8MBK+Ou4XWP/eGe3xhskM20PnDmGckEDJo/gVR0ccqU4FY8CAwEAAaMTMBEwDwYD
-VR0RBAgwBocEfwAAATAJBgcqhkjOPQQBA0cAMEQCIHL2SDtdiPT8UMh0IabyxPfQ
-QGmhSJOYNv42FuyVpigSAiBI5H4yoEvATQhfyGP5Z38t3Hh3eOwOou54YNkHfbMK
-0w==
------END CERTIFICATE-----
diff --git a/chromium/net/data/ssl/certificates/README b/chromium/net/data/ssl/certificates/README
index 0ef59fca041..9634a282a8b 100644
--- a/chromium/net/data/ssl/certificates/README
+++ b/chromium/net/data/ssl/certificates/README
@@ -99,6 +99,13 @@ unit tests.
26 Feb 2022 and are generated by
net/data/ssl/scripts/generate-redundant-test-chains.sh.
+- multi-root-chain1.pem
+- multi-root-chain2.pem
+ Two chains, A -> B -> C -> D and A -> B -> C2 -> E (C and C2 share the
+ same public key) to test that certificate validation caching does not
+ interfere with the chain_verify_callback used by CertVerifyProcChromeOS.
+ See CertVerifyProcChromeOSTest.
+
- comodo.chain.pem : A certificate chain for www.comodo.com which should be
recognised as EV. Expires Jun 21 2013.
diff --git a/chromium/net/data/ssl/certificates/multi-root-chain1.pem b/chromium/net/data/ssl/certificates/multi-root-chain1.pem
new file mode 100644
index 00000000000..da8d08aa5b5
--- /dev/null
+++ b/chromium/net/data/ssl/certificates/multi-root-chain1.pem
@@ -0,0 +1,328 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvYF0kDZ7Nd/z6hjKuWSoWIWQKt4ivpoLl73+YZgq5S3PnrwM
+I14hTxmttT6c1KoFWnFOt5mBp2s1CSJDhL7pccZwO2+IxCu6CrjBfEK+P9gLiIdT
+gnujctRCU/N9y4RQYDQchmU2++ZHT2Fgw5t7SlduLKyQ7kcNspLiu+h84nQ+co8C
+kUHoLmAagIN2uIf9818E3VaX8xEEcJ5I4pblZqOF3bL2kR7ow6Z5lLfkbiU4J6N5
+mZxTuovJz9Ze4MDGBZpgRnMlVqQCPNw21WASD8n5TsAeK3Rg7wMwilN+yArrK5vb
+o2MDhbi6yv2T/fOPh5cRJNUR81Csuh+d1+QwfQIDAQABAoIBAA6ExYZq9iOZhdlw
+js+HW7J0gSgXnrfVm3/DqaKWgurOCLMTmyZ2hrzFrd5N7rwITqKwPaSpWRqXhxet
+DVk1OzNhTaXwFJ1a8ET0BLbdci/4AGI0Y/yCNnKMuowuAnw+Jd5I/8p4JK9F5D67
+qisyVU7LxgAcNHpc7Tq6MC7PUAoVVd7uPy0NtSD3JikwYWHUv+BtOm8P5jugy8Kr
+qMN0c0/qsZ0SedfT1k/IIfu79DZZ5hBl5rUhgMYjuycd0Hi5qOa7o2fdVXJh2eLo
+HY3cQTymWlibutABv//P+NsteyrXXFkcmPjNXHy5WpvmT2RR5Hxx57hJck/7xVjv
+u8B7AgECgYEA9vLjrUeO2Wei5BsL9I5xB+epwaXAUsaugnRXePG5i2HhCRfOeU9f
+PKuv0F4yuHNre9j1w5X/5WzxnFhDSb1QLbFXlgFCltL5a3RypygC9B0r7VFQCaLI
+ODjX5h06PZq6oLLiLSyVlOSrcPXNJLuhMqNnHuOUacUtOIx4B2vpwP0CgYEAxHOU
+YlMwUhgmPKZ/R7lNYMAKKUAiuKEkFM0K1K0/GnizhYDnc2atxORk54ZD6aGYuf9y
+xBWmOZobNTmGw9bnQhePai/xHUoJgs8KWN5e8pBJXAouL12pqRC4sQCIVc9R3J+7
+djTPTco0oexkUsbe1rr4hCqoR9iUePxl7wY5BYECgYEA8nA6fV+HKoDINlEnR4yg
+Aza4PdjQG3Pa11AIoEAP/Hq3RwoMNqRpx1J2ZIZWHSeTGh9CCCY297Ig8XDlfntR
+P8qfRjEugovVOl00Qk7Rt378JRxzC0K4dhm4O73t85T4K9PyoI7ouyhT964ZHDro
+YqJxFq4ugjiF0MJ3BDI5ZrECgYAItf9MZNftq/h2FAPs0ECoG5vXvGpNuYd6DKWA
+TLZRnCyJrO+WZGUsJ9x5j7CPOYUmKjeSjksynqy6LXTWVj8m5RiM4tdULyZA0KFq
+02FubAt0s1bc7tBJGN63qohhFbJRkBul4C2ZC3BOBcdlDEBxURUX9zRDC016F+cF
+NEdvAQKBgQCtJhaEt1dzbB0qYEZz7kYr3QP1hz/6XfnAd8wmJNQQQ8yctNlJc+dH
+Foo3qwU2uJWFbByBxIvkmCDTAOCJwOONP/MhigvDQth/sa+TbY6y39XywL0TyG/p
+X19RWD9TYNG7C9y9Z8Jcz+u9iCd5enqLYw7Qle9gq3jwK1GXsV5zEQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=B CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:81:74:90:36:7b:35:df:f3:ea:18:ca:b9:64:
+ a8:58:85:90:2a:de:22:be:9a:0b:97:bd:fe:61:98:
+ 2a:e5:2d:cf:9e:bc:0c:23:5e:21:4f:19:ad:b5:3e:
+ 9c:d4:aa:05:5a:71:4e:b7:99:81:a7:6b:35:09:22:
+ 43:84:be:e9:71:c6:70:3b:6f:88:c4:2b:ba:0a:b8:
+ c1:7c:42:be:3f:d8:0b:88:87:53:82:7b:a3:72:d4:
+ 42:53:f3:7d:cb:84:50:60:34:1c:86:65:36:fb:e6:
+ 47:4f:61:60:c3:9b:7b:4a:57:6e:2c:ac:90:ee:47:
+ 0d:b2:92:e2:bb:e8:7c:e2:74:3e:72:8f:02:91:41:
+ e8:2e:60:1a:80:83:76:b8:87:fd:f3:5f:04:dd:56:
+ 97:f3:11:04:70:9e:48:e2:96:e5:66:a3:85:dd:b2:
+ f6:91:1e:e8:c3:a6:79:94:b7:e4:6e:25:38:27:a3:
+ 79:99:9c:53:ba:8b:c9:cf:d6:5e:e0:c0:c6:05:9a:
+ 60:46:73:25:56:a4:02:3c:dc:36:d5:60:12:0f:c9:
+ f9:4e:c0:1e:2b:74:60:ef:03:30:8a:53:7e:c8:0a:
+ eb:2b:9b:db:a3:63:03:85:b8:ba:ca:fd:93:fd:f3:
+ 8f:87:97:11:24:d5:11:f3:50:ac:ba:1f:9d:d7:e4:
+ 30:7d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 2C:58:4E:D7:58:62:F8:D6:BF:BE:80:5C:04:3A:39:B1:41:8C:D3:D7
+ X509v3 Authority Key Identifier:
+ keyid:CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 0d:09:9b:f0:11:81:5a:5d:f1:85:73:fb:f2:c8:bc:82:99:27:
+ fd:58:c7:6a:d6:28:f1:09:63:4e:61:fd:68:3a:23:0a:10:fe:
+ 58:7b:20:a3:7a:69:e6:74:55:5c:14:4f:ec:d7:e2:10:aa:8b:
+ a6:3f:99:c1:ef:bb:48:a9:42:b8:9a:8e:65:75:7b:cc:5c:bd:
+ e4:c0:63:e2:d1:6b:38:32:5f:33:b3:fc:0c:33:b0:8b:dd:10:
+ 54:15:99:2d:c4:62:68:3f:af:3f:e2:d9:52:33:d4:31:4c:ed:
+ c7:28:76:bb:21:6b:b9:41:fb:88:40:52:e1:52:76:31:af:d8:
+ a6:63:d1:d9:a5:e4:95:39:74:a8:73:f5:b0:c4:4e:bf:ef:a1:
+ 87:2e:94:a0:bd:cc:e3:90:45:5c:5a:18:15:0d:09:47:45:0f:
+ 1e:d7:cf:d3:8d:c6:b0:54:c4:5f:21:50:5c:b5:0d:eb:c3:15:
+ 3d:ee:88:fa:b4:80:93:4d:2e:00:9e:46:b6:fb:e8:64:32:51:
+ 8a:35:81:7a:d3:71:73:8d:2a:27:d4:57:2f:66:4c:fc:99:81:
+ 06:66:1f:5b:e6:a4:e5:35:eb:f3:e5:e3:f3:31:78:fe:b0:03:
+ f9:5d:77:97:70:d7:53:52:50:df:15:5b:3e:fc:7e:9a:1f:6a:
+ ce:c2:37:2f
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMGAxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAw
+DgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9gXSQNns13/PqGMq5ZKhYhZAq3iK+mguXvf5h
+mCrlLc+evAwjXiFPGa21PpzUqgVacU63mYGnazUJIkOEvulxxnA7b4jEK7oKuMF8
+Qr4/2AuIh1OCe6Ny1EJT833LhFBgNByGZTb75kdPYWDDm3tKV24srJDuRw2ykuK7
+6HzidD5yjwKRQeguYBqAg3a4h/3zXwTdVpfzEQRwnkjiluVmo4XdsvaRHujDpnmU
+t+RuJTgno3mZnFO6i8nP1l7gwMYFmmBGcyVWpAI83DbVYBIPyflOwB4rdGDvAzCK
+U37ICusrm9ujYwOFuLrK/ZP984+HlxEk1RHzUKy6H53X5DB9AgMBAAGjbzBtMAwG
+A1UdEwEB/wQCMAAwHQYDVR0OBBYEFCxYTtdYYvjWv76AXAQ6ObFBjNPXMB8GA1Ud
+IwQYMBaAFM0YJ0+IsSaFuY+cAKT4Gv0Bhl7sMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEADQmb8BGBWl3xhXP78si8gpkn
+/VjHatYo8QljTmH9aDojChD+WHsgo3pp5nRVXBRP7NfiEKqLpj+Zwe+7SKlCuJqO
+ZXV7zFy95MBj4tFrODJfM7P8DDOwi90QVBWZLcRiaD+vP+LZUjPUMUztxyh2uyFr
+uUH7iEBS4VJ2Ma/YpmPR2aXklTl0qHP1sMROv++hhy6UoL3M45BFXFoYFQ0JR0UP
+HtfP043GsFTEXyFQXLUN68MVPe6I+rSAk00uAJ5GtvvoZDJRijWBetNxc40qJ9RX
+L2ZM/JmBBmYfW+ak5TXr8+Xj8zF4/rAD+V13l3DXU1JQ3xVbPvx+mh9qzsI3Lw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4097 (0x1001)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=C CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=B CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ab:18:b1:0e:c2:f9:61:1a:13:c4:95:b8:65:d8:
+ cb:36:2f:72:e8:e1:97:44:1d:0d:55:a0:cc:02:12:
+ 04:a4:78:f6:df:c3:6f:c9:b8:50:15:d8:5c:21:ed:
+ 8f:c4:e7:db:81:da:fe:01:60:39:63:99:e4:04:dc:
+ 15:ea:59:6b:35:da:1a:e3:3e:dd:f0:71:8f:64:79:
+ 1a:30:b1:be:5a:43:9c:88:1a:a0:26:2f:5d:6c:b6:
+ 8b:b8:6d:10:b0:97:f4:a1:d3:7b:56:3d:dd:12:19:
+ a3:5e:02:24:9e:19:8e:d2:0b:e2:ea:8e:a2:cc:a1:
+ 8e:f0:49:e1:81:ae:3a:a6:71:d5:e8:e4:80:27:01:
+ 58:66:8c:bd:a0:4f:08:01:59:5c:c3:e3:d5:6f:49:
+ 73:24:66:f5:25:b3:9e:a1:29:21:de:e9:d5:ad:b0:
+ 1d:fc:b7:4c:f7:5a:9a:2b:5a:2c:af:07:aa:c2:82:
+ 5a:36:06:1d:27:2d:90:c7:45:1e:7b:f4:7a:8a:fe:
+ 90:c1:79:c9:8f:4e:67:52:48:ea:0b:dd:d7:fe:84:
+ 54:47:2f:d9:d0:ca:11:07:59:b0:90:08:0b:76:a2:
+ ec:30:a5:45:aa:d7:61:39:84:43:33:97:22:b6:45:
+ c8:e8:ab:73:5f:79:a8:13:55:2f:71:a2:c9:21:aa:
+ 9b:df
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 00:80:9b:99:4b:81:b1:76:49:5c:f2:99:ef:88:93:18:75:68:
+ eb:26:9e:87:95:b6:91:23:c0:b5:78:58:96:97:9c:27:38:fa:
+ f3:e5:c3:a2:34:40:91:6d:45:2c:7f:63:51:98:5b:53:4b:91:
+ ce:0f:e2:32:63:e7:e5:f9:21:6b:b7:f9:94:ac:33:13:5c:27:
+ 9b:98:e7:7f:44:50:29:98:68:81:53:6b:a1:43:d8:04:40:cb:
+ f2:cf:18:39:a5:24:c9:88:b6:b7:76:cb:dd:18:2e:1d:24:3e:
+ d3:59:e9:04:48:9d:59:0c:0f:d0:79:6a:93:7d:2b:b9:8e:50:
+ 34:00:0d:48:ae:10:bd:80:04:74:da:06:27:15:ad:88:ec:36:
+ 61:51:80:fe:6b:1d:46:37:0e:ea:23:60:c1:79:bf:03:2a:d2:
+ 80:9e:2c:10:3a:bc:2d:50:6e:f7:f9:d5:ec:11:d9:b4:62:bc:
+ 09:2c:05:31:06:bf:b9:e1:d5:1e:02:a9:1a:c5:c4:13:bf:b7:
+ 8e:6a:08:51:57:af:db:7b:09:74:bd:c7:bd:3c:de:0a:51:8a:
+ fe:82:0b:4b:34:74:10:4b:4b:34:fd:42:28:48:10:db:5d:6d:
+ 64:80:b1:3c:5c:04:86:32:6c:25:87:db:23:dc:e4:42:e4:71:
+ f9:b1:88:74
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgICEAEwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQyBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMA8xDTALBgNVBAMMBEIg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrGLEOwvlhGhPElbhl
+2Ms2L3Lo4ZdEHQ1VoMwCEgSkePbfw2/JuFAV2Fwh7Y/E59uB2v4BYDljmeQE3BXq
+WWs12hrjPt3wcY9keRowsb5aQ5yIGqAmL11stou4bRCwl/Sh03tWPd0SGaNeAiSe
+GY7SC+LqjqLMoY7wSeGBrjqmcdXo5IAnAVhmjL2gTwgBWVzD49VvSXMkZvUls56h
+KSHe6dWtsB38t0z3WporWiyvB6rCglo2Bh0nLZDHRR579HqK/pDBecmPTmdSSOoL
+3df+hFRHL9nQyhEHWbCQCAt2ouwwpUWq12E5hEMzlyK2Rcjoq3NfeagTVS9xoskh
+qpvfAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM0YJ0+IsSaF
+uY+cAKT4Gv0Bhl7sMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEA
+AICbmUuBsXZJXPKZ74iTGHVo6yaeh5W2kSPAtXhYlpecJzj68+XDojRAkW1FLH9j
+UZhbU0uRzg/iMmPn5fkha7f5lKwzE1wnm5jnf0RQKZhogVNroUPYBEDL8s8YOaUk
+yYi2t3bL3RguHSQ+01npBEidWQwP0Hlqk30ruY5QNAANSK4QvYAEdNoGJxWtiOw2
+YVGA/msdRjcO6iNgwXm/AyrSgJ4sEDq8LVBu9/nV7BHZtGK8CSwFMQa/ueHVHgKp
+GsXEE7+3jmoIUVev23sJdL3HvTzeClGK/oILSzR0EEtLNP1CKEgQ211tZICxPFwE
+hjJsJYfbI9zkQuRx+bGIdA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4099 (0x1003)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=D Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=C CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ca:21:76:fd:8b:0f:24:ba:dc:ab:cd:0e:31:94:
+ 01:90:57:79:18:3c:58:61:f5:7e:b7:2f:e6:46:d3:
+ 41:e8:4e:29:29:a8:9d:eb:d6:df:69:d5:b1:10:de:
+ 6b:17:2d:8d:8a:1c:d7:dc:80:85:74:c2:7a:6e:a6:
+ 75:7a:2a:76:42:fb:65:6b:8c:a9:2f:c0:5e:76:1e:
+ bc:35:85:b2:4b:35:a4:97:33:15:76:7d:4f:6e:d0:
+ 3b:45:04:fe:dc:a0:11:67:15:2d:3a:c3:07:c6:db:
+ f2:89:25:92:5e:db:70:bd:88:e9:f0:c7:54:6f:8e:
+ ab:cf:ce:ca:ad:bb:44:72:bf:e9:b5:ab:ba:68:15:
+ 6d:1e:e1:66:d6:60:d8:bd:dc:ab:d3:e8:2f:4c:ee:
+ 29:46:36:c7:b1:61:af:20:19:cc:98:1c:78:5f:4d:
+ 97:7a:de:2f:d9:fd:f0:b8:47:34:ff:ed:73:07:eb:
+ 90:54:11:e2:1b:8e:68:5a:c1:72:a9:af:df:e9:f1:
+ f5:ca:0e:72:03:90:1b:af:64:d6:ee:ce:67:57:1b:
+ fb:c7:f1:c2:5c:97:81:cd:d6:22:7c:26:bf:cd:6a:
+ b9:99:5f:58:63:5f:ce:05:1c:7d:f1:a9:d3:f8:4c:
+ fe:10:82:a4:14:2c:67:97:6c:82:2f:98:38:83:50:
+ bf:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ F5:87:AF:4C:B3:4A:88:7F:EB:92:D3:C7:28:78:91:D9:02:4A:71:DF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 7a:34:4b:a9:c4:e0:82:72:8f:2d:a6:3e:c7:5a:60:61:18:d4:
+ 29:34:96:05:1e:9c:16:23:30:84:2b:72:10:3f:27:cd:0a:e1:
+ 23:0a:fe:6d:86:08:fb:d2:3f:fc:8d:d1:1f:c5:44:bb:89:dc:
+ ae:27:c3:20:99:a3:af:d8:ec:d6:cf:07:e6:59:ae:f4:26:18:
+ 39:78:44:24:4e:aa:93:c2:27:d3:05:ac:06:94:94:8a:d9:fc:
+ 5d:d8:cb:b9:71:14:c8:95:be:29:ee:65:cb:ff:3e:70:05:ef:
+ f9:12:6b:11:cb:37:6d:2d:8a:0e:9d:0d:3b:d8:eb:94:a2:f7:
+ d9:c6:0e:56:18:64:69:e6:3f:3c:64:da:37:c8:f8:f9:dd:1d:
+ 20:e1:af:61:54:09:5f:64:ce:b0:3e:38:fe:fc:20:23:96:d8:
+ 9b:d3:08:23:34:c9:0e:f9:ef:04:96:4a:c8:4c:6e:22:ff:95:
+ 52:db:a8:7f:58:50:eb:10:ca:6a:7b:44:fc:7b:e0:d0:9c:f1:
+ 3d:5a:c6:26:b4:8b:8a:cb:c9:40:94:f6:c8:14:5a:c8:7f:53:
+ 79:a9:d1:83:21:36:a2:ee:3a:50:45:d6:2d:a4:47:ea:67:94:
+ ec:5f:e5:c2:1f:0a:a5:1f:7a:62:ce:d7:b0:b2:bb:e6:af:ab:
+ d5:ed:f6:39
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgICEAMwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJRCBS
+b290IENBMB4XDTE0MDIwNDA0MjU1OFoXDTI0MDIwMjA0MjU1OFowDzENMAsGA1UE
+AwwEQyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMohdv2LDyS6
+3KvNDjGUAZBXeRg8WGH1frcv5kbTQehOKSmonevW32nVsRDeaxctjYoc19yAhXTC
+em6mdXoqdkL7ZWuMqS/AXnYevDWFsks1pJczFXZ9T27QO0UE/tygEWcVLTrDB8bb
+8oklkl7bcL2I6fDHVG+Oq8/Oyq27RHK/6bWrumgVbR7hZtZg2L3cq9PoL0zuKUY2
+x7FhryAZzJgceF9Nl3reL9n98LhHNP/tcwfrkFQR4huOaFrBcqmv3+nx9coOcgOQ
+G69k1u7OZ1cb+8fxwlyXgc3WInwmv81quZlfWGNfzgUcffGp0/hM/hCCpBQsZ5ds
+gi+YOINQv3ECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU9Yev
+TLNKiH/rktPHKHiR2QJKcd8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQB6NEupxOCCco8tpj7HWmBhGNQpNJYFHpwWIzCEK3IQPyfNCuEjCv5thgj7
+0j/8jdEfxUS7idyuJ8MgmaOv2OzWzwfmWa70Jhg5eEQkTqqTwifTBawGlJSK2fxd
+2Mu5cRTIlb4p7mXL/z5wBe/5EmsRyzdtLYoOnQ072OuUovfZxg5WGGRp5j88ZNo3
+yPj53R0g4a9hVAlfZM6wPjj+/CAjltib0wgjNMkO+e8ElkrITG4i/5VS26h/WFDr
+EMpqe0T8e+DQnPE9WsYmtIuKy8lAlPbIFFrIf1N5qdGDITai7jpQRdYtpEfqZ5Ts
+X+XCHwqlH3piztewsrvmr6vV7fY5
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 13476701051823430697 (0xbb06dc343a119c29)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=D Root CA
+ Validity
+ Not Before: Feb 4 04:25:57 2014 GMT
+ Not After : Feb 2 04:25:57 2024 GMT
+ Subject: CN=D Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a8:4f:68:60:f8:f7:db:96:c7:0a:33:3a:bb:24:
+ 01:ec:06:15:68:9e:33:b1:2c:4e:b6:e0:23:bc:ef:
+ 4e:b6:03:09:86:21:72:85:bf:5a:c4:88:54:f8:44:
+ 45:a9:aa:7c:74:50:3c:43:5f:c1:e5:0f:59:8c:e2:
+ 57:48:8f:d9:d9:89:53:01:4a:24:8b:70:b6:38:5d:
+ d4:e3:14:d3:ba:49:83:a5:b7:31:0f:10:2d:06:40:
+ bc:11:39:53:67:04:21:b2:52:a9:61:66:34:2d:3e:
+ ce:0a:9e:96:e7:60:3d:e4:7a:10:9c:1d:e6:8d:a4:
+ 11:20:e9:c5:60:7d:00:d4:03:ff:1b:92:ba:d1:43:
+ 44:12:2e:b9:3e:77:0f:98:33:60:e8:df:67:d8:08:
+ 2d:1a:b4:62:3e:75:6b:76:b8:a1:d1:8f:09:4d:aa:
+ 88:f8:61:90:6c:ce:84:15:85:f8:bd:ba:40:e9:33:
+ 22:ed:63:fa:1b:cf:6a:0d:96:91:11:e1:c7:3a:b9:
+ c3:7a:42:d3:78:5c:c7:c7:b1:72:05:63:92:22:28:
+ b7:ea:3c:0a:d0:6d:9c:aa:6d:60:66:29:bb:43:6a:
+ c0:2b:ce:ef:05:5c:7c:d9:8b:c4:9e:65:80:f0:32:
+ 67:b9:4b:9c:65:7a:df:62:a2:2e:b4:14:50:15:87:
+ b5:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ A6:3C:3F:34:0B:14:89:E3:BF:92:BA:F8:18:0B:E3:C6:31:4C:77:D0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 1e:e6:98:ac:39:4a:f0:c2:c4:70:76:dd:c7:26:2c:61:00:be:
+ fb:73:1e:fd:72:d1:25:a0:84:51:ee:e7:76:f6:72:8e:89:8b:
+ 69:ef:1c:10:ae:ce:4a:29:2b:d6:43:52:3f:32:30:c4:3f:3f:
+ 2a:3e:c8:af:cc:d8:0d:54:b9:74:ac:0c:9e:43:71:c1:56:88:
+ e8:92:a3:40:f4:c0:73:e3:28:bd:93:02:af:c2:1b:66:5e:8c:
+ 5b:0e:48:55:c6:48:aa:f5:c6:c2:c1:cc:83:ac:59:d2:40:25:
+ f9:84:52:8f:57:97:1d:ea:3d:24:26:19:a8:ba:60:1a:dc:bc:
+ 7d:cc:8d:9a:fc:0f:7f:3d:b8:b4:ef:b9:47:28:0f:07:0b:57:
+ ea:3a:12:c8:01:69:8e:49:46:f8:19:cf:ad:69:5a:94:48:f2:
+ 54:3d:b0:99:02:c8:d6:ff:4e:a7:cf:d1:b4:e5:94:92:ce:3f:
+ d1:86:1b:01:6b:51:cd:94:cc:c1:2c:dd:4d:43:c2:e5:cd:21:
+ 9f:3f:ec:88:b7:e4:9d:4c:f5:55:61:2c:75:f9:4b:f7:2f:ba:
+ 2f:6f:d9:f7:1e:70:b5:a5:2f:ea:e9:b6:b0:61:34:0a:20:55:
+ c0:73:af:4a:d3:32:64:7d:cc:c4:3f:b2:45:ad:1e:4c:f6:ad:
+ d9:bf:a7:e0
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIJALsG3DQ6EZwpMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
+BAMMCUQgUm9vdCBDQTAeFw0xNDAyMDQwNDI1NTdaFw0yNDAyMDIwNDI1NTdaMBQx
+EjAQBgNVBAMMCUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKhPaGD499uWxwozOrskAewGFWieM7EsTrbgI7zvTrYDCYYhcoW/WsSIVPhE
+RamqfHRQPENfweUPWYziV0iP2dmJUwFKJItwtjhd1OMU07pJg6W3MQ8QLQZAvBE5
+U2cEIbJSqWFmNC0+zgqeludgPeR6EJwd5o2kESDpxWB9ANQD/xuSutFDRBIuuT53
+D5gzYOjfZ9gILRq0Yj51a3a4odGPCU2qiPhhkGzOhBWF+L26QOkzIu1j+hvPag2W
+kRHhxzq5w3pC03hcx8excgVjkiIot+o8CtBtnKptYGYpu0NqwCvO7wVcfNmLxJ5l
+gPAyZ7lLnGV632KiLrQUUBWHtacCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUpjw/NAsUieO/krr4GAvjxjFMd9AwDgYDVR0PAQH/BAQDAgEGMA0G
+CSqGSIb3DQEBBQUAA4IBAQAe5pisOUrwwsRwdt3HJixhAL77cx79ctEloIRR7ud2
+9nKOiYtp7xwQrs5KKSvWQ1I/MjDEPz8qPsivzNgNVLl0rAyeQ3HBVojokqNA9MBz
+4yi9kwKvwhtmXoxbDkhVxkiq9cbCwcyDrFnSQCX5hFKPV5cd6j0kJhmoumAa3Lx9
+zI2a/A9/Pbi077lHKA8HC1fqOhLIAWmOSUb4Gc+taVqUSPJUPbCZAsjW/06nz9G0
+5ZSSzj/RhhsBa1HNlMzBLN1NQ8LlzSGfP+yIt+SdTPVVYSx1+Uv3L7ovb9n3HnC1
+pS/q6bawYTQKIFXAc69K0zJkfczEP7JFrR5M9q3Zv6fg
+-----END CERTIFICATE-----
diff --git a/chromium/net/data/ssl/certificates/multi-root-chain2.pem b/chromium/net/data/ssl/certificates/multi-root-chain2.pem
new file mode 100644
index 00000000000..b853bb3f7be
--- /dev/null
+++ b/chromium/net/data/ssl/certificates/multi-root-chain2.pem
@@ -0,0 +1,328 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvYF0kDZ7Nd/z6hjKuWSoWIWQKt4ivpoLl73+YZgq5S3PnrwM
+I14hTxmttT6c1KoFWnFOt5mBp2s1CSJDhL7pccZwO2+IxCu6CrjBfEK+P9gLiIdT
+gnujctRCU/N9y4RQYDQchmU2++ZHT2Fgw5t7SlduLKyQ7kcNspLiu+h84nQ+co8C
+kUHoLmAagIN2uIf9818E3VaX8xEEcJ5I4pblZqOF3bL2kR7ow6Z5lLfkbiU4J6N5
+mZxTuovJz9Ze4MDGBZpgRnMlVqQCPNw21WASD8n5TsAeK3Rg7wMwilN+yArrK5vb
+o2MDhbi6yv2T/fOPh5cRJNUR81Csuh+d1+QwfQIDAQABAoIBAA6ExYZq9iOZhdlw
+js+HW7J0gSgXnrfVm3/DqaKWgurOCLMTmyZ2hrzFrd5N7rwITqKwPaSpWRqXhxet
+DVk1OzNhTaXwFJ1a8ET0BLbdci/4AGI0Y/yCNnKMuowuAnw+Jd5I/8p4JK9F5D67
+qisyVU7LxgAcNHpc7Tq6MC7PUAoVVd7uPy0NtSD3JikwYWHUv+BtOm8P5jugy8Kr
+qMN0c0/qsZ0SedfT1k/IIfu79DZZ5hBl5rUhgMYjuycd0Hi5qOa7o2fdVXJh2eLo
+HY3cQTymWlibutABv//P+NsteyrXXFkcmPjNXHy5WpvmT2RR5Hxx57hJck/7xVjv
+u8B7AgECgYEA9vLjrUeO2Wei5BsL9I5xB+epwaXAUsaugnRXePG5i2HhCRfOeU9f
+PKuv0F4yuHNre9j1w5X/5WzxnFhDSb1QLbFXlgFCltL5a3RypygC9B0r7VFQCaLI
+ODjX5h06PZq6oLLiLSyVlOSrcPXNJLuhMqNnHuOUacUtOIx4B2vpwP0CgYEAxHOU
+YlMwUhgmPKZ/R7lNYMAKKUAiuKEkFM0K1K0/GnizhYDnc2atxORk54ZD6aGYuf9y
+xBWmOZobNTmGw9bnQhePai/xHUoJgs8KWN5e8pBJXAouL12pqRC4sQCIVc9R3J+7
+djTPTco0oexkUsbe1rr4hCqoR9iUePxl7wY5BYECgYEA8nA6fV+HKoDINlEnR4yg
+Aza4PdjQG3Pa11AIoEAP/Hq3RwoMNqRpx1J2ZIZWHSeTGh9CCCY297Ig8XDlfntR
+P8qfRjEugovVOl00Qk7Rt378JRxzC0K4dhm4O73t85T4K9PyoI7ouyhT964ZHDro
+YqJxFq4ugjiF0MJ3BDI5ZrECgYAItf9MZNftq/h2FAPs0ECoG5vXvGpNuYd6DKWA
+TLZRnCyJrO+WZGUsJ9x5j7CPOYUmKjeSjksynqy6LXTWVj8m5RiM4tdULyZA0KFq
+02FubAt0s1bc7tBJGN63qohhFbJRkBul4C2ZC3BOBcdlDEBxURUX9zRDC016F+cF
+NEdvAQKBgQCtJhaEt1dzbB0qYEZz7kYr3QP1hz/6XfnAd8wmJNQQQ8yctNlJc+dH
+Foo3qwU2uJWFbByBxIvkmCDTAOCJwOONP/MhigvDQth/sa+TbY6y39XywL0TyG/p
+X19RWD9TYNG7C9y9Z8Jcz+u9iCd5enqLYw7Qle9gq3jwK1GXsV5zEQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=B CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:81:74:90:36:7b:35:df:f3:ea:18:ca:b9:64:
+ a8:58:85:90:2a:de:22:be:9a:0b:97:bd:fe:61:98:
+ 2a:e5:2d:cf:9e:bc:0c:23:5e:21:4f:19:ad:b5:3e:
+ 9c:d4:aa:05:5a:71:4e:b7:99:81:a7:6b:35:09:22:
+ 43:84:be:e9:71:c6:70:3b:6f:88:c4:2b:ba:0a:b8:
+ c1:7c:42:be:3f:d8:0b:88:87:53:82:7b:a3:72:d4:
+ 42:53:f3:7d:cb:84:50:60:34:1c:86:65:36:fb:e6:
+ 47:4f:61:60:c3:9b:7b:4a:57:6e:2c:ac:90:ee:47:
+ 0d:b2:92:e2:bb:e8:7c:e2:74:3e:72:8f:02:91:41:
+ e8:2e:60:1a:80:83:76:b8:87:fd:f3:5f:04:dd:56:
+ 97:f3:11:04:70:9e:48:e2:96:e5:66:a3:85:dd:b2:
+ f6:91:1e:e8:c3:a6:79:94:b7:e4:6e:25:38:27:a3:
+ 79:99:9c:53:ba:8b:c9:cf:d6:5e:e0:c0:c6:05:9a:
+ 60:46:73:25:56:a4:02:3c:dc:36:d5:60:12:0f:c9:
+ f9:4e:c0:1e:2b:74:60:ef:03:30:8a:53:7e:c8:0a:
+ eb:2b:9b:db:a3:63:03:85:b8:ba:ca:fd:93:fd:f3:
+ 8f:87:97:11:24:d5:11:f3:50:ac:ba:1f:9d:d7:e4:
+ 30:7d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 2C:58:4E:D7:58:62:F8:D6:BF:BE:80:5C:04:3A:39:B1:41:8C:D3:D7
+ X509v3 Authority Key Identifier:
+ keyid:CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 0d:09:9b:f0:11:81:5a:5d:f1:85:73:fb:f2:c8:bc:82:99:27:
+ fd:58:c7:6a:d6:28:f1:09:63:4e:61:fd:68:3a:23:0a:10:fe:
+ 58:7b:20:a3:7a:69:e6:74:55:5c:14:4f:ec:d7:e2:10:aa:8b:
+ a6:3f:99:c1:ef:bb:48:a9:42:b8:9a:8e:65:75:7b:cc:5c:bd:
+ e4:c0:63:e2:d1:6b:38:32:5f:33:b3:fc:0c:33:b0:8b:dd:10:
+ 54:15:99:2d:c4:62:68:3f:af:3f:e2:d9:52:33:d4:31:4c:ed:
+ c7:28:76:bb:21:6b:b9:41:fb:88:40:52:e1:52:76:31:af:d8:
+ a6:63:d1:d9:a5:e4:95:39:74:a8:73:f5:b0:c4:4e:bf:ef:a1:
+ 87:2e:94:a0:bd:cc:e3:90:45:5c:5a:18:15:0d:09:47:45:0f:
+ 1e:d7:cf:d3:8d:c6:b0:54:c4:5f:21:50:5c:b5:0d:eb:c3:15:
+ 3d:ee:88:fa:b4:80:93:4d:2e:00:9e:46:b6:fb:e8:64:32:51:
+ 8a:35:81:7a:d3:71:73:8d:2a:27:d4:57:2f:66:4c:fc:99:81:
+ 06:66:1f:5b:e6:a4:e5:35:eb:f3:e5:e3:f3:31:78:fe:b0:03:
+ f9:5d:77:97:70:d7:53:52:50:df:15:5b:3e:fc:7e:9a:1f:6a:
+ ce:c2:37:2f
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMGAxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAw
+DgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9gXSQNns13/PqGMq5ZKhYhZAq3iK+mguXvf5h
+mCrlLc+evAwjXiFPGa21PpzUqgVacU63mYGnazUJIkOEvulxxnA7b4jEK7oKuMF8
+Qr4/2AuIh1OCe6Ny1EJT833LhFBgNByGZTb75kdPYWDDm3tKV24srJDuRw2ykuK7
+6HzidD5yjwKRQeguYBqAg3a4h/3zXwTdVpfzEQRwnkjiluVmo4XdsvaRHujDpnmU
+t+RuJTgno3mZnFO6i8nP1l7gwMYFmmBGcyVWpAI83DbVYBIPyflOwB4rdGDvAzCK
+U37ICusrm9ujYwOFuLrK/ZP984+HlxEk1RHzUKy6H53X5DB9AgMBAAGjbzBtMAwG
+A1UdEwEB/wQCMAAwHQYDVR0OBBYEFCxYTtdYYvjWv76AXAQ6ObFBjNPXMB8GA1Ud
+IwQYMBaAFM0YJ0+IsSaFuY+cAKT4Gv0Bhl7sMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEADQmb8BGBWl3xhXP78si8gpkn
+/VjHatYo8QljTmH9aDojChD+WHsgo3pp5nRVXBRP7NfiEKqLpj+Zwe+7SKlCuJqO
+ZXV7zFy95MBj4tFrODJfM7P8DDOwi90QVBWZLcRiaD+vP+LZUjPUMUztxyh2uyFr
+uUH7iEBS4VJ2Ma/YpmPR2aXklTl0qHP1sMROv++hhy6UoL3M45BFXFoYFQ0JR0UP
+HtfP043GsFTEXyFQXLUN68MVPe6I+rSAk00uAJ5GtvvoZDJRijWBetNxc40qJ9RX
+L2ZM/JmBBmYfW+ak5TXr8+Xj8zF4/rAD+V13l3DXU1JQ3xVbPvx+mh9qzsI3Lw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4097 (0x1001)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=C CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=B CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ab:18:b1:0e:c2:f9:61:1a:13:c4:95:b8:65:d8:
+ cb:36:2f:72:e8:e1:97:44:1d:0d:55:a0:cc:02:12:
+ 04:a4:78:f6:df:c3:6f:c9:b8:50:15:d8:5c:21:ed:
+ 8f:c4:e7:db:81:da:fe:01:60:39:63:99:e4:04:dc:
+ 15:ea:59:6b:35:da:1a:e3:3e:dd:f0:71:8f:64:79:
+ 1a:30:b1:be:5a:43:9c:88:1a:a0:26:2f:5d:6c:b6:
+ 8b:b8:6d:10:b0:97:f4:a1:d3:7b:56:3d:dd:12:19:
+ a3:5e:02:24:9e:19:8e:d2:0b:e2:ea:8e:a2:cc:a1:
+ 8e:f0:49:e1:81:ae:3a:a6:71:d5:e8:e4:80:27:01:
+ 58:66:8c:bd:a0:4f:08:01:59:5c:c3:e3:d5:6f:49:
+ 73:24:66:f5:25:b3:9e:a1:29:21:de:e9:d5:ad:b0:
+ 1d:fc:b7:4c:f7:5a:9a:2b:5a:2c:af:07:aa:c2:82:
+ 5a:36:06:1d:27:2d:90:c7:45:1e:7b:f4:7a:8a:fe:
+ 90:c1:79:c9:8f:4e:67:52:48:ea:0b:dd:d7:fe:84:
+ 54:47:2f:d9:d0:ca:11:07:59:b0:90:08:0b:76:a2:
+ ec:30:a5:45:aa:d7:61:39:84:43:33:97:22:b6:45:
+ c8:e8:ab:73:5f:79:a8:13:55:2f:71:a2:c9:21:aa:
+ 9b:df
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 00:80:9b:99:4b:81:b1:76:49:5c:f2:99:ef:88:93:18:75:68:
+ eb:26:9e:87:95:b6:91:23:c0:b5:78:58:96:97:9c:27:38:fa:
+ f3:e5:c3:a2:34:40:91:6d:45:2c:7f:63:51:98:5b:53:4b:91:
+ ce:0f:e2:32:63:e7:e5:f9:21:6b:b7:f9:94:ac:33:13:5c:27:
+ 9b:98:e7:7f:44:50:29:98:68:81:53:6b:a1:43:d8:04:40:cb:
+ f2:cf:18:39:a5:24:c9:88:b6:b7:76:cb:dd:18:2e:1d:24:3e:
+ d3:59:e9:04:48:9d:59:0c:0f:d0:79:6a:93:7d:2b:b9:8e:50:
+ 34:00:0d:48:ae:10:bd:80:04:74:da:06:27:15:ad:88:ec:36:
+ 61:51:80:fe:6b:1d:46:37:0e:ea:23:60:c1:79:bf:03:2a:d2:
+ 80:9e:2c:10:3a:bc:2d:50:6e:f7:f9:d5:ec:11:d9:b4:62:bc:
+ 09:2c:05:31:06:bf:b9:e1:d5:1e:02:a9:1a:c5:c4:13:bf:b7:
+ 8e:6a:08:51:57:af:db:7b:09:74:bd:c7:bd:3c:de:0a:51:8a:
+ fe:82:0b:4b:34:74:10:4b:4b:34:fd:42:28:48:10:db:5d:6d:
+ 64:80:b1:3c:5c:04:86:32:6c:25:87:db:23:dc:e4:42:e4:71:
+ f9:b1:88:74
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgICEAEwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQyBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMA8xDTALBgNVBAMMBEIg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrGLEOwvlhGhPElbhl
+2Ms2L3Lo4ZdEHQ1VoMwCEgSkePbfw2/JuFAV2Fwh7Y/E59uB2v4BYDljmeQE3BXq
+WWs12hrjPt3wcY9keRowsb5aQ5yIGqAmL11stou4bRCwl/Sh03tWPd0SGaNeAiSe
+GY7SC+LqjqLMoY7wSeGBrjqmcdXo5IAnAVhmjL2gTwgBWVzD49VvSXMkZvUls56h
+KSHe6dWtsB38t0z3WporWiyvB6rCglo2Bh0nLZDHRR579HqK/pDBecmPTmdSSOoL
+3df+hFRHL9nQyhEHWbCQCAt2ouwwpUWq12E5hEMzlyK2Rcjoq3NfeagTVS9xoskh
+qpvfAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM0YJ0+IsSaF
+uY+cAKT4Gv0Bhl7sMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEA
+AICbmUuBsXZJXPKZ74iTGHVo6yaeh5W2kSPAtXhYlpecJzj68+XDojRAkW1FLH9j
+UZhbU0uRzg/iMmPn5fkha7f5lKwzE1wnm5jnf0RQKZhogVNroUPYBEDL8s8YOaUk
+yYi2t3bL3RguHSQ+01npBEidWQwP0Hlqk30ruY5QNAANSK4QvYAEdNoGJxWtiOw2
+YVGA/msdRjcO6iNgwXm/AyrSgJ4sEDq8LVBu9/nV7BHZtGK8CSwFMQa/ueHVHgKp
+GsXEE7+3jmoIUVev23sJdL3HvTzeClGK/oILSzR0EEtLNP1CKEgQ211tZICxPFwE
+hjJsJYfbI9zkQuRx+bGIdA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4100 (0x1004)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=E Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=C CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ca:21:76:fd:8b:0f:24:ba:dc:ab:cd:0e:31:94:
+ 01:90:57:79:18:3c:58:61:f5:7e:b7:2f:e6:46:d3:
+ 41:e8:4e:29:29:a8:9d:eb:d6:df:69:d5:b1:10:de:
+ 6b:17:2d:8d:8a:1c:d7:dc:80:85:74:c2:7a:6e:a6:
+ 75:7a:2a:76:42:fb:65:6b:8c:a9:2f:c0:5e:76:1e:
+ bc:35:85:b2:4b:35:a4:97:33:15:76:7d:4f:6e:d0:
+ 3b:45:04:fe:dc:a0:11:67:15:2d:3a:c3:07:c6:db:
+ f2:89:25:92:5e:db:70:bd:88:e9:f0:c7:54:6f:8e:
+ ab:cf:ce:ca:ad:bb:44:72:bf:e9:b5:ab:ba:68:15:
+ 6d:1e:e1:66:d6:60:d8:bd:dc:ab:d3:e8:2f:4c:ee:
+ 29:46:36:c7:b1:61:af:20:19:cc:98:1c:78:5f:4d:
+ 97:7a:de:2f:d9:fd:f0:b8:47:34:ff:ed:73:07:eb:
+ 90:54:11:e2:1b:8e:68:5a:c1:72:a9:af:df:e9:f1:
+ f5:ca:0e:72:03:90:1b:af:64:d6:ee:ce:67:57:1b:
+ fb:c7:f1:c2:5c:97:81:cd:d6:22:7c:26:bf:cd:6a:
+ b9:99:5f:58:63:5f:ce:05:1c:7d:f1:a9:d3:f8:4c:
+ fe:10:82:a4:14:2c:67:97:6c:82:2f:98:38:83:50:
+ bf:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ F5:87:AF:4C:B3:4A:88:7F:EB:92:D3:C7:28:78:91:D9:02:4A:71:DF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 8c:05:c2:e8:03:57:4c:17:bd:69:1c:2f:3a:3e:9a:c5:11:2d:
+ 94:c1:38:4d:f1:a8:aa:29:9c:40:d6:99:63:79:8a:96:60:be:
+ 5a:ba:54:8b:db:a6:f0:66:ae:f1:48:54:2d:a4:9f:ec:ba:e3:
+ 75:77:c0:54:1d:ce:b1:87:74:61:91:19:14:28:74:b0:ca:7a:
+ a0:fe:da:eb:26:be:86:cc:db:65:c4:c8:17:fb:9f:78:c3:df:
+ 39:f5:4f:77:5c:05:49:b5:fa:c5:9e:04:35:4f:ff:72:73:6d:
+ 8a:31:70:83:3b:19:c2:14:09:9a:2f:f2:ea:cb:13:b4:fa:56:
+ d2:a9:34:88:56:9c:f2:3e:22:46:56:ad:62:05:20:3b:ed:2e:
+ 6d:c5:02:de:da:53:8f:79:6c:42:29:c4:b6:5b:9a:31:d2:9b:
+ 07:3d:62:22:fa:c9:7c:f6:96:e4:a3:f2:5f:46:65:35:ba:af:
+ 34:9b:2d:30:eb:de:ac:6f:b3:af:e6:7f:10:80:8d:f6:eb:0a:
+ 03:74:75:8f:ae:e1:13:70:4a:4a:55:e1:f0:f3:69:ff:7e:fb:
+ 76:7b:5e:ae:ad:5e:59:6c:af:35:e9:08:f6:16:fe:3c:85:e3:
+ 91:97:0c:23:63:d5:07:40:23:23:eb:20:a8:e1:05:e3:ae:44:
+ 9f:38:f1:61
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgICEAQwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJRSBS
+b290IENBMB4XDTE0MDIwNDA0MjU1OFoXDTI0MDIwMjA0MjU1OFowDzENMAsGA1UE
+AwwEQyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMohdv2LDyS6
+3KvNDjGUAZBXeRg8WGH1frcv5kbTQehOKSmonevW32nVsRDeaxctjYoc19yAhXTC
+em6mdXoqdkL7ZWuMqS/AXnYevDWFsks1pJczFXZ9T27QO0UE/tygEWcVLTrDB8bb
+8oklkl7bcL2I6fDHVG+Oq8/Oyq27RHK/6bWrumgVbR7hZtZg2L3cq9PoL0zuKUY2
+x7FhryAZzJgceF9Nl3reL9n98LhHNP/tcwfrkFQR4huOaFrBcqmv3+nx9coOcgOQ
+G69k1u7OZ1cb+8fxwlyXgc3WInwmv81quZlfWGNfzgUcffGp0/hM/hCCpBQsZ5ds
+gi+YOINQv3ECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU9Yev
+TLNKiH/rktPHKHiR2QJKcd8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQCMBcLoA1dMF71pHC86PprFES2UwThN8aiqKZxA1pljeYqWYL5aulSL26bw
+Zq7xSFQtpJ/suuN1d8BUHc6xh3RhkRkUKHSwynqg/trrJr6GzNtlxMgX+594w985
+9U93XAVJtfrFngQ1T/9yc22KMXCDOxnCFAmaL/LqyxO0+lbSqTSIVpzyPiJGVq1i
+BSA77S5txQLe2lOPeWxCKcS2W5ox0psHPWIi+sl89pbko/JfRmU1uq80my0w696s
+b7Ov5n8QgI326woDdHWPruETcEpKVeHw82n/fvt2e16urV5ZbK816Qj2Fv48heOR
+lwwjY9UHQCMj6yCo4QXjrkSfOPFh
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 15337932515437853935 (0xd4db485be3963cef)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=E Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=E Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a4:0f:61:93:2e:cc:e7:2f:fa:30:d2:6a:ca:eb:
+ 21:bb:f8:26:55:f3:70:42:c0:48:ca:51:2b:11:20:
+ 32:16:86:38:8f:d3:fe:88:5a:a4:81:3b:77:1f:82:
+ e0:26:08:d1:8f:d6:03:98:2d:21:eb:d0:bb:f2:fc:
+ 25:83:98:95:e9:d2:cc:a6:4e:0d:fb:70:6a:0c:33:
+ ca:bb:a1:d4:27:30:69:f7:0c:86:e7:82:26:56:c4:
+ 95:aa:f5:1f:50:8c:95:bf:9a:2d:40:a8:d2:9d:2b:
+ 78:b0:87:a9:20:b1:73:e1:99:64:cd:22:81:72:76:
+ d0:fa:46:80:0a:45:50:c5:c1:92:8b:f4:1d:e4:73:
+ 74:90:53:7f:6c:43:6a:9b:e0:8d:87:96:85:8c:86:
+ 72:21:8c:17:78:ab:b7:fd:1a:ad:9f:37:03:53:4f:
+ 94:66:5d:99:fa:cd:d8:c9:0b:0b:dc:83:5f:c8:40:
+ 9d:50:e8:aa:de:a1:35:8a:ea:e0:26:10:cc:c0:9d:
+ 0c:aa:23:df:68:b3:7e:e3:55:e4:91:b2:f5:97:15:
+ 22:6a:71:5e:83:19:41:cb:99:8e:84:33:cd:15:7f:
+ a1:90:42:5b:ab:f6:40:b8:67:97:d7:65:0f:d3:a5:
+ 0a:96:cd:9c:aa:fc:57:87:6b:54:ed:4f:b1:4c:01:
+ 81:cd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ B1:D9:42:46:8A:51:56:85:13:A1:BF:84:84:20:40:7A:ED:54:DC:9B
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 44:46:db:03:28:bd:b3:32:9d:48:cb:5f:dc:2d:3f:5e:1a:52:
+ a8:61:ef:fb:97:7a:0e:a9:a9:83:5c:1c:7e:b9:e4:7a:12:57:
+ d0:5b:8f:c1:34:b8:5a:7f:ee:d0:c5:c9:b5:a3:68:0d:ce:68:
+ 78:08:44:f7:0f:50:bc:c8:87:15:f6:79:5a:1e:21:cc:6d:c9:
+ 31:70:ad:58:fc:9d:71:d1:2f:25:eb:e1:b6:b8:50:d0:90:28:
+ f2:48:c7:9f:f2:a4:63:ba:13:03:05:60:41:93:4e:ab:02:0f:
+ 27:ae:e9:51:f2:45:ba:47:4c:f6:17:82:24:39:a7:5d:c3:eb:
+ 2d:47:fb:14:06:f8:10:67:e4:98:aa:57:68:9e:f3:5a:b1:82:
+ 24:e6:dc:0f:cc:53:83:a5:a3:53:9f:d0:31:76:21:50:d0:6c:
+ af:d5:19:4e:41:b9:d1:b1:eb:bf:02:34:c5:ab:b8:d6:5e:33:
+ 13:26:e6:82:42:01:94:77:3e:94:35:5e:d5:73:16:7d:fb:a6:
+ 2d:a7:b8:8a:b4:49:56:d5:7b:5f:ec:d0:fe:ca:7a:78:56:ea:
+ 1c:a7:61:94:30:a8:92:32:50:40:47:8f:0d:be:f9:96:e4:0a:
+ 6f:29:7b:41:7d:87:2a:be:b8:42:17:cc:4a:42:ec:c3:1b:2e:
+ b6:7f:17:54
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIJANTbSFvjljzvMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
+BAMMCUUgUm9vdCBDQTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMBQx
+EjAQBgNVBAMMCUUgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKQPYZMuzOcv+jDSasrrIbv4JlXzcELASMpRKxEgMhaGOI/T/ohapIE7dx+C
+4CYI0Y/WA5gtIevQu/L8JYOYlenSzKZODftwagwzyruh1CcwafcMhueCJlbElar1
+H1CMlb+aLUCo0p0reLCHqSCxc+GZZM0igXJ20PpGgApFUMXBkov0HeRzdJBTf2xD
+apvgjYeWhYyGciGMF3irt/0arZ83A1NPlGZdmfrN2MkLC9yDX8hAnVDoqt6hNYrq
+4CYQzMCdDKoj32izfuNV5JGy9ZcVImpxXoMZQcuZjoQzzRV/oZBCW6v2QLhnl9dl
+D9OlCpbNnKr8V4drVO1PsUwBgc0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUsdlCRopRVoUTob+EhCBAeu1U3JswDgYDVR0PAQH/BAQDAgEGMA0G
+CSqGSIb3DQEBBQUAA4IBAQBERtsDKL2zMp1Iy1/cLT9eGlKoYe/7l3oOqamDXBx+
+ueR6ElfQW4/BNLhaf+7Qxcm1o2gNzmh4CET3D1C8yIcV9nlaHiHMbckxcK1Y/J1x
+0S8l6+G2uFDQkCjySMef8qRjuhMDBWBBk06rAg8nrulR8kW6R0z2F4IkOaddw+st
+R/sUBvgQZ+SYqldonvNasYIk5twPzFODpaNTn9AxdiFQ0Gyv1RlOQbnRseu/AjTF
+q7jWXjMTJuaCQgGUdz6UNV7VcxZ9+6Ytp7iKtElW1Xtf7ND+ynp4Vuocp2GUMKiS
+MlBAR48NvvmW5ApvKXtBfYcqvrhCF8xKQuzDGy62fxdU
+-----END CERTIFICATE-----
diff --git a/chromium/net/data/ssl/scripts/generate-aia-certs.sh b/chromium/net/data/ssl/scripts/generate-aia-certs.sh
index 98b858e139a..76b86cf5418 100755
--- a/chromium/net/data/ssl/scripts/generate-aia-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-aia-certs.sh
@@ -9,15 +9,15 @@
try() {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
# Create the serial number files.
-try echo 1 > out/aia-test-root-serial
-try echo 1 > out/aia-test-intermediate-serial
+try /bin/sh -c "echo 01 > out/aia-test-root-serial"
+try /bin/sh -c "echo 01 > out/aia-test-intermediate-serial"
# Create the signers' DB files.
touch out/aia-test-root-index.txt
@@ -47,7 +47,8 @@ CA_COMMON_NAME="AIA Test Root CA" \
-out out/aia-test-root.pem \
-signkey out/aia-test-root.key \
-extfile aia-test.cnf \
- -extensions ca_cert
+ -extensions ca_cert \
+ -text
# Generate the intermediate
CA_COMMON_NAME="AIA Test Intermediate CA" \
@@ -89,3 +90,11 @@ CA_COMMON_NAME="AIA Test Intermediate CA" \
-out out/aia-test-cert.pem \
-config aia-test.cnf \
-extensions user_cert
+
+# Copy to the file names that are actually checked in.
+try cp out/aia-test-cert.pem ../certificates/aia-cert.pem
+try openssl x509 \
+ -outform der \
+ -in out/aia-test-intermediate.pem \
+ -out ../certificates/aia-intermediate.der
+try cp out/aia-test-root.pem ../certificates/aia-root.pem
diff --git a/chromium/net/data/ssl/scripts/generate-bad-eku-certs.sh b/chromium/net/data/ssl/scripts/generate-bad-eku-certs.sh
index 11e41d4da5a..5335b843fae 100755
--- a/chromium/net/data/ssl/scripts/generate-bad-eku-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-bad-eku-certs.sh
@@ -11,22 +11,22 @@
try () {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
-eku_test_root="eku-test-root"
+eku_test_root="2048-rsa-root"
# Create the serial number files.
-try echo 1 > out/$eku_test_root-serial
+try /bin/sh -c "echo 01 > \"out/$eku_test_root-serial\""
# Make sure the signers' DB files exist.
-touch out/$eku_test_root-index.txt
+touch "out/$eku_test_root-index.txt"
# Generate one root CA certificate.
-try openssl genrsa -out out/$eku_test_root.key 2048
+try openssl genrsa -out "out/$eku_test_root.key" 2048
CA_COMMON_NAME="2048 RSA Test Root CA" \
CA_DIR=out \
@@ -36,9 +36,9 @@ CA_COMMON_NAME="2048 RSA Test Root CA" \
CERT_TYPE=root \
try openssl req \
-new \
- -key out/$eku_test_root.key \
+ -key "out/$eku_test_root.key" \
-extensions ca_cert \
- -out out/$eku_test_root.csr \
+ -out "out/$eku_test_root.csr" \
-config ca.cnf
CA_COMMON_NAME="2048 RSA Test Root CA" \
@@ -46,20 +46,22 @@ CA_COMMON_NAME="2048 RSA Test Root CA" \
CA_NAME=req_env_dn \
try openssl x509 \
-req -days 3650 \
- -in out/$eku_test_root.csr \
+ -in "out/$eku_test_root.csr" \
-extensions ca_cert \
- -signkey out/$eku_test_root.key \
- -out out/$eku_test_root.pem
+ -extfile ca.cnf \
+ -signkey "out/$eku_test_root.key" \
+ -out "out/$eku_test_root.pem" \
+ -text
# Generate EE certs.
for cert_type in non-crit-codeSigning crit-codeSigning
do
- try openssl genrsa -out out/$cert_type.key 2048
+ try openssl genrsa -out "out/$cert_type.key" 2048
try openssl req \
-new \
- -key out/$cert_type.key \
- -out out/$cert_type.csr \
+ -key "out/$cert_type.key" \
+ -out "out/$cert_type.csr" \
-config eku-test.cnf \
-reqexts "$cert_type"
@@ -71,7 +73,14 @@ do
CERT_TYPE=root \
try openssl ca \
-batch \
- -in out/$cert_type.csr \
- -out out/$cert_type.pem \
+ -in "out/$cert_type.csr" \
+ -out "out/$cert_type.pem" \
-config ca.cnf
done
+
+# Copy to the file names that are actually checked in.
+try cp "out/$eku_test_root.pem" ../certificates/eku-test-root.pem
+try /bin/sh -c "cat out/crit-codeSigning.key out/crit-codeSigning.pem \
+ > ../certificates/crit-codeSigning-chain.pem"
+try /bin/sh -c "cat out/non-crit-codeSigning.key out/non-crit-codeSigning.pem \
+ > ../certificates/non-crit-codeSigning-chain.pem"
diff --git a/chromium/net/data/ssl/scripts/generate-client-certificates.sh b/chromium/net/data/ssl/scripts/generate-client-certificates.sh
index 33782993036..7dce623a157 100755
--- a/chromium/net/data/ssl/scripts/generate-client-certificates.sh
+++ b/chromium/net/data/ssl/scripts/generate-client-certificates.sh
@@ -20,17 +20,17 @@
try () {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
echo Create the serial number files and indices.
-serial = 100
+serial=1000
for i in B C E
do
- try echo $serial > out/$i-serial
+ try /bin/sh -c "echo $serial > out/$i-serial"
serial=$(expr $serial + 1)
touch out/$i-index.txt
touch out/$i-index.txt.attr
@@ -60,6 +60,7 @@ COMMON_NAME="C Root CA" \
-req -days 3650 \
-in out/C.csr \
-extensions ca_cert \
+ -extfile client-certs.cnf \
-signkey out/C.key \
-out out/C.pem
@@ -138,8 +139,8 @@ COMMON_NAME="E CA" \
echo Package the client certs and private keys into PKCS12 files
# This is done for easily importing all of the certs needed for clients.
-cat out/A.pem out/A.key out/B.pem out/C.pem > out/A-chain.pem
-cat out/D.pem out/D.key out/E.pem out/C.pem > out/D-chain.pem
+try /bin/sh -c "cat out/A.pem out/A.key out/B.pem out/C.pem > out/A-chain.pem"
+try /bin/sh -c "cat out/D.pem out/D.key out/E.pem out/C.pem > out/D-chain.pem"
try openssl pkcs12 \
-in out/A-chain.pem \
@@ -154,10 +155,10 @@ try openssl pkcs12 \
-passout pass:chrome
echo Package the client certs for unit tests
-cp out/A.pem client_1.pem
-cp out/A.key client_1.key
-cp out/B.pem client_1_ca.pem
+try cp out/A.pem ../certificates/client_1.pem
+try cp out/A.key ../certificates/client_1.key
+try cp out/B.pem ../certificates/client_1_ca.pem
-cp out/D.pem client_2.pem
-cp out/D.key client_2.key
-cp out/E.pem client_2_ca.pem
+try cp out/D.pem ../certificates/client_2.pem
+try cp out/D.key ../certificates/client_2.key
+try cp out/E.pem ../certificates/client_2_ca.pem
diff --git a/chromium/net/data/ssl/scripts/generate-cross-signed-certs.sh b/chromium/net/data/ssl/scripts/generate-cross-signed-certs.sh
index a40ca74a082..c9f94d02230 100755
--- a/chromium/net/data/ssl/scripts/generate-cross-signed-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-cross-signed-certs.sh
@@ -20,14 +20,14 @@
try() {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
-try echo 1 > out/2048-sha1-root-serial
-try echo 2 > out/2048-md5-root-serial
+try /bin/sh -c "echo 01 > out/2048-sha1-root-serial"
+try /bin/sh -c "echo 02 > out/2048-md5-root-serial"
touch out/2048-sha1-root-index.txt
touch out/2048-md5-root-index.txt
diff --git a/chromium/net/data/ssl/scripts/generate-duplicate-cn-certs.sh b/chromium/net/data/ssl/scripts/generate-duplicate-cn-certs.sh
index 8e48454342f..fa2c0df24fe 100755
--- a/chromium/net/data/ssl/scripts/generate-duplicate-cn-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-duplicate-cn-certs.sh
@@ -16,24 +16,14 @@
try () {
echo "$@"
- $@ || exit 1
-}
-
-generate_key_command () {
- case "$1" in
- rsa)
- echo genrsa
- ;;
- *)
- exit 1
- esac
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
echo Create the serial number and index files.
-try echo 1 > out/B-serial
+try /bin/sh -c "echo 01 > out/B-serial"
try touch out/B-index.txt
echo Generate the keys.
@@ -42,12 +32,7 @@ try openssl genrsa -out out/B.key 2048
echo Generate the B CSR.
CA_COMMON_NAME="B Root CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=B CERTIFICATE=B \
+ CERTIFICATE=B \
try openssl req \
-new \
-key out/B.key \
@@ -56,8 +41,6 @@ CA_COMMON_NAME="B Root CA" \
echo B signs itself.
CA_COMMON_NAME="B Root CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
try openssl x509 \
-req -days 3650 \
-in out/B.csr \
@@ -85,12 +68,7 @@ SUBJECT_NAME=req_duplicate_cn_2 \
echo B signs A1.
CA_COMMON_NAME="B CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=sha1 \
- CERT_TYPE=intermediate \
- TYPE=B CERTIFICATE=B \
+ CERTIFICATE=B \
try openssl ca \
-batch \
-extensions user_cert \
@@ -100,12 +78,7 @@ CA_COMMON_NAME="B CA" \
echo B signs A2.
CA_COMMON_NAME="B CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=sha1 \
- CERT_TYPE=intermediate \
- TYPE=B CERTIFICATE=B \
+ CERTIFICATE=B \
try openssl ca \
-batch \
-extensions user_cert \
@@ -128,5 +101,5 @@ try openssl pkcs12 \
-out ../certificates/duplicate_cn_2.p12 \
-passout pass:chrome
-cp out/A1.pem ../certificates/duplicate_cn_1.pem
-cp out/A2.pem ../certificates/duplicate_cn_2.pem
+try cp out/A1.pem ../certificates/duplicate_cn_1.pem
+try cp out/A2.pem ../certificates/duplicate_cn_2.pem
diff --git a/chromium/net/data/ssl/scripts/generate-multi-root-test-chains.sh b/chromium/net/data/ssl/scripts/generate-multi-root-test-chains.sh
new file mode 100755
index 00000000000..6f88325a3cc
--- /dev/null
+++ b/chromium/net/data/ssl/scripts/generate-multi-root-test-chains.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+# Copyright 2014 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.
+
+# This script generates two chains of test certificates:
+#
+# 1. A (end-entity) -> B -> C -> D (self-signed root)
+# 2. A (end-entity) -> B -> C2 -> E (self-signed root)
+#
+# C and C2 have the same subject and keypair.
+#
+# We use these cert chains in CertVerifyProcChromeOSTest
+# to ensure that multiple verification paths are properly handled.
+
+try () {
+ echo "$@"
+ "$@" || exit 1
+}
+
+try rm -rf out
+try mkdir out
+
+echo Create the serial number files.
+serial=1000
+for i in B C C2 D E
+do
+ try /bin/sh -c "echo $serial > out/$i-serial"
+ serial=$(expr $serial + 1)
+done
+
+echo Generate the keys.
+try openssl genrsa -out out/A.key 2048
+try openssl genrsa -out out/B.key 2048
+try openssl genrsa -out out/C.key 2048
+try openssl genrsa -out out/D.key 2048
+try openssl genrsa -out out/E.key 2048
+
+echo Generate the D CSR.
+CA_COMMON_NAME="D Root CA" \
+ CERTIFICATE=D \
+ try openssl req \
+ -new \
+ -key out/D.key \
+ -out out/D.csr \
+ -config redundant-ca.cnf
+
+echo D signs itself.
+CA_COMMON_NAME="D Root CA" \
+ try openssl x509 \
+ -req -days 3650 \
+ -in out/D.csr \
+ -extensions ca_cert \
+ -extfile redundant-ca.cnf \
+ -signkey out/D.key \
+ -out out/D.pem \
+ -text
+
+echo Generate the E CSR.
+CA_COMMON_NAME="E Root CA" \
+ CERTIFICATE=E \
+ try openssl req \
+ -new \
+ -key out/E.key \
+ -out out/E.csr \
+ -config redundant-ca.cnf
+
+echo E signs itself.
+CA_COMMON_NAME="E Root CA" \
+ try openssl x509 \
+ -req -days 3650 \
+ -in out/E.csr \
+ -extensions ca_cert \
+ -extfile redundant-ca.cnf \
+ -signkey out/E.key \
+ -out out/E.pem \
+ -text
+
+echo Generate the C2 intermediary CSR.
+CA_COMMON_NAME="C CA" \
+ CERTIFICATE=C2 \
+ try openssl req \
+ -new \
+ -key out/C.key \
+ -out out/C2.csr \
+ -config redundant-ca.cnf
+
+echo Generate the B and C intermediaries\' CSRs.
+for i in B C
+do
+ CA_COMMON_NAME="$i CA" \
+ CERTIFICATE="$i" \
+ try openssl req \
+ -new \
+ -key "out/$i.key" \
+ -out "out/$i.csr" \
+ -config redundant-ca.cnf
+done
+
+echo D signs the C intermediate.
+# Make sure the signer's DB file exists.
+touch out/D-index.txt
+CA_COMMON_NAME="D Root CA" \
+ CERTIFICATE=D \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/C.csr \
+ -out out/C.pem \
+ -config redundant-ca.cnf
+
+echo E signs the C2 intermediate.
+# Make sure the signer's DB file exists.
+touch out/E-index.txt
+CA_COMMON_NAME="E Root CA" \
+ CERTIFICATE=E \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/C2.csr \
+ -out out/C2.pem \
+ -config redundant-ca.cnf
+
+echo C signs the B intermediate.
+touch out/C-index.txt
+CA_COMMON_NAME="C CA" \
+ CERTIFICATE=C \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/B.csr \
+ -out out/B.pem \
+ -config redundant-ca.cnf
+
+echo Generate the A end-entity CSR.
+try openssl req \
+ -new \
+ -key out/A.key \
+ -out out/A.csr \
+ -config ee.cnf
+
+echo B signs A.
+touch out/B-index.txt
+CA_COMMON_NAME="B CA" \
+ CERTIFICATE=B \
+ try openssl ca \
+ -batch \
+ -extensions user_cert \
+ -in out/A.csr \
+ -out out/A.pem \
+ -config redundant-ca.cnf
+
+echo Create multi-root-chain1.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C.pem out/D.pem \
+ > ../certificates/multi-root-chain1.pem"
+
+echo Create multi-root-chain2.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C2.pem out/E.pem \
+ > ../certificates/multi-root-chain2.pem"
+
diff --git a/chromium/net/data/ssl/scripts/generate-policy-certs.sh b/chromium/net/data/ssl/scripts/generate-policy-certs.sh
index 4a6b35dc463..5507d5b1399 100755
--- a/chromium/net/data/ssl/scripts/generate-policy-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-policy-certs.sh
@@ -12,15 +12,15 @@
try() {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
# Create the serial number files.
-try echo 1 > out/policy-root-serial
-try echo 1 > out/policy-intermediate-serial
+try /bin/sh -c "echo 01 > out/policy-root-serial"
+try /bin/sh -c "echo 01 > out/policy-intermediate-serial"
# Create the signers' DB files.
touch out/policy-root-index.txt
@@ -50,7 +50,8 @@ COMMON_NAME="Policy Test Root CA" \
-out out/policy-root.pem \
-signkey out/policy-root.key \
-extfile policy.cnf \
- -extensions ca_cert
+ -extensions ca_cert \
+ -text
# Generate the intermediate
COMMON_NAME="Policy Test Intermediate CA" \
@@ -91,6 +92,6 @@ COMMON_NAME="Policy Test Intermediate CA" \
-config policy.cnf \
-extensions user_cert
-cat out/policy-cert.pem \
+try /bin/sh -c "cat out/policy-cert.pem \
out/policy-intermediate.pem \
- out/policy-root.pem >../certificates/explicit-policy-chain.pem
+ out/policy-root.pem >../certificates/explicit-policy-chain.pem"
diff --git a/chromium/net/data/ssl/scripts/generate-redundant-test-chains.sh b/chromium/net/data/ssl/scripts/generate-redundant-test-chains.sh
index 58768e84963..d7fd17bdc86 100755
--- a/chromium/net/data/ssl/scripts/generate-redundant-test-chains.sh
+++ b/chromium/net/data/ssl/scripts/generate-redundant-test-chains.sh
@@ -21,27 +21,17 @@
try () {
echo "$@"
- $@ || exit 1
-}
-
-generate_key_command () {
- case "$1" in
- rsa)
- echo genrsa
- ;;
- *)
- exit 1
- esac
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
echo Create the serial number files.
-serial=100
+serial=1000
for i in B C C2 D
do
- try echo $serial > out/$i-serial
+ try /bin/sh -c "echo $serial > out/$i-serial"
serial=$(expr $serial + 1)
done
@@ -53,12 +43,7 @@ try openssl genrsa -out out/D.key 2048
echo Generate the D CSR.
CA_COMMON_NAME="D Root CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=D CERTIFICATE=D \
+ CERTIFICATE=D \
try openssl req \
-new \
-key out/D.key \
@@ -67,23 +52,18 @@ CA_COMMON_NAME="D Root CA" \
echo D signs itself.
CA_COMMON_NAME="D Root CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
try openssl x509 \
-req -days 3650 \
-in out/D.csr \
-extensions ca_cert \
+ -extfile redundant-ca.cnf \
-signkey out/D.key \
- -out out/D.pem
+ -out out/D.pem \
+ -text
echo Generate the C2 root CSR.
CA_COMMON_NAME="C CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=C2 CERTIFICATE=C2 \
+ CERTIFICATE=C2 \
try openssl req \
-new \
-key out/C.key \
@@ -92,26 +72,21 @@ CA_COMMON_NAME="C CA" \
echo C2 signs itself.
CA_COMMON_NAME="C CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
try openssl x509 \
-req -days 3650 \
-in out/C2.csr \
-extensions ca_cert \
+ -extfile redundant-ca.cnf \
-signkey out/C.key \
- -out out/C2.pem
+ -out out/C2.pem \
+ -text
echo Generate the B and C intermediaries\' CSRs.
for i in B C
do
name="$i Intermediate CA"
CA_COMMON_NAME="$i CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=$i CERTIFICATE=$i \
+ CERTIFICATE=$i \
try openssl req \
-new \
-key out/$i.key \
@@ -123,12 +98,7 @@ echo D signs the C intermediate.
# Make sure the signer's DB file exists.
touch out/D-index.txt
CA_COMMON_NAME="D Root CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=D CERTIFICATE=D \
+ CERTIFICATE=D \
try openssl ca \
-batch \
-extensions ca_cert \
@@ -139,12 +109,7 @@ CA_COMMON_NAME="D Root CA" \
echo C signs the B intermediate.
touch out/C-index.txt
CA_COMMON_NAME="C CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=2048 \
- ALGO=rsa \
- CERT_TYPE=root \
- TYPE=C CERTIFICATE=C \
+ CERTIFICATE=C \
try openssl ca \
-batch \
-extensions ca_cert \
@@ -162,12 +127,7 @@ try openssl req \
echo B signs A.
touch out/B-index.txt
CA_COMMON_NAME="B CA" \
- CA_DIR=out \
- CA_NAME=req_env_dn \
- KEY_SIZE=$signer_key_size \
- ALGO=$signer_algo \
- CERT_TYPE=intermediate \
- TYPE=B CERTIFICATE=B \
+ CERTIFICATE=B \
try openssl ca \
-batch \
-extensions user_cert \
@@ -176,12 +136,13 @@ CA_COMMON_NAME="B CA" \
-config redundant-ca.cnf
echo Create redundant-server-chain.pem
-cat out/A.key out/A.pem out/B.pem out/C.pem out/D.pem \
- > redundant-server-chain.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C.pem out/D.pem \
+ > ../certificates/redundant-server-chain.pem"
echo Create redundant-validated-chain.pem
-cat out/A.key out/A.pem out/B.pem out/C2.pem > redundant-validated-chain.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C2.pem \
+ > ../certificates/redundant-validated-chain.pem"
echo Create redundant-validated-chain-root.pem
-cp out/C2.pem redundant-validated-chain-root.pem
+try cp out/C2.pem ../certificates/redundant-validated-chain-root.pem
diff --git a/chromium/net/data/ssl/scripts/generate-test-certs.sh b/chromium/net/data/ssl/scripts/generate-test-certs.sh
index 9cff81072c6..6323de312fd 100755
--- a/chromium/net/data/ssl/scripts/generate-test-certs.sh
+++ b/chromium/net/data/ssl/scripts/generate-test-certs.sh
@@ -9,13 +9,13 @@
try() {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
try rm -rf out
try mkdir out
-try echo 1 > out/2048-sha1-root-serial
+try /bin/sh -c "echo 01 > out/2048-sha1-root-serial"
touch out/2048-sha1-root-index.txt
# Generate the key
@@ -34,10 +34,10 @@ CA_COMMON_NAME="Test Root CA" \
-req -days 3650 \
-in out/2048-sha1-root.req \
-out out/2048-sha1-root.pem \
- -text \
-signkey out/2048-sha1-root.key \
-extfile ca.cnf \
- -extensions ca_cert
+ -extensions ca_cert \
+ -text
# Generate the leaf certificate requests
try openssl req \
@@ -72,10 +72,10 @@ CA_COMMON_NAME="Test Root CA" \
-out out/ok_cert.pem \
-config ca.cnf
-cat out/ok_cert.key out/ok_cert.pem \
- > ../certificates/ok_cert.pem
-cat out/expired_cert.key out/expired_cert.pem \
- > ../certificates/expired_cert.pem
-cat out/2048-sha1-root.key out/2048-sha1-root.pem \
- > ../certificates/root_ca_cert.pem
+try /bin/sh -c "cat out/ok_cert.key out/ok_cert.pem \
+ > ../certificates/ok_cert.pem"
+try /bin/sh -c "cat out/expired_cert.key out/expired_cert.pem \
+ > ../certificates/expired_cert.pem"
+try /bin/sh -c "cat out/2048-sha1-root.key out/2048-sha1-root.pem \
+ > ../certificates/root_ca_cert.pem"
diff --git a/chromium/net/data/ssl/scripts/generate-weak-test-chains.sh b/chromium/net/data/ssl/scripts/generate-weak-test-chains.sh
index 845c8ab60e3..3bf239c5aaa 100755
--- a/chromium/net/data/ssl/scripts/generate-weak-test-chains.sh
+++ b/chromium/net/data/ssl/scripts/generate-weak-test-chains.sh
@@ -11,7 +11,7 @@ key_types="768-rsa 1024-rsa 2048-rsa prime256v1-ecdsa"
try () {
echo "$@"
- $@ || exit 1
+ "$@" || exit 1
}
generate_key_command () {
@@ -34,10 +34,10 @@ try rm -rf out
try mkdir out
# Create the serial number files.
-try echo 1 > out/2048-rsa-root-serial
+try /bin/sh -c "echo 01 > out/2048-rsa-root-serial"
for key_type in $key_types
do
- try echo 1 > out/$key_type-intermediate-serial
+ try /bin/sh -c "echo 01 > out/$key_type-intermediate-serial"
done
# Generate one root CA certificate.
@@ -63,8 +63,10 @@ CA_COMMON_NAME="2048 RSA Test Root CA" \
-req -days 3650 \
-in out/2048-rsa-root.csr \
-extensions ca_cert \
+ -extfile ca.cnf \
-signkey out/2048-rsa-root.key \
- -out out/2048-rsa-root.pem
+ -out out/2048-rsa-root.pem \
+ -text
# Generate private keys of all types and strengths for intermediate CAs and
# end-entities.
@@ -166,3 +168,5 @@ do
done
done
+# Copy final outputs.
+try cp out/*root*pem out/*intermediate*pem ../certificates
diff --git a/chromium/net/data/ssl/scripts/redundant-ca.cnf b/chromium/net/data/ssl/scripts/redundant-ca.cnf
index e1b24e0cc40..b03eb817a3b 100644
--- a/chromium/net/data/ssl/scripts/redundant-ca.cnf
+++ b/chromium/net/data/ssl/scripts/redundant-ca.cnf
@@ -1,21 +1,18 @@
+CA_DIR = out
+
[ca]
default_ca = CA_root
preserve = yes
# The default test root, used to generate certificates and CRLs.
[CA_root]
-dir = $ENV::CA_DIR
-key_size = $ENV::KEY_SIZE
-algo = $ENV::ALGO
-cert_type = $ENV::CERT_TYPE
-type = $ENV::TYPE
-certificate = $ENV::CERTIFICATE
-database = $dir/$type-index.txt
-new_certs_dir = $dir
-serial = $dir/$type-serial
-certificate = $dir/$certificate.pem
-private_key = $dir/$type.key
-RANDFILE = $dir/rand
+dir = ${ENV::CA_DIR}
+database = ${dir}/${ENV::CERTIFICATE}-index.txt
+new_certs_dir = ${dir}
+serial = ${dir}/${ENV::CERTIFICATE}-serial
+certificate = ${dir}/${ENV::CERTIFICATE}.pem
+private_key = ${dir}/${ENV::CERTIFICATE}.key
+RANDFILE = ${dir}/rand
default_days = 3650
default_crl_days = 30
default_md = sha1
@@ -51,30 +48,13 @@ commonName = optional
emailAddress = optional
[req]
-# The request section used to generate the root CA certificate. This should
-# not be used to generate end-entity certificates. For certificates other
-# than the root CA, see README to find the appropriate configuration file
-# (ie: openssl_cert.cnf).
-default_bits = $ENV::KEY_SIZE
+# The request section used to generate certificate requests.
+default_bits = 2048
default_md = sha1
string_mask = utf8only
prompt = no
encrypt_key = no
-distinguished_name = $ENV::CA_NAME
-
-[req_ca_dn]
-C = US
-ST = California
-L = Mountain View
-O = Test CA
-CN = Test Root 2 CA
-
-[req_intermediate_dn]
-C = US
-ST = California
-L = Mountain View
-O = Test CA
-CN = Test Intermediate 2 CA
+distinguished_name = req_env_dn
[req_env_dn]
-CN = $ENV::CA_COMMON_NAME
+CN = ${ENV::CA_COMMON_NAME}
diff --git a/chromium/net/data/url_request_unittest/308-without-location-header b/chromium/net/data/url_request_unittest/308-without-location-header
new file mode 100644
index 00000000000..b6d7cb2762d
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/308-without-location-header
@@ -0,0 +1 @@
+This is not a redirect. \ No newline at end of file
diff --git a/chromium/net/data/url_request_unittest/308-without-location-header.mock-http-headers b/chromium/net/data/url_request_unittest/308-without-location-header.mock-http-headers
new file mode 100644
index 00000000000..c04ed8afc08
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/308-without-location-header.mock-http-headers
@@ -0,0 +1 @@
+HTTP/1.0 308 Resume Incomplete
diff --git a/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable b/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable
new file mode 100644
index 00000000000..78981922613
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable
@@ -0,0 +1 @@
+a
diff --git a/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers b/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers
new file mode 100644
index 00000000000..fce22716064
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers
@@ -0,0 +1,4 @@
+HTTP/1.1 302 Yo
+Location: /echo
+Content-Length: 1
+Cache-control: max-age=60000
diff --git a/chromium/net/data/url_request_unittest/redirect308-to-echo b/chromium/net/data/url_request_unittest/redirect308-to-echo
new file mode 100644
index 00000000000..78981922613
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/redirect308-to-echo
@@ -0,0 +1 @@
+a
diff --git a/chromium/net/data/url_request_unittest/redirect308-to-echo.mock-http-headers b/chromium/net/data/url_request_unittest/redirect308-to-echo.mock-http-headers
new file mode 100644
index 00000000000..17a1a2244ed
--- /dev/null
+++ b/chromium/net/data/url_request_unittest/redirect308-to-echo.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 308 Yo
+Location: /echo
diff --git a/chromium/net/data/websocket/close-with-split-packet_wsh.py b/chromium/net/data/websocket/close-with-split-packet_wsh.py
index d5185c4114b..3bd5b58b25a 100644
--- a/chromium/net/data/websocket/close-with-split-packet_wsh.py
+++ b/chromium/net/data/websocket/close-with-split-packet_wsh.py
@@ -13,17 +13,12 @@ def web_socket_do_extra_handshake(_request):
def web_socket_transfer_data(request):
- line = request.ws_stream.receive_message()
- if line is None:
- return
- if isinstance(line, unicode):
- request.ws_stream.send_message(line, binary=False)
- else:
- request.ws_stream.send_message(line, binary=True)
+ # Just waiting...
+ request.ws_stream.receive_message()
def web_socket_passive_closing_handshake(request):
- code = struct.pack('!H', 1000)
+ code = struct.pack('!H', 3004)
packet = stream.create_close_frame(code + 'split test'.encode('utf-8'))
request.connection.write(packet[:1])
request.connection.write(packet[1:])
diff --git a/chromium/net/data/websocket/split_packet_check.html b/chromium/net/data/websocket/split_packet_check.html
index 8e273ec2849..a7f4c365f56 100644
--- a/chromium/net/data/websocket/split_packet_check.html
+++ b/chromium/net/data/websocket/split_packet_check.html
@@ -26,7 +26,7 @@ ws.onopen = function()
ws.onclose = function(event)
{
// Check wasClean, then set proper title.
- if (event.wasClean)
+ if (event.wasClean && event.code === 3004 && event.reason === 'split test')
document.title = 'PASS';
else
document.title = 'FAIL';
diff --git a/chromium/net/disk_cache/backend_unittest.cc b/chromium/net/disk_cache/backend_unittest.cc
index f3c02fdf09f..f9df5bdd22e 100644
--- a/chromium/net/disk_cache/backend_unittest.cc
+++ b/chromium/net/disk_cache/backend_unittest.cc
@@ -15,26 +15,29 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/disk_cache_test_base.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/histogram_macros.h"
-#include "net/disk_cache/mapped_file.h"
-#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/simple/simple_backend_impl.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "net/disk_cache/simple/simple_test_util.h"
#include "net/disk_cache/simple/simple_util.h"
-#include "net/disk_cache/tracing_cache_backend.h"
+#include "net/disk_cache/tracing/tracing_cache_backend.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include "base/win/scoped_handle.h"
#endif
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
+
using base::Time;
namespace {
@@ -479,7 +482,7 @@ TEST_F(DiskCacheBackendTest, ExternalFiles) {
const int kSize = 50;
scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize));
CacheTestFillBuffer(buffer1->data(), kSize, false);
- ASSERT_EQ(kSize, file_util::WriteFile(filename, buffer1->data(), kSize));
+ ASSERT_EQ(kSize, base::WriteFile(filename, buffer1->data(), kSize));
// Now let's create a file with the cache.
disk_cache::Entry* entry;
@@ -533,10 +536,9 @@ TEST_F(DiskCacheBackendTest, ShutdownWithPendingFileIO) {
// Here and below, tests that simulate crashes are not compiled in LeakSanitizer
// builds because they contain a lot of intentional memory leaks.
-// The wrapper scripts used to run tests under Valgrind Memcheck and
-// Heapchecker will also disable these tests under those tools. See:
+// The wrapper scripts used to run tests under Valgrind Memcheck will also
+// disable these tests. See:
// tools/valgrind/gtest_exclude/net_unittests.gtest-memcheck.txt
-// tools/heapcheck/net_unittests.gtest-heapcheck.txt
#if !defined(LEAK_SANITIZER)
// We'll be leaking from this test.
TEST_F(DiskCacheBackendTest, ShutdownWithPendingFileIO_Fast) {
@@ -666,10 +668,13 @@ TEST_F(DiskCacheBackendTest, ShutdownWithPendingCreate_Fast) {
}
#endif
+// Disabled on android since this test requires cache creator to create
+// blockfile caches.
+#if !defined(OS_ANDROID)
TEST_F(DiskCacheTest, TruncatedIndex) {
ASSERT_TRUE(CleanupCacheDir());
base::FilePath index = cache_path_.AppendASCII("index");
- ASSERT_EQ(5, file_util::WriteFile(index, "hello", 5));
+ ASSERT_EQ(5, base::WriteFile(index, "hello", 5));
base::Thread cache_thread("CacheThread");
ASSERT_TRUE(cache_thread.StartWithOptions(
@@ -691,6 +696,7 @@ TEST_F(DiskCacheTest, TruncatedIndex) {
ASSERT_FALSE(backend);
}
+#endif
void DiskCacheBackendTest::BackendSetSize() {
const int cache_size = 0x10000; // 64 kB
@@ -1602,7 +1608,6 @@ void DiskCacheBackendTest::BackendDoomBetween() {
AddDelay();
Time middle_end = Time::Now();
- AddDelay();
ASSERT_EQ(net::OK, CreateEntry("fourth", &entry));
entry->Close();
@@ -1835,6 +1840,9 @@ class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
// Tests that the disk cache successfully joins the control group, dropping the
// existing cache in favour of a new empty cache.
+// Disabled on android since this test requires cache creator to create
+// blockfile caches.
+#if !defined(OS_ANDROID)
TEST_F(DiskCacheTest, SimpleCacheControlJoin) {
base::Thread cache_thread("CacheThread");
ASSERT_TRUE(cache_thread.StartWithOptions(
@@ -1865,6 +1873,7 @@ TEST_F(DiskCacheTest, SimpleCacheControlJoin) {
ASSERT_EQ(net::OK, cb.GetResult(rv));
EXPECT_EQ(0, base_cache->GetEntryCount());
}
+#endif
// Tests that the disk cache can restart in the control group preserving
// existing entries.
@@ -1943,6 +1952,9 @@ TEST_F(DiskCacheTest, SimpleCacheControlLeave) {
}
// Tests that the cache is properly restarted on recovery error.
+// Disabled on android since this test requires cache creator to create
+// blockfile caches.
+#if !defined(OS_ANDROID)
TEST_F(DiskCacheBackendTest, DeleteOld) {
ASSERT_TRUE(CopyTestCache("wrong_version"));
SetNewEviction();
@@ -1969,6 +1981,7 @@ TEST_F(DiskCacheBackendTest, DeleteOld) {
cache_.reset();
EXPECT_TRUE(CheckCacheIntegrity(cache_path_, new_eviction_, mask_));
}
+#endif
// We want to be able to deal with messed up entries on disk.
void DiskCacheBackendTest::BackendInvalidEntry2() {
@@ -2728,6 +2741,21 @@ TEST_F(DiskCacheTest, Backend_UsageStatsTimer) {
helper.WaitUntilCacheIoFinished(1);
}
+TEST_F(DiskCacheBackendTest, TimerNotCreated) {
+ ASSERT_TRUE(CopyTestCache("wrong_version"));
+
+ scoped_ptr<disk_cache::BackendImpl> cache;
+ cache.reset(new disk_cache::BackendImpl(
+ cache_path_, base::MessageLoopProxy::current().get(), NULL));
+ ASSERT_TRUE(NULL != cache.get());
+ cache->SetUnitTestMode();
+ ASSERT_NE(net::OK, cache->SyncInit());
+
+ ASSERT_TRUE(NULL == cache->GetTimerForTest());
+
+ DisableIntegrityCheck();
+}
+
TEST_F(DiskCacheBackendTest, Backend_UsageStats) {
InitCache();
disk_cache::Entry* entry;
@@ -3213,7 +3241,8 @@ TEST_F(DiskCacheBackendTest, SimpleDoomRecent) {
BackendDoomRecent();
}
-TEST_F(DiskCacheBackendTest, SimpleDoomBetween) {
+// crbug.com/330926, crbug.com/370677
+TEST_F(DiskCacheBackendTest, DISABLED_SimpleDoomBetween) {
SetSimpleCacheMode();
BackendDoomBetween();
}
@@ -3300,7 +3329,7 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenBadFile) {
header.initial_magic_number = GG_UINT64_C(0xbadf00d);
EXPECT_EQ(
implicit_cast<int>(sizeof(header)),
- file_util::WriteFile(entry_file1_path, reinterpret_cast<char*>(&header),
+ base::WriteFile(entry_file1_path, reinterpret_cast<char*>(&header),
sizeof(header)));
ASSERT_EQ(net::ERR_FAILED, OpenEntry(key, &entry));
}
diff --git a/chromium/net/disk_cache/addr.cc b/chromium/net/disk_cache/blockfile/addr.cc
index 8f41e6fe278..ab418c4dc03 100644
--- a/chromium/net/disk_cache/addr.cc
+++ b/chromium/net/disk_cache/blockfile/addr.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/addr.h"
+#include "net/disk_cache/blockfile/addr.h"
#include "base/logging.h"
diff --git a/chromium/net/disk_cache/addr.h b/chromium/net/disk_cache/blockfile/addr.h
index f0fb1ca5701..99d2c930709 100644
--- a/chromium/net/disk_cache/addr.h
+++ b/chromium/net/disk_cache/blockfile/addr.h
@@ -5,11 +5,11 @@
// This is an internal class that handles the address of a cache record.
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_ADDR_H_
-#define NET_DISK_CACHE_ADDR_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_ADDR_H_
+#define NET_DISK_CACHE_BLOCKFILE_ADDR_H_
#include "net/base/net_export.h"
-#include "net/disk_cache/disk_format_base.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
namespace disk_cache {
@@ -54,6 +54,18 @@ const int kFirstAdditionalBlockFileV3 = 7;
// 0000 0011 0000 0000 0000 0000 0000 0000 : number of contiguous blocks 1-4
// 0000 0000 1111 1111 0000 0000 0000 0000 : file selector 0 - 255
// 0000 0000 0000 0000 1111 1111 1111 1111 : block# 0 - 65,535 (2^16)
+//
+// Note that an Addr can be used to "point" to a variety of different objects,
+// from a given type of entry to random blobs of data. Conceptually, an Addr is
+// just a number that someone can inspect to find out how to locate the desired
+// record. Most users will not care about the specific bits inside Addr, for
+// example, what parts of it point to a file number; only the code that has to
+// select a specific file would care about those specific bits.
+//
+// From a general point of view, an Addr has a total capacity of 2^24 entities,
+// in that it has 24 bits that can identify individual records. Note that the
+// address space is bigger for independent files (2^28), but that would not be
+// the general case.
class NET_EXPORT_PRIVATE Addr {
public:
Addr() : value_(0) {}
@@ -108,14 +120,6 @@ class NET_EXPORT_PRIVATE Addr {
return value_ != other.value_;
}
- static Addr FromEntryAddress(uint32 value) {
- return Addr(kInitializedMask + (BLOCK_ENTRIES << kFileTypeOffset) + value);
- }
-
- static Addr FromEvictedAddress(uint32 value) {
- return Addr(kInitializedMask + (BLOCK_EVICTED << kFileTypeOffset) + value);
- }
-
static int BlockSizeForFileType(FileType file_type) {
switch (file_type) {
case RANKINGS:
@@ -181,4 +185,4 @@ class NET_EXPORT_PRIVATE Addr {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_ADDR_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_ADDR_H_
diff --git a/chromium/net/disk_cache/addr_unittest.cc b/chromium/net/disk_cache/blockfile/addr_unittest.cc
index a6da03cf777..eaed65976b2 100644
--- a/chromium/net/disk_cache/addr_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/addr_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/addr.h"
+#include "net/disk_cache/blockfile/addr.h"
#include "net/disk_cache/disk_cache_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/net/disk_cache/backend_impl.cc b/chromium/net/disk_cache/blockfile/backend_impl.cc
index 9e2131715db..a70bc74c943 100644
--- a/chromium/net/disk_cache/backend_impl.cc
+++ b/chromium/net/disk_cache/blockfile/backend_impl.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/hash.h"
#include "base/message_loop/message_loop.h"
@@ -21,16 +22,17 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/net_errors.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/errors.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/file.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+#include "net/disk_cache/blockfile/webfonts_histogram.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/errors.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/file.h"
-// This has to be defined before including histogram_macros.h from this file.
-#define NET_DISK_CACHE_BACKEND_IMPL_CC_
-#include "net/disk_cache/histogram_macros.h"
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ this
using base::Time;
using base::TimeDelta;
@@ -204,14 +206,11 @@ int BackendImpl::SyncInit() {
num_refs_ = num_pending_io_ = max_refs_ = 0;
entry_count_ = byte_count_ = 0;
+ bool should_create_timer = false;
if (!restarted_) {
buffer_bytes_ = 0;
trace_object_ = TraceObject::GetTraceObject();
- // Create a recurrent timer of 30 secs.
- int timer_delay = unit_test_ ? 1000 : 30000;
- timer_.reset(new base::RepeatingTimer<BackendImpl>());
- timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(timer_delay), this,
- &BackendImpl::OnStatsTimer);
+ should_create_timer = true;
}
init_ = true;
@@ -288,6 +287,14 @@ int BackendImpl::SyncInit() {
FlushIndex();
+ if (!disabled_ && should_create_timer) {
+ // Create a recurrent timer of 30 secs.
+ int timer_delay = unit_test_ ? 1000 : 30000;
+ timer_.reset(new base::RepeatingTimer<BackendImpl>());
+ timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(timer_delay), this,
+ &BackendImpl::OnStatsTimer);
+ }
+
return disabled_ ? net::ERR_FAILED : net::OK;
}
@@ -473,6 +480,9 @@ EntryImpl* BackendImpl::OpenEntryImpl(const std::string& key) {
// The entry was already evicted.
cache_entry->Release();
cache_entry = NULL;
+ web_fonts_histogram::RecordEvictedEntry(key);
+ } else if (!cache_entry) {
+ web_fonts_histogram::RecordCacheMiss(key);
}
int current_size = data_->header.num_bytes / (1024 * 1024);
@@ -499,6 +509,7 @@ EntryImpl* BackendImpl::OpenEntryImpl(const std::string& key) {
CACHE_UMA(HOURS, "AllOpenByTotalHours.Hit", 0, total_hours);
CACHE_UMA(HOURS, "AllOpenByUseHours.Hit", 0, use_hours);
stats_.OnEvent(Stats::OPEN_HIT);
+ web_fonts_histogram::RecordCacheHit(cache_entry);
SIMPLE_STATS_COUNTER("disk_cache.hit");
return cache_entry;
}
@@ -663,15 +674,12 @@ bool BackendImpl::CreateExternalFile(Addr* address) {
continue;
}
base::FilePath name = GetFileName(file_address);
- int flags = base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_CREATE |
- base::PLATFORM_FILE_EXCLUSIVE_WRITE;
- base::PlatformFileError error;
- scoped_refptr<disk_cache::File> file(new disk_cache::File(
- base::CreatePlatformFile(name, flags, NULL, &error)));
- if (!file->IsValid()) {
- if (error != base::PLATFORM_FILE_ERROR_EXISTS) {
+ int flags = base::File::FLAG_READ | base::File::FLAG_WRITE |
+ base::File::FLAG_CREATE | base::File::FLAG_EXCLUSIVE_WRITE;
+ base::File file(name, flags);
+ if (!file.IsValid()) {
+ base::File::Error error = file.error_details();
+ if (error != base::File::FILE_ERROR_EXISTS) {
LOG(ERROR) << "Unable to create file: " << error;
return false;
}
@@ -998,6 +1006,9 @@ void BackendImpl::OnWrite(int32 bytes) {
}
void BackendImpl::OnStatsTimer() {
+ if (disabled_)
+ return;
+
stats_.OnEvent(Stats::TIMER);
int64 time = stats_.GetCounter(Stats::TIMER);
int64 current = stats_.GetCounter(Stats::OPEN_ENTRIES);
@@ -1089,6 +1100,10 @@ void BackendImpl::TrimDeletedListForTest(bool empty) {
eviction_.TrimDeletedList(empty);
}
+base::RepeatingTimer<BackendImpl>* BackendImpl::GetTimerForTest() {
+ return timer_.get();
+}
+
int BackendImpl::SelfCheck() {
if (!init_) {
LOG(ERROR) << "Init failed";
@@ -1254,17 +1269,16 @@ bool BackendImpl::InitBackingStore(bool* file_created) {
base::FilePath index_name = path_.AppendASCII(kIndexName);
- int flags = base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_OPEN_ALWAYS |
- base::PLATFORM_FILE_EXCLUSIVE_WRITE;
- scoped_refptr<disk_cache::File> file(new disk_cache::File(
- base::CreatePlatformFile(index_name, flags, file_created, NULL)));
-
- if (!file->IsValid())
+ int flags = base::File::FLAG_READ | base::File::FLAG_WRITE |
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_EXCLUSIVE_WRITE;
+ base::File base_file(index_name, flags);
+ if (!base_file.IsValid())
return false;
bool ret = true;
+ *file_created = base_file.created();
+
+ scoped_refptr<disk_cache::File> file(new disk_cache::File(base_file.Pass()));
if (*file_created)
ret = CreateBackingStore(file.get());
@@ -1273,7 +1287,7 @@ bool BackendImpl::InitBackingStore(bool* file_created) {
return false;
index_ = new MappedFile();
- data_ = reinterpret_cast<Index*>(index_->Init(index_name, 0));
+ data_ = static_cast<Index*>(index_->Init(index_name, 0));
if (!data_) {
LOG(ERROR) << "Unable to map Index file";
return false;
@@ -2004,9 +2018,8 @@ bool BackendImpl::CheckIndex() {
if (!mask_)
mask_ = data_->header.table_len - 1;
- // Load the table into memory with a single read.
- scoped_ptr<char[]> buf(new char[current_size]);
- return index_->Read(buf.get(), current_size, 0);
+ // Load the table into memory.
+ return index_->Preload();
}
int BackendImpl::CheckAllEntries() {
diff --git a/chromium/net/disk_cache/backend_impl.h b/chromium/net/disk_cache/blockfile/backend_impl.h
index 61b95b3ab0b..33452586347 100644
--- a/chromium/net/disk_cache/backend_impl.h
+++ b/chromium/net/disk_cache/blockfile/backend_impl.h
@@ -4,20 +4,20 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_BACKEND_IMPL_H_
-#define NET_DISK_CACHE_BACKEND_IMPL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_H_
+#define NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_H_
#include "base/containers/hash_tables.h"
#include "base/files/file_path.h"
#include "base/timer/timer.h"
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/blockfile/block_files.h"
+#include "net/disk_cache/blockfile/eviction.h"
+#include "net/disk_cache/blockfile/in_flight_backend_io.h"
+#include "net/disk_cache/blockfile/rankings.h"
+#include "net/disk_cache/blockfile/stats.h"
+#include "net/disk_cache/blockfile/stress_support.h"
+#include "net/disk_cache/blockfile/trace.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/eviction.h"
-#include "net/disk_cache/in_flight_backend_io.h"
-#include "net/disk_cache/rankings.h"
-#include "net/disk_cache/stats.h"
-#include "net/disk_cache/stress_support.h"
-#include "net/disk_cache/trace.h"
namespace net {
class NetLog;
@@ -246,6 +246,9 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
// entries. This method should be called directly on the cache thread.
void TrimDeletedListForTest(bool empty);
+ // Only intended for testing
+ base::RepeatingTimer<BackendImpl>* GetTimerForTest();
+
// Performs a simple self-check, and returns the number of dirty items
// or an error code (negative value).
int SelfCheck();
@@ -394,4 +397,4 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BACKEND_IMPL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_H_
diff --git a/chromium/net/disk_cache/v3/backend_impl_v3.cc b/chromium/net/disk_cache/blockfile/backend_impl_v3.cc
index 92ea272226b..ce0b8d01f13 100644
--- a/chromium/net/disk_cache/v3/backend_impl_v3.cc
+++ b/chromium/net/disk_cache/blockfile/backend_impl_v3.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl_v3.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -21,15 +21,18 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/net_errors.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+#include "net/disk_cache/blockfile/entry_impl_v3.h"
+#include "net/disk_cache/blockfile/errors.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/file.h"
+#include "net/disk_cache/blockfile/histogram_macros_v3.h"
+#include "net/disk_cache/blockfile/index_table_v3.h"
+#include "net/disk_cache/blockfile/storage_block-inl.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/errors.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/file.h"
-// This has to be defined before including histogram_macros.h from this file.
-#define NET_DISK_CACHE_BACKEND_IMPL_CC_
-#include "net/disk_cache/histogram_macros.h"
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ this
using base::Time;
using base::TimeDelta;
@@ -37,40 +40,12 @@ using base::TimeTicks;
namespace {
-const char* kIndexName = "index";
-
-// Seems like ~240 MB correspond to less than 50k entries for 99% of the people.
-// Note that the actual target is to keep the index table load factor under 55%
-// for most users.
-const int k64kEntriesStore = 240 * 1000 * 1000;
-const int kBaseTableLen = 64 * 1024;
+#if defined(V3_NOT_JUST_YET_READY)
const int kDefaultCacheSize = 80 * 1024 * 1024;
// Avoid trimming the cache for the first 5 minutes (10 timer ticks).
const int kTrimDelay = 10;
-
-int DesiredIndexTableLen(int32 storage_size) {
- if (storage_size <= k64kEntriesStore)
- return kBaseTableLen;
- if (storage_size <= k64kEntriesStore * 2)
- return kBaseTableLen * 2;
- if (storage_size <= k64kEntriesStore * 4)
- return kBaseTableLen * 4;
- if (storage_size <= k64kEntriesStore * 8)
- return kBaseTableLen * 8;
-
- // The biggest storage_size for int32 requires a 4 MB table.
- return kBaseTableLen * 16;
-}
-
-int MaxStorageSizeForTable(int table_len) {
- return table_len * (k64kEntriesStore / kBaseTableLen);
-}
-
-size_t GetIndexSize(int table_len) {
- size_t table_size = sizeof(disk_cache::CacheAddr) * table_len;
- return sizeof(disk_cache::IndexHeader) + table_size;
-}
+#endif // defined(V3_NOT_JUST_YET_READY).
} // namespace
@@ -78,13 +53,12 @@ size_t GetIndexSize(int table_len) {
namespace disk_cache {
-BackendImpl::BackendImpl(const base::FilePath& path,
- base::MessageLoopProxy* cache_thread,
- net::NetLog* net_log)
- : background_queue_(this, cache_thread),
+BackendImplV3::BackendImplV3(const base::FilePath& path,
+ base::MessageLoopProxy* cache_thread,
+ net::NetLog* net_log)
+ : index_(NULL),
path_(path),
- block_files_(path),
- mask_(0),
+ block_files_(),
max_size_(0),
up_ticks_(0),
cache_type_(net::DISK_CACHE),
@@ -92,81 +66,38 @@ BackendImpl::BackendImpl(const base::FilePath& path,
user_flags_(0),
init_(false),
restarted_(false),
- unit_test_(false),
read_only_(false),
disabled_(false),
- new_eviction_(false),
+ lru_eviction_(true),
first_timer_(true),
user_load_(false),
net_log_(net_log),
- done_(true, false),
ptr_factory_(this) {
}
-BackendImpl::BackendImpl(const base::FilePath& path,
- uint32 mask,
- base::MessageLoopProxy* cache_thread,
- net::NetLog* net_log)
- : background_queue_(this, cache_thread),
- path_(path),
- block_files_(path),
- mask_(mask),
- max_size_(0),
- up_ticks_(0),
- cache_type_(net::DISK_CACHE),
- uma_report_(0),
- user_flags_(kMask),
- init_(false),
- restarted_(false),
- unit_test_(false),
- read_only_(false),
- disabled_(false),
- new_eviction_(false),
- first_timer_(true),
- user_load_(false),
- net_log_(net_log),
- done_(true, false),
- ptr_factory_(this) {
+BackendImplV3::~BackendImplV3() {
+ CleanupCache();
}
-BackendImpl::~BackendImpl() {
- if (user_flags_ & kNoRandom) {
- // This is a unit test, so we want to be strict about not leaking entries
- // and completing all the work.
- background_queue_.WaitForPendingIO();
- } else {
- // This is most likely not a test, so we want to do as little work as
- // possible at this time, at the price of leaving dirty entries behind.
- background_queue_.DropPendingIO();
- }
-
- if (background_queue_.BackgroundIsCurrentThread()) {
- // Unit tests may use the same thread for everything.
- CleanupCache();
- } else {
- background_queue_.background_thread()->PostTask(
- FROM_HERE, base::Bind(&FinalCleanupCallback, base::Unretained(this)));
- // http://crbug.com/74623
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
- done_.Wait();
- }
-}
+int BackendImplV3::Init(const CompletionCallback& callback) {
+ DCHECK(!init_);
+ if (init_)
+ return net::ERR_FAILED;
-int BackendImpl::Init(const CompletionCallback& callback) {
- background_queue_.Init(callback);
return net::ERR_IO_PENDING;
}
// ------------------------------------------------------------------------
-int BackendImpl::OpenPrevEntry(void** iter, Entry** prev_entry,
- const CompletionCallback& callback) {
+#if defined(V3_NOT_JUST_YET_READY)
+int BackendImplV3::OpenPrevEntry(void** iter, Entry** prev_entry,
+ const CompletionCallback& callback) {
DCHECK(!callback.is_null());
- background_queue_.OpenPrevEntry(iter, prev_entry, callback);
- return net::ERR_IO_PENDING;
+ return OpenFollowingEntry(true, iter, prev_entry, callback);
}
+#endif // defined(V3_NOT_JUST_YET_READY).
-bool BackendImpl::SetMaxSize(int max_bytes) {
+bool BackendImplV3::SetMaxSize(int max_bytes) {
COMPILE_ASSERT(sizeof(max_bytes) == sizeof(max_size_), unsupported_int_model);
if (max_bytes < 0)
return false;
@@ -179,28 +110,29 @@ bool BackendImpl::SetMaxSize(int max_bytes) {
if (max_bytes >= kint32max - kint32max / 10)
max_bytes = kint32max - kint32max / 10 - 1;
- user_flags_ |= kMaxSize;
+ user_flags_ |= MAX_SIZE;
max_size_ = max_bytes;
return true;
}
-void BackendImpl::SetType(net::CacheType type) {
+void BackendImplV3::SetType(net::CacheType type) {
DCHECK_NE(net::MEMORY_CACHE, type);
cache_type_ = type;
}
-bool BackendImpl::CreateBlock(FileType block_type, int block_count,
- Addr* block_address) {
+bool BackendImplV3::CreateBlock(FileType block_type, int block_count,
+ Addr* block_address) {
return block_files_.CreateBlock(block_type, block_count, block_address);
}
-void BackendImpl::UpdateRank(EntryImpl* entry, bool modified) {
+#if defined(V3_NOT_JUST_YET_READY)
+void BackendImplV3::UpdateRank(EntryImplV3* entry, bool modified) {
if (read_only_ || (!modified && cache_type() == net::SHADER_CACHE))
return;
eviction_.UpdateRank(entry, modified);
}
-void BackendImpl::InternalDoomEntry(EntryImpl* entry) {
+void BackendImplV3::InternalDoomEntry(EntryImplV3* entry) {
uint32 hash = entry->GetHash();
std::string key = entry->GetKey();
Addr entry_addr = entry->entry()->address();
@@ -230,20 +162,20 @@ void BackendImpl::InternalDoomEntry(EntryImpl* entry) {
FlushIndex();
}
-void BackendImpl::OnEntryDestroyBegin(Addr address) {
+void BackendImplV3::OnEntryDestroyBegin(Addr address) {
EntriesMap::iterator it = open_entries_.find(address.value());
if (it != open_entries_.end())
open_entries_.erase(it);
}
-void BackendImpl::OnEntryDestroyEnd() {
+void BackendImplV3::OnEntryDestroyEnd() {
DecreaseNumRefs();
if (data_->header.num_bytes > max_size_ && !read_only_ &&
(up_ticks_ > kTrimDelay || user_flags_ & kNoRandom))
eviction_.TrimCache(false);
}
-EntryImpl* BackendImpl::GetOpenEntry(CacheRankingsBlock* rankings) const {
+EntryImplV3* BackendImplV3::GetOpenEntry(Addr address) const {
DCHECK(rankings->HasData());
EntriesMap::const_iterator it =
open_entries_.find(rankings->Data()->contents);
@@ -255,11 +187,11 @@ EntryImpl* BackendImpl::GetOpenEntry(CacheRankingsBlock* rankings) const {
return NULL;
}
-int BackendImpl::MaxFileSize() const {
+int BackendImplV3::MaxFileSize() const {
return max_size_ / 8;
}
-void BackendImpl::ModifyStorageSize(int32 old_size, int32 new_size) {
+void BackendImplV3::ModifyStorageSize(int32 old_size, int32 new_size) {
if (disabled_ || old_size == new_size)
return;
if (old_size > new_size)
@@ -267,19 +199,17 @@ void BackendImpl::ModifyStorageSize(int32 old_size, int32 new_size) {
else
AddStorageSize(new_size - old_size);
- FlushIndex();
-
// Update the usage statistics.
stats_.ModifyStorageStats(old_size, new_size);
}
-void BackendImpl::TooMuchStorageRequested(int32 size) {
+void BackendImplV3::TooMuchStorageRequested(int32 size) {
stats_.ModifyStorageStats(0, size);
}
-bool BackendImpl::IsAllocAllowed(int current_size, int new_size) {
+bool BackendImplV3::IsAllocAllowed(int current_size, int new_size) {
DCHECK_GT(new_size, current_size);
- if (user_flags_ & kNoBuffering)
+ if (user_flags_ & NO_BUFFERING)
return false;
int to_add = new_size - current_size;
@@ -287,37 +217,38 @@ bool BackendImpl::IsAllocAllowed(int current_size, int new_size) {
return false;
buffer_bytes_ += to_add;
- CACHE_UMA(COUNTS_50000, "BufferBytes", 0, buffer_bytes_ / 1024);
+ CACHE_UMA(COUNTS_50000, "BufferBytes", buffer_bytes_ / 1024);
return true;
}
+#endif // defined(V3_NOT_JUST_YET_READY).
-void BackendImpl::BufferDeleted(int size) {
- buffer_bytes_ -= size;
+void BackendImplV3::BufferDeleted(int size) {
DCHECK_GE(size, 0);
+ buffer_bytes_ -= size;
+ DCHECK_GE(buffer_bytes_, 0);
}
-bool BackendImpl::IsLoaded() const {
- CACHE_UMA(COUNTS, "PendingIO", 0, num_pending_io_);
- if (user_flags_ & kNoLoadProtection)
+bool BackendImplV3::IsLoaded() const {
+ if (user_flags_ & NO_LOAD_PROTECTION)
return false;
- return (num_pending_io_ > 5 || user_load_);
+ return user_load_;
}
-std::string BackendImpl::HistogramName(const char* name, int experiment) const {
- if (!experiment)
- return base::StringPrintf("DiskCache.%d.%s", cache_type_, name);
- return base::StringPrintf("DiskCache.%d.%s_%d", cache_type_,
- name, experiment);
+std::string BackendImplV3::HistogramName(const char* name) const {
+ static const char* names[] = { "Http", "", "Media", "AppCache", "Shader" };
+ DCHECK_NE(cache_type_, net::MEMORY_CACHE);
+ return base::StringPrintf("DiskCache3.%s_%s", name, names[cache_type_]);
}
-base::WeakPtr<BackendImpl> BackendImpl::GetWeakPtr() {
+base::WeakPtr<BackendImplV3> BackendImplV3::GetWeakPtr() {
return ptr_factory_.GetWeakPtr();
}
+#if defined(V3_NOT_JUST_YET_READY)
// We want to remove biases from some histograms so we only send data once per
// week.
-bool BackendImpl::ShouldReportAgain() {
+bool BackendImplV3::ShouldReportAgain() {
if (uma_report_)
return uma_report_ == 2;
@@ -332,62 +263,64 @@ bool BackendImpl::ShouldReportAgain() {
return false;
}
-void BackendImpl::FirstEviction() {
- DCHECK(data_->header.create_time);
+void BackendImplV3::FirstEviction() {
+ IndexHeaderV3* header = index_.header();
+ header->flags |= CACHE_EVICTED;
+ DCHECK(header->create_time);
if (!GetEntryCount())
return; // This is just for unit tests.
- Time create_time = Time::FromInternalValue(data_->header.create_time);
- CACHE_UMA(AGE, "FillupAge", 0, create_time);
+ Time create_time = Time::FromInternalValue(header->create_time);
+ CACHE_UMA(AGE, "FillupAge", create_time);
int64 use_time = stats_.GetCounter(Stats::TIMER);
- CACHE_UMA(HOURS, "FillupTime", 0, static_cast<int>(use_time / 120));
- CACHE_UMA(PERCENTAGE, "FirstHitRatio", 0, stats_.GetHitRatio());
+ CACHE_UMA(HOURS, "FillupTime", static_cast<int>(use_time / 120));
+ CACHE_UMA(PERCENTAGE, "FirstHitRatio", stats_.GetHitRatio());
if (!use_time)
use_time = 1;
- CACHE_UMA(COUNTS_10000, "FirstEntryAccessRate", 0,
- static_cast<int>(data_->header.num_entries / use_time));
- CACHE_UMA(COUNTS, "FirstByteIORate", 0,
- static_cast<int>((data_->header.num_bytes / 1024) / use_time));
+ CACHE_UMA(COUNTS_10000, "FirstEntryAccessRate",
+ static_cast<int>(header->num_entries / use_time));
+ CACHE_UMA(COUNTS, "FirstByteIORate",
+ static_cast<int>((header->num_bytes / 1024) / use_time));
- int avg_size = data_->header.num_bytes / GetEntryCount();
- CACHE_UMA(COUNTS, "FirstEntrySize", 0, avg_size);
+ int avg_size = header->num_bytes / GetEntryCount();
+ CACHE_UMA(COUNTS, "FirstEntrySize", avg_size);
int large_entries_bytes = stats_.GetLargeEntriesSize();
- int large_ratio = large_entries_bytes * 100 / data_->header.num_bytes;
- CACHE_UMA(PERCENTAGE, "FirstLargeEntriesRatio", 0, large_ratio);
+ int large_ratio = large_entries_bytes * 100 / header->num_bytes;
+ CACHE_UMA(PERCENTAGE, "FirstLargeEntriesRatio", large_ratio);
- if (new_eviction_) {
- CACHE_UMA(PERCENTAGE, "FirstResurrectRatio", 0, stats_.GetResurrectRatio());
- CACHE_UMA(PERCENTAGE, "FirstNoUseRatio", 0,
- data_->header.lru.sizes[0] * 100 / data_->header.num_entries);
- CACHE_UMA(PERCENTAGE, "FirstLowUseRatio", 0,
- data_->header.lru.sizes[1] * 100 / data_->header.num_entries);
- CACHE_UMA(PERCENTAGE, "FirstHighUseRatio", 0,
- data_->header.lru.sizes[2] * 100 / data_->header.num_entries);
+ if (!lru_eviction_) {
+ CACHE_UMA(PERCENTAGE, "FirstResurrectRatio", stats_.GetResurrectRatio());
+ CACHE_UMA(PERCENTAGE, "FirstNoUseRatio",
+ header->num_no_use_entries * 100 / header->num_entries);
+ CACHE_UMA(PERCENTAGE, "FirstLowUseRatio",
+ header->num_low_use_entries * 100 / header->num_entries);
+ CACHE_UMA(PERCENTAGE, "FirstHighUseRatio",
+ header->num_high_use_entries * 100 / header->num_entries);
}
stats_.ResetRatios();
}
-void BackendImpl::OnEvent(Stats::Counters an_event) {
+void BackendImplV3::OnEvent(Stats::Counters an_event) {
stats_.OnEvent(an_event);
}
-void BackendImpl::OnRead(int32 bytes) {
+void BackendImplV3::OnRead(int32 bytes) {
DCHECK_GE(bytes, 0);
byte_count_ += bytes;
if (byte_count_ < 0)
byte_count_ = kint32max;
}
-void BackendImpl::OnWrite(int32 bytes) {
+void BackendImplV3::OnWrite(int32 bytes) {
// We use the same implementation as OnRead... just log the number of bytes.
OnRead(bytes);
}
-void BackendImpl::OnStatsTimer() {
+void BackendImplV3::OnTimerTick() {
stats_.OnEvent(Stats::TIMER);
int64 time = stats_.GetCounter(Stats::TIMER);
int64 current = stats_.GetCounter(Stats::OPEN_ENTRIES);
@@ -403,10 +336,10 @@ void BackendImpl::OnStatsTimer() {
stats_.SetCounter(Stats::MAX_ENTRIES, max_refs_);
}
- CACHE_UMA(COUNTS, "NumberOfReferences", 0, num_refs_);
+ CACHE_UMA(COUNTS, "NumberOfReferences", num_refs_);
- CACHE_UMA(COUNTS_10000, "EntryAccessRate", 0, entry_count_);
- CACHE_UMA(COUNTS, "ByteIORate", 0, byte_count_ / 1024);
+ CACHE_UMA(COUNTS_10000, "EntryAccessRate", entry_count_);
+ CACHE_UMA(COUNTS, "ByteIORate", byte_count_ / 1024);
// These values cover about 99.5% of the population (Oct 2011).
user_load_ = (entry_count_ > 300 || byte_count_ > 7 * 1024 * 1024);
@@ -427,41 +360,40 @@ void BackendImpl::OnStatsTimer() {
StoreStats();
}
-void BackendImpl::SetUnitTestMode() {
- user_flags_ |= kUnitTestMode;
- unit_test_ = true;
+void BackendImplV3::SetUnitTestMode() {
+ user_flags_ |= UNIT_TEST_MODE;
}
-void BackendImpl::SetUpgradeMode() {
- user_flags_ |= kUpgradeMode;
+void BackendImplV3::SetUpgradeMode() {
+ user_flags_ |= UPGRADE_MODE;
read_only_ = true;
}
-void BackendImpl::SetNewEviction() {
- user_flags_ |= kNewEviction;
- new_eviction_ = true;
+void BackendImplV3::SetNewEviction() {
+ user_flags_ |= EVICTION_V2;
+ lru_eviction_ = false;
}
-void BackendImpl::SetFlags(uint32 flags) {
+void BackendImplV3::SetFlags(uint32 flags) {
user_flags_ |= flags;
}
-int BackendImpl::FlushQueueForTest(const CompletionCallback& callback) {
+int BackendImplV3::FlushQueueForTest(const CompletionCallback& callback) {
background_queue_.FlushQueue(callback);
return net::ERR_IO_PENDING;
}
-void BackendImpl::TrimForTest(bool empty) {
+void BackendImplV3::TrimForTest(bool empty) {
eviction_.SetTestMode();
eviction_.TrimCache(empty);
}
-void BackendImpl::TrimDeletedListForTest(bool empty) {
+void BackendImplV3::TrimDeletedListForTest(bool empty) {
eviction_.SetTestMode();
eviction_.TrimDeletedList(empty);
}
-int BackendImpl::SelfCheck() {
+int BackendImplV3::SelfCheck() {
if (!init_) {
LOG(ERROR) << "Init failed";
return ERR_INIT_FAILED;
@@ -487,26 +419,19 @@ int BackendImpl::SelfCheck() {
// ------------------------------------------------------------------------
-net::CacheType BackendImpl::GetCacheType() const {
+net::CacheType BackendImplV3::GetCacheType() const {
return cache_type_;
}
-int32 BackendImpl::GetEntryCount() const {
- if (!index_.get() || disabled_)
+int32 BackendImplV3::GetEntryCount() const {
+ if (disabled_)
return 0;
- // num_entries includes entries already evicted.
- int32 not_deleted = data_->header.num_entries -
- data_->header.lru.sizes[Rankings::DELETED];
-
- if (not_deleted < 0) {
- NOTREACHED();
- not_deleted = 0;
- }
-
- return not_deleted;
+ DCHECK(init_);
+ return index_.header()->num_entries;
}
-EntryImpl* BackendImpl::OpenEntryImpl(const std::string& key) {
+int BackendImplV3::OpenEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) {
if (disabled_)
return NULL;
@@ -550,7 +475,8 @@ EntryImpl* BackendImpl::OpenEntryImpl(const std::string& key) {
return cache_entry;
}
-EntryImpl* BackendImpl::CreateEntryImpl(const std::string& key) {
+int BackendImplV3::CreateEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) {
if (disabled_ || key.empty())
return NULL;
@@ -648,7 +574,8 @@ EntryImpl* BackendImpl::CreateEntryImpl(const std::string& key) {
return cache_entry.get();
}
-int BackendImpl::SyncDoomEntry(const std::string& key) {
+int BackendImplV3::DoomEntry(const std::string& key,
+ const CompletionCallback& callback) {
if (disabled_)
return net::ERR_FAILED;
@@ -661,7 +588,7 @@ int BackendImpl::SyncDoomEntry(const std::string& key) {
return net::OK;
}
-int BackendImpl::SyncDoomAllEntries() {
+int BackendImplV3::DoomAllEntries(const CompletionCallback& callback) {
// This is not really an error, but it is an interesting condition.
ReportError(ERR_CACHE_DOOMED);
stats_.OnEvent(Stats::DOOM_CACHE);
@@ -677,8 +604,9 @@ int BackendImpl::SyncDoomAllEntries() {
}
}
-int BackendImpl::SyncDoomEntriesBetween(const base::Time initial_time,
- const base::Time end_time) {
+int BackendImplV3::DoomEntriesBetween(base::Time initial_time,
+ base::Time end_time,
+ const CompletionCallback& callback) {
DCHECK_NE(net::APP_CACHE, cache_type_);
if (end_time.is_null())
return SyncDoomEntriesSince(initial_time);
@@ -714,9 +642,8 @@ int BackendImpl::SyncDoomEntriesBetween(const base::Time initial_time,
return net::OK;
}
-// We use OpenNextEntryImpl to retrieve elements from the cache, until we get
-// entries that are too old.
-int BackendImpl::SyncDoomEntriesSince(const base::Time initial_time) {
+int BackendImplV3::DoomEntriesSince(base::Time initial_time,
+ const CompletionCallback& callback) {
DCHECK_NE(net::APP_CACHE, cache_type_);
if (disabled_)
return net::ERR_FAILED;
@@ -740,19 +667,20 @@ int BackendImpl::SyncDoomEntriesSince(const base::Time initial_time) {
}
}
-int BackendImpl::OpenNextEntry(void** iter, Entry** next_entry,
- const CompletionCallback& callback) {
+int BackendImplV3::OpenNextEntry(void** iter, Entry** next_entry,
+ const CompletionCallback& callback) {
DCHECK(!callback.is_null());
background_queue_.OpenNextEntry(iter, next_entry, callback);
return net::ERR_IO_PENDING;
}
-void BackendImpl::EndEnumeration(void** iter) {
- background_queue_.EndEnumeration(*iter);
+void BackendImplV3::EndEnumeration(void** iter) {
+ scoped_ptr<IndexIterator> iterator(
+ reinterpret_cast<IndexIterator*>(*iter));
*iter = NULL;
}
-void BackendImpl::GetStats(StatsItems* stats) {
+void BackendImplV3::GetStats(StatsItems* stats) {
if (disabled_)
return;
@@ -781,7 +709,7 @@ void BackendImpl::GetStats(StatsItems* stats) {
stats_.GetItems(stats);
}
-void BackendImpl::SyncOnExternalCacheHit(const std::string& key) {
+void BackendImplV3::OnExternalCacheHit(const std::string& key) {
if (disabled_)
return;
@@ -800,7 +728,7 @@ void BackendImpl::SyncOnExternalCacheHit(const std::string& key) {
// The maximum cache size will be either set explicitly by the caller, or
// calculated by this code.
-void BackendImpl::AdjustMaxCacheSize(int table_len) {
+void BackendImplV3::AdjustMaxCacheSize(int table_len) {
if (max_size_)
return;
@@ -833,7 +761,7 @@ void BackendImpl::AdjustMaxCacheSize(int table_len) {
max_size_= current_max_size;
}
-bool BackendImpl::InitStats() {
+bool BackendImplV3::InitStats() {
Addr address(data_->header.stats);
int size = stats_.StorageSize();
@@ -871,7 +799,7 @@ bool BackendImpl::InitStats() {
return true;
}
-void BackendImpl::StoreStats() {
+void BackendImplV3::StoreStats() {
int size = stats_.StorageSize();
scoped_ptr<char[]> data(new char[size]);
Addr address;
@@ -889,7 +817,7 @@ void BackendImpl::StoreStats() {
file->Write(data.get(), size, offset); // ignore result.
}
-void BackendImpl::RestartCache(bool failure) {
+void BackendImplV3::RestartCache(bool failure) {
int64 errors = stats_.GetCounter(Stats::FATAL_ERROR);
int64 full_dooms = stats_.GetCounter(Stats::DOOM_CACHE);
int64 partial_dooms = stats_.GetCounter(Stats::DOOM_RECENT);
@@ -916,13 +844,9 @@ void BackendImpl::RestartCache(bool failure) {
}
}
-void BackendImpl::PrepareForRestart() {
- // Reset the mask_ if it was not given by the user.
- if (!(user_flags_ & kMask))
- mask_ = 0;
-
- if (!(user_flags_ & kNewEviction))
- new_eviction_ = false;
+void BackendImplV3::PrepareForRestart() {
+ if (!(user_flags_ & EVICTION_V2))
+ lru_eviction_ = true;
disabled_ = true;
data_->header.crash = 0;
@@ -935,7 +859,7 @@ void BackendImpl::PrepareForRestart() {
restarted_ = true;
}
-void BackendImpl::CleanupCache() {
+void BackendImplV3::CleanupCache() {
Trace("Backend Cleanup");
eviction_.Stop();
timer_.reset();
@@ -960,7 +884,7 @@ void BackendImpl::CleanupCache() {
done_.Signal();
}
-int BackendImpl::NewEntry(Addr address, EntryImpl** entry) {
+int BackendImplV3::NewEntry(Addr address, EntryImplV3** entry) {
EntriesMap::iterator it = open_entries_.find(address.value());
if (it != open_entries_.end()) {
// Easy job. This entry is already in memory.
@@ -1038,9 +962,11 @@ int BackendImpl::NewEntry(Addr address, EntryImpl** entry) {
}
// This is the actual implementation for OpenNextEntry and OpenPrevEntry.
-EntryImpl* BackendImpl::OpenFollowingEntry(bool forward, void** iter) {
+int BackendImplV3::OpenFollowingEntry(bool forward, void** iter,
+ Entry** next_entry,
+ const CompletionCallback& callback) {
if (disabled_)
- return NULL;
+ return net::ERR_FAILED;
DCHECK(iter);
@@ -1115,23 +1041,23 @@ EntryImpl* BackendImpl::OpenFollowingEntry(bool forward, void** iter) {
return next_entry;
}
-void BackendImpl::AddStorageSize(int32 bytes) {
+void BackendImplV3::AddStorageSize(int32 bytes) {
data_->header.num_bytes += bytes;
DCHECK_GE(data_->header.num_bytes, 0);
}
-void BackendImpl::SubstractStorageSize(int32 bytes) {
+void BackendImplV3::SubstractStorageSize(int32 bytes) {
data_->header.num_bytes -= bytes;
DCHECK_GE(data_->header.num_bytes, 0);
}
-void BackendImpl::IncreaseNumRefs() {
+void BackendImplV3::IncreaseNumRefs() {
num_refs_++;
if (max_refs_ < num_refs_)
max_refs_ = num_refs_;
}
-void BackendImpl::DecreaseNumRefs() {
+void BackendImplV3::DecreaseNumRefs() {
DCHECK(num_refs_);
num_refs_--;
@@ -1140,20 +1066,20 @@ void BackendImpl::DecreaseNumRefs() {
FROM_HERE, base::Bind(&BackendImpl::RestartCache, GetWeakPtr(), true));
}
-void BackendImpl::IncreaseNumEntries() {
- data_->header.num_entries++;
- DCHECK_GT(data_->header.num_entries, 0);
+void BackendImplV3::IncreaseNumEntries() {
+ index_.header()->num_entries++;
+ DCHECK_GT(index_.header()->num_entries, 0);
}
-void BackendImpl::DecreaseNumEntries() {
- data_->header.num_entries--;
- if (data_->header.num_entries < 0) {
+void BackendImplV3::DecreaseNumEntries() {
+ index_.header()->num_entries--;
+ if (index_.header()->num_entries < 0) {
NOTREACHED();
- data_->header.num_entries = 0;
+ index_.header()->num_entries = 0;
}
}
-int BackendImpl::SyncInit() {
+int BackendImplV3::SyncInit() {
#if defined(NET_BUILD_STRESS_CACHE)
// Start evictions right away.
up_ticks_ = kTrimDelay * 2;
@@ -1176,9 +1102,9 @@ int BackendImpl::SyncInit() {
trace_object_ = TraceObject::GetTraceObject();
// Create a recurrent timer of 30 secs.
int timer_delay = unit_test_ ? 1000 : 30000;
- timer_.reset(new base::RepeatingTimer<BackendImpl>());
+ timer_.reset(new base::RepeatingTimer<BackendImplV3>());
timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(timer_delay), this,
- &BackendImpl::OnStatsTimer);
+ &BackendImplV3::OnStatsTimer);
}
init_ = true;
@@ -1258,7 +1184,7 @@ int BackendImpl::SyncInit() {
return disabled_ ? net::ERR_FAILED : net::OK;
}
-EntryImpl* BackendImpl::ResurrectEntry(EntryImpl* deleted_entry) {
+EntryImpl* BackendImplV3::ResurrectEntry(EntryImpl* deleted_entry) {
if (ENTRY_NORMAL == deleted_entry->entry()->Data()->state) {
deleted_entry->Release();
stats_.OnEvent(Stats::CREATE_MISS);
@@ -1277,7 +1203,7 @@ EntryImpl* BackendImpl::ResurrectEntry(EntryImpl* deleted_entry) {
return deleted_entry;
}
-EntryImpl* BackendImpl::CreateEntryImpl(const std::string& key) {
+EntryImpl* BackendImplV3::CreateEntryImpl(const std::string& key) {
if (disabled_ || key.empty())
return NULL;
@@ -1375,7 +1301,7 @@ EntryImpl* BackendImpl::CreateEntryImpl(const std::string& key) {
return cache_entry.get();
}
-void BackendImpl::LogStats() {
+void BackendImplV3::LogStats() {
StatsItems stats;
GetStats(&stats);
@@ -1383,59 +1309,45 @@ void BackendImpl::LogStats() {
VLOG(1) << stats[index].first << ": " << stats[index].second;
}
-void BackendImpl::ReportStats() {
- CACHE_UMA(COUNTS, "Entries", 0, data_->header.num_entries);
+void BackendImplV3::ReportStats() {
+ IndexHeaderV3* header = index_.header();
+ CACHE_UMA(COUNTS, "Entries", header->num_entries);
- int current_size = data_->header.num_bytes / (1024 * 1024);
+ int current_size = header->num_bytes / (1024 * 1024);
int max_size = max_size_ / (1024 * 1024);
- int hit_ratio_as_percentage = stats_.GetHitRatio();
-
- CACHE_UMA(COUNTS_10000, "Size2", 0, current_size);
- // For any bin in HitRatioBySize2, the hit ratio of caches of that size is the
- // ratio of that bin's total count to the count in the same bin in the Size2
- // histogram.
- if (base::RandInt(0, 99) < hit_ratio_as_percentage)
- CACHE_UMA(COUNTS_10000, "HitRatioBySize2", 0, current_size);
- CACHE_UMA(COUNTS_10000, "MaxSize2", 0, max_size);
+
+ CACHE_UMA(COUNTS_10000, "Size", current_size);
+ CACHE_UMA(COUNTS_10000, "MaxSize", max_size);
if (!max_size)
max_size++;
- CACHE_UMA(PERCENTAGE, "UsedSpace", 0, current_size * 100 / max_size);
+ CACHE_UMA(PERCENTAGE, "UsedSpace", current_size * 100 / max_size);
- CACHE_UMA(COUNTS_10000, "AverageOpenEntries2", 0,
+ CACHE_UMA(COUNTS_10000, "AverageOpenEntries",
static_cast<int>(stats_.GetCounter(Stats::OPEN_ENTRIES)));
- CACHE_UMA(COUNTS_10000, "MaxOpenEntries2", 0,
+ CACHE_UMA(COUNTS_10000, "MaxOpenEntries",
static_cast<int>(stats_.GetCounter(Stats::MAX_ENTRIES)));
stats_.SetCounter(Stats::MAX_ENTRIES, 0);
- CACHE_UMA(COUNTS_10000, "TotalFatalErrors", 0,
+ CACHE_UMA(COUNTS_10000, "TotalFatalErrors",
static_cast<int>(stats_.GetCounter(Stats::FATAL_ERROR)));
- CACHE_UMA(COUNTS_10000, "TotalDoomCache", 0,
+ CACHE_UMA(COUNTS_10000, "TotalDoomCache",
static_cast<int>(stats_.GetCounter(Stats::DOOM_CACHE)));
- CACHE_UMA(COUNTS_10000, "TotalDoomRecentEntries", 0,
+ CACHE_UMA(COUNTS_10000, "TotalDoomRecentEntries",
static_cast<int>(stats_.GetCounter(Stats::DOOM_RECENT)));
stats_.SetCounter(Stats::FATAL_ERROR, 0);
stats_.SetCounter(Stats::DOOM_CACHE, 0);
stats_.SetCounter(Stats::DOOM_RECENT, 0);
int64 total_hours = stats_.GetCounter(Stats::TIMER) / 120;
- if (!data_->header.create_time || !data_->header.lru.filled) {
- int cause = data_->header.create_time ? 0 : 1;
- if (!data_->header.lru.filled)
- cause |= 2;
- CACHE_UMA(CACHE_ERROR, "ShortReport", 0, cause);
- CACHE_UMA(HOURS, "TotalTimeNotFull", 0, static_cast<int>(total_hours));
+ if (!(header->flags & CACHE_EVICTED)) {
+ CACHE_UMA(HOURS, "TotalTimeNotFull", static_cast<int>(total_hours));
return;
}
// This is an up to date client that will report FirstEviction() data. After
// that event, start reporting this:
- CACHE_UMA(HOURS, "TotalTime", 0, static_cast<int>(total_hours));
- // For any bin in HitRatioByTotalTime, the hit ratio of caches of that total
- // time is the ratio of that bin's total count to the count in the same bin in
- // the TotalTime histogram.
- if (base::RandInt(0, 99) < hit_ratio_as_percentage)
- CACHE_UMA(HOURS, "HitRatioByTotalTime", 0, implicit_cast<int>(total_hours));
+ CACHE_UMA(HOURS, "TotalTime", static_cast<int>(total_hours));
int64 use_hours = stats_.GetCounter(Stats::LAST_REPORT_TIMER) / 120;
stats_.SetCounter(Stats::LAST_REPORT_TIMER, stats_.GetCounter(Stats::TIMER));
@@ -1445,41 +1357,32 @@ void BackendImpl::ReportStats() {
if (use_hours)
use_hours = total_hours - use_hours;
- if (!use_hours || !GetEntryCount() || !data_->header.num_bytes)
+ if (!use_hours || !GetEntryCount() || !header->num_bytes)
return;
- CACHE_UMA(HOURS, "UseTime", 0, static_cast<int>(use_hours));
- // For any bin in HitRatioByUseTime, the hit ratio of caches of that use time
- // is the ratio of that bin's total count to the count in the same bin in the
- // UseTime histogram.
- if (base::RandInt(0, 99) < hit_ratio_as_percentage)
- CACHE_UMA(HOURS, "HitRatioByUseTime", 0, implicit_cast<int>(use_hours));
- CACHE_UMA(PERCENTAGE, "HitRatio", 0, hit_ratio_as_percentage);
+ CACHE_UMA(HOURS, "UseTime", static_cast<int>(use_hours));
int64 trim_rate = stats_.GetCounter(Stats::TRIM_ENTRY) / use_hours;
- CACHE_UMA(COUNTS, "TrimRate", 0, static_cast<int>(trim_rate));
-
- int avg_size = data_->header.num_bytes / GetEntryCount();
- CACHE_UMA(COUNTS, "EntrySize", 0, avg_size);
- CACHE_UMA(COUNTS, "EntriesFull", 0, data_->header.num_entries);
+ CACHE_UMA(COUNTS, "TrimRate", static_cast<int>(trim_rate));
- CACHE_UMA(PERCENTAGE, "IndexLoad", 0,
- data_->header.num_entries * 100 / (mask_ + 1));
+ int avg_size = header->num_bytes / GetEntryCount();
+ CACHE_UMA(COUNTS, "EntrySize", avg_size);
+ CACHE_UMA(COUNTS, "EntriesFull", header->num_entries);
int large_entries_bytes = stats_.GetLargeEntriesSize();
- int large_ratio = large_entries_bytes * 100 / data_->header.num_bytes;
- CACHE_UMA(PERCENTAGE, "LargeEntriesRatio", 0, large_ratio);
+ int large_ratio = large_entries_bytes * 100 / header->num_bytes;
+ CACHE_UMA(PERCENTAGE, "LargeEntriesRatio", large_ratio);
- if (new_eviction_) {
- CACHE_UMA(PERCENTAGE, "ResurrectRatio", 0, stats_.GetResurrectRatio());
- CACHE_UMA(PERCENTAGE, "NoUseRatio", 0,
- data_->header.lru.sizes[0] * 100 / data_->header.num_entries);
- CACHE_UMA(PERCENTAGE, "LowUseRatio", 0,
- data_->header.lru.sizes[1] * 100 / data_->header.num_entries);
- CACHE_UMA(PERCENTAGE, "HighUseRatio", 0,
- data_->header.lru.sizes[2] * 100 / data_->header.num_entries);
- CACHE_UMA(PERCENTAGE, "DeletedRatio", 0,
- data_->header.lru.sizes[4] * 100 / data_->header.num_entries);
+ if (!lru_eviction_) {
+ CACHE_UMA(PERCENTAGE, "ResurrectRatio", stats_.GetResurrectRatio());
+ CACHE_UMA(PERCENTAGE, "NoUseRatio",
+ header->num_no_use_entries * 100 / header->num_entries);
+ CACHE_UMA(PERCENTAGE, "LowUseRatio",
+ header->num_low_use_entries * 100 / header->num_entries);
+ CACHE_UMA(PERCENTAGE, "HighUseRatio",
+ header->num_high_use_entries * 100 / header->num_entries);
+ CACHE_UMA(PERCENTAGE, "DeletedRatio",
+ header->num_evicted_entries * 100 / header->num_entries);
}
stats_.ResetRatios();
@@ -1489,16 +1392,16 @@ void BackendImpl::ReportStats() {
block_files_.ReportStats();
}
-void BackendImpl::ReportError(int error) {
+void BackendImplV3::ReportError(int error) {
STRESS_DCHECK(!error || error == ERR_PREVIOUS_CRASH ||
error == ERR_CACHE_CREATED);
// We transmit positive numbers, instead of direct error codes.
DCHECK_LE(error, 0);
- CACHE_UMA(CACHE_ERROR, "Error", 0, error * -1);
+ CACHE_UMA(CACHE_ERROR, "Error", error * -1);
}
-bool BackendImpl::CheckIndex() {
+bool BackendImplV3::CheckIndex() {
DCHECK(data_);
size_t current_size = index_->GetLength();
@@ -1561,7 +1464,7 @@ bool BackendImpl::CheckIndex() {
return index_->Read(buf.get(), current_size, 0);
}
-int BackendImpl::CheckAllEntries() {
+int BackendImplV3::CheckAllEntries() {
int num_dirty = 0;
int num_entries = 0;
DCHECK(mask_ < kuint32max);
@@ -1604,7 +1507,7 @@ int BackendImpl::CheckAllEntries() {
return num_dirty;
}
-bool BackendImpl::CheckEntry(EntryImpl* cache_entry) {
+bool BackendImplV3::CheckEntry(EntryImpl* cache_entry) {
bool ok = block_files_.IsValid(cache_entry->entry()->address());
ok = ok && block_files_.IsValid(cache_entry->rankings()->address());
EntryStore* data = cache_entry->entry()->Data();
@@ -1619,7 +1522,7 @@ bool BackendImpl::CheckEntry(EntryImpl* cache_entry) {
return ok && cache_entry->rankings()->VerifyHash();
}
-int BackendImpl::MaxBuffersSize() {
+int BackendImplV3::MaxBuffersSize() {
static int64 total_memory = base::SysInfo::AmountOfPhysicalMemory();
static bool done = false;
@@ -1637,4 +1540,68 @@ int BackendImpl::MaxBuffersSize() {
return static_cast<int>(total_memory);
}
+#endif // defined(V3_NOT_JUST_YET_READY).
+
+bool BackendImplV3::IsAllocAllowed(int current_size, int new_size) {
+ return false;
+}
+
+net::CacheType BackendImplV3::GetCacheType() const {
+ return cache_type_;
+}
+
+int32 BackendImplV3::GetEntryCount() const {
+ return 0;
+}
+
+int BackendImplV3::OpenEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::CreateEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::DoomEntry(const std::string& key,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::DoomAllEntries(const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::DoomEntriesBetween(base::Time initial_time,
+ base::Time end_time,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::DoomEntriesSince(base::Time initial_time,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int BackendImplV3::OpenNextEntry(void** iter, Entry** next_entry,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+void BackendImplV3::EndEnumeration(void** iter) {
+ NOTIMPLEMENTED();
+}
+
+void BackendImplV3::GetStats(StatsItems* stats) {
+ NOTIMPLEMENTED();
+}
+
+void BackendImplV3::OnExternalCacheHit(const std::string& key) {
+ NOTIMPLEMENTED();
+}
+
+void BackendImplV3::CleanupCache() {
+}
+
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/v3/backend_impl_v3.h b/chromium/net/disk_cache/blockfile/backend_impl_v3.h
index 08cc5b1dd52..37f90aaf15d 100644
--- a/chromium/net/disk_cache/v3/backend_impl_v3.h
+++ b/chromium/net/disk_cache/blockfile/backend_impl_v3.h
@@ -4,20 +4,20 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_BACKEND_IMPL_H_
-#define NET_DISK_CACHE_BACKEND_IMPL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_V3_H_
#include "base/containers/hash_tables.h"
#include "base/files/file_path.h"
#include "base/timer/timer.h"
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/blockfile/block_bitmaps_v3.h"
+#include "net/disk_cache/blockfile/block_files.h"
+#include "net/disk_cache/blockfile/eviction_v3.h"
+#include "net/disk_cache/blockfile/index_table_v3.h"
+#include "net/disk_cache/blockfile/stats.h"
+#include "net/disk_cache/blockfile/stress_support.h"
+#include "net/disk_cache/blockfile/trace.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/eviction.h"
-#include "net/disk_cache/in_flight_backend_io.h"
-#include "net/disk_cache/rankings.h"
-#include "net/disk_cache/stats.h"
-#include "net/disk_cache/stress_support.h"
-#include "net/disk_cache/trace.h"
namespace net {
class NetLog;
@@ -25,29 +25,27 @@ class NetLog;
namespace disk_cache {
-enum BackendFlags {
- kNone = 0,
- kMask = 1, // A mask (for the index table) was specified.
- kMaxSize = 1 << 1, // A maximum size was provided.
- kUnitTestMode = 1 << 2, // We are modifying the behavior for testing.
- kUpgradeMode = 1 << 3, // This is the upgrade tool (dump).
- kNewEviction = 1 << 4, // Use of new eviction was specified.
- kNoRandom = 1 << 5, // Don't add randomness to the behavior.
- kNoLoadProtection = 1 << 6, // Don't act conservatively under load.
- kNoBuffering = 1 << 7 // Disable extended IO buffering.
-};
+class EntryImplV3;
// This class implements the Backend interface. An object of this
// class handles the operations of the cache for a particular profile.
-class NET_EXPORT_PRIVATE BackendImpl : public Backend {
- friend class Eviction;
+class NET_EXPORT_PRIVATE BackendImplV3 : public Backend {
public:
- BackendImpl(const base::FilePath& path, base::MessageLoopProxy* cache_thread,
- net::NetLog* net_log);
- // mask can be used to limit the usable size of the hash table, for testing.
- BackendImpl(const base::FilePath& path, uint32 mask,
- base::MessageLoopProxy* cache_thread, net::NetLog* net_log);
- virtual ~BackendImpl();
+ enum BackendFlags {
+ MAX_SIZE = 1 << 1, // A maximum size was provided.
+ UNIT_TEST_MODE = 1 << 2, // We are modifying the behavior for testing.
+ UPGRADE_MODE = 1 << 3, // This is the upgrade tool (dump).
+ EVICTION_V2 = 1 << 4, // Use of new eviction was specified.
+ BASIC_UNIT_TEST = 1 << 5, // Identifies almost all unit tests.
+ NO_LOAD_PROTECTION = 1 << 6, // Don't act conservatively under load.
+ NO_BUFFERING = 1 << 7, // Disable extended IO buffering.
+ NO_CLEAN_ON_EXIT = 1 << 8 // Avoid saving data at exit time.
+ };
+
+ BackendImplV3(const base::FilePath& path,
+ base::MessageLoopProxy* cache_thread,
+ net::NetLog* net_log);
+ virtual ~BackendImplV3();
// Performs general initialization for this current instance of the cache.
int Init(const CompletionCallback& callback);
@@ -67,10 +65,10 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
Addr* block_address);
// Updates the ranking information for an entry.
- void UpdateRank(EntryImpl* entry, bool modified);
+ void UpdateRank(EntryImplV3* entry, bool modified);
// Permanently deletes an entry, but still keeps track of it.
- void InternalDoomEntry(EntryImpl* entry);
+ void InternalDoomEntry(EntryImplV3* entry);
// This method must be called when an entry is released for the last time, so
// the entry should not be used anymore. |address| is the cache address of the
@@ -81,10 +79,10 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
// released.
void OnEntryDestroyEnd();
- // If the data stored by the provided |rankings| points to an open entry,
- // returns a pointer to that entry, otherwise returns NULL. Note that this
- // method does NOT increase the ref counter for the entry.
- EntryImpl* GetOpenEntry(CacheRankingsBlock* rankings) const;
+ // If the |address| corresponds to an open entry, returns a pointer to that
+ // entry, otherwise returns NULL. Note that this method does not increase the
+ // ref counter for the entry.
+ EntryImplV3* GetOpenEntry(Addr address) const;
// Returns the id being used on this run of the cache.
int32 GetCurrentEntryId() const;
@@ -112,10 +110,9 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
// Returns true if this instance seems to be under heavy load.
bool IsLoaded() const;
- // Returns the full histogram name, for the given base |name| and experiment,
- // and the current cache type. The name will be "DiskCache.t.name_e" where n
- // is the cache type and e the provided |experiment|.
- std::string HistogramName(const char* name, int experiment) const;
+ // Returns the full histogram name, for the given base |name| and the current
+ // cache type. The name will be "DiskCache3.name_type".
+ std::string HistogramName(const char* name) const;
net::CacheType cache_type() const {
return cache_type_;
@@ -126,7 +123,7 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
}
// Returns a weak pointer to this object.
- base::WeakPtr<BackendImpl> GetWeakPtr();
+ base::WeakPtr<BackendImplV3> GetWeakPtr();
// Returns true if we should send histograms for this user again. The caller
// must call this function only once per run (because it returns always the
@@ -143,8 +140,8 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
void OnRead(int bytes);
void OnWrite(int bytes);
- // Timer callback to calculate usage statistics.
- void OnStatsTimer();
+ // Timer callback to calculate usage statistics and perform backups.
+ void OnTimerTick();
// Sets internal parameters to enable unit testing mode.
void SetUnitTestMode();
@@ -195,25 +192,28 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
virtual void OnExternalCacheHit(const std::string& key) OVERRIDE;
private:
- typedef base::hash_map<CacheAddr, EntryImpl*> EntriesMap;
-
- void AdjustMaxCacheSize(int table_len);
+ friend class EvictionV3;
+ typedef base::hash_map<CacheAddr, EntryImplV3*> EntriesMap;
+ class Worker;
- bool InitStats();
+ void AdjustMaxCacheSize();
+ bool InitStats(void* stats_data);
void StoreStats();
// Deletes the cache and starts again.
void RestartCache(bool failure);
void PrepareForRestart();
+ // Performs final cleanup.
void CleanupCache();
// Creates a new entry object. Returns zero on success, or a disk_cache error
// on failure.
- int NewEntry(Addr address, EntryImpl** entry);
+ int NewEntry(Addr address, EntryImplV3** entry);
// Opens the next or previous entry on a cache iteration.
- EntryImpl* OpenFollowingEntry(bool forward, void** iter);
+ int OpenFollowingEntry(bool forward, void** iter, Entry** next_entry,
+ const CompletionCallback& callback);
// Handles the used storage count.
void AddStorageSize(int32 bytes);
@@ -241,48 +241,44 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
int CheckAllEntries();
// Part of the self test. Returns false if the entry is corrupt.
- bool CheckEntry(EntryImpl* cache_entry);
+ bool CheckEntry(EntryImplV3* cache_entry);
// Returns the maximum total memory for the memory buffers.
int MaxBuffersSize();
- scoped_refptr<MappedFile> index_; // The main cache index.
+ IndexTable index_;
base::FilePath path_; // Path to the folder used as backing storage.
- BlockFiles block_files_; // Set of files used to store all data.
+ BlockBitmaps block_files_;
int32 max_size_; // Maximum data size for this instance.
- Eviction eviction_; // Handler of the eviction algorithm.
- EntriesMap open_entries_; // Map of open entries.
+ EvictionV3 eviction_; // Handler of the eviction algorithm.
+ EntriesMap open_entries_;
int num_refs_; // Number of referenced cache entries.
int max_refs_; // Max number of referenced cache entries.
int entry_count_; // Number of entries accessed lately.
int byte_count_; // Number of bytes read/written lately.
int buffer_bytes_; // Total size of the temporary entries' buffers.
- int up_ticks_; // The number of timer ticks received (OnStatsTimer).
+ int up_ticks_; // The number of timer ticks received (OnTimerTick).
net::CacheType cache_type_;
int uma_report_; // Controls transmission of UMA data.
uint32 user_flags_; // Flags set by the user.
bool init_; // controls the initialization of the system.
bool restarted_;
- bool unit_test_;
bool read_only_; // Prevents updates of the rankings data (used by tools).
bool disabled_;
- bool new_eviction_; // What eviction algorithm should be used.
+ bool lru_eviction_; // What eviction algorithm should be used.
bool first_timer_; // True if the timer has not been called.
bool user_load_; // True if we see a high load coming from the caller.
net::NetLog* net_log_;
Stats stats_; // Usage statistics.
- scoped_ptr<base::RepeatingTimer<BackendImpl> > timer_; // Usage timer.
+ scoped_ptr<base::RepeatingTimer<BackendImplV3> > timer_; // Usage timer.
scoped_refptr<TraceObject> trace_object_; // Initializes internal tracing.
- base::WeakPtrFactory<BackendImpl> ptr_factory_;
+ base::WeakPtrFactory<BackendImplV3> ptr_factory_;
- DISALLOW_COPY_AND_ASSIGN(BackendImpl);
+ DISALLOW_COPY_AND_ASSIGN(BackendImplV3);
};
-// Returns the preferred max cache size given the available disk space.
-NET_EXPORT_PRIVATE int PreferedCacheSize(int64 available);
-
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BACKEND_IMPL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BACKEND_IMPL_V3_H_
diff --git a/chromium/net/disk_cache/v3/backend_worker.cc b/chromium/net/disk_cache/blockfile/backend_worker_v3.cc
index be12a96eebe..ab432c4a397 100644
--- a/chromium/net/disk_cache/v3/backend_worker.cc
+++ b/chromium/net/disk_cache/blockfile/backend_worker_v3.cc
@@ -2,34 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_worker_v3.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
-#include "base/hash.h"
#include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/stats_counters.h"
-#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/sys_info.h"
-#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/errors.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/file.h"
-
-// This has to be defined before including histogram_macros.h from this file.
-#define NET_DISK_CACHE_BACKEND_IMPL_CC_
-#include "net/disk_cache/histogram_macros.h"
+#include "net/disk_cache/blockfile/errors.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/file.h"
using base::Time;
using base::TimeDelta;
@@ -37,6 +24,8 @@ using base::TimeTicks;
namespace {
+#if defined(V3_NOT_JUST_YET_READY)
+
const char* kIndexName = "index";
// Seems like ~240 MB correspond to less than 50k entries for 99% of the people.
@@ -96,6 +85,7 @@ bool InitExperiment(disk_cache::IndexHeader* header, bool cache_created) {
header->experiment = disk_cache::NO_EXPERIMENT;
return true;
}
+#endif // defined(V3_NOT_JUST_YET_READY).
} // namespace
@@ -103,31 +93,15 @@ bool InitExperiment(disk_cache::IndexHeader* header, bool cache_created) {
namespace disk_cache {
-BackendImpl::BackendImpl(const base::FilePath& path,
- base::MessageLoopProxy* cache_thread,
- net::NetLog* net_log)
- : background_queue_(this, cache_thread),
- path_(path),
- block_files_(path),
- mask_(0),
- max_size_(0),
- up_ticks_(0),
- cache_type_(net::DISK_CACHE),
- uma_report_(0),
- user_flags_(0),
- init_(false),
- restarted_(false),
- unit_test_(false),
- read_only_(false),
- disabled_(false),
- new_eviction_(false),
- first_timer_(true),
- user_load_(false),
- net_log_(net_log),
- done_(true, false),
- ptr_factory_(this) {
+BackendImplV3::Worker::Worker(const base::FilePath& path,
+ base::MessageLoopProxy* main_thread)
+ : path_(path),
+ block_files_(path),
+ init_(false) {
}
+#if defined(V3_NOT_JUST_YET_READY)
+
int BackendImpl::SyncInit() {
#if defined(NET_BUILD_STRESS_CACHE)
// Start evictions right away.
@@ -482,4 +456,13 @@ bool BackendImpl::InitStats() {
return true;
}
+#endif // defined(V3_NOT_JUST_YET_READY).
+
+int BackendImplV3::Worker::Init(const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+BackendImplV3::Worker::~Worker() {
+}
+
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/v3/backend_worker.h b/chromium/net/disk_cache/blockfile/backend_worker_v3.h
index 42fd4b232f8..d0829b96345 100644
--- a/chromium/net/disk_cache/v3/backend_worker.h
+++ b/chromium/net/disk_cache/blockfile/backend_worker_v3.h
@@ -4,35 +4,28 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_BACKEND_IMPL_H_
-#define NET_DISK_CACHE_BACKEND_IMPL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BACKEND_WORKER_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_BACKEND_WORKER_V3_H_
#include "base/containers/hash_tables.h"
#include "base/files/file_path.h"
-#include "base/timer/timer.h"
-#include "net/disk_cache/block_files.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/eviction.h"
-#include "net/disk_cache/in_flight_backend_io.h"
-#include "net/disk_cache/rankings.h"
-#include "net/disk_cache/stats.h"
-#include "net/disk_cache/stress_support.h"
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/backend_impl_v3.h"
+#include "net/disk_cache/blockfile/block_files.h"
namespace disk_cache {
-// This class implements the Backend interface. An object of this
-// class handles the operations of the cache for a particular profile.
-class NET_EXPORT_PRIVATE BackendImpl : public Backend {
- friend class Eviction;
+class BackendImplV3::Worker : public base::RefCountedThreadSafe<Worker> {
public:
- BackendImpl(const base::FilePath& path, base::MessageLoopProxy* cache_thread,
- net::NetLog* net_log);
+ Worker(const base::FilePath& path, base::MessageLoopProxy* main_thread);
// Performs general initialization for this current instance of the cache.
int Init(const CompletionCallback& callback);
private:
+ friend class base::RefCountedThreadSafe<Worker>;
+
+ ~Worker();
void CleanupCache();
// Returns the full name for an external storage file.
@@ -42,9 +35,6 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
bool CreateBackingStore(disk_cache::File* file);
bool InitBackingStore(bool* file_created);
- // Reports an uncommon, recoverable error.
- void ReportError(int error);
-
// Performs basic checks on the index file. Returns false on failure.
bool CheckIndex();
@@ -52,9 +42,9 @@ class NET_EXPORT_PRIVATE BackendImpl : public Backend {
BlockFiles block_files_; // Set of files used to store all data.
bool init_; // controls the initialization of the system.
- DISALLOW_COPY_AND_ASSIGN(BackendImpl);
+ DISALLOW_COPY_AND_ASSIGN(Worker);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BACKEND_IMPL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BACKEND_WORKER_V3_H_
diff --git a/chromium/net/disk_cache/bitmap.cc b/chromium/net/disk_cache/blockfile/bitmap.cc
index 6d469dfe3dc..cfbf8460d84 100644
--- a/chromium/net/disk_cache/bitmap.cc
+++ b/chromium/net/disk_cache/blockfile/bitmap.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/bitmap.h"
+#include "net/disk_cache/blockfile/bitmap.h"
#include <algorithm>
diff --git a/chromium/net/disk_cache/bitmap.h b/chromium/net/disk_cache/blockfile/bitmap.h
index 81c434c39b4..dc05157f7a4 100644
--- a/chromium/net/disk_cache/bitmap.h
+++ b/chromium/net/disk_cache/blockfile/bitmap.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_BITMAP_H_
-#define NET_DISK_CACHE_BITMAP_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BITMAP_H_
+#define NET_DISK_CACHE_BLOCKFILE_BITMAP_H_
#include "base/basictypes.h"
#include "net/base/net_export.h"
@@ -133,4 +133,4 @@ class NET_EXPORT_PRIVATE Bitmap {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BITMAP_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BITMAP_H_
diff --git a/chromium/net/disk_cache/bitmap_unittest.cc b/chromium/net/disk_cache/blockfile/bitmap_unittest.cc
index d80ea742682..a0748bf81a5 100644
--- a/chromium/net/disk_cache/bitmap_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/bitmap_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/bitmap.h"
+#include "net/disk_cache/blockfile/bitmap.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(BitmapTest, OverAllocate) {
diff --git a/chromium/net/disk_cache/v3/block_bitmaps.cc b/chromium/net/disk_cache/blockfile/block_bitmaps_v3.cc
index b68ecdd14f9..984ce491404 100644
--- a/chromium/net/disk_cache/v3/block_bitmaps.cc
+++ b/chromium/net/disk_cache/blockfile/block_bitmaps_v3.cc
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/v3/block_bitmaps.h"
+#include "net/disk_cache/blockfile/block_bitmaps_v3.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
-#include "net/disk_cache/disk_format_base.h"
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
+#include "net/disk_cache/blockfile/trace.h"
using base::TimeTicks;
diff --git a/chromium/net/disk_cache/v3/block_bitmaps.h b/chromium/net/disk_cache/blockfile/block_bitmaps_v3.h
index 111d57b0b13..40eefb14a74 100644
--- a/chromium/net/disk_cache/v3/block_bitmaps.h
+++ b/chromium/net/disk_cache/blockfile/block_bitmaps_v3.h
@@ -4,13 +4,13 @@
// See net/disk_cache/disk_cache.h for the public interface.
-#ifndef NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
-#define NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BLOCK_BITMAPS_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_BLOCK_BITMAPS_V3_H_
#include "base/files/file_path.h"
#include "net/base/net_export.h"
-#include "net/disk_cache/addr.h"
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/block_files.h"
namespace disk_cache {
@@ -62,4 +62,4 @@ class NET_EXPORT_PRIVATE BlockBitmaps {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BLOCK_BITMAPS_V3_H_
diff --git a/chromium/net/disk_cache/v3/block_bitmaps_unittest.cc b/chromium/net/disk_cache/blockfile/block_bitmaps_v3_unittest.cc
index 981bdecfcea..bf22d8824f6 100644
--- a/chromium/net/disk_cache/v3/block_bitmaps_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/block_bitmaps_v3_unittest.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/addr.h"
-#include "net/disk_cache/block_files.h"
-#include "net/disk_cache/disk_format_base.h"
-#include "net/disk_cache/v3/block_bitmaps.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/block_bitmaps_v3.h"
+#include "net/disk_cache/blockfile/block_files.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
#include "testing/gtest/include/gtest/gtest.h"
// Tests that we add and remove blocks correctly.
diff --git a/chromium/net/disk_cache/block_files.cc b/chromium/net/disk_cache/blockfile/block_files.cc
index d8d13e8f2bf..ae2e1b32e0b 100644
--- a/chromium/net/disk_cache/block_files.cc
+++ b/chromium/net/disk_cache/blockfile/block_files.cc
@@ -2,7 +2,7 @@
// 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/blockfile/block_files.h"
#include "base/atomicops.h"
#include "base/files/file_path.h"
@@ -11,9 +11,10 @@
#include "base/strings/stringprintf.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
+#include "net/disk_cache/blockfile/file_lock.h"
+#include "net/disk_cache/blockfile/stress_support.h"
+#include "net/disk_cache/blockfile/trace.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/file_lock.h"
-#include "net/disk_cache/trace.h"
using base::TimeTicks;
@@ -63,7 +64,7 @@ bool BlockHeader::CreateMapBlock(int size, int* index) {
}
if (!target) {
- NOTREACHED();
+ STRESS_NOTREACHED();
return false;
}
@@ -83,7 +84,7 @@ bool BlockHeader::CreateMapBlock(int size, int* index) {
disk_cache::FileLock lock(header_);
int index_offset = j * 4 + 4 - target;
*index = current * 32 + index_offset;
- DLOG_IF(ERROR, *index / 4 != (*index + size - 1) / 4) << "Bit mismatch";
+ STRESS_DCHECK(*index / 4 == (*index + size - 1) / 4);
uint32 to_add = ((1 << size) - 1) << index_offset;
header_->num_entries++;
@@ -96,7 +97,7 @@ bool BlockHeader::CreateMapBlock(int size, int* index) {
header_->hints[target - 1] = current;
header_->empty[target - 1]--;
- DCHECK_GE(header_->empty[target - 1], 0);
+ STRESS_DCHECK(header_->empty[target - 1] >= 0);
if (target != size) {
header_->empty[target - size - 1]++;
}
@@ -133,20 +134,20 @@ void BlockHeader::DeleteMapBlock(int index, int size) {
int new_type = GetMapBlockType(new_value);
disk_cache::FileLock lock(header_);
- DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
+ STRESS_DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
uint8 to_clear = ((1 << size) - 1) << (index % 8);
- DCHECK((byte_map[byte_index] & to_clear) == to_clear);
+ STRESS_DCHECK((byte_map[byte_index] & to_clear) == to_clear);
byte_map[byte_index] &= ~to_clear;
if (update_counters) {
if (bits_at_end)
header_->empty[bits_at_end - 1]--;
header_->empty[new_type - 1]++;
- DCHECK_GE(header_->empty[bits_at_end - 1], 0);
+ STRESS_DCHECK(header_->empty[bits_at_end - 1] >= 0);
}
base::subtle::MemoryBarrier();
header_->num_entries--;
- DCHECK_GE(header_->num_entries, 0);
+ STRESS_DCHECK(header_->num_entries >= 0);
HISTOGRAM_TIMES("DiskCache.DeleteBlock", TimeTicks::Now() - start);
}
@@ -162,7 +163,7 @@ bool BlockHeader::UsedMapBlock(int index, int size) {
if (index % 8 >= 4)
map_block >>= 4;
- DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
+ STRESS_DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
uint8 to_clear = ((1 << size) - 1) << (index % 8);
return ((byte_map[byte_index] & to_clear) == to_clear);
}
@@ -444,12 +445,10 @@ bool BlockFiles::IsValid(Addr address) {
bool BlockFiles::CreateBlockFile(int index, FileType file_type, bool force) {
base::FilePath name = Name(index);
- int flags =
- force ? base::PLATFORM_FILE_CREATE_ALWAYS : base::PLATFORM_FILE_CREATE;
- flags |= base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE;
+ int flags = force ? base::File::FLAG_CREATE_ALWAYS : base::File::FLAG_CREATE;
+ flags |= base::File::FLAG_WRITE | base::File::FLAG_EXCLUSIVE_WRITE;
- scoped_refptr<File> file(new File(
- base::CreatePlatformFile(name, flags, NULL, NULL)));
+ scoped_refptr<File> file(new File(base::File(name, flags)));
if (!file->IsValid())
return false;
@@ -507,9 +506,8 @@ bool BlockFiles::OpenBlockFile(int index) {
}
if (index == 0) {
- // Load the links file into memory with a single read.
- scoped_ptr<char[]> buf(new char[file_len]);
- if (!file->Read(buf.get(), file_len, 0))
+ // Load the links file into memory.
+ if (!file->Preload())
return false;
}
diff --git a/chromium/net/disk_cache/block_files.h b/chromium/net/disk_cache/blockfile/block_files.h
index f8d5483a0b3..f84c314944a 100644
--- a/chromium/net/disk_cache/block_files.h
+++ b/chromium/net/disk_cache/blockfile/block_files.h
@@ -4,8 +4,8 @@
// 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_
+#ifndef NET_DISK_CACHE_BLOCKFILE_BLOCK_FILES_H_
+#define NET_DISK_CACHE_BLOCKFILE_BLOCK_FILES_H_
#include <vector>
@@ -13,9 +13,9 @@
#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/disk_format_base.h"
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
namespace base {
class ThreadChecker;
@@ -166,4 +166,4 @@ class NET_EXPORT_PRIVATE BlockFiles {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BLOCK_FILES_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_BLOCK_FILES_H_
diff --git a/chromium/net/disk_cache/block_files_unittest.cc b/chromium/net/disk_cache/blockfile/block_files_unittest.cc
index 4a095c992ec..bbd89814a42 100644
--- a/chromium/net/disk_cache/block_files_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/block_files_unittest.cc
@@ -4,7 +4,7 @@
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/blockfile/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"
@@ -25,7 +25,7 @@ int NumberOfFiles(const base::FilePath& path) {
return count;
}
-} // namespace;
+} // namespace
namespace disk_cache {
@@ -270,7 +270,7 @@ TEST_F(DiskCacheTest, BlockFiles_InvalidFile) {
char header[kBlockHeaderSize];
memset(header, 'a', kBlockHeaderSize);
EXPECT_EQ(kBlockHeaderSize,
- file_util::WriteFile(filename, header, kBlockHeaderSize));
+ base::WriteFile(filename, header, kBlockHeaderSize));
EXPECT_TRUE(NULL == files.GetFile(addr));
diff --git a/chromium/net/disk_cache/disk_cache_perftest.cc b/chromium/net/disk_cache/blockfile/disk_cache_perftest.cc
index 6adc4bca709..571cea2ee25 100644
--- a/chromium/net/disk_cache/disk_cache_perftest.cc
+++ b/chromium/net/disk_cache/blockfile/disk_cache_perftest.cc
@@ -17,8 +17,8 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/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"
@@ -183,15 +183,15 @@ TEST_F(DiskCacheTest, CacheBackendPerformance) {
base::MessageLoop::current()->RunUntilIdle();
cache.reset();
- ASSERT_TRUE(file_util::EvictFileFromSystemCache(
+ ASSERT_TRUE(base::EvictFileFromSystemCache(
cache_path_.AppendASCII("index")));
- ASSERT_TRUE(file_util::EvictFileFromSystemCache(
+ ASSERT_TRUE(base::EvictFileFromSystemCache(
cache_path_.AppendASCII("data_0")));
- ASSERT_TRUE(file_util::EvictFileFromSystemCache(
+ ASSERT_TRUE(base::EvictFileFromSystemCache(
cache_path_.AppendASCII("data_1")));
- ASSERT_TRUE(file_util::EvictFileFromSystemCache(
+ ASSERT_TRUE(base::EvictFileFromSystemCache(
cache_path_.AppendASCII("data_2")));
- ASSERT_TRUE(file_util::EvictFileFromSystemCache(
+ ASSERT_TRUE(base::EvictFileFromSystemCache(
cache_path_.AppendASCII("data_3")));
rv = disk_cache::CreateCacheBackend(
diff --git a/chromium/net/disk_cache/disk_format.cc b/chromium/net/disk_cache/blockfile/disk_format.cc
index 5b08954e088..4d398c28aa6 100644
--- a/chromium/net/disk_cache/disk_format.cc
+++ b/chromium/net/disk_cache/blockfile/disk_format.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/disk_format.h"
+#include "net/disk_cache/blockfile/disk_format.h"
namespace disk_cache {
diff --git a/chromium/net/disk_cache/disk_format.h b/chromium/net/disk_cache/blockfile/disk_format.h
index 5d7597ab17d..95ac58b6274 100644
--- a/chromium/net/disk_cache/disk_format.h
+++ b/chromium/net/disk_cache/blockfile/disk_format.h
@@ -42,12 +42,12 @@
// being currently used, it means that the entry was not properly closed on a
// previous run, so it is discarded.
-#ifndef NET_DISK_CACHE_DISK_FORMAT_H_
-#define NET_DISK_CACHE_DISK_FORMAT_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_H_
+#define NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_H_
#include "base/basictypes.h"
#include "net/base/net_export.h"
-#include "net/disk_cache/disk_format_base.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
namespace disk_cache {
@@ -150,4 +150,4 @@ COMPILE_ASSERT(sizeof(RankingsNode) == 36, bad_RankingsNode);
} // namespace disk_cache
-#endif // NET_DISK_CACHE_DISK_FORMAT_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_H_
diff --git a/chromium/net/disk_cache/disk_format_base.h b/chromium/net/disk_cache/blockfile/disk_format_base.h
index 31983817fcf..9f4c16e4fd4 100644
--- a/chromium/net/disk_cache/disk_format_base.h
+++ b/chromium/net/disk_cache/blockfile/disk_format_base.h
@@ -17,8 +17,8 @@
// chain of files is, any given block can be located directly by its address,
// which contains the file number and starting block inside the file.
-#ifndef NET_DISK_CACHE_DISK_FORMAT_BASE_H_
-#define NET_DISK_CACHE_DISK_FORMAT_BASE_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_BASE_H_
+#define NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_BASE_H_
#include "base/basictypes.h"
#include "net/base/net_export.h"
@@ -33,6 +33,7 @@ const uint32 kBlockCurrentVersion = 0x30000; // Version 3.0.
const uint32 kBlockMagic = 0xC104CAC3;
const int kBlockHeaderSize = 8192; // Two pages: almost 64k entries
const int kMaxBlocks = (kBlockHeaderSize - 80) * 8;
+const int kNumExtraBlocks = 1024; // How fast files grow.
// Bitmap to track used blocks on a block-file.
typedef uint32 AllocBitmap[kMaxBlocks / 32];
@@ -128,4 +129,4 @@ COMPILE_ASSERT(sizeof(SparseData) == sizeof(SparseHeader) + kNumSparseBits / 8,
} // namespace disk_cache
-#endif // NET_DISK_CACHE_DISK_FORMAT_BASE_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_BASE_H_
diff --git a/chromium/net/disk_cache/v3/disk_format_v3.h b/chromium/net/disk_cache/blockfile/disk_format_v3.h
index 56163770cfa..f5811cc0fc4 100644
--- a/chromium/net/disk_cache/v3/disk_format_v3.h
+++ b/chromium/net/disk_cache/blockfile/disk_format_v3.h
@@ -37,23 +37,28 @@
// internal structures are modified, so it is possible to detect (most of the
// time) when the process dies in the middle of an update. There are dedicated
// backup files for cache bitmaps, used to detect entries out of date.
+//
+// Although cache files are to be consumed on the same machine that creates
+// them, if files are to be moved accross machines, little endian storage is
+// assumed.
-#ifndef NET_DISK_CACHE_V3_DISK_FORMAT_V3_H_
-#define NET_DISK_CACHE_V3_DISK_FORMAT_V3_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_V3_H_
#include "base/basictypes.h"
-#include "net/disk_cache/disk_format_base.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
namespace disk_cache {
-const int kBaseTableLen = 0x10000;
+const int kBaseTableLen = 0x400;
const uint32 kIndexMagicV3 = 0xC103CAC3;
const uint32 kVersion3 = 0x30000; // Version 3.0.
// Flags for a given cache.
enum CacheFlags {
- CACHE_EVICTION_2 = 1, // Keep multiple lists for eviction.
- CACHE_EVICTED = 1 << 1 // Already evicted at least one entry.
+ SMALL_CACHE = 1 << 0, // See IndexCell.
+ CACHE_EVICTION_2 = 1 << 1, // Keep multiple lists for eviction.
+ CACHE_EVICTED = 1 << 2 // Already evicted at least one entry.
};
// Header for the master index file.
@@ -119,30 +124,81 @@ COMPILE_ASSERT(ENTRY_USED <= 7, group_uses_3_bits);
struct IndexCell {
void Clear() { memset(this, 0, sizeof(*this)); }
- uint64 address : 22;
- uint64 hash : 18;
- uint64 timestamp : 20;
- uint64 reuse : 4;
- uint8 state : 3;
- uint8 group : 3;
- uint8 sum : 2;
+ // A cell is a 9 byte bit-field that stores 7 values:
+ // location : 22 bits
+ // id : 18 bits
+ // timestamp : 20 bits
+ // reuse : 4 bits
+ // state : 3 bits
+ // group : 3 bits
+ // sum : 2 bits
+ // The id is derived from the full hash of the entry.
+ //
+ // The actual layout is as follows:
+ //
+ // first_part (low order 32 bits):
+ // 0000 0000 0011 1111 1111 1111 1111 1111 : location
+ // 1111 1111 1100 0000 0000 0000 0000 0000 : id
+ //
+ // first_part (high order 32 bits):
+ // 0000 0000 0000 0000 0000 0000 1111 1111 : id
+ // 0000 1111 1111 1111 1111 1111 0000 0000 : timestamp
+ // 1111 0000 0000 0000 0000 0000 0000 0000 : reuse
+ //
+ // last_part:
+ // 0000 0111 : state
+ // 0011 1000 : group
+ // 1100 0000 : sum
+ //
+ // The small-cache version of the format moves some bits from the location to
+ // the id fileds, like so:
+ // location : 16 bits
+ // id : 24 bits
+ //
+ // first_part (low order 32 bits):
+ // 0000 0000 0000 0000 1111 1111 1111 1111 : location
+ // 1111 1111 1111 1111 0000 0000 0000 0000 : id
+ //
+ // The actual bit distribution between location and id is determined by the
+ // table size (IndexHeaderV3.table_len). Tables smaller than 65536 entries
+ // use the small-cache version; after that size, caches should have the
+ // SMALL_CACHE flag cleared.
+ //
+ // To locate a given entry after recovering the location from the cell, the
+ // file type and file number are appended (see disk_cache/addr.h). For a large
+ // table only the file type is implied; for a small table, the file number
+ // is also implied, and it should be the first file for that type of entry,
+ // as determined by the EntryGroup (two files in total, one for active entries
+ // and another one for evicted entries).
+ //
+ // For example, a small table may store something like 0x1234 as the location
+ // field. That means it stores the entry number 0x1234. If that record belongs
+ // to a deleted entry, the regular cache address may look something like
+ // BLOCK_EVICTED + 1 block + file number 6 + entry number 0x1234
+ // so Addr = 0xf0061234
+ //
+ // If that same Addr is stored on a large table, the location field would be
+ // 0x61234
+
+ uint64 first_part;
+ uint8 last_part;
};
COMPILE_ASSERT(sizeof(IndexCell) == 9, bad_IndexCell);
+const int kCellsPerBucket = 4;
struct IndexBucket {
- IndexCell cells[4];
+ IndexCell cells[kCellsPerBucket];
int32 next;
- uint32 hash : 24; // The last byte is only defined for buckets of
- uint32 reserved : 8; // the extra table.
+ uint32 hash; // The high order byte is reserved (should be zero).
};
COMPILE_ASSERT(sizeof(IndexBucket) == 44, bad_IndexBucket);
-const int kBytesPerCell = 44 / 4;
+const int kBytesPerCell = 44 / kCellsPerBucket;
// The main cache index. Backed by a file named index_tb1.
// The extra table (index_tb2) has a similar format, but different size.
struct Index {
// Default size. Actual size controlled by header.table_len.
- IndexBucket table[kBaseTableLen / 4];
+ IndexBucket table[kBaseTableLen / kCellsPerBucket];
};
#pragma pack(pop)
@@ -187,4 +243,4 @@ COMPILE_ASSERT(sizeof(ShortEntryRecord) == 48, bad_ShortEntryRecord);
} // namespace disk_cache
-#endif // NET_DISK_CACHE_V3_DISK_FORMAT_V3_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_DISK_FORMAT_V3_H_
diff --git a/chromium/net/disk_cache/entry_impl.cc b/chromium/net/disk_cache/blockfile/entry_impl.cc
index 4b6e4cf2b04..6ff52358485 100644
--- a/chromium/net/disk_cache/entry_impl.cc
+++ b/chromium/net/disk_cache/blockfile/entry_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/entry_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
#include "base/hash.h"
#include "base/message_loop/message_loop.h"
@@ -10,13 +10,16 @@
#include "base/strings/string_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/bitmap.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/bitmap.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+#include "net/disk_cache/blockfile/sparse_control.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/histogram_macros.h"
#include "net/disk_cache/net_log_parameters.h"
-#include "net/disk_cache/sparse_control.h"
+
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
using base::Time;
using base::TimeDelta;
@@ -59,7 +62,7 @@ class SyncCallback: public disk_cache::FileIOCallback {
void SyncCallback::OnFileIOComplete(int bytes_copied) {
entry_->DecrementIoCount();
if (!callback_.is_null()) {
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().EndEvent(
end_event_type_,
disk_cache::CreateNetLogReadWriteCompleteCallback(bytes_copied));
@@ -312,7 +315,7 @@ void EntryImpl::DoomImpl() {
int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, false));
@@ -320,7 +323,7 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalReadData(index, offset, buf, buf_len, callback);
- if (result != net::ERR_IO_PENDING && net_log_.IsLoggingAllEvents()) {
+ if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteCompleteCallback(result));
@@ -331,7 +334,7 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback,
bool truncate) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate));
@@ -340,7 +343,7 @@ int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalWriteData(index, offset, buf, buf_len, callback,
truncate);
- if (result != net::ERR_IO_PENDING && net_log_.IsLoggingAllEvents()) {
+ if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteCompleteCallback(result));
diff --git a/chromium/net/disk_cache/entry_impl.h b/chromium/net/disk_cache/blockfile/entry_impl.h
index 13d077c2f8e..c0b332b7168 100644
--- a/chromium/net/disk_cache/entry_impl.h
+++ b/chromium/net/disk_cache/blockfile/entry_impl.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_ENTRY_IMPL_H_
-#define NET_DISK_CACHE_ENTRY_IMPL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_H_
+#define NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_H_
#include "base/memory/scoped_ptr.h"
#include "net/base/net_log.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/storage_block-inl.h"
+#include "net/disk_cache/blockfile/storage_block.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/storage_block.h"
-#include "net/disk_cache/storage_block-inl.h"
namespace disk_cache {
@@ -275,4 +275,4 @@ class NET_EXPORT_PRIVATE EntryImpl
} // namespace disk_cache
-#endif // NET_DISK_CACHE_ENTRY_IMPL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_H_
diff --git a/chromium/net/disk_cache/v3/entry_impl_v3.cc b/chromium/net/disk_cache/blockfile/entry_impl_v3.cc
index 35b2e5644a7..c8898e069d6 100644
--- a/chromium/net/disk_cache/v3/entry_impl_v3.cc
+++ b/chromium/net/disk_cache/blockfile/entry_impl_v3.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/entry_impl.h"
+#include "net/disk_cache/blockfile/entry_impl_v3.h"
#include "base/hash.h"
#include "base/message_loop/message_loop.h"
@@ -10,12 +10,16 @@
#include "base/strings/string_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/bitmap.h"
+#include "net/disk_cache/blockfile/backend_impl_v3.h"
+#include "net/disk_cache/blockfile/bitmap.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+#include "net/disk_cache/blockfile/histogram_macros_v3.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/histogram_macros.h"
#include "net/disk_cache/net_log_parameters.h"
-#include "net/disk_cache/sparse_control.h"
+// #include "net/disk_cache/blockfile/sparse_control_v3.h"
+
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
using base::Time;
using base::TimeDelta;
@@ -29,14 +33,17 @@ const int kMaxBufferSize = 1024 * 1024; // 1 MB.
namespace disk_cache {
+typedef StorageBlock<EntryRecord> CacheEntryBlockV3;
+typedef StorageBlock<ShortEntryRecord> CacheShortEntryBlock;
+
// This class handles individual memory buffers that store data before it is
// sent to disk. The buffer can start at any offset, but if we try to write to
// anywhere in the first 16KB of the file (kMaxBlockSize), we set the offset to
// zero. The buffer grows up to a size determined by the backend, to keep the
// total memory used under control.
-class EntryImpl::UserBuffer {
+class EntryImplV3::UserBuffer {
public:
- explicit UserBuffer(BackendImpl* backend)
+ explicit UserBuffer(BackendImplV3* backend)
: backend_(backend->GetWeakPtr()), offset_(0), grow_allowed_(true) {
buffer_.reserve(kMaxBlockSize);
}
@@ -75,14 +82,14 @@ class EntryImpl::UserBuffer {
int capacity() { return static_cast<int>(buffer_.capacity()); }
bool GrowBuffer(int required, int limit);
- base::WeakPtr<BackendImpl> backend_;
+ base::WeakPtr<BackendImplV3> backend_;
int offset_;
std::vector<char> buffer_;
bool grow_allowed_;
DISALLOW_COPY_AND_ASSIGN(UserBuffer);
};
-bool EntryImpl::UserBuffer::PreWrite(int offset, int len) {
+bool EntryImplV3::UserBuffer::PreWrite(int offset, int len) {
DCHECK_GE(offset, 0);
DCHECK_GE(len, 0);
DCHECK_GE(offset + len, 0);
@@ -104,7 +111,7 @@ bool EntryImpl::UserBuffer::PreWrite(int offset, int len) {
return GrowBuffer(required, kMaxBufferSize * 6 / 5);
}
-void EntryImpl::UserBuffer::Truncate(int offset) {
+void EntryImplV3::UserBuffer::Truncate(int offset) {
DCHECK_GE(offset, 0);
DCHECK_GE(offset, offset_);
DVLOG(3) << "Buffer truncate at " << offset << " current " << offset_;
@@ -114,7 +121,7 @@ void EntryImpl::UserBuffer::Truncate(int offset) {
buffer_.resize(offset);
}
-void EntryImpl::UserBuffer::Write(int offset, IOBuffer* buf, int len) {
+void EntryImplV3::UserBuffer::Write(int offset, IOBuffer* buf, int len) {
DCHECK_GE(offset, 0);
DCHECK_GE(len, 0);
DCHECK_GE(offset + len, 0);
@@ -146,7 +153,7 @@ void EntryImpl::UserBuffer::Write(int offset, IOBuffer* buf, int len) {
buffer_.insert(buffer_.end(), buffer, buffer + len);
}
-bool EntryImpl::UserBuffer::PreRead(int eof, int offset, int* len) {
+bool EntryImplV3::UserBuffer::PreRead(int eof, int offset, int* len) {
DCHECK_GE(offset, 0);
DCHECK_GT(*len, 0);
@@ -171,7 +178,7 @@ bool EntryImpl::UserBuffer::PreRead(int eof, int offset, int* len) {
return (offset - offset_ < Size());
}
-int EntryImpl::UserBuffer::Read(int offset, IOBuffer* buf, int len) {
+int EntryImplV3::UserBuffer::Read(int offset, IOBuffer* buf, int len) {
DCHECK_GE(offset, 0);
DCHECK_GT(len, 0);
DCHECK(Size() || offset < offset_);
@@ -196,7 +203,7 @@ int EntryImpl::UserBuffer::Read(int offset, IOBuffer* buf, int len) {
return len + clean_bytes;
}
-void EntryImpl::UserBuffer::Reset() {
+void EntryImplV3::UserBuffer::Reset() {
if (!grow_allowed_) {
if (backend_)
backend_->BufferDeleted(capacity() - kMaxBlockSize);
@@ -209,7 +216,7 @@ void EntryImpl::UserBuffer::Reset() {
buffer_.clear();
}
-bool EntryImpl::UserBuffer::GrowBuffer(int required, int limit) {
+bool EntryImplV3::UserBuffer::GrowBuffer(int required, int limit) {
DCHECK_GE(required, 0);
int current_size = capacity();
if (required <= current_size)
@@ -237,18 +244,22 @@ bool EntryImpl::UserBuffer::GrowBuffer(int required, int limit) {
// ------------------------------------------------------------------------
-EntryImpl::EntryImpl(BackendImpl* backend, Addr address, bool read_only)
- : entry_(NULL, Addr(0)), node_(NULL, Addr(0)),
- backend_(backend->GetWeakPtr()), doomed_(false), read_only_(read_only),
- dirty_(false) {
- entry_.LazyInit(backend->File(address), address);
+EntryImplV3::EntryImplV3(BackendImplV3* backend, Addr address, bool read_only)
+ : backend_(backend->GetWeakPtr()),
+ address_(address),
+ doomed_(false),
+ read_only_(read_only),
+ dirty_(true),
+ modified_(false) {
for (int i = 0; i < kNumStreams; i++) {
unreported_size_[i] = 0;
}
}
-bool EntryImpl::CreateEntry(Addr node_address, const std::string& key,
- uint32 hash) {
+#if defined(V3_NOT_JUST_YET_READY)
+
+bool EntryImplV3::CreateEntry(Addr node_address, const std::string& key,
+ uint32 hash) {
Trace("Create entry In");
EntryStore* entry_store = entry_.Data();
RankingsNode* node = node_.Data();
@@ -294,11 +305,11 @@ bool EntryImpl::CreateEntry(Addr node_address, const std::string& key,
return true;
}
-uint32 EntryImpl::GetHash() {
+uint32 EntryImplV3::GetHash() {
return entry_.Data()->hash;
}
-bool EntryImpl::IsSameEntry(const std::string& key, uint32 hash) {
+bool EntryImplV3::IsSameEntry(const std::string& key, uint32 hash) {
if (entry_.Data()->hash != hash ||
static_cast<size_t>(entry_.Data()->key_len) != key.size())
return false;
@@ -306,7 +317,7 @@ bool EntryImpl::IsSameEntry(const std::string& key, uint32 hash) {
return (key.compare(GetKey()) == 0);
}
-void EntryImpl::InternalDoom() {
+void EntryImplV3::InternalDoom() {
net_log_.AddEvent(net::NetLog::TYPE_ENTRY_DOOM);
DCHECK(node_.HasData());
if (!node_.Data()->dirty) {
@@ -320,7 +331,7 @@ void EntryImpl::InternalDoom() {
// first 256 bytes), and values that should be set from the entry creation.
// Basically, even if there is something wrong with this entry, we want to see
// if it is possible to load the rankings node and delete them together.
-bool EntryImpl::SanityCheck() {
+bool EntryImplV3::SanityCheck() {
if (!entry_.VerifyHash())
return false;
@@ -365,7 +376,7 @@ bool EntryImpl::SanityCheck() {
return true;
}
-bool EntryImpl::DataSanityCheck() {
+bool EntryImplV3::DataSanityCheck() {
EntryStore* stored = entry_.Data();
Addr key_addr(stored->long_key);
@@ -395,7 +406,7 @@ bool EntryImpl::DataSanityCheck() {
return true;
}
-void EntryImpl::FixForDelete() {
+void EntryImplV3::FixForDelete() {
EntryStore* stored = entry_.Data();
Addr key_addr(stored->long_key);
@@ -422,13 +433,13 @@ void EntryImpl::FixForDelete() {
entry_.Store();
}
-void EntryImpl::SetTimes(base::Time last_used, base::Time last_modified) {
+void EntryImplV3::SetTimes(base::Time last_used, base::Time last_modified) {
node_.Data()->last_used = last_used.ToInternalValue();
node_.Data()->last_modified = last_modified.ToInternalValue();
node_.set_modified();
}
-void EntryImpl::BeginLogging(net::NetLog* net_log, bool created) {
+void EntryImplV3::BeginLogging(net::NetLog* net_log, bool created) {
DCHECK(!net_log_.net_log());
net_log_ = net::BoundNetLog::Make(
net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY);
@@ -437,18 +448,18 @@ void EntryImpl::BeginLogging(net::NetLog* net_log, bool created) {
CreateNetLogEntryCreationCallback(this, created));
}
-const net::BoundNetLog& EntryImpl::net_log() const {
+const net::BoundNetLog& EntryImplV3::net_log() const {
return net_log_;
}
// ------------------------------------------------------------------------
-void EntryImpl::Doom() {
+void EntryImplV3::Doom() {
if (background_queue_)
background_queue_->DoomEntryImpl(this);
}
-void EntryImpl::DoomImpl() {
+void EntryImplV3::DoomImpl() {
if (doomed_ || !backend_)
return;
@@ -456,12 +467,12 @@ void EntryImpl::DoomImpl() {
backend_->InternalDoomEntry(this);
}
-void EntryImpl::Close() {
+void EntryImplV3::Close() {
if (background_queue_)
background_queue_->CloseEntryImpl(this);
}
-std::string EntryImpl::GetKey() const {
+std::string EntryImplV3::GetKey() const {
CacheEntryBlock* entry = const_cast<CacheEntryBlock*>(&entry_);
int key_len = entry->Data()->key_len;
if (key_len <= kMaxInternalKeyLength)
@@ -493,17 +504,17 @@ std::string EntryImpl::GetKey() const {
return key_;
}
-Time EntryImpl::GetLastUsed() const {
+Time EntryImplV3::GetLastUsed() const {
CacheRankingsBlock* node = const_cast<CacheRankingsBlock*>(&node_);
return Time::FromInternalValue(node->Data()->last_used);
}
-Time EntryImpl::GetLastModified() const {
+Time EntryImplV3::GetLastModified() const {
CacheRankingsBlock* node = const_cast<CacheRankingsBlock*>(&node_);
return Time::FromInternalValue(node->Data()->last_modified);
}
-int32 EntryImpl::GetDataSize(int index) const {
+int32 EntryImplV3::GetDataSize(int index) const {
if (index < 0 || index >= kNumStreams)
return 0;
@@ -511,8 +522,8 @@ int32 EntryImpl::GetDataSize(int index) const {
return entry->Data()->data_size[index];
}
-int EntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
+int EntryImplV3::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
if (callback.is_null())
return ReadDataImpl(index, offset, buf, buf_len, callback);
@@ -536,7 +547,7 @@ int EntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, false));
@@ -544,7 +555,7 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalReadData(index, offset, buf, buf_len, callback);
- if (result != net::ERR_IO_PENDING && net_log_.IsLoggingAllEvents()) {
+ if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteCompleteCallback(result));
@@ -552,8 +563,8 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
return result;
}
-int EntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback, bool truncate) {
+int EntryImplV3::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback, bool truncate) {
if (callback.is_null())
return WriteDataImpl(index, offset, buf, buf_len, callback, truncate);
@@ -575,7 +586,7 @@ int EntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback,
bool truncate) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate));
@@ -584,7 +595,7 @@ int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalWriteData(index, offset, buf, buf_len, callback,
truncate);
- if (result != net::ERR_IO_PENDING && net_log_.IsLoggingAllEvents()) {
+ if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteCompleteCallback(result));
@@ -592,8 +603,8 @@ int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
return result;
}
-int EntryImpl::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
+int EntryImplV3::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
if (callback.is_null())
return ReadSparseDataImpl(offset, buf, buf_len, callback);
@@ -618,8 +629,8 @@ int EntryImpl::ReadSparseDataImpl(int64 offset, IOBuffer* buf, int buf_len,
return result;
}
-int EntryImpl::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
+int EntryImplV3::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
if (callback.is_null())
return WriteSparseDataImpl(offset, buf, buf_len, callback);
@@ -644,8 +655,8 @@ int EntryImpl::WriteSparseDataImpl(int64 offset, IOBuffer* buf, int buf_len,
return result;
}
-int EntryImpl::GetAvailableRange(int64 offset, int len, int64* start,
- const CompletionCallback& callback) {
+int EntryImplV3::GetAvailableRange(int64 offset, int len, int64* start,
+ const CompletionCallback& callback) {
if (!background_queue_)
return net::ERR_UNEXPECTED;
@@ -661,7 +672,7 @@ int EntryImpl::GetAvailableRangeImpl(int64 offset, int len, int64* start) {
return sparse_->GetAvailableRange(offset, len, start);
}
-bool EntryImpl::CouldBeSparse() const {
+bool EntryImplV3::CouldBeSparse() const {
if (sparse_.get())
return true;
@@ -670,19 +681,19 @@ bool EntryImpl::CouldBeSparse() const {
return sparse->CouldBeSparse();
}
-void EntryImpl::CancelSparseIO() {
+void EntryImplV3::CancelSparseIO() {
if (background_queue_)
background_queue_->CancelSparseIO(this);
}
-void EntryImpl::CancelSparseIOImpl() {
+void EntryImplV3::CancelSparseIOImpl() {
if (!sparse_.get())
return;
sparse_->CancelIO();
}
-int EntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
+int EntryImplV3::ReadyForSparseIO(const CompletionCallback& callback) {
if (!sparse_.get())
return net::OK;
@@ -693,7 +704,7 @@ int EntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
return net::ERR_IO_PENDING;
}
-int EntryImpl::ReadyForSparseIOImpl(const CompletionCallback& callback) {
+int EntryImplV3::ReadyForSparseIOImpl(const CompletionCallback& callback) {
DCHECK(sparse_.get());
return sparse_->ReadyToUse(callback);
}
@@ -706,7 +717,7 @@ int EntryImpl::ReadyForSparseIOImpl(const CompletionCallback& callback) {
// read partial information from an entry (don't have to worry about returning
// data related to a previous cache entry because the range was not fully
// written before).
-EntryImpl::~EntryImpl() {
+EntryImplV3::~EntryImplV3() {
if (!backend_) {
entry_.clear_modified();
node_.clear_modified();
@@ -1346,50 +1357,108 @@ void EntryImpl::GetData(int index, char** buffer, Addr* address) {
}
}
-void EntryImpl::ReportIOTime(Operation op, const base::TimeTicks& start) {
+#endif // defined(V3_NOT_JUST_YET_READY).
+
+void EntryImplV3::ReportIOTime(Operation op, const base::TimeTicks& start) {
if (!backend_)
return;
switch (op) {
case kRead:
- CACHE_UMA(AGE_MS, "ReadTime", 0, start);
+ CACHE_UMA(AGE_MS, "ReadTime", start);
break;
case kWrite:
- CACHE_UMA(AGE_MS, "WriteTime", 0, start);
+ CACHE_UMA(AGE_MS, "WriteTime", start);
break;
case kSparseRead:
- CACHE_UMA(AGE_MS, "SparseReadTime", 0, start);
+ CACHE_UMA(AGE_MS, "SparseReadTime", start);
break;
case kSparseWrite:
- CACHE_UMA(AGE_MS, "SparseWriteTime", 0, start);
+ CACHE_UMA(AGE_MS, "SparseWriteTime", start);
break;
case kAsyncIO:
- CACHE_UMA(AGE_MS, "AsyncIOTime", 0, start);
+ CACHE_UMA(AGE_MS, "AsyncIOTime", start);
break;
case kReadAsync1:
- CACHE_UMA(AGE_MS, "AsyncReadDispatchTime", 0, start);
+ CACHE_UMA(AGE_MS, "AsyncReadDispatchTime", start);
break;
case kWriteAsync1:
- CACHE_UMA(AGE_MS, "AsyncWriteDispatchTime", 0, start);
+ CACHE_UMA(AGE_MS, "AsyncWriteDispatchTime", start);
break;
default:
NOTREACHED();
}
}
-void EntryImpl::Log(const char* msg) {
- int dirty = 0;
- if (node_.HasData()) {
- dirty = node_.Data()->dirty;
- }
+void EntryImplV3::Log(const char* msg) {
+ Trace("%s 0x%p 0x%x", msg, reinterpret_cast<void*>(this), address_);
+ Trace(" data: 0x%x 0x%x", entry_->data_addr[0], entry_->data_addr[1]);
+ Trace(" doomed: %d", doomed_);
+}
- Trace("%s 0x%p 0x%x 0x%x", msg, reinterpret_cast<void*>(this),
- entry_.address().value(), node_.address().value());
+void EntryImplV3::Doom() {
+ NOTIMPLEMENTED();
+}
+
+void EntryImplV3::Close() {
+ NOTIMPLEMENTED();
+}
+
+std::string EntryImplV3::GetKey() const {
+ return std::string();
+}
- Trace(" data: 0x%x 0x%x 0x%x", entry_.Data()->data_addr[0],
- entry_.Data()->data_addr[1], entry_.Data()->long_key);
+Time EntryImplV3::GetLastUsed() const {
+ return Time();
+}
+
+Time EntryImplV3::GetLastModified() const {
+ return Time();
+}
+
+int32 EntryImplV3::GetDataSize(int index) const {
+ return 0;
+}
+
+int EntryImplV3::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int EntryImplV3::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback, bool truncate) {
+ return net::ERR_FAILED;
+}
+
+int EntryImplV3::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int EntryImplV3::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+int EntryImplV3::GetAvailableRange(int64 offset, int len, int64* start,
+ const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
+
+bool EntryImplV3::CouldBeSparse() const {
+ return false;
+}
+
+void EntryImplV3::CancelSparseIO() {
+ NOTIMPLEMENTED();
+}
+
+int EntryImplV3::ReadyForSparseIO(const CompletionCallback& callback) {
+ return net::ERR_FAILED;
+}
- Trace(" doomed: %d 0x%x", doomed_, dirty);
+EntryImplV3::~EntryImplV3() {
+ NOTIMPLEMENTED();
}
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/v3/entry_impl_v3.h b/chromium/net/disk_cache/blockfile/entry_impl_v3.h
index af5bb4f5561..6be0c710875 100644
--- a/chromium/net/disk_cache/v3/entry_impl_v3.h
+++ b/chromium/net/disk_cache/blockfile/entry_impl_v3.h
@@ -2,28 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_ENTRY_IMPL_H_
-#define NET_DISK_CACHE_ENTRY_IMPL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_V3_H_
+
+#include <string>
#include "base/memory/scoped_ptr.h"
#include "net/base/net_log.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+#include "net/disk_cache/blockfile/storage_block.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/storage_block.h"
-#include "net/disk_cache/storage_block-inl.h"
namespace disk_cache {
-class BackendImpl;
-class InFlightBackendIO;
-class SparseControl;
+class BackendImplV3;
+class SparseControlV3;
// This class implements the Entry interface. An object of this
// class represents a single entry on the cache.
-class NET_EXPORT_PRIVATE EntryImpl
+class NET_EXPORT_PRIVATE EntryImplV3
: public Entry,
- public base::RefCounted<EntryImpl> {
- friend class base::RefCounted<EntryImpl>;
- friend class SparseControl;
+ public base::RefCounted<EntryImplV3> {
+ friend class base::RefCounted<EntryImplV3>;
+ // friend class SparseControlV3;
public:
enum Operation {
kRead,
@@ -35,42 +36,27 @@ class NET_EXPORT_PRIVATE EntryImpl
kWriteAsync1
};
- EntryImpl(BackendImpl* backend, Addr address, bool read_only);
-
- // Background implementation of the Entry interface.
- void DoomImpl();
- int ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
- int WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback, bool truncate);
- int ReadSparseDataImpl(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
- int WriteSparseDataImpl(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
- int GetAvailableRangeImpl(int64 offset, int len, int64* start);
- void CancelSparseIOImpl();
- int ReadyForSparseIOImpl(const CompletionCallback& callback);
-
- // Performs the initialization of a EntryImpl that will be added to the
+ EntryImplV3(BackendImplV3* backend, Addr address, bool read_only);
+
+ // Performs the initialization of a EntryImplV3 that will be added to the
// cache.
bool CreateEntry(Addr node_address, const std::string& key, uint32 hash);
uint32 GetHash();
+ uint32 GetHash() const;
+ Addr GetAddress() const;
+ int GetReuseCounter() const;
+ void SetReuseCounter(int count);
+ int GetRefetchCounter() const;
+ void SetRefetchCounter(int count);
+
// Returns true if this entry matches the lookup arguments.
bool IsSameEntry(const std::string& key, uint32 hash);
// Permamently destroys this entry.
void InternalDoom();
- bool dirty() {
- return dirty_;
- }
-
- bool doomed() {
- return doomed_;
- }
-
// Returns false if the entry is clearly invalid.
bool SanityCheck();
bool DataSanityCheck();
@@ -82,9 +68,9 @@ class NET_EXPORT_PRIVATE EntryImpl
// the upgrade tool.
void SetTimes(base::Time last_used, base::Time last_modified);
- // Logs a begin event and enables logging for the EntryImpl. Will also cause
- // an end event to be logged on destruction. The EntryImpl must have its key
- // initialized before this is called. |created| is true if the Entry was
+ // Logs a begin event and enables logging for the EntryImplV3. Will also cause
+ // an end event to be logged on destruction. The EntryImplV3 must have its key
+ // initialized before this is called. |created| is true if the Entry was
// created rather than opened.
void BeginLogging(net::NetLog* net_log, bool created);
@@ -118,7 +104,7 @@ class NET_EXPORT_PRIVATE EntryImpl
};
class UserBuffer;
- virtual ~EntryImpl();
+ virtual ~EntryImplV3();
// Do all the work for ReadDataImpl and WriteDataImpl. Implemented as
// separate functions to make logging of results simpler.
@@ -199,25 +185,24 @@ class NET_EXPORT_PRIVATE EntryImpl
// Logs this entry to the internal trace buffer.
void Log(const char* msg);
- CacheEntryBlock entry_; // Key related information for this entry.
- CacheRankingsBlock node_; // Rankings related information for this entry.
- base::WeakPtr<BackendImpl> backend_; // Back pointer to the cache.
- base::WeakPtr<InFlightBackendIO> background_queue_; // In-progress queue.
+ scoped_ptr<EntryRecord> entry_; // Basic record for this entry.
+ scoped_ptr<ShortEntryRecord> short_entry_; // Valid for evicted entries.
+ base::WeakPtr<BackendImplV3> backend_; // Back pointer to the cache.
scoped_ptr<UserBuffer> user_buffers_[kNumStreams]; // Stores user data.
- // Files to store external user data and key.
- scoped_refptr<File> files_[kNumStreams + 1];
mutable std::string key_; // Copy of the key.
+ Addr address_;
int unreported_size_[kNumStreams]; // Bytes not reported yet to the backend.
bool doomed_; // True if this entry was removed from the cache.
- bool read_only_; // True if not yet writing.
- bool dirty_; // True if we detected that this is a dirty entry.
- scoped_ptr<SparseControl> sparse_; // Support for sparse entries.
+ bool read_only_;
+ bool dirty_; // True if there is something to write.
+ bool modified_;
+ // scoped_ptr<SparseControlV3> sparse_; // Support for sparse entries.
net::BoundNetLog net_log_;
- DISALLOW_COPY_AND_ASSIGN(EntryImpl);
+ DISALLOW_COPY_AND_ASSIGN(EntryImplV3);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_ENTRY_IMPL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_ENTRY_IMPL_V3_H_
diff --git a/chromium/net/disk_cache/errors.h b/chromium/net/disk_cache/blockfile/errors.h
index 1c69d42f770..565eb049050 100644
--- a/chromium/net/disk_cache/errors.h
+++ b/chromium/net/disk_cache/blockfile/errors.h
@@ -4,8 +4,8 @@
// Error codes reported by self tests or to UMA.
-#ifndef NET_DISK_CACHE_ERRORS_H__
-#define NET_DISK_CACHE_ERRORS_H__
+#ifndef NET_DISK_CACHE_BLOCKFILE_ERRORS_H_
+#define NET_DISK_CACHE_BLOCKFILE_ERRORS_H_
namespace disk_cache {
@@ -30,4 +30,4 @@ enum {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_ERRORS_H__
+#endif // NET_DISK_CACHE_BLOCKFILE_ERRORS_H_
diff --git a/chromium/net/disk_cache/eviction.cc b/chromium/net/disk_cache/blockfile/eviction.cc
index 47b52552ef5..20d17fcd41d 100644
--- a/chromium/net/disk_cache/eviction.cc
+++ b/chromium/net/disk_cache/blockfile/eviction.cc
@@ -26,7 +26,7 @@
// size so that we have a chance to see an element again and move it to another
// list.
-#include "net/disk_cache/eviction.h"
+#include "net/disk_cache/blockfile/eviction.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
@@ -34,12 +34,16 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/histogram_macros.h"
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+#include "net/disk_cache/blockfile/trace.h"
+#include "net/disk_cache/blockfile/webfonts_histogram.h"
+
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
using base::Time;
using base::TimeTicks;
@@ -287,6 +291,7 @@ bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty,
return false;
}
+ web_fonts_histogram::RecordEviction(entry);
ReportTrimTimes(entry);
if (empty || !new_eviction_) {
entry->DoomImpl();
diff --git a/chromium/net/disk_cache/eviction.h b/chromium/net/disk_cache/blockfile/eviction.h
index f6224a936fd..e49883bac2e 100644
--- a/chromium/net/disk_cache/eviction.h
+++ b/chromium/net/disk_cache/blockfile/eviction.h
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_EVICTION_H_
-#define NET_DISK_CACHE_EVICTION_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_EVICTION_H_
+#define NET_DISK_CACHE_BLOCKFILE_EVICTION_H_
#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
-#include "net/disk_cache/rankings.h"
+#include "net/disk_cache/blockfile/rankings.h"
namespace disk_cache {
@@ -88,4 +88,4 @@ class Eviction {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_EVICTION_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_EVICTION_H_
diff --git a/chromium/net/disk_cache/v3/eviction_v3.cc b/chromium/net/disk_cache/blockfile/eviction_v3.cc
index 91275fc7bc2..9d404a9172a 100644
--- a/chromium/net/disk_cache/v3/eviction_v3.cc
+++ b/chromium/net/disk_cache/blockfile/eviction_v3.cc
@@ -26,7 +26,7 @@
// size so that we have a chance to see an element again and move it to another
// list.
-#include "net/disk_cache/eviction.h"
+#include "net/disk_cache/blockfile/eviction_v3.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
@@ -34,11 +34,13 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/experiments.h"
-#include "net/disk_cache/histogram_macros.h"
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/backend_impl_v3.h"
+#include "net/disk_cache/blockfile/entry_impl_v3.h"
+#include "net/disk_cache/blockfile/experiments.h"
+#include "net/disk_cache/blockfile/histogram_macros_v3.h"
+#include "net/disk_cache/blockfile/trace.h"
+
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
using base::Time;
using base::TimeTicks;
@@ -46,9 +48,12 @@ using base::TimeTicks;
namespace {
const int kCleanUpMargin = 1024 * 1024;
+
+#if defined(V3_NOT_JUST_YET_READY)
const int kHighUse = 10; // Reuse count to be on the HIGH_USE list.
const int kTargetTime = 24 * 7; // Time to be evicted (hours since last use).
const int kMaxDelayedTrims = 60;
+#endif // defined(V3_NOT_JUST_YET_READY).
int LowWaterAdjust(int high_water) {
if (high_water < kCleanUpMargin)
@@ -57,9 +62,11 @@ int LowWaterAdjust(int high_water) {
return high_water - kCleanUpMargin;
}
+#if defined(V3_NOT_JUST_YET_READY)
bool FallingBehind(int current_size, int max_size) {
return current_size > max_size - kCleanUpMargin * 20;
}
+#endif // defined(V3_NOT_JUST_YET_READY).
} // namespace
@@ -67,24 +74,25 @@ namespace disk_cache {
// The real initialization happens during Init(), init_ is the only member that
// has to be initialized here.
-Eviction::Eviction()
+EvictionV3::EvictionV3()
: backend_(NULL),
+ index_(NULL),
+ header_(NULL),
init_(false),
ptr_factory_(this) {
}
-Eviction::~Eviction() {
+EvictionV3::~EvictionV3() {
}
-void Eviction::Init(BackendImpl* backend) {
+void EvictionV3::Init(BackendImplV3* backend) {
// We grab a bunch of info from the backend to make the code a little cleaner
// when we're actually doing work.
backend_ = backend;
- rankings_ = &backend->rankings_;
- header_ = &backend_->data_->header;
+ index_ = &backend_->index_;
+ header_ = index_->header();
max_size_ = LowWaterAdjust(backend_->max_size_);
- index_size_ = backend->mask_ + 1;
- new_eviction_ = backend->new_eviction_;
+ lru_ = backend->lru_eviction_;
first_trim_ = true;
trimming_ = false;
delay_trim_ = false;
@@ -93,7 +101,7 @@ void Eviction::Init(BackendImpl* backend) {
test_mode_ = false;
}
-void Eviction::Stop() {
+void EvictionV3::Stop() {
// It is possible for the backend initialization to fail, in which case this
// object was never initialized... and there is nothing to do.
if (!init_)
@@ -106,7 +114,8 @@ void Eviction::Stop() {
ptr_factory_.InvalidateWeakPtrs();
}
-void Eviction::TrimCache(bool empty) {
+#if defined(V3_NOT_JUST_YET_READY)
+void EvictionV3::TrimCache() {
if (backend_->disabled_ || trimming_)
return;
@@ -144,7 +153,7 @@ void Eviction::TrimCache(bool empty) {
(TimeTicks::Now() - start).InMilliseconds() > 20)) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&Eviction::TrimCache, ptr_factory_.GetWeakPtr(), false));
+ base::Bind(&EvictionV3::TrimCache, ptr_factory_.GetWeakPtr(), false));
break;
}
}
@@ -161,7 +170,7 @@ void Eviction::TrimCache(bool empty) {
return;
}
-void Eviction::OnOpenEntryV2(EntryImpl* entry) {
+void EvictionV3::OnOpenEntry(EntryImplV3* entry) {
EntryStore* info = entry->entry()->Data();
DCHECK_EQ(ENTRY_NORMAL, info->state);
@@ -182,7 +191,7 @@ void Eviction::OnOpenEntryV2(EntryImpl* entry) {
}
}
-void Eviction::OnCreateEntryV2(EntryImpl* entry) {
+void EvictionV3::OnCreateEntry(EntryImplV3* entry) {
EntryStore* info = entry->entry()->Data();
switch (info->state) {
case ENTRY_NORMAL: {
@@ -211,18 +220,18 @@ void Eviction::OnCreateEntryV2(EntryImpl* entry) {
rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry));
}
-void Eviction::SetTestMode() {
+void EvictionV3::SetTestMode() {
test_mode_ = true;
}
-void Eviction::TrimDeletedList(bool empty) {
+void EvictionV3::TrimDeletedList(bool empty) {
DCHECK(test_mode_ && new_eviction_);
TrimDeleted(empty);
}
// -----------------------------------------------------------------------
-void Eviction::PostDelayedTrim() {
+void EvictionV3::PostDelayedTrim() {
// Prevent posting multiple tasks.
if (delay_trim_)
return;
@@ -230,11 +239,11 @@ void Eviction::PostDelayedTrim() {
trim_delays_++;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- base::Bind(&Eviction::DelayedTrim, ptr_factory_.GetWeakPtr()),
+ base::Bind(&EvictionV3::DelayedTrim, ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(1000));
}
-void Eviction::DelayedTrim() {
+void EvictionV3::DelayedTrim() {
delay_trim_ = false;
if (trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded())
return PostDelayedTrim();
@@ -242,7 +251,7 @@ void Eviction::DelayedTrim() {
TrimCache(false);
}
-bool Eviction::ShouldTrim() {
+bool EvictionV3::ShouldTrim() {
if (!FallingBehind(header_->num_bytes, max_size_) &&
trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded()) {
return false;
@@ -253,7 +262,7 @@ bool Eviction::ShouldTrim() {
return true;
}
-bool Eviction::ShouldTrimDeleted() {
+bool EvictionV3::ShouldTrimDeleted() {
int index_load = header_->num_entries * 100 / index_size_;
// If the index is not loaded, the deleted list will tend to double the size
@@ -266,7 +275,7 @@ bool Eviction::ShouldTrimDeleted() {
bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty,
Rankings::List list) {
- EntryImpl* entry = backend_->GetEnumeratedEntry(node, list);
+ EntryImplV3* entry = backend_->GetEnumeratedEntry(node, list);
if (!entry) {
Trace("NewEntry failed on Trim 0x%x", node->address().value());
return false;
@@ -293,7 +302,7 @@ bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty,
return true;
}
-void Eviction::TrimCacheV2(bool empty) {
+void EvictionV3::TrimCacheV2(bool empty) {
Trace("*** Trim Cache ***");
trimming_ = true;
TimeTicks start = TimeTicks::Now();
@@ -362,7 +371,7 @@ void Eviction::TrimCacheV2(bool empty) {
} else if (ShouldTrimDeleted()) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&Eviction::TrimDeleted, ptr_factory_.GetWeakPtr(), empty));
+ base::Bind(&EvictionV3::TrimDeleted, ptr_factory_.GetWeakPtr(), empty));
}
if (empty) {
@@ -379,7 +388,7 @@ void Eviction::TrimCacheV2(bool empty) {
// This is a minimal implementation that just discards the oldest nodes.
// TODO(rvargas): Do something better here.
-void Eviction::TrimDeleted(bool empty) {
+void EvictionV3::TrimDeleted(bool empty) {
Trace("*** Trim Deleted ***");
if (backend_->disabled_)
return;
@@ -403,7 +412,7 @@ void Eviction::TrimDeleted(bool empty) {
if (deleted_entries && !empty && ShouldTrimDeleted()) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&Eviction::TrimDeleted, ptr_factory_.GetWeakPtr(), false));
+ base::Bind(&EvictionV3::TrimDeleted, ptr_factory_.GetWeakPtr(), false));
}
CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start);
@@ -412,7 +421,7 @@ void Eviction::TrimDeleted(bool empty) {
return;
}
-void Eviction::ReportTrimTimes(EntryImpl* entry) {
+void EvictionV3::ReportTrimTimes(EntryImplV3* entry) {
if (first_trim_) {
first_trim_ = false;
if (backend_->ShouldReportAgain()) {
@@ -440,7 +449,7 @@ void Eviction::ReportTrimTimes(EntryImpl* entry) {
}
}
-bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) {
+bool EvictionV3::NodeIsOldEnough(CacheRankingsBlock* node, int list) {
if (!node)
return false;
@@ -452,7 +461,7 @@ bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) {
return (Time::Now() - used).InHours() > kTargetTime * multiplier;
}
-int Eviction::SelectListByLength(Rankings::ScopedRankingsBlock* next) {
+int EvictionV3::SelectListByLength(Rankings::ScopedRankingsBlock* next) {
int data_entries = header_->num_entries -
header_->lru.sizes[Rankings::DELETED];
// Start by having each list to be roughly the same size.
@@ -472,7 +481,7 @@ int Eviction::SelectListByLength(Rankings::ScopedRankingsBlock* next) {
return list;
}
-void Eviction::ReportListStats() {
+void EvictionV3::ReportListStats() {
if (!new_eviction_)
return;
@@ -498,5 +507,6 @@ void Eviction::ReportListStats() {
CACHE_UMA(AGE, "DeletedAge", 0,
Time::FromInternalValue(last4.get()->Data()->last_used));
}
+#endif // defined(V3_NOT_JUST_YET_READY).
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/v3/eviction_v3.h b/chromium/net/disk_cache/blockfile/eviction_v3.h
index 1f05b0e0881..01fe6136115 100644
--- a/chromium/net/disk_cache/v3/eviction_v3.h
+++ b/chromium/net/disk_cache/blockfile/eviction_v3.h
@@ -2,27 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_EVICTION_H_
-#define NET_DISK_CACHE_EVICTION_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_EVICTION_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_EVICTION_V3_H_
#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/rankings.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+#include "net/disk_cache/blockfile/index_table_v3.h"
namespace disk_cache {
-class BackendImpl;
-class EntryImpl;
+class BackendImplV3;
+class CacheRankingsBlock;
+class EntryImplV3;
+
+namespace Rankings {
+typedef int List;
+}
// This class implements the eviction algorithm for the cache and it is tightly
// integrated with BackendImpl.
-class Eviction {
+class EvictionV3 {
public:
- Eviction();
- ~Eviction();
+ EvictionV3();
+ ~EvictionV3();
- void Init(BackendImpl* backend);
+ void Init(BackendImplV3* backend);
void Stop();
// Deletes entries from the cache until the current size is below the limit.
@@ -31,8 +36,8 @@ class Eviction {
void TrimCache(bool empty);
// Notifications of interesting events for a given entry.
- void OnOpenEntry(EntryImpl* entry);
- void OnCreateEntry(EntryImpl* entry);
+ void OnOpenEntry(EntryImplV3* entry);
+ void OnCreateEntry(EntryImplV3* entry);
// Testing interface.
void SetTestMode();
@@ -49,26 +54,25 @@ class Eviction {
void TrimDeleted(bool empty);
bool NodeIsOldEnough(CacheRankingsBlock* node, int list);
- int SelectListByLength(Rankings::ScopedRankingsBlock* next);
+ int SelectListByLength();
void ReportListStats();
- BackendImpl* backend_;
- Rankings* rankings_;
- IndexHeader* header_;
+ BackendImplV3* backend_;
+ IndexTable* index_;
+ IndexHeaderV3* header_;
int max_size_;
int trim_delays_;
- int index_size_;
- bool new_eviction_;
+ bool lru_;
bool first_trim_;
bool trimming_;
bool delay_trim_;
bool init_;
bool test_mode_;
- base::WeakPtrFactory<Eviction> ptr_factory_;
+ base::WeakPtrFactory<EvictionV3> ptr_factory_;
- DISALLOW_COPY_AND_ASSIGN(Eviction);
+ DISALLOW_COPY_AND_ASSIGN(EvictionV3);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_EVICTION_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_EVICTION_V3_H_
diff --git a/chromium/net/disk_cache/experiments.h b/chromium/net/disk_cache/blockfile/experiments.h
index d7d4e58437b..53b6582b0d7 100644
--- a/chromium/net/disk_cache/experiments.h
+++ b/chromium/net/disk_cache/blockfile/experiments.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_EXPERIMENTS_H_
-#define NET_DISK_CACHE_EXPERIMENTS_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_EXPERIMENTS_H_
+#define NET_DISK_CACHE_BLOCKFILE_EXPERIMENTS_H_
namespace disk_cache {
@@ -25,4 +25,4 @@ enum {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_EXPERIMENTS_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_EXPERIMENTS_H_
diff --git a/chromium/net/disk_cache/file.cc b/chromium/net/disk_cache/blockfile/file.cc
index 6b569518354..17c77a4e099 100644
--- a/chromium/net/disk_cache/file.cc
+++ b/chromium/net/disk_cache/blockfile/file.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/file.h"
namespace disk_cache {
diff --git a/chromium/net/disk_cache/file.h b/chromium/net/disk_cache/blockfile/file.h
index 190f7cb9f82..9425d598501 100644
--- a/chromium/net/disk_cache/file.h
+++ b/chromium/net/disk_cache/blockfile/file.h
@@ -4,11 +4,11 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_FILE_H_
-#define NET_DISK_CACHE_FILE_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_FILE_H_
+#define NET_DISK_CACHE_BLOCKFILE_FILE_H_
+#include "base/files/file.h"
#include "base/memory/ref_counted.h"
-#include "base/platform_file.h"
#include "net/base/net_export.h"
namespace base {
@@ -39,15 +39,12 @@ class NET_EXPORT_PRIVATE File : public base::RefCounted<File> {
// Initializes the object to use the passed in file instead of opening it with
// the Init() call. No asynchronous operations can be performed with this
// object.
- explicit File(base::PlatformFile file);
+ explicit File(base::File file);
// Initializes the object to point to a given file. The file must aready exist
// on disk, and allow shared read and write.
bool Init(const base::FilePath& name);
- // Returns the handle or file descriptor.
- base::PlatformFile platform_file() const;
-
// Returns true if the file was opened properly.
bool IsValid() const;
@@ -76,6 +73,9 @@ class NET_EXPORT_PRIVATE File : public base::RefCounted<File> {
protected:
virtual ~File();
+ // Returns the handle or file descriptor.
+ base::PlatformFile platform_file() const;
+
private:
// Performs the actual asynchronous write. If notify is set and there is no
// callback, the call will be re-synchronized.
@@ -89,12 +89,12 @@ class NET_EXPORT_PRIVATE File : public base::RefCounted<File> {
bool init_;
bool mixed_;
- base::PlatformFile platform_file_; // Regular, asynchronous IO handle.
- base::PlatformFile sync_platform_file_; // Synchronous IO handle.
+ base::File base_file_; // Regular, asynchronous IO handle.
+ base::File sync_base_file_; // Synchronous IO handle.
DISALLOW_COPY_AND_ASSIGN(File);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_FILE_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_FILE_H_
diff --git a/chromium/net/disk_cache/file_block.h b/chromium/net/disk_cache/blockfile/file_block.h
index 25709207df6..fdcfcd05f51 100644
--- a/chromium/net/disk_cache/file_block.h
+++ b/chromium/net/disk_cache/blockfile/file_block.h
@@ -4,8 +4,8 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_FILE_BLOCK_H__
-#define NET_DISK_CACHE_FILE_BLOCK_H__
+#ifndef NET_DISK_CACHE_BLOCKFILE_FILE_BLOCK_H_
+#define NET_DISK_CACHE_BLOCKFILE_FILE_BLOCK_H_
namespace disk_cache {
@@ -28,4 +28,4 @@ class FileBlock {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_FILE_BLOCK_H__
+#endif // NET_DISK_CACHE_BLOCKFILE_FILE_BLOCK_H_
diff --git a/chromium/net/disk_cache/file_ios.cc b/chromium/net/disk_cache/blockfile/file_ios.cc
index 9cad1551f15..4659e75c652 100644
--- a/chromium/net/disk_cache/file_ios.cc
+++ b/chromium/net/disk_cache/blockfile/file_ios.cc
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/file.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/worker_pool.h"
#include "net/base/net_errors.h"
+#include "net/disk_cache/blockfile/in_flight_io.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/in_flight_io.h"
namespace {
@@ -144,7 +144,7 @@ void FileInFlightIO::OnOperationComplete(disk_cache::BackgroundIO* operation,
callback->OnFileIOComplete(bytes);
}
-// A static object tha will broker all async operations.
+// A static object that will broker all async operations.
FileInFlightIO* s_file_operations = NULL;
// Returns the current FileInFlightIO.
@@ -166,62 +166,46 @@ void DeleteFileInFlightIO() {
namespace disk_cache {
-File::File(base::PlatformFile file)
+File::File(base::File file)
: init_(true),
mixed_(true),
- platform_file_(file),
- sync_platform_file_(base::kInvalidPlatformFileValue) {
+ base_file_(file.Pass()) {
}
bool File::Init(const base::FilePath& name) {
- if (init_)
+ if (base_file_.IsValid())
return false;
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE;
- platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL);
- if (platform_file_ < 0) {
- platform_file_ = 0;
- return false;
- }
-
- init_ = true;
- return true;
-}
-
-base::PlatformFile File::platform_file() const {
- return platform_file_;
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE;
+ base_file_.Initialize(name, flags);
+ return base_file_.IsValid();
}
bool File::IsValid() const {
- if (!init_)
- return false;
- return (base::kInvalidPlatformFileValue != platform_file_);
+ return base_file_.IsValid();
}
bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (buffer_len > static_cast<size_t>(kint32max) ||
offset > static_cast<size_t>(kint32max)) {
return false;
}
- int ret = base::ReadPlatformFile(platform_file_, offset,
- static_cast<char*>(buffer), buffer_len);
+ int ret = base_file_.Read(offset, static_cast<char*>(buffer), buffer_len);
return (static_cast<size_t>(ret) == buffer_len);
}
bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (buffer_len > static_cast<size_t>(kint32max) ||
offset > static_cast<size_t>(kint32max)) {
return false;
}
- int ret = base::WritePlatformFile(platform_file_, offset,
- static_cast<const char*>(buffer),
- buffer_len);
+ int ret = base_file_.Write(offset, static_cast<const char*>(buffer),
+ buffer_len);
return (static_cast<size_t>(ret) == buffer_len);
}
@@ -230,7 +214,7 @@ bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
// closed while the IO is in flight).
bool File::Read(void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (!callback) {
if (completed)
*completed = true;
@@ -248,7 +232,7 @@ bool File::Read(void* buffer, size_t buffer_len, size_t offset,
bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (!callback) {
if (completed)
*completed = true;
@@ -259,17 +243,16 @@ bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
}
bool File::SetLength(size_t length) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (length > kuint32max)
return false;
- return base::TruncatePlatformFile(platform_file_, length);
+ return base_file_.SetLength(length);
}
size_t File::GetLength() {
- DCHECK(init_);
- int64 len = base::SeekPlatformFile(platform_file_,
- base::PLATFORM_FILE_FROM_END, 0);
+ DCHECK(base_file_.IsValid());
+ int64 len = base_file_.GetLength();
if (len > static_cast<int64>(kuint32max))
return kuint32max;
@@ -292,13 +275,15 @@ void File::DropPendingIO() {
}
File::~File() {
- if (IsValid())
- base::ClosePlatformFile(platform_file_);
+}
+
+base::PlatformFile File::platform_file() const {
+ return base_file_.GetPlatformFile();
}
bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
return false;
diff --git a/chromium/net/disk_cache/file_lock.cc b/chromium/net/disk_cache/blockfile/file_lock.cc
index 3d1cfa52d8b..edd8d08628d 100644
--- a/chromium/net/disk_cache/file_lock.cc
+++ b/chromium/net/disk_cache/blockfile/file_lock.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/file_lock.h"
+#include "net/disk_cache/blockfile/file_lock.h"
#include "base/atomicops.h"
diff --git a/chromium/net/disk_cache/file_lock.h b/chromium/net/disk_cache/blockfile/file_lock.h
index 7fcf75df05a..3486aba18d1 100644
--- a/chromium/net/disk_cache/file_lock.h
+++ b/chromium/net/disk_cache/blockfile/file_lock.h
@@ -4,11 +4,11 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_FILE_LOCK_H_
-#define NET_DISK_CACHE_FILE_LOCK_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_FILE_LOCK_H_
+#define NET_DISK_CACHE_BLOCKFILE_FILE_LOCK_H_
#include "net/base/net_export.h"
-#include "net/disk_cache/disk_format_base.h"
+#include "net/disk_cache/blockfile/disk_format_base.h"
namespace disk_cache {
@@ -42,4 +42,4 @@ class NET_EXPORT_PRIVATE FileLock {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_FILE_LOCK_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_FILE_LOCK_H_
diff --git a/chromium/net/disk_cache/file_posix.cc b/chromium/net/disk_cache/blockfile/file_posix.cc
index e1ff6fc4198..c451aa3b9f1 100644
--- a/chromium/net/disk_cache/file_posix.cc
+++ b/chromium/net/disk_cache/blockfile/file_posix.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/file.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
@@ -17,7 +17,7 @@
namespace {
// The maximum number of threads for this pool.
-const int kMaxThreads = 20;
+const int kMaxThreads = 5;
class FileWorkerPool : public base::SequencedWorkerPool {
public:
@@ -34,68 +34,52 @@ base::LazyInstance<FileWorkerPool>::Leaky s_worker_pool =
namespace disk_cache {
-File::File(base::PlatformFile file)
+File::File(base::File file)
: init_(true),
mixed_(true),
- platform_file_(file),
- sync_platform_file_(base::kInvalidPlatformFileValue) {
+ base_file_(file.Pass()) {
}
bool File::Init(const base::FilePath& name) {
- if (init_)
+ if (base_file_.IsValid())
return false;
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE;
- platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL);
- if (platform_file_ < 0) {
- platform_file_ = 0;
- return false;
- }
-
- init_ = true;
- return true;
-}
-
-base::PlatformFile File::platform_file() const {
- return platform_file_;
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE;
+ base_file_.Initialize(name, flags);
+ return base_file_.IsValid();
}
bool File::IsValid() const {
- if (!init_)
- return false;
- return (base::kInvalidPlatformFileValue != platform_file_);
+ return base_file_.IsValid();
}
bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (buffer_len > static_cast<size_t>(kint32max) ||
offset > static_cast<size_t>(kint32max)) {
return false;
}
- int ret = base::ReadPlatformFile(platform_file_, offset,
- static_cast<char*>(buffer), buffer_len);
+ int ret = base_file_.Read(offset, static_cast<char*>(buffer), buffer_len);
return (static_cast<size_t>(ret) == buffer_len);
}
bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (buffer_len > static_cast<size_t>(kint32max) ||
offset > static_cast<size_t>(kint32max)) {
return false;
}
- int ret = base::WritePlatformFile(platform_file_, offset,
- static_cast<const char*>(buffer),
- buffer_len);
+ int ret = base_file_.Write(offset, static_cast<const char*>(buffer),
+ buffer_len);
return (static_cast<size_t>(ret) == buffer_len);
}
bool File::Read(void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (!callback) {
if (completed)
*completed = true;
@@ -118,7 +102,7 @@ bool File::Read(void* buffer, size_t buffer_len, size_t offset,
bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (!callback) {
if (completed)
*completed = true;
@@ -140,17 +124,16 @@ bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
}
bool File::SetLength(size_t length) {
- DCHECK(init_);
+ DCHECK(base_file_.IsValid());
if (length > kuint32max)
return false;
- return base::TruncatePlatformFile(platform_file_, length);
+ return base_file_.SetLength(length);
}
size_t File::GetLength() {
- DCHECK(init_);
- int64 len = base::SeekPlatformFile(platform_file_,
- base::PLATFORM_FILE_FROM_END, 0);
+ DCHECK(base_file_.IsValid());
+ int64 len = base_file_.GetLength();
if (len > static_cast<int64>(kuint32max))
return kuint32max;
@@ -173,8 +156,10 @@ void File::DropPendingIO() {
File::~File() {
- if (IsValid())
- base::ClosePlatformFile(platform_file_);
+}
+
+base::PlatformFile File::platform_file() const {
+ return base_file_.GetPlatformFile();
}
// Runs on a worker thread.
diff --git a/chromium/net/disk_cache/file_win.cc b/chromium/net/disk_cache/blockfile/file_win.cc
index 1492c42c6e4..bf313128b46 100644
--- a/chromium/net/disk_cache/file_win.cc
+++ b/chromium/net/disk_cache/blockfile/file_win.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/file.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
@@ -69,9 +69,10 @@ MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
namespace disk_cache {
-File::File(base::PlatformFile file)
- : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE),
- sync_platform_file_(file) {
+File::File(base::File file)
+ : init_(true),
+ mixed_(true),
+ sync_base_file_(file.Pass()) {
}
bool File::Init(const base::FilePath& name) {
@@ -81,46 +82,31 @@ bool File::Init(const base::FilePath& name) {
DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
- platform_file_ = CreateFile(name.value().c_str(), access, sharing, NULL,
- OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ base_file_ =
+ base::File(CreateFile(name.value().c_str(), access, sharing, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL));
- if (INVALID_HANDLE_VALUE == platform_file_)
+ if (!base_file_.IsValid())
return false;
base::MessageLoopForIO::current()->RegisterIOHandler(
- platform_file_, g_completion_handler.Pointer());
+ base_file_.GetPlatformFile(), g_completion_handler.Pointer());
init_ = true;
- sync_platform_file_ = CreateFile(name.value().c_str(), access, sharing, NULL,
- OPEN_EXISTING, 0, NULL);
+ sync_base_file_ =
+ base::File(CreateFile(name.value().c_str(), access, sharing, NULL,
+ OPEN_EXISTING, 0, NULL));
- if (INVALID_HANDLE_VALUE == sync_platform_file_)
+ if (!sync_base_file_.IsValid())
return false;
return true;
}
-File::~File() {
- if (!init_)
- return;
-
- if (INVALID_HANDLE_VALUE != platform_file_)
- CloseHandle(platform_file_);
- if (INVALID_HANDLE_VALUE != sync_platform_file_)
- CloseHandle(sync_platform_file_);
-}
-
-base::PlatformFile File::platform_file() const {
- DCHECK(init_);
- return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ :
- platform_file_;
-}
-
bool File::IsValid() const {
if (!init_)
return false;
- return (INVALID_HANDLE_VALUE != platform_file_ ||
- INVALID_HANDLE_VALUE != sync_platform_file_);
+ return base_file_.IsValid() || sync_base_file_.IsValid();
}
bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
@@ -128,16 +114,9 @@ bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
if (buffer_len > ULONG_MAX || offset > LONG_MAX)
return false;
- DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
- NULL, FILE_BEGIN);
- if (INVALID_SET_FILE_POINTER == ret)
- return false;
-
- DWORD actual;
- DWORD size = static_cast<DWORD>(buffer_len);
- if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL))
- return false;
- return actual == size;
+ int ret = sync_base_file_.Read(offset, static_cast<char*>(buffer),
+ buffer_len);
+ return static_cast<int>(buffer_len) == ret;
}
bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
@@ -145,16 +124,9 @@ bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
return false;
- DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
- NULL, FILE_BEGIN);
- if (INVALID_SET_FILE_POINTER == ret)
- return false;
-
- DWORD actual;
- DWORD size = static_cast<DWORD>(buffer_len);
- if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL))
- return false;
- return actual == size;
+ int ret = sync_base_file_.Write(offset, static_cast<const char*>(buffer),
+ buffer_len);
+ return static_cast<int>(buffer_len) == ret;
}
// We have to increase the ref counter of the file before performing the IO to
@@ -176,7 +148,8 @@ bool File::Read(void* buffer, size_t buffer_len, size_t offset,
DWORD size = static_cast<DWORD>(buffer_len);
DWORD actual;
- if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) {
+ if (!ReadFile(base_file_.GetPlatformFile(), buffer, size, &actual,
+ data->overlapped())) {
*completed = false;
if (GetLastError() == ERROR_IO_PENDING)
return true;
@@ -204,6 +177,15 @@ bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
return AsyncWrite(buffer, buffer_len, offset, callback, completed);
}
+File::~File() {
+}
+
+base::PlatformFile File::platform_file() const {
+ DCHECK(init_);
+ return base_file_.IsValid() ? base_file_.GetPlatformFile() :
+ sync_base_file_.GetPlatformFile();
+}
+
bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
FileIOCallback* callback, bool* completed) {
DCHECK(init_);
@@ -216,7 +198,8 @@ bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
DWORD size = static_cast<DWORD>(buffer_len);
DWORD actual;
- if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) {
+ if (!WriteFile(base_file_.GetPlatformFile(), buffer, size, &actual,
+ data->overlapped())) {
*completed = false;
if (GetLastError() == ERROR_IO_PENDING)
return true;
diff --git a/chromium/net/disk_cache/blockfile/histogram_macros.h b/chromium/net/disk_cache/blockfile/histogram_macros.h
new file mode 100644
index 00000000000..61cc6eaa733
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/histogram_macros.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2011 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.
+
+// This file contains macros to simplify histogram reporting from the disk
+// cache. The main issue is that we want to have separate histograms for each
+// type of cache (regular vs. media, etc), without adding the complexity of
+// keeping track of a potentially large number of histogram objects that have to
+// survive the backend object that created them.
+
+#ifndef NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_H_
+#define NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_H_
+
+#include "base/metrics/histogram.h"
+
+// -----------------------------------------------------------------------------
+
+// These histograms follow the definition of UMA_HISTOGRAMN_XXX except that
+// the counter is not cached locally.
+
+#define CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ do { \
+ base::HistogramBase* counter = base::Histogram::FactoryGet( \
+ name, min, max, bucket_count, \
+ base::Histogram::kUmaTargetedHistogramFlag); \
+ counter->Add(sample); \
+ } while (0)
+
+#define CACHE_HISTOGRAM_COUNTS(name, sample) CACHE_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000000, 50)
+
+#define CACHE_HISTOGRAM_COUNTS_10000(name, sample) \
+ CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50)
+
+#define CACHE_HISTOGRAM_COUNTS_50000(name, sample) \
+ CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 50000000, 50)
+
+#define CACHE_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ do { \
+ base::HistogramBase* counter = base::Histogram::FactoryTimeGet( \
+ name, min, max, bucket_count, \
+ base::Histogram::kUmaTargetedHistogramFlag); \
+ counter->AddTime(sample); \
+ } while (0)
+
+#define CACHE_HISTOGRAM_TIMES(name, sample) CACHE_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ base::TimeDelta::FromSeconds(10), 50)
+
+#define CACHE_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
+ base::HistogramBase* counter = base::LinearHistogram::FactoryGet( \
+ name, 1, boundary_value, boundary_value + 1, \
+ base::Histogram::kUmaTargetedHistogramFlag); \
+ counter->Add(sample); \
+ } while (0)
+
+#define CACHE_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ CACHE_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+// -----------------------------------------------------------------------------
+
+// HISTOGRAM_HOURS will collect time related data with a granularity of hours
+// and normal values of a few months.
+#define CACHE_HISTOGRAM_HOURS CACHE_HISTOGRAM_COUNTS_10000
+
+// HISTOGRAM_AGE will collect time elapsed since |initial_time|, with a
+// granularity of hours and normal values of a few months.
+#define CACHE_HISTOGRAM_AGE(name, initial_time) \
+ CACHE_HISTOGRAM_COUNTS_10000(name, \
+ (base::Time::Now() - initial_time).InHours())
+
+// HISTOGRAM_AGE_MS will collect time elapsed since |initial_time|, with the
+// normal resolution of the UMA_HISTOGRAM_TIMES.
+#define CACHE_HISTOGRAM_AGE_MS(name, initial_time)\
+ CACHE_HISTOGRAM_TIMES(name, base::TimeTicks::Now() - initial_time)
+
+#define CACHE_HISTOGRAM_CACHE_ERROR(name, sample) \
+ CACHE_HISTOGRAM_ENUMERATION(name, sample, 50)
+
+// Generates a UMA histogram of the given type, generating the proper name for
+// it (asking backend_->HistogramName), and adding the provided sample.
+// For example, to generate a regualar UMA_HISTOGRAM_COUNTS, this macro would
+// be used as:
+// CACHE_UMA(COUNTS, "MyName", 0, 20);
+// CACHE_UMA(COUNTS, "MyExperiment", 530, 55);
+// which roughly translates to:
+// UMA_HISTOGRAM_COUNTS("DiskCache.2.MyName", 20); // "2" is the CacheType.
+// UMA_HISTOGRAM_COUNTS("DiskCache.2.MyExperiment_530", 55);
+//
+#define CACHE_UMA(type, name, experiment, sample) {\
+ const std::string my_name =\
+ CACHE_UMA_BACKEND_IMPL_OBJ->HistogramName(name, experiment);\
+ switch (CACHE_UMA_BACKEND_IMPL_OBJ->cache_type()) {\
+ default:\
+ NOTREACHED();\
+ /* Fall-through. */\
+ case net::DISK_CACHE:\
+ case net::MEDIA_CACHE:\
+ case net::APP_CACHE:\
+ case net::SHADER_CACHE:\
+ case net::PNACL_CACHE:\
+ CACHE_HISTOGRAM_##type(my_name.data(), sample);\
+ break;\
+ }\
+ }
+
+#endif // NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_H_
diff --git a/chromium/net/disk_cache/histogram_macros.h b/chromium/net/disk_cache/blockfile/histogram_macros_v3.h
index 651bce96f2d..16ccd063378 100644
--- a/chromium/net/disk_cache/histogram_macros.h
+++ b/chromium/net/disk_cache/blockfile/histogram_macros_v3.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -8,20 +8,18 @@
// keeping track of a potentially large number of histogram objects that have to
// survive the backend object that created them.
-#ifndef NET_DISK_CACHE_HISTOGRAM_MACROS_H_
-#define NET_DISK_CACHE_HISTOGRAM_MACROS_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_V3_H_
// -----------------------------------------------------------------------------
// These histograms follow the definition of UMA_HISTOGRAMN_XXX except that
// whenever the name changes (the experiment group changes), the histrogram
// object is re-created.
-// Note: These macros are only run on one thread, so the declarations of
-// |counter| was made static (i.e., there will be no race for reinitialization).
#define CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
do { \
- static base::HistogramBase* counter(NULL); \
+ base::HistogramBase* counter(NULL); \
if (!counter || name != counter->histogram_name()) \
counter = base::Histogram::FactoryGet( \
name, min, max, bucket_count, \
@@ -40,7 +38,7 @@
#define CACHE_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
do { \
- static base::HistogramBase* counter(NULL); \
+ base::HistogramBase* counter(NULL); \
if (!counter || name != counter->histogram_name()) \
counter = base::Histogram::FactoryTimeGet( \
name, min, max, bucket_count, \
@@ -53,7 +51,7 @@
base::TimeDelta::FromSeconds(10), 50)
#define CACHE_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
- static base::HistogramBase* counter(NULL); \
+ base::HistogramBase* counter(NULL); \
if (!counter || name != counter->histogram_name()) \
counter = base::LinearHistogram::FactoryGet( \
name, 1, boundary_value, boundary_value + 1, \
@@ -84,44 +82,28 @@
#define CACHE_HISTOGRAM_CACHE_ERROR(name, sample) \
CACHE_HISTOGRAM_ENUMERATION(name, sample, 50)
-#ifdef NET_DISK_CACHE_BACKEND_IMPL_CC_
-#define BACKEND_OBJ this
-#else
-#define BACKEND_OBJ backend_
-#endif
-
// Generates a UMA histogram of the given type, generating the proper name for
// it (asking backend_->HistogramName), and adding the provided sample.
// For example, to generate a regualar UMA_HISTOGRAM_COUNTS, this macro would
// be used as:
-// CACHE_UMA(COUNTS, "MyName", 0, 20);
-// CACHE_UMA(COUNTS, "MyExperiment", 530, 55);
-// which roughly translates to:
-// UMA_HISTOGRAM_COUNTS("DiskCache.2.MyName", 20); // "2" is the CacheType.
-// UMA_HISTOGRAM_COUNTS("DiskCache.2.MyExperiment_530", 55);
+// CACHE_UMA(COUNTS, "MyName", 20);
+// which may translate to:
+// UMA_HISTOGRAM_COUNTS("DiskCache3.MyName_AppCache", 20);
//
-#define CACHE_UMA(type, name, experiment, sample) {\
- const std::string my_name = BACKEND_OBJ->HistogramName(name, experiment);\
- switch (BACKEND_OBJ->cache_type()) {\
+#define CACHE_UMA(type, name, sample) {\
+ const std::string my_name =\
+ CACHE_UMA_BACKEND_IMPL_OBJ->HistogramName(name);\
+ switch (CACHE_UMA_BACKEND_IMPL_OBJ->cache_type()) {\
case net::DISK_CACHE:\
- CACHE_HISTOGRAM_##type(my_name.data(), sample);\
- break;\
case net::MEDIA_CACHE:\
- CACHE_HISTOGRAM_##type(my_name.data(), sample);\
- break;\
case net::APP_CACHE:\
- CACHE_HISTOGRAM_##type(my_name.data(), sample);\
- break;\
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;\
}\
}
-#endif // NET_DISK_CACHE_HISTOGRAM_MACROS_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_HISTOGRAM_MACROS_V3_H_
diff --git a/chromium/net/disk_cache/in_flight_backend_io.cc b/chromium/net/disk_cache/blockfile/in_flight_backend_io.cc
index 3ed9e4dd191..c9df6808b35 100644
--- a/chromium/net/disk_cache/in_flight_backend_io.cc
+++ b/chromium/net/disk_cache/blockfile/in_flight_backend_io.cc
@@ -2,16 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/in_flight_backend_io.h"
+#include "net/disk_cache/blockfile/in_flight_backend_io.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/histogram_macros.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
namespace disk_cache {
diff --git a/chromium/net/disk_cache/in_flight_backend_io.h b/chromium/net/disk_cache/blockfile/in_flight_backend_io.h
index 4ff081d07c5..70438ab81ce 100644
--- a/chromium/net/disk_cache/in_flight_backend_io.h
+++ b/chromium/net/disk_cache/blockfile/in_flight_backend_io.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
-#define NET_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_BACKEND_IO_H_
+#define NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_BACKEND_IO_H_
#include <list>
#include <string>
@@ -12,7 +12,7 @@
#include "base/time/time.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
-#include "net/disk_cache/in_flight_io.h"
+#include "net/disk_cache/blockfile/in_flight_io.h"
namespace disk_cache {
@@ -220,4 +220,4 @@ class InFlightBackendIO : public InFlightIO {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_BACKEND_IO_H_
diff --git a/chromium/net/disk_cache/in_flight_io.cc b/chromium/net/disk_cache/blockfile/in_flight_io.cc
index 467814f22f1..6e9485d5f11 100644
--- a/chromium/net/disk_cache/in_flight_io.cc
+++ b/chromium/net/disk_cache/blockfile/in_flight_io.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/in_flight_io.h"
+#include "net/disk_cache/blockfile/in_flight_io.h"
#include "base/bind.h"
#include "base/location.h"
diff --git a/chromium/net/disk_cache/in_flight_io.h b/chromium/net/disk_cache/blockfile/in_flight_io.h
index 2a3330445ba..78b6f4d444f 100644
--- a/chromium/net/disk_cache/in_flight_io.h
+++ b/chromium/net/disk_cache/blockfile/in_flight_io.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_IN_FLIGHT_IO_H_
-#define NET_DISK_CACHE_IN_FLIGHT_IO_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_IO_H_
+#define NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_IO_H_
#include <set>
@@ -133,4 +133,4 @@ class InFlightIO {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_IN_FLIGHT_IO_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_IN_FLIGHT_IO_H_
diff --git a/chromium/net/disk_cache/blockfile/index_table_v3.cc b/chromium/net/disk_cache/blockfile/index_table_v3.cc
new file mode 100644
index 00000000000..999d5ac6cbd
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/index_table_v3.cc
@@ -0,0 +1,1149 @@
+// Copyright 2014 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/blockfile/index_table_v3.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+
+#include "base/bits.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+
+using base::Time;
+using base::TimeDelta;
+using disk_cache::CellInfo;
+using disk_cache::CellList;
+using disk_cache::IndexCell;
+using disk_cache::IndexIterator;
+
+namespace {
+
+// The following constants describe the bitfields of an IndexCell so they are
+// implicitly synchronized with the descrption of IndexCell on file_format_v3.h.
+const uint64 kCellLocationMask = (1 << 22) - 1;
+const uint64 kCellIdMask = (1 << 18) - 1;
+const uint64 kCellTimestampMask = (1 << 20) - 1;
+const uint64 kCellReuseMask = (1 << 4) - 1;
+const uint8 kCellStateMask = (1 << 3) - 1;
+const uint8 kCellGroupMask = (1 << 3) - 1;
+const uint8 kCellSumMask = (1 << 2) - 1;
+
+const uint64 kCellSmallTableLocationMask = (1 << 16) - 1;
+const uint64 kCellSmallTableIdMask = (1 << 24) - 1;
+
+const int kCellIdOffset = 22;
+const int kCellTimestampOffset = 40;
+const int kCellReuseOffset = 60;
+const int kCellGroupOffset = 3;
+const int kCellSumOffset = 6;
+
+const int kCellSmallTableIdOffset = 16;
+
+// The number of bits that a hash has to be shifted to grab the part that
+// defines the cell id.
+const int kHashShift = 14;
+const int kSmallTableHashShift = 8;
+
+// Unfortunately we have to break the abstaction a little here: the file number
+// where entries are stored is outside of the control of this code, and it is
+// usually part of the stored address. However, for small tables we only store
+// 16 bits of the address so the file number is never stored on a cell. We have
+// to infere the file number from the type of entry (normal vs evicted), and
+// the knowledge that given that the table will not keep more than 64k entries,
+// a single file of each type is enough.
+const int kEntriesFile = disk_cache::BLOCK_ENTRIES - 1;
+const int kEvictedEntriesFile = disk_cache::BLOCK_EVICTED - 1;
+const int kMaxLocation = 1 << 22;
+const int kMinFileNumber = 1 << 16;
+
+uint32 GetCellLocation(const IndexCell& cell) {
+ return cell.first_part & kCellLocationMask;
+}
+
+uint32 GetCellSmallTableLocation(const IndexCell& cell) {
+ return cell.first_part & kCellSmallTableLocationMask;
+}
+
+uint32 GetCellId(const IndexCell& cell) {
+ return (cell.first_part >> kCellIdOffset) & kCellIdMask;
+}
+
+uint32 GetCellSmallTableId(const IndexCell& cell) {
+ return (cell.first_part >> kCellSmallTableIdOffset) &
+ kCellSmallTableIdMask;
+}
+
+int GetCellTimestamp(const IndexCell& cell) {
+ return (cell.first_part >> kCellTimestampOffset) & kCellTimestampMask;
+}
+
+int GetCellReuse(const IndexCell& cell) {
+ return (cell.first_part >> kCellReuseOffset) & kCellReuseMask;
+}
+
+int GetCellState(const IndexCell& cell) {
+ return cell.last_part & kCellStateMask;
+}
+
+int GetCellGroup(const IndexCell& cell) {
+ return (cell.last_part >> kCellGroupOffset) & kCellGroupMask;
+}
+
+int GetCellSum(const IndexCell& cell) {
+ return (cell.last_part >> kCellSumOffset) & kCellSumMask;
+}
+
+void SetCellLocation(IndexCell* cell, uint32 address) {
+ DCHECK_LE(address, static_cast<uint32>(kCellLocationMask));
+ cell->first_part &= ~kCellLocationMask;
+ cell->first_part |= address;
+}
+
+void SetCellSmallTableLocation(IndexCell* cell, uint32 address) {
+ DCHECK_LE(address, static_cast<uint32>(kCellSmallTableLocationMask));
+ cell->first_part &= ~kCellSmallTableLocationMask;
+ cell->first_part |= address;
+}
+
+void SetCellId(IndexCell* cell, uint32 hash) {
+ DCHECK_LE(hash, static_cast<uint32>(kCellIdMask));
+ cell->first_part &= ~(kCellIdMask << kCellIdOffset);
+ cell->first_part |= static_cast<int64>(hash) << kCellIdOffset;
+}
+
+void SetCellSmallTableId(IndexCell* cell, uint32 hash) {
+ DCHECK_LE(hash, static_cast<uint32>(kCellSmallTableIdMask));
+ cell->first_part &= ~(kCellSmallTableIdMask << kCellSmallTableIdOffset);
+ cell->first_part |= static_cast<int64>(hash) << kCellSmallTableIdOffset;
+}
+
+void SetCellTimestamp(IndexCell* cell, int timestamp) {
+ DCHECK_LT(timestamp, 1 << 20);
+ DCHECK_GE(timestamp, 0);
+ cell->first_part &= ~(kCellTimestampMask << kCellTimestampOffset);
+ cell->first_part |= static_cast<int64>(timestamp) << kCellTimestampOffset;
+}
+
+void SetCellReuse(IndexCell* cell, int count) {
+ DCHECK_LT(count, 16);
+ DCHECK_GE(count, 0);
+ cell->first_part &= ~(kCellReuseMask << kCellReuseOffset);
+ cell->first_part |= static_cast<int64>(count) << kCellReuseOffset;
+}
+
+void SetCellState(IndexCell* cell, disk_cache::EntryState state) {
+ cell->last_part &= ~kCellStateMask;
+ cell->last_part |= state;
+}
+
+void SetCellGroup(IndexCell* cell, disk_cache::EntryGroup group) {
+ cell->last_part &= ~(kCellGroupMask << kCellGroupOffset);
+ cell->last_part |= group << kCellGroupOffset;
+}
+
+void SetCellSum(IndexCell* cell, int sum) {
+ DCHECK_LT(sum, 4);
+ DCHECK_GE(sum, 0);
+ cell->last_part &= ~(kCellSumMask << kCellSumOffset);
+ cell->last_part |= sum << kCellSumOffset;
+}
+
+// This is a very particular way to calculate the sum, so it will not match if
+// compared a gainst a pure 2 bit, modulo 2 sum.
+int CalculateCellSum(const IndexCell& cell) {
+ uint32* words = bit_cast<uint32*>(&cell);
+ uint8* bytes = bit_cast<uint8*>(&cell);
+ uint32 result = words[0] + words[1];
+ result += result >> 16;
+ result += (result >> 8) + (bytes[8] & 0x3f);
+ result += result >> 4;
+ result += result >> 2;
+ return result & 3;
+}
+
+bool SanityCheck(const IndexCell& cell) {
+ if (GetCellSum(cell) != CalculateCellSum(cell))
+ return false;
+
+ if (GetCellState(cell) > disk_cache::ENTRY_USED ||
+ GetCellGroup(cell) == disk_cache::ENTRY_RESERVED ||
+ GetCellGroup(cell) > disk_cache::ENTRY_EVICTED) {
+ return false;
+ }
+
+ return true;
+}
+
+int FileNumberFromLocation(int location) {
+ return location / kMinFileNumber;
+}
+
+int StartBlockFromLocation(int location) {
+ return location % kMinFileNumber;
+}
+
+bool IsValidAddress(disk_cache::Addr address) {
+ if (!address.is_initialized() ||
+ (address.file_type() != disk_cache::BLOCK_EVICTED &&
+ address.file_type() != disk_cache::BLOCK_ENTRIES)) {
+ return false;
+ }
+
+ return address.FileNumber() < FileNumberFromLocation(kMaxLocation);
+}
+
+bool IsNormalState(const IndexCell& cell) {
+ disk_cache::EntryState state =
+ static_cast<disk_cache::EntryState>(GetCellState(cell));
+ DCHECK_NE(state, disk_cache::ENTRY_FREE);
+ return state != disk_cache::ENTRY_DELETED &&
+ state != disk_cache::ENTRY_FIXING;
+}
+
+inline int GetNextBucket(int min_bucket_num, int max_bucket_num,
+ disk_cache::IndexBucket* table,
+ disk_cache::IndexBucket** bucket) {
+ if (!(*bucket)->next)
+ return 0;
+
+ int bucket_num = (*bucket)->next / disk_cache::kCellsPerBucket;
+ if (bucket_num < min_bucket_num || bucket_num > max_bucket_num) {
+ // The next bucket must fall within the extra table. Note that this is not
+ // an uncommon path as growing the table may not cleanup the link from the
+ // main table to the extra table, and that cleanup is performed here when
+ // accessing that bucket for the first time. This behavior has to change if
+ // the tables are ever shrinked.
+ (*bucket)->next = 0;
+ return 0;
+ }
+ *bucket = &table[bucket_num - min_bucket_num];
+ return bucket_num;
+}
+
+// Updates the |iterator| with the current |cell|. This cell may cause all
+// previous cells to be deleted (when a new target timestamp is found), the cell
+// may be added to the list (if it matches the target timestamp), or may it be
+// ignored.
+void UpdateIterator(const disk_cache::EntryCell& cell,
+ int limit_time,
+ IndexIterator* iterator) {
+ int time = cell.GetTimestamp();
+ // Look for not interesting times.
+ if (iterator->forward && time <= limit_time)
+ return;
+ if (!iterator->forward && time >= limit_time)
+ return;
+
+ if ((iterator->forward && time < iterator->timestamp) ||
+ (!iterator->forward && time > iterator->timestamp)) {
+ // This timestamp is better than the one we had.
+ iterator->timestamp = time;
+ iterator->cells.clear();
+ }
+ if (time == iterator->timestamp) {
+ CellInfo cell_info = { cell.hash(), cell.GetAddress() };
+ iterator->cells.push_back(cell_info);
+ }
+}
+
+void InitIterator(IndexIterator* iterator) {
+ iterator->cells.clear();
+ iterator->timestamp = iterator->forward ? kint32max : 0;
+}
+
+} // namespace
+
+namespace disk_cache {
+
+EntryCell::~EntryCell() {
+}
+
+bool EntryCell::IsValid() const {
+ return GetCellLocation(cell_) != 0;
+}
+
+// This code has to map the cell address (up to 22 bits) to a general cache Addr
+// (up to 24 bits of general addressing). It also set the implied file_number
+// in the case of small tables. See also the comment by the definition of
+// kEntriesFile.
+Addr EntryCell::GetAddress() const {
+ uint32 location = GetLocation();
+ int file_number = FileNumberFromLocation(location);
+ if (small_table_) {
+ DCHECK_EQ(0, file_number);
+ file_number = (GetGroup() == ENTRY_EVICTED) ? kEvictedEntriesFile :
+ kEntriesFile;
+ }
+ DCHECK_NE(0, file_number);
+ FileType file_type = (GetGroup() == ENTRY_EVICTED) ? BLOCK_EVICTED :
+ BLOCK_ENTRIES;
+ return Addr(file_type, 1, file_number, StartBlockFromLocation(location));
+}
+
+EntryState EntryCell::GetState() const {
+ return static_cast<EntryState>(GetCellState(cell_));
+}
+
+EntryGroup EntryCell::GetGroup() const {
+ return static_cast<EntryGroup>(GetCellGroup(cell_));
+}
+
+int EntryCell::GetReuse() const {
+ return GetCellReuse(cell_);
+}
+
+int EntryCell::GetTimestamp() const {
+ return GetCellTimestamp(cell_);
+}
+
+void EntryCell::SetState(EntryState state) {
+ SetCellState(&cell_, state);
+}
+
+void EntryCell::SetGroup(EntryGroup group) {
+ SetCellGroup(&cell_, group);
+}
+
+void EntryCell::SetReuse(int count) {
+ SetCellReuse(&cell_, count);
+}
+
+void EntryCell::SetTimestamp(int timestamp) {
+ SetCellTimestamp(&cell_, timestamp);
+}
+
+// Static.
+EntryCell EntryCell::GetEntryCellForTest(int32 cell_num,
+ uint32 hash,
+ Addr address,
+ IndexCell* cell,
+ bool small_table) {
+ if (cell) {
+ EntryCell entry_cell(cell_num, hash, *cell, small_table);
+ return entry_cell;
+ }
+
+ return EntryCell(cell_num, hash, address, small_table);
+}
+
+void EntryCell::SerializaForTest(IndexCell* destination) {
+ FixSum();
+ Serialize(destination);
+}
+
+EntryCell::EntryCell() : cell_num_(0), hash_(0), small_table_(false) {
+ cell_.Clear();
+}
+
+EntryCell::EntryCell(int32 cell_num,
+ uint32 hash,
+ Addr address,
+ bool small_table)
+ : cell_num_(cell_num),
+ hash_(hash),
+ small_table_(small_table) {
+ DCHECK(IsValidAddress(address) || !address.value());
+
+ cell_.Clear();
+ SetCellState(&cell_, ENTRY_NEW);
+ SetCellGroup(&cell_, ENTRY_NO_USE);
+ if (small_table) {
+ DCHECK(address.FileNumber() == kEntriesFile ||
+ address.FileNumber() == kEvictedEntriesFile);
+ SetCellSmallTableLocation(&cell_, address.start_block());
+ SetCellSmallTableId(&cell_, hash >> kSmallTableHashShift);
+ } else {
+ uint32 location = address.FileNumber() << 16 | address.start_block();
+ SetCellLocation(&cell_, location);
+ SetCellId(&cell_, hash >> kHashShift);
+ }
+}
+
+EntryCell::EntryCell(int32 cell_num,
+ uint32 hash,
+ const IndexCell& cell,
+ bool small_table)
+ : cell_num_(cell_num),
+ hash_(hash),
+ cell_(cell),
+ small_table_(small_table) {
+}
+
+void EntryCell::FixSum() {
+ SetCellSum(&cell_, CalculateCellSum(cell_));
+}
+
+uint32 EntryCell::GetLocation() const {
+ if (small_table_)
+ return GetCellSmallTableLocation(cell_);
+
+ return GetCellLocation(cell_);
+}
+
+uint32 EntryCell::RecomputeHash() {
+ if (small_table_) {
+ hash_ &= (1 << kSmallTableHashShift) - 1;
+ hash_ |= GetCellSmallTableId(cell_) << kSmallTableHashShift;
+ return hash_;
+ }
+
+ hash_ &= (1 << kHashShift) - 1;
+ hash_ |= GetCellId(cell_) << kHashShift;
+ return hash_;
+}
+
+void EntryCell::Serialize(IndexCell* destination) const {
+ *destination = cell_;
+}
+
+EntrySet::EntrySet() : evicted_count(0), current(0) {
+}
+
+EntrySet::~EntrySet() {
+}
+
+IndexIterator::IndexIterator() {
+}
+
+IndexIterator::~IndexIterator() {
+}
+
+IndexTableInitData::IndexTableInitData() {
+}
+
+IndexTableInitData::~IndexTableInitData() {
+}
+
+// -----------------------------------------------------------------------
+
+IndexTable::IndexTable(IndexTableBackend* backend)
+ : backend_(backend),
+ header_(NULL),
+ main_table_(NULL),
+ extra_table_(NULL),
+ modified_(false),
+ small_table_(false) {
+}
+
+IndexTable::~IndexTable() {
+}
+
+// For a general description of the index tables see:
+// http://www.chromium.org/developers/design-documents/network-stack/disk-cache/disk-cache-v3#TOC-Index
+//
+// The index is split between two tables: the main_table_ and the extra_table_.
+// The main table can grow only by doubling its number of cells, while the
+// extra table can grow slowly, because it only contain cells that overflow
+// from the main table. In order to locate a given cell, part of the hash is
+// used directly as an index into the main table; once that bucket is located,
+// all cells with that partial hash (i.e., belonging to that bucket) are
+// inspected, and if present, the next bucket (located on the extra table) is
+// then located. For more information on bucket chaining see:
+// http://www.chromium.org/developers/design-documents/network-stack/disk-cache/disk-cache-v3#TOC-Buckets
+//
+// There are two cases when increasing the size:
+// - Doubling the size of the main table
+// - Adding more entries to the extra table
+//
+// For example, consider a 64k main table with 8k cells on the extra table (for
+// a total of 72k cells). Init can be called to add another 8k cells at the end
+// (grow to 80k cells). When the size of the extra table approaches 64k, Init
+// can be called to double the main table (to 128k) and go back to a small extra
+// table.
+void IndexTable::Init(IndexTableInitData* params) {
+ bool growing = header_ != NULL;
+ scoped_ptr<IndexBucket[]> old_extra_table;
+ header_ = &params->index_bitmap->header;
+
+ if (params->main_table) {
+ if (main_table_) {
+ // This is doubling the size of main table.
+ DCHECK_EQ(base::bits::Log2Floor(header_->table_len),
+ base::bits::Log2Floor(backup_header_->table_len) + 1);
+ int extra_size = (header()->max_bucket - mask_) * kCellsPerBucket;
+ DCHECK_GE(extra_size, 0);
+
+ // Doubling the size implies deleting the extra table and moving as many
+ // cells as we can to the main table, so we first copy the old one. This
+ // is not required when just growing the extra table because we don't
+ // move any cell in that case.
+ old_extra_table.reset(new IndexBucket[extra_size]);
+ memcpy(old_extra_table.get(), extra_table_,
+ extra_size * sizeof(IndexBucket));
+ memset(params->extra_table, 0, extra_size * sizeof(IndexBucket));
+ }
+ main_table_ = params->main_table;
+ }
+ DCHECK(main_table_);
+ extra_table_ = params->extra_table;
+
+ // extra_bits_ is really measured against table-size specific values.
+ const int kMaxAbsoluteExtraBits = 12; // From smallest to largest table.
+ const int kMaxExtraBitsSmallTable = 6; // From smallest to 64K table.
+
+ extra_bits_ = base::bits::Log2Floor(header_->table_len) -
+ base::bits::Log2Floor(kBaseTableLen);
+ DCHECK_GE(extra_bits_, 0);
+ DCHECK_LT(extra_bits_, kMaxAbsoluteExtraBits);
+
+ // Note that following the previous code the constants could be derived as
+ // kMaxAbsoluteExtraBits = base::bits::Log2Floor(max table len) -
+ // base::bits::Log2Floor(kBaseTableLen);
+ // = 22 - base::bits::Log2Floor(1024) = 22 - 10;
+ // kMaxExtraBitsSmallTable = base::bits::Log2Floor(max 16 bit table) - 10.
+
+ mask_ = ((kBaseTableLen / kCellsPerBucket) << extra_bits_) - 1;
+ small_table_ = extra_bits_ < kMaxExtraBitsSmallTable;
+ if (!small_table_)
+ extra_bits_ -= kMaxExtraBitsSmallTable;
+
+ // table_len keeps the max number of cells stored by the index. We need a
+ // bitmap with 1 bit per cell, and that bitmap has num_words 32-bit words.
+ int num_words = (header_->table_len + 31) / 32;
+
+ if (old_extra_table) {
+ // All the cells from the extra table are moving to the new tables so before
+ // creating the bitmaps, clear the part of the bitmap referring to the extra
+ // table.
+ int old_main_table_bit_words = ((mask_ >> 1) + 1) * kCellsPerBucket / 32;
+ DCHECK_GT(num_words, old_main_table_bit_words);
+ memset(params->index_bitmap->bitmap + old_main_table_bit_words, 0,
+ (num_words - old_main_table_bit_words) * sizeof(int32));
+
+ DCHECK(growing);
+ int old_num_words = (backup_header_.get()->table_len + 31) / 32;
+ DCHECK_GT(old_num_words, old_main_table_bit_words);
+ memset(backup_bitmap_storage_.get() + old_main_table_bit_words, 0,
+ (old_num_words - old_main_table_bit_words) * sizeof(int32));
+ }
+ bitmap_.reset(new Bitmap(params->index_bitmap->bitmap, header_->table_len,
+ num_words));
+
+ if (growing) {
+ int old_num_words = (backup_header_.get()->table_len + 31) / 32;
+ DCHECK_GE(num_words, old_num_words);
+ scoped_ptr<uint32[]> storage(new uint32[num_words]);
+ memcpy(storage.get(), backup_bitmap_storage_.get(),
+ old_num_words * sizeof(int32));
+ memset(storage.get() + old_num_words, 0,
+ (num_words - old_num_words) * sizeof(int32));
+
+ backup_bitmap_storage_.swap(storage);
+ backup_header_->table_len = header_->table_len;
+ } else {
+ backup_bitmap_storage_.reset(params->backup_bitmap.release());
+ backup_header_.reset(params->backup_header.release());
+ }
+
+ num_words = (backup_header_->table_len + 31) / 32;
+ backup_bitmap_.reset(new Bitmap(backup_bitmap_storage_.get(),
+ backup_header_->table_len, num_words));
+ if (old_extra_table)
+ MoveCells(old_extra_table.get());
+
+ if (small_table_)
+ DCHECK(header_->flags & SMALL_CACHE);
+
+ // All tables and backups are needed for operation.
+ DCHECK(main_table_);
+ DCHECK(extra_table_);
+ DCHECK(bitmap_.get());
+}
+
+void IndexTable::Shutdown() {
+ header_ = NULL;
+ main_table_ = NULL;
+ extra_table_ = NULL;
+ bitmap_.reset();
+ backup_bitmap_.reset();
+ backup_header_.reset();
+ backup_bitmap_storage_.reset();
+ modified_ = false;
+}
+
+// The general method for locating cells is to:
+// 1. Get the first bucket. This usually means directly indexing the table (as
+// this method does), or iterating through all possible buckets.
+// 2. Iterate through all the cells in that first bucket.
+// 3. If there is a linked bucket, locate it directly in the extra table.
+// 4. Go back to 2, as needed.
+//
+// One consequence of this pattern is that we never start looking at buckets in
+// the extra table, unless we are following a link from the main table.
+EntrySet IndexTable::LookupEntries(uint32 hash) {
+ EntrySet entries;
+ int bucket_num = static_cast<int>(hash & mask_);
+ IndexBucket* bucket = &main_table_[bucket_num];
+ do {
+ for (int i = 0; i < kCellsPerBucket; i++) {
+ IndexCell* current_cell = &bucket->cells[i];
+ if (!GetLocation(*current_cell))
+ continue;
+ if (!SanityCheck(*current_cell)) {
+ NOTREACHED();
+ int cell_num = bucket_num * kCellsPerBucket + i;
+ current_cell->Clear();
+ bitmap_->Set(cell_num, false);
+ backup_bitmap_->Set(cell_num, false);
+ modified_ = true;
+ continue;
+ }
+ int cell_num = bucket_num * kCellsPerBucket + i;
+ if (MisplacedHash(*current_cell, hash)) {
+ HandleMisplacedCell(current_cell, cell_num, hash & mask_);
+ } else if (IsHashMatch(*current_cell, hash)) {
+ EntryCell entry_cell(cell_num, hash, *current_cell, small_table_);
+ CheckState(entry_cell);
+ if (entry_cell.GetState() != ENTRY_DELETED) {
+ entries.cells.push_back(entry_cell);
+ if (entry_cell.GetGroup() == ENTRY_EVICTED)
+ entries.evicted_count++;
+ }
+ }
+ }
+ bucket_num = GetNextBucket(mask_ + 1, header()->max_bucket, extra_table_,
+ &bucket);
+ } while (bucket_num);
+ return entries;
+}
+
+EntryCell IndexTable::CreateEntryCell(uint32 hash, Addr address) {
+ DCHECK(IsValidAddress(address));
+ DCHECK(address.FileNumber() || address.start_block());
+
+ int bucket_num = static_cast<int>(hash & mask_);
+ int cell_num = 0;
+ IndexBucket* bucket = &main_table_[bucket_num];
+ IndexCell* current_cell = NULL;
+ bool found = false;
+ do {
+ for (int i = 0; i < kCellsPerBucket && !found; i++) {
+ current_cell = &bucket->cells[i];
+ if (!GetLocation(*current_cell)) {
+ cell_num = bucket_num * kCellsPerBucket + i;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ bucket_num = GetNextBucket(mask_ + 1, header()->max_bucket, extra_table_,
+ &bucket);
+ } while (bucket_num);
+
+ if (!found) {
+ bucket_num = NewExtraBucket();
+ if (bucket_num) {
+ cell_num = bucket_num * kCellsPerBucket;
+ bucket->next = cell_num;
+ bucket = &extra_table_[bucket_num - (mask_ + 1)];
+ bucket->hash = hash & mask_;
+ found = true;
+ } else {
+ // address 0 is a reserved value, and the caller interprets it as invalid.
+ address.set_value(0);
+ }
+ }
+
+ EntryCell entry_cell(cell_num, hash, address, small_table_);
+ if (address.file_type() == BLOCK_EVICTED)
+ entry_cell.SetGroup(ENTRY_EVICTED);
+ else
+ entry_cell.SetGroup(ENTRY_NO_USE);
+ Save(&entry_cell);
+
+ if (found) {
+ bitmap_->Set(cell_num, true);
+ backup_bitmap_->Set(cell_num, true);
+ header()->used_cells++;
+ modified_ = true;
+ }
+
+ return entry_cell;
+}
+
+EntryCell IndexTable::FindEntryCell(uint32 hash, Addr address) {
+ return FindEntryCellImpl(hash, address, false);
+}
+
+int IndexTable::CalculateTimestamp(Time time) {
+ TimeDelta delta = time - Time::FromInternalValue(header_->base_time);
+ return std::max(delta.InMinutes(), 0);
+}
+
+base::Time IndexTable::TimeFromTimestamp(int timestamp) {
+ return Time::FromInternalValue(header_->base_time) +
+ TimeDelta::FromMinutes(timestamp);
+}
+
+void IndexTable::SetSate(uint32 hash, Addr address, EntryState state) {
+ EntryCell cell = FindEntryCellImpl(hash, address, state == ENTRY_FREE);
+ if (!cell.IsValid()) {
+ NOTREACHED();
+ return;
+ }
+
+ EntryState old_state = cell.GetState();
+ switch (state) {
+ case ENTRY_FREE:
+ DCHECK_EQ(old_state, ENTRY_DELETED);
+ break;
+ case ENTRY_NEW:
+ DCHECK_EQ(old_state, ENTRY_FREE);
+ break;
+ case ENTRY_OPEN:
+ DCHECK_EQ(old_state, ENTRY_USED);
+ break;
+ case ENTRY_MODIFIED:
+ DCHECK_EQ(old_state, ENTRY_OPEN);
+ break;
+ case ENTRY_DELETED:
+ DCHECK(old_state == ENTRY_NEW || old_state == ENTRY_OPEN ||
+ old_state == ENTRY_MODIFIED);
+ break;
+ case ENTRY_USED:
+ DCHECK(old_state == ENTRY_NEW || old_state == ENTRY_OPEN ||
+ old_state == ENTRY_MODIFIED);
+ break;
+ case ENTRY_FIXING:
+ break;
+ };
+
+ modified_ = true;
+ if (state == ENTRY_DELETED) {
+ bitmap_->Set(cell.cell_num(), false);
+ backup_bitmap_->Set(cell.cell_num(), false);
+ } else if (state == ENTRY_FREE) {
+ cell.Clear();
+ Write(cell);
+ header()->used_cells--;
+ return;
+ }
+ cell.SetState(state);
+
+ Save(&cell);
+}
+
+void IndexTable::UpdateTime(uint32 hash, Addr address, base::Time current) {
+ EntryCell cell = FindEntryCell(hash, address);
+ if (!cell.IsValid())
+ return;
+
+ int minutes = CalculateTimestamp(current);
+
+ // Keep about 3 months of headroom.
+ const int kMaxTimestamp = (1 << 20) - 60 * 24 * 90;
+ if (minutes > kMaxTimestamp) {
+ // TODO(rvargas):
+ // Update header->old_time and trigger a timer
+ // Rebaseline timestamps and don't update sums
+ // Start a timer (about 2 backups)
+ // fix all ckecksums and trigger another timer
+ // update header->old_time because rebaseline is done.
+ minutes = std::min(minutes, (1 << 20) - 1);
+ }
+
+ cell.SetTimestamp(minutes);
+ Save(&cell);
+}
+
+void IndexTable::Save(EntryCell* cell) {
+ cell->FixSum();
+ Write(*cell);
+}
+
+void IndexTable::GetOldest(IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use) {
+ no_use->forward = true;
+ low_use->forward = true;
+ high_use->forward = true;
+ InitIterator(no_use);
+ InitIterator(low_use);
+ InitIterator(high_use);
+
+ WalkTables(-1, no_use, low_use, high_use);
+}
+
+bool IndexTable::GetNextCells(IndexIterator* iterator) {
+ int current_time = iterator->timestamp;
+ InitIterator(iterator);
+
+ WalkTables(current_time, iterator, iterator, iterator);
+ return !iterator->cells.empty();
+}
+
+void IndexTable::OnBackupTimer() {
+ if (!modified_)
+ return;
+
+ int num_words = (header_->table_len + 31) / 32;
+ int num_bytes = num_words * 4 + static_cast<int>(sizeof(*header_));
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(num_bytes));
+ memcpy(buffer->data(), header_, sizeof(*header_));
+ memcpy(buffer->data() + sizeof(*header_), backup_bitmap_storage_.get(),
+ num_words * 4);
+ backend_->SaveIndex(buffer, num_bytes);
+ modified_ = false;
+}
+
+// -----------------------------------------------------------------------
+
+EntryCell IndexTable::FindEntryCellImpl(uint32 hash, Addr address,
+ bool allow_deleted) {
+ int bucket_num = static_cast<int>(hash & mask_);
+ IndexBucket* bucket = &main_table_[bucket_num];
+ do {
+ for (int i = 0; i < kCellsPerBucket; i++) {
+ IndexCell* current_cell = &bucket->cells[i];
+ if (!GetLocation(*current_cell))
+ continue;
+ DCHECK(SanityCheck(*current_cell));
+ if (IsHashMatch(*current_cell, hash)) {
+ // We have a match.
+ int cell_num = bucket_num * kCellsPerBucket + i;
+ EntryCell entry_cell(cell_num, hash, *current_cell, small_table_);
+ if (entry_cell.GetAddress() != address)
+ continue;
+
+ if (!allow_deleted && entry_cell.GetState() == ENTRY_DELETED)
+ continue;
+
+ return entry_cell;
+ }
+ }
+ bucket_num = GetNextBucket(mask_ + 1, header()->max_bucket, extra_table_,
+ &bucket);
+ } while (bucket_num);
+ return EntryCell();
+}
+
+void IndexTable::CheckState(const EntryCell& cell) {
+ int current_state = cell.GetState();
+ if (current_state != ENTRY_FIXING) {
+ bool present = ((current_state & 3) != 0); // Look at the last two bits.
+ if (present != bitmap_->Get(cell.cell_num()) ||
+ present != backup_bitmap_->Get(cell.cell_num())) {
+ // There's a mismatch.
+ if (current_state == ENTRY_DELETED) {
+ // We were in the process of deleting this entry. Finish now.
+ backend_->DeleteCell(cell);
+ } else {
+ current_state = ENTRY_FIXING;
+ EntryCell bad_cell(cell);
+ bad_cell.SetState(ENTRY_FIXING);
+ Save(&bad_cell);
+ }
+ }
+ }
+
+ if (current_state == ENTRY_FIXING)
+ backend_->FixCell(cell);
+}
+
+void IndexTable::Write(const EntryCell& cell) {
+ IndexBucket* bucket = NULL;
+ int bucket_num = cell.cell_num() / kCellsPerBucket;
+ if (bucket_num < static_cast<int32>(mask_ + 1)) {
+ bucket = &main_table_[bucket_num];
+ } else {
+ DCHECK_LE(bucket_num, header()->max_bucket);
+ bucket = &extra_table_[bucket_num - (mask_ + 1)];
+ }
+
+ int cell_number = cell.cell_num() % kCellsPerBucket;
+ if (GetLocation(bucket->cells[cell_number]) && cell.GetLocation()) {
+ DCHECK_EQ(cell.GetLocation(),
+ GetLocation(bucket->cells[cell_number]));
+ }
+ cell.Serialize(&bucket->cells[cell_number]);
+}
+
+int IndexTable::NewExtraBucket() {
+ int safe_window = (header()->table_len < kNumExtraBlocks * 2) ?
+ kNumExtraBlocks / 4 : kNumExtraBlocks;
+ if (header()->table_len - header()->max_bucket * kCellsPerBucket <
+ safe_window) {
+ backend_->GrowIndex();
+ }
+
+ if (header()->max_bucket * kCellsPerBucket ==
+ header()->table_len - kCellsPerBucket) {
+ return 0;
+ }
+
+ header()->max_bucket++;
+ return header()->max_bucket;
+}
+
+void IndexTable::WalkTables(int limit_time,
+ IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use) {
+ header_->num_no_use_entries = 0;
+ header_->num_low_use_entries = 0;
+ header_->num_high_use_entries = 0;
+ header_->num_evicted_entries = 0;
+
+ for (int i = 0; i < static_cast<int32>(mask_ + 1); i++) {
+ int bucket_num = i;
+ IndexBucket* bucket = &main_table_[i];
+ do {
+ UpdateFromBucket(bucket, i, limit_time, no_use, low_use, high_use);
+
+ bucket_num = GetNextBucket(mask_ + 1, header()->max_bucket, extra_table_,
+ &bucket);
+ } while (bucket_num);
+ }
+ header_->num_entries = header_->num_no_use_entries +
+ header_->num_low_use_entries +
+ header_->num_high_use_entries +
+ header_->num_evicted_entries;
+ modified_ = true;
+}
+
+void IndexTable::UpdateFromBucket(IndexBucket* bucket, int bucket_hash,
+ int limit_time,
+ IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use) {
+ for (int i = 0; i < kCellsPerBucket; i++) {
+ IndexCell& current_cell = bucket->cells[i];
+ if (!GetLocation(current_cell))
+ continue;
+ DCHECK(SanityCheck(current_cell));
+ if (!IsNormalState(current_cell))
+ continue;
+
+ EntryCell entry_cell(0, GetFullHash(current_cell, bucket_hash),
+ current_cell, small_table_);
+ switch (GetCellGroup(current_cell)) {
+ case ENTRY_NO_USE:
+ UpdateIterator(entry_cell, limit_time, no_use);
+ header_->num_no_use_entries++;
+ break;
+ case ENTRY_LOW_USE:
+ UpdateIterator(entry_cell, limit_time, low_use);
+ header_->num_low_use_entries++;
+ break;
+ case ENTRY_HIGH_USE:
+ UpdateIterator(entry_cell, limit_time, high_use);
+ header_->num_high_use_entries++;
+ break;
+ case ENTRY_EVICTED:
+ header_->num_evicted_entries++;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+}
+
+// This code is only called from Init() so the internal state of this object is
+// in flux (this method is performing the last steps of re-initialization). As
+// such, random methods are not supposed to work at this point, so whatever this
+// method calls should be relatively well controlled and it may require some
+// degree of "stable state faking".
+void IndexTable::MoveCells(IndexBucket* old_extra_table) {
+ int max_hash = (mask_ + 1) / 2;
+ int max_bucket = header()->max_bucket;
+ header()->max_bucket = mask_;
+ int used_cells = header()->used_cells;
+
+ // Consider a large cache: a cell stores the upper 18 bits of the hash
+ // (h >> 14). If the table is say 8 times the original size (growing from 4x),
+ // the bit that we are interested in would be the 3rd bit of the stored value,
+ // in other words 'multiplier' >> 1.
+ uint32 new_bit = (1 << extra_bits_) >> 1;
+
+ scoped_ptr<IndexBucket[]> old_main_table;
+ IndexBucket* source_table = main_table_;
+ bool upgrade_format = !extra_bits_;
+ if (upgrade_format) {
+ // This method should deal with migrating a small table to a big one. Given
+ // that the first thing to do is read the old table, set small_table_ for
+ // the size of the old table. Now, when moving a cell, the result cannot be
+ // placed in the old table or we will end up reading it again and attempting
+ // to move it, so we have to copy the whole table at once.
+ DCHECK(!small_table_);
+ small_table_ = true;
+ old_main_table.reset(new IndexBucket[max_hash]);
+ memcpy(old_main_table.get(), main_table_, max_hash * sizeof(IndexBucket));
+ memset(main_table_, 0, max_hash * sizeof(IndexBucket));
+ source_table = old_main_table.get();
+ }
+
+ for (int i = 0; i < max_hash; i++) {
+ int bucket_num = i;
+ IndexBucket* bucket = &source_table[i];
+ do {
+ for (int j = 0; j < kCellsPerBucket; j++) {
+ IndexCell& current_cell = bucket->cells[j];
+ if (!GetLocation(current_cell))
+ continue;
+ DCHECK(SanityCheck(current_cell));
+ if (bucket_num == i) {
+ if (upgrade_format || (GetHashValue(current_cell) & new_bit)) {
+ // Move this cell to the upper half of the table.
+ MoveSingleCell(&current_cell, bucket_num * kCellsPerBucket + j, i,
+ true);
+ }
+ } else {
+ // All cells on extra buckets have to move.
+ MoveSingleCell(&current_cell, bucket_num * kCellsPerBucket + j, i,
+ true);
+ }
+ }
+
+ // There is no need to clear the old bucket->next value because if falls
+ // within the main table so it will be fixed when attempting to follow
+ // the link.
+ bucket_num = GetNextBucket(max_hash, max_bucket, old_extra_table,
+ &bucket);
+ } while (bucket_num);
+ }
+
+ DCHECK_EQ(header()->used_cells, used_cells);
+
+ if (upgrade_format) {
+ small_table_ = false;
+ header()->flags &= ~SMALL_CACHE;
+ }
+}
+
+void IndexTable::MoveSingleCell(IndexCell* current_cell, int cell_num,
+ int main_table_index, bool growing) {
+ uint32 hash = GetFullHash(*current_cell, main_table_index);
+ EntryCell old_cell(cell_num, hash, *current_cell, small_table_);
+
+ // This method may be called when moving entries from a small table to a
+ // normal table. In that case, the caller (MoveCells) has to read the old
+ // table, so it needs small_table_ set to true, but this method needs to
+ // write to the new table so small_table_ has to be set to false, and the
+ // value restored to true before returning.
+ bool upgrade_format = !extra_bits_ && growing;
+ if (upgrade_format)
+ small_table_ = false;
+ EntryCell new_cell = CreateEntryCell(hash, old_cell.GetAddress());
+
+ if (!new_cell.IsValid()) {
+ // We'll deal with this entry later.
+ if (upgrade_format)
+ small_table_ = true;
+ return;
+ }
+
+ new_cell.SetState(old_cell.GetState());
+ new_cell.SetGroup(old_cell.GetGroup());
+ new_cell.SetReuse(old_cell.GetReuse());
+ new_cell.SetTimestamp(old_cell.GetTimestamp());
+ Save(&new_cell);
+ modified_ = true;
+ if (upgrade_format)
+ small_table_ = true;
+
+ if (old_cell.GetState() == ENTRY_DELETED) {
+ bitmap_->Set(new_cell.cell_num(), false);
+ backup_bitmap_->Set(new_cell.cell_num(), false);
+ }
+
+ if (!growing || cell_num / kCellsPerBucket == main_table_index) {
+ // Only delete entries that live on the main table.
+ if (!upgrade_format) {
+ old_cell.Clear();
+ Write(old_cell);
+ }
+
+ if (cell_num != new_cell.cell_num()) {
+ bitmap_->Set(old_cell.cell_num(), false);
+ backup_bitmap_->Set(old_cell.cell_num(), false);
+ }
+ }
+ header()->used_cells--;
+}
+
+void IndexTable::HandleMisplacedCell(IndexCell* current_cell, int cell_num,
+ int main_table_index) {
+ NOTREACHED(); // No unit tests yet.
+
+ // The cell may be misplaced, or a duplicate cell exists with this data.
+ uint32 hash = GetFullHash(*current_cell, main_table_index);
+ MoveSingleCell(current_cell, cell_num, main_table_index, false);
+
+ // Now look for a duplicate cell.
+ CheckBucketList(hash & mask_);
+}
+
+void IndexTable::CheckBucketList(int bucket_num) {
+ typedef std::pair<int, EntryGroup> AddressAndGroup;
+ std::set<AddressAndGroup> entries;
+ IndexBucket* bucket = &main_table_[bucket_num];
+ int bucket_hash = bucket_num;
+ do {
+ for (int i = 0; i < kCellsPerBucket; i++) {
+ IndexCell* current_cell = &bucket->cells[i];
+ if (!GetLocation(*current_cell))
+ continue;
+ if (!SanityCheck(*current_cell)) {
+ NOTREACHED();
+ current_cell->Clear();
+ continue;
+ }
+ int cell_num = bucket_num * kCellsPerBucket + i;
+ EntryCell cell(cell_num, GetFullHash(*current_cell, bucket_hash),
+ *current_cell, small_table_);
+ if (!entries.insert(std::make_pair(cell.GetAddress().value(),
+ cell.GetGroup())).second) {
+ current_cell->Clear();
+ continue;
+ }
+ CheckState(cell);
+ }
+
+ bucket_num = GetNextBucket(mask_ + 1, header()->max_bucket, extra_table_,
+ &bucket);
+ } while (bucket_num);
+}
+
+uint32 IndexTable::GetLocation(const IndexCell& cell) {
+ if (small_table_)
+ return GetCellSmallTableLocation(cell);
+
+ return GetCellLocation(cell);
+}
+
+uint32 IndexTable::GetHashValue(const IndexCell& cell) {
+ if (small_table_)
+ return GetCellSmallTableId(cell);
+
+ return GetCellId(cell);
+}
+
+uint32 IndexTable::GetFullHash(const IndexCell& cell, uint32 lower_part) {
+ // It is OK for the high order bits of lower_part to overlap with the stored
+ // part of the hash.
+ if (small_table_)
+ return (GetCellSmallTableId(cell) << kSmallTableHashShift) | lower_part;
+
+ return (GetCellId(cell) << kHashShift) | lower_part;
+}
+
+// All the bits stored in the cell should match the provided hash.
+bool IndexTable::IsHashMatch(const IndexCell& cell, uint32 hash) {
+ hash = small_table_ ? hash >> kSmallTableHashShift : hash >> kHashShift;
+ return GetHashValue(cell) == hash;
+}
+
+bool IndexTable::MisplacedHash(const IndexCell& cell, uint32 hash) {
+ if (!extra_bits_)
+ return false;
+
+ uint32 mask = (1 << extra_bits_) - 1;
+ hash = small_table_ ? hash >> kSmallTableHashShift : hash >> kHashShift;
+ return (GetHashValue(cell) & mask) != (hash & mask);
+}
+
+} // namespace disk_cache
diff --git a/chromium/net/disk_cache/blockfile/index_table_v3.h b/chromium/net/disk_cache/blockfile/index_table_v3.h
new file mode 100644
index 00000000000..598aeecf8a7
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/index_table_v3.h
@@ -0,0 +1,279 @@
+// Copyright 2014 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_BLOCKFILE_INDEX_TABLE_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_INDEX_TABLE_V3_H_
+
+// The IndexTable class is in charge of handling all the details about the main
+// index table of the cache. It provides methods to locate entries in the cache,
+// create new entries and modify existing entries. It hides the fact that the
+// table is backed up across multiple physical files, and that the files can
+// grow and be remapped while the cache is in use. However, note that this class
+// doesn't do any direct management of the backing files, and it operates only
+// with the tables in memory.
+//
+// When the current index needs to grow, the backend is notified so that files
+// are extended and remapped as needed. After that, the IndexTable should be
+// re-initialized with the new structures. Note that the IndexTable instance is
+// still functional while the backend performs file IO.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/bitmap.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace disk_cache {
+
+class BackendImplV3;
+struct InitResult;
+
+// An EntryCell represents a single entity stored by the index table. Users are
+// expected to handle and store EntryCells on their own to track operations that
+// they are performing with a given entity, as opposed to deal with pointers to
+// individual positions on the table, given that the whole table can be moved to
+// another place, and that would invalidate any pointers to individual cells in
+// the table.
+// However, note that it is also possible for an entity to be moved from one
+// position to another, so an EntryCell may be invalid by the time a long
+// operation completes. In that case, the caller should consult the table again
+// using FindEntryCell().
+class NET_EXPORT_PRIVATE EntryCell {
+ public:
+ ~EntryCell();
+
+ bool IsValid() const;
+
+ int32 cell_num() const { return cell_num_; }
+ uint32 hash() const { return hash_; }
+
+ Addr GetAddress() const;
+ EntryState GetState() const;
+ EntryGroup GetGroup() const;
+ int GetReuse() const;
+ int GetTimestamp() const;
+
+ void SetState(EntryState state);
+ void SetGroup(EntryGroup group);
+ void SetReuse(int count);
+ void SetTimestamp(int timestamp);
+
+ static EntryCell GetEntryCellForTest(int32 cell_num,
+ uint32 hash,
+ Addr address,
+ IndexCell* cell,
+ bool small_table);
+ void SerializaForTest(IndexCell* destination);
+
+ private:
+ friend class IndexTable;
+ friend class CacheDumperHelper;
+
+ EntryCell();
+ EntryCell(int32 cell_num, uint32 hash, Addr address, bool small_table);
+ EntryCell(int32 cell_num,
+ uint32 hash,
+ const IndexCell& cell,
+ bool small_table);
+
+ void Clear() { cell_.Clear(); }
+ void FixSum();
+
+ // Returns the raw value stored on the index table.
+ uint32 GetLocation() const;
+
+ // Recalculates hash_ assuming that only the low order bits are valid and the
+ // rest come from cell_.
+ uint32 RecomputeHash();
+
+ void Serialize(IndexCell* destination) const;
+
+ int32 cell_num_;
+ uint32 hash_;
+ IndexCell cell_;
+ bool small_table_;
+};
+
+// Keeps a collection of EntryCells in order to be processed.
+struct NET_EXPORT_PRIVATE EntrySet {
+ EntrySet();
+ ~EntrySet();
+
+ int evicted_count; // The numebr of evicted entries in this set.
+ size_t current; // The number of the cell that is being processed.
+ std::vector<EntryCell> cells;
+};
+
+// A given entity referenced by the index table is uniquely identified by the
+// combination of hash and address.
+struct CellInfo { uint32 hash; Addr address; };
+typedef std::vector<CellInfo> CellList;
+
+// An index iterator is used to get a group of cells that share the same
+// timestamp. When this structure is passed to GetNextCells(), the caller sets
+// the initial timestamp and direction; whet it is used with GetOldest, the
+// initial values are ignored.
+struct NET_EXPORT_PRIVATE IndexIterator {
+ IndexIterator();
+ ~IndexIterator();
+
+ CellList cells;
+ int timestamp; // The current low resolution timestamp for |cells|.
+ bool forward; // The direction of the iteration, in time.
+};
+
+// Methods that the backend has to implement to support the table. Note that the
+// backend is expected to own all IndexTable instances, so it is expected to
+// outlive the table.
+class NET_EXPORT_PRIVATE IndexTableBackend {
+ public:
+ virtual ~IndexTableBackend() {}
+
+ // The index has to grow.
+ virtual void GrowIndex() = 0;
+
+ // Save the index to the backup file.
+ virtual void SaveIndex(net::IOBuffer* buffer, int buffer_len) = 0;
+
+ // Deletes or fixes an invalid cell from the backend.
+ virtual void DeleteCell(EntryCell cell) = 0;
+ virtual void FixCell(EntryCell cell) = 0;
+};
+
+// The data required to initialize an index. Note that not all fields have to
+// be provided when growing the tables.
+struct NET_EXPORT_PRIVATE IndexTableInitData {
+ IndexTableInitData();
+ ~IndexTableInitData();
+
+ IndexBitmap* index_bitmap;
+ IndexBucket* main_table;
+ IndexBucket* extra_table;
+ scoped_ptr<IndexHeaderV3> backup_header;
+ scoped_ptr<uint32[]> backup_bitmap;
+};
+
+// See the description at the top of this file.
+class NET_EXPORT_PRIVATE IndexTable {
+ public:
+ explicit IndexTable(IndexTableBackend* backend);
+ ~IndexTable();
+
+ // Initializes the object, or re-initializes it when the backing files grow.
+ // Note that the only supported way to initialize this objeect is using
+ // pointers that come from the files being directly mapped in memory. If that
+ // is not the case, it must be emulated in a convincing way, for example
+ // making sure that the tables for re-init look the same as the tables to be
+ // replaced.
+ void Init(IndexTableInitData* params);
+
+ // Releases the resources acquired during Init().
+ void Shutdown();
+
+ // Locates a resouce on the index. Returns a list of all resources that match
+ // the provided hash.
+ EntrySet LookupEntries(uint32 hash);
+
+ // Creates a new cell to store a new resource.
+ EntryCell CreateEntryCell(uint32 hash, Addr address);
+
+ // Locates a particular cell. This method allows a caller to perform slow
+ // operations with some entries while the index evolves, by returning the
+ // current state of a cell. If the desired cell cannot be located, the return
+ // object will be invalid.
+ EntryCell FindEntryCell(uint32 hash, Addr address);
+
+ // Returns an IndexTable timestamp for a given absolute time. The actual
+ // resolution of the timestamp should be considered an implementation detail,
+ // but it certainly is lower than seconds. The important part is that a group
+ // of cells will share the same timestamp (see IndexIterator).
+ int CalculateTimestamp(base::Time time);
+
+ // Returns the equivalent time for a cell timestamp.
+ base::Time TimeFromTimestamp(int timestamp);
+
+ // Updates a particular cell.
+ void SetSate(uint32 hash, Addr address, EntryState state);
+ void UpdateTime(uint32 hash, Addr address, base::Time current);
+
+ // Saves the contents of |cell| to the table.
+ void Save(EntryCell* cell);
+
+ // Returns the oldest entries for each group of entries. The initial values
+ // for the provided iterators are ignored. Entries are assigned to iterators
+ // according to their EntryGroup.
+ void GetOldest(IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use);
+
+ // Returns the next group of entries for the provided iterator. This method
+ // does not return the cells matching the initial iterator's timestamp,
+ // but rather cells after (or before, depending on the iterator's |forward|
+ // member) that timestamp.
+ bool GetNextCells(IndexIterator* iterator);
+
+ // Called each time the index should save the backup information. The caller
+ // can assume that anything that needs to be saved is saved when this method
+ // is called, and that there is only one source of timming information, and
+ // that source is controlled by the owner of this object.
+ void OnBackupTimer();
+
+ IndexHeaderV3* header() { return header_; }
+ const IndexHeaderV3* header() const { return header_; }
+
+ private:
+ EntryCell FindEntryCellImpl(uint32 hash, Addr address, bool allow_deleted);
+ void CheckState(const EntryCell& cell);
+ void Write(const EntryCell& cell);
+ int NewExtraBucket();
+ void WalkTables(int limit_time,
+ IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use);
+ void UpdateFromBucket(IndexBucket* bucket, int bucket_hash,
+ int limit_time,
+ IndexIterator* no_use,
+ IndexIterator* low_use,
+ IndexIterator* high_use);
+ void MoveCells(IndexBucket* old_extra_table);
+ void MoveSingleCell(IndexCell* current_cell, int cell_num,
+ int main_table_index, bool growing);
+ void HandleMisplacedCell(IndexCell* current_cell, int cell_num,
+ int main_table_index);
+ void CheckBucketList(int bucket_id);
+
+ uint32 GetLocation(const IndexCell& cell);
+ uint32 GetHashValue(const IndexCell& cell);
+ uint32 GetFullHash(const IndexCell& cell, uint32 lower_part);
+ bool IsHashMatch(const IndexCell& cell, uint32 hash);
+ bool MisplacedHash(const IndexCell& cell, uint32 hash);
+
+ IndexTableBackend* backend_;
+ IndexHeaderV3* header_;
+ scoped_ptr<Bitmap> bitmap_;
+ scoped_ptr<Bitmap> backup_bitmap_;
+ scoped_ptr<uint32[]> backup_bitmap_storage_;
+ scoped_ptr<IndexHeaderV3> backup_header_;
+ IndexBucket* main_table_;
+ IndexBucket* extra_table_;
+ uint32 mask_; // Binary mask to map a hash to the hash table.
+ int extra_bits_; // How many bits are in mask_ above the default value.
+ bool modified_;
+ bool small_table_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexTable);
+};
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_BLOCKFILE_INDEX_TABLE_V3_H_
diff --git a/chromium/net/disk_cache/blockfile/index_table_v3_unittest.cc b/chromium/net/disk_cache/blockfile/index_table_v3_unittest.cc
new file mode 100644
index 00000000000..b7a93a2d900
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/index_table_v3_unittest.cc
@@ -0,0 +1,706 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/disk_format_v3.h"
+#include "net/disk_cache/blockfile/index_table_v3.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using disk_cache::EntryCell;
+using disk_cache::IndexCell;
+using disk_cache::IndexTable;
+using disk_cache::IndexTableInitData;
+
+namespace {
+
+int GetChecksum(const IndexCell& source) {
+ // Only the cell pointer is relevant.
+ disk_cache::Addr addr;
+ IndexCell* cell = const_cast<IndexCell*>(&source);
+ EntryCell entry = EntryCell::GetEntryCellForTest(0, 0, addr, cell, false);
+
+ IndexCell result;
+ entry.SerializaForTest(&result);
+ return result.last_part >> 6;
+}
+
+class MockIndexBackend : public disk_cache::IndexTableBackend {
+ public:
+ MockIndexBackend() : grow_called_(false), buffer_len_(-1) {}
+ virtual ~MockIndexBackend() {}
+
+ bool grow_called() const { return grow_called_; }
+ int buffer_len() const { return buffer_len_; }
+
+ virtual void GrowIndex() OVERRIDE { grow_called_ = true; }
+ virtual void SaveIndex(net::IOBuffer* buffer, int buffer_len) OVERRIDE {
+ buffer_len_ = buffer_len;
+ }
+ virtual void DeleteCell(EntryCell cell) OVERRIDE {}
+ virtual void FixCell(EntryCell cell) OVERRIDE {}
+
+ private:
+ bool grow_called_;
+ int buffer_len_;
+};
+
+class TestCacheTables {
+ public:
+ // |num_entries| is the capacity of the main table. The extra table is half
+ // the size of the main table.
+ explicit TestCacheTables(int num_entries);
+ ~TestCacheTables() {}
+
+ void GetInitData(IndexTableInitData* result);
+ void CopyFrom(const TestCacheTables& other);
+ base::Time start_time() const { return start_time_; }
+
+ private:
+ scoped_ptr<uint64[]> main_bitmap_;
+ scoped_ptr<disk_cache::IndexBucket[]> main_table_;
+ scoped_ptr<disk_cache::IndexBucket[]> extra_table_;
+ base::Time start_time_;
+ int num_bitmap_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCacheTables);
+};
+
+TestCacheTables::TestCacheTables(int num_entries) {
+ DCHECK_GE(num_entries, 1024);
+ DCHECK_EQ(num_entries, num_entries / 1024 * 1024);
+ main_table_.reset(new disk_cache::IndexBucket[num_entries]);
+ extra_table_.reset(new disk_cache::IndexBucket[num_entries / 2]);
+ memset(main_table_.get(), 0, num_entries * sizeof(*main_table_.get()));
+ memset(extra_table_.get(), 0, num_entries / 2 * sizeof(*extra_table_.get()));
+
+ // We allow IndexBitmap smaller than a page because the code should not really
+ // depend on that.
+ num_bitmap_bytes_ = (num_entries + num_entries / 2) / 8;
+ size_t required_size = sizeof(disk_cache::IndexHeaderV3) + num_bitmap_bytes_;
+ main_bitmap_.reset(new uint64[required_size / sizeof(uint64)]);
+ memset(main_bitmap_.get(), 0, required_size);
+
+ disk_cache::IndexHeaderV3* header =
+ reinterpret_cast<disk_cache::IndexHeaderV3*>(main_bitmap_.get());
+
+ header->magic = disk_cache::kIndexMagicV3;
+ header->version = disk_cache::kVersion3;
+ header->table_len = num_entries + num_entries / 2;
+ header->max_bucket = num_entries / 4 - 1;
+
+ start_time_ = base::Time::Now();
+ header->create_time = start_time_.ToInternalValue();
+ header->base_time =
+ (start_time_ - base::TimeDelta::FromDays(20)).ToInternalValue();
+
+ if (num_entries < 64 * 1024)
+ header->flags = disk_cache::SMALL_CACHE;
+}
+
+void TestCacheTables::GetInitData(IndexTableInitData* result) {
+ result->index_bitmap =
+ reinterpret_cast<disk_cache::IndexBitmap*>(main_bitmap_.get());
+
+ result->main_table = main_table_.get();
+ result->extra_table = extra_table_.get();
+
+ result->backup_header.reset(new disk_cache::IndexHeaderV3);
+ memcpy(result->backup_header.get(), result->index_bitmap,
+ sizeof(result->index_bitmap->header));
+
+ result->backup_bitmap.reset(new uint32[num_bitmap_bytes_ / sizeof(uint32)]);
+ memcpy(result->backup_bitmap.get(), result->index_bitmap->bitmap,
+ num_bitmap_bytes_);
+}
+
+void TestCacheTables::CopyFrom(const TestCacheTables& other) {
+ disk_cache::IndexBitmap* this_bitmap =
+ reinterpret_cast<disk_cache::IndexBitmap*>(main_bitmap_.get());
+ disk_cache::IndexBitmap* other_bitmap =
+ reinterpret_cast<disk_cache::IndexBitmap*>(other.main_bitmap_.get());
+
+ DCHECK_GE(this_bitmap->header.table_len, other_bitmap->header.table_len);
+ DCHECK_GE(num_bitmap_bytes_, other.num_bitmap_bytes_);
+
+ memcpy(this_bitmap->bitmap, other_bitmap->bitmap, other.num_bitmap_bytes_);
+
+ int main_table_buckets = (other_bitmap->header.table_len * 2 / 3) / 4;
+ int extra_table_buckets = (other_bitmap->header.table_len * 1 / 3) / 4;
+ memcpy(main_table_.get(), other.main_table_.get(),
+ main_table_buckets * sizeof(disk_cache::IndexBucket));
+ memcpy(extra_table_.get(), other.extra_table_.get(),
+ extra_table_buckets * sizeof(disk_cache::IndexBucket));
+
+ this_bitmap->header.num_entries = other_bitmap->header.num_entries;
+ this_bitmap->header.used_cells = other_bitmap->header.used_cells;
+ this_bitmap->header.max_bucket = other_bitmap->header.max_bucket;
+ this_bitmap->header.create_time = other_bitmap->header.create_time;
+ this_bitmap->header.base_time = other_bitmap->header.base_time;
+ this_bitmap->header.flags = other_bitmap->header.flags;
+ start_time_ = other.start_time_;
+}
+
+} // namespace
+
+TEST(DiskCacheIndexTable, EntryCell) {
+ uint32 hash = 0x55aa6699;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 0x4531);
+ bool small_table = true;
+ int cell_num = 88;
+ int reuse = 6;
+ int timestamp = 123456;
+ disk_cache::EntryState state = disk_cache::ENTRY_MODIFIED;
+ disk_cache::EntryGroup group = disk_cache::ENTRY_HIGH_USE;
+
+ for (int i = 0; i < 4; i++) {
+ SCOPED_TRACE(i);
+ EntryCell entry = EntryCell::GetEntryCellForTest(cell_num, hash, addr, NULL,
+ small_table);
+ EXPECT_EQ(disk_cache::ENTRY_NO_USE, entry.GetGroup());
+ EXPECT_EQ(disk_cache::ENTRY_NEW, entry.GetState());
+
+ entry.SetGroup(group);
+ entry.SetState(state);
+ entry.SetReuse(reuse);
+ entry.SetTimestamp(timestamp);
+
+ EXPECT_TRUE(entry.IsValid());
+ EXPECT_EQ(hash, entry.hash());
+ EXPECT_EQ(cell_num, entry.cell_num());
+ EXPECT_EQ(addr.value(), entry.GetAddress().value());
+
+ EXPECT_EQ(group, entry.GetGroup());
+ EXPECT_EQ(state, entry.GetState());
+ EXPECT_EQ(reuse, entry.GetReuse());
+ EXPECT_EQ(timestamp, entry.GetTimestamp());
+
+ // Store the data and read it again.
+ IndexCell cell;
+ entry.SerializaForTest(&cell);
+
+ EntryCell entry2 = EntryCell::GetEntryCellForTest(cell_num, hash, addr,
+ &cell, small_table);
+
+ EXPECT_EQ(addr.value(), entry2.GetAddress().value());
+
+ EXPECT_EQ(group, entry2.GetGroup());
+ EXPECT_EQ(state, entry2.GetState());
+ EXPECT_EQ(reuse, entry2.GetReuse());
+ EXPECT_EQ(timestamp, entry2.GetTimestamp());
+
+ small_table = !small_table;
+ if (i == 1) {
+ hash = ~hash;
+ cell_num *= 5;
+ state = disk_cache::ENTRY_USED;
+ group = disk_cache::ENTRY_EVICTED;
+ addr = disk_cache::Addr(disk_cache::BLOCK_EVICTED, 1, 6, 0x18a5);
+ reuse = 15; // 4 bits
+ timestamp = 0xfffff; // 20 bits.
+ }
+ }
+}
+
+// Goes over some significant values for a cell's sum.
+TEST(DiskCacheIndexTable, EntryCellSum) {
+ IndexCell source;
+ source.Clear();
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part++;
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.Clear();
+ source.last_part = 0x80;
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.last_part = 0x55;
+ EXPECT_EQ(3, GetChecksum(source));
+
+ source.first_part = 0x555555;
+ EXPECT_EQ(2, GetChecksum(source));
+
+ source.last_part = 0;
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.first_part = GG_UINT64_C(0x8000000080000000);
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part = GG_UINT64_C(0x4000000040000000);
+ EXPECT_EQ(2, GetChecksum(source));
+
+ source.first_part = GG_UINT64_C(0x200000020000000);
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.first_part = GG_UINT64_C(0x100000010010000);
+ EXPECT_EQ(3, GetChecksum(source));
+
+ source.first_part = 0x80008000;
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part = GG_UINT64_C(0x800000008000);
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.first_part = 0x8080;
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part = 0x800080;
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.first_part = 0x88;
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part = 0x808;
+ EXPECT_EQ(1, GetChecksum(source));
+
+ source.first_part = 0xA;
+ EXPECT_EQ(0, GetChecksum(source));
+
+ source.first_part = 0x22;
+ EXPECT_EQ(1, GetChecksum(source));
+}
+
+TEST(DiskCacheIndexTable, Basics) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+
+ IndexTable index(NULL);
+ index.Init(&init_data);
+
+ // Write some entries.
+ disk_cache::CellList entries;
+ for (int i = 0; i < 250; i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = i * i * 1111 + i * 11;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+ }
+
+ // Read them back.
+ for (size_t i = 0; i < entries.size(); i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = entries[i].hash;
+ disk_cache::Addr addr = entries[i].address;
+
+ disk_cache::EntrySet found_entries = index.LookupEntries(hash);
+ ASSERT_EQ(1u, found_entries.cells.size());
+ EXPECT_TRUE(found_entries.cells[0].IsValid());
+ EXPECT_EQ(hash, found_entries.cells[0].hash());
+ EXPECT_EQ(addr.value(), found_entries.cells[0].GetAddress().value());
+
+ EntryCell entry = index.FindEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+ EXPECT_EQ(hash, entry.hash());
+ EXPECT_EQ(addr.value(), entry.GetAddress().value());
+
+ // Delete the first 100 entries.
+ if (i < 100)
+ index.SetSate(hash, addr, disk_cache::ENTRY_DELETED);
+ }
+
+ // See what we have now.
+ for (size_t i = 0; i < entries.size(); i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = entries[i].hash;
+ disk_cache::Addr addr = entries[i].address;
+
+ disk_cache::EntrySet found_entries = index.LookupEntries(hash);
+ if (i < 100) {
+ EXPECT_EQ(0u, found_entries.cells.size());
+ } else {
+ ASSERT_EQ(1u, found_entries.cells.size());
+ EXPECT_TRUE(found_entries.cells[0].IsValid());
+ EXPECT_EQ(hash, found_entries.cells[0].hash());
+ EXPECT_EQ(addr.value(), found_entries.cells[0].GetAddress().value());
+ }
+ }
+}
+
+// Tests handling of multiple entries with the same hash.
+TEST(DiskCacheIndexTable, SameHash) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+
+ IndexTable index(NULL);
+ index.Init(&init_data);
+
+ disk_cache::CellList entries;
+ uint32 hash = 0x55aa55bb;
+ for (int i = 0; i < 6; i++) {
+ SCOPED_TRACE(i);
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+ }
+
+ disk_cache::EntrySet found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(0, found_entries.evicted_count);
+ ASSERT_EQ(6u, found_entries.cells.size());
+
+ for (size_t i = 0; i < found_entries.cells.size(); i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(entries[i].address, found_entries.cells[i].GetAddress());
+ }
+
+ // Now verify handling of entries on different states.
+ index.SetSate(hash, entries[0].address, disk_cache::ENTRY_DELETED);
+ index.SetSate(hash, entries[1].address, disk_cache::ENTRY_DELETED);
+ index.SetSate(hash, entries[2].address, disk_cache::ENTRY_USED);
+ index.SetSate(hash, entries[3].address, disk_cache::ENTRY_USED);
+ index.SetSate(hash, entries[4].address, disk_cache::ENTRY_USED);
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(0, found_entries.evicted_count);
+ ASSERT_EQ(4u, found_entries.cells.size());
+
+ index.SetSate(hash, entries[3].address, disk_cache::ENTRY_OPEN);
+ index.SetSate(hash, entries[4].address, disk_cache::ENTRY_OPEN);
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(0, found_entries.evicted_count);
+ ASSERT_EQ(4u, found_entries.cells.size());
+
+ index.SetSate(hash, entries[4].address, disk_cache::ENTRY_MODIFIED);
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(0, found_entries.evicted_count);
+ ASSERT_EQ(4u, found_entries.cells.size());
+
+ index.SetSate(hash, entries[1].address, disk_cache::ENTRY_FREE);
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(0, found_entries.evicted_count);
+ ASSERT_EQ(4u, found_entries.cells.size());
+
+ // FindEntryCell should not see deleted entries.
+ EntryCell entry = index.FindEntryCell(hash, entries[0].address);
+ EXPECT_FALSE(entry.IsValid());
+
+ // A free entry is gone.
+ entry = index.FindEntryCell(hash, entries[1].address);
+ EXPECT_FALSE(entry.IsValid());
+
+ // Locate a used entry, and evict it. This is not really a correct operation
+ // in that an existing cell doesn't transition to evicted; instead a new cell
+ // for the evicted entry (on a different block file) should be created. Still,
+ // at least evicted_count would be valid.
+ entry = index.FindEntryCell(hash, entries[2].address);
+ EXPECT_TRUE(entry.IsValid());
+ entry.SetGroup(disk_cache::ENTRY_EVICTED);
+ index.Save(&entry);
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(1, found_entries.evicted_count);
+ ASSERT_EQ(4u, found_entries.cells.size());
+
+ // Now use the proper way to get an evicted entry.
+ disk_cache::Addr addr2(disk_cache::BLOCK_EVICTED, 1, 6, 6); // Any address.
+ entry = index.CreateEntryCell(hash, addr2);
+ EXPECT_TRUE(entry.IsValid());
+ EXPECT_EQ(disk_cache::ENTRY_EVICTED, entry.GetGroup());
+
+ found_entries = index.LookupEntries(hash);
+ EXPECT_EQ(2, found_entries.evicted_count);
+ ASSERT_EQ(5u, found_entries.cells.size());
+}
+
+TEST(DiskCacheIndexTable, Timestamps) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+
+ IndexTable index(NULL);
+ index.Init(&init_data);
+
+ // The granularity should be 1 minute.
+ int timestamp1 = index.CalculateTimestamp(cache.start_time());
+ int timestamp2 = index.CalculateTimestamp(cache.start_time() +
+ base::TimeDelta::FromSeconds(59));
+ EXPECT_EQ(timestamp1, timestamp2);
+
+ int timestamp3 = index.CalculateTimestamp(cache.start_time() +
+ base::TimeDelta::FromSeconds(61));
+ EXPECT_EQ(timestamp1 + 1, timestamp3);
+
+ int timestamp4 = index.CalculateTimestamp(cache.start_time() +
+ base::TimeDelta::FromSeconds(119));
+ EXPECT_EQ(timestamp1 + 1, timestamp4);
+
+ int timestamp5 = index.CalculateTimestamp(cache.start_time() +
+ base::TimeDelta::FromSeconds(121));
+ EXPECT_EQ(timestamp1 + 2, timestamp5);
+
+ int timestamp6 = index.CalculateTimestamp(cache.start_time() -
+ base::TimeDelta::FromSeconds(30));
+ EXPECT_EQ(timestamp1 - 1, timestamp6);
+
+ // The base should be 20 days in the past.
+ int timestamp7 = index.CalculateTimestamp(cache.start_time() -
+ base::TimeDelta::FromDays(20));
+ int timestamp8 = index.CalculateTimestamp(cache.start_time() -
+ base::TimeDelta::FromDays(35));
+ EXPECT_EQ(timestamp7, timestamp8);
+ EXPECT_EQ(0, timestamp8);
+
+ int timestamp9 = index.CalculateTimestamp(cache.start_time() -
+ base::TimeDelta::FromDays(19));
+ EXPECT_NE(0, timestamp9);
+}
+
+// Tests GetOldest and GetNextCells.
+TEST(DiskCacheIndexTable, Iterations) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+
+ IndexTable index(NULL);
+ index.Init(&init_data);
+
+ base::Time time = cache.start_time();
+
+ // Write some entries.
+ disk_cache::CellList entries;
+ for (int i = 0; i < 44; i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = i; // The entries will be ordered on the table.
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1);
+ if (i < 10 || i == 40)
+ addr = disk_cache::Addr(disk_cache::BLOCK_EVICTED, 1, 6, i * 13 + 1);
+
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+
+ if (i < 10 || i == 40) {
+ // Do nothing. These are ENTRY_EVICTED by default.
+ } else if (i < 20 || i == 41) {
+ entry.SetGroup(disk_cache::ENTRY_HIGH_USE);
+ index.Save(&entry);
+ } else if (i < 30 || i == 42) {
+ entry.SetGroup(disk_cache::ENTRY_LOW_USE);
+ index.Save(&entry);
+ }
+
+ // Entries [30,39] and 43 are marked as ENTRY_NO_USE (the default).
+
+ if (!(i % 10))
+ time += base::TimeDelta::FromMinutes(1);
+
+ index.UpdateTime(hash, addr, time);
+ }
+
+ // Get the oldest entries of each group.
+ disk_cache::IndexIterator no_use, low_use, high_use;
+ index.GetOldest(&no_use, &low_use, &high_use);
+ ASSERT_EQ(10u, no_use.cells.size());
+ ASSERT_EQ(10u, low_use.cells.size());
+ ASSERT_EQ(10u, high_use.cells.size());
+
+ EXPECT_EQ(entries[10].hash, high_use.cells[0].hash);
+ EXPECT_EQ(entries[19].hash, high_use.cells[9].hash);
+ EXPECT_EQ(entries[20].hash, low_use.cells[0].hash);
+ EXPECT_EQ(entries[29].hash, low_use.cells[9].hash);
+ EXPECT_EQ(entries[30].hash, no_use.cells[0].hash);
+ EXPECT_EQ(entries[39].hash, no_use.cells[9].hash);
+
+ // Now start an iteration from the head (most recent entry).
+ disk_cache::IndexIterator iterator;
+ iterator.timestamp = index.CalculateTimestamp(time) + 1;
+ iterator.forward = false; // Back in time.
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(3u, iterator.cells.size());
+ EXPECT_EQ(entries[41].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[42].hash, iterator.cells[1].hash);
+ EXPECT_EQ(entries[43].hash, iterator.cells[2].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[30].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[39].hash, iterator.cells[9].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[20].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[29].hash, iterator.cells[9].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[10].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[19].hash, iterator.cells[9].hash);
+
+ ASSERT_FALSE(index.GetNextCells(&iterator));
+
+ // Now start an iteration from the tail (oldest entry).
+ iterator.timestamp = 0;
+ iterator.forward = true;
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[10].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[19].hash, iterator.cells[9].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[20].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[29].hash, iterator.cells[9].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(10u, iterator.cells.size());
+ EXPECT_EQ(entries[30].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[39].hash, iterator.cells[9].hash);
+
+ ASSERT_TRUE(index.GetNextCells(&iterator));
+ ASSERT_EQ(3u, iterator.cells.size());
+ EXPECT_EQ(entries[41].hash, iterator.cells[0].hash);
+ EXPECT_EQ(entries[42].hash, iterator.cells[1].hash);
+ EXPECT_EQ(entries[43].hash, iterator.cells[2].hash);
+}
+
+// Tests doubling of the table.
+TEST(DiskCacheIndexTable, Doubling) {
+ IndexTable index(NULL);
+ int size = 1024;
+ scoped_ptr<TestCacheTables> cache(new TestCacheTables(size));
+ int entry_id = 0;
+ disk_cache::CellList entries;
+
+ // Go from 1024 to 256k cells.
+ for (int resizes = 0; resizes <= 8; resizes++) {
+ scoped_ptr<TestCacheTables> old_cache(cache.Pass());
+ cache.reset(new TestCacheTables(size));
+ cache.get()->CopyFrom(*old_cache.get());
+
+ IndexTableInitData init_data;
+ cache.get()->GetInitData(&init_data);
+ index.Init(&init_data);
+
+ // Write some entries.
+ for (int i = 0; i < 250; i++, entry_id++) {
+ SCOPED_TRACE(entry_id);
+ uint32 hash = entry_id * i * 321 + entry_id * 13;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, entry_id * 17 + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+ }
+ size *= 2;
+ }
+
+ // Access all the entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ SCOPED_TRACE(i);
+ disk_cache::EntrySet found_entries = index.LookupEntries(entries[i].hash);
+ ASSERT_EQ(1u, found_entries.cells.size());
+ EXPECT_TRUE(found_entries.cells[0].IsValid());
+ }
+}
+
+// Tests bucket chaining when growing the index.
+TEST(DiskCacheIndexTable, BucketChains) {
+ IndexTable index(NULL);
+ int size = 1024;
+ scoped_ptr<TestCacheTables> cache(new TestCacheTables(size));
+ disk_cache::CellList entries;
+
+ IndexTableInitData init_data;
+ cache.get()->GetInitData(&init_data);
+ index.Init(&init_data);
+
+ // Write some entries.
+ for (int i = 0; i < 8; i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = i * 256;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 7 + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+ }
+
+ // Double the size.
+ scoped_ptr<TestCacheTables> old_cache(cache.Pass());
+ cache.reset(new TestCacheTables(size * 2));
+ cache.get()->CopyFrom(*old_cache.get());
+
+ cache.get()->GetInitData(&init_data);
+ index.Init(&init_data);
+
+ // Write more entries, starting with the upper half of the table.
+ for (int i = 9; i < 11; i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = i * 256;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 7 + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ disk_cache::CellInfo info = { hash, addr };
+ entries.push_back(info);
+ }
+
+ // Access all the entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ SCOPED_TRACE(i);
+ disk_cache::EntrySet found_entries = index.LookupEntries(entries[i].hash);
+ ASSERT_EQ(1u, found_entries.cells.size());
+ EXPECT_TRUE(found_entries.cells[0].IsValid());
+ }
+}
+
+// Tests that GrowIndex is called.
+TEST(DiskCacheIndexTable, GrowIndex) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+ MockIndexBackend backend;
+
+ IndexTable index(&backend);
+ index.Init(&init_data);
+
+ // Write some entries.
+ for (int i = 0; i < 512; i++) {
+ SCOPED_TRACE(i);
+ uint32 hash = 0;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i + 1);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+ }
+
+ EXPECT_TRUE(backend.grow_called());
+}
+
+TEST(DiskCacheIndexTable, SaveIndex) {
+ TestCacheTables cache(1024);
+ IndexTableInitData init_data;
+ cache.GetInitData(&init_data);
+ MockIndexBackend backend;
+
+ IndexTable index(&backend);
+ index.Init(&init_data);
+
+ uint32 hash = 0;
+ disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 6);
+ EntryCell entry = index.CreateEntryCell(hash, addr);
+ EXPECT_TRUE(entry.IsValid());
+
+ index.OnBackupTimer();
+ int expected = (1024 + 512) / 8 + sizeof(disk_cache::IndexHeaderV3);
+ EXPECT_EQ(expected, backend.buffer_len());
+}
diff --git a/chromium/net/disk_cache/mapped_file.cc b/chromium/net/disk_cache/blockfile/mapped_file.cc
index dd745ac5268..d26cead89aa 100644
--- a/chromium/net/disk_cache/mapped_file.cc
+++ b/chromium/net/disk_cache/blockfile/mapped_file.cc
@@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
+
+#include <algorithm>
+
+#include "base/memory/scoped_ptr.h"
namespace disk_cache {
@@ -32,4 +36,11 @@ bool MappedFile::Store(const FileBlock* block,
return Write(block->buffer(), block->size(), offset, callback, completed);
}
+bool MappedFile::Preload() {
+ size_t file_len = GetLength();
+ scoped_ptr<char[]> buf(new char[file_len]);
+ if (!Read(buf.get(), file_len, 0))
+ return false;
+ return true;
+}
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/mapped_file.h b/chromium/net/disk_cache/blockfile/mapped_file.h
index ba7f4adae65..f0efa41ed77 100644
--- a/chromium/net/disk_cache/mapped_file.h
+++ b/chromium/net/disk_cache/blockfile/mapped_file.h
@@ -4,12 +4,12 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_MAPPED_FILE_H_
-#define NET_DISK_CACHE_MAPPED_FILE_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_MAPPED_FILE_H_
+#define NET_DISK_CACHE_BLOCKFILE_MAPPED_FILE_H_
#include "net/base/net_export.h"
-#include "net/disk_cache/file.h"
-#include "net/disk_cache/file_block.h"
+#include "net/disk_cache/blockfile/file.h"
+#include "net/disk_cache/blockfile/file_block.h"
namespace base {
class FilePath;
@@ -46,6 +46,10 @@ class NET_EXPORT_PRIVATE MappedFile : public File {
// Flush the memory-mapped section to disk (synchronously).
void Flush();
+ // Heats up the file system cache and make sure the file is fully
+ // readable (synchronously).
+ bool Preload();
+
private:
virtual ~MappedFile();
@@ -75,4 +79,4 @@ class ScopedFlush {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_MAPPED_FILE_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_MAPPED_FILE_H_
diff --git a/chromium/net/disk_cache/mapped_file_avoid_mmap_posix.cc b/chromium/net/disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc
index 940466ece6a..3936358236d 100644
--- a/chromium/net/disk_cache/mapped_file_avoid_mmap_posix.cc
+++ b/chromium/net/disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
#include <stdlib.h>
diff --git a/chromium/net/disk_cache/mapped_file_posix.cc b/chromium/net/disk_cache/blockfile/mapped_file_posix.cc
index 576d02afaea..966fc72686e 100644
--- a/chromium/net/disk_cache/mapped_file_posix.cc
+++ b/chromium/net/disk_cache/blockfile/mapped_file_posix.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
#include <errno.h>
#include <sys/mman.h>
diff --git a/chromium/net/disk_cache/mapped_file_unittest.cc b/chromium/net/disk_cache/blockfile/mapped_file_unittest.cc
index 7afb8cc4e1c..8b3a1d0f990 100644
--- a/chromium/net/disk_cache/mapped_file_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/mapped_file_unittest.cc
@@ -5,10 +5,10 @@
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
+#include "net/disk_cache/blockfile/file_block.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
#include "net/disk_cache/disk_cache_test_base.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/file_block.h"
-#include "net/disk_cache/mapped_file.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
diff --git a/chromium/net/disk_cache/mapped_file_win.cc b/chromium/net/disk_cache/blockfile/mapped_file_win.cc
index b795bf47835..d725238e32e 100644
--- a/chromium/net/disk_cache/mapped_file_win.cc
+++ b/chromium/net/disk_cache/blockfile/mapped_file_win.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
diff --git a/chromium/net/disk_cache/rankings.cc b/chromium/net/disk_cache/blockfile/rankings.cc
index ff9913e252a..7c93b431bcb 100644
--- a/chromium/net/disk_cache/rankings.cc
+++ b/chromium/net/disk_cache/blockfile/rankings.cc
@@ -2,15 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/rankings.h"
+#include "net/disk_cache/blockfile/rankings.h"
#include "base/metrics/histogram.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/errors.h"
-#include "net/disk_cache/histogram_macros.h"
-#include "net/disk_cache/stress_support.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/errors.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+#include "net/disk_cache/blockfile/stress_support.h"
+
+// Provide a BackendImpl object to macros from histogram_macros.h.
+#define CACHE_UMA_BACKEND_IMPL_OBJ backend_
using base::Time;
using base::TimeTicks;
diff --git a/chromium/net/disk_cache/rankings.h b/chromium/net/disk_cache/blockfile/rankings.h
index cd94eaf1c59..4224d0058eb 100644
--- a/chromium/net/disk_cache/rankings.h
+++ b/chromium/net/disk_cache/blockfile/rankings.h
@@ -4,15 +4,15 @@
// See net/disk_cache/disk_cache.h for the public interface.
-#ifndef NET_DISK_CACHE_RANKINGS_H_
-#define NET_DISK_CACHE_RANKINGS_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_RANKINGS_H_
+#define NET_DISK_CACHE_BLOCKFILE_RANKINGS_H_
#include <list>
#include "base/memory/scoped_ptr.h"
-#include "net/disk_cache/addr.h"
-#include "net/disk_cache/mapped_file.h"
-#include "net/disk_cache/storage_block.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
+#include "net/disk_cache/blockfile/storage_block.h"
namespace disk_cache {
@@ -211,4 +211,4 @@ class Rankings {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_RANKINGS_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_RANKINGS_H_
diff --git a/chromium/net/disk_cache/sparse_control.cc b/chromium/net/disk_cache/blockfile/sparse_control.cc
index b96ccc9faff..e251d84e7c0 100644
--- a/chromium/net/disk_cache/sparse_control.cc
+++ b/chromium/net/disk_cache/blockfile/sparse_control.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/sparse_control.h"
+#include "net/disk_cache/blockfile/sparse_control.h"
#include "base/bind.h"
#include "base/format_macros.h"
@@ -13,9 +13,9 @@
#include "base/time/time.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/file.h"
#include "net/disk_cache/net_log_parameters.h"
using base::Time;
@@ -161,7 +161,7 @@ net::NetLog::EventType GetSparseEventType(
void LogChildOperationEnd(const net::BoundNetLog& net_log,
disk_cache::SparseControl::SparseOperation operation,
int result) {
- if (net_log.IsLoggingAllEvents()) {
+ if (net_log.IsLogging()) {
net::NetLog::EventType event_type;
switch (operation) {
case disk_cache::SparseControl::kReadOperation:
@@ -252,8 +252,10 @@ int SparseControl::StartIO(SparseOperation op, int64 offset, net::IOBuffer* buf,
return net::ERR_INVALID_ARGUMENT;
// We only support up to 64 GB.
- if (offset + buf_len >= 0x1000000000LL || offset + buf_len < 0)
+ if (static_cast<uint64>(offset) + static_cast<unsigned int>(buf_len) >=
+ GG_UINT64_C(0x1000000000)) {
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
+ }
DCHECK(!user_buf_.get());
DCHECK(user_callback_.is_null());
@@ -273,7 +275,7 @@ int SparseControl::StartIO(SparseOperation op, int64 offset, net::IOBuffer* buf,
finished_ = false;
abort_ = false;
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
GetSparseEventType(operation_),
CreateNetLogSparseOperationCallback(offset_, buf_len_));
@@ -682,14 +684,14 @@ void SparseControl::DoChildrenIO() {
// Range operations are finished synchronously, often without setting
// |finished_| to true.
if (kGetRangeOperation == operation_ &&
- entry_->net_log().IsLoggingAllEvents()) {
+ entry_->net_log().IsLogging()) {
entry_->net_log().EndEvent(
net::NetLog::TYPE_SPARSE_GET_RANGE,
CreateNetLogGetAvailableRangeResultCallback(offset_, result_));
}
if (finished_) {
if (kGetRangeOperation != operation_ &&
- entry_->net_log().IsLoggingAllEvents()) {
+ entry_->net_log().IsLogging()) {
entry_->net_log().EndEvent(GetSparseEventType(operation_));
}
if (pending_)
@@ -719,7 +721,7 @@ bool SparseControl::DoChildIO() {
int rv = 0;
switch (operation_) {
case kReadOperation:
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
net::NetLog::TYPE_SPARSE_READ_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child_->net_log().source(),
@@ -729,7 +731,7 @@ bool SparseControl::DoChildIO() {
child_len_, callback);
break;
case kWriteOperation:
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child_->net_log().source(),
@@ -837,7 +839,7 @@ void SparseControl::OnChildIOCompleted(int result) {
// We'll return the current result of the operation, which may be less than
// the bytes to read or write, but the user cancelled the operation.
abort_ = false;
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().AddEvent(net::NetLog::TYPE_CANCELLED);
entry_->net_log().EndEvent(GetSparseEventType(operation_));
}
diff --git a/chromium/net/disk_cache/sparse_control.h b/chromium/net/disk_cache/blockfile/sparse_control.h
index a018e18742e..d3450a3eaec 100644
--- a/chromium/net/disk_cache/sparse_control.h
+++ b/chromium/net/disk_cache/blockfile/sparse_control.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_SPARSE_CONTROL_H_
-#define NET_DISK_CACHE_SPARSE_CONTROL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_H_
+#define NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_H_
#include <string>
#include <vector>
@@ -11,8 +11,8 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "net/base/completion_callback.h"
-#include "net/disk_cache/bitmap.h"
-#include "net/disk_cache/disk_format.h"
+#include "net/disk_cache/blockfile/bitmap.h"
+#include "net/disk_cache/blockfile/disk_format.h"
namespace net {
class IOBuffer;
@@ -174,4 +174,4 @@ class SparseControl {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_SPARSE_CONTROL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_H_
diff --git a/chromium/net/disk_cache/v3/sparse_control_v3.cc b/chromium/net/disk_cache/blockfile/sparse_control_v3.cc
index d9700adc89c..66a60a21bb1 100644
--- a/chromium/net/disk_cache/v3/sparse_control_v3.cc
+++ b/chromium/net/disk_cache/blockfile/sparse_control_v3.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/sparse_control.h"
+#include "net/disk_cache/blockfile/sparse_control.h"
#include "base/bind.h"
#include "base/format_macros.h"
@@ -13,9 +13,9 @@
#include "base/time/time.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/file.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/file.h"
#include "net/disk_cache/net_log_parameters.h"
using base::Time;
@@ -163,7 +163,7 @@ net::NetLog::EventType GetSparseEventType(
void LogChildOperationEnd(const net::BoundNetLog& net_log,
disk_cache::SparseControl::SparseOperation operation,
int result) {
- if (net_log.IsLoggingAllEvents()) {
+ if (net_log.IsLogging()) {
net::NetLog::EventType event_type;
switch (operation) {
case disk_cache::SparseControl::kReadOperation:
@@ -254,7 +254,7 @@ int SparseControl::StartIO(SparseOperation op, int64 offset, net::IOBuffer* buf,
finished_ = false;
abort_ = false;
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
GetSparseEventType(operation_),
CreateNetLogSparseOperationCallback(offset_, buf_len_));
@@ -563,7 +563,7 @@ bool SparseControl::DoChildIO() {
int rv = 0;
switch (operation_) {
case kReadOperation:
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
net::NetLog::TYPE_SPARSE_READ_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child_->net_log().source(),
@@ -573,7 +573,7 @@ bool SparseControl::DoChildIO() {
child_len_, callback);
break;
case kWriteOperation:
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().BeginEvent(
net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child_->net_log().source(),
@@ -846,7 +846,7 @@ void SparseControl::OnChildIOCompleted(int result) {
// We'll return the current result of the operation, which may be less than
// the bytes to read or write, but the user cancelled the operation.
abort_ = false;
- if (entry_->net_log().IsLoggingAllEvents()) {
+ if (entry_->net_log().IsLogging()) {
entry_->net_log().AddEvent(net::NetLog::TYPE_CANCELLED);
entry_->net_log().EndEvent(GetSparseEventType(operation_));
}
diff --git a/chromium/net/disk_cache/v3/sparse_control_v3.h b/chromium/net/disk_cache/blockfile/sparse_control_v3.h
index 8455ad724b5..495ae191612 100644
--- a/chromium/net/disk_cache/v3/sparse_control_v3.h
+++ b/chromium/net/disk_cache/blockfile/sparse_control_v3.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_SPARSE_CONTROL_H_
-#define NET_DISK_CACHE_SPARSE_CONTROL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_V3_H_
+#define NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_V3_H_
#include <string>
#include <vector>
@@ -11,7 +11,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "net/base/completion_callback.h"
-#include "net/disk_cache/bitmap.h"
+#include "net/disk_cache/blockfile/bitmap.h"
#include "net/disk_cache/disk_format.h"
namespace net {
@@ -172,4 +172,4 @@ class SparseControl {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_SPARSE_CONTROL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_SPARSE_CONTROL_V3_H_
diff --git a/chromium/net/disk_cache/stats.cc b/chromium/net/disk_cache/blockfile/stats.cc
index b05ecc6ec0b..70592e4bded 100644
--- a/chromium/net/disk_cache/stats.cc
+++ b/chromium/net/disk_cache/blockfile/stats.cc
@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/stats.h"
+#include "net/disk_cache/blockfile/stats.h"
#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram.h"
#include "base/metrics/histogram_samples.h"
+#include "base/metrics/sample_vector.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -88,13 +92,10 @@ bool VerifyStats(OnDiskStats* stats) {
return true;
}
-Stats::Stats() : size_histogram_(NULL) {
+Stats::Stats() {
}
Stats::~Stats() {
- if (size_histogram_) {
- size_histogram_->Disable();
- }
}
bool Stats::Init(void* data, int num_bytes, Addr address) {
@@ -123,17 +124,32 @@ bool Stats::Init(void* data, int num_bytes, Addr address) {
}
void Stats::InitSizeHistogram() {
- // It seems impossible to support this histogram for more than one
- // simultaneous objects with the current infrastructure.
+ // Only generate this histogram for the main cache.
static bool first_time = true;
- if (first_time) {
- first_time = false;
- if (!size_histogram_) {
- // Stats may be reused when the cache is re-created, but we want only one
- // histogram at any given time.
- size_histogram_ = StatsHistogram::FactoryGet("DiskCache.SizeStats", this);
- }
+ if (!first_time)
+ return;
+
+ first_time = false;
+ int min = 1;
+ int max = 64 * 1024;
+ int num_buckets = 75;
+ base::BucketRanges ranges(num_buckets + 1);
+ base::Histogram::InitializeBucketRanges(min, max, &ranges);
+
+ base::HistogramBase* stats_histogram = base::Histogram::FactoryGet(
+ "DiskCache.SizeStats2", min, max, num_buckets,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+
+ base::SampleVector samples(&ranges);
+ for (int i = 0; i < kDataSizesLength; i++) {
+ // This is a good time to fix any inconsistent data. The count should be
+ // always positive, but if it's not, reset the value now.
+ if (data_sizes_[i] < 0)
+ data_sizes_[i] = 0;
+
+ samples.Accumulate(GetBucketRange(i) / 1024, data_sizes_[i]);
}
+ stats_histogram->AddSamples(samples);
}
int Stats::StorageSize() {
@@ -248,15 +264,6 @@ int Stats::GetBucketRange(size_t i) const {
return n;
}
-void Stats::Snapshot(base::HistogramSamples* samples) const {
- for (int i = 0; i < kDataSizesLength; i++) {
- int count = data_sizes_[i];
- if (count < 0)
- count = 0;
- samples->Accumulate(GetBucketRange(i), count);
- }
-}
-
// The array will be filled this way:
// index size
// 0 [0, 1024)
diff --git a/chromium/net/disk_cache/stats.h b/chromium/net/disk_cache/blockfile/stats.h
index 440334af207..8e1293d61d4 100644
--- a/chromium/net/disk_cache/stats.h
+++ b/chromium/net/disk_cache/blockfile/stats.h
@@ -2,15 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_STATS_H_
-#define NET_DISK_CACHE_STATS_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_STATS_H_
+#define NET_DISK_CACHE_BLOCKFILE_STATS_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
-#include "net/disk_cache/addr.h"
-#include "net/disk_cache/stats_histogram.h"
+#include "net/disk_cache/blockfile/addr.h"
namespace base {
class HistogramSamples;
@@ -83,23 +82,19 @@ class Stats {
// Returns the number of bytes copied.
int SerializeStats(void* data, int num_bytes, Addr* address);
- // Support for StatsHistograms. Together, these methods allow StatsHistograms
- // to take a snapshot of the data_sizes_ as the histogram data.
- int GetBucketRange(size_t i) const;
- void Snapshot(base::HistogramSamples* samples) const;
-
private:
+ // Supports generation of SizeStats histogram data.
+ int GetBucketRange(size_t i) const;
int GetStatsBucket(int32 size);
int GetRatio(Counters hit, Counters miss) const;
Addr storage_addr_;
int data_sizes_[kDataSizesLength];
int64 counters_[MAX_COUNTER];
- StatsHistogram* size_histogram_;
DISALLOW_COPY_AND_ASSIGN(Stats);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_STATS_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_STATS_H_
diff --git a/chromium/net/disk_cache/storage_block-inl.h b/chromium/net/disk_cache/blockfile/storage_block-inl.h
index b9061861d8f..f919c128c40 100644
--- a/chromium/net/disk_cache/storage_block-inl.h
+++ b/chromium/net/disk_cache/blockfile/storage_block-inl.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_STORAGE_BLOCK_INL_H_
-#define NET_DISK_CACHE_STORAGE_BLOCK_INL_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_INL_H_
+#define NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_INL_H_
-#include "net/disk_cache/storage_block.h"
+#include "net/disk_cache/blockfile/storage_block.h"
#include "base/hash.h"
#include "base/logging.h"
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/trace.h"
namespace disk_cache {
@@ -202,4 +202,4 @@ template<typename T> uint32 StorageBlock<T>::CalculateHash() const {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_STORAGE_BLOCK_INL_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_INL_H_
diff --git a/chromium/net/disk_cache/storage_block.h b/chromium/net/disk_cache/blockfile/storage_block.h
index f7690ed5c4d..0d514741b6a 100644
--- a/chromium/net/disk_cache/storage_block.h
+++ b/chromium/net/disk_cache/blockfile/storage_block.h
@@ -4,11 +4,11 @@
// See net/disk_cache/disk_cache.h for the public interface.
-#ifndef NET_DISK_CACHE_STORAGE_BLOCK_H_
-#define NET_DISK_CACHE_STORAGE_BLOCK_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_H_
+#define NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_H_
-#include "net/disk_cache/addr.h"
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/blockfile/addr.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
namespace disk_cache {
@@ -94,4 +94,4 @@ class StorageBlock : public FileBlock {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_STORAGE_BLOCK_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_STORAGE_BLOCK_H_
diff --git a/chromium/net/disk_cache/storage_block_unittest.cc b/chromium/net/disk_cache/blockfile/storage_block_unittest.cc
index f91b2b92ff2..0ed528e93b2 100644
--- a/chromium/net/disk_cache/storage_block_unittest.cc
+++ b/chromium/net/disk_cache/blockfile/storage_block_unittest.cc
@@ -3,9 +3,9 @@
// found in the LICENSE file.
#include "base/files/file_path.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/storage_block.h"
-#include "net/disk_cache/storage_block-inl.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/storage_block-inl.h"
+#include "net/disk_cache/blockfile/storage_block.h"
#include "net/disk_cache/disk_cache_test_base.h"
#include "net/disk_cache/disk_cache_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/net/disk_cache/stress_cache.cc b/chromium/net/disk_cache/blockfile/stress_cache.cc
index 9c3c5a6333e..f28e8af9fd5 100644
--- a/chromium/net/disk_cache/stress_cache.cc
+++ b/chromium/net/disk_cache/blockfile/stress_cache.cc
@@ -36,11 +36,11 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/stress_support.h"
+#include "net/disk_cache/blockfile/trace.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/stress_support.h"
-#include "net/disk_cache/trace.h"
#if defined(OS_WIN)
#include "base/logging_win.h"
@@ -56,7 +56,7 @@ int RunSlave(int iteration) {
base::FilePath exe;
PathService::Get(base::FILE_EXE, &exe);
- CommandLine cmdline(exe);
+ base::CommandLine cmdline(exe);
cmdline.AppendArg(base::IntToString(iteration));
base::ProcessHandle handle;
@@ -271,7 +271,7 @@ int main(int argc, const char* argv[]) {
#if defined(OS_WIN)
logging::LogEventProvider::Initialize(kStressCacheTraceProviderName);
#else
- CommandLine::Init(argc, argv);
+ base::CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
logging::InitLogging(settings);
@@ -279,7 +279,7 @@ int main(int argc, const char* argv[]) {
// Some time for the memory manager to flush stuff.
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(3));
- base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
+ base::MessageLoopForIO message_loop;
char* end;
long int iteration = strtol(argv[1], &end, 0);
diff --git a/chromium/net/disk_cache/stress_support.h b/chromium/net/disk_cache/blockfile/stress_support.h
index 48d7b5b7df2..5383c5d3e3c 100644
--- a/chromium/net/disk_cache/stress_support.h
+++ b/chromium/net/disk_cache/blockfile/stress_support.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_STRESS_SUPPORT_H_
-#define NET_DISK_CACHE_STRESS_SUPPORT_H_
+#ifndef NET_DISK_CACHE_BLOCKFILE_STRESS_SUPPORT_H_
+#define NET_DISK_CACHE_BLOCKFILE_STRESS_SUPPORT_H_
#include "base/logging.h"
@@ -36,4 +36,4 @@ namespace disk_cache {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_STRESS_SUPPORT_H_
+#endif // NET_DISK_CACHE_BLOCKFILE_STRESS_SUPPORT_H_
diff --git a/chromium/net/disk_cache/trace.cc b/chromium/net/disk_cache/blockfile/trace.cc
index 56ebe9bb7ea..7f21e2f7609 100644
--- a/chromium/net/disk_cache/trace.cc
+++ b/chromium/net/disk_cache/blockfile/trace.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/trace.h"
+#include "net/disk_cache/blockfile/trace.h"
#include <stdio.h>
#if defined(OS_WIN)
@@ -12,7 +12,7 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
-#include "net/disk_cache/stress_support.h"
+#include "net/disk_cache/blockfile/stress_support.h"
// Change this value to 1 to enable tracing on a release build. By default,
// tracing is enabled only on debug builds.
diff --git a/chromium/net/disk_cache/trace.h b/chromium/net/disk_cache/blockfile/trace.h
index b0bf1ad7284..6805e6c60c2 100644
--- a/chromium/net/disk_cache/trace.h
+++ b/chromium/net/disk_cache/blockfile/trace.h
@@ -6,8 +6,8 @@
// keep a static circular buffer where we store the last traced events, so we
// can review the cache recent behavior should we need it.
-#ifndef NET_DISK_CACHE_TRACE_H__
-#define NET_DISK_CACHE_TRACE_H__
+#ifndef NET_DISK_CACHE_BLOCKFILE_TRACE_H_
+#define NET_DISK_CACHE_BLOCKFILE_TRACE_H_
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
@@ -38,4 +38,4 @@ NET_EXPORT_PRIVATE void Trace(const char* format, ...);
} // namespace disk_cache
-#endif // NET_DISK_CACHE_TRACE_H__
+#endif // NET_DISK_CACHE_BLOCKFILE_TRACE_H_
diff --git a/chromium/net/disk_cache/blockfile/webfonts_histogram.cc b/chromium/net/disk_cache/blockfile/webfonts_histogram.cc
new file mode 100644
index 00000000000..a92d94224ec
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/webfonts_histogram.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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/blockfile/webfonts_histogram.h"
+
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
+#include "net/disk_cache/blockfile/histogram_macros.h"
+
+namespace {
+
+enum WebFontDiskCacheEventType {
+ CACHE_EVENT_MISS,
+ CACHE_EVENT_HIT,
+ CACHE_EVENT_EVICTED_ENTRY,
+ CACHE_EVENT_MAX
+};
+
+// Tests if the substring of str that begins at pos starts with substr. If so,
+// returns true and advances pos by the length of substr.
+bool Consume(const std::string& str, const base::StringPiece& substr,
+ std::string::size_type* pos) {
+ if (!str.compare(*pos, substr.length(), substr.data())) {
+ *pos += substr.length();
+ return true;
+ }
+ return false;
+}
+
+const char kRoboto[] = "roboto";
+const char kOpenSans[] = "opensans";
+const char kOthers[] = "others";
+
+// Check if the given string is a URL for a font resource of Google Fonts.
+// If so, returns a label for UMA histogram ("roboto", "opensans" or "others").
+const char* HistogramLabel(const std::string& str) {
+ std::string::size_type pos = 0;
+ if (Consume(str, "http://", &pos) || Consume(str, "https://", &pos)) {
+ if (Consume(str, "themes.googleusercontent.com/static/fonts/", &pos) ||
+ Consume(str, "ssl.gstatic.com/fonts/", &pos) ||
+ Consume(str, "fonts.gstatic.com/s/", &pos)) {
+ if (Consume(str, kRoboto, &pos))
+ return kRoboto;
+ if (Consume(str, kOpenSans, &pos))
+ return kOpenSans;
+ return kOthers;
+ }
+ }
+ return NULL;
+}
+
+std::string HistogramName(const char* prefix, const char* label) {
+ return base::StringPrintf("WebFont.%s_%s", prefix, label);
+}
+
+void RecordCacheEvent(WebFontDiskCacheEventType type, const char* label) {
+ CACHE_HISTOGRAM_ENUMERATION(HistogramName("DiskCacheHit", label),
+ type, CACHE_EVENT_MAX);
+}
+
+} // namespace
+
+namespace disk_cache {
+namespace web_fonts_histogram {
+
+void RecordCacheMiss(const std::string& key) {
+ const char* label = HistogramLabel(key);
+ if (label)
+ RecordCacheEvent(CACHE_EVENT_MISS, label);
+}
+
+void RecordEvictedEntry(const std::string& key) {
+ const char* label = HistogramLabel(key);
+ if (label)
+ RecordCacheEvent(CACHE_EVENT_EVICTED_ENTRY, label);
+}
+
+void RecordCacheHit(EntryImpl* entry) {
+ const char* label = HistogramLabel(entry->GetKey());
+ if (!label)
+ return;
+ EntryStore* info = entry->entry()->Data();
+ CACHE_HISTOGRAM_COUNTS_10000(HistogramName("DiskCache.ReuseCount.Hit", label),
+ info->reuse_count);
+ CACHE_HISTOGRAM_AGE(HistogramName("DiskCache.EntryAge.Hit", label),
+ base::Time::FromInternalValue(info->creation_time));
+ RecordCacheEvent(CACHE_EVENT_HIT, label);
+}
+
+void RecordEviction(EntryImpl* entry) {
+ const char* label = HistogramLabel(entry->GetKey());
+ if (!label)
+ return;
+ EntryStore* info = entry->entry()->Data();
+ CACHE_HISTOGRAM_COUNTS_10000(
+ HistogramName("DiskCache.ReuseCount.Evict", label),
+ info->reuse_count);
+ CACHE_HISTOGRAM_AGE(HistogramName("DiskCache.EntryAge.Evict", label),
+ base::Time::FromInternalValue(info->creation_time));
+}
+
+} // namespace web_fonts_histogram
+} // namespace disk_cache
diff --git a/chromium/net/disk_cache/blockfile/webfonts_histogram.h b/chromium/net/disk_cache/blockfile/webfonts_histogram.h
new file mode 100644
index 00000000000..1a920a95514
--- /dev/null
+++ b/chromium/net/disk_cache/blockfile/webfonts_histogram.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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_BLOCKFILE_WEBFONTS_HISTOGRAM_H_
+#define NET_DISK_CACHE_BLOCKFILE_WEBFONTS_HISTOGRAM_H_
+
+#include <string>
+
+namespace disk_cache {
+
+class EntryImpl;
+
+// A collection of functions for histogram reporting about web fonts.
+namespace web_fonts_histogram {
+
+void RecordCacheMiss(const std::string& key);
+void RecordEvictedEntry(const std::string& key);
+void RecordCacheHit(EntryImpl* entry);
+void RecordEviction(EntryImpl* entry);
+
+} // namespace web_fonts_histogram
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_BLOCKFILE_WEBFONTS_HISTOGRAM_H_
diff --git a/chromium/net/disk_cache/cache_creator.cc b/chromium/net/disk_cache/cache_creator.cc
index 1fee41f2738..866df10e372 100644
--- a/chromium/net/disk_cache/cache_creator.cc
+++ b/chromium/net/disk_cache/cache_creator.cc
@@ -7,10 +7,10 @@
#include "base/strings/stringprintf.h"
#include "net/base/cache_type.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/simple/simple_backend_impl.h"
#ifdef USE_TRACING_CACHE_BACKEND
@@ -79,11 +79,14 @@ CacheCreator::~CacheCreator() {
}
int CacheCreator::Run() {
- // TODO(gavinp,pasko): Turn Simple Cache on for more cache types as
- // appropriate.
- if (backend_type_ == net::CACHE_BACKEND_SIMPLE &&
- (type_ == net::DISK_CACHE || type_ == net::APP_CACHE ||
- type_ == net::MEDIA_CACHE)) {
+#if defined(OS_ANDROID)
+ static const bool kSimpleBackendIsDefault = true;
+#else
+ static const bool kSimpleBackendIsDefault = false;
+#endif
+ if (backend_type_ == net::CACHE_BACKEND_SIMPLE ||
+ (backend_type_ == net::CACHE_BACKEND_DEFAULT &&
+ kSimpleBackendIsDefault)) {
disk_cache::SimpleBackendImpl* simple_cache =
new disk_cache::SimpleBackendImpl(path_, max_bytes_, type_,
thread_.get(), net_log_);
@@ -91,6 +94,11 @@ int CacheCreator::Run() {
return simple_cache->Init(
base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this)));
}
+
+ // Avoid references to blockfile functions on Android to reduce binary size.
+#if defined(OS_ANDROID)
+ return net::ERR_FAILED;
+#else
disk_cache::BackendImpl* new_cache =
new disk_cache::BackendImpl(path_, thread_.get(), net_log_);
created_cache_.reset(new_cache);
@@ -101,6 +109,7 @@ int CacheCreator::Run() {
base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this)));
DCHECK_EQ(net::ERR_IO_PENDING, rv);
return rv;
+#endif
}
void CacheCreator::DoCallback(int result) {
diff --git a/chromium/net/disk_cache/cache_util.cc b/chromium/net/disk_cache/cache_util.cc
index 4452e7b0aa9..276f2f3381b 100644
--- a/chromium/net/disk_cache/cache_util.cc
+++ b/chromium/net/disk_cache/cache_util.cc
@@ -9,6 +9,7 @@
#include "base/location.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/worker_pool.h"
@@ -118,7 +119,7 @@ bool DelayedCacheCleanup(const base::FilePath& full_path) {
std::string name_str = name.value();
#elif defined(OS_WIN)
// We created this file so it should only contain ASCII.
- std::string name_str = WideToASCII(name.value());
+ std::string name_str = base::UTF16ToASCII(name.value());
#endif
base::FilePath to_delete = GetTempCacheName(path, name_str);
diff --git a/chromium/net/disk_cache/disk_cache.h b/chromium/net/disk_cache/disk_cache.h
index 3ae8bf4369b..e2963d871fd 100644
--- a/chromium/net/disk_cache/disk_cache.h
+++ b/chromium/net/disk_cache/disk_cache.h
@@ -109,6 +109,7 @@ class NET_EXPORT Backend {
// either direction by using null Time values for either argument. The return
// value is a net error code. If this method returns ERR_IO_PENDING, the
// |callback| will be invoked when the operation completes.
+ // Entries with |initial_time| <= access time < |end_time| are deleted.
virtual int DoomEntriesBetween(base::Time initial_time,
base::Time end_time,
const CompletionCallback& callback) = 0;
@@ -116,6 +117,7 @@ class NET_EXPORT Backend {
// Marks all entries accessed since |initial_time| for deletion. The return
// value is a net error code. If this method returns ERR_IO_PENDING, the
// |callback| will be invoked when the operation completes.
+ // Entries with |initial_time| <= access time are deleted.
virtual int DoomEntriesSince(base::Time initial_time,
const CompletionCallback& callback) = 0;
diff --git a/chromium/net/disk_cache/disk_cache_test_base.cc b/chromium/net/disk_cache/disk_cache_test_base.cc
index 3cf98a86b14..045765dd448 100644
--- a/chromium/net/disk_cache/disk_cache_test_base.cc
+++ b/chromium/net/disk_cache/disk_cache_test_base.cc
@@ -11,11 +11,11 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/simple/simple_backend_impl.h"
#include "net/disk_cache/simple/simple_index.h"
diff --git a/chromium/net/disk_cache/disk_cache_test_util.cc b/chromium/net/disk_cache/disk_cache_test_util.cc
index 8f334f05370..3f69492b79d 100644
--- a/chromium/net/disk_cache/disk_cache_test_util.cc
+++ b/chromium/net/disk_cache/disk_cache_test_util.cc
@@ -4,14 +4,15 @@
#include "net/disk_cache/disk_cache_test_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/path_service.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/file.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/file.h"
using base::Time;
using base::TimeDelta;
@@ -42,16 +43,14 @@ void CacheTestFillBuffer(char* buffer, size_t len, bool no_nulls) {
}
bool CreateCacheTestFile(const base::FilePath& name) {
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE;
+ int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_WRITE;
- scoped_refptr<disk_cache::File> file(new disk_cache::File(
- base::CreatePlatformFile(name, flags, NULL, NULL)));
- if (!file->IsValid())
+ base::File file(name, flags);
+ if (!file.IsValid())
return false;
- file->SetLength(4 * 1024 * 1024);
+ file.SetLength(4 * 1024 * 1024);
return true;
}
diff --git a/chromium/net/disk_cache/entry_unittest.cc b/chromium/net/disk_cache/entry_unittest.cc
index 291dedfb433..359f7250845 100644
--- a/chromium/net/disk_cache/entry_unittest.cc
+++ b/chromium/net/disk_cache/entry_unittest.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/platform_thread.h"
@@ -14,11 +15,11 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
#include "net/disk_cache/disk_cache_test_base.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/entry_impl.h"
-#include "net/disk_cache/mem_entry_impl.h"
+#include "net/disk_cache/memory/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"
@@ -40,22 +41,22 @@ class DiskCacheEntryTest : public DiskCacheTestWithCache {
void InternalAsyncIO();
void ExternalSyncIO();
void ExternalAsyncIO();
- void ReleaseBuffer();
+ void ReleaseBuffer(int stream_index);
void StreamAccess();
void GetKey();
- void GetTimes();
- void GrowData();
- void TruncateData();
- void ZeroLengthIO();
+ void GetTimes(int stream_index);
+ void GrowData(int stream_index);
+ void TruncateData(int stream_index);
+ void ZeroLengthIO(int stream_index);
void Buffering();
void SizeAtCreate();
- void SizeChanges();
- void ReuseEntry(int size);
- void InvalidData();
- void ReadWriteDestroyBuffer();
+ void SizeChanges(int stream_index);
+ void ReuseEntry(int size, int stream_index);
+ void InvalidData(int stream_index);
+ void ReadWriteDestroyBuffer(int stream_index);
void DoomNormalEntry();
void DoomEntryNextToOpenEntry();
- void DoomedEntry();
+ void DoomedEntry(int stream_index);
void BasicSparseIO();
void HugeSparseIO();
void GetAvailableRange();
@@ -611,7 +612,7 @@ TEST_F(DiskCacheEntryTest, MemoryOnlyExternalAsyncIO) {
}
// Tests that IOBuffers are not referenced after IO completes.
-void DiskCacheEntryTest::ReleaseBuffer() {
+void DiskCacheEntryTest::ReleaseBuffer(int stream_index) {
disk_cache::Entry* entry = NULL;
ASSERT_EQ(net::OK, CreateEntry("the first key", &entry));
ASSERT_TRUE(NULL != entry);
@@ -621,8 +622,8 @@ void DiskCacheEntryTest::ReleaseBuffer() {
CacheTestFillBuffer(buffer->data(), kBufferSize, false);
net::ReleaseBufferCompletionCallback cb(buffer.get());
- int rv =
- entry->WriteData(0, 0, buffer.get(), kBufferSize, cb.callback(), false);
+ int rv = entry->WriteData(
+ stream_index, 0, buffer.get(), kBufferSize, cb.callback(), false);
EXPECT_EQ(kBufferSize, cb.GetResult(rv));
entry->Close();
}
@@ -630,13 +631,13 @@ void DiskCacheEntryTest::ReleaseBuffer() {
TEST_F(DiskCacheEntryTest, ReleaseBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- ReleaseBuffer();
+ ReleaseBuffer(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyReleaseBuffer) {
SetMemoryOnlyMode();
InitCache();
- ReleaseBuffer();
+ ReleaseBuffer(0);
}
void DiskCacheEntryTest::StreamAccess() {
@@ -758,7 +759,7 @@ TEST_F(DiskCacheEntryTest, MemoryOnlyGetKey) {
GetKey();
}
-void DiskCacheEntryTest::GetTimes() {
+void DiskCacheEntryTest::GetTimes(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
@@ -770,7 +771,7 @@ void DiskCacheEntryTest::GetTimes() {
AddDelay();
Time t2 = Time::Now();
EXPECT_TRUE(t2 > t1);
- EXPECT_EQ(0, WriteData(entry, 0, 200, NULL, 0, false));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 200, NULL, 0, false));
if (type_ == net::APP_CACHE) {
EXPECT_TRUE(entry->GetLastModified() < t2);
} else {
@@ -783,7 +784,7 @@ void DiskCacheEntryTest::GetTimes() {
EXPECT_TRUE(t3 > t2);
const int kSize = 200;
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
- EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 0, buffer.get(), kSize));
if (type_ == net::APP_CACHE) {
EXPECT_TRUE(entry->GetLastUsed() < t2);
EXPECT_TRUE(entry->GetLastModified() < t2);
@@ -799,28 +800,28 @@ void DiskCacheEntryTest::GetTimes() {
TEST_F(DiskCacheEntryTest, GetTimes) {
InitCache();
- GetTimes();
+ GetTimes(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyGetTimes) {
SetMemoryOnlyMode();
InitCache();
- GetTimes();
+ GetTimes(0);
}
TEST_F(DiskCacheEntryTest, AppCacheGetTimes) {
SetCacheType(net::APP_CACHE);
InitCache();
- GetTimes();
+ GetTimes(0);
}
TEST_F(DiskCacheEntryTest, ShaderCacheGetTimes) {
SetCacheType(net::SHADER_CACHE);
InitCache();
- GetTimes();
+ GetTimes(0);
}
-void DiskCacheEntryTest::GrowData() {
+void DiskCacheEntryTest::GrowData(int stream_index) {
std::string key1("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key1, &entry));
@@ -832,78 +833,82 @@ void DiskCacheEntryTest::GrowData() {
memset(buffer2->data(), 0, kSize);
base::strlcpy(buffer1->data(), "the data", kSize);
- EXPECT_EQ(10, WriteData(entry, 0, 0, buffer1.get(), 10, false));
- EXPECT_EQ(10, ReadData(entry, 0, 0, buffer2.get(), 10));
+ EXPECT_EQ(10, WriteData(entry, stream_index, 0, buffer1.get(), 10, false));
+ EXPECT_EQ(10, ReadData(entry, stream_index, 0, buffer2.get(), 10));
EXPECT_STREQ("the data", buffer2->data());
- EXPECT_EQ(10, entry->GetDataSize(0));
+ EXPECT_EQ(10, entry->GetDataSize(stream_index));
- EXPECT_EQ(2000, WriteData(entry, 0, 0, buffer1.get(), 2000, false));
- EXPECT_EQ(2000, entry->GetDataSize(0));
- EXPECT_EQ(2000, ReadData(entry, 0, 0, buffer2.get(), 2000));
+ EXPECT_EQ(2000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 2000, false));
+ EXPECT_EQ(2000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(2000, ReadData(entry, stream_index, 0, buffer2.get(), 2000));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), 2000));
- EXPECT_EQ(20000, WriteData(entry, 0, 0, buffer1.get(), kSize, false));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(20000, ReadData(entry, 0, 0, buffer2.get(), kSize));
+ EXPECT_EQ(20000,
+ WriteData(entry, stream_index, 0, buffer1.get(), kSize, false));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(20000, ReadData(entry, stream_index, 0, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), kSize));
entry->Close();
memset(buffer2->data(), 0, kSize);
std::string key2("Second key");
ASSERT_EQ(net::OK, CreateEntry(key2, &entry));
- EXPECT_EQ(10, WriteData(entry, 0, 0, buffer1.get(), 10, false));
- EXPECT_EQ(10, entry->GetDataSize(0));
+ EXPECT_EQ(10, WriteData(entry, stream_index, 0, buffer1.get(), 10, false));
+ EXPECT_EQ(10, entry->GetDataSize(stream_index));
entry->Close();
// Go from an internal address to a bigger block size.
ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
- EXPECT_EQ(2000, WriteData(entry, 0, 0, buffer1.get(), 2000, false));
- EXPECT_EQ(2000, entry->GetDataSize(0));
- EXPECT_EQ(2000, ReadData(entry, 0, 0, buffer2.get(), 2000));
+ EXPECT_EQ(2000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 2000, false));
+ EXPECT_EQ(2000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(2000, ReadData(entry, stream_index, 0, buffer2.get(), 2000));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), 2000));
entry->Close();
memset(buffer2->data(), 0, kSize);
// Go from an internal address to an external one.
ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
- EXPECT_EQ(20000, WriteData(entry, 0, 0, buffer1.get(), kSize, false));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(20000, ReadData(entry, 0, 0, buffer2.get(), kSize));
+ EXPECT_EQ(20000,
+ WriteData(entry, stream_index, 0, buffer1.get(), kSize, false));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(20000, ReadData(entry, stream_index, 0, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), kSize));
entry->Close();
// Double check the size from disk.
ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
- EXPECT_EQ(20000, entry->GetDataSize(0));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
// Now extend the entry without actual data.
- EXPECT_EQ(0, WriteData(entry, 0, 45500, buffer1.get(), 0, false));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 45500, buffer1.get(), 0, false));
entry->Close();
// And check again from disk.
ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
- EXPECT_EQ(45500, entry->GetDataSize(0));
+ EXPECT_EQ(45500, entry->GetDataSize(stream_index));
entry->Close();
}
TEST_F(DiskCacheEntryTest, GrowData) {
InitCache();
- GrowData();
+ GrowData(0);
}
TEST_F(DiskCacheEntryTest, GrowDataNoBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- GrowData();
+ GrowData(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyGrowData) {
SetMemoryOnlyMode();
InitCache();
- GrowData();
+ GrowData(0);
}
-void DiskCacheEntryTest::TruncateData() {
+void DiskCacheEntryTest::TruncateData(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -917,86 +922,91 @@ void DiskCacheEntryTest::TruncateData() {
memset(buffer2->data(), 0, kSize2);
// Simple truncation:
- EXPECT_EQ(200, WriteData(entry, 0, 0, buffer1.get(), 200, false));
- EXPECT_EQ(200, entry->GetDataSize(0));
- EXPECT_EQ(100, WriteData(entry, 0, 0, buffer1.get(), 100, false));
- EXPECT_EQ(200, entry->GetDataSize(0));
- EXPECT_EQ(100, WriteData(entry, 0, 0, buffer1.get(), 100, true));
- EXPECT_EQ(100, entry->GetDataSize(0));
- EXPECT_EQ(0, WriteData(entry, 0, 50, buffer1.get(), 0, true));
- EXPECT_EQ(50, entry->GetDataSize(0));
- EXPECT_EQ(0, WriteData(entry, 0, 0, buffer1.get(), 0, true));
- EXPECT_EQ(0, entry->GetDataSize(0));
+ EXPECT_EQ(200, WriteData(entry, stream_index, 0, buffer1.get(), 200, false));
+ EXPECT_EQ(200, entry->GetDataSize(stream_index));
+ EXPECT_EQ(100, WriteData(entry, stream_index, 0, buffer1.get(), 100, false));
+ EXPECT_EQ(200, entry->GetDataSize(stream_index));
+ EXPECT_EQ(100, WriteData(entry, stream_index, 0, buffer1.get(), 100, true));
+ EXPECT_EQ(100, entry->GetDataSize(stream_index));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 50, buffer1.get(), 0, true));
+ EXPECT_EQ(50, entry->GetDataSize(stream_index));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 0, buffer1.get(), 0, true));
+ EXPECT_EQ(0, entry->GetDataSize(stream_index));
entry->Close();
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
// Go to an external file.
- EXPECT_EQ(20000, WriteData(entry, 0, 0, buffer1.get(), 20000, true));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(20000, ReadData(entry, 0, 0, buffer2.get(), 20000));
+ EXPECT_EQ(20000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 20000, true));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(20000, ReadData(entry, stream_index, 0, buffer2.get(), 20000));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), 20000));
memset(buffer2->data(), 0, kSize2);
// External file truncation
- EXPECT_EQ(18000, WriteData(entry, 0, 0, buffer1.get(), 18000, false));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(18000, WriteData(entry, 0, 0, buffer1.get(), 18000, true));
- EXPECT_EQ(18000, entry->GetDataSize(0));
- EXPECT_EQ(0, WriteData(entry, 0, 17500, buffer1.get(), 0, true));
- EXPECT_EQ(17500, entry->GetDataSize(0));
+ EXPECT_EQ(18000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 18000, false));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(18000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 18000, true));
+ EXPECT_EQ(18000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 17500, buffer1.get(), 0, true));
+ EXPECT_EQ(17500, entry->GetDataSize(stream_index));
// And back to an internal block.
- EXPECT_EQ(600, WriteData(entry, 0, 1000, buffer1.get(), 600, true));
- EXPECT_EQ(1600, entry->GetDataSize(0));
- EXPECT_EQ(600, ReadData(entry, 0, 1000, buffer2.get(), 600));
+ EXPECT_EQ(600,
+ WriteData(entry, stream_index, 1000, buffer1.get(), 600, true));
+ EXPECT_EQ(1600, entry->GetDataSize(stream_index));
+ EXPECT_EQ(600, ReadData(entry, stream_index, 1000, buffer2.get(), 600));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), 600));
- EXPECT_EQ(1000, ReadData(entry, 0, 0, buffer2.get(), 1000));
+ EXPECT_EQ(1000, ReadData(entry, stream_index, 0, buffer2.get(), 1000));
EXPECT_TRUE(!memcmp(buffer1->data(), buffer2->data(), 1000))
<< "Preserves previous data";
// Go from external file to zero length.
- EXPECT_EQ(20000, WriteData(entry, 0, 0, buffer1.get(), 20000, true));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(0, WriteData(entry, 0, 0, buffer1.get(), 0, true));
- EXPECT_EQ(0, entry->GetDataSize(0));
+ EXPECT_EQ(20000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 20000, true));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 0, buffer1.get(), 0, true));
+ EXPECT_EQ(0, entry->GetDataSize(stream_index));
entry->Close();
}
TEST_F(DiskCacheEntryTest, TruncateData) {
InitCache();
- TruncateData();
+ TruncateData(0);
}
TEST_F(DiskCacheEntryTest, TruncateDataNoBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- TruncateData();
+ TruncateData(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyTruncateData) {
SetMemoryOnlyMode();
InitCache();
- TruncateData();
+ TruncateData(0);
}
-void DiskCacheEntryTest::ZeroLengthIO() {
+void DiskCacheEntryTest::ZeroLengthIO(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
- EXPECT_EQ(0, ReadData(entry, 0, 0, NULL, 0));
- EXPECT_EQ(0, WriteData(entry, 0, 0, NULL, 0, false));
+ EXPECT_EQ(0, ReadData(entry, stream_index, 0, NULL, 0));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 0, NULL, 0, false));
// This write should extend the entry.
- EXPECT_EQ(0, WriteData(entry, 0, 1000, NULL, 0, false));
- EXPECT_EQ(0, ReadData(entry, 0, 500, NULL, 0));
- EXPECT_EQ(0, ReadData(entry, 0, 2000, NULL, 0));
- EXPECT_EQ(1000, entry->GetDataSize(0));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 1000, NULL, 0, false));
+ EXPECT_EQ(0, ReadData(entry, stream_index, 500, NULL, 0));
+ EXPECT_EQ(0, ReadData(entry, stream_index, 2000, NULL, 0));
+ EXPECT_EQ(1000, entry->GetDataSize(stream_index));
- EXPECT_EQ(0, WriteData(entry, 0, 100000, NULL, 0, true));
- EXPECT_EQ(0, ReadData(entry, 0, 50000, NULL, 0));
- EXPECT_EQ(100000, entry->GetDataSize(0));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 100000, NULL, 0, true));
+ EXPECT_EQ(0, ReadData(entry, stream_index, 50000, NULL, 0));
+ EXPECT_EQ(100000, entry->GetDataSize(stream_index));
// Let's verify the actual content.
const int kSize = 20;
@@ -1004,15 +1014,15 @@ void DiskCacheEntryTest::ZeroLengthIO() {
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
CacheTestFillBuffer(buffer->data(), kSize, false);
- EXPECT_EQ(kSize, ReadData(entry, 0, 500, buffer.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 500, buffer.get(), kSize));
EXPECT_TRUE(!memcmp(buffer->data(), zeros, kSize));
CacheTestFillBuffer(buffer->data(), kSize, false);
- EXPECT_EQ(kSize, ReadData(entry, 0, 5000, buffer.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 5000, buffer.get(), kSize));
EXPECT_TRUE(!memcmp(buffer->data(), zeros, kSize));
CacheTestFillBuffer(buffer->data(), kSize, false);
- EXPECT_EQ(kSize, ReadData(entry, 0, 50000, buffer.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 50000, buffer.get(), kSize));
EXPECT_TRUE(!memcmp(buffer->data(), zeros, kSize));
entry->Close();
@@ -1020,19 +1030,19 @@ void DiskCacheEntryTest::ZeroLengthIO() {
TEST_F(DiskCacheEntryTest, ZeroLengthIO) {
InitCache();
- ZeroLengthIO();
+ ZeroLengthIO(0);
}
TEST_F(DiskCacheEntryTest, ZeroLengthIONoBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- ZeroLengthIO();
+ ZeroLengthIO(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyZeroLengthIO) {
SetMemoryOnlyMode();
InitCache();
- ZeroLengthIO();
+ ZeroLengthIO(0);
}
// Tests that we handle the content correctly when buffering, a feature of the
@@ -1149,7 +1159,7 @@ TEST_F(DiskCacheEntryTest, MemoryOnlySizeAtCreate) {
// Some extra tests to make sure that buffering works properly when changing
// the entry size.
-void DiskCacheEntryTest::SizeChanges() {
+void DiskCacheEntryTest::SizeChanges(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -1161,68 +1171,83 @@ void DiskCacheEntryTest::SizeChanges() {
CacheTestFillBuffer(buffer1->data(), kSize, true);
CacheTestFillBuffer(buffer2->data(), kSize, true);
- EXPECT_EQ(kSize, WriteData(entry, 1, 0, buffer1.get(), kSize, true));
- EXPECT_EQ(kSize, WriteData(entry, 1, 17000, buffer1.get(), kSize, true));
- EXPECT_EQ(kSize, WriteData(entry, 1, 23000, buffer1.get(), kSize, true));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 0, buffer1.get(), kSize, true));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 17000, buffer1.get(), kSize, true));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 23000, buffer1.get(), kSize, true));
entry->Close();
// Extend the file and read between the old size and the new write.
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
- EXPECT_EQ(23000 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(kSize, WriteData(entry, 1, 25000, buffer1.get(), kSize, true));
- EXPECT_EQ(25000 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(kSize, ReadData(entry, 1, 24000, buffer2.get(), kSize));
+ EXPECT_EQ(23000 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 25000, buffer1.get(), kSize, true));
+ EXPECT_EQ(25000 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 24000, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), zeros, kSize));
// Read at the end of the old file size.
- EXPECT_EQ(kSize,
- ReadData(entry, 1, 23000 + kSize - 35, buffer2.get(), kSize));
+ EXPECT_EQ(
+ kSize,
+ ReadData(entry, stream_index, 23000 + kSize - 35, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), buffer1->data() + kSize - 35, 35));
// Read slightly before the last write.
CacheTestFillBuffer(buffer2->data(), kSize, true);
- EXPECT_EQ(kSize, ReadData(entry, 1, 24900, buffer2.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 24900, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), zeros, 100));
EXPECT_TRUE(!memcmp(buffer2->data() + 100, buffer1->data(), kSize - 100));
// Extend the entry a little more.
- EXPECT_EQ(kSize, WriteData(entry, 1, 26000, buffer1.get(), kSize, true));
- EXPECT_EQ(26000 + kSize, entry->GetDataSize(1));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 26000, buffer1.get(), kSize, true));
+ EXPECT_EQ(26000 + kSize, entry->GetDataSize(stream_index));
CacheTestFillBuffer(buffer2->data(), kSize, true);
- EXPECT_EQ(kSize, ReadData(entry, 1, 25900, buffer2.get(), kSize));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 25900, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), zeros, 100));
EXPECT_TRUE(!memcmp(buffer2->data() + 100, buffer1->data(), kSize - 100));
// And now reduce the size.
- EXPECT_EQ(kSize, WriteData(entry, 1, 25000, buffer1.get(), kSize, true));
- EXPECT_EQ(25000 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(28, ReadData(entry, 1, 25000 + kSize - 28, buffer2.get(), kSize));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 25000, buffer1.get(), kSize, true));
+ EXPECT_EQ(25000 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(
+ 28,
+ ReadData(entry, stream_index, 25000 + kSize - 28, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), buffer1->data() + kSize - 28, 28));
// Reduce the size with a buffer that is not extending the size.
- EXPECT_EQ(kSize, WriteData(entry, 1, 24000, buffer1.get(), kSize, false));
- EXPECT_EQ(25000 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(kSize, WriteData(entry, 1, 24500, buffer1.get(), kSize, true));
- EXPECT_EQ(24500 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(kSize, ReadData(entry, 1, 23900, buffer2.get(), kSize));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 24000, buffer1.get(), kSize, false));
+ EXPECT_EQ(25000 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 24500, buffer1.get(), kSize, true));
+ EXPECT_EQ(24500 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 23900, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), zeros, 100));
EXPECT_TRUE(!memcmp(buffer2->data() + 100, buffer1->data(), kSize - 100));
// And now reduce the size below the old size.
- EXPECT_EQ(kSize, WriteData(entry, 1, 19000, buffer1.get(), kSize, true));
- EXPECT_EQ(19000 + kSize, entry->GetDataSize(1));
- EXPECT_EQ(kSize, ReadData(entry, 1, 18900, buffer2.get(), kSize));
+ EXPECT_EQ(kSize,
+ WriteData(entry, stream_index, 19000, buffer1.get(), kSize, true));
+ EXPECT_EQ(19000 + kSize, entry->GetDataSize(stream_index));
+ EXPECT_EQ(kSize, ReadData(entry, stream_index, 18900, buffer2.get(), kSize));
EXPECT_TRUE(!memcmp(buffer2->data(), zeros, 100));
EXPECT_TRUE(!memcmp(buffer2->data() + 100, buffer1->data(), kSize - 100));
// Verify that the actual file is truncated.
entry->Close();
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
- EXPECT_EQ(19000 + kSize, entry->GetDataSize(1));
+ EXPECT_EQ(19000 + kSize, entry->GetDataSize(stream_index));
// Extend the newly opened file with a zero length write, expect zero fill.
- EXPECT_EQ(0, WriteData(entry, 1, 20000 + kSize, buffer1.get(), 0, false));
- EXPECT_EQ(kSize, ReadData(entry, 1, 19000 + kSize, buffer1.get(), kSize));
+ EXPECT_EQ(
+ 0,
+ WriteData(entry, stream_index, 20000 + kSize, buffer1.get(), 0, false));
+ EXPECT_EQ(kSize,
+ ReadData(entry, stream_index, 19000 + kSize, buffer1.get(), kSize));
EXPECT_EQ(0, memcmp(buffer1->data(), zeros, kSize));
entry->Close();
@@ -1230,18 +1255,18 @@ void DiskCacheEntryTest::SizeChanges() {
TEST_F(DiskCacheEntryTest, SizeChanges) {
InitCache();
- SizeChanges();
+ SizeChanges(1);
}
TEST_F(DiskCacheEntryTest, SizeChangesNoBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- SizeChanges();
+ SizeChanges(1);
}
// Write more than the total cache capacity but to a single entry. |size| is the
// amount of bytes to write each time.
-void DiskCacheEntryTest::ReuseEntry(int size) {
+void DiskCacheEntryTest::ReuseEntry(int size, int stream_index) {
std::string key1("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key1, &entry));
@@ -1254,8 +1279,9 @@ void DiskCacheEntryTest::ReuseEntry(int size) {
CacheTestFillBuffer(buffer->data(), size, false);
for (int i = 0; i < 15; i++) {
- EXPECT_EQ(0, WriteData(entry, 0, 0, buffer.get(), 0, true));
- EXPECT_EQ(size, WriteData(entry, 0, 0, buffer.get(), size, false));
+ EXPECT_EQ(0, WriteData(entry, stream_index, 0, buffer.get(), 0, true));
+ EXPECT_EQ(size,
+ WriteData(entry, stream_index, 0, buffer.get(), size, false));
entry->Close();
ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
}
@@ -1268,31 +1294,31 @@ void DiskCacheEntryTest::ReuseEntry(int size) {
TEST_F(DiskCacheEntryTest, ReuseExternalEntry) {
SetMaxSize(200 * 1024);
InitCache();
- ReuseEntry(20 * 1024);
+ ReuseEntry(20 * 1024, 0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyReuseExternalEntry) {
SetMemoryOnlyMode();
SetMaxSize(200 * 1024);
InitCache();
- ReuseEntry(20 * 1024);
+ ReuseEntry(20 * 1024, 0);
}
TEST_F(DiskCacheEntryTest, ReuseInternalEntry) {
SetMaxSize(100 * 1024);
InitCache();
- ReuseEntry(10 * 1024);
+ ReuseEntry(10 * 1024, 0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyReuseInternalEntry) {
SetMemoryOnlyMode();
SetMaxSize(100 * 1024);
InitCache();
- ReuseEntry(10 * 1024);
+ ReuseEntry(10 * 1024, 0);
}
// Reading somewhere that was not written should return zeros.
-void DiskCacheEntryTest::InvalidData() {
+void DiskCacheEntryTest::InvalidData(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -1308,49 +1334,56 @@ void DiskCacheEntryTest::InvalidData() {
memset(buffer2->data(), 0, kSize2);
// Simple data grow:
- EXPECT_EQ(200, WriteData(entry, 0, 400, buffer1.get(), 200, false));
- EXPECT_EQ(600, entry->GetDataSize(0));
- EXPECT_EQ(100, ReadData(entry, 0, 300, buffer3.get(), 100));
+ EXPECT_EQ(200,
+ WriteData(entry, stream_index, 400, buffer1.get(), 200, false));
+ EXPECT_EQ(600, entry->GetDataSize(stream_index));
+ EXPECT_EQ(100, ReadData(entry, stream_index, 300, buffer3.get(), 100));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 100));
entry->Close();
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
// The entry is now on disk. Load it and extend it.
- EXPECT_EQ(200, WriteData(entry, 0, 800, buffer1.get(), 200, false));
- EXPECT_EQ(1000, entry->GetDataSize(0));
- EXPECT_EQ(100, ReadData(entry, 0, 700, buffer3.get(), 100));
+ EXPECT_EQ(200,
+ WriteData(entry, stream_index, 800, buffer1.get(), 200, false));
+ EXPECT_EQ(1000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(100, ReadData(entry, stream_index, 700, buffer3.get(), 100));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 100));
entry->Close();
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
// This time using truncate.
- EXPECT_EQ(200, WriteData(entry, 0, 1800, buffer1.get(), 200, true));
- EXPECT_EQ(2000, entry->GetDataSize(0));
- EXPECT_EQ(100, ReadData(entry, 0, 1500, buffer3.get(), 100));
+ EXPECT_EQ(200,
+ WriteData(entry, stream_index, 1800, buffer1.get(), 200, true));
+ EXPECT_EQ(2000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(100, ReadData(entry, stream_index, 1500, buffer3.get(), 100));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 100));
// Go to an external file.
- EXPECT_EQ(200, WriteData(entry, 0, 19800, buffer1.get(), 200, false));
- EXPECT_EQ(20000, entry->GetDataSize(0));
- EXPECT_EQ(4000, ReadData(entry, 0, 14000, buffer3.get(), 4000));
+ EXPECT_EQ(200,
+ WriteData(entry, stream_index, 19800, buffer1.get(), 200, false));
+ EXPECT_EQ(20000, entry->GetDataSize(stream_index));
+ EXPECT_EQ(4000, ReadData(entry, stream_index, 14000, buffer3.get(), 4000));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 4000));
// And back to an internal block.
- EXPECT_EQ(600, WriteData(entry, 0, 1000, buffer1.get(), 600, true));
- EXPECT_EQ(1600, entry->GetDataSize(0));
- EXPECT_EQ(600, ReadData(entry, 0, 1000, buffer3.get(), 600));
+ EXPECT_EQ(600,
+ WriteData(entry, stream_index, 1000, buffer1.get(), 600, true));
+ EXPECT_EQ(1600, entry->GetDataSize(stream_index));
+ EXPECT_EQ(600, ReadData(entry, stream_index, 1000, buffer3.get(), 600));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer1->data(), 600));
// Extend it again.
- EXPECT_EQ(600, WriteData(entry, 0, 2000, buffer1.get(), 600, false));
- EXPECT_EQ(2600, entry->GetDataSize(0));
- EXPECT_EQ(200, ReadData(entry, 0, 1800, buffer3.get(), 200));
+ EXPECT_EQ(600,
+ WriteData(entry, stream_index, 2000, buffer1.get(), 600, false));
+ EXPECT_EQ(2600, entry->GetDataSize(stream_index));
+ EXPECT_EQ(200, ReadData(entry, stream_index, 1800, buffer3.get(), 200));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 200));
// And again (with truncation flag).
- EXPECT_EQ(600, WriteData(entry, 0, 3000, buffer1.get(), 600, true));
- EXPECT_EQ(3600, entry->GetDataSize(0));
- EXPECT_EQ(200, ReadData(entry, 0, 2800, buffer3.get(), 200));
+ EXPECT_EQ(600,
+ WriteData(entry, stream_index, 3000, buffer1.get(), 600, true));
+ EXPECT_EQ(3600, entry->GetDataSize(stream_index));
+ EXPECT_EQ(200, ReadData(entry, stream_index, 2800, buffer3.get(), 200));
EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 200));
entry->Close();
@@ -1358,23 +1391,23 @@ void DiskCacheEntryTest::InvalidData() {
TEST_F(DiskCacheEntryTest, InvalidData) {
InitCache();
- InvalidData();
+ InvalidData(0);
}
TEST_F(DiskCacheEntryTest, InvalidDataNoBuffer) {
InitCache();
cache_impl_->SetFlags(disk_cache::kNoBuffering);
- InvalidData();
+ InvalidData(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyInvalidData) {
SetMemoryOnlyMode();
InitCache();
- InvalidData();
+ InvalidData(0);
}
// Tests that the cache preserves the buffer of an IO operation.
-void DiskCacheEntryTest::ReadWriteDestroyBuffer() {
+void DiskCacheEntryTest::ReadWriteDestroyBuffer(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -1385,7 +1418,8 @@ void DiskCacheEntryTest::ReadWriteDestroyBuffer() {
net::TestCompletionCallback cb;
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer.get(), kSize, cb.callback(), false));
+ entry->WriteData(
+ stream_index, 0, buffer.get(), kSize, cb.callback(), false));
// Release our reference to the buffer.
buffer = NULL;
@@ -1395,8 +1429,9 @@ void DiskCacheEntryTest::ReadWriteDestroyBuffer() {
buffer = new net::IOBuffer(kSize);
CacheTestFillBuffer(buffer->data(), kSize, false);
- EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0, 0, buffer.get(), kSize, cb.callback()));
+ EXPECT_EQ(
+ net::ERR_IO_PENDING,
+ entry->ReadData(stream_index, 0, buffer.get(), kSize, cb.callback()));
buffer = NULL;
EXPECT_EQ(kSize, cb.WaitForResult());
@@ -1405,7 +1440,7 @@ void DiskCacheEntryTest::ReadWriteDestroyBuffer() {
TEST_F(DiskCacheEntryTest, ReadWriteDestroyBuffer) {
InitCache();
- ReadWriteDestroyBuffer();
+ ReadWriteDestroyBuffer(0);
}
void DiskCacheEntryTest::DoomNormalEntry() {
@@ -1485,7 +1520,7 @@ TEST_F(DiskCacheEntryTest, AppCacheDoomEntryNextToOpenEntry) {
}
// Verify that basic operations work as expected with doomed entries.
-void DiskCacheEntryTest::DoomedEntry() {
+void DiskCacheEntryTest::DoomedEntry(int stream_index) {
std::string key("the first key");
disk_cache::Entry* entry;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -1503,8 +1538,9 @@ void DiskCacheEntryTest::DoomedEntry() {
CacheTestFillBuffer(buffer1->data(), kSize1, false);
memset(buffer2->data(), 0, kSize2);
- EXPECT_EQ(2000, WriteData(entry, 0, 0, buffer1.get(), 2000, false));
- EXPECT_EQ(2000, ReadData(entry, 0, 0, buffer2.get(), 2000));
+ EXPECT_EQ(2000,
+ WriteData(entry, stream_index, 0, buffer1.get(), 2000, false));
+ EXPECT_EQ(2000, ReadData(entry, stream_index, 0, buffer2.get(), 2000));
EXPECT_EQ(0, memcmp(buffer1->data(), buffer2->data(), kSize1));
EXPECT_EQ(key, entry->GetKey());
EXPECT_TRUE(initial < entry->GetLastModified());
@@ -1515,13 +1551,13 @@ void DiskCacheEntryTest::DoomedEntry() {
TEST_F(DiskCacheEntryTest, DoomedEntry) {
InitCache();
- DoomedEntry();
+ DoomedEntry(0);
}
TEST_F(DiskCacheEntryTest, MemoryOnlyDoomedEntry) {
SetMemoryOnlyMode();
InitCache();
- DoomedEntry();
+ DoomedEntry(0);
}
// Tests that we discard entries if the data is missing.
@@ -2315,7 +2351,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheExternalAsyncIO) {
TEST_F(DiskCacheEntryTest, SimpleCacheReleaseBuffer) {
SetSimpleCacheMode();
InitCache();
- ReleaseBuffer();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ ReleaseBuffer(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheStreamAccess) {
@@ -2333,25 +2372,37 @@ TEST_F(DiskCacheEntryTest, SimpleCacheGetKey) {
TEST_F(DiskCacheEntryTest, SimpleCacheGetTimes) {
SetSimpleCacheMode();
InitCache();
- GetTimes();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ GetTimes(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheGrowData) {
SetSimpleCacheMode();
InitCache();
- GrowData();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ GrowData(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheTruncateData) {
SetSimpleCacheMode();
InitCache();
- TruncateData();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ TruncateData(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheZeroLengthIO) {
SetSimpleCacheMode();
InitCache();
- ZeroLengthIO();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ ZeroLengthIO(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheSizeAtCreate) {
@@ -2364,32 +2415,52 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReuseExternalEntry) {
SetSimpleCacheMode();
SetMaxSize(200 * 1024);
InitCache();
- ReuseEntry(20 * 1024);
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ ReuseEntry(20 * 1024, i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheReuseInternalEntry) {
SetSimpleCacheMode();
SetMaxSize(100 * 1024);
InitCache();
- ReuseEntry(10 * 1024);
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ ReuseEntry(10 * 1024, i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheSizeChanges) {
SetSimpleCacheMode();
InitCache();
- SizeChanges();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ SizeChanges(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheInvalidData) {
SetSimpleCacheMode();
InitCache();
- InvalidData();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ InvalidData(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheReadWriteDestroyBuffer) {
+ // Proving that the test works well with optimistic operations enabled is
+ // subtle, instead run only in APP_CACHE mode to disable optimistic
+ // operations. Stream 0 always uses optimistic operations, so the test is not
+ // run on stream 0.
+ SetCacheType(net::APP_CACHE);
SetSimpleCacheMode();
InitCache();
- ReadWriteDestroyBuffer();
+ for (int i = 1; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ ReadWriteDestroyBuffer(i);
+ }
}
TEST_F(DiskCacheEntryTest, SimpleCacheDoomEntry) {
@@ -2407,7 +2478,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomEntryNextToOpenEntry) {
TEST_F(DiskCacheEntryTest, SimpleCacheDoomedEntry) {
SetSimpleCacheMode();
InitCache();
- DoomedEntry();
+ // Stream 2 is excluded because the implementation does not support writing to
+ // it on a doomed entry, if it was previously lazily omitted.
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount - 1; ++i) {
+ EXPECT_EQ(net::OK, DoomAllEntries());
+ DoomedEntry(i);
+ }
}
// Creates an entry with corrupted last byte in stream 0.
@@ -2433,17 +2509,14 @@ bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const std::string& key,
// Corrupt the last byte of the data.
base::FilePath entry_file0_path = cache_path_.AppendASCII(
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)
+ base::File entry_file0(entry_file0_path,
+ base::File::FLAG_WRITE | base::File::FLAG_OPEN);
+ if (!entry_file0.IsValid())
return false;
int64 file_offset =
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;
+ EXPECT_EQ(1, entry_file0.Write(file_offset, "X", 1));
*data_size = kDataSize;
return true;
}
@@ -2495,14 +2568,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheErrorThenDoom) {
}
bool TruncatePath(const base::FilePath& file_path, int64 length) {
- const int flags = base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_OPEN;
- base::PlatformFile file =
- base::CreatePlatformFile(file_path, flags, NULL, NULL);
- if (base::kInvalidPlatformFileValue == file)
+ base::File file(file_path, base::File::FLAG_WRITE | base::File::FLAG_OPEN);
+ if (!file.IsValid())
return false;
- const bool result = base::TruncatePlatformFile(file, length);
- base::ClosePlatformFile(file);
- return result;
+ return file.SetLength(length);
}
TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) {
@@ -3013,7 +3082,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::GetFilenameFromKeyAndFileIndex(key, i));
- base::PlatformFileInfo info;
+ base::File::Info info;
EXPECT_FALSE(base::GetFileInfo(entry_file_path, &info));
}
}
@@ -3160,6 +3229,11 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) {
std::string key2("the key prefix");
for (int i = 0; i < kNumExtraEntries; i++) {
+ if (i == kNumExtraEntries - 2) {
+ // Create a distinct timestamp for the last two entries. These entries
+ // will be checked for outliving the eviction.
+ AddDelay();
+ }
ASSERT_EQ(net::OK, CreateEntry(key2 + base::StringPrintf("%d", i), &entry));
ScopedEntryPtr entry_closer(entry);
EXPECT_EQ(kWriteSize,
@@ -3448,36 +3522,35 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonSequentialWrite) {
CacheTestFillBuffer(buffer1->data(), kSize, false);
char* buffer1_data = buffer1->data() + kHalfSize;
memcpy(buffer2->data(), buffer1_data, kHalfSize);
- disk_cache::Entry* entry = NULL;
+ disk_cache::Entry* entry = NULL;
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
- EXPECT_NE(null, entry);
-
- int offset = kHalfSize;
- int buf_len = kHalfSize;
-
- EXPECT_EQ(buf_len,
- WriteData(entry, 0, offset, buffer2.get(), buf_len, false));
- offset = 0;
- buf_len = kHalfSize;
- EXPECT_EQ(buf_len,
- WriteData(entry, 0, offset, buffer1.get(), buf_len, false));
entry->Close();
+ for (int i = 0; i < disk_cache::kSimpleEntryStreamCount; ++i) {
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ EXPECT_NE(null, entry);
- disk_cache::Entry* entry2 = NULL;
- ASSERT_EQ(net::OK, OpenEntry(key, &entry2));
- EXPECT_EQ(entry, entry2);
+ int offset = kHalfSize;
+ int buf_len = kHalfSize;
- scoped_refptr<net::IOBuffer> buffer1_read1(new net::IOBuffer(kSize));
- EXPECT_EQ(kSize, ReadData(entry2, 0, 0, buffer1_read1.get(), kSize));
- EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read1->data(), kSize));
+ EXPECT_EQ(buf_len,
+ WriteData(entry, i, offset, buffer2.get(), buf_len, false));
+ offset = 0;
+ buf_len = kHalfSize;
+ EXPECT_EQ(buf_len,
+ WriteData(entry, i, offset, buffer1.get(), buf_len, false));
+ entry->Close();
- // Check that we are not leaking.
- ASSERT_NE(entry, null);
- EXPECT_TRUE(
- static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef());
- entry->Close();
- entry = NULL;
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+
+ scoped_refptr<net::IOBuffer> buffer1_read1(new net::IOBuffer(kSize));
+ EXPECT_EQ(kSize, ReadData(entry, i, 0, buffer1_read1.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read1->data(), kSize));
+ // Check that we are not leaking.
+ ASSERT_NE(entry, null);
+ EXPECT_TRUE(static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef());
+ entry->Close();
+ }
}
// Test that changing stream1 size does not affect stream0 (stream0 and stream1
@@ -3516,10 +3589,9 @@ TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
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);
+ base::File entry_file0(entry_file0_path,
+ base::File::FLAG_READ | base::File::FLAG_OPEN);
+ ASSERT_TRUE(entry_file0.IsValid());
int data_size[disk_cache::kSimpleEntryStreamCount] = {kSize, stream1_size, 0};
int sparse_data_size = 0;
@@ -3527,11 +3599,9 @@ TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
base::Time::Now(), base::Time::Now(), data_size, sparse_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)));
+ ASSERT_EQ(static_cast<int>(sizeof(eof_record)),
+ entry_file0.Read(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);
diff --git a/chromium/net/disk_cache/flash/flash_cache_test_base.cc b/chromium/net/disk_cache/flash/flash_cache_test_base.cc
deleted file mode 100644
index 164eb338970..00000000000
--- a/chromium/net/disk_cache/flash/flash_cache_test_base.cc
+++ /dev/null
@@ -1,29 +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/disk_cache/flash/flash_cache_test_base.h"
-
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/time/time.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/storage.h"
-
-FlashCacheTest::FlashCacheTest() {
- int seed = static_cast<int>(base::Time::Now().ToInternalValue());
- srand(seed);
-}
-
-FlashCacheTest::~FlashCacheTest() {
-}
-
-void FlashCacheTest::SetUp() {
- const base::FilePath::StringType kCachePath = FILE_PATH_LITERAL("cache");
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- path_ = temp_dir_.path().Append(kCachePath);
-}
-
-void FlashCacheTest::TearDown() {
-}
diff --git a/chromium/net/disk_cache/flash/flash_cache_test_base.h b/chromium/net/disk_cache/flash/flash_cache_test_base.h
deleted file mode 100644
index eb082276171..00000000000
--- a/chromium/net/disk_cache/flash/flash_cache_test_base.h
+++ /dev/null
@@ -1,43 +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_DISK_CACHE_DISK_CACHE_FLASH_TEST_BASE_H_
-#define NET_DISK_CACHE_DISK_CACHE_FLASH_TEST_BASE_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/disk_cache/flash/format.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const int32 kNumTestSegments = 10;
-const int32 kStorageSize = kNumTestSegments * disk_cache::kFlashSegmentSize;
-
-} // namespace
-
-namespace disk_cache {
-
-class LogStore;
-
-} // namespace disk_cache
-
-class FlashCacheTest : public testing::Test {
- protected:
- FlashCacheTest();
- virtual ~FlashCacheTest();
-
- virtual void SetUp() OVERRIDE;
- virtual void TearDown() OVERRIDE;
-
- base::ScopedTempDir temp_dir_;
- base::FilePath path_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FlashCacheTest);
-};
-
-#endif // NET_DISK_CACHE_DISK_CACHE_FLASH_TEST_BASE_H_
diff --git a/chromium/net/disk_cache/flash/flash_entry_impl.cc b/chromium/net/disk_cache/flash/flash_entry_impl.cc
deleted file mode 100644
index a0e785cae10..00000000000
--- a/chromium/net/disk_cache/flash/flash_entry_impl.cc
+++ /dev/null
@@ -1,150 +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 "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/task_runner_util.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/disk_cache/flash/flash_entry_impl.h"
-#include "net/disk_cache/flash/internal_entry.h"
-
-namespace disk_cache {
-
-FlashEntryImpl::FlashEntryImpl(const std::string& key,
- LogStore* store,
- base::MessageLoopProxy* cache_thread)
- : init_(false),
- key_(key),
- new_internal_entry_(new InternalEntry(key, store)),
- cache_thread_(cache_thread) {
- memset(stream_sizes_, 0, sizeof(stream_sizes_));
-}
-
-FlashEntryImpl::FlashEntryImpl(int32 id,
- LogStore* store,
- base::MessageLoopProxy* cache_thread)
- : init_(false),
- old_internal_entry_(new InternalEntry(id, store)),
- cache_thread_(cache_thread) {
-}
-
-int FlashEntryImpl::Init(const CompletionCallback& callback) {
- if (new_internal_entry_.get()) {
- DCHECK(callback.is_null());
- init_ = true;
- return net::OK;
- }
- DCHECK(!callback.is_null() && old_internal_entry_.get());
- callback_ = callback;
- PostTaskAndReplyWithResult(cache_thread_.get(),
- FROM_HERE,
- Bind(&InternalEntry::Init, old_internal_entry_),
- Bind(&FlashEntryImpl::OnInitComplete, this));
- return net::ERR_IO_PENDING;
-}
-
-void FlashEntryImpl::Doom() {
- DCHECK(init_);
- NOTREACHED();
-}
-
-void FlashEntryImpl::Close() {
- DCHECK(init_);
- Release();
-}
-
-std::string FlashEntryImpl::GetKey() const {
- DCHECK(init_);
- return key_;
-}
-
-base::Time FlashEntryImpl::GetLastUsed() const {
- DCHECK(init_);
- NOTREACHED();
- return base::Time::Now();
-}
-
-base::Time FlashEntryImpl::GetLastModified() const {
- DCHECK(init_);
- NOTREACHED();
- return base::Time::Now();
-}
-
-int32 FlashEntryImpl::GetDataSize(int index) const {
- DCHECK(init_);
- return new_internal_entry_->GetDataSize(index);
-}
-
-int FlashEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- DCHECK(init_);
- return new_internal_entry_->ReadData(index, offset, buf, buf_len, callback);
-}
-
-int FlashEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback,
- bool truncate) {
- DCHECK(init_);
- return new_internal_entry_->WriteData(index, offset, buf, buf_len, callback);
-}
-
-int FlashEntryImpl::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- DCHECK(init_);
- NOTREACHED();
- return net::ERR_FAILED;
-}
-
-int FlashEntryImpl::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- DCHECK(init_);
- NOTREACHED();
- return net::ERR_FAILED;
-}
-
-int FlashEntryImpl::GetAvailableRange(int64 offset, int len, int64* start,
- const CompletionCallback& callback) {
- DCHECK(init_);
- NOTREACHED();
- return net::ERR_FAILED;
-}
-
-bool FlashEntryImpl::CouldBeSparse() const {
- DCHECK(init_);
- NOTREACHED();
- return false;
-}
-
-void FlashEntryImpl::CancelSparseIO() {
- DCHECK(init_);
- NOTREACHED();
-}
-
-int FlashEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
- DCHECK(init_);
- NOTREACHED();
- return net::ERR_FAILED;
-}
-
-void FlashEntryImpl::OnInitComplete(
- scoped_ptr<KeyAndStreamSizes> key_and_stream_sizes) {
- DCHECK(!callback_.is_null());
- if (!key_and_stream_sizes) {
- callback_.Run(net::ERR_FAILED);
- } else {
- key_ = key_and_stream_sizes->key;
- memcpy(stream_sizes_, key_and_stream_sizes->stream_sizes,
- sizeof(stream_sizes_));
- init_ = true;
- callback_.Run(net::OK);
- }
-}
-
-FlashEntryImpl::~FlashEntryImpl() {
- cache_thread_->PostTask(FROM_HERE,
- Bind(&InternalEntry::Close, new_internal_entry_));
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/flash_entry_impl.h b/chromium/net/disk_cache/flash/flash_entry_impl.h
deleted file mode 100644
index 32f489f899d..00000000000
--- a/chromium/net/disk_cache/flash/flash_entry_impl.h
+++ /dev/null
@@ -1,98 +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_DISK_CACHE_FLASH_ENTRY_IMPL_H_
-#define NET_DISK_CACHE_FLASH_ENTRY_IMPL_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/flash/internal_entry.h"
-
-namespace base {
-
-class MessageLoopProxy;
-
-} // namespace base
-
-namespace disk_cache {
-
-class InternalEntry;
-class IOBuffer;
-class LogStore;
-
-// We use split objects to minimize the context switches between the main thread
-// and the cache thread in the most common case of creating a new entry.
-//
-// All calls on a new entry are served synchronously. When an object is
-// destructed (via final Close() call), a message is posted to the cache thread
-// to save the object to storage.
-//
-// When an entry is not new, every asynchronous call is posted to the cache
-// thread, just as before; synchronous calls like GetKey() and GetDataSize() are
-// served from the main thread.
-class NET_EXPORT_PRIVATE FlashEntryImpl
- : public Entry,
- public base::RefCountedThreadSafe<FlashEntryImpl> {
- friend class base::RefCountedThreadSafe<FlashEntryImpl>;
- public:
- FlashEntryImpl(const std::string& key,
- LogStore* store,
- base::MessageLoopProxy* cache_thread);
- FlashEntryImpl(int32 id,
- LogStore* store,
- base::MessageLoopProxy* cache_thread);
-
- int Init(const CompletionCallback& callback);
-
- // disk_cache::Entry interface.
- virtual void Doom() OVERRIDE;
- virtual void Close() OVERRIDE;
- virtual std::string GetKey() const OVERRIDE;
- virtual base::Time GetLastUsed() const OVERRIDE;
- virtual base::Time GetLastModified() const OVERRIDE;
- virtual int32 GetDataSize(int index) const OVERRIDE;
- virtual int ReadData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) OVERRIDE;
- virtual int WriteData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback,
- bool truncate) OVERRIDE;
- virtual int ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) OVERRIDE;
- virtual int WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) OVERRIDE;
- virtual int GetAvailableRange(int64 offset, int len, int64* start,
- const CompletionCallback& callback) OVERRIDE;
- virtual bool CouldBeSparse() const OVERRIDE;
- virtual void CancelSparseIO() OVERRIDE;
- virtual int ReadyForSparseIO(const CompletionCallback& callback) OVERRIDE;
-
- private:
- void OnInitComplete(scoped_ptr<KeyAndStreamSizes> key_and_stream_sizes);
- virtual ~FlashEntryImpl();
-
- bool init_;
- std::string key_;
- int stream_sizes_[kFlashLogStoreEntryNumStreams];
-
- // Used if |this| is an newly created entry.
- scoped_refptr<InternalEntry> new_internal_entry_;
-
- // Used if |this| is an existing entry.
- scoped_refptr<InternalEntry> old_internal_entry_;
-
- // Copy of the callback for asynchronous calls on |old_internal_entry_|.
- CompletionCallback callback_;
-
- scoped_refptr<base::MessageLoopProxy> cache_thread_;
-
- DISALLOW_COPY_AND_ASSIGN(FlashEntryImpl);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_ENTRY_IMPL_H_
diff --git a/chromium/net/disk_cache/flash/format.h b/chromium/net/disk_cache/flash/format.h
deleted file mode 100644
index 872d96b34a2..00000000000
--- a/chromium/net/disk_cache/flash/format.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2011 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_FLASH_FORMAT_H_
-#define NET_DISK_CACHE_FLASH_FORMAT_H_
-
-namespace disk_cache {
-
-// Storage constants.
-const int32 kFlashPageSize = 8 * 1024;
-const int32 kFlashBlockSize = 512 * kFlashPageSize;
-
-// Segment constants.
-const int32 kFlashSegmentSize = 4 * 1024 * 1024;
-const int32 kFlashSmallEntrySize = 4 * 1024;
-const size_t kFlashMaxEntryCount = kFlashSegmentSize / kFlashSmallEntrySize - 1;
-
-// Segment summary consists of a fixed region at the end of the segment
-// containing a counter specifying the number of saved offsets followed by the
-// offsets.
-const int32 kFlashSummarySize = (1 + kFlashMaxEntryCount) * sizeof(int32);
-const int32 kFlashSegmentFreeSpace = kFlashSegmentSize - kFlashSummarySize;
-
-// An entry consists of a fixed number of streams.
-const int32 kFlashLogStoreEntryNumStreams = 4;
-const int32 kFlashLogStoreEntryHeaderSize =
- kFlashLogStoreEntryNumStreams * sizeof(int32);
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_FORMAT_H_
diff --git a/chromium/net/disk_cache/flash/internal_entry.cc b/chromium/net/disk_cache/flash/internal_entry.cc
deleted file mode 100644
index c6bae8926aa..00000000000
--- a/chromium/net/disk_cache/flash/internal_entry.cc
+++ /dev/null
@@ -1,86 +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/disk_cache/flash/internal_entry.h"
-
-#include "base/memory/ref_counted.h"
-#include "net/base/completion_callback.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/log_store_entry.h"
-
-using net::IOBuffer;
-using net::StringIOBuffer;
-using net::CompletionCallback;
-
-namespace disk_cache {
-
-KeyAndStreamSizes::KeyAndStreamSizes() {
-}
-
-InternalEntry::InternalEntry(const std::string& key, LogStore* store)
- : store_(store),
- entry_(new LogStoreEntry(store_)) {
- entry_->Init();
- WriteKey(entry_.get(), key);
-}
-
-InternalEntry::InternalEntry(int32 id, LogStore* store)
- : store_(store),
- entry_(new LogStoreEntry(store_, id)) {
-}
-
-InternalEntry::~InternalEntry() {
-}
-
-scoped_ptr<KeyAndStreamSizes> InternalEntry::Init() {
- scoped_ptr<KeyAndStreamSizes> null;
- if (entry_->IsNew())
- return null.Pass();
- if (!entry_->Init())
- return null.Pass();
-
- scoped_ptr<KeyAndStreamSizes> rv(new KeyAndStreamSizes);
- if (!ReadKey(entry_.get(), &rv->key))
- return null.Pass();
- for (int i = 0; i < kFlashLogStoreEntryNumStreams; ++i)
- rv->stream_sizes[i] = entry_->GetDataSize(i+1);
- return rv.Pass();
-}
-
-int32 InternalEntry::GetDataSize(int index) const {
- return entry_->GetDataSize(++index);
-}
-
-int InternalEntry::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- return entry_->ReadData(++index, offset, buf, buf_len);
-}
-
-int InternalEntry::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- return entry_->WriteData(++index, offset, buf, buf_len);
-}
-
-void InternalEntry::Close() {
- entry_->Close();
-}
-
-bool InternalEntry::WriteKey(LogStoreEntry* entry, const std::string& key) {
- int key_size = static_cast<int>(key.size());
- scoped_refptr<IOBuffer> key_buf(new StringIOBuffer(key));
- return entry->WriteData(0, 0, key_buf.get(), key_size) == key_size;
-}
-
-bool InternalEntry::ReadKey(LogStoreEntry* entry, std::string* key) {
- int key_size = entry->GetDataSize(0);
- scoped_refptr<net::IOBuffer> key_buf(new net::IOBuffer(key_size));
- if (entry->ReadData(0, 0, key_buf.get(), key_size) != key_size)
- return false;
- key->assign(key_buf->data(), key_size);
- return true;
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/internal_entry.h b/chromium/net/disk_cache/flash/internal_entry.h
deleted file mode 100644
index eeb2793f4c1..00000000000
--- a/chromium/net/disk_cache/flash/internal_entry.h
+++ /dev/null
@@ -1,63 +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_DISK_CACHE_FLASH_INTERNAL_ENTRY_H_
-#define NET_DISK_CACHE_FLASH_INTERNAL_ENTRY_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_export.h"
-#include "net/disk_cache/flash/format.h"
-
-namespace net {
-
-class IOBuffer;
-
-} // namespace net
-
-namespace disk_cache {
-
-struct KeyAndStreamSizes {
- KeyAndStreamSizes();
- std::string key;
- int stream_sizes[kFlashLogStoreEntryNumStreams];
-};
-
-class LogStore;
-class LogStoreEntry;
-
-// Actual entry implementation that does all the work of reading, writing and
-// storing data.
-class NET_EXPORT_PRIVATE InternalEntry
- : public base::RefCountedThreadSafe<InternalEntry> {
- friend class base::RefCountedThreadSafe<InternalEntry>;
- public:
- InternalEntry(const std::string& key, LogStore* store);
- InternalEntry(int32 id, LogStore* store);
-
- scoped_ptr<KeyAndStreamSizes> Init();
- int32 GetDataSize(int index) const;
- int ReadData(int index, int offset, net::IOBuffer* buf, int buf_len,
- const net::CompletionCallback& callback);
- int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
- const net::CompletionCallback& callback);
- void Close();
-
- private:
- bool WriteKey(LogStoreEntry* entry, const std::string& key);
- bool ReadKey(LogStoreEntry* entry, std::string* key);
- ~InternalEntry();
-
- LogStore* store_;
- scoped_ptr<LogStoreEntry> entry_;
-
- DISALLOW_COPY_AND_ASSIGN(InternalEntry);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_INTERNAL_ENTRY_H_
diff --git a/chromium/net/disk_cache/flash/log_store.cc b/chromium/net/disk_cache/flash/log_store.cc
deleted file mode 100644
index a2a82827027..00000000000
--- a/chromium/net/disk_cache/flash/log_store.cc
+++ /dev/null
@@ -1,185 +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 "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/stl_util.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/segment.h"
-#include "net/disk_cache/flash/storage.h"
-
-namespace disk_cache {
-
-LogStore::LogStore(const base::FilePath& path, int32 size)
- : storage_(path, size),
- num_segments_(size / kFlashSegmentSize),
- open_segments_(num_segments_),
- write_index_(0),
- current_entry_id_(-1),
- current_entry_num_bytes_left_to_write_(0),
- init_(false),
- closed_(false) {
- DCHECK(size % kFlashSegmentSize == 0);
-}
-
-LogStore::~LogStore() {
- DCHECK(!init_ || closed_);
- STLDeleteElements(&open_segments_);
-}
-
-bool LogStore::Init() {
- DCHECK(!init_);
- if (!storage_.Init())
- return false;
-
- // TODO(agayev): Once we start persisting segment metadata to disk, we will
- // start from where we left off during the last shutdown.
- scoped_ptr<Segment> segment(new Segment(write_index_, false, &storage_));
- if (!segment->Init())
- return false;
-
- segment->AddUser();
- open_segments_[write_index_] = segment.release();
- init_ = true;
- return true;
-}
-
-bool LogStore::Close() {
- DCHECK(init_ && !closed_);
- open_segments_[write_index_]->ReleaseUser();
- if (!open_segments_[write_index_]->Close())
- return false;
- closed_ = true;
- return true;
- // TODO(agayev): persist metadata to disk.
-}
-
-bool LogStore::CreateEntry(int32 size, int32* id) {
- DCHECK(init_ && !closed_);
- DCHECK(current_entry_id_ == -1 && size <= disk_cache::kFlashSegmentFreeSpace);
-
- // TODO(agayev): Avoid large entries from leaving the segments almost empty.
- if (!open_segments_[write_index_]->CanHold(size)) {
- if (!open_segments_[write_index_]->Close())
- return false;
-
- open_segments_[write_index_]->ReleaseUser();
- if (open_segments_[write_index_]->HasNoUsers()) {
- delete open_segments_[write_index_];
- open_segments_[write_index_] = NULL;
- }
-
- write_index_ = GetNextSegmentIndex();
- scoped_ptr<Segment> segment(new Segment(write_index_, false, &storage_));
- if (!segment->Init())
- return false;
-
- segment->AddUser();
- open_segments_[write_index_] = segment.release();
- }
-
- *id = open_segments_[write_index_]->write_offset();
- open_segments_[write_index_]->StoreOffset(*id);
- current_entry_id_ = *id;
- current_entry_num_bytes_left_to_write_ = size;
- open_entries_.insert(current_entry_id_);
- return true;
-}
-
-void LogStore::DeleteEntry(int32 id, int32 size) {
- DCHECK(init_ && !closed_);
- DCHECK(open_entries_.find(id) == open_entries_.end());
- // TODO(agayev): Increment the number of dead bytes in the segment metadata
- // for the segment identified by |index|.
-}
-
-bool LogStore::WriteData(const void* buffer, int32 size) {
- DCHECK(init_ && !closed_);
- DCHECK(current_entry_id_ != -1 &&
- size <= current_entry_num_bytes_left_to_write_);
- if (open_segments_[write_index_]->WriteData(buffer, size)) {
- current_entry_num_bytes_left_to_write_ -= size;
- return true;
- }
- return false;
-}
-
-bool LogStore::OpenEntry(int32 id) {
- DCHECK(init_ && !closed_);
- if (open_entries_.find(id) != open_entries_.end())
- return false;
-
- // Segment is already open.
- int32 index = id / disk_cache::kFlashSegmentSize;
- if (open_segments_[index]) {
- if (!open_segments_[index]->HaveOffset(id))
- return false;
- open_segments_[index]->AddUser();
- open_entries_.insert(id);
- return true;
- }
-
- // Segment is not open.
- scoped_ptr<Segment> segment(new Segment(index, true, &storage_));
- if (!segment->Init() || !segment->HaveOffset(id))
- return false;
-
- segment->AddUser();
- open_segments_[index] = segment.release();
- open_entries_.insert(id);
- return true;
-}
-
-bool LogStore::ReadData(int32 id, void* buffer, int32 size,
- int32 offset) const {
- DCHECK(init_ && !closed_);
- DCHECK(open_entries_.find(id) != open_entries_.end());
-
- int32 index = id / disk_cache::kFlashSegmentSize;
- DCHECK(open_segments_[index] && open_segments_[index]->HaveOffset(id));
- return open_segments_[index]->ReadData(buffer, size, id + offset);
-}
-
-void LogStore::CloseEntry(int32 id) {
- DCHECK(init_ && !closed_);
- std::set<int32>::iterator entry_iter = open_entries_.find(id);
- DCHECK(entry_iter != open_entries_.end());
-
- if (current_entry_id_ != -1) {
- DCHECK(id == current_entry_id_ && !current_entry_num_bytes_left_to_write_);
- open_entries_.erase(entry_iter);
- current_entry_id_ = -1;
- return;
- }
-
- int32 index = id / disk_cache::kFlashSegmentSize;
- DCHECK(open_segments_[index]);
- open_entries_.erase(entry_iter);
-
- open_segments_[index]->ReleaseUser();
- if (open_segments_[index]->HasNoUsers()) {
- delete open_segments_[index];
- open_segments_[index] = NULL;
- }
-}
-
-int32 LogStore::GetNextSegmentIndex() {
- DCHECK(init_ && !closed_);
- int32 next_index = (write_index_ + 1) % num_segments_;
-
- while (InUse(next_index)) {
- next_index = (next_index + 1) % num_segments_;
- DCHECK_NE(next_index, write_index_);
- }
- return next_index;
-}
-
-bool LogStore::InUse(int32 index) const {
- DCHECK(init_ && !closed_);
- DCHECK(index >= 0 && index < num_segments_);
- return open_segments_[index] != NULL;
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/log_store.h b/chromium/net/disk_cache/flash/log_store.h
deleted file mode 100644
index e53b83e1625..00000000000
--- a/chromium/net/disk_cache/flash/log_store.h
+++ /dev/null
@@ -1,101 +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_DISK_CACHE_FLASH_LOG_STORE_H_
-#define NET_DISK_CACHE_FLASH_LOG_STORE_H_
-
-#include <set>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "net/base/net_export.h"
-#include "net/disk_cache/flash/storage.h"
-
-namespace disk_cache {
-
-class Segment;
-
-// This class implements a general purpose store for storing and retrieving
-// entries consisting of arbitrary binary data. The store has log semantics,
-// i.e. it's not possible to overwrite data in place. In order to update an
-// entry, a new version must be written. Only one entry can be written to at
-// any given time, while concurrent reading of multiple entries is supported.
-class NET_EXPORT_PRIVATE LogStore {
- public:
- LogStore(const base::FilePath& path, int32 size);
- ~LogStore();
-
- // Performs initialization. Must be the first function called and further
- // calls should be made only if it is successful.
- bool Init();
-
- // Closes the store. Should be the last function called before destruction.
- bool Close();
-
- // Creates an entry of |size| bytes. The id of the created entry is stored in
- // |entry_id|.
- bool CreateEntry(int32 size, int32* entry_id);
-
- // Deletes |entry_id|; the client should keep track of |size| and provide it
- // here. Only inactive (i.e. not currently open or being created) entries can
- // be deleted.
- void DeleteEntry(int32 entry_id, int32 size);
-
- // Appends data to the end of the last created entry.
- bool WriteData(const void* buffer, int32 size);
-
- // Opens an entry with id |entry_id|.
- bool OpenEntry(int32 entry_id);
-
- // Reads |size| bytes starting from |offset| into |buffer|, where |offset| is
- // relative to the entry's content, from an entry identified by |entry_id|.
- bool ReadData(int32 entry_id, void* buffer, int32 size, int32 offset) const;
-
- // Closes an entry that was either opened with OpenEntry or created with
- // CreateEntry.
- void CloseEntry(int32 id);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(FlashCacheTest, LogStoreReadFromClosedSegment);
- FRIEND_TEST_ALL_PREFIXES(FlashCacheTest, LogStoreSegmentSelectionIsFifo);
- FRIEND_TEST_ALL_PREFIXES(FlashCacheTest, LogStoreInUseSegmentIsSkipped);
- FRIEND_TEST_ALL_PREFIXES(FlashCacheTest, LogStoreReadFromCurrentAfterClose);
-
- int32 GetNextSegmentIndex();
- bool InUse(int32 segment_index) const;
-
- Storage storage_;
-
- int32 num_segments_;
-
- // Currently open segments, either for reading or writing. There can only be
- // one segment open for writing, and multiple open for reading.
- std::vector<Segment*> open_segments_;
-
- // The index of the segment currently being written to. It's an index to
- // |open_segments_| vector.
- int32 write_index_;
-
- // Ids of entries currently open, either CreatEntry'ed or OpenEntry'ed.
- std::set<int32> open_entries_;
-
- // Id of the entry that is currently being written to, -1 if there is no entry
- // currently being written to.
- int32 current_entry_id_;
-
- // Number of bytes left to be written to the entry identified by
- // |current_entry_id_|. Its value makes sense iff |current_entry_id_| is not
- // -1.
- int32 current_entry_num_bytes_left_to_write_;
-
- bool init_; // Init was called.
- bool closed_; // Close was called.
-
- DISALLOW_COPY_AND_ASSIGN(LogStore);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_LOG_STORE_H_
diff --git a/chromium/net/disk_cache/flash/log_store_entry.cc b/chromium/net/disk_cache/flash/log_store_entry.cc
deleted file mode 100644
index 1e26ec54461..00000000000
--- a/chromium/net/disk_cache/flash/log_store_entry.cc
+++ /dev/null
@@ -1,171 +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 "base/logging.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/log_store_entry.h"
-
-namespace disk_cache {
-
-LogStoreEntry::LogStoreEntry(LogStore* store)
- : store_(store), id_(-1), init_(false), closed_(false), deleted_(false) {
- DCHECK(store);
-}
-
-LogStoreEntry::LogStoreEntry(LogStore* store, int32 id)
- : store_(store), id_(id), init_(false), closed_(false), deleted_(false) {
- DCHECK(store);
-}
-
-LogStoreEntry::~LogStoreEntry() {
- DCHECK(!init_ || closed_);
-}
-
-bool LogStoreEntry::Init() {
- DCHECK(!init_);
- if (IsNew()) {
- init_ = true;
- return true;
- }
-
- int32 stream_sizes[kFlashLogStoreEntryNumStreams];
- COMPILE_ASSERT(sizeof(stream_sizes) == kFlashLogStoreEntryHeaderSize,
- invalid_log_store_entry_header_size);
-
- if (!store_->OpenEntry(id_) ||
- !store_->ReadData(id_, stream_sizes, kFlashLogStoreEntryHeaderSize, 0)) {
- return false;
- }
- for (int i = 0, offset = kFlashLogStoreEntryHeaderSize;
- i < kFlashLogStoreEntryNumStreams; ++i) {
- streams_[i].offset = offset;
- streams_[i].size = stream_sizes[i];
- offset += stream_sizes[i];
- }
- init_ = true;
- return true;
-}
-
-bool LogStoreEntry::Close() {
- DCHECK(init_ && !closed_);
-
- if (IsNew()) {
- closed_ = deleted_ ? true : Save();
- } else {
- store_->CloseEntry(id_);
- if (deleted_)
- store_->DeleteEntry(id_, Size());
- closed_ = true;
- }
- return closed_;
-}
-
-int32 LogStoreEntry::id() const {
- DCHECK(init_);
- return id_;
-}
-
-int32 LogStoreEntry::GetDataSize(int index) const {
- DCHECK(init_);
- return InvalidStream(index) ? 0 : streams_[index].size;
-}
-
-int LogStoreEntry::ReadData(int index, int offset, net::IOBuffer* buf,
- int buf_len) {
- DCHECK(init_);
- if (InvalidStream(index))
- return net::ERR_INVALID_ARGUMENT;
-
- int stream_size = streams_[index].size;
- if (offset >= stream_size || offset < 0 || buf_len == 0)
- return 0;
- if (offset + buf_len > stream_size)
- buf_len = stream_size - offset;
-
- if (!IsNew()) {
- offset += streams_[index].offset;
- if (store_->ReadData(id_, buf->data(), buf_len, offset))
- return buf_len;
- return net::ERR_FAILED;
- }
- memcpy(buf->data(), &streams_[index].write_buffer[offset], buf_len);
- return buf_len;
-}
-
-int LogStoreEntry::WriteData(int index, int offset, net::IOBuffer* buf,
- int buf_len) {
- DCHECK(init_ && !closed_);
- if (InvalidStream(index))
- return net::ERR_INVALID_ARGUMENT;
-
- DCHECK(offset >= 0 && buf_len >= 0);
- Stream& stream = streams_[index];
- size_t new_size = static_cast<size_t>(offset + buf_len);
- if (new_size) {
- // TODO(agayev): Currently, only append and overwrite is supported. Add
- // support for arbitrary writes.
- DCHECK(!offset || offset == stream.size);
- if (stream.write_buffer.size() < new_size)
- stream.write_buffer.resize(new_size);
- memcpy(&streams_[index].write_buffer[offset], buf->data(), buf_len);
- }
- stream.size = new_size;
- return buf_len;
-}
-
-void LogStoreEntry::Delete() {
- DCHECK(init_ && !closed_);
- deleted_ = true;
-}
-
-bool LogStoreEntry::IsNew() const {
- return id_ == -1;
-}
-
-bool LogStoreEntry::InvalidStream(int stream_index) const {
- return stream_index < 0 || stream_index >= kFlashLogStoreEntryNumStreams;
-}
-
-int32 LogStoreEntry::Size() const {
- DCHECK(init_);
- int32 size = kFlashLogStoreEntryHeaderSize;
- for (int i = 0; i < kFlashLogStoreEntryNumStreams; ++i)
- size += streams_[i].size;
- DCHECK(size > 0 && size <= kFlashSegmentFreeSpace);
- return size;
-}
-
-bool LogStoreEntry::Save() {
- DCHECK(init_ && !closed_ && !deleted_ && IsNew());
- int32 stream_sizes[kFlashLogStoreEntryNumStreams];
- COMPILE_ASSERT(sizeof(stream_sizes) == kFlashLogStoreEntryHeaderSize,
- invalid_log_store_entry_header_size);
-
- for (int i = 0; i < kFlashLogStoreEntryNumStreams; ++i)
- stream_sizes[i] = streams_[i].size;
-
- if (!store_->CreateEntry(Size(), &id_))
- return false;
- if (!store_->WriteData(stream_sizes, kFlashLogStoreEntryHeaderSize))
- return false;
- for (int i = 0; i < kFlashLogStoreEntryNumStreams; ++i) {
- if (streams_[i].size > 0 &&
- !store_->WriteData(&streams_[i].write_buffer[0], streams_[i].size)) {
- return false;
- }
- }
- store_->CloseEntry(id_);
- return true;
-}
-
-LogStoreEntry::Stream::Stream() : offset(0), size(0) {
-}
-
-LogStoreEntry::Stream::~Stream() {
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/log_store_entry.h b/chromium/net/disk_cache/flash/log_store_entry.h
deleted file mode 100644
index 579194913af..00000000000
--- a/chromium/net/disk_cache/flash/log_store_entry.h
+++ /dev/null
@@ -1,65 +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_DISK_CACHE_FLASH_LOG_STORE_ENTRY_H_
-#define NET_DISK_CACHE_FLASH_LOG_STORE_ENTRY_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "net/base/net_export.h"
-#include "net/disk_cache/flash/format.h"
-
-namespace net {
-class IOBuffer;
-};
-
-namespace disk_cache {
-
-class LogStore;
-
-class NET_EXPORT_PRIVATE LogStoreEntry {
- public:
- explicit LogStoreEntry(LogStore* store);
- LogStoreEntry(LogStore* store, int32 id);
- ~LogStoreEntry();
-
- bool Init();
- bool Close();
-
- int32 id() const;
- bool IsNew() const;
- int32 GetDataSize(int index) const;
-
- int ReadData(int index, int offset, net::IOBuffer* buf, int buf_len);
- int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len);
- void Delete();
-
- private:
- struct Stream {
- Stream();
- ~Stream();
- int offset;
- int size;
- std::vector<char> write_buffer;
- };
-
- bool InvalidStream(int stream_index) const;
- int32 Size() const;
- bool Save();
-
- LogStore* store_;
- int32 id_;
- Stream streams_[kFlashLogStoreEntryNumStreams];
- bool init_;
- bool closed_;
- bool deleted_;
-
- DISALLOW_COPY_AND_ASSIGN(LogStoreEntry);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_LOG_STORE_ENTRY_H_
diff --git a/chromium/net/disk_cache/flash/log_store_entry_unittest.cc b/chromium/net/disk_cache/flash/log_store_entry_unittest.cc
deleted file mode 100644
index 100c9a83ea0..00000000000
--- a/chromium/net/disk_cache/flash/log_store_entry_unittest.cc
+++ /dev/null
@@ -1,69 +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 "base/memory/scoped_ptr.h"
-#include "net/base/io_buffer.h"
-#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/flash/flash_cache_test_base.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/log_store_entry.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using disk_cache::LogStoreEntry;
-
-// Tests the behavior of a LogStoreEntry with empty streams.
-TEST_F(FlashCacheTest, LogStoreEntryEmpty) {
- disk_cache::LogStore log_store(path_, kStorageSize);
- ASSERT_TRUE(log_store.Init());
-
- scoped_ptr<LogStoreEntry> entry(new LogStoreEntry(&log_store));
- EXPECT_TRUE(entry->Init());
- EXPECT_TRUE(entry->Close());
-
- entry.reset(new LogStoreEntry(&log_store, entry->id()));
- EXPECT_TRUE(entry->Init());
-
- for (int i = 0; i < disk_cache::kFlashLogStoreEntryNumStreams; ++i) {
- const int kSize = 1024;
- scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kSize));
- EXPECT_EQ(0, entry->GetDataSize(i));
- EXPECT_EQ(0, entry->ReadData(i, 0, buf.get(), kSize));
- }
- EXPECT_TRUE(entry->Close());
- ASSERT_TRUE(log_store.Close());
-}
-
-TEST_F(FlashCacheTest, LogStoreEntryWriteRead) {
- disk_cache::LogStore log_store(path_, kStorageSize);
- ASSERT_TRUE(log_store.Init());
-
- scoped_ptr<LogStoreEntry> entry(new LogStoreEntry(&log_store));
- EXPECT_TRUE(entry->Init());
-
- int sizes[disk_cache::kFlashLogStoreEntryNumStreams] = {333, 444, 555, 666};
- scoped_refptr<net::IOBuffer> buffers[
- disk_cache::kFlashLogStoreEntryNumStreams];
-
- for (int i = 0; i < disk_cache::kFlashLogStoreEntryNumStreams; ++i) {
- buffers[i] = new net::IOBuffer(sizes[i]);
- CacheTestFillBuffer(buffers[i]->data(), sizes[i], false);
- EXPECT_EQ(sizes[i], entry->WriteData(i, 0, buffers[i].get(), sizes[i]));
- }
- EXPECT_TRUE(entry->Close());
-
- int32 id = entry->id();
- entry.reset(new LogStoreEntry(&log_store, id));
- EXPECT_TRUE(entry->Init());
-
- for (int i = 0; i < disk_cache::kFlashLogStoreEntryNumStreams; ++i) {
- EXPECT_EQ(sizes[i], entry->GetDataSize(i));
- scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(sizes[i]));
- EXPECT_EQ(sizes[i], entry->ReadData(i, 0, buffer.get(), sizes[i]));
- EXPECT_EQ(0, memcmp(buffers[i]->data(), buffer->data(), sizes[i]));
- }
- EXPECT_TRUE(entry->Close());
- EXPECT_EQ(id, entry->id());
- ASSERT_TRUE(log_store.Close());
-}
diff --git a/chromium/net/disk_cache/flash/log_store_unittest.cc b/chromium/net/disk_cache/flash/log_store_unittest.cc
deleted file mode 100644
index 2678316d499..00000000000
--- a/chromium/net/disk_cache/flash/log_store_unittest.cc
+++ /dev/null
@@ -1,131 +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/disk_cache/flash/flash_cache_test_base.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/log_store.h"
-#include "net/disk_cache/flash/segment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace disk_cache {
-
-TEST_F(FlashCacheTest, LogStoreCreateEntry) {
- LogStore log_store(path_, kStorageSize);
- EXPECT_TRUE(log_store.Init());
-
- const int32 kSize = 100;
- const std::string buf(kSize, 0);
-
- int32 id;
- EXPECT_TRUE(log_store.CreateEntry(kSize, &id));
- EXPECT_TRUE(log_store.WriteData(buf.data(), kSize/2));
- EXPECT_TRUE(log_store.WriteData(buf.data(), kSize/2));
- log_store.CloseEntry(id);
-
- EXPECT_TRUE(log_store.Close());
-}
-
-// Also tests reading from current segment.
-TEST_F(FlashCacheTest, LogStoreOpenEntry) {
- LogStore log_store(path_, kStorageSize);
- EXPECT_TRUE(log_store.Init());
-
- const int32 kSize = 100;
- const std::vector<char> expected(kSize, 'b');
-
- int32 id;
- EXPECT_TRUE(log_store.CreateEntry(kSize, &id));
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize));
- log_store.CloseEntry(id);
-
- EXPECT_TRUE(log_store.OpenEntry(id));
- std::vector<char> actual(kSize, 0);
- EXPECT_TRUE(log_store.ReadData(id, &actual[0], kSize, 0));
- log_store.CloseEntry(id);
-
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(log_store.Close());
-}
-
-// Also tests that writing advances segments.
-TEST_F(FlashCacheTest, LogStoreReadFromClosedSegment) {
- LogStore log_store(path_, kStorageSize);
- EXPECT_TRUE(log_store.Init());
-
- const int32 kSize = disk_cache::kFlashSegmentFreeSpace;
- const std::vector<char> expected(kSize, 'a');
-
- // First two entries go to segment 0.
- int32 id1;
- EXPECT_EQ(0, log_store.write_index_);
- EXPECT_TRUE(log_store.CreateEntry(kSize/2, &id1));
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize/2));
- log_store.CloseEntry(id1);
-
- int32 id2;
- EXPECT_EQ(0, log_store.write_index_);
- EXPECT_TRUE(log_store.CreateEntry(kSize/2, &id2));
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize/2));
- log_store.CloseEntry(id2);
-
- // This entry goes to segment 1.
- int32 id3;
- EXPECT_TRUE(log_store.CreateEntry(kSize, &id3));
- EXPECT_EQ(1, log_store.write_index_);
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize));
- log_store.CloseEntry(id3);
-
- // We read from segment 0.
- EXPECT_TRUE(log_store.OpenEntry(id1));
- std::vector<char> actual(kSize, 0);
- EXPECT_TRUE(log_store.ReadData(id1, &actual[0], kSize, id1));
- log_store.CloseEntry(id1);
-
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(log_store.Close());
-}
-
-TEST_F(FlashCacheTest, LogStoreReadFromCurrentAfterClose) {
- LogStore log_store(path_, kStorageSize);
- EXPECT_TRUE(log_store.Init());
-
- const int32 kSize = disk_cache::kFlashSegmentFreeSpace;
- const std::vector<char> expected(kSize, 'a');
-
- int32 id1;
- EXPECT_EQ(0, log_store.write_index_);
- EXPECT_TRUE(log_store.CreateEntry(kSize/2, &id1));
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize/2));
- log_store.CloseEntry(id1);
-
- // Create a reference to above entry.
- EXPECT_TRUE(log_store.OpenEntry(id1));
-
- // This entry fills the first segment.
- int32 id2;
- EXPECT_EQ(0, log_store.write_index_);
- EXPECT_TRUE(log_store.CreateEntry(kSize/2, &id2));
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize/2));
- log_store.CloseEntry(id2);
-
- // Creating this entry forces closing of the first segment.
- int32 id3;
- EXPECT_TRUE(log_store.CreateEntry(kSize, &id3));
- EXPECT_EQ(1, log_store.write_index_);
- EXPECT_TRUE(log_store.WriteData(&expected[0], kSize));
- log_store.CloseEntry(id3);
-
- // Now attempt to read from the closed segment.
- std::vector<char> actual(kSize, 0);
- EXPECT_TRUE(log_store.ReadData(id1, &actual[0], kSize, id1));
- log_store.CloseEntry(id1);
-
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(log_store.Close());
-}
-
-// TODO(agayev): Add a test that confirms that in-use segment is not selected as
-// the next write segment.
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/segment.cc b/chromium/net/disk_cache/flash/segment.cc
deleted file mode 100644
index 3457497a22f..00000000000
--- a/chromium/net/disk_cache/flash/segment.cc
+++ /dev/null
@@ -1,122 +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 <algorithm>
-
-#include "base/logging.h"
-#include "net/disk_cache/flash/format.h"
-#include "net/disk_cache/flash/segment.h"
-#include "net/disk_cache/flash/storage.h"
-
-namespace disk_cache {
-
-Segment::Segment(int32 index, bool read_only, Storage* storage)
- : index_(index),
- num_users_(0),
- read_only_(read_only),
- init_(false),
- storage_(storage),
- offset_(index * kFlashSegmentSize),
- summary_offset_(offset_ + kFlashSegmentSize - kFlashSummarySize),
- write_offset_(offset_) {
- DCHECK(storage);
- DCHECK(storage->size() % kFlashSegmentSize == 0);
-}
-
-Segment::~Segment() {
- DCHECK(!init_ || read_only_);
- if (num_users_ != 0)
- LOG(WARNING) << "Users exist, but we don't care? " << num_users_;
-}
-
-bool Segment::HaveOffset(int32 offset) const {
- DCHECK(init_);
- return std::binary_search(offsets_.begin(), offsets_.end(), offset);
-}
-
-void Segment::AddUser() {
- DCHECK(init_);
- ++num_users_;
-}
-
-void Segment::ReleaseUser() {
- DCHECK(init_);
- --num_users_;
-}
-
-bool Segment::HasNoUsers() const {
- DCHECK(init_);
- return num_users_ == 0;
-}
-
-bool Segment::Init() {
- DCHECK(!init_);
-
- if (offset_ < 0 || offset_ + kFlashSegmentSize > storage_->size())
- return false;
-
- if (!read_only_) {
- init_ = true;
- return true;
- }
-
- int32 summary[kFlashMaxEntryCount + 1];
- if (!storage_->Read(summary, kFlashSummarySize, summary_offset_))
- return false;
-
- size_t entry_count = summary[0];
- DCHECK_LE(entry_count, kFlashMaxEntryCount);
-
- std::vector<int32> tmp(summary + 1, summary + 1 + entry_count);
- offsets_.swap(tmp);
- init_ = true;
- return true;
-}
-
-bool Segment::WriteData(const void* buffer, int32 size) {
- DCHECK(init_ && !read_only_);
- DCHECK(write_offset_ + size <= summary_offset_);
- if (!storage_->Write(buffer, size, write_offset_))
- return false;
- write_offset_ += size;
- return true;
-}
-
-void Segment::StoreOffset(int32 offset) {
- DCHECK(init_ && !read_only_);
- DCHECK(offsets_.size() < kFlashMaxEntryCount);
- offsets_.push_back(offset);
-}
-
-bool Segment::ReadData(void* buffer, int32 size, int32 offset) const {
- DCHECK(init_);
- DCHECK(offset >= offset_ && offset + size <= offset_ + kFlashSegmentSize);
- return storage_->Read(buffer, size, offset);
-}
-
-bool Segment::Close() {
- DCHECK(init_);
- if (read_only_)
- return true;
-
- DCHECK(offsets_.size() <= kFlashMaxEntryCount);
-
- int32 summary[kFlashMaxEntryCount + 1];
- memset(summary, 0, kFlashSummarySize);
- summary[0] = offsets_.size();
- std::copy(offsets_.begin(), offsets_.end(), summary + 1);
- if (!storage_->Write(summary, kFlashSummarySize, summary_offset_))
- return false;
-
- read_only_ = true;
- return true;
-}
-
-bool Segment::CanHold(int32 size) const {
- DCHECK(init_);
- return offsets_.size() < kFlashMaxEntryCount &&
- write_offset_ + size <= summary_offset_;
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/segment.h b/chromium/net/disk_cache/flash/segment.h
deleted file mode 100644
index 97551e2531d..00000000000
--- a/chromium/net/disk_cache/flash/segment.h
+++ /dev/null
@@ -1,118 +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_DISK_CACHE_FLASH_SEGMENT_H_
-#define NET_DISK_CACHE_FLASH_SEGMENT_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "net/base/net_export.h"
-
-namespace disk_cache {
-
-class Storage;
-
-// The underlying storage represented by Storage class, is divided into fixed
-// size logical segments, represented by this class. Since segment size is
-// fixed, the storage size should be a multiple of segment size. The picture
-// below describes the relation between storage and segments:
-//
-// |-----------+-----------+-----+-------------+-----------|
-// | segment 0 | segment 1 | ... | segment n-1 | segment n |
-// |-----------+-----------+-----+-------------+-----------|
-//
-// |-------------------------------------------------------|
-// | storage |
-// |-------------------------------------------------------|
-//
-// A segment is constructed by taking its index within the storage, a flag
-// indicating whether it is a read-only segment and a pointer to the storage on
-// which it resides. It provides an API for reading/writing entries residing on
-// it. Init() function must be called right after the construction of a segment
-// and one should proceed to calling other functions only if Init() has
-// succeeded. After a successful initialization, one may proceed to call
-// non-mutating functions; mutating functions can be called if the segment is
-// not read-only. Finally, Close() function must be called right before the
-// destruction. Calling Close() makes the segment immutable, which means
-// mutating functions cannot be called on the object after that.
-//
-// Segment can only be used as a log, i.e. all writes are laid out sequentially
-// on a segment. As a result, WriteData() function does not take an offset.
-// Current write offset can be learned by calling write_offset().
-//
-// Once the entries are written to the Segment and Close() called on it and the
-// object destroyed, we should later be able to instantiate a read-only Segment
-// object and recreate all the entries that were previously written to it. To
-// achieve this, a tiny region of Segment is used for its metadata and Segment
-// provides two calls for interacting with metadata: StoreOffset() and
-// GetOffsets(). The former can be used to store an offset that was returned by
-// write_offset() and the latter can be used to retrieve all the offsets that
-// were stored in the Segment. Before attempting to write an entry, the client
-// should call CanHold() to make sure that there is enough space in the segment.
-//
-// ReadData can be called over the range that was previously written with
-// WriteData. Reading from area that was not written will fail.
-
-class NET_EXPORT_PRIVATE Segment {
- public:
- // |index| is the index of this segment on |storage|. If the storage size is
- // X and the segment size is Y, where X >> Y and X % Y == 0, then the valid
- // values for the index are integers within the range [0, X/Y). Thus, if
- // |index| is given value Z, then it covers bytes on storage starting at the
- // offset Z*Y and ending at the offset Z*Y+Y-1.
- Segment(int32 index, bool read_only, Storage* storage);
- ~Segment();
-
- int32 index() const { return index_; }
- int32 write_offset() const { return write_offset_; }
-
- bool HaveOffset(int32 offset) const;
- std::vector<int32> GetOffsets() const { return offsets_; }
-
- // Manage the number of users of this segment.
- void AddUser();
- void ReleaseUser();
- bool HasNoUsers() const;
-
- // Performs segment initialization. Must be the first function called on the
- // segment and further calls should be made only if it is successful.
- bool Init();
-
- // Writes |size| bytes of data from |buffer| to segment, returns false if
- // fails and true if succeeds. Can block for a long time.
- bool WriteData(const void* buffer, int32 size);
-
- // Reads |size| bytes of data living at |offset| into |buffer| returns true on
- // success and false on failure.
- bool ReadData(void* buffer, int32 size, int32 offset) const;
-
- // Stores the offset in the metadata.
- void StoreOffset(int32 offset);
-
- // Closes the segment, returns true on success and false on failure. Closing
- // a segment makes it immutable.
- bool Close();
-
- // Returns true if segment can accommodate an entry of |size| bytes.
- bool CanHold(int32 size) const;
-
- private:
- int32 index_;
- int32 num_users_;
- bool read_only_; // Indicates whether the segment can be written to.
- bool init_; // Indicates whether segment was initialized.
- Storage* storage_; // Storage on which the segment resides.
- const int32 offset_; // Offset of the segment on |storage_|.
- const int32 summary_offset_; // Offset of the segment summary.
- int32 write_offset_; // Current write offset.
- std::vector<int32> offsets_;
-
- DISALLOW_COPY_AND_ASSIGN(Segment);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_SEGMENT_H_
diff --git a/chromium/net/disk_cache/flash/segment_unittest.cc b/chromium/net/disk_cache/flash/segment_unittest.cc
deleted file mode 100644
index 3f61701cbe0..00000000000
--- a/chromium/net/disk_cache/flash/segment_unittest.cc
+++ /dev/null
@@ -1,152 +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 "base/memory/scoped_ptr.h"
-#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/flash/segment.h"
-#include "net/disk_cache/flash/storage.h"
-#include "net/disk_cache/flash/flash_cache_test_base.h"
-#include "net/disk_cache/flash/format.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-template<int SIZE>
-struct Entry {
- enum { size = SIZE };
-
- Entry() { CacheTestFillBuffer(data, size, false); }
-
- bool operator==(const Entry& rhs) const {
- return std::equal(data, data + size, rhs.data);
- }
-
- char data[size];
-};
-
-const int32 kSmallEntrySize = 100;
-const int32 kLargeEntrySize = disk_cache::kFlashSegmentSize / 4;
-
-typedef Entry<kSmallEntrySize> SmallEntry;
-typedef Entry<kLargeEntrySize> LargeEntry;
-
-const int32 kSegmentFreeSpace = disk_cache::kFlashSegmentSize -
- disk_cache::kFlashSummarySize;
-
-} // namespace
-
-TEST_F(FlashCacheTest, SegmentUserTracking) {
- disk_cache::Storage storage(path_, kStorageSize);
- ASSERT_TRUE(storage.Init());
-
- scoped_ptr<disk_cache::Segment> segment(
- new disk_cache::Segment(0, false, &storage));
- EXPECT_TRUE(segment->Init());
-
- EXPECT_TRUE(segment->HasNoUsers());
- segment->AddUser();
- segment->AddUser();
- EXPECT_FALSE(segment->HasNoUsers());
-
- segment->ReleaseUser();
- EXPECT_FALSE(segment->HasNoUsers());
- segment->ReleaseUser();
- EXPECT_TRUE(segment->HasNoUsers());
-
- EXPECT_TRUE(segment->Close());
-}
-
-TEST_F(FlashCacheTest, SegmentCreateDestroy) {
- disk_cache::Storage storage(path_, kStorageSize);
- ASSERT_TRUE(storage.Init());
-
- int32 index = 0;
- scoped_ptr<disk_cache::Segment> segment(
- new disk_cache::Segment(index, false, &storage));
- EXPECT_TRUE(segment->Init());
- EXPECT_TRUE(segment->Close());
-
- index = kNumTestSegments - 1;
- segment.reset(new disk_cache::Segment(index, false, &storage));
- EXPECT_TRUE(segment->Init());
- EXPECT_TRUE(segment->Close());
-
- int32 invalid_index = kNumTestSegments;
- segment.reset(new disk_cache::Segment(invalid_index, false, &storage));
- EXPECT_FALSE(segment->Init());
-
- invalid_index = -1;
- segment.reset(new disk_cache::Segment(invalid_index, false, &storage));
- EXPECT_FALSE(segment->Init());
-}
-
-TEST_F(FlashCacheTest, SegmentWriteDataReadData) {
- disk_cache::Storage storage(path_, kStorageSize);
- ASSERT_TRUE(storage.Init());
-
- int32 index = rand() % kNumTestSegments;
- scoped_ptr<disk_cache::Segment> segment(
- new disk_cache::Segment(index, false, &storage));
-
- EXPECT_TRUE(segment->Init());
- SmallEntry entry1;
- EXPECT_TRUE(segment->CanHold(entry1.size));
- int32 offset = segment->write_offset();
- EXPECT_TRUE(segment->WriteData(entry1.data, entry1.size));
- segment->StoreOffset(offset);
- EXPECT_TRUE(segment->Close());
-
- segment.reset(new disk_cache::Segment(index, true, &storage));
- EXPECT_TRUE(segment->Init());
- SmallEntry entry2;
- EXPECT_TRUE(segment->ReadData(entry2.data, entry2.size, offset));
- EXPECT_EQ(entry1, entry2);
- EXPECT_TRUE(segment->Close());
-}
-
-TEST_F(FlashCacheTest, SegmentFillWithSmallEntries) {
- disk_cache::Storage storage(path_, kStorageSize);
- ASSERT_TRUE(storage.Init());
-
- int32 index = rand() % kNumTestSegments;
- scoped_ptr<disk_cache::Segment> segment(
- new disk_cache::Segment(index, false, &storage));
-
- EXPECT_TRUE(segment->Init());
- SmallEntry entry;
- int32 num_bytes_written = 0;
- while (segment->CanHold(entry.size)) {
- int32 offset = segment->write_offset();
- EXPECT_TRUE(segment->WriteData(entry.data, entry.size));
- segment->StoreOffset(offset);
- num_bytes_written += entry.size;
- }
- int32 space_left = kSegmentFreeSpace - num_bytes_written;
- EXPECT_GE(space_left, entry.size);
- EXPECT_EQ(segment->GetOffsets().size(), disk_cache::kFlashMaxEntryCount);
- EXPECT_TRUE(segment->Close());
-}
-
-TEST_F(FlashCacheTest, SegmentFillWithLargeEntries) {
- disk_cache::Storage storage(path_, kStorageSize);
- ASSERT_TRUE(storage.Init());
-
- int32 index = rand() % kNumTestSegments;
- scoped_ptr<disk_cache::Segment> segment(
- new disk_cache::Segment(index, false, &storage));
-
- EXPECT_TRUE(segment->Init());
- scoped_ptr<LargeEntry> entry(new LargeEntry);
- int32 num_bytes_written = 0;
- while (segment->CanHold(entry->size)) {
- int32 offset = segment->write_offset();
- EXPECT_TRUE(segment->WriteData(entry->data, entry->size));
- segment->StoreOffset(offset);
- num_bytes_written += entry->size;
- }
- int32 space_left = kSegmentFreeSpace - num_bytes_written;
- EXPECT_LT(space_left, entry->size);
- EXPECT_LT(segment->GetOffsets().size(), disk_cache::kFlashMaxEntryCount);
- EXPECT_TRUE(segment->Close());
-}
diff --git a/chromium/net/disk_cache/flash/storage.cc b/chromium/net/disk_cache/flash/storage.cc
deleted file mode 100644
index c7136ef869b..00000000000
--- a/chromium/net/disk_cache/flash/storage.cc
+++ /dev/null
@@ -1,63 +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/disk_cache/flash/storage.h"
-
-#include <fcntl.h>
-
-#include "base/logging.h"
-#include "base/platform_file.h"
-#include "net/base/net_errors.h"
-#include "net/disk_cache/flash/format.h"
-
-namespace disk_cache {
-
-Storage::Storage(const base::FilePath& path,
- int32 size)
- : path_(path), size_(size) {
- COMPILE_ASSERT(kFlashPageSize % 2 == 0, invalid_page_size);
- COMPILE_ASSERT(kFlashBlockSize % kFlashPageSize == 0, invalid_block_size);
- DCHECK(size_ % kFlashBlockSize == 0);
-}
-
-bool Storage::Init() {
- int flags = base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_OPEN_ALWAYS;
-
- file_ = base::CreatePlatformFile(path_, flags, NULL, NULL);
- if (file_ == base::kInvalidPlatformFileValue)
- return false;
-
- // TODO(agayev): if file already exists, do some validation and return either
- // true or false based on the result.
-
-#if defined(OS_LINUX)
- fallocate(file_, 0, 0, size_);
-#endif
-
- return true;
-}
-
-Storage::~Storage() {
- base::ClosePlatformFile(file_);
-}
-
-bool Storage::Read(void* buffer, int32 size, int32 offset) {
- DCHECK(offset >= 0 && offset + size <= size_);
-
- int rv = base::ReadPlatformFile(file_, offset, static_cast<char*>(buffer),
- size);
- return rv == size;
-}
-
-bool Storage::Write(const void* buffer, int32 size, int32 offset) {
- DCHECK(offset >= 0 && offset + size <= size_);
-
- int rv = base::WritePlatformFile(file_, offset,
- static_cast<const char*>(buffer), size);
- return rv == size;
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/flash/storage.h b/chromium/net/disk_cache/flash/storage.h
deleted file mode 100644
index 38c4a4e488b..00000000000
--- a/chromium/net/disk_cache/flash/storage.h
+++ /dev/null
@@ -1,35 +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_DISK_CACHE_FLASH_STORAGE_H_
-#define NET_DISK_CACHE_FLASH_STORAGE_H_
-
-#include "base/basictypes.h"
-#include "base/platform_file.h"
-#include "net/base/net_export.h"
-
-namespace disk_cache {
-
-class NET_EXPORT_PRIVATE Storage {
- public:
- Storage(const base::FilePath& path, int32 size);
- bool Init();
- ~Storage();
-
- int32 size() const { return size_; }
-
- bool Read(void* buffer, int32 size, int32 offset);
- bool Write(const void* buffer, int32 size, int32 offset);
-
- private:
- base::FilePath path_;
- int32 size_;
- base::PlatformFile file_;
-
- DISALLOW_COPY_AND_ASSIGN(Storage);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_FLASH_STORAGE_H_
diff --git a/chromium/net/disk_cache/flash/storage_unittest.cc b/chromium/net/disk_cache/flash/storage_unittest.cc
deleted file mode 100644
index e8a1e3f18c8..00000000000
--- a/chromium/net/disk_cache/flash/storage_unittest.cc
+++ /dev/null
@@ -1,41 +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 "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "net/base/io_buffer.h"
-#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/flash/flash_cache_test_base.h"
-#include "net/disk_cache/flash/storage.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const int32 kSizes[] = {512, 1024, 4096, 133, 1333, 13333};
-const int32 kOffsets[] = {0, 1, 3333, 125, 12443, 4431};
-
-} // namespace
-
-TEST_F(FlashCacheTest, StorageReadWrite) {
- disk_cache::Storage storage(path_, kStorageSize);
- EXPECT_TRUE(storage.Init());
-
- for (size_t i = 0; i < arraysize(kOffsets); ++i) {
- int32 size = kSizes[i];
- int32 offset = kOffsets[i];
-
- scoped_refptr<net::IOBuffer> write_buffer(new net::IOBuffer(size));
- scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(size));
-
- CacheTestFillBuffer(write_buffer->data(), size, false);
-
- bool rv = storage.Write(write_buffer->data(), size, offset);
- EXPECT_TRUE(rv);
-
- rv = storage.Read(read_buffer->data(), size, offset);
- EXPECT_TRUE(rv);
-
- EXPECT_EQ(0, memcmp(read_buffer->data(), write_buffer->data(), size));
- }
-}
diff --git a/chromium/net/disk_cache/mem_backend_impl.cc b/chromium/net/disk_cache/memory/mem_backend_impl.cc
index ccd868b6530..e69c00edf63 100644
--- a/chromium/net/disk_cache/mem_backend_impl.cc
+++ b/chromium/net/disk_cache/memory/mem_backend_impl.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/memory/mem_backend_impl.h"
#include "base/logging.h"
#include "base/sys_info.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/mem_entry_impl.h"
+#include "net/disk_cache/memory/mem_entry_impl.h"
using base::Time;
diff --git a/chromium/net/disk_cache/mem_backend_impl.h b/chromium/net/disk_cache/memory/mem_backend_impl.h
index 8da39cc7f26..5f31be54da9 100644
--- a/chromium/net/disk_cache/mem_backend_impl.h
+++ b/chromium/net/disk_cache/memory/mem_backend_impl.h
@@ -4,13 +4,13 @@
// See net/disk_cache/disk_cache.h for the public interface of the cache.
-#ifndef NET_DISK_CACHE_MEM_BACKEND_IMPL_H__
-#define NET_DISK_CACHE_MEM_BACKEND_IMPL_H__
+#ifndef NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_
+#define NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_
#include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/mem_rankings.h"
+#include "net/disk_cache/memory/mem_rankings.h"
namespace net {
class NetLog;
@@ -117,4 +117,4 @@ class NET_EXPORT_PRIVATE MemBackendImpl : public Backend {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_MEM_BACKEND_IMPL_H__
+#endif // NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_
diff --git a/chromium/net/disk_cache/mem_entry_impl.cc b/chromium/net/disk_cache/memory/mem_entry_impl.cc
index 7d0095898b4..7c6199b951b 100644
--- a/chromium/net/disk_cache/mem_entry_impl.cc
+++ b/chromium/net/disk_cache/memory/mem_entry_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mem_entry_impl.h"
+#include "net/disk_cache/memory/mem_entry_impl.h"
#include "base/bind.h"
#include "base/logging.h"
@@ -10,7 +10,7 @@
#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/net_log_parameters.h"
using base::Time;
@@ -185,7 +185,7 @@ int32 MemEntryImpl::GetDataSize(int index) const {
int MemEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, false));
@@ -193,7 +193,7 @@ int MemEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalReadData(index, offset, buf, buf_len);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_READ_DATA,
CreateNetLogReadWriteCompleteCallback(result));
@@ -203,7 +203,7 @@ int MemEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
int MemEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback, bool truncate) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate));
@@ -211,7 +211,7 @@ int MemEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
int result = InternalWriteData(index, offset, buf, buf_len, truncate);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_ENTRY_WRITE_DATA,
CreateNetLogReadWriteCompleteCallback(result));
@@ -221,39 +221,39 @@ int MemEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
int MemEntryImpl::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_SPARSE_READ,
CreateNetLogSparseOperationCallback(offset, buf_len));
}
int result = InternalReadSparseData(offset, buf, buf_len);
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.EndEvent(net::NetLog::TYPE_SPARSE_READ);
return result;
}
int MemEntryImpl::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_SPARSE_WRITE,
CreateNetLogSparseOperationCallback(offset, buf_len));
}
int result = InternalWriteSparseData(offset, buf, buf_len);
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.EndEvent(net::NetLog::TYPE_SPARSE_WRITE);
return result;
}
int MemEntryImpl::GetAvailableRange(int64 offset, int len, int64* start,
const CompletionCallback& callback) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_SPARSE_GET_RANGE,
CreateNetLogSparseOperationCallback(offset, len));
}
int result = GetAvailableRange(offset, len, start);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEvent(
net::NetLog::TYPE_SPARSE_GET_RANGE,
CreateNetLogGetAvailableRangeResultCallback(*start, result));
@@ -373,7 +373,7 @@ int MemEntryImpl::InternalReadSparseData(int64 offset, IOBuffer* buf,
// we should stop.
if (child_offset < child->child_first_pos_)
break;
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_SPARSE_READ_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child->net_log().source(),
@@ -381,7 +381,7 @@ int MemEntryImpl::InternalReadSparseData(int64 offset, IOBuffer* buf,
}
int ret = child->ReadData(kSparseData, child_offset, io_buf.get(),
io_buf->BytesRemaining(), CompletionCallback());
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(
net::NetLog::TYPE_SPARSE_READ_CHILD_DATA, ret);
}
@@ -430,7 +430,7 @@ int MemEntryImpl::InternalWriteSparseData(int64 offset, IOBuffer* buf,
// Keep a record of the last byte position (exclusive) in the child.
int data_size = child->GetDataSize(kSparseData);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.BeginEvent(
net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child->net_log().source(),
@@ -443,7 +443,7 @@ int MemEntryImpl::InternalWriteSparseData(int64 offset, IOBuffer* buf,
// continuous we may want to discard this write.
int ret = child->WriteData(kSparseData, child_offset, io_buf.get(),
write_len, CompletionCallback(), true);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(
net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA, ret);
}
diff --git a/chromium/net/disk_cache/mem_entry_impl.h b/chromium/net/disk_cache/memory/mem_entry_impl.h
index b84cc39ef22..aec8d2237b5 100644
--- a/chromium/net/disk_cache/mem_entry_impl.h
+++ b/chromium/net/disk_cache/memory/mem_entry_impl.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_MEM_ENTRY_IMPL_H_
-#define NET_DISK_CACHE_MEM_ENTRY_IMPL_H_
+#ifndef NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_
+#define NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_
#include "base/containers/hash_tables.h"
#include "base/gtest_prod_util.h"
@@ -182,4 +182,4 @@ class MemEntryImpl : public Entry {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_MEM_ENTRY_IMPL_H_
+#endif // NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_
diff --git a/chromium/net/disk_cache/mem_rankings.cc b/chromium/net/disk_cache/memory/mem_rankings.cc
index d5f4a6536a7..ba5e00b9938 100644
--- a/chromium/net/disk_cache/mem_rankings.cc
+++ b/chromium/net/disk_cache/memory/mem_rankings.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/mem_rankings.h"
+#include "net/disk_cache/memory/mem_rankings.h"
#include "base/logging.h"
-#include "net/disk_cache/mem_entry_impl.h"
+#include "net/disk_cache/memory/mem_entry_impl.h"
namespace disk_cache {
diff --git a/chromium/net/disk_cache/mem_rankings.h b/chromium/net/disk_cache/memory/mem_rankings.h
index fa906888639..b75bfc14fb9 100644
--- a/chromium/net/disk_cache/mem_rankings.h
+++ b/chromium/net/disk_cache/memory/mem_rankings.h
@@ -4,8 +4,8 @@
// See net/disk_cache/disk_cache.h for the public interface.
-#ifndef NET_DISK_CACHE_MEM_RANKINGS_H__
-#define NET_DISK_CACHE_MEM_RANKINGS_H__
+#ifndef NET_DISK_CACHE_MEMORY_MEM_RANKINGS_H_
+#define NET_DISK_CACHE_MEMORY_MEM_RANKINGS_H_
#include "base/basictypes.h"
@@ -41,4 +41,4 @@ class MemRankings {
} // namespace disk_cache
-#endif // NET_DISK_CACHE_MEM_RANKINGS_H__
+#endif // NET_DISK_CACHE_MEMORY_MEM_RANKINGS_H_
diff --git a/chromium/net/disk_cache/simple/simple_backend_impl.cc b/chromium/net/disk_cache/simple/simple_backend_impl.cc
index 7260857e0f1..5e8c3f2ffbf 100644
--- a/chromium/net/disk_cache/simple/simple_backend_impl.cc
+++ b/chromium/net/disk_cache/simple/simple_backend_impl.cc
@@ -565,13 +565,13 @@ int SimpleBackendImpl::DoomEntryFromHash(uint64 entry_hash,
Entry** entry = new Entry*();
scoped_ptr<Entry*> scoped_entry(entry);
- base::hash_map<uint64, std::vector<Closure> >::iterator it =
+ base::hash_map<uint64, std::vector<Closure> >::iterator pending_it =
entries_pending_doom_.find(entry_hash);
- if (it != entries_pending_doom_.end()) {
+ if (pending_it != entries_pending_doom_.end()) {
Callback<int(const net::CompletionCallback&)> operation =
base::Bind(&SimpleBackendImpl::DoomEntryFromHash,
base::Unretained(this), entry_hash);
- it->second.push_back(base::Bind(&RunOperationAndCallback,
+ pending_it->second.push_back(base::Bind(&RunOperationAndCallback,
operation, callback));
return net::ERR_IO_PENDING;
}
diff --git a/chromium/net/disk_cache/simple/simple_entry_impl.cc b/chromium/net/disk_cache/simple/simple_entry_impl.cc
index 45ec1f9fd1e..cc2a1ea032d 100644
--- a/chromium/net/disk_cache/simple/simple_entry_impl.cc
+++ b/chromium/net/disk_cache/simple/simple_entry_impl.cc
@@ -338,7 +338,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
const CompletionCallback& callback) {
DCHECK(io_thread_checker_.CalledOnValidThread());
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_CALL,
CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len,
false));
@@ -346,7 +346,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
buf_len < 0) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
CreateNetLogReadWriteCompleteCallback(net::ERR_INVALID_ARGUMENT));
}
@@ -356,7 +356,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
}
if (pending_operations_.empty() && (offset >= GetDataSize(stream_index) ||
offset < 0 || !buf_len)) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
CreateNetLogReadWriteCompleteCallback(0));
}
@@ -385,7 +385,7 @@ int SimpleEntryImpl::WriteData(int stream_index,
bool truncate) {
DCHECK(io_thread_checker_.CalledOnValidThread());
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_CALL,
CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len,
@@ -394,7 +394,7 @@ int SimpleEntryImpl::WriteData(int stream_index,
if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
offset < 0 || buf_len < 0) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
CreateNetLogReadWriteCompleteCallback(net::ERR_INVALID_ARGUMENT));
@@ -403,7 +403,7 @@ int SimpleEntryImpl::WriteData(int stream_index,
return net::ERR_INVALID_ARGUMENT;
}
if (backend_.get() && offset + buf_len > backend_->GetMaxFileSize()) {
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
@@ -445,7 +445,7 @@ int SimpleEntryImpl::WriteData(int stream_index,
}
op_callback = CompletionCallback();
ret_value = buf_len;
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_OPTIMISTIC,
CreateNetLogReadWriteCompleteCallback(buf_len));
@@ -807,7 +807,7 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
DCHECK(io_thread_checker_.CalledOnValidThread());
ScopedOperationRunner operation_runner(this);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_BEGIN,
CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len,
@@ -823,7 +823,7 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(callback, net::ERR_FAILED));
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
@@ -889,7 +889,7 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
DCHECK(io_thread_checker_.CalledOnValidThread());
ScopedOperationRunner operation_runner(this);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_BEGIN,
CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len,
@@ -898,7 +898,7 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) {
RecordWriteResult(cache_type_, WRITE_RESULT_BAD_STATE);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
@@ -1226,7 +1226,7 @@ void SimpleEntryImpl::ReadOperationComplete(
crc_check_state_[stream_index] = CRC_CHECK_NOT_DONE;
}
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
CreateNetLogReadWriteCompleteCallback(*result));
@@ -1244,7 +1244,7 @@ void SimpleEntryImpl::WriteOperationComplete(
RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
else
RecordWriteResult(cache_type_, WRITE_RESULT_SYNC_WRITE_FAILURE);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
CreateNetLogReadWriteCompleteCallback(*result));
}
@@ -1314,7 +1314,7 @@ void SimpleEntryImpl::ChecksumOperationComplete(
DCHECK_EQ(STATE_IO_PENDING, state_);
DCHECK(result);
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEventWithNetErrorCode(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_CHECKSUM_END,
*result);
@@ -1329,7 +1329,7 @@ void SimpleEntryImpl::ChecksumOperationComplete(
} else {
RecordReadResult(cache_type_, READ_RESULT_SYNC_CHECKSUM_FAILURE);
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
CreateNetLogReadWriteCompleteCallback(*result));
}
diff --git a/chromium/net/disk_cache/simple/simple_histogram_macros.h b/chromium/net/disk_cache/simple/simple_histogram_macros.h
index f800a6f40a3..39fdff73ceb 100644
--- a/chromium/net/disk_cache/simple/simple_histogram_macros.h
+++ b/chromium/net/disk_cache/simple/simple_histogram_macros.h
@@ -16,6 +16,9 @@
#define SIMPLE_CACHE_THUNK(uma_type, args) UMA_HISTOGRAM_##uma_type args
+// TODO(pasko): add histograms for shader cache as soon as it becomes possible
+// for a user to get shader cache with the |SimpleBackendImpl| without altering
+// any flags.
#define SIMPLE_CACHE_UMA(uma_type, uma_name, cache_type, ...) \
do { \
switch (cache_type) { \
@@ -31,6 +34,8 @@
SIMPLE_CACHE_THUNK( \
uma_type, ("SimpleCache.Media." uma_name, ##__VA_ARGS__)); \
break; \
+ case net::SHADER_CACHE: \
+ break; \
default: \
NOTREACHED(); \
break; \
diff --git a/chromium/net/disk_cache/simple/simple_index.cc b/chromium/net/disk_cache/simple/simple_index.cc
index dafc0e72177..6d449cc82fa 100644
--- a/chromium/net/disk_cache/simple/simple_index.cc
+++ b/chromium/net/disk_cache/simple/simple_index.cc
@@ -39,8 +39,8 @@ namespace {
// How many milliseconds we delay writing the index to disk since the last cache
// operation has happened.
-const int kDefaultWriteToDiskDelayMSecs = 20000;
-const int kDefaultWriteToDiskOnBackgroundDelayMSecs = 100;
+const int kWriteToDiskDelayMSecs = 20000;
+const int kWriteToDiskOnBackgroundDelayMSecs = 100;
// Divides the cache space into this amount of parts to evict when only one part
// is left.
@@ -167,28 +167,10 @@ SimpleIndex::~SimpleIndex() {
void SimpleIndex::Initialize(base::Time cache_mtime) {
DCHECK(io_thread_checker_.CalledOnValidThread());
- // Take the foreground and background index flush delays from the experiment
- // settings only if both are valid.
- foreground_flush_delay_ = kDefaultWriteToDiskDelayMSecs;
- background_flush_delay_ = kDefaultWriteToDiskOnBackgroundDelayMSecs;
- const std::string index_flush_intervals = base::FieldTrialList::FindFullName(
- "SimpleCacheIndexFlushDelay_Foreground_Background");
- if (!index_flush_intervals.empty()) {
- base::StringTokenizer tokens(index_flush_intervals, "_");
- int foreground_delay, background_delay;
- if (tokens.GetNext() &&
- base::StringToInt(tokens.token(), &foreground_delay) &&
- tokens.GetNext() &&
- base::StringToInt(tokens.token(), &background_delay)) {
- foreground_flush_delay_ = foreground_delay;
- background_flush_delay_ = background_delay;
- }
- }
-
#if defined(OS_ANDROID)
if (base::android::IsVMInitialized()) {
- activity_status_listener_.reset(new base::android::ActivityStatus::Listener(
- base::Bind(&SimpleIndex::OnActivityStateChange, AsWeakPtr())));
+ app_status_listener_.reset(new base::android::ApplicationStatusListener(
+ base::Bind(&SimpleIndex::OnApplicationStateChange, AsWeakPtr())));
}
#endif
@@ -388,8 +370,8 @@ void SimpleIndex::InsertInEntrySet(
void SimpleIndex::PostponeWritingToDisk() {
if (!initialized_)
return;
- const int delay = app_on_background_ ? background_flush_delay_
- : foreground_flush_delay_;
+ const int delay = app_on_background_ ? kWriteToDiskOnBackgroundDelayMSecs
+ : kWriteToDiskDelayMSecs;
// If the timer is already active, Start() will just Reset it, postponing it.
write_to_disk_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(delay), write_to_disk_cb_);
@@ -455,15 +437,15 @@ void SimpleIndex::MergeInitializingSet(
}
#if defined(OS_ANDROID)
-void SimpleIndex::OnActivityStateChange(
- base::android::ActivityState state) {
+void SimpleIndex::OnApplicationStateChange(
+ base::android::ApplicationState state) {
DCHECK(io_thread_checker_.CalledOnValidThread());
// For more info about android activities, see:
// developer.android.com/training/basics/activity-lifecycle/pausing.html
- // These values are defined in the file ActivityStatus.java
- if (state == base::android::ACTIVITY_STATE_RESUMED) {
+ if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
app_on_background_ = false;
- } else if (state == base::android::ACTIVITY_STATE_STOPPED) {
+ } else if (state ==
+ base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
app_on_background_ = true;
WriteToDisk();
}
diff --git a/chromium/net/disk_cache/simple/simple_index.h b/chromium/net/disk_cache/simple/simple_index.h
index 6c0d81c99a6..f2e4a2e9ea4 100644
--- a/chromium/net/disk_cache/simple/simple_index.h
+++ b/chromium/net/disk_cache/simple/simple_index.h
@@ -25,7 +25,7 @@
#include "net/base/net_export.h"
#if defined(OS_ANDROID)
-#include "base/android/activity_status.h"
+#include "base/android/application_status_listener.h"
#endif
class Pickle;
@@ -149,9 +149,9 @@ class NET_EXPORT_PRIVATE SimpleIndex
void MergeInitializingSet(scoped_ptr<SimpleIndexLoadResult> load_result);
#if defined(OS_ANDROID)
- void OnActivityStateChange(base::android::ActivityState state);
+ void OnApplicationStateChange(base::android::ApplicationState state);
- scoped_ptr<base::android::ActivityStatus::Listener> activity_status_listener_;
+ scoped_ptr<base::android::ApplicationStatusListener> app_status_listener_;
#endif
// The owner of |this| must ensure the |delegate_| outlives |this|.
@@ -195,12 +195,6 @@ class NET_EXPORT_PRIVATE SimpleIndex
// background we can write the index much more frequently, to insure fresh
// index on next startup.
bool app_on_background_;
-
- // The time in milliseconds for the index to be idle before it gets flushed to
- // the disk. When the app is on foreground the delay is different from the
- // background state.
- int foreground_flush_delay_;
- int background_flush_delay_;
};
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/simple/simple_index_file.cc b/chromium/net/disk_cache/simple/simple_index_file.cc
index abd9f7a7ae0..0b375f4a6cc 100644
--- a/chromium/net/disk_cache/simple/simple_index_file.cc
+++ b/chromium/net/disk_cache/simple/simple_index_file.cc
@@ -66,7 +66,7 @@ void UmaRecordIndexInitMethod(IndexInitMethod method,
}
bool WritePickleFile(Pickle* pickle, const base::FilePath& file_name) {
- int bytes_written = file_util::WriteFile(
+ int bytes_written = base::WriteFile(
file_name, static_cast<const char*>(pickle->data()), pickle->size());
if (bytes_written != implicit_cast<int>(pickle->size())) {
base::DeleteFile(file_name, /* recursive = */ false);
@@ -95,7 +95,7 @@ void ProcessEntryFile(SimpleIndex::EntrySet* entries,
return;
}
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
if (!base::GetFileInfo(file_path, &file_info)) {
LOG(ERROR) << "Could not get file info for " << file_path.value();
return;
@@ -195,7 +195,6 @@ void SimpleIndexFile::SyncWriteToDisk(net::CacheType cache_type,
// 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";
@@ -434,7 +433,7 @@ void SimpleIndexFile::SyncRestoreFromDisk(
const base::FilePath& cache_directory,
const base::FilePath& index_file_path,
SimpleIndexLoadResult* out_result) {
- LOG(INFO) << "Simple Cache Index is being restored from disk.";
+ VLOG(1) << "Simple Cache Index is being restored from disk.";
base::DeleteFile(index_file_path, /* recursive = */ false);
out_result->Reset();
SimpleIndex::EntrySet* entries = &out_result->entries;
diff --git a/chromium/net/disk_cache/simple/simple_index_file_unittest.cc b/chromium/net/disk_cache/simple/simple_index_file_unittest.cc
index 17aa595bba9..212320b1025 100644
--- a/chromium/net/disk_cache/simple/simple_index_file_unittest.cc
+++ b/chromium/net/disk_cache/simple/simple_index_file_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/hash.h"
#include "base/logging.h"
@@ -169,9 +170,8 @@ TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) {
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()));
+ base::WriteFile(index_path,
+ kDummyData.data(), kDummyData.size()));
ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
EXPECT_FALSE(
WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
@@ -243,7 +243,7 @@ TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
const std::string kDummyData = "nothing to be seen here";
EXPECT_EQ(
implicit_cast<int>(kDummyData.size()),
- file_util::WriteFile(index_path, kDummyData.data(), kDummyData.size()));
+ base::WriteFile(index_path, kDummyData.data(), kDummyData.size()));
base::Time fake_cache_mtime;
ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
&fake_cache_mtime));
@@ -269,19 +269,16 @@ TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
const base::FilePath cache_path = cache_dir.path();
// Write an old fake index file.
- base::PlatformFileError error;
- base::PlatformFile file = base::CreatePlatformFile(
- cache_path.AppendASCII("index"),
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
- NULL,
- &error);
+ base::File file(cache_path.AppendASCII("index"),
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
disk_cache::FakeIndexData file_contents;
file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
file_contents.version = 5;
- int bytes_written = base::WritePlatformFile(
- file, 0, reinterpret_cast<char*>(&file_contents), sizeof(file_contents));
- ASSERT_TRUE(base::ClosePlatformFile(file));
+ int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents),
+ sizeof(file_contents));
ASSERT_EQ((int)sizeof(file_contents), bytes_written);
+ file.Close();
// Write the index file. The format is incorrect, but for transitioning from
// v5 it does not matter.
@@ -289,9 +286,9 @@ TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
const base::FilePath old_index_file =
cache_path.AppendASCII("the-real-index");
ASSERT_EQ(implicit_cast<int>(index_file_contents.size()),
- file_util::WriteFile(old_index_file,
- index_file_contents.data(),
- index_file_contents.size()));
+ base::WriteFile(old_index_file,
+ index_file_contents.data(),
+ index_file_contents.size()));
// Upgrade the cache.
ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path));
diff --git a/chromium/net/disk_cache/simple/simple_index_unittest.cc b/chromium/net/disk_cache/simple/simple_index_unittest.cc
index cee7bf33c4d..0afbaa2a058 100644
--- a/chromium/net/disk_cache/simple/simple_index_unittest.cc
+++ b/chromium/net/disk_cache/simple/simple_index_unittest.cc
@@ -198,7 +198,6 @@ TEST_F(EntryMetadataTest, Serialize) {
}
TEST_F(SimpleIndexTest, IndexSizeCorrectOnMerge) {
- typedef disk_cache::SimpleIndex::EntrySet EntrySet;
index()->SetMaxSize(100);
index()->Insert(hashes_.at<2>());
index()->UpdateEntrySize(hashes_.at<2>(), 2);
diff --git a/chromium/net/disk_cache/simple/simple_synchronous_entry.cc b/chromium/net/disk_cache/simple/simple_synchronous_entry.cc
index 81f52e15b7d..14be1d4d317 100644
--- a/chromium/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/chromium/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -23,24 +23,9 @@
#include "net/disk_cache/simple/simple_util.h"
#include "third_party/zlib/zlib.h"
-using base::kInvalidPlatformFileValue;
-using base::ClosePlatformFile;
+using base::File;
using base::FilePath;
-using base::GetPlatformFileInfo;
-using base::PlatformFileError;
-using base::PlatformFileInfo;
-using base::PLATFORM_FILE_CREATE;
-using base::PLATFORM_FILE_ERROR_EXISTS;
-using base::PLATFORM_FILE_ERROR_NOT_FOUND;
-using base::PLATFORM_FILE_OK;
-using base::PLATFORM_FILE_OPEN;
-using base::PLATFORM_FILE_OPEN_ALWAYS;
-using base::PLATFORM_FILE_READ;
-using base::PLATFORM_FILE_WRITE;
-using base::ReadPlatformFile;
using base::Time;
-using base::TruncatePlatformFile;
-using base::WritePlatformFile;
namespace {
@@ -301,8 +286,9 @@ void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op,
// be handled in the SimpleEntryImpl.
DCHECK_LT(0, in_entry_op.buf_len);
DCHECK(!empty_file_omitted_[file_index]);
- int bytes_read = ReadPlatformFile(
- files_[file_index], file_offset, out_buf->data(), in_entry_op.buf_len);
+ File* file = const_cast<File*>(&files_[file_index]);
+ int bytes_read =
+ file->Read(file_offset, out_buf->data(), in_entry_op.buf_len);
if (bytes_read > 0) {
entry_stat->set_last_used(Time::Now());
*out_crc32 = crc32(crc32(0L, Z_NULL, 0),
@@ -343,7 +329,7 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
*out_result = net::ERR_CACHE_WRITE_FAILURE;
return;
}
- PlatformFileError error;
+ File::Error error;
if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) {
RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE);
Doom();
@@ -364,7 +350,7 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
// 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)) {
+ if (!files_[file_index].SetLength(file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
@@ -372,8 +358,7 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
}
}
if (buf_len > 0) {
- if (WritePlatformFile(
- files_[file_index], file_offset, in_buf->data(), buf_len) !=
+ if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) !=
buf_len) {
RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
Doom();
@@ -387,7 +372,7 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
} else {
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)) {
+ if (!files_[file_index].SetLength(file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
@@ -488,8 +473,8 @@ void SimpleSynchronousEntry::WriteSparseData(
// This is a pessimistic estimate; it assumes the entire buffer is going to
// be appended as a new range, not written over existing ranges.
if (sparse_data_size + buf_len > max_sparse_data_size) {
- DLOG(INFO) << "Truncating sparse data file (" << sparse_data_size << " + "
- << buf_len << " > " << max_sparse_data_size << ")";
+ DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + "
+ << buf_len << " > " << max_sparse_data_size << ")";
TruncateSparseFile();
}
@@ -624,7 +609,7 @@ void SimpleSynchronousEntry::CheckEOFRecord(int index,
return;
}
if (has_crc32 && crc32 != expected_crc32) {
- DLOG(INFO) << "EOF record had bad crc.";
+ DVLOG(1) << "EOF record had bad crc.";
*out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
Doom();
@@ -640,12 +625,11 @@ void SimpleSynchronousEntry::Close(
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)) {
+ if (files_[0].Write(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.";
+ DVLOG(1) << "Could not write stream 0 data.";
Doom();
}
@@ -668,18 +652,18 @@ void SimpleSynchronousEntry::Close(
// next open will yield wrong stream sizes. On stream 1 and stream 2 proper
// resizing of the file is handled in SimpleSynchronousEntry::WriteData().
if (stream_index == 0 &&
- !TruncatePlatformFile(files_[file_index], eof_offset)) {
+ !files_[file_index].SetLength(eof_offset)) {
RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
- DLOG(INFO) << "Could not truncate stream 0 file.";
+ DVLOG(1) << "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)) {
+ if (files_[file_index].Write(eof_offset,
+ reinterpret_cast<const char*>(&eof_record),
+ sizeof(eof_record)) !=
+ sizeof(eof_record)) {
RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
- DLOG(INFO) << "Could not write eof record.";
+ DVLOG(1) << "Could not write eof record.";
Doom();
break;
}
@@ -688,8 +672,7 @@ void SimpleSynchronousEntry::Close(
if (empty_file_omitted_[i])
continue;
- bool did_close_file = ClosePlatformFile(files_[i]);
- DCHECK(did_close_file);
+ files_[i].Close();
const int64 file_size = entry_stat.GetFileSize(key_, i);
SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
"LastClusterSize", cache_type_,
@@ -700,10 +683,8 @@ void SimpleSynchronousEntry::Close(
cluster_loss * 100 / (cluster_loss + file_size));
}
- if (sparse_file_open()) {
- bool did_close_file = ClosePlatformFile(sparse_file_);
- CHECK(did_close_file);
- }
+ if (sparse_file_open())
+ sparse_file_.Close();
if (files_created_) {
const int stream2_file_index = GetFileIndexFromStreamIndex(2);
@@ -724,12 +705,9 @@ SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type,
entry_hash_(entry_hash),
key_(key),
have_open_files_(false),
- initialized_(false),
- sparse_file_(kInvalidPlatformFileValue) {
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- files_[i] = kInvalidPlatformFileValue;
+ initialized_(false) {
+ for (int i = 0; i < kSimpleEntryFileCount; ++i)
empty_file_omitted_[i] = false;
- }
}
SimpleSynchronousEntry::~SimpleSynchronousEntry() {
@@ -740,26 +718,27 @@ SimpleSynchronousEntry::~SimpleSynchronousEntry() {
bool SimpleSynchronousEntry::MaybeOpenFile(
int file_index,
- PlatformFileError* out_error) {
+ File::Error* out_error) {
DCHECK(out_error);
FilePath filename = GetFilenameFromFileIndex(file_index);
- int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
- files_[file_index] = CreatePlatformFile(filename, flags, NULL, out_error);
+ int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;
+ files_[file_index].Initialize(filename, flags);
+ *out_error = files_[file_index].error_details();
- if (CanOmitEmptyFile(file_index) &&
- *out_error == PLATFORM_FILE_ERROR_NOT_FOUND) {
+ if (CanOmitEmptyFile(file_index) && !files_[file_index].IsValid() &&
+ *out_error == File::FILE_ERROR_NOT_FOUND) {
empty_file_omitted_[file_index] = true;
return true;
}
- return *out_error == PLATFORM_FILE_OK;
+ return files_[file_index].IsValid();
}
bool SimpleSynchronousEntry::MaybeCreateFile(
int file_index,
FileRequired file_required,
- PlatformFileError* out_error) {
+ File::Error* out_error) {
DCHECK(out_error);
if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) {
@@ -768,19 +747,20 @@ bool SimpleSynchronousEntry::MaybeCreateFile(
}
FilePath filename = GetFilenameFromFileIndex(file_index);
- int flags = PLATFORM_FILE_CREATE | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
- files_[file_index] = CreatePlatformFile(filename, flags, NULL, out_error);
+ int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE;
+ files_[file_index].Initialize(filename, flags);
+ *out_error = files_[file_index].error_details();
empty_file_omitted_[file_index] = false;
- return *out_error == PLATFORM_FILE_OK;
+ return files_[file_index].IsValid();
}
bool SimpleSynchronousEntry::OpenFiles(
bool had_index,
SimpleEntryStat* out_entry_stat) {
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- PlatformFileError error;
+ File::Error error;
if (!MaybeOpenFile(i, &error)) {
// TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
// We can calculate the third as the sum or difference of the other two.
@@ -788,16 +768,16 @@ bool SimpleSynchronousEntry::OpenFiles(
cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR, had_index);
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncOpenPlatformFileError", cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
if (had_index) {
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncOpenPlatformFileError_WithIndex", cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
} else {
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncOpenPlatformFileError_WithoutIndex",
cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
}
while (--i >= 0)
CloseFile(i);
@@ -814,8 +794,8 @@ bool SimpleSynchronousEntry::OpenFiles(
continue;
}
- PlatformFileInfo file_info;
- bool success = GetPlatformFileInfo(files_[i], &file_info);
+ File::Info file_info;
+ bool success = files_[i].GetInfo(&file_info);
base::Time file_last_modified;
if (!success) {
DLOG(WARNING) << "Could not get platform file info.";
@@ -860,23 +840,23 @@ bool SimpleSynchronousEntry::CreateFiles(
bool had_index,
SimpleEntryStat* out_entry_stat) {
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- PlatformFileError error;
+ File::Error error;
if (!MaybeCreateFile(i, FILE_NOT_REQUIRED, &error)) {
// TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
// We can calculate the third as the sum or difference of the other two.
RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR, had_index);
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncCreatePlatformFileError", cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
if (had_index) {
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncCreatePlatformFileError_WithIndex", cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
} else {
SIMPLE_CACHE_UMA(ENUMERATION,
"SyncCreatePlatformFileError_WithoutIndex",
cache_type_,
- -error, -base::PLATFORM_FILE_ERROR_MAX);
+ -error, -base::File::FILE_ERROR_MAX);
}
while (--i >= 0)
CloseFile(i);
@@ -901,16 +881,12 @@ void SimpleSynchronousEntry::CloseFile(int index) {
if (empty_file_omitted_[index]) {
empty_file_omitted_[index] = false;
} else {
- DCHECK_NE(kInvalidPlatformFileValue, files_[index]);
- bool did_close = ClosePlatformFile(files_[index]);
- DCHECK(did_close);
- files_[index] = kInvalidPlatformFileValue;
+ DCHECK(files_[index].IsValid());
+ files_[index].Close();
}
- if (sparse_file_open()) {
- bool did_close = CloseSparseFile();
- DCHECK(did_close);
- }
+ if (sparse_file_open())
+ CloseSparseFile();
}
void SimpleSynchronousEntry::CloseFiles() {
@@ -934,8 +910,7 @@ int SimpleSynchronousEntry::InitializeForOpen(
SimpleFileHeader header;
int header_read_result =
- ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
- sizeof(header));
+ files_[i].Read(0, reinterpret_cast<char*>(&header), sizeof(header));
if (header_read_result != sizeof(header)) {
DLOG(WARNING) << "Cannot read header from entry.";
RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_HEADER, had_index);
@@ -958,8 +933,8 @@ int SimpleSynchronousEntry::InitializeForOpen(
}
scoped_ptr<char[]> key(new char[header.key_length]);
- int key_read_result = ReadPlatformFile(files_[i], sizeof(header),
- key.get(), header.key_length);
+ int key_read_result = files_[i].Read(sizeof(header), key.get(),
+ header.key_length);
if (key_read_result != implicit_cast<int>(header.key_length)) {
DLOG(WARNING) << "Cannot read key from entry.";
RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_KEY, had_index);
@@ -1005,7 +980,7 @@ int SimpleSynchronousEntry::InitializeForOpen(
DCHECK(CanOmitEmptyFile(stream2_file_index));
if (!empty_file_omitted_[stream2_file_index] &&
out_entry_stat->data_size(2) == 0) {
- DLOG(INFO) << "Removing empty stream 2 file.";
+ DVLOG(1) << "Removing empty stream 2 file.";
CloseFile(stream2_file_index);
DeleteFileForEntryHash(path_, entry_hash_, stream2_file_index);
empty_file_omitted_[stream2_file_index] = true;
@@ -1030,15 +1005,15 @@ bool SimpleSynchronousEntry::InitializeCreatedFile(
header.key_length = key_.size();
header.key_hash = base::Hash(key_);
- int bytes_written = WritePlatformFile(
- files_[file_index], 0, reinterpret_cast<char*>(&header), sizeof(header));
+ int bytes_written = files_[file_index].Write(
+ 0, reinterpret_cast<char*>(&header), sizeof(header));
if (bytes_written != sizeof(header)) {
*out_result = CREATE_ENTRY_CANT_WRITE_HEADER;
return false;
}
- bytes_written = WritePlatformFile(
- files_[file_index], sizeof(header), key_.data(), key_.size());
+ bytes_written = files_[file_index].Write(sizeof(header), key_.data(),
+ key_.size());
if (bytes_written != implicit_cast<int>(key_.size())) {
*out_result = CREATE_ENTRY_CANT_WRITE_KEY;
return false;
@@ -1100,8 +1075,9 @@ int SimpleSynchronousEntry::ReadAndValidateStream0(
*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);
+ File* file = const_cast<File*>(&files_[0]);
+ int bytes_read =
+ file->Read(file_offset, (*stream_0_data)->data(), stream_0_size);
if (bytes_read != stream_0_size)
return net::ERR_FAILED;
@@ -1113,7 +1089,7 @@ int SimpleSynchronousEntry::ReadAndValidateStream0(
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.";
+ DVLOG(1) << "EOF record had bad crc.";
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
return net::ERR_FAILED;
}
@@ -1130,17 +1106,17 @@ int SimpleSynchronousEntry::GetEOFRecordData(int index,
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)) {
+ File* file = const_cast<File*>(&files_[file_index]);
+ if (file->Read(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.";
+ DVLOG(1) << "EOF record had bad magic number.";
return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
}
@@ -1208,14 +1184,12 @@ bool SimpleSynchronousEntry::OpenSparseFileIfExists(
FilePath filename = path_.AppendASCII(
GetSparseFilenameFromEntryHash(entry_hash_));
- int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
- bool created;
- PlatformFileError error;
- sparse_file_ = CreatePlatformFile(filename, flags, &created, &error);
- if (error == PLATFORM_FILE_ERROR_NOT_FOUND)
- return true;
+ int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;
+ sparse_file_.Initialize(filename, flags);
+ if (sparse_file_.IsValid())
+ return ScanSparseFile(out_sparse_data_size);
- return ScanSparseFile(out_sparse_data_size);
+ return sparse_file_.error_details() == File::FILE_ERROR_NOT_FOUND;
}
bool SimpleSynchronousEntry::CreateSparseFile() {
@@ -1223,30 +1197,24 @@ bool SimpleSynchronousEntry::CreateSparseFile() {
FilePath filename = path_.AppendASCII(
GetSparseFilenameFromEntryHash(entry_hash_));
- int flags = PLATFORM_FILE_CREATE | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
- bool created;
- PlatformFileError error;
- sparse_file_ = CreatePlatformFile(filename, flags, &created, &error);
- if (error != PLATFORM_FILE_OK)
+ int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE;
+ sparse_file_.Initialize(filename, flags);
+ if (!sparse_file_.IsValid())
return false;
return InitializeSparseFile();
}
-bool SimpleSynchronousEntry::CloseSparseFile() {
+void SimpleSynchronousEntry::CloseSparseFile() {
DCHECK(sparse_file_open());
-
- bool did_close = ClosePlatformFile(sparse_file_);
- if (did_close)
- sparse_file_ = kInvalidPlatformFileValue;
- return did_close;
+ sparse_file_.Close();
}
bool SimpleSynchronousEntry::TruncateSparseFile() {
DCHECK(sparse_file_open());
int64 header_and_key_length = sizeof(SimpleFileHeader) + key_.size();
- if (!TruncatePlatformFile(sparse_file_, header_and_key_length)) {
+ if (!sparse_file_.SetLength(header_and_key_length)) {
DLOG(WARNING) << "Could not truncate sparse file";
return false;
}
@@ -1266,15 +1234,14 @@ bool SimpleSynchronousEntry::InitializeSparseFile() {
header.key_hash = base::Hash(key_);
int header_write_result =
- WritePlatformFile(sparse_file_, 0, reinterpret_cast<char*>(&header),
- sizeof(header));
+ sparse_file_.Write(0, reinterpret_cast<char*>(&header), sizeof(header));
if (header_write_result != sizeof(header)) {
DLOG(WARNING) << "Could not write sparse file header";
return false;
}
- int key_write_result = WritePlatformFile(sparse_file_, sizeof(header),
- key_.data(), key_.size());
+ int key_write_result = sparse_file_.Write(sizeof(header), key_.data(),
+ key_.size());
if (key_write_result != implicit_cast<int>(key_.size())) {
DLOG(WARNING) << "Could not write sparse file key";
return false;
@@ -1293,8 +1260,7 @@ bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) {
SimpleFileHeader header;
int header_read_result =
- ReadPlatformFile(sparse_file_, 0, reinterpret_cast<char*>(&header),
- sizeof(header));
+ sparse_file_.Read(0, reinterpret_cast<char*>(&header), sizeof(header));
if (header_read_result != sizeof(header)) {
DLOG(WARNING) << "Could not read header from sparse file.";
return false;
@@ -1316,10 +1282,9 @@ bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) {
while (1) {
SimpleFileSparseRangeHeader range_header;
int range_header_read_result =
- ReadPlatformFile(sparse_file_,
- range_header_offset,
- reinterpret_cast<char*>(&range_header),
- sizeof(range_header));
+ sparse_file_.Read(range_header_offset,
+ reinterpret_cast<char*>(&range_header),
+ sizeof(range_header));
if (range_header_read_result == 0)
break;
if (range_header_read_result != sizeof(range_header)) {
@@ -1359,9 +1324,7 @@ bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range,
DCHECK_GE(range->length, offset);
DCHECK_GE(range->length, offset + len);
- int bytes_read = ReadPlatformFile(sparse_file_,
- range->file_offset + offset,
- buf, len);
+ int bytes_read = sparse_file_.Read(range->file_offset + offset, buf, len);
if (bytes_read < len) {
DLOG(WARNING) << "Could not read sparse range.";
return false;
@@ -1406,19 +1369,16 @@ bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range,
header.length = range->length;
header.data_crc32 = range->data_crc32;
- int bytes_written = WritePlatformFile(sparse_file_,
- range->file_offset - sizeof(header),
- reinterpret_cast<char*>(&header),
- sizeof(header));
+ int bytes_written = sparse_file_.Write(range->file_offset - sizeof(header),
+ reinterpret_cast<char*>(&header),
+ sizeof(header));
if (bytes_written != implicit_cast<int>(sizeof(header))) {
DLOG(WARNING) << "Could not rewrite sparse range header.";
return false;
}
}
- int bytes_written = WritePlatformFile(sparse_file_,
- range->file_offset + offset,
- buf, len);
+ int bytes_written = sparse_file_.Write(range->file_offset + offset, buf, len);
if (bytes_written < len) {
DLOG(WARNING) << "Could not write sparse range.";
return false;
@@ -1444,20 +1404,16 @@ bool SimpleSynchronousEntry::AppendSparseRange(int64 offset,
header.length = len;
header.data_crc32 = data_crc32;
- int bytes_written = WritePlatformFile(sparse_file_,
- sparse_tail_offset_,
- reinterpret_cast<char*>(&header),
- sizeof(header));
+ int bytes_written = sparse_file_.Write(sparse_tail_offset_,
+ reinterpret_cast<char*>(&header),
+ sizeof(header));
if (bytes_written != implicit_cast<int>(sizeof(header))) {
DLOG(WARNING) << "Could not append sparse range header.";
return false;
}
sparse_tail_offset_ += bytes_written;
- bytes_written = WritePlatformFile(sparse_file_,
- sparse_tail_offset_,
- buf,
- len);
+ bytes_written = sparse_file_.Write(sparse_tail_offset_, buf, len);
if (bytes_written < len) {
DLOG(WARNING) << "Could not append sparse range data.";
return false;
diff --git a/chromium/net/disk_cache/simple/simple_synchronous_entry.h b/chromium/net/disk_cache/simple/simple_synchronous_entry.h
index 2ae4c0d87b6..739a4889493 100644
--- a/chromium/net/disk_cache/simple/simple_synchronous_entry.h
+++ b/chromium/net/disk_cache/simple/simple_synchronous_entry.h
@@ -11,10 +11,10 @@
#include <utility>
#include <vector>
+#include "base/files/file.h"
#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"
@@ -213,13 +213,13 @@ class SimpleSynchronousEntry {
// or if the file was not found and is allowed to be omitted if the
// corresponding stream is empty.
bool MaybeOpenFile(int file_index,
- base::PlatformFileError* out_error);
+ base::File::Error* out_error);
// Creates one of the cache entry files if necessary. If the file is allowed
// to be omitted if the corresponding stream is empty, and if |file_required|
// is FILE_NOT_REQUIRED, then the file is not created; otherwise, it is.
bool MaybeCreateFile(int file_index,
FileRequired file_required,
- base::PlatformFileError* out_error);
+ base::File::Error* out_error);
bool OpenFiles(bool had_index,
SimpleEntryStat* out_entry_stat);
bool CreateFiles(bool had_index,
@@ -268,7 +268,7 @@ class SimpleSynchronousEntry {
bool CreateSparseFile();
// Closes the sparse data file.
- bool CloseSparseFile();
+ void CloseSparseFile();
// Writes the header to the (newly-created) sparse file.
bool InitializeSparseFile();
@@ -305,7 +305,7 @@ class SimpleSynchronousEntry {
base::FilePath GetFilenameFromFileIndex(int file_index);
bool sparse_file_open() const {
- return sparse_file_ != base::kInvalidPlatformFileValue;
+ return sparse_file_.IsValid();
}
const net::CacheType cache_type_;
@@ -316,7 +316,7 @@ class SimpleSynchronousEntry {
bool have_open_files_;
bool initialized_;
- base::PlatformFile files_[kSimpleEntryFileCount];
+ base::File files_[kSimpleEntryFileCount];
// True if the corresponding stream is empty and therefore no on-disk file
// was created to store it.
@@ -325,7 +325,7 @@ class SimpleSynchronousEntry {
typedef std::map<int64, SparseRange> SparseRangeOffsetMap;
typedef SparseRangeOffsetMap::iterator SparseRangeIterator;
SparseRangeOffsetMap sparse_ranges_;
- base::PlatformFile sparse_file_;
+ base::File sparse_file_;
// Offset of the end of the sparse file (where the next sparse range will be
// written).
int64 sparse_tail_offset_;
diff --git a/chromium/net/disk_cache/simple/simple_test_util.cc b/chromium/net/disk_cache/simple/simple_test_util.cc
index 9a275588230..2d3edcb22a1 100644
--- a/chromium/net/disk_cache/simple/simple_test_util.cc
+++ b/chromium/net/disk_cache/simple/simple_test_util.cc
@@ -4,7 +4,8 @@
#include "net/disk_cache/simple/simple_test_util.h"
-#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
#include "net/disk_cache/simple/simple_util.h"
namespace disk_cache {
@@ -14,19 +15,14 @@ bool CreateCorruptFileForTests(const std::string& key,
const base::FilePath& cache_path) {
base::FilePath entry_file_path = cache_path.AppendASCII(
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);
+ int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
+ base::File entry_file(entry_file_path, flags);
- if (base::kInvalidPlatformFileValue == entry_file)
- return false;
- if (base::WritePlatformFile(entry_file, 0, "dummy", 1) != 1)
- return false;
- if (!base::ClosePlatformFile(entry_file))
+ if (!entry_file.IsValid())
return false;
- return true;
+ return entry_file.Write(0, "dummy", 1) == 1;
}
-} // namespace simple_backend
+} // namespace simple_util
} // namespace disk_cache
diff --git a/chromium/net/disk_cache/simple/simple_util.cc b/chromium/net/disk_cache/simple/simple_util.cc
index 0ad2a05d5a3..4feeeecaf0c 100644
--- a/chromium/net/disk_cache/simple/simple_util.cc
+++ b/chromium/net/disk_cache/simple/simple_util.cc
@@ -28,16 +28,18 @@ bool GetNanoSecsFromStat(const struct stat& st,
#if defined(OS_ANDROID)
*out_sec = st.st_mtime;
*out_nsec = st.st_mtime_nsec;
+ return true;
#elif defined(OS_LINUX)
*out_sec = st.st_mtim.tv_sec;
*out_nsec = st.st_mtim.tv_nsec;
+ return true;
#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
*out_sec = st.st_mtimespec.tv_sec;
*out_nsec = st.st_mtimespec.tv_nsec;
+ return true;
#else
return false;
#endif
- return true;
}
} // namespace
@@ -125,7 +127,7 @@ bool GetMTime(const base::FilePath& path, base::Time* out_mtime) {
return true;
}
#endif
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
if (!base::GetFileInfo(path, &file_info))
return false;
*out_mtime = file_info.last_modified;
diff --git a/chromium/net/disk_cache/simple/simple_util.h b/chromium/net/disk_cache/simple/simple_util.h
index f76215121a4..b6ca85bb23b 100644
--- a/chromium/net/disk_cache/simple/simple_util.h
+++ b/chromium/net/disk_cache/simple/simple_util.h
@@ -66,7 +66,7 @@ NET_EXPORT_PRIVATE int64 GetFileSizeFromKeyAndDataSize(const std::string& key,
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.
+// functions in file.h, the time resolution is milliseconds.
NET_EXPORT_PRIVATE bool GetMTime(const base::FilePath& path,
base::Time* out_mtime);
} // namespace simple_backend
diff --git a/chromium/net/disk_cache/simple/simple_version_upgrade.cc b/chromium/net/disk_cache/simple/simple_version_upgrade.cc
index dfc6ef4394b..f5bf2dc76ac 100644
--- a/chromium/net/disk_cache/simple/simple_version_upgrade.cc
+++ b/chromium/net/disk_cache/simple/simple_version_upgrade.cc
@@ -7,6 +7,7 @@
#include <cstring>
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
@@ -29,20 +30,17 @@ void LogMessageFailedUpgradeFromVersion(int 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);
+ base::File file(file_name, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ if (!file.IsValid())
+ return false;
+
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)) {
+ int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents),
+ sizeof(file_contents));
+ if (bytes_written != sizeof(file_contents)) {
LOG(ERROR) << "Failed to write fake index file: "
<< file_name.LossyDisplayName();
return false;
@@ -135,29 +133,27 @@ bool UpgradeSimpleCacheOnDisk(const base::FilePath& path) {
// 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) {
+ base::File fake_index_file(fake_index,
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+
+ if (!fake_index_file.IsValid()) {
+ if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
+ return WriteFakeIndexFile(fake_index);
+ }
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) ||
+ int bytes_read = fake_index_file.Read(0,
+ reinterpret_cast<char*>(&file_header),
+ sizeof(file_header));
+ if (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;
}
+ fake_index_file.Close();
uint32 version_from = file_header.version;
if (version_from < kMinVersionAbleToUpgrade ||
diff --git a/chromium/net/disk_cache/simple/simple_version_upgrade_unittest.cc b/chromium/net/disk_cache/simple/simple_version_upgrade_unittest.cc
index a5493f7c6eb..15f4fa16ec8 100644
--- a/chromium/net/disk_cache/simple/simple_version_upgrade_unittest.cc
+++ b/chromium/net/disk_cache/simple/simple_version_upgrade_unittest.cc
@@ -39,7 +39,7 @@ bool WriteFakeIndexFileV5(const base::FilePath& cache_path) {
data.unused_must_be_zero2 = 0;
const base::FilePath file_name = cache_path.AppendASCII("index");
return sizeof(data) ==
- file_util::WriteFile(
+ base::WriteFile(
file_name, reinterpret_cast<const char*>(&data), sizeof(data));
}
@@ -55,7 +55,7 @@ TEST(SimpleVersionUpgradeTest, FailsToMigrateBackwards) {
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(
+ base::WriteFile(
file_name, reinterpret_cast<const char*>(&data), sizeof(data)));
EXPECT_FALSE(disk_cache::UpgradeSimpleCacheOnDisk(cache_dir.path()));
}
@@ -69,7 +69,7 @@ TEST(SimpleVersionUpgradeTest, FakeIndexVersionGetsUpdated) {
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(
+ base::WriteFile(
index_file, file_contents.data(), file_contents.size()));
// Upgrade.
@@ -96,7 +96,7 @@ TEST(SimpleVersionUpgradeTest, UpgradeV5V6IndexMustDisappear) {
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(
+ base::WriteFile(
index_file, file_contents.data(), file_contents.size()));
// Create a few entry-like files.
@@ -109,7 +109,7 @@ TEST(SimpleVersionUpgradeTest, UpgradeV5V6IndexMustDisappear) {
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),
+ base::WriteFile(cache_path.AppendASCII(file_name),
entry_contents.data(),
entry_contents.size()));
}
diff --git a/chromium/net/disk_cache/stats_histogram.cc b/chromium/net/disk_cache/stats_histogram.cc
deleted file mode 100644
index 2a675501955..00000000000
--- a/chromium/net/disk_cache/stats_histogram.cc
+++ /dev/null
@@ -1,94 +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/disk_cache/stats_histogram.h"
-
-#include "base/debug/leak_annotations.h"
-#include "base/logging.h"
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/sample_vector.h"
-#include "base/metrics/statistics_recorder.h"
-#include "net/disk_cache/stats.h"
-
-namespace disk_cache {
-
-using base::BucketRanges;
-using base::Histogram;
-using base::HistogramSamples;
-using base::SampleVector;
-using base::StatisticsRecorder;
-
-StatsHistogram::StatsHistogram(const std::string& name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const Stats* stats)
- : Histogram(name, minimum, maximum, ranges),
- stats_(stats) {}
-
-StatsHistogram::~StatsHistogram() {}
-
-// static
-void StatsHistogram::InitializeBucketRanges(const Stats* stats,
- BucketRanges* ranges) {
- for (size_t i = 0; i < ranges->size(); ++i) {
- ranges->set_range(i, stats->GetBucketRange(i));
- }
- ranges->ResetChecksum();
-}
-
-StatsHistogram* StatsHistogram::FactoryGet(const std::string& name,
- const Stats* stats) {
- Sample minimum = 1;
- Sample maximum = disk_cache::Stats::kDataSizesLength - 1;
- size_t bucket_count = disk_cache::Stats::kDataSizesLength;
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (!histogram) {
- DCHECK(stats);
-
- // To avoid racy destruction at shutdown, the following will be leaked.
- BucketRanges* ranges = new BucketRanges(bucket_count + 1);
- InitializeBucketRanges(stats, ranges);
- const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
-
- // To avoid racy destruction at shutdown, the following will be leaked.
- StatsHistogram* stats_histogram =
- new StatsHistogram(name, minimum, maximum, registered_ranges, stats);
- stats_histogram->SetFlags(kUmaTargetedHistogramFlag);
- histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(stats_histogram);
- }
-
- DCHECK(base::HISTOGRAM == histogram->GetHistogramType());
- DCHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
-
- // We're preparing for an otherwise unsafe upcast by ensuring we have the
- // proper class type.
- StatsHistogram* return_histogram = static_cast<StatsHistogram*>(histogram);
- return return_histogram;
-}
-
-void StatsHistogram::Disable() {
- stats_ = NULL;
-}
-
-scoped_ptr<HistogramSamples> StatsHistogram::SnapshotSamples() const {
- scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges()));
- if (stats_)
- stats_->Snapshot(samples.get());
-
- // Only report UMA data once.
- StatsHistogram* mutable_me = const_cast<StatsHistogram*>(this);
- mutable_me->ClearFlags(kUmaTargetedHistogramFlag);
-
- return samples.PassAs<HistogramSamples>();
-}
-
-int StatsHistogram::FindCorruption(const HistogramSamples& samples) const {
- // This class won't monitor inconsistencies.
- return HistogramBase::NO_INCONSISTENCIES;
-}
-
-} // namespace disk_cache
diff --git a/chromium/net/disk_cache/stats_histogram.h b/chromium/net/disk_cache/stats_histogram.h
deleted file mode 100644
index 2e481f52e42..00000000000
--- a/chromium/net/disk_cache/stats_histogram.h
+++ /dev/null
@@ -1,58 +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_DISK_CACHE_STATS_HISTOGRAM_H_
-#define NET_DISK_CACHE_STATS_HISTOGRAM_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
-
-namespace base {
-class BucketRanges;
-class HistogramSamples;
-class SampleVector;
-} // namespace base
-
-namespace disk_cache {
-
-class Stats;
-
-// This class provides support for sending the disk cache size stats as a UMA
-// histogram. We'll provide our own storage and management for the data, and a
-// SampleVector with a copy of our data.
-//
-// Class derivation of Histogram "deprecated," and should not be copied, and
-// may eventually go away.
-//
-class StatsHistogram : public base::Histogram {
- public:
- StatsHistogram(const std::string& name,
- Sample minimum,
- Sample maximum,
- const base::BucketRanges* ranges,
- const Stats* stats);
- virtual ~StatsHistogram();
-
- static void InitializeBucketRanges(const Stats* stats,
- base::BucketRanges* ranges);
- static StatsHistogram* FactoryGet(const std::string& name,
- const Stats* stats);
-
- // Disables this histogram when the underlying Stats go away.
- void Disable();
-
- virtual scoped_ptr<base::HistogramSamples> SnapshotSamples() const OVERRIDE;
- virtual int FindCorruption(
- const base::HistogramSamples& samples) const OVERRIDE;
-
- private:
- const Stats* stats_;
- DISALLOW_COPY_AND_ASSIGN(StatsHistogram);
-};
-
-} // namespace disk_cache
-
-#endif // NET_DISK_CACHE_STATS_HISTOGRAM_H_
diff --git a/chromium/net/disk_cache/tracing_cache_backend.cc b/chromium/net/disk_cache/tracing/tracing_cache_backend.cc
index 4966133f00d..1efdb99963d 100644
--- a/chromium/net/disk_cache/tracing_cache_backend.cc
+++ b/chromium/net/disk_cache/tracing/tracing_cache_backend.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/tracing_cache_backend.h"
+#include "net/disk_cache/tracing/tracing_cache_backend.h"
#include "net/base/net_errors.h"
diff --git a/chromium/net/disk_cache/tracing_cache_backend.h b/chromium/net/disk_cache/tracing/tracing_cache_backend.h
index 304a7334d83..b8d18696f54 100644
--- a/chromium/net/disk_cache/tracing_cache_backend.h
+++ b/chromium/net/disk_cache/tracing/tracing_cache_backend.h
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_DISK_CACHE_TRACING_CACHE_BACKEND_H_
-#define NET_DISK_CACHE_TRACING_CACHE_BACKEND_H_
+#ifndef NET_DISK_CACHE_TRACING_TRACING_CACHE_BACKEND_H_
+#define NET_DISK_CACHE_TRACING_TRACING_CACHE_BACKEND_H_
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "net/disk_cache/blockfile/stats.h"
#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/stats.h"
namespace disk_cache {
@@ -78,4 +81,4 @@ class NET_EXPORT TracingCacheBackend : public Backend,
} // namespace disk_cache
-#endif // NET_DISK_CACHE_TRACING_CACHE_BACKEND_H_
+#endif // NET_DISK_CACHE_TRACING_TRACING_CACHE_BACKEND_H_
diff --git a/chromium/net/dns/address_sorter_posix_unittest.cc b/chromium/net/dns/address_sorter_posix_unittest.cc
index c4517379957..dea7ade0fea 100644
--- a/chromium/net/dns/address_sorter_posix_unittest.cc
+++ b/chromium/net/dns/address_sorter_posix_unittest.cc
@@ -43,11 +43,11 @@ class TestUDPClientSocket : public DatagramClientSocket {
NOTIMPLEMENTED();
return OK;
}
- virtual bool SetReceiveBufferSize(int32) OVERRIDE {
- return true;
+ virtual int SetReceiveBufferSize(int32) OVERRIDE {
+ return OK;
}
- virtual bool SetSendBufferSize(int32) OVERRIDE {
- return true;
+ virtual int SetSendBufferSize(int32) OVERRIDE {
+ return OK;
}
virtual void Close() OVERRIDE {}
diff --git a/chromium/net/dns/address_sorter_win.cc b/chromium/net/dns/address_sorter_win.cc
index 3e1afa734cc..813bd93c38b 100644
--- a/chromium/net/dns/address_sorter_win.cc
+++ b/chromium/net/dns/address_sorter_win.cc
@@ -124,8 +124,8 @@ class AddressSorterWin : public AddressSorter {
const CallbackType callback_;
const size_t buffer_size_;
- scoped_ptr_malloc<SOCKET_ADDRESS_LIST> input_buffer_;
- scoped_ptr_malloc<SOCKET_ADDRESS_LIST> output_buffer_;
+ scoped_ptr<SOCKET_ADDRESS_LIST, base::FreeDeleter> input_buffer_;
+ scoped_ptr<SOCKET_ADDRESS_LIST, base::FreeDeleter> output_buffer_;
bool success_;
DISALLOW_COPY_AND_ASSIGN(Job);
diff --git a/chromium/net/dns/dns_config_service.cc b/chromium/net/dns/dns_config_service.cc
index 66131825571..bfa4b8db84e 100644
--- a/chromium/net/dns/dns_config_service.cc
+++ b/chromium/net/dns/dns_config_service.cc
@@ -8,9 +8,91 @@
#include "base/metrics/histogram.h"
#include "base/values.h"
#include "net/base/ip_endpoint.h"
+#include "net/base/ip_pattern.h"
namespace net {
+NameServerClassifier::NameServerClassifier() {
+ // Google Public DNS addresses from:
+ // https://developers.google.com/speed/public-dns/docs/using
+ AddRule("8.8.8.8", NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS);
+ AddRule("8.8.4.4", NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS);
+ AddRule("2001:4860:4860:0:0:0:0:8888", NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS),
+ AddRule("2001:4860:4860:0:0:0:0:8844", NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS),
+
+ // Count localhost as private, since we don't know what upstream it uses:
+ AddRule("127.*.*.*", NAME_SERVERS_TYPE_PRIVATE);
+ AddRule("0:0:0:0:0:0:0:1", NAME_SERVERS_TYPE_PRIVATE);
+
+ // RFC 1918 private addresses:
+ AddRule("10.*.*.*", NAME_SERVERS_TYPE_PRIVATE);
+ AddRule("172.[16-31].*.*", NAME_SERVERS_TYPE_PRIVATE);
+ AddRule("192.168.*.*", NAME_SERVERS_TYPE_PRIVATE);
+
+ // IPv4 link-local addresses:
+ AddRule("169.254.*.*", NAME_SERVERS_TYPE_PRIVATE);
+
+ // IPv6 link-local addresses:
+ AddRule("fe80:*:*:*:*:*:*:*", NAME_SERVERS_TYPE_PRIVATE);
+
+ // Anything else counts as public:
+ AddRule("*.*.*.*", NAME_SERVERS_TYPE_PUBLIC);
+ AddRule("*:*:*:*:*:*:*:*", NAME_SERVERS_TYPE_PUBLIC);
+}
+
+NameServerClassifier::~NameServerClassifier() {}
+
+NameServerClassifier::NameServersType NameServerClassifier::GetNameServersType(
+ const std::vector<IPEndPoint>& nameservers) const {
+ NameServersType type = NAME_SERVERS_TYPE_NONE;
+ for (std::vector<IPEndPoint>::const_iterator it = nameservers.begin();
+ it != nameservers.end();
+ ++it) {
+ type = MergeNameServersTypes(type, GetNameServerType(it->address()));
+ }
+ return type;
+}
+
+struct NameServerClassifier::NameServerTypeRule {
+ NameServerTypeRule(const char* pattern_string, NameServersType type)
+ : type(type) {
+ bool parsed = pattern.ParsePattern(pattern_string);
+ DCHECK(parsed);
+ }
+
+ IPPattern pattern;
+ NameServersType type;
+};
+
+void NameServerClassifier::AddRule(const char* pattern_string,
+ NameServersType address_type) {
+ rules_.push_back(new NameServerTypeRule(pattern_string, address_type));
+}
+
+NameServerClassifier::NameServersType NameServerClassifier::GetNameServerType(
+ const IPAddressNumber& address) const {
+ for (ScopedVector<NameServerTypeRule>::const_iterator it = rules_.begin();
+ it != rules_.end();
+ ++it) {
+ if ((*it)->pattern.Match(address))
+ return (*it)->type;
+ }
+ NOTREACHED();
+ return NAME_SERVERS_TYPE_NONE;
+}
+
+NameServerClassifier::NameServersType
+NameServerClassifier::MergeNameServersTypes(NameServersType a,
+ NameServersType b) {
+ if (a == NAME_SERVERS_TYPE_NONE)
+ return b;
+ if (b == NAME_SERVERS_TYPE_NONE)
+ return a;
+ if (a == b)
+ return a;
+ return NAME_SERVERS_TYPE_MIXED;
+}
+
// Default values are taken from glibc resolv.h except timeout which is set to
// |kDnsTimeoutSeconds|.
DnsConfig::DnsConfig()
@@ -153,6 +235,10 @@ void DnsConfigService::OnConfigRead(const DnsConfig& config) {
base::TimeTicks::Now() - last_sent_empty_time_);
}
UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigChange", changed);
+ UMA_HISTOGRAM_ENUMERATION(
+ "AsyncDNS.NameServersType",
+ classifier_.GetNameServersType(dns_config_.nameservers),
+ NameServerClassifier::NAME_SERVERS_TYPE_MAX_VALUE);
have_config_ = true;
if (have_hosts_ || watch_failed_)
diff --git a/chromium/net/dns/dns_config_service.h b/chromium/net/dns/dns_config_service.h
index 7386e601790..bfb57850bb7 100644
--- a/chromium/net/dns/dns_config_service.h
+++ b/chromium/net/dns/dns_config_service.h
@@ -11,6 +11,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/threading/non_thread_safe.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -31,6 +32,37 @@ namespace net {
// TODO(szym): Remove code which reads timeout from system.
const unsigned kDnsTimeoutSeconds = 1;
+// Classifies nameserver address lists for histograms.
+class NET_EXPORT_PRIVATE NameServerClassifier {
+ public:
+ // This is used in a histogram (AsyncDNS.NameServersType); add new entries
+ // right before MAX_VALUE.
+ enum NameServersType {
+ NAME_SERVERS_TYPE_NONE,
+ NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS,
+ NAME_SERVERS_TYPE_PRIVATE,
+ NAME_SERVERS_TYPE_PUBLIC,
+ NAME_SERVERS_TYPE_MIXED,
+ NAME_SERVERS_TYPE_MAX_VALUE
+ };
+
+ NameServerClassifier();
+ ~NameServerClassifier();
+
+ NameServersType GetNameServersType(
+ const std::vector<net::IPEndPoint>& nameservers) const;
+
+ private:
+ struct NameServerTypeRule;
+
+ void AddRule(const char* pattern_string, NameServersType type);
+ NameServersType GetNameServerType(const IPAddressNumber& address) const;
+ static NameServersType MergeNameServersTypes(NameServersType a,
+ NameServersType b);
+
+ ScopedVector<NameServerTypeRule> rules_;
+};
+
// DnsConfig stores configuration of the system resolver.
struct NET_EXPORT_PRIVATE DnsConfig {
DnsConfig();
@@ -90,7 +122,6 @@ struct NET_EXPORT_PRIVATE DnsConfig {
bool use_local_ipv6;
};
-
// Service for reading system DNS settings, on demand or when signalled by
// internal watchers and NetworkChangeNotifier.
class NET_EXPORT_PRIVATE DnsConfigService
@@ -175,6 +206,8 @@ class NET_EXPORT_PRIVATE DnsConfigService
// Started in Invalidate*, cleared in On*Read.
base::OneShotTimer<DnsConfigService> timer_;
+ NameServerClassifier classifier_;
+
DISALLOW_COPY_AND_ASSIGN(DnsConfigService);
};
diff --git a/chromium/net/dns/dns_config_service_posix.cc b/chromium/net/dns/dns_config_service_posix.cc
index b352d3269c0..2c298ddedbb 100644
--- a/chromium/net/dns/dns_config_service_posix.cc
+++ b/chromium/net/dns/dns_config_service_posix.cc
@@ -25,19 +25,28 @@
#include "net/dns/dns_config_watcher_mac.h"
#endif
+#if defined(OS_ANDROID)
+#include <sys/system_properties.h>
+#include "net/base/network_change_notifier.h"
+#endif
+
namespace net {
-#if !defined(OS_ANDROID)
namespace internal {
namespace {
+#if !defined(OS_ANDROID)
const base::FilePath::CharType* kFilePathHosts =
FILE_PATH_LITERAL("/etc/hosts");
+#else
+const base::FilePath::CharType* kFilePathHosts =
+ FILE_PATH_LITERAL("/system/etc/hosts");
+#endif
#if defined(OS_IOS)
-// There is no plublic API to watch the DNS configuration on iOS.
+// There is no public API to watch the DNS configuration on iOS.
class DnsConfigWatcher {
public:
typedef base::Callback<void(bool succeeded)> CallbackType;
@@ -47,6 +56,32 @@ class DnsConfigWatcher {
}
};
+#elif defined(OS_ANDROID)
+// On Android, assume DNS config may have changed on every network change.
+class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver {
+ public:
+ DnsConfigWatcher() {
+ NetworkChangeNotifier::AddNetworkChangeObserver(this);
+ }
+
+ virtual ~DnsConfigWatcher() {
+ NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+ }
+
+ bool Watch(const base::Callback<void(bool succeeded)>& callback) {
+ callback_ = callback;
+ return true;
+ }
+
+ virtual void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type)
+ OVERRIDE {
+ if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE)
+ callback_.Run(true);
+ }
+
+ private:
+ base::Callback<void(bool succeeded)> callback_;
+};
#elif !defined(OS_MACOSX)
// DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}.
@@ -78,6 +113,7 @@ class DnsConfigWatcher {
};
#endif
+#if !defined(OS_ANDROID)
ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
ConfigParsePosixResult result;
config->unhandled_options = false;
@@ -122,14 +158,52 @@ ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds);
return result;
}
+#else // defined(OS_ANDROID)
+// Theoretically, this is bad. __system_property_get is not a supported API
+// (but it's currently visible to anyone using Bionic), and the properties
+// are implementation details that may disappear in future Android releases.
+// Practically, libcutils provides property_get, which is a public API, and the
+// DNS code (and its clients) are already robust against failing to get the DNS
+// config for whatever reason, so the properties can disappear and the world
+// won't end.
+// TODO(ttuttle): Depend on libcutils, then switch this (and other uses of
+// __system_property_get) to property_get.
+ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) {
+ std::string dns1_string, dns2_string;
+ char property_value[PROP_VALUE_MAX];
+ __system_property_get("net.dns1", property_value);
+ dns1_string = property_value;
+ __system_property_get("net.dns2", property_value);
+ dns2_string = property_value;
+ if (dns1_string.length() == 0 && dns2_string.length() == 0)
+ return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
+
+ IPAddressNumber dns1_number, dns2_number;
+ bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number);
+ bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number);
+ if (!parsed1 && !parsed2)
+ return CONFIG_PARSE_POSIX_BAD_ADDRESS;
+
+ if (parsed1) {
+ IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort);
+ dns_config->nameservers.push_back(dns1);
+ }
+ if (parsed2) {
+ IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort);
+ dns_config->nameservers.push_back(dns2);
+ }
+
+ return CONFIG_PARSE_POSIX_OK;
+}
+#endif
} // namespace
class DnsConfigServicePosix::Watcher {
public:
explicit Watcher(DnsConfigServicePosix* service)
- : weak_factory_(this),
- service_(service) {}
+ : service_(service),
+ weak_factory_(this) {}
~Watcher() {}
bool Watch() {
@@ -172,16 +246,18 @@ class DnsConfigServicePosix::Watcher {
service_->OnHostsChanged(!error);
}
- base::WeakPtrFactory<Watcher> weak_factory_;
DnsConfigServicePosix* service_;
DnsConfigWatcher config_watcher_;
base::FilePathWatcher hosts_watcher_;
+ base::WeakPtrFactory<Watcher> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(Watcher);
};
// A SerialWorker that uses libresolv to initialize res_state and converts
-// it to DnsConfig.
+// it to DnsConfig (except on Android, where it reads system properties
+// net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
class DnsConfigServicePosix::ConfigReader : public SerialWorker {
public:
explicit ConfigReader(DnsConfigServicePosix* service)
@@ -311,6 +387,7 @@ void DnsConfigServicePosix::OnHostsChanged(bool succeeded) {
}
}
+#if !defined(OS_ANDROID)
ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
DnsConfig* dns_config) {
CHECK(dns_config != NULL);
@@ -410,6 +487,7 @@ ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
}
return CONFIG_PARSE_POSIX_OK;
}
+#endif // !defined(OS_ANDROID)
} // namespace internal
@@ -418,20 +496,4 @@ scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix());
}
-#else // defined(OS_ANDROID)
-// Android NDK provides only a stub <resolv.h> header.
-class StubDnsConfigService : public DnsConfigService {
- public:
- StubDnsConfigService() {}
- virtual ~StubDnsConfigService() {}
- private:
- virtual void ReadNow() OVERRIDE {}
- virtual bool StartWatching() OVERRIDE { return false; }
-};
-// static
-scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
- return scoped_ptr<DnsConfigService>(new StubDnsConfigService());
-}
-#endif
-
} // namespace net
diff --git a/chromium/net/dns/dns_config_service_posix.h b/chromium/net/dns/dns_config_service_posix.h
index be19ab90b08..ef28154c364 100644
--- a/chromium/net/dns/dns_config_service_posix.h
+++ b/chromium/net/dns/dns_config_service_posix.h
@@ -5,9 +5,11 @@
#ifndef NET_DNS_DNS_CONFIG_SERVICE_POSIX_H_
#define NET_DNS_DNS_CONFIG_SERVICE_POSIX_H_
+#if !defined(OS_ANDROID)
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
+#endif
#include "base/compiler_specific.h"
#include "net/base/net_export.h"
@@ -57,9 +59,11 @@ enum ConfigParsePosixResult {
CONFIG_PARSE_POSIX_MAX // Bounding values for enumeration.
};
+#if !defined(OS_ANDROID)
// Fills in |dns_config| from |res|.
ConfigParsePosixResult NET_EXPORT_PRIVATE ConvertResStateToDnsConfig(
const struct __res_state& res, DnsConfig* dns_config);
+#endif
} // namespace internal
diff --git a/chromium/net/dns/dns_config_service_posix_unittest.cc b/chromium/net/dns/dns_config_service_posix_unittest.cc
index 92e46ed1977..1bfbe0892af 100644
--- a/chromium/net/dns/dns_config_service_posix_unittest.cc
+++ b/chromium/net/dns/dns_config_service_posix_unittest.cc
@@ -9,6 +9,8 @@
#include "testing/gtest/include/gtest/gtest.h"
+#if !defined(OS_ANDROID)
+
namespace net {
namespace {
@@ -154,3 +156,5 @@ TEST(DnsConfigServicePosixTest, RejectEmptyNameserver) {
} // namespace
} // namespace net
+
+#endif // !OS_ANDROID
diff --git a/chromium/net/dns/dns_config_service_unittest.cc b/chromium/net/dns/dns_config_service_unittest.cc
index f42068032de..e71baefb4ed 100644
--- a/chromium/net/dns/dns_config_service_unittest.cc
+++ b/chromium/net/dns/dns_config_service_unittest.cc
@@ -9,13 +9,107 @@
#include "base/cancelable_callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
#include "base/test/test_timeouts.h"
+#include "net/base/net_util.h"
+#include "net/dns/dns_protocol.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
+const NameServerClassifier::NameServersType kNone =
+ NameServerClassifier::NAME_SERVERS_TYPE_NONE;
+const NameServerClassifier::NameServersType kGoogle =
+ NameServerClassifier::NAME_SERVERS_TYPE_GOOGLE_PUBLIC_DNS;
+const NameServerClassifier::NameServersType kPrivate =
+ NameServerClassifier::NAME_SERVERS_TYPE_PRIVATE;
+const NameServerClassifier::NameServersType kPublic =
+ NameServerClassifier::NAME_SERVERS_TYPE_PUBLIC;
+const NameServerClassifier::NameServersType kMixed =
+ NameServerClassifier::NAME_SERVERS_TYPE_MIXED;
+
+class NameServerClassifierTest : public testing::Test {
+ protected:
+ NameServerClassifier::NameServersType Classify(
+ const std::string& servers_string) {
+ std::vector<std::string> server_strings;
+ base::SplitString(servers_string, ' ', &server_strings);
+
+ std::vector<IPEndPoint> servers;
+ for (std::vector<std::string>::const_iterator it = server_strings.begin();
+ it != server_strings.end();
+ ++it) {
+ if (*it == "")
+ continue;
+
+ IPAddressNumber address;
+ bool parsed = ParseIPLiteralToNumber(*it, &address);
+ EXPECT_TRUE(parsed);
+ servers.push_back(IPEndPoint(address, dns_protocol::kDefaultPort));
+ }
+
+ return classifier_.GetNameServersType(servers);
+ }
+
+ private:
+ NameServerClassifier classifier_;
+};
+
+TEST_F(NameServerClassifierTest, None) {
+ EXPECT_EQ(kNone, Classify(""));
+}
+
+TEST_F(NameServerClassifierTest, Google) {
+ EXPECT_EQ(kGoogle, Classify("8.8.8.8"));
+ EXPECT_EQ(kGoogle, Classify("8.8.8.8 8.8.4.4"));
+ EXPECT_EQ(kGoogle, Classify("2001:4860:4860::8888"));
+ EXPECT_EQ(kGoogle, Classify("2001:4860:4860::8888 2001:4860:4860::8844"));
+ EXPECT_EQ(kGoogle, Classify("2001:4860:4860::8888 8.8.8.8"));
+
+ // Make sure nobody took any shortcuts on the IP matching:
+ EXPECT_EQ(kPublic, Classify("8.8.8.4"));
+ EXPECT_EQ(kPublic, Classify("8.8.4.8"));
+ EXPECT_EQ(kPublic, Classify("2001:4860:4860::8884"));
+ EXPECT_EQ(kPublic, Classify("2001:4860:4860::8848"));
+ EXPECT_EQ(kPublic, Classify("2001:4860:4860::1:8888"));
+ EXPECT_EQ(kPublic, Classify("2001:4860:4860:1::8888"));
+}
+
+TEST_F(NameServerClassifierTest, PrivateLocalhost) {
+ EXPECT_EQ(kPrivate, Classify("127.0.0.1"));
+ EXPECT_EQ(kPrivate, Classify("::1"));
+}
+
+TEST_F(NameServerClassifierTest, PrivateRfc1918) {
+ EXPECT_EQ(kPrivate, Classify("10.0.0.0 10.255.255.255"));
+ EXPECT_EQ(kPrivate, Classify("172.16.0.0 172.31.255.255"));
+ EXPECT_EQ(kPrivate, Classify("192.168.0.0 192.168.255.255"));
+ EXPECT_EQ(kPrivate, Classify("10.1.1.1 172.16.1.1 192.168.1.1"));
+}
+
+TEST_F(NameServerClassifierTest, PrivateIPv4LinkLocal) {
+ EXPECT_EQ(kPrivate, Classify("169.254.0.0 169.254.255.255"));
+}
+
+TEST_F(NameServerClassifierTest, PrivateIPv6LinkLocal) {
+ EXPECT_EQ(kPrivate,
+ Classify("fe80:: fe80:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+}
+
+TEST_F(NameServerClassifierTest, Public) {
+ EXPECT_EQ(kPublic, Classify("4.2.2.1"));
+ EXPECT_EQ(kPublic, Classify("4.2.2.1 4.2.2.2"));
+}
+
+TEST_F(NameServerClassifierTest, Mixed) {
+ EXPECT_EQ(kMixed, Classify("8.8.8.8 192.168.1.1"));
+ EXPECT_EQ(kMixed, Classify("8.8.8.8 4.2.2.1"));
+ EXPECT_EQ(kMixed, Classify("192.168.1.1 4.2.2.1"));
+ EXPECT_EQ(kMixed, Classify("8.8.8.8 192.168.1.1 4.2.2.1"));
+}
+
class DnsConfigServiceTest : public testing::Test {
public:
void OnConfigChanged(const DnsConfig& config) {
diff --git a/chromium/net/dns/dns_config_service_win.cc b/chromium/net/dns/dns_config_service_win.cc
index fe97b74f73e..4d837f0a004 100644
--- a/chromium/net/dns/dns_config_service_win.cc
+++ b/chromium/net/dns/dns_config_service_win.cc
@@ -113,16 +113,16 @@ class RegistryReader : public base::NonThreadSafe {
};
// Wrapper for GetAdaptersAddresses. Returns NULL if failed.
-scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) {
+scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> ReadIpHelper(ULONG flags) {
base::ThreadRestrictions::AssertIOAllowed();
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> out;
+ scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out;
ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
UINT rv = ERROR_BUFFER_OVERFLOW;
// Try up to three times.
for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
tries++) {
- out.reset(reinterpret_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
+ out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, out.get(), &len);
}
if (rv != NO_ERROR)
@@ -139,23 +139,23 @@ bool ParseDomainASCII(const base::string16& widestr, std::string* domain) {
return false;
// Check if already ASCII.
- if (IsStringASCII(widestr)) {
- *domain = UTF16ToASCII(widestr);
+ if (base::IsStringASCII(widestr)) {
+ *domain = base::UTF16ToASCII(widestr);
return true;
}
// Otherwise try to convert it from IDN to punycode.
const int kInitialBufferSize = 256;
- url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode;
- if (!url_canon::IDNToASCII(widestr.data(), widestr.length(), &punycode))
+ url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode;
+ if (!url::IDNToASCII(widestr.data(), widestr.length(), &punycode))
return false;
// |punycode_output| should now be ASCII; convert it to a std::string.
// (We could use UTF16ToASCII() instead, but that requires an extra string
// copy. Since ASCII is a subset of UTF8 the following is equivalent).
- bool success = UTF16ToUTF8(punycode.data(), punycode.length(), domain);
+ bool success = base::UTF16ToUTF8(punycode.data(), punycode.length(), domain);
DCHECK(success);
- DCHECK(IsStringASCII(*domain));
+ DCHECK(base::IsStringASCII(*domain));
return success && !domain->empty();
}
@@ -250,7 +250,7 @@ HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) {
if (have_ipv4 && have_ipv6)
return HOSTS_PARSE_WIN_OK;
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses =
+ scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses =
ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_MULTICAST |
diff --git a/chromium/net/dns/dns_config_service_win.h b/chromium/net/dns/dns_config_service_win.h
index 9503dc8593f..d26aed54595 100644
--- a/chromium/net/dns/dns_config_service_win.h
+++ b/chromium/net/dns/dns_config_service_win.h
@@ -65,7 +65,7 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings {
// Filled in by GetAdapterAddresses. Note that the alternative
// GetNetworkParams does not include IPv6 addresses.
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses;
+ scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses;
// SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\SearchList
RegString policy_search_list;
diff --git a/chromium/net/dns/dns_config_service_win_unittest.cc b/chromium/net/dns/dns_config_service_win_unittest.cc
index 3f3e4ed1e37..7ac01d1e536 100644
--- a/chromium/net/dns/dns_config_service_win_unittest.cc
+++ b/chromium/net/dns/dns_config_service_win_unittest.cc
@@ -58,7 +58,7 @@ struct AdapterInfo {
int ports[4];
};
-scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> CreateAdapterAddresses(
+scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> CreateAdapterAddresses(
const AdapterInfo* infos) {
size_t num_adapters = 0;
size_t num_addresses = 0;
@@ -72,8 +72,8 @@ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> CreateAdapterAddresses(
size_t heap_size = num_adapters * sizeof(IP_ADAPTER_ADDRESSES) +
num_addresses * (sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) +
sizeof(struct sockaddr_storage));
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> heap(
- reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(heap_size)));
+ scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> heap(
+ static_cast<IP_ADAPTER_ADDRESSES*>(malloc(heap_size)));
CHECK(heap.get());
memset(heap.get(), 0, heap_size);
diff --git a/chromium/net/dns/dns_config_watcher_mac.cc b/chromium/net/dns/dns_config_watcher_mac.cc
index a92db0cf179..0139472cbd1 100644
--- a/chromium/net/dns/dns_config_watcher_mac.cc
+++ b/chromium/net/dns/dns_config_watcher_mac.cc
@@ -102,5 +102,5 @@ ConfigParsePosixResult DnsConfigWatcher::CheckDnsConfig() {
return CONFIG_PARSE_POSIX_OK;
}
-} // naespace internal
+} // namespace internal
} // namespace net
diff --git a/chromium/net/dns/dns_config_watcher_mac.h b/chromium/net/dns/dns_config_watcher_mac.h
index 0c3fc6a1a20..d3e9924626c 100644
--- a/chromium/net/dns/dns_config_watcher_mac.h
+++ b/chromium/net/dns/dns_config_watcher_mac.h
@@ -22,5 +22,5 @@ class DnsConfigWatcher {
NotifyWatcherMac watcher_;
};
-} // naespace internal
+} // namespace internal
} // namespace net
diff --git a/chromium/net/dns/dns_query.cc b/chromium/net/dns/dns_query.cc
index 270757e7be9..530508a61de 100644
--- a/chromium/net/dns/dns_query.cc
+++ b/chromium/net/dns/dns_query.cc
@@ -6,8 +6,8 @@
#include <limits>
+#include "base/big_endian.h"
#include "base/sys_byteorder.h"
-#include "net/base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/base/io_buffer.h"
#include "net/dns/dns_protocol.h"
@@ -33,7 +33,8 @@ DnsQuery::DnsQuery(uint16 id, const base::StringPiece& qname, uint16 qtype)
header->qdcount = base::HostToNet16(1);
// Write question section after the header.
- BigEndianWriter writer(reinterpret_cast<char*>(header + 1), question_size);
+ base::BigEndianWriter writer(reinterpret_cast<char*>(header + 1),
+ question_size);
writer.WriteBytes(qname.data(), qname.size());
writer.WriteU16(qtype);
writer.WriteU16(dns_protocol::kClassIN);
@@ -59,9 +60,8 @@ base::StringPiece DnsQuery::qname() const {
uint16 DnsQuery::qtype() const {
uint16 type;
- ReadBigEndian<uint16>(io_buffer_->data() +
- sizeof(dns_protocol::Header) +
- qname_size_, &type);
+ base::ReadBigEndian<uint16>(
+ io_buffer_->data() + sizeof(dns_protocol::Header) + qname_size_, &type);
return type;
}
diff --git a/chromium/net/dns/dns_response.cc b/chromium/net/dns/dns_response.cc
index d29d3c4813c..64db818b2b9 100644
--- a/chromium/net/dns/dns_response.cc
+++ b/chromium/net/dns/dns_response.cc
@@ -4,10 +4,10 @@
#include "net/dns/dns_response.h"
+#include "base/big_endian.h"
#include "base/strings/string_util.h"
#include "base/sys_byteorder.h"
#include "net/base/address_list.h"
-#include "net/base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -73,7 +73,7 @@ unsigned DnsRecordParser::ReadName(const void* const vpos,
if (seen > length_)
return 0;
uint16 offset;
- ReadBigEndian<uint16>(p, &offset);
+ base::ReadBigEndian<uint16>(p, &offset);
offset &= dns_protocol::kOffsetMask;
p = packet_ + offset;
if (p >= end)
@@ -113,8 +113,8 @@ bool DnsRecordParser::ReadRecord(DnsResourceRecord* out) {
size_t consumed = ReadName(cur_, &out->name);
if (!consumed)
return false;
- BigEndianReader reader(cur_ + consumed,
- packet_ + length_ - (cur_ + consumed));
+ base::BigEndianReader reader(cur_ + consumed,
+ packet_ + length_ - (cur_ + consumed));
uint16 rdlen;
if (reader.ReadU16(&out->type) &&
reader.ReadU16(&out->klass) &&
@@ -251,7 +251,7 @@ uint16 DnsResponse::qtype() const {
// QTYPE starts where QNAME ends.
const size_t type_offset = parser_.GetOffset() - 2 * sizeof(uint16);
uint16 type;
- ReadBigEndian<uint16>(io_buffer_->data() + type_offset, &type);
+ base::ReadBigEndian<uint16>(io_buffer_->data() + type_offset, &type);
return type;
}
diff --git a/chromium/net/dns/dns_test_util.cc b/chromium/net/dns/dns_test_util.cc
index 63014213faa..01acb346fa9 100644
--- a/chromium/net/dns/dns_test_util.cc
+++ b/chromium/net/dns/dns_test_util.cc
@@ -6,11 +6,11 @@
#include <string>
+#include "base/big_endian.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/sys_byteorder.h"
-#include "net/base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -119,7 +119,7 @@ class MockTransaction : public DnsTransaction,
// Write answer with loopback IP address.
header->ancount = base::HostToNet16(1);
- BigEndianWriter writer(buffer + nbytes, answer_size);
+ base::BigEndianWriter writer(buffer + nbytes, answer_size);
writer.WriteU16(kPointerToQueryName);
writer.WriteU16(qtype_);
writer.WriteU16(net::dns_protocol::kClassIN);
diff --git a/chromium/net/dns/dns_transaction.cc b/chromium/net/dns/dns_transaction.cc
index 170ea678d4b..130922c6025 100644
--- a/chromium/net/dns/dns_transaction.cc
+++ b/chromium/net/dns/dns_transaction.cc
@@ -8,6 +8,7 @@
#include <string>
#include <vector>
+#include "base/big_endian.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@@ -21,7 +22,6 @@
#include "base/threading/non_thread_safe.h"
#include "base/timer/timer.h"
#include "base/values.h"
-#include "net/base/big_endian.h"
#include "net/base/completion_callback.h"
#include "net/base/dns_util.h"
#include "net/base/io_buffer.h"
@@ -388,7 +388,8 @@ class DnsTCPAttempt : public DnsAttempt {
if (rv < 0)
return rv;
- WriteBigEndian<uint16>(length_buffer_->data(), query_->io_buffer()->size());
+ base::WriteBigEndian<uint16>(length_buffer_->data(),
+ query_->io_buffer()->size());
buffer_ =
new DrainableIOBuffer(length_buffer_.get(), length_buffer_->size());
next_state_ = STATE_SEND_LENGTH;
@@ -446,7 +447,7 @@ class DnsTCPAttempt : public DnsAttempt {
buffer_->BytesRemaining(),
base::Bind(&DnsTCPAttempt::OnIOComplete, base::Unretained(this)));
}
- ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
+ base::ReadBigEndian<uint16>(length_buffer_->data(), &response_length_);
// Check if advertised response is too short. (Optimization only.)
if (response_length_ < query_->io_buffer()->size())
return ERR_DNS_MALFORMED_RESPONSE;
diff --git a/chromium/net/dns/dns_transaction_unittest.cc b/chromium/net/dns/dns_transaction_unittest.cc
index 7040e44be16..67a045719b0 100644
--- a/chromium/net/dns/dns_transaction_unittest.cc
+++ b/chromium/net/dns/dns_transaction_unittest.cc
@@ -10,7 +10,6 @@
#include "base/rand_util.h"
#include "base/sys_byteorder.h"
#include "base/test/test_timeouts.h"
-#include "net/base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/base/net_log.h"
#include "net/dns/dns_protocol.h"
diff --git a/chromium/net/dns/host_resolver.cc b/chromium/net/dns/host_resolver.cc
index 0435091a54f..3b6ea04d778 100644
--- a/chromium/net/dns/host_resolver.cc
+++ b/chromium/net/dns/host_resolver.cc
@@ -24,10 +24,11 @@ namespace {
// that limit this to 6, so we're temporarily holding it at that level.
const size_t kDefaultMaxProcTasks = 6u;
-PrioritizedDispatcher::Limits GetDispatcherLimits(
- const HostResolver::Options& options) {
- PrioritizedDispatcher::Limits limits(NUM_PRIORITIES,
- options.max_concurrent_resolves);
+} // namespace
+
+PrioritizedDispatcher::Limits HostResolver::Options::GetDispatcherLimits()
+ const {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, max_concurrent_resolves);
// If not using default, do not use the field trial.
if (limits.total_jobs != HostResolver::kDefaultParallelism)
@@ -82,8 +83,6 @@ PrioritizedDispatcher::Limits GetDispatcherLimits(
return limits;
}
-} // namespace
-
HostResolver::Options::Options()
: max_concurrent_resolves(kDefaultParallelism),
max_retry_attempts(kDefaultRetryAttempts),
@@ -95,7 +94,8 @@ HostResolver::RequestInfo::RequestInfo(const HostPortPair& host_port_pair)
address_family_(ADDRESS_FAMILY_UNSPECIFIED),
host_resolver_flags_(0),
allow_cached_response_(true),
- is_speculative_(false) {}
+ is_speculative_(false),
+ is_my_ip_address_(false) {}
HostResolver::~HostResolver() {
}
@@ -116,22 +116,15 @@ base::Value* HostResolver::GetDnsConfigAsValue() const {
}
// static
-scoped_ptr<HostResolver>
-HostResolver::CreateSystemResolver(const Options& options, NetLog* net_log) {
- scoped_ptr<HostCache> cache;
- if (options.enable_caching)
- cache = HostCache::CreateDefaultCache();
- return scoped_ptr<HostResolver>(new HostResolverImpl(
- cache.Pass(),
- GetDispatcherLimits(options),
- HostResolverImpl::ProcTaskParams(NULL, options.max_retry_attempts),
- net_log));
+scoped_ptr<HostResolver> HostResolver::CreateSystemResolver(
+ const Options& options,
+ NetLog* net_log) {
+ return scoped_ptr<HostResolver>(new HostResolverImpl(options, net_log));
}
// static
-scoped_ptr<HostResolver>
-HostResolver::CreateDefaultResolver(NetLog* net_log) {
- return CreateSystemResolver(Options(), net_log);
+scoped_ptr<HostResolver> HostResolver::CreateDefaultResolver(NetLog* net_log) {
+ return scoped_ptr<HostResolver>(new HostResolverImpl(Options(), net_log));
}
HostResolver::HostResolver() {
diff --git a/chromium/net/dns/host_resolver.h b/chromium/net/dns/host_resolver.h
index 2964fe600df..5b3b3c7a7c6 100644
--- a/chromium/net/dns/host_resolver.h
+++ b/chromium/net/dns/host_resolver.h
@@ -13,6 +13,7 @@
#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
#include "net/base/net_util.h"
+#include "net/base/prioritized_dispatcher.h"
#include "net/base/request_priority.h"
namespace base {
@@ -48,6 +49,8 @@ class NET_EXPORT HostResolver {
struct NET_EXPORT Options {
Options();
+ PrioritizedDispatcher::Limits GetDispatcherLimits() const;
+
size_t max_concurrent_resolves;
size_t max_retry_attempts;
bool enable_caching;
@@ -85,6 +88,9 @@ class NET_EXPORT HostResolver {
bool is_speculative() const { return is_speculative_; }
void set_is_speculative(bool b) { is_speculative_ = b; }
+ bool is_my_ip_address() const { return is_my_ip_address_; }
+ void set_is_my_ip_address(bool b) { is_my_ip_address_ = b; }
+
private:
// The hostname to resolve, and the port to use in resulting sockaddrs.
HostPortPair host_port_pair_;
@@ -100,18 +106,20 @@ class NET_EXPORT HostResolver {
// Whether this request was started by the DNS prefetcher.
bool is_speculative_;
+
+ // Indicates a request for myIpAddress (to differentiate from other requests
+ // for localhost, currently used by Chrome OS).
+ bool is_my_ip_address_;
};
// Opaque type used to cancel a request.
typedef void* RequestHandle;
- // This value can be passed into CreateSystemResolver as the
- // |max_concurrent_resolves| parameter. It will select a default level of
- // concurrency.
+ // Set Options.max_concurrent_resolves to this to select a default level
+ // of concurrency.
static const size_t kDefaultParallelism = 0;
- // This value can be passed into CreateSystemResolver as the
- // |max_retry_attempts| parameter.
+ // Set Options.max_retry_attempts to this to select a default retry value.
static const size_t kDefaultRetryAttempts = -1;
// If any completion callbacks are pending when the resolver is destroyed,
diff --git a/chromium/net/dns/host_resolver_impl.cc b/chromium/net/dns/host_resolver_impl.cc
index 57ab3d63881..d5602a4c674 100644
--- a/chromium/net/dns/host_resolver_impl.cc
+++ b/chromium/net/dns/host_resolver_impl.cc
@@ -307,7 +307,7 @@ base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number,
(LPWSTR)&error_string,
0, // Buffer size.
0); // Arguments (unused).
- dict->SetString("os_error_string", WideToUTF8(error_string));
+ dict->SetString("os_error_string", base::WideToUTF8(error_string));
LocalFree(error_string);
#endif
}
@@ -1258,9 +1258,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job,
DCHECK(!is_queued());
PrioritizedDispatcher::Handle handle;
if (!at_head) {
- handle = resolver_->dispatcher_.Add(this, priority());
+ handle = resolver_->dispatcher_->Add(this, priority());
} else {
- handle = resolver_->dispatcher_.AddAtHead(this, priority());
+ handle = resolver_->dispatcher_->AddAtHead(this, priority());
}
// The dispatcher could have started |this| in the above call to Add, which
// could have called Schedule again. In that case |handle| will be null,
@@ -1399,10 +1399,10 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job,
void ReduceToOneJobSlot() {
DCHECK_GE(num_occupied_job_slots_, 1u);
if (is_queued()) {
- resolver_->dispatcher_.Cancel(handle_);
+ resolver_->dispatcher_->Cancel(handle_);
handle_.Reset();
} else if (num_occupied_job_slots_ > 1) {
- resolver_->dispatcher_.OnJobFinished();
+ resolver_->dispatcher_->OnJobFinished();
--num_occupied_job_slots_;
}
DCHECK_EQ(1u, num_occupied_job_slots_);
@@ -1412,7 +1412,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job,
if (is_queued()) {
if (priority() != static_cast<RequestPriority>(handle_.priority()))
priority_change_time_ = base::TimeTicks::Now();
- handle_ = resolver_->dispatcher_.ChangePriority(handle_, priority());
+ handle_ = resolver_->dispatcher_->ChangePriority(handle_, priority());
}
}
@@ -1659,9 +1659,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job,
KillDnsTask();
// Signal dispatcher that a slot has opened.
- resolver_->dispatcher_.OnJobFinished();
+ resolver_->dispatcher_->OnJobFinished();
} else if (is_queued()) {
- resolver_->dispatcher_.Cancel(handle_);
+ resolver_->dispatcher_->Cancel(handle_);
handle_.Reset();
}
@@ -1783,38 +1783,36 @@ HostResolverImpl::ProcTaskParams::ProcTaskParams(
max_retry_attempts(max_retry_attempts),
unresponsive_delay(base::TimeDelta::FromMilliseconds(6000)),
retry_factor(2) {
+ // Maximum of 4 retry attempts for host resolution.
+ static const size_t kDefaultMaxRetryAttempts = 4u;
+ if (max_retry_attempts == HostResolver::kDefaultRetryAttempts)
+ max_retry_attempts = kDefaultMaxRetryAttempts;
}
HostResolverImpl::ProcTaskParams::~ProcTaskParams() {}
-HostResolverImpl::HostResolverImpl(
- scoped_ptr<HostCache> cache,
- const PrioritizedDispatcher::Limits& job_limits,
- const ProcTaskParams& proc_params,
- NetLog* net_log)
- : cache_(cache.Pass()),
- dispatcher_(job_limits),
- max_queued_jobs_(job_limits.total_jobs * 100u),
- proc_params_(proc_params),
+HostResolverImpl::HostResolverImpl(const Options& options, NetLog* net_log)
+ : max_queued_jobs_(0),
+ proc_params_(NULL, options.max_retry_attempts),
net_log_(net_log),
default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
- weak_ptr_factory_(this),
- probe_weak_ptr_factory_(this),
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) {
-
- DCHECK_GE(dispatcher_.num_priorities(), static_cast<size_t>(NUM_PRIORITIES));
+ fallback_to_proctask_(true),
+ weak_ptr_factory_(this),
+ probe_weak_ptr_factory_(this) {
+ if (options.enable_caching)
+ cache_ = HostCache::CreateDefaultCache();
- // Maximum of 4 retry attempts for host resolution.
- static const size_t kDefaultMaxRetryAttempts = 4u;
+ PrioritizedDispatcher::Limits job_limits = options.GetDispatcherLimits();
+ dispatcher_.reset(new PrioritizedDispatcher(job_limits));
+ max_queued_jobs_ = job_limits.total_jobs * 100u;
- if (proc_params_.max_retry_attempts == HostResolver::kDefaultRetryAttempts)
- proc_params_.max_retry_attempts = kDefaultMaxRetryAttempts;
+ DCHECK_GE(dispatcher_->num_priorities(), static_cast<size_t>(NUM_PRIORITIES));
#if defined(OS_WIN)
EnsureWinsockInit();
@@ -1842,7 +1840,7 @@ HostResolverImpl::HostResolverImpl(
HostResolverImpl::~HostResolverImpl() {
// Prevent the dispatcher from starting new jobs.
- dispatcher_.SetLimitsToZero();
+ dispatcher_->SetLimitsToZero();
// It's now safe for Jobs to call KillDsnTask on destruction, because
// OnJobComplete will not start any new jobs.
STLDeleteValues(&jobs_);
@@ -1852,7 +1850,7 @@ HostResolverImpl::~HostResolverImpl() {
}
void HostResolverImpl::SetMaxQueuedJobs(size_t value) {
- DCHECK_EQ(0u, dispatcher_.num_queued_jobs());
+ DCHECK_EQ(0u, dispatcher_->num_queued_jobs());
DCHECK_GT(value, 0u);
max_queued_jobs_ = value;
}
@@ -1900,8 +1898,8 @@ int HostResolverImpl::Resolve(const RequestInfo& info,
job->Schedule(false);
// Check for queue overflow.
- if (dispatcher_.num_queued_jobs() > max_queued_jobs_) {
- Job* evicted = static_cast<Job*>(dispatcher_.EvictOldestLowest());
+ if (dispatcher_->num_queued_jobs() > max_queued_jobs_) {
+ Job* evicted = static_cast<Job*>(dispatcher_->EvictOldestLowest());
DCHECK(evicted);
evicted->OnEvicted(); // Deletes |evicted|.
if (evicted == job) {
@@ -2155,7 +2153,12 @@ HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 };
IPAddressNumber address(kIPv6Address,
kIPv6Address + arraysize(kIPv6Address));
- bool rv6 = IsGloballyReachable(address, net_log);
+ BoundNetLog probe_net_log = BoundNetLog::Make(
+ net_log.net_log(), NetLog::SOURCE_IPV6_REACHABILITY_CHECK);
+ probe_net_log.BeginEvent(NetLog::TYPE_IPV6_REACHABILITY_CHECK,
+ net_log.source().ToEventParametersCallback());
+ bool rv6 = IsGloballyReachable(address, probe_net_log);
+ probe_net_log.EndEvent(NetLog::TYPE_IPV6_REACHABILITY_CHECK);
if (rv6)
net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_SUPPORTED);
@@ -2198,8 +2201,8 @@ void HostResolverImpl::AbortAllInProgressJobs() {
// aborting the old ones. This is needed so that it won't start the second
// DnsTransaction for a job in |jobs_to_abort| if the DnsConfig just became
// invalid.
- PrioritizedDispatcher::Limits limits = dispatcher_.GetLimits();
- dispatcher_.SetLimits(
+ PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits();
+ dispatcher_->SetLimits(
PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0));
// Life check to bail once |this| is deleted.
@@ -2212,20 +2215,20 @@ void HostResolverImpl::AbortAllInProgressJobs() {
}
if (self)
- dispatcher_.SetLimits(limits);
+ dispatcher_->SetLimits(limits);
}
void HostResolverImpl::AbortDnsTasks() {
// Pause the dispatcher so it won't start any new dispatcher jobs while
// aborting the old ones. This is needed so that it won't start the second
// DnsTransaction for a job if the DnsConfig just changed.
- PrioritizedDispatcher::Limits limits = dispatcher_.GetLimits();
- dispatcher_.SetLimits(
+ PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits();
+ dispatcher_->SetLimits(
PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0));
for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
it->second->AbortDnsTask();
- dispatcher_.SetLimits(limits);
+ dispatcher_->SetLimits(limits);
}
void HostResolverImpl::TryServingAllJobsFromHosts() {
diff --git a/chromium/net/dns/host_resolver_impl.h b/chromium/net/dns/host_resolver_impl.h
index e7f5f6541ca..7a0fb577d76 100644
--- a/chromium/net/dns/host_resolver_impl.h
+++ b/chromium/net/dns/host_resolver_impl.h
@@ -14,10 +14,8 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/time/time.h"
-#include "net/base/capturing_net_log.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
-#include "net/base/prioritized_dispatcher.h"
#include "net/dns/host_cache.h"
#include "net/dns/host_resolver.h"
#include "net/dns/host_resolver_proc.h"
@@ -98,21 +96,17 @@ class NET_EXPORT HostResolverImpl
uint32 retry_factor;
};
- // Creates a HostResolver that first uses the local cache |cache|, and then
- // falls back to |proc_params.resolver_proc|.
+ // Creates a HostResolver as specified by |options|.
//
- // If |cache| is NULL, then no caching is used. Otherwise we take
- // ownership of the |cache| pointer, and will free it during destruction.
+ // If Options.enable_caching is true, a cache is created using
+ // HostCache::CreateDefaultCache(). Otherwise no cache is used.
//
- // |job_limits| specifies the maximum number of jobs that the resolver will
- // run at once. This upper-bounds the total number of outstanding
- // DNS transactions (not counting retransmissions and retries).
+ // Options.GetDispatcherLimits() determines the maximum number of jobs that
+ // the resolver will run at once. This upper-bounds the total number of
+ // outstanding DNS transactions (not counting retransmissions and retries).
//
// |net_log| must remain valid for the life of the HostResolverImpl.
- HostResolverImpl(scoped_ptr<HostCache> cache,
- const PrioritizedDispatcher::Limits& job_limits,
- const ProcTaskParams& proc_params,
- NetLog* net_log);
+ HostResolverImpl(const Options& options, NetLog* net_log);
// If any completion callbacks are pending when the resolver is destroyed,
// the host resolutions are cancelled, and the completion callbacks will not
@@ -146,6 +140,10 @@ class NET_EXPORT HostResolverImpl
virtual HostCache* GetHostCache() OVERRIDE;
virtual base::Value* GetDnsConfigAsValue() const OVERRIDE;
+ void set_proc_params_for_test(const ProcTaskParams& proc_params) {
+ proc_params_ = proc_params;
+ }
+
private:
friend class HostResolverImplTest;
class Job;
@@ -238,7 +236,7 @@ class NET_EXPORT HostResolverImpl
// HostResolverImpl::Job could occupy multiple PrioritizedDispatcher job
// slots.
size_t num_running_dispatcher_jobs_for_tests() const {
- return dispatcher_.num_running_jobs();
+ return dispatcher_->num_running_jobs();
}
// Cache of host resolution results.
@@ -248,7 +246,7 @@ class NET_EXPORT HostResolverImpl
JobMap jobs_;
// Starts Jobs according to their priority and the configured limits.
- PrioritizedDispatcher dispatcher_;
+ scoped_ptr<PrioritizedDispatcher> dispatcher_;
// Limit on the maximum number of jobs queued in |dispatcher_|.
size_t max_queued_jobs_;
@@ -261,10 +259,6 @@ class NET_EXPORT HostResolverImpl
// Address family to use when the request doesn't specify one.
AddressFamily default_address_family_;
- base::WeakPtrFactory<HostResolverImpl> weak_ptr_factory_;
-
- base::WeakPtrFactory<HostResolverImpl> probe_weak_ptr_factory_;
-
// If present, used by DnsTask and ServeFromHosts to resolve requests.
scoped_ptr<DnsClient> dns_client_;
@@ -293,6 +287,10 @@ class NET_EXPORT HostResolverImpl
// Allow fallback to ProcTask if DnsTask fails.
bool fallback_to_proctask_;
+ base::WeakPtrFactory<HostResolverImpl> weak_ptr_factory_;
+
+ base::WeakPtrFactory<HostResolverImpl> probe_weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
};
diff --git a/chromium/net/dns/host_resolver_impl_unittest.cc b/chromium/net/dns/host_resolver_impl_unittest.cc
index 5d19fdcda12..4ab8fdf1c36 100644
--- a/chromium/net/dns/host_resolver_impl_unittest.cc
+++ b/chromium/net/dns/host_resolver_impl_unittest.cc
@@ -24,7 +24,6 @@
#include "net/base/net_util.h"
#include "net/dns/dns_client.h"
#include "net/dns/dns_test_util.h"
-#include "net/dns/host_cache.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,9 +34,12 @@ namespace {
const size_t kMaxJobs = 10u;
const size_t kMaxRetryAttempts = 4u;
-PrioritizedDispatcher::Limits DefaultLimits() {
- PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, kMaxJobs);
- return limits;
+HostResolver::Options DefaultOptions() {
+ HostResolver::Options options;
+ options.max_concurrent_resolves = kMaxJobs;
+ options.max_retry_attempts = kMaxRetryAttempts;
+ options.enable_caching = true;
+ return options;
}
HostResolverImpl::ProcTaskParams DefaultParams(
@@ -422,7 +424,7 @@ class HostResolverImplTest : public testing::Test {
HostResolverImplTest() : proc_(new MockHostResolverProc()) {}
void CreateResolver() {
- CreateResolverWithLimitsAndParams(DefaultLimits(),
+ CreateResolverWithLimitsAndParams(kMaxJobs,
DefaultParams(proc_.get()));
}
@@ -431,8 +433,7 @@ class HostResolverImplTest : public testing::Test {
void CreateSerialResolver() {
HostResolverImpl::ProcTaskParams params = DefaultParams(proc_.get());
params.max_retry_attempts = 0u;
- PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
- CreateResolverWithLimitsAndParams(limits, params);
+ CreateResolverWithLimitsAndParams(1u, params);
}
protected:
@@ -470,10 +471,12 @@ class HostResolverImplTest : public testing::Test {
}
virtual void CreateResolverWithLimitsAndParams(
- const PrioritizedDispatcher::Limits& limits,
+ size_t max_concurrent_resolves,
const HostResolverImpl::ProcTaskParams& params) {
- resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
- limits, params, NULL));
+ HostResolverImpl::Options options = DefaultOptions();
+ options.max_concurrent_resolves = max_concurrent_resolves;
+ resolver_.reset(new HostResolverImpl(options, NULL));
+ resolver_->set_proc_params_for_test(params);
}
// The Request will not be made until a call to |Resolve()|, and the Job will
@@ -599,7 +602,13 @@ TEST_F(HostResolverImplTest, AbortedAsynchronousLookup) {
EXPECT_FALSE(req0->completed());
}
-TEST_F(HostResolverImplTest, NumericIPv4Address) {
+#if defined(THREAD_SANITIZER)
+// Use of WorkerPool in HostResolverImpl causes a data race. crbug.com/334140
+#define MAYBE_NumericIPv4Address DISABLED_NumericIPv4Address
+#else
+#define MAYBE_NumericIPv4Address NumericIPv4Address
+#endif
+TEST_F(HostResolverImplTest, MAYBE_NumericIPv4Address) {
// Stevens says dotted quads with AI_UNSPEC resolve to a single sockaddr_in.
Request* req = CreateRequest("127.1.2.3", 5555);
EXPECT_EQ(OK, req->Resolve());
@@ -607,7 +616,13 @@ TEST_F(HostResolverImplTest, NumericIPv4Address) {
EXPECT_TRUE(req->HasOneAddress("127.1.2.3", 5555));
}
-TEST_F(HostResolverImplTest, NumericIPv6Address) {
+#if defined(THREAD_SANITIZER)
+// Use of WorkerPool in HostResolverImpl causes a data race. crbug.com/334140
+#define MAYBE_NumericIPv6Address DISABLED_NumericIPv6Address
+#else
+#define MAYBE_NumericIPv6Address NumericIPv6Address
+#endif
+TEST_F(HostResolverImplTest, MAYBE_NumericIPv6Address) {
// Resolve a plain IPv6 address. Don't worry about [brackets], because
// the caller should have removed them.
Request* req = CreateRequest("2001:db8::1", 5555);
@@ -616,12 +631,27 @@ TEST_F(HostResolverImplTest, NumericIPv6Address) {
EXPECT_TRUE(req->HasOneAddress("2001:db8::1", 5555));
}
-TEST_F(HostResolverImplTest, EmptyHost) {
+#if defined(THREAD_SANITIZER)
+// Use of WorkerPool in HostResolverImpl causes a data race. crbug.com/334140
+#define MAYBE_EmptyHost DISABLED_EmptyHost
+#else
+#define MAYBE_EmptyHost EmptyHost
+#endif
+TEST_F(HostResolverImplTest, MAYBE_EmptyHost) {
Request* req = CreateRequest(std::string(), 5555);
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->Resolve());
}
-TEST_F(HostResolverImplTest, EmptyDotsHost) {
+#if defined(THREAD_SANITIZER)
+// There's a data race in this test that may lead to use-after-free.
+// If the test starts to crash without ThreadSanitizer it needs to be disabled
+// globally. See http://crbug.com/268946 (stacks for this test in
+// crbug.com/333567).
+#define MAYBE_EmptyDotsHost DISABLED_EmptyDotsHost
+#else
+#define MAYBE_EmptyDotsHost EmptyDotsHost
+#endif
+TEST_F(HostResolverImplTest, MAYBE_EmptyDotsHost) {
for (int i = 0; i < 16; ++i) {
Request* req = CreateRequest(std::string(i, '.'), 5555);
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->Resolve());
@@ -808,10 +838,10 @@ TEST_F(HostResolverImplTest, StartWithinCallback) {
set_handler(new MyHandler());
// Turn off caching for this host resolver.
- resolver_.reset(new HostResolverImpl(scoped_ptr<HostCache>(),
- DefaultLimits(),
- DefaultParams(proc_.get()),
- NULL));
+ HostResolver::Options options = DefaultOptions();
+ options.enable_caching = false;
+ resolver_.reset(new HostResolverImpl(options, NULL));
+ resolver_->set_proc_params_for_test(DefaultParams(proc_.get()));
for (size_t i = 0; i < 4; ++i) {
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80 + i)->Resolve()) << i;
@@ -1245,11 +1275,8 @@ TEST_F(HostResolverImplTest, MultipleAttempts) {
// (500ms * 3).
params.unresponsive_delay = base::TimeDelta::FromMilliseconds(500);
- resolver_.reset(
- new HostResolverImpl(HostCache::CreateDefaultCache(),
- DefaultLimits(),
- params,
- NULL));
+ resolver_.reset(new HostResolverImpl(DefaultOptions(), NULL));
+ resolver_->set_proc_params_for_test(params);
// Resolve "host1".
HostResolver::RequestInfo info(HostPortPair("host1", 70));
@@ -1326,12 +1353,12 @@ class HostResolverImplDnsTest : public HostResolverImplTest {
// HostResolverImplTest implementation:
virtual void CreateResolverWithLimitsAndParams(
- const PrioritizedDispatcher::Limits& limits,
+ size_t max_concurrent_resolves,
const HostResolverImpl::ProcTaskParams& params) OVERRIDE {
- resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
- limits,
- params,
- NULL));
+ HostResolverImpl::Options options = DefaultOptions();
+ options.max_concurrent_resolves = max_concurrent_resolves;
+ resolver_.reset(new HostResolverImpl(options, NULL));
+ resolver_->set_proc_params_for_test(params);
// Disable IPv6 support probing.
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
dns_client_ = new MockDnsClient(DnsConfig(), dns_rules_);
@@ -1661,10 +1688,9 @@ TEST_F(HostResolverImplDnsTest, DontDisableDnsClientOnSporadicFailure) {
TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) {
// Use regular SystemHostResolverCall!
scoped_refptr<HostResolverProc> proc(new SystemHostResolverProc());
- resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(),
- DefaultLimits(),
- DefaultParams(proc.get()),
- NULL));
+ resolver_.reset(new HostResolverImpl(DefaultOptions(), NULL));
+ resolver_->set_proc_params_for_test(DefaultParams(proc.get()));
+
resolver_->SetDnsClient(
scoped_ptr<DnsClient>(new MockDnsClient(DnsConfig(), dns_rules_)));
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
@@ -1759,9 +1785,7 @@ TEST_F(HostResolverImplDnsTest, CancelWithTwoTransactionsActive) {
// Delete a resolver with some active requests and some queued requests.
TEST_F(HostResolverImplDnsTest, DeleteWithActiveTransactions) {
// At most 10 Jobs active at once.
- CreateResolverWithLimitsAndParams(
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, 10u),
- DefaultParams(proc_.get()));
+ CreateResolverWithLimitsAndParams(10u, DefaultParams(proc_.get()));
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
ChangeDnsConfig(CreateValidDnsConfig());
@@ -1872,9 +1896,7 @@ TEST_F(HostResolverImplDnsTest, SerialResolver) {
// Test the case where the AAAA query is started when another transaction
// completes.
TEST_F(HostResolverImplDnsTest, AAAAStartsAfterOtherJobFinishes) {
- CreateResolverWithLimitsAndParams(
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, 2),
- DefaultParams(proc_.get()));
+ CreateResolverWithLimitsAndParams(2u, DefaultParams(proc_.get()));
set_fallback_to_proctask(false);
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
ChangeDnsConfig(CreateValidDnsConfig());
@@ -1933,9 +1955,7 @@ TEST_F(HostResolverImplDnsTest, InvalidDnsConfigWithPendingRequests) {
// make sure that aborting the first HostResolverImpl::Job does not trigger
// another DnsTransaction on the second Job when it releases its second
// prioritized dispatcher slot.
- CreateResolverWithLimitsAndParams(
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u),
- DefaultParams(proc_.get()));
+ CreateResolverWithLimitsAndParams(3u, DefaultParams(proc_.get()));
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
ChangeDnsConfig(CreateValidDnsConfig());
@@ -1976,9 +1996,7 @@ TEST_F(HostResolverImplDnsTest,
// occupying two slots has its DnsTask aborted is the case most likely to run
// into problems.
for (size_t limit = 1u; limit < 6u; ++limit) {
- CreateResolverWithLimitsAndParams(
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, limit),
- DefaultParams(proc_.get()));
+ CreateResolverWithLimitsAndParams(limit, DefaultParams(proc_.get()));
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
ChangeDnsConfig(CreateValidDnsConfig());
@@ -2025,9 +2043,7 @@ TEST_F(HostResolverImplDnsTest, ManuallyDisableDnsClientWithPendingRequests) {
// make sure that aborting the first HostResolverImpl::Job does not trigger
// another DnsTransaction on the second Job when it releases its second
// prioritized dispatcher slot.
- CreateResolverWithLimitsAndParams(
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, 3u),
- DefaultParams(proc_.get()));
+ CreateResolverWithLimitsAndParams(3u, DefaultParams(proc_.get()));
resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_UNSPECIFIED);
ChangeDnsConfig(CreateValidDnsConfig());
diff --git a/chromium/net/dns/mdns_cache.h b/chromium/net/dns/mdns_cache.h
index 27a14d8fa44..6a38fc8f0e2 100644
--- a/chromium/net/dns/mdns_cache.h
+++ b/chromium/net/dns/mdns_cache.h
@@ -55,6 +55,7 @@ class NET_EXPORT_PRIVATE MDnsCache {
enum UpdateType {
RecordAdded,
RecordChanged,
+ RecordRemoved,
NoChange
};
diff --git a/chromium/net/dns/mdns_client.cc b/chromium/net/dns/mdns_client.cc
index 93f1d61d7cf..c55de6e9a98 100644
--- a/chromium/net/dns/mdns_client.cc
+++ b/chromium/net/dns/mdns_client.cc
@@ -67,7 +67,7 @@ IPEndPoint GetMDnsIPEndPoint(AddressFamily address_family) {
InterfaceIndexFamilyList GetMDnsInterfacesToBind() {
NetworkInterfaceList network_list;
InterfaceIndexFamilyList interfaces;
- if (!GetNetworkList(&network_list))
+ if (!GetNetworkList(&network_list, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
return interfaces;
for (size_t i = 0; i < network_list.size(); ++i) {
AddressFamily family = GetAddressFamily(network_list[i].address);
diff --git a/chromium/net/dns/mdns_client.h b/chromium/net/dns/mdns_client.h
index ab7916ed47d..675f8de1656 100644
--- a/chromium/net/dns/mdns_client.h
+++ b/chromium/net/dns/mdns_client.h
@@ -27,7 +27,7 @@ class RecordParsed;
// time out after a reasonable number of seconds.
class NET_EXPORT MDnsTransaction {
public:
- // Used to signify what type of result the transaction has recieved.
+ // Used to signify what type of result the transaction has received.
enum Result {
// Passed whenever a record is found.
RESULT_RECORD,
@@ -113,6 +113,9 @@ class NET_EXPORT MDnsListener {
// Start the listener. Return true on success.
virtual bool Start() = 0;
+ // Actively refresh any received records.
+ virtual void SetActiveRefresh(bool active_refresh) = 0;
+
// Get the host or service name for this query.
// Return an empty string for no name.
virtual const std::string& GetName() const = 0;
diff --git a/chromium/net/dns/mdns_client_impl.cc b/chromium/net/dns/mdns_client_impl.cc
index c95b86c6d17..68cee7a87ec 100644
--- a/chromium/net/dns/mdns_client_impl.cc
+++ b/chromium/net/dns/mdns_client_impl.cc
@@ -26,6 +26,13 @@ namespace net {
namespace {
const unsigned MDnsTransactionTimeoutSeconds = 3;
+// The fractions of the record's original TTL after which an active listener
+// (one that had |SetActiveRefresh(true)| called) will send a query to refresh
+// its cache. This happens both at 85% of the original TTL and again at 95% of
+// the original TTL.
+const double kListenerRefreshRatio1 = 0.85;
+const double kListenerRefreshRatio2 = 0.95;
+const unsigned kMillisecondsPerSecond = 1000;
} // namespace
@@ -192,10 +199,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Note: We store cache keys rather than record pointers to avoid
// erroneous behavior in case a packet contains multiple exclusive
// records with the same type and name.
- std::map<MDnsCache::Key, MDnsListener::UpdateType> update_keys;
+ std::map<MDnsCache::Key, MDnsCache::UpdateType> update_keys;
if (!response->InitParseWithoutQuery(bytes_read)) {
- LOG(WARNING) << "Could not understand an mDNS packet.";
+ DVLOG(1) << "Could not understand an mDNS packet.";
return; // Message is unreadable.
}
@@ -213,10 +220,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
&parser, base::Time::Now());
if (!record) {
- LOG(WARNING) << "Could not understand an mDNS record.";
+ DVLOG(1) << "Could not understand an mDNS record.";
if (offset == parser.GetOffset()) {
- LOG(WARNING) << "Abandoned parsing the rest of the packet.";
+ DVLOG(1) << "Abandoned parsing the rest of the packet.";
return; // The parser did not advance, abort reading the packet.
} else {
continue; // We may be able to extract other records from the packet.
@@ -225,7 +232,7 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
if ((record->klass() & dns_protocol::kMDnsClassMask) !=
dns_protocol::kClassIN) {
- LOG(WARNING) << "Received an mDNS record with non-IN class. Ignoring.";
+ DVLOG(1) << "Received an mDNS record with non-IN class. Ignoring.";
continue; // Ignore all records not in the IN class.
}
@@ -235,29 +242,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Cleanup time may have changed.
ScheduleCleanup(cache_.next_expiration());
- if (update != MDnsCache::NoChange) {
- MDnsListener::UpdateType update_external;
-
- switch (update) {
- case MDnsCache::RecordAdded:
- update_external = MDnsListener::RECORD_ADDED;
- break;
- case MDnsCache::RecordChanged:
- update_external = MDnsListener::RECORD_CHANGED;
- break;
- case MDnsCache::NoChange:
- default:
- NOTREACHED();
- // Dummy assignment to suppress compiler warning.
- update_external = MDnsListener::RECORD_CHANGED;
- break;
- }
-
- update_keys.insert(std::make_pair(update_key, update_external));
- }
+ update_keys.insert(std::make_pair(update_key, update));
}
- for (std::map<MDnsCache::Key, MDnsListener::UpdateType>::iterator i =
+ for (std::map<MDnsCache::Key, MDnsCache::UpdateType>::iterator i =
update_keys.begin(); i != update_keys.end(); i++) {
const RecordParsed* record = cache_.LookupKey(i->first);
if (!record)
@@ -311,14 +299,14 @@ void MDnsClientImpl::Core::OnConnectionError(int error) {
}
void MDnsClientImpl::Core::AlertListeners(
- MDnsListener::UpdateType update_type,
+ MDnsCache::UpdateType update_type,
const ListenerKey& key,
const RecordParsed* record) {
ListenerMap::iterator listener_map_iterator = listeners_.find(key);
if (listener_map_iterator == listeners_.end()) return;
FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second,
- AlertDelegate(update_type, record));
+ HandleRecordUpdate(update_type, record));
}
void MDnsClientImpl::Core::AddListener(
@@ -392,7 +380,7 @@ void MDnsClientImpl::Core::DoCleanup() {
void MDnsClientImpl::Core::OnRecordRemoved(
const RecordParsed* record) {
- AlertListeners(MDnsListener::RECORD_REMOVED,
+ AlertListeners(MDnsCache::RecordRemoved,
ListenerKey(record->name(), record->type()), record);
}
@@ -449,7 +437,14 @@ MDnsListenerImpl::MDnsListenerImpl(
MDnsListener::Delegate* delegate,
MDnsClientImpl* client)
: rrtype_(rrtype), name_(name), client_(client), delegate_(delegate),
- started_(false) {
+ started_(false), active_refresh_(false) {
+}
+
+MDnsListenerImpl::~MDnsListenerImpl() {
+ if (started_) {
+ DCHECK(client_->core());
+ client_->core()->RemoveListener(this);
+ }
}
bool MDnsListenerImpl::Start() {
@@ -463,10 +458,15 @@ bool MDnsListenerImpl::Start() {
return true;
}
-MDnsListenerImpl::~MDnsListenerImpl() {
+void MDnsListenerImpl::SetActiveRefresh(bool active_refresh) {
+ active_refresh_ = active_refresh;
+
if (started_) {
- DCHECK(client_->core());
- client_->core()->RemoveListener(this);
+ if (!active_refresh_) {
+ next_refresh_.Cancel();
+ } else if (last_update_ != base::Time()) {
+ ScheduleNextRefresh();
+ }
}
}
@@ -478,10 +478,40 @@ uint16 MDnsListenerImpl::GetType() const {
return rrtype_;
}
-void MDnsListenerImpl::AlertDelegate(MDnsListener::UpdateType update_type,
- const RecordParsed* record) {
+void MDnsListenerImpl::HandleRecordUpdate(MDnsCache::UpdateType update_type,
+ const RecordParsed* record) {
DCHECK(started_);
- delegate_->OnRecordUpdate(update_type, record);
+
+ if (update_type != MDnsCache::RecordRemoved) {
+ ttl_ = record->ttl();
+ last_update_ = record->time_created();
+
+ ScheduleNextRefresh();
+ }
+
+ if (update_type != MDnsCache::NoChange) {
+ MDnsListener::UpdateType update_external;
+
+ switch (update_type) {
+ case MDnsCache::RecordAdded:
+ update_external = MDnsListener::RECORD_ADDED;
+ break;
+ case MDnsCache::RecordChanged:
+ update_external = MDnsListener::RECORD_CHANGED;
+ break;
+ case MDnsCache::RecordRemoved:
+ update_external = MDnsListener::RECORD_REMOVED;
+ break;
+ case MDnsCache::NoChange:
+ default:
+ NOTREACHED();
+ // Dummy assignment to suppress compiler warning.
+ update_external = MDnsListener::RECORD_CHANGED;
+ break;
+ }
+
+ delegate_->OnRecordUpdate(update_external, record);
+ }
}
void MDnsListenerImpl::AlertNsecRecord() {
@@ -489,6 +519,47 @@ void MDnsListenerImpl::AlertNsecRecord() {
delegate_->OnNsecRecord(name_, rrtype_);
}
+void MDnsListenerImpl::ScheduleNextRefresh() {
+ DCHECK(last_update_ != base::Time());
+
+ if (!active_refresh_)
+ return;
+
+ // A zero TTL is a goodbye packet and should not be refreshed.
+ if (ttl_ == 0) {
+ next_refresh_.Cancel();
+ return;
+ }
+
+ next_refresh_.Reset(base::Bind(&MDnsListenerImpl::DoRefresh,
+ AsWeakPtr()));
+
+ // Schedule refreshes at both 85% and 95% of the original TTL. These will both
+ // be canceled and rescheduled if the record's TTL is updated due to a
+ // response being received.
+ base::Time next_refresh1 = last_update_ + base::TimeDelta::FromMilliseconds(
+ static_cast<int>(kMillisecondsPerSecond *
+ kListenerRefreshRatio1 * ttl_));
+
+ base::Time next_refresh2 = last_update_ + base::TimeDelta::FromMilliseconds(
+ static_cast<int>(kMillisecondsPerSecond *
+ kListenerRefreshRatio2 * ttl_));
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ next_refresh_.callback(),
+ next_refresh1 - base::Time::Now());
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ next_refresh_.callback(),
+ next_refresh2 - base::Time::Now());
+}
+
+void MDnsListenerImpl::DoRefresh() {
+ client_->core()->SendQuery(rrtype_, name_);
+}
+
MDnsTransactionImpl::MDnsTransactionImpl(
uint16 rrtype,
const std::string& name,
diff --git a/chromium/net/dns/mdns_client_impl.h b/chromium/net/dns/mdns_client_impl.h
index f75598560ab..76607880bef 100644
--- a/chromium/net/dns/mdns_client_impl.h
+++ b/chromium/net/dns/mdns_client_impl.h
@@ -133,7 +133,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
ListenerMap;
// Alert listeners of an update to the cache.
- void AlertListeners(MDnsListener::UpdateType update_type,
+ void AlertListeners(MDnsCache::UpdateType update_type,
const ListenerKey& key, const RecordParsed* record);
// Schedule a cache cleanup to a specific time, cancelling other cleanups.
@@ -156,7 +156,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
MDnsClientImpl* client_;
MDnsCache cache_;
- base::CancelableCallback<void()> cleanup_callback_;
+ base::CancelableClosure cleanup_callback_;
base::Time scheduled_cleanup_;
scoped_ptr<MDnsConnection> connection_;
@@ -204,6 +204,9 @@ class MDnsListenerImpl : public MDnsListener,
// MDnsListener implementation:
virtual bool Start() OVERRIDE;
+ // Actively refresh any received records.
+ virtual void SetActiveRefresh(bool active_refresh) OVERRIDE;
+
virtual const std::string& GetName() const OVERRIDE;
virtual uint16 GetType() const OVERRIDE;
@@ -211,19 +214,27 @@ class MDnsListenerImpl : public MDnsListener,
MDnsListener::Delegate* delegate() { return delegate_; }
// Alert the delegate of a record update.
- void AlertDelegate(MDnsListener::UpdateType update_type,
- const RecordParsed* record_parsed);
+ void HandleRecordUpdate(MDnsCache::UpdateType update_type,
+ const RecordParsed* record_parsed);
// Alert the delegate of the existence of an Nsec record.
void AlertNsecRecord();
private:
+ void ScheduleNextRefresh();
+ void DoRefresh();
+
uint16 rrtype_;
std::string name_;
MDnsClientImpl* client_;
MDnsListener::Delegate* delegate_;
+ base::Time last_update_;
+ uint32 ttl_;
bool started_;
+ bool active_refresh_;
+
+ base::CancelableClosure next_refresh_;
DISALLOW_COPY_AND_ASSIGN(MDnsListenerImpl);
};
diff --git a/chromium/net/dns/mdns_client_unittest.cc b/chromium/net/dns/mdns_client_unittest.cc
index 7ea4ac97448..0186839da04 100644
--- a/chromium/net/dns/mdns_client_unittest.cc
+++ b/chromium/net/dns/mdns_client_unittest.cc
@@ -241,6 +241,25 @@ const uint8 kQueryPacketPrivet[] = {
0x00, 0x01, // CLASS is IN.
};
+const uint8 kQueryPacketPrivetA[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x00, 0x00, // No flags.
+ 0x00, 0x01, // One question.
+ 0x00, 0x00, // 0 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Question
+ // This part is echoed back from the respective query.
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x01, // TYPE is A.
+ 0x00, 0x01, // CLASS is IN.
+};
+
const uint8 kSamplePacketAdditionalOnly[] = {
// Header
0x00, 0x00, // ID is zeroed out
@@ -303,8 +322,8 @@ const uint8 kSamplePacketAPrivet[] = {
0x00,
0x00, 0x01, // TYPE is A.
0x00, 0x01, // CLASS is IN.
- 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
- 0x24, 0x74,
+ 0x00, 0x00, // TTL (4 bytes) is 5 seconds
+ 0x00, 0x05,
0x00, 0x04, // RDLENGTH is 4 bytes.
0xc0, 0x0c,
0x00, 0x02,
@@ -1008,6 +1027,31 @@ TEST_F(MDnsTest, NsecConflictRemoval) {
}
+TEST_F(MDnsTest, RefreshQuery) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+ scoped_ptr<MDnsListener> listener_privet =
+ test_client_.CreateListener(dns_protocol::kTypeA, "_privet._tcp.local",
+ &delegate_privet);
+
+ listener_privet->SetActiveRefresh(true);
+ ASSERT_TRUE(listener_privet->Start());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_ADDED, _));
+
+ SimulatePacketReceive(kSamplePacketAPrivet,
+ sizeof(kSamplePacketAPrivet));
+
+ // Expecting 2 calls (one for ipv4 and one for ipv6) for each of the 2
+ // scheduled refresh queries.
+ EXPECT_CALL(socket_factory_, OnSendTo(
+ MakeString(kQueryPacketPrivetA, sizeof(kQueryPacketPrivetA))))
+ .Times(4);
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_REMOVED, _));
+
+ RunFor(base::TimeDelta::FromSeconds(6));
+}
+
// Note: These tests assume that the ipv4 socket will always be created first.
// This is a simplifying assumption based on the way the code works now.
class SimpleMockSocketFactory : public MDnsSocketFactory {
diff --git a/chromium/net/dns/mock_mdns_socket_factory.h b/chromium/net/dns/mock_mdns_socket_factory.h
index 7cc8fed9ab3..1ecc4be01d2 100644
--- a/chromium/net/dns/mock_mdns_socket_factory.h
+++ b/chromium/net/dns/mock_mdns_socket_factory.h
@@ -33,8 +33,8 @@ class MockMDnsDatagramServerSocket : public DatagramServerSocket {
const std::string address,
const CompletionCallback& callback));
- MOCK_METHOD1(SetReceiveBufferSize, bool(int32 size));
- MOCK_METHOD1(SetSendBufferSize, bool(int32 size));
+ MOCK_METHOD1(SetReceiveBufferSize, int(int32 size));
+ MOCK_METHOD1(SetSendBufferSize, int(int32 size));
MOCK_METHOD0(Close, void());
@@ -54,6 +54,8 @@ class MockMDnsDatagramServerSocket : public DatagramServerSocket {
MOCK_METHOD1(SetDiffServCodePoint, int(DiffServCodePoint dscp));
+ MOCK_METHOD0(DetachFromThread, void());
+
void SetResponsePacket(std::string response_packet);
int HandleRecvNow(IOBuffer* buffer, int size, IPEndPoint* address,
diff --git a/chromium/net/dns/record_parsed.cc b/chromium/net/dns/record_parsed.cc
index bee6c7aac63..4ae6ed29210 100644
--- a/chromium/net/dns/record_parsed.cc
+++ b/chromium/net/dns/record_parsed.cc
@@ -53,7 +53,7 @@ scoped_ptr<const RecordParsed> RecordParsed::CreateFrom(
rdata = NsecRecordRdata::Create(record.rdata, *parser);
break;
default:
- LOG(WARNING) << "Unknown RData type for recieved record: " << record.type;
+ DVLOG(1) << "Unknown RData type for received record: " << record.type;
return scoped_ptr<const RecordParsed>();
}
diff --git a/chromium/net/dns/record_rdata.cc b/chromium/net/dns/record_rdata.cc
index 4ebc643a377..0c605593ab7 100644
--- a/chromium/net/dns/record_rdata.cc
+++ b/chromium/net/dns/record_rdata.cc
@@ -4,7 +4,7 @@
#include "net/dns/record_rdata.h"
-#include "net/base/big_endian.h"
+#include "base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/dns/dns_protocol.h"
#include "net/dns/dns_response.h"
@@ -29,7 +29,7 @@ scoped_ptr<SrvRecordRdata> SrvRecordRdata::Create(
scoped_ptr<SrvRecordRdata> rdata(new SrvRecordRdata);
- BigEndianReader reader(data.data(), data.size());
+ base::BigEndianReader reader(data.data(), data.size());
// 2 bytes for priority, 2 bytes for weight, 2 bytes for port.
reader.ReadU16(&rdata->priority_);
reader.ReadU16(&rdata->weight_);
diff --git a/chromium/net/dns/record_rdata.h b/chromium/net/dns/record_rdata.h
index f83a48650e1..def8e33b1a3 100644
--- a/chromium/net/dns/record_rdata.h
+++ b/chromium/net/dns/record_rdata.h
@@ -12,7 +12,6 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
-#include "net/base/big_endian.h"
#include "net/base/net_export.h"
#include "net/base/net_util.h"
#include "net/dns/dns_protocol.h"
diff --git a/chromium/net/base/filter.cc b/chromium/net/filter/filter.cc
index 42313fd9ffe..d77272a809d 100644
--- a/chromium/net/base/filter.cc
+++ b/chromium/net/filter/filter.cc
@@ -1,15 +1,18 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/filter.h"
+#include "net/filter/filter.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
-#include "net/base/gzip_filter.h"
+#include "net/base/filename_util_unsafe.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_util.h"
-#include "net/base/sdch_filter.h"
+#include "net/filter/gzip_filter.h"
+#include "net/filter/sdch_filter.h"
+#include "net/url_request/url_request_context.h"
+#include "url/gurl.h"
namespace {
@@ -170,11 +173,13 @@ void Filter::FixupEncodingTypes(
encoding_types->clear();
GURL url;
+ std::string disposition;
success = filter_context.GetURL(&url);
DCHECK(success);
- base::FilePath filename =
- base::FilePath().AppendASCII(url.ExtractFileName());
- base::FilePath::StringType extension = filename.Extension();
+ filter_context.GetContentDisposition(&disposition);
+ // Don't supply a MIME type here, since that may cause disk IO.
+ base::FilePath::StringType extension =
+ GenerateFileExtensionUnsafe(url, disposition, "UTF-8", "", "", "");
if (filter_context.IsDownload()) {
// We don't want to decompress gzipped files when the user explicitly
@@ -369,7 +374,11 @@ Filter* Filter::PrependNewFilter(FilterType type_id,
break;
case FILTER_TYPE_SDCH:
case FILTER_TYPE_SDCH_POSSIBLE:
- first_filter.reset(InitSdchFilter(type_id, filter_context, buffer_size));
+ if (filter_context.GetURLRequestContext()->sdch_manager() &&
+ SdchManager::sdch_enabled()) {
+ first_filter.reset(
+ InitSdchFilter(type_id, filter_context, buffer_size));
+ }
break;
default:
break;
diff --git a/chromium/net/base/filter.h b/chromium/net/filter/filter.h
index 9511511f778..70509f4ced5 100644
--- a/chromium/net/base/filter.h
+++ b/chromium/net/filter/filter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
//
@@ -26,8 +26,8 @@
//
// The lifetime of a Filter instance is completely controlled by its caller.
-#ifndef NET_BASE_FILTER_H__
-#define NET_BASE_FILTER_H__
+#ifndef NET_FILTER_FILTER_H__
+#define NET_FILTER_FILTER_H__
#include <string>
#include <vector>
@@ -43,6 +43,7 @@ class GURL;
namespace net {
+class URLRequestContext;
class IOBuffer;
//------------------------------------------------------------------------------
@@ -74,6 +75,10 @@ class NET_EXPORT_PRIVATE FilterContext {
// Return false if gurl is not present.
virtual bool GetURL(GURL* gurl) const = 0;
+ // What Content-Disposition header came with this data?
+ // Return false if no header was present.
+ virtual bool GetContentDisposition(std::string* disposition) const = 0;
+
// When was this data requested from a server?
virtual base::Time GetRequestTime() const = 0;
@@ -94,6 +99,9 @@ class NET_EXPORT_PRIVATE FilterContext {
// For example: 200 is ok. 4xx are error codes. etc.
virtual int GetResponseCode() const = 0;
+ // The URLRequestContext associated with the request.
+ virtual const URLRequestContext* GetURLRequestContext() const = 0;
+
// The following method forces the context to emit a specific set of
// statistics as selected by the argument.
virtual void RecordPacketStats(StatisticSelector statistic) const = 0;
@@ -273,4 +281,4 @@ class NET_EXPORT_PRIVATE Filter {
} // namespace net
-#endif // NET_BASE_FILTER_H__
+#endif // NET_FILTER_FILTER_H__
diff --git a/chromium/net/base/filter_unittest.cc b/chromium/net/filter/filter_unittest.cc
index 73bde73810d..08a2861699e 100644
--- a/chromium/net/base/filter_unittest.cc
+++ b/chromium/net/filter/filter_unittest.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/filter.h"
-#include "net/base/mock_filter_context.h"
+#include "net/filter/filter.h"
+#include "net/filter/mock_filter_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -82,6 +82,23 @@ TEST(FilterTest, ApacheGzip) {
EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}
+TEST(FilterTest, GzipContentDispositionFilename) {
+ MockFilterContext filter_context;
+ filter_context.SetSdchResponse(false);
+
+ const std::string kGzipMime("application/x-tar");
+ const std::string kContentDisposition("attachment; filename=\"foo.tgz\"");
+ const std::string kURL("http://foo.com/getfoo.php");
+ std::vector<Filter::FilterType> encoding_types;
+
+ encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
+ filter_context.SetMimeType(kGzipMime);
+ filter_context.SetURL(GURL(kURL));
+ filter_context.SetContentDisposition(kContentDisposition);
+ Filter::FixupEncodingTypes(filter_context, &encoding_types);
+ ASSERT_EQ(0U, encoding_types.size());
+}
+
TEST(FilterTest, SdchEncoding) {
// Handle content encodings including SDCH.
const std::string kTextHtmlMime("text/html");
diff --git a/chromium/net/base/gzip_filter.cc b/chromium/net/filter/gzip_filter.cc
index 2d02bce7a01..36fe01cb1ce 100644
--- a/chromium/net/base/gzip_filter.cc
+++ b/chromium/net/filter/gzip_filter.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/gzip_filter.h"
+#include "net/filter/gzip_filter.h"
#include "base/logging.h"
-#include "net/base/gzip_header.h"
+#include "net/filter/gzip_header.h"
#include "third_party/zlib/zlib.h"
namespace net {
diff --git a/chromium/net/base/gzip_filter.h b/chromium/net/filter/gzip_filter.h
index 0365a920056..0ae284a187a 100644
--- a/chromium/net/base/gzip_filter.h
+++ b/chromium/net/filter/gzip_filter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -12,12 +12,12 @@
// GZipFilter is a subclass of Filter. See the latter's header file filter.h
// for sample usage.
-#ifndef NET_BASE_GZIP_FILTER_H_
-#define NET_BASE_GZIP_FILTER_H_
+#ifndef NET_FILTER_GZIP_FILTER_H_
+#define NET_FILTER_GZIP_FILTER_H_
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
-#include "net/base/filter.h"
+#include "net/filter/filter.h"
typedef struct z_stream_s z_stream;
@@ -148,4 +148,4 @@ class GZipFilter : public Filter {
} // namespace net
-#endif // NET_BASE_GZIP_FILTER_H__
+#endif // NET_FILTER_GZIP_FILTER_H__
diff --git a/chromium/net/base/gzip_filter_unittest.cc b/chromium/net/filter/gzip_filter_unittest.cc
index 5e14a4584cc..97d7b7d2814 100644
--- a/chromium/net/base/gzip_filter_unittest.cc
+++ b/chromium/net/filter/gzip_filter_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -8,9 +8,9 @@
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
-#include "net/base/gzip_filter.h"
-#include "net/base/mock_filter_context.h"
#include "net/base/io_buffer.h"
+#include "net/filter/gzip_filter.h"
+#include "net/filter/mock_filter_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/zlib/zlib.h"
diff --git a/chromium/net/base/gzip_header.cc b/chromium/net/filter/gzip_header.cc
index 88ad4d7a840..4d1cad91489 100644
--- a/chromium/net/base/gzip_header.cc
+++ b/chromium/net/filter/gzip_header.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/gzip_header.h"
+#include "net/filter/gzip_header.h"
#include <algorithm>
diff --git a/chromium/net/base/gzip_header.h b/chromium/net/filter/gzip_header.h
index 82e55c24e07..986afc44a7b 100644
--- a/chromium/net/base/gzip_header.h
+++ b/chromium/net/filter/gzip_header.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -12,8 +12,8 @@
// you've read from a file or socket.
//
-#ifndef NET_BASE_GZIP_HEADER_H_
-#define NET_BASE_GZIP_HEADER_H_
+#ifndef NET_FILTER_GZIP_HEADER_H_
+#define NET_FILTER_GZIP_HEADER_H_
#include "base/basictypes.h"
@@ -91,4 +91,4 @@ class GZipHeader {
} // namespace net
-#endif // NET_BASE_GZIP_HEADER_H_
+#endif // NET_FILTER_GZIP_HEADER_H_
diff --git a/chromium/net/base/mock_filter_context.cc b/chromium/net/filter/mock_filter_context.cc
index 70345892c23..eb3484c19eb 100644
--- a/chromium/net/base/mock_filter_context.cc
+++ b/chromium/net/filter/mock_filter_context.cc
@@ -1,8 +1,10 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/mock_filter_context.h"
+#include "net/filter/mock_filter_context.h"
+
+#include "net/url_request/url_request_context.h"
namespace net {
@@ -10,7 +12,8 @@ MockFilterContext::MockFilterContext()
: is_cached_content_(false),
is_download_(false),
is_sdch_response_(false),
- response_code_(-1) {
+ response_code_(-1),
+ context_(new URLRequestContext()) {
}
MockFilterContext::~MockFilterContext() {}
@@ -27,6 +30,13 @@ bool MockFilterContext::GetURL(GURL* gurl) const {
return true;
}
+bool MockFilterContext::GetContentDisposition(std::string* disposition) const {
+ if (content_disposition_.empty())
+ return false;
+ *disposition = content_disposition_;
+ return true;
+}
+
// What was this data requested from a server?
base::Time MockFilterContext::GetRequestTime() const {
return request_time_;
@@ -42,4 +52,8 @@ int64 MockFilterContext::GetByteReadCount() const { return 0; }
int MockFilterContext::GetResponseCode() const { return response_code_; }
+const URLRequestContext* MockFilterContext::GetURLRequestContext() const {
+ return context_.get();
+}
+
} // namespace net
diff --git a/chromium/net/base/mock_filter_context.h b/chromium/net/filter/mock_filter_context.h
index 592be5505b2..9bb6235a3a9 100644
--- a/chromium/net/base/mock_filter_context.h
+++ b/chromium/net/filter/mock_filter_context.h
@@ -1,17 +1,20 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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_BASE_MOCK_FILTER_CONTEXT_H_
-#define NET_BASE_MOCK_FILTER_CONTEXT_H_
+#ifndef NET_FILTER_MOCK_FILTER_CONTEXT_H_
+#define NET_FILTER_MOCK_FILTER_CONTEXT_H_
#include <string>
-#include "net/base/filter.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/filter/filter.h"
#include "url/gurl.h"
namespace net {
+class URLRequestContext;
+
class MockFilterContext : public FilterContext {
public:
MockFilterContext();
@@ -19,6 +22,9 @@ class MockFilterContext : public FilterContext {
void SetMimeType(const std::string& mime_type) { mime_type_ = mime_type; }
void SetURL(const GURL& gurl) { gurl_ = gurl; }
+ void SetContentDisposition(const std::string& disposition) {
+ content_disposition_ = disposition;
+ }
void SetRequestTime(const base::Time time) { request_time_ = time; }
void SetCached(bool is_cached) { is_cached_content_ = is_cached; }
void SetDownload(bool is_download) { is_download_ = is_download; }
@@ -26,6 +32,9 @@ class MockFilterContext : public FilterContext {
void SetSdchResponse(bool is_sdch_response) {
is_sdch_response_ = is_sdch_response;
}
+ URLRequestContext* GetModifiableURLRequestContext() const {
+ return context_.get();
+ }
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
@@ -33,6 +42,10 @@ class MockFilterContext : public FilterContext {
// Return false if gurl is not present.
virtual bool GetURL(GURL* gurl) const OVERRIDE;
+ // What Content-Disposition did the server supply for this data?
+ // Return false if Content-Disposition was not present.
+ virtual bool GetContentDisposition(std::string* disposition) const OVERRIDE;
+
// What was this data requested from a server?
virtual base::Time GetRequestTime() const OVERRIDE;
@@ -50,21 +63,26 @@ class MockFilterContext : public FilterContext {
virtual int GetResponseCode() const OVERRIDE;
+ // The URLRequestContext associated with the request.
+ virtual const URLRequestContext* GetURLRequestContext() const OVERRIDE;
+
virtual void RecordPacketStats(StatisticSelector statistic) const OVERRIDE {}
private:
int buffer_size_;
std::string mime_type_;
+ std::string content_disposition_;
GURL gurl_;
base::Time request_time_;
bool is_cached_content_;
bool is_download_;
bool is_sdch_response_;
int response_code_;
+ scoped_ptr<URLRequestContext> context_;
DISALLOW_COPY_AND_ASSIGN(MockFilterContext);
};
} // namespace net
-#endif // NET_BASE_MOCK_FILTER_CONTEXT_H_
+#endif // NET_FILTER_MOCK_FILTER_CONTEXT_H_
diff --git a/chromium/net/base/sdch_filter.cc b/chromium/net/filter/sdch_filter.cc
index 89891b66f49..2ef5ad51d7c 100644
--- a/chromium/net/base/sdch_filter.cc
+++ b/chromium/net/filter/sdch_filter.cc
@@ -1,16 +1,18 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/base/sdch_filter.h"
+#include "net/filter/sdch_filter.h"
-#include <limits.h>
#include <ctype.h>
+#include <limits.h>
+
#include <algorithm>
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "net/base/sdch_manager.h"
+#include "net/url_request/url_request_context.h"
#include "sdch/open-vcdiff/src/google/vcdecoder.h"
@@ -22,6 +24,7 @@ SdchFilter::SdchFilter(const FilterContext& filter_context)
dictionary_hash_(),
dictionary_hash_is_plausible_(false),
dictionary_(NULL),
+ url_request_context_(filter_context.GetURLRequestContext()),
dest_buffer_excess_(),
dest_buffer_excess_index_(0),
source_bytes_(0),
@@ -31,6 +34,7 @@ SdchFilter::SdchFilter(const FilterContext& filter_context)
DCHECK(success);
success = filter_context.GetURL(&url_);
DCHECK(success);
+ DCHECK(url_request_context_->sdch_manager());
}
SdchFilter::~SdchFilter() {
@@ -50,7 +54,8 @@ SdchFilter::~SdchFilter() {
// Make it possible for the user to hit reload, and get non-sdch content.
// Note this will "wear off" quickly enough, and is just meant to assure
// in some rare case that the user is not stuck.
- SdchManager::BlacklistDomain(url_);
+ url_request_context_->sdch_manager()->BlacklistDomain(
+ url_);
UMA_HISTOGRAM_COUNTS("Sdch3.PartialBytesIn",
static_cast<int>(filter_context_.GetByteReadCount()));
UMA_HISTOGRAM_COUNTS("Sdch3.PartialVcdiffIn", source_bytes_);
@@ -87,7 +92,8 @@ SdchFilter::~SdchFilter() {
filter_context_.RecordPacketStats(FilterContext::SDCH_DECODE);
// Allow latency experiments to proceed.
- SdchManager::Global()->SetAllowLatencyExperiment(url_, true);
+ url_request_context_->sdch_manager()->SetAllowLatencyExperiment(
+ url_, true);
return;
}
case PASS_THROUGH: {
@@ -212,7 +218,7 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer,
SdchManager::SdchErrorRecovery(SdchManager::PASSING_THROUGH_NON_SDCH);
decoding_status_ = PASS_THROUGH;
// ... but further back-off on advertising SDCH support.
- SdchManager::BlacklistDomain(url_);
+ url_request_context_->sdch_manager()->BlacklistDomain(url_);
}
if (decoding_status_ == PASS_THROUGH) {
@@ -222,7 +228,7 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer,
if (std::string::npos == mime_type_.find("text/html")) {
// Since we can't do a meta-refresh (along with an exponential
// backoff), we'll just make sure this NEVER happens again.
- SdchManager::BlacklistDomainForever(url_);
+ url_request_context_->sdch_manager()->BlacklistDomainForever(url_);
if (filter_context_.IsCachedContent())
SdchManager::SdchErrorRecovery(
SdchManager::CACHED_META_REFRESH_UNSUPPORTED);
@@ -241,7 +247,7 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer,
} else {
// Since it wasn't in the cache, we definately need at least some
// period of blacklisting to get the correct content.
- SdchManager::BlacklistDomain(url_);
+ url_request_context_->sdch_manager()->BlacklistDomain(url_);
SdchManager::SdchErrorRecovery(SdchManager::META_REFRESH_RECOVERY);
}
decoding_status_ = META_REFRESH_RECOVERY;
@@ -330,18 +336,19 @@ Filter::FilterStatus SdchFilter::InitializeDictionary() {
else
next_stream_data_ = NULL;
- DCHECK(!dictionary_.get());
+ DCHECK(!dictionary_);
dictionary_hash_is_plausible_ = true; // Assume plausible, but check.
- SdchManager::Dictionary* dictionary = NULL;
- if ('\0' == dictionary_hash_[kServerIdLength - 1])
- SdchManager::Global()->GetVcdiffDictionary(std::string(dictionary_hash_, 0,
- kServerIdLength - 1),
- url_, &dictionary);
- else
+ if ('\0' == dictionary_hash_[kServerIdLength - 1]) {
+ SdchManager* manager(url_request_context_->sdch_manager());
+ manager->GetVcdiffDictionary(
+ std::string(dictionary_hash_, 0, kServerIdLength - 1),
+ url_, &dictionary_);
+ } else {
dictionary_hash_is_plausible_ = false;
+ }
- if (!dictionary) {
+ if (!dictionary_) {
DCHECK(dictionary_hash_.size() == kServerIdLength);
// Since dictionary was not found, check to see if hash was even plausible.
for (size_t i = 0; i < kServerIdLength - 1; ++i) {
@@ -358,7 +365,6 @@ Filter::FilterStatus SdchFilter::InitializeDictionary() {
decoding_status_ = DECODING_ERROR;
return FILTER_ERROR;
}
- dictionary_ = dictionary;
vcdiff_streaming_decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder);
vcdiff_streaming_decoder_->SetAllowVcdTarget(false);
vcdiff_streaming_decoder_->StartDecoding(dictionary_->text().data(),
diff --git a/chromium/net/base/sdch_filter.h b/chromium/net/filter/sdch_filter.h
index 9f7cf9ae9c8..3cea340a9b2 100644
--- a/chromium/net/base/sdch_filter.h
+++ b/chromium/net/filter/sdch_filter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -11,15 +11,15 @@
// SdchFilter is also a subclass of Filter. See the latter's header file
// filter.h for sample usage.
-#ifndef NET_BASE_SDCH_FILTER_H_
-#define NET_BASE_SDCH_FILTER_H_
+#ifndef NET_FILTER_SDCH_FILTER_H_
+#define NET_FILTER_SDCH_FILTER_H_
#include <string>
#include "base/memory/scoped_ptr.h"
-#include "net/base/filter.h"
#include "net/base/net_export.h"
#include "net/base/sdch_manager.h"
+#include "net/filter/filter.h"
namespace open_vcdiff {
class VCDiffStreamingDecoder;
@@ -95,6 +95,13 @@ class NET_EXPORT_PRIVATE SdchFilter : public Filter {
// That char* data is part of the dictionary_ we hold a reference to.
scoped_refptr<SdchManager::Dictionary> dictionary_;
+ // We keep a copy of the URLRequestContext for use in the destructor, (at
+ // which point GetURLRequestContext() will likely return null because of
+ // the disassociation of the URLRequest from the URLRequestJob). This is
+ // safe because the URLRequestJob (and any filters) are guaranteed to be
+ // deleted before the URLRequestContext is destroyed.
+ const URLRequestContext* const url_request_context_;
+
// The decoder may demand a larger output buffer than the target of
// ReadFilteredData so we buffer the excess output between calls.
std::string dest_buffer_excess_;
@@ -125,4 +132,4 @@ class NET_EXPORT_PRIVATE SdchFilter : public Filter {
} // namespace net
-#endif // NET_BASE_SDCH_FILTER_H_
+#endif // NET_FILTER_SDCH_FILTER_H_
diff --git a/chromium/net/base/sdch_filter_unittest.cc b/chromium/net/filter/sdch_filter_unittest.cc
index 1cc70cb58be..28da615c2e9 100644
--- a/chromium/net/base/sdch_filter_unittest.cc
+++ b/chromium/net/filter/sdch_filter_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -10,10 +10,10 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "net/base/filter.h"
#include "net/base/io_buffer.h"
-#include "net/base/mock_filter_context.h"
-#include "net/base/sdch_filter.h"
+#include "net/filter/mock_filter_context.h"
+#include "net/filter/sdch_filter.h"
+#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_http_job.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/zlib.h"
@@ -55,16 +55,24 @@ class SdchFilterTest : public testing::Test {
vcdiff_compressed_data_(kSdchCompressedTestData,
sizeof(kSdchCompressedTestData) - 1),
expanded_(kTestData, sizeof(kTestData) - 1),
- sdch_manager_(new SdchManager) {
+ sdch_manager_(new SdchManager),
+ filter_context_(new MockFilterContext) {
+ URLRequestContext* url_request_context =
+ filter_context_->GetModifiableURLRequestContext();
+
+ url_request_context->set_sdch_manager(sdch_manager_.get());
}
+ MockFilterContext* filter_context() { return filter_context_.get(); }
+
std::string NewSdchCompressedData(const std::string dictionary);
const std::string test_vcdiff_dictionary_;
const std::string vcdiff_compressed_data_;
const std::string expanded_; // Desired final, decompressed data.
- scoped_ptr<SdchManager> sdch_manager_; // A singleton database.
+ scoped_ptr<SdchManager> sdch_manager_;
+ scoped_ptr<MockFilterContext> filter_context_;
};
std::string SdchFilterTest::NewSdchCompressedData(
@@ -153,10 +161,9 @@ TEST_F(SdchFilterTest, EmptyInputOk) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
char output_buffer[20];
- MockFilterContext filter_context;
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// With no input data, try to read output.
@@ -173,12 +180,11 @@ TEST_F(SdchFilterTest, PassThroughWhenTentative) {
// Selective a tentative filter (which can fall back to pass through).
filter_types.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
char output_buffer[20];
- MockFilterContext filter_context;
// Response code needs to be 200 to allow a pass through.
- filter_context.SetResponseCode(200);
+ filter_context()->SetResponseCode(200);
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Supply enough data to force a pass-through mode..
std::string non_gzip_content("not GZIPed data");
@@ -211,14 +217,13 @@ TEST_F(SdchFilterTest, RefreshBadReturnCode) {
// Selective a tentative filter (which can fall back to pass through).
filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
char output_buffer[20];
- MockFilterContext filter_context;
// Response code needs to be 200 to allow a pass through.
- filter_context.SetResponseCode(403);
+ filter_context()->SetResponseCode(403);
// Meta refresh will only appear for html content
- filter_context.SetMimeType("text/html");
+ filter_context()->SetMimeType("text/html");
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Supply enough data to force a pass-through mode, which means we have
// provided more than 9 characters that can't be a dictionary hash.
@@ -252,15 +257,14 @@ TEST_F(SdchFilterTest, ErrorOnBadReturnCode) {
// Selective a tentative filter (which can fall back to pass through).
filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
char output_buffer[20];
- MockFilterContext filter_context;
// Response code needs to be 200 to allow a pass through.
- filter_context.SetResponseCode(403);
+ filter_context()->SetResponseCode(403);
// Meta refresh will only appear for html content, so set to something else
// to induce an error (we can't meta refresh).
- filter_context.SetMimeType("anything");
+ filter_context()->SetMimeType("anything");
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Supply enough data to force a pass-through mode, which means we have
// provided more than 9 characters that can't be a dictionary hash.
@@ -289,14 +293,13 @@ TEST_F(SdchFilterTest, ErrorOnBadReturnCodeWithHtml) {
// Selective a tentative filter (which can fall back to pass through).
filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
char output_buffer[20];
- MockFilterContext filter_context;
// Response code needs to be 200 to allow a pass through.
- filter_context.SetResponseCode(403);
+ filter_context()->SetResponseCode(403);
// Meta refresh will only appear for html content
- filter_context.SetMimeType("text/html");
+ filter_context()->SetMimeType("text/html");
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Supply enough data to force a pass-through mode, which means we have
// provided more than 9 characters that can't be a dictionary hash.
@@ -330,10 +333,9 @@ TEST_F(SdchFilterTest, BasicBadDictionary) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
char output_buffer[20];
- MockFilterContext filter_context;
std::string url_string("http://ignore.com");
- filter_context.SetURL(GURL(url_string));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Supply bogus data (which doesn't yet specify a full dictionary hash).
// Dictionary hash is 8 characters followed by a null.
@@ -373,9 +375,9 @@ TEST_F(SdchFilterTest, BasicBadDictionary) {
EXPECT_EQ(0, output_bytes_or_buffer_size);
EXPECT_EQ(Filter::FILTER_ERROR, status);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
- SdchManager::ClearBlacklistings();
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
+ EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
+ sdch_manager_->ClearBlacklistings();
+ EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
}
TEST_F(SdchFilterTest, DictionaryAddOnce) {
@@ -392,12 +394,15 @@ TEST_F(SdchFilterTest, DictionaryAddOnce) {
const std::string kSampleDomain2 = "sdchtest2.com";
- // Construct a second SDCH dictionary from a VCDIFF dictionary.
- std::string dictionary2(NewSdchDictionary(kSampleDomain2));
+ // Don't test adding a second dictionary if our limits are tight.
+ if (SdchManager::kMaxDictionaryCount > 1) {
+ // Construct a second SDCH dictionary from a VCDIFF dictionary.
+ std::string dictionary2(NewSdchDictionary(kSampleDomain2));
- std::string url_string2 = "http://" + kSampleDomain2;
- GURL url2(url_string2);
- EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary2, url2));
+ std::string url_string2 = "http://" + kSampleDomain2;
+ GURL url2(url_string2);
+ EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary2, url2));
+ }
}
TEST_F(SdchFilterTest, BasicDictionary) {
@@ -415,10 +420,9 @@ TEST_F(SdchFilterTest, BasicDictionary) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(url);
+ filter_context()->SetURL(url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
size_t feed_block_size = 100;
size_t output_block_size = 100;
@@ -428,7 +432,7 @@ TEST_F(SdchFilterTest, BasicDictionary) {
EXPECT_EQ(output, expanded_);
// Decode with really small buffers (size 1) to check for edge effects.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 1;
output_block_size = 1;
@@ -453,9 +457,8 @@ TEST_F(SdchFilterTest, NoDecodeHttps) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("https://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("https://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -484,9 +487,8 @@ TEST_F(SdchFilterTest, NoDecodeFtp) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("ftp://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("ftp://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -511,9 +513,8 @@ TEST_F(SdchFilterTest, NoDecodeFileColon) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("file://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("file://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -538,9 +539,8 @@ TEST_F(SdchFilterTest, NoDecodeAboutColon) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("about://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("about://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -565,9 +565,8 @@ TEST_F(SdchFilterTest, NoDecodeJavaScript) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("javascript://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("javascript://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -592,9 +591,8 @@ TEST_F(SdchFilterTest, CanStillDecodeHttp) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetURL(GURL("http://" + kSampleDomain));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL("http://" + kSampleDomain));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
const size_t feed_block_size(100);
const size_t output_block_size(100);
@@ -621,10 +619,9 @@ TEST_F(SdchFilterTest, CrossDomainDictionaryUse) {
// Decode with content arriving from the "wrong" domain.
// This tests SdchManager::CanSet().
- MockFilterContext filter_context;
GURL wrong_domain_url("http://www.wrongdomain.com");
- filter_context.SetURL(wrong_domain_url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(wrong_domain_url);
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
size_t feed_block_size = 100;
size_t output_block_size = 100;
@@ -633,13 +630,18 @@ TEST_F(SdchFilterTest, CrossDomainDictionaryUse) {
filter.get(), &output));
EXPECT_EQ(output.size(), 0u); // No output written.
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url));
- SdchManager::ClearBlacklistings();
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url));
+ EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
+ EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(wrong_domain_url));
+ sdch_manager_->ClearBlacklistings();
+ EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(wrong_domain_url));
}
TEST_F(SdchFilterTest, DictionaryPathValidation) {
+ // Can't test path distinction between dictionaries if we aren't allowed
+ // more than one dictionary.
+ if (SdchManager::kMaxDictionaryCount <= 1)
+ return;
+
// Construct a valid SDCH dictionary from a VCDIFF dictionary.
const std::string kSampleDomain = "sdchtest.com";
std::string dictionary(NewSdchDictionary(kSampleDomain));
@@ -661,9 +663,8 @@ TEST_F(SdchFilterTest, DictionaryPathValidation) {
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
// Test decode the path data, arriving from a valid path.
- MockFilterContext filter_context;
- filter_context.SetURL(GURL(url_string + path));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string + path));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
size_t feed_block_size = 100;
size_t output_block_size = 100;
@@ -674,8 +675,8 @@ TEST_F(SdchFilterTest, DictionaryPathValidation) {
EXPECT_EQ(output, expanded_);
// Test decode the path data, arriving from a invalid path.
- filter_context.SetURL(GURL(url_string));
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 100;
output_block_size = 100;
@@ -684,12 +685,17 @@ TEST_F(SdchFilterTest, DictionaryPathValidation) {
output_block_size, filter.get(), &output));
EXPECT_EQ(output.size(), 0u); // No output written.
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
- SdchManager::ClearBlacklistings();
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
+ EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
+ sdch_manager_->ClearBlacklistings();
+ EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
}
TEST_F(SdchFilterTest, DictionaryPortValidation) {
+ // Can't test port distinction between dictionaries if we aren't allowed
+ // more than one dictionary.
+ if (SdchManager::kMaxDictionaryCount <= 1)
+ return;
+
// Construct a valid SDCH dictionary from a VCDIFF dictionary.
const std::string kSampleDomain = "sdchtest.com";
std::string dictionary(NewSdchDictionary(kSampleDomain));
@@ -714,9 +720,8 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) {
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
// Test decode the port data, arriving from a valid port.
- MockFilterContext filter_context;
- filter_context.SetURL(GURL(url_string + ":" + port));
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string + ":" + port));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
size_t feed_block_size = 100;
size_t output_block_size = 100;
@@ -726,8 +731,8 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) {
EXPECT_EQ(output, expanded_);
// Test decode the port data, arriving from a valid (default) port.
- filter_context.SetURL(GURL(url_string)); // Default port.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string)); // Default port.
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 100;
output_block_size = 100;
@@ -737,8 +742,8 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) {
EXPECT_EQ(output, expanded_);
// Test decode the port data, arriving from a invalid port.
- filter_context.SetURL(GURL(url_string + ":" + port + "1"));
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(GURL(url_string + ":" + port + "1"));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 100;
output_block_size = 100;
@@ -747,9 +752,9 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) {
output_block_size, filter.get(), &output));
EXPECT_EQ(output.size(), 0u); // No output written.
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
- SdchManager::ClearBlacklistings();
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string)));
+ EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
+ sdch_manager_->ClearBlacklistings();
+ EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
}
//------------------------------------------------------------------------------
@@ -845,10 +850,9 @@ TEST_F(SdchFilterTest, FilterChaining) {
CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size());
CHECK_GT(kLargeInputBufferSize, sdch_compressed.size());
CHECK_GT(kLargeInputBufferSize, expanded_.size());
- MockFilterContext filter_context;
- filter_context.SetURL(url);
+ filter_context()->SetURL(url);
scoped_ptr<Filter> filter(
- SdchFilterChainingTest::Factory(filter_types, filter_context,
+ SdchFilterChainingTest::Factory(filter_types, *filter_context(),
kLargeInputBufferSize));
EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
filter->stream_buffer_size());
@@ -875,9 +879,9 @@ TEST_F(SdchFilterTest, FilterChaining) {
// We'll go even further, and force the chain to flush the buffer between the
// two filters more than once (that is why we multiply by 2).
CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size());
- filter_context.SetURL(url);
+ filter_context()->SetURL(url);
filter.reset(
- SdchFilterChainingTest::Factory(filter_types, filter_context,
+ SdchFilterChainingTest::Factory(filter_types, *filter_context(),
kMidSizedInputBufferSize));
EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize),
filter->stream_buffer_size());
@@ -890,7 +894,7 @@ TEST_F(SdchFilterTest, FilterChaining) {
EXPECT_EQ(output, expanded_);
// Next try with a tiny input and output buffer to cover edge effects.
- filter.reset(SdchFilterChainingTest::Factory(filter_types, filter_context,
+ filter.reset(SdchFilterChainingTest::Factory(filter_types, *filter_context(),
kLargeInputBufferSize));
EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
filter->stream_buffer_size());
@@ -923,17 +927,16 @@ TEST_F(SdchFilterTest, DefaultGzipIfSdch) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_SDCH);
- MockFilterContext filter_context;
- filter_context.SetMimeType("anything/mime");
- filter_context.SetSdchResponse(true);
- Filter::FixupEncodingTypes(filter_context, &filter_types);
+ filter_context()->SetMimeType("anything/mime");
+ filter_context()->SetSdchResponse(true);
+ Filter::FixupEncodingTypes(*filter_context(), &filter_types);
ASSERT_EQ(filter_types.size(), 2u);
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH);
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
// First try with a large buffer (larger than test input, or compressed data).
- filter_context.SetURL(url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(url);
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Verify that chained filter is waiting for data.
@@ -950,7 +953,7 @@ TEST_F(SdchFilterTest, DefaultGzipIfSdch) {
EXPECT_EQ(output, expanded_);
// Next try with a tiny buffer to cover edge effects.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 1;
output_block_size = 1;
@@ -982,18 +985,17 @@ TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_GZIP);
- MockFilterContext filter_context;
- filter_context.SetMimeType("anything/mime");
- filter_context.SetSdchResponse(true);
- Filter::FixupEncodingTypes(filter_context, &filter_types);
+ filter_context()->SetMimeType("anything/mime");
+ filter_context()->SetSdchResponse(true);
+ Filter::FixupEncodingTypes(*filter_context(), &filter_types);
ASSERT_EQ(filter_types.size(), 3u);
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
// First try with a large buffer (larger than test input, or compressed data).
- filter_context.SetURL(url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(url);
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Verify that chained filter is waiting for data.
@@ -1010,7 +1012,7 @@ TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) {
EXPECT_EQ(output, expanded_);
// Next try with a tiny buffer to cover edge effects.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 1;
output_block_size = 1;
@@ -1040,17 +1042,16 @@ TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) {
// System should automatically add the missing (optional) sdch,gzip.
std::vector<Filter::FilterType> filter_types;
- MockFilterContext filter_context;
- filter_context.SetMimeType("anything/mime");
- filter_context.SetSdchResponse(true);
- Filter::FixupEncodingTypes(filter_context, &filter_types);
+ filter_context()->SetMimeType("anything/mime");
+ filter_context()->SetSdchResponse(true);
+ Filter::FixupEncodingTypes(*filter_context(), &filter_types);
ASSERT_EQ(filter_types.size(), 2u);
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
// First try with a large buffer (larger than test input, or compressed data).
- filter_context.SetURL(url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
+ filter_context()->SetURL(url);
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Verify that chained filter is waiting for data.
@@ -1067,7 +1068,7 @@ TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) {
EXPECT_EQ(output, expanded_);
// Next try with a tiny buffer to cover edge effects.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 1;
output_block_size = 1;
@@ -1102,19 +1103,17 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) {
std::vector<Filter::FilterType> filter_types;
filter_types.push_back(Filter::FILTER_TYPE_GZIP);
- MockFilterContext filter_context;
- filter_context.SetMimeType("anything/mime");
- filter_context.SetSdchResponse(true);
- Filter::FixupEncodingTypes(filter_context, &filter_types);
+ filter_context()->SetMimeType("anything/mime");
+ filter_context()->SetSdchResponse(true);
+ Filter::FixupEncodingTypes(*filter_context(), &filter_types);
ASSERT_EQ(filter_types.size(), 3u);
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
// First try with a large buffer (larger than test input, or compressed data).
- filter_context.SetURL(url);
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
-
+ filter_context()->SetURL(url);
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// Verify that chained filter is waiting for data.
char tiny_output_buffer[10];
@@ -1130,7 +1129,7 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) {
EXPECT_EQ(output, expanded_);
// Next try with a tiny buffer to cover edge effects.
- filter.reset(Filter::Factory(filter_types, filter_context));
+ filter.reset(Filter::Factory(filter_types, *filter_context()));
feed_block_size = 1;
output_block_size = 1;
@@ -1140,254 +1139,4 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) {
EXPECT_EQ(output, expanded_);
}
-TEST_F(SdchFilterTest, DomainSupported) {
- GURL google_url("http://www.google.com");
-
- net::SdchManager::EnableSdchSupport(false);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(google_url));
- net::SdchManager::EnableSdchSupport(true);
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(google_url));
-}
-
-TEST_F(SdchFilterTest, DomainBlacklisting) {
- GURL test_url("http://www.test.com");
- GURL google_url("http://www.google.com");
-
- SdchManager::BlacklistDomain(test_url);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(test_url));
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(google_url));
-
- SdchManager::BlacklistDomain(google_url);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(google_url));
-}
-
-TEST_F(SdchFilterTest, DomainBlacklistingCaseSensitivity) {
- GURL test_url("http://www.TesT.com");
- GURL test2_url("http://www.tEst.com");
-
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(test_url));
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(test2_url));
- SdchManager::BlacklistDomain(test_url);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(test2_url));
-}
-
-TEST_F(SdchFilterTest, BlacklistingReset) {
- GURL gurl("http://mytest.DoMain.com");
- std::string domain(gurl.host());
-
- SdchManager::ClearBlacklistings();
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0);
- EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), 0);
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl));
-}
-
-TEST_F(SdchFilterTest, BlacklistingSingleBlacklist) {
- GURL gurl("http://mytest.DoMain.com");
- std::string domain(gurl.host());
- SdchManager::ClearBlacklistings();
-
- SdchManager::Global()->BlacklistDomain(gurl);
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 1);
- EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), 1);
-
- // Check that any domain lookup reduces the blacklist counter.
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(gurl));
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0);
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl));
-}
-
-TEST_F(SdchFilterTest, BlacklistingExponential) {
- GURL gurl("http://mytest.DoMain.com");
- std::string domain(gurl.host());
- SdchManager::ClearBlacklistings();
-
- int exponential = 1;
- for (int i = 1; i < 100; ++i) {
- SdchManager::Global()->BlacklistDomain(gurl);
- EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), exponential);
-
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), exponential);
- EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(gurl));
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), exponential - 1);
-
- // Simulate a large number of domain checks (which eventually remove the
- // blacklisting).
- SdchManager::ClearDomainBlacklisting(domain);
- EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0);
- EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl));
-
- // Predict what exponential backoff will be.
- exponential = 1 + 2 * exponential;
- if (exponential < 0)
- exponential = INT_MAX; // We don't wrap.
- }
-}
-
-TEST_F(SdchFilterTest, CanSetExactMatchDictionary) {
- std::string dictionary_domain("x.y.z.google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Perfect match should work.
- EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://" + dictionary_domain)));
-}
-
-TEST_F(SdchFilterTest, FailToSetDomainMismatchDictionary) {
- std::string dictionary_domain("x.y.z.google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Fail the "domain match" requirement.
- EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://y.z.google.com")));
-}
-
-TEST_F(SdchFilterTest, FailToSetDotHostPrefixDomainDictionary) {
- std::string dictionary_domain("x.y.z.google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Fail the HD with D being the domain and H having a dot requirement.
- EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://w.x.y.z.google.com")));
-}
-
-TEST_F(SdchFilterTest, FailToSetRepeatPrefixWithDotDictionary) {
- // Make sure that a prefix that matches the domain postfix won't confuse
- // the validation checks.
- std::string dictionary_domain("www.google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Fail the HD with D being the domain and H having a dot requirement.
- EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://www.google.com.www.google.com")));
-}
-
-TEST_F(SdchFilterTest, CanSetLeadingDotDomainDictionary) {
- // Make sure that a prefix that matches the domain postfix won't confuse
- // the validation checks.
- std::string dictionary_domain(".google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Verify that a leading dot in the domain is acceptable, as long as the host
- // name does not contain any dots preceding the matched domain name.
- EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://www.google.com")));
-}
-
-// Make sure the order of the tests is not helping us or confusing things.
-// See test CanSetExactMatchDictionary above for first try.
-TEST_F(SdchFilterTest, CanStillSetExactMatchDictionary) {
- std::string dictionary_domain("x.y.z.google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- // Perfect match should *STILL* work.
- EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://" + dictionary_domain)));
-}
-
-// Make sure the DOS protection precludes the addition of too many dictionaries.
-TEST_F(SdchFilterTest, TooManyDictionaries) {
- std::string dictionary_domain(".google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- size_t count = 0;
- while (count <= SdchManager::kMaxDictionaryCount + 1) {
- if (!sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://www.google.com")))
- break;
-
- dictionary_text += " "; // Create dictionary with different SHA signature.
- ++count;
- }
- EXPECT_EQ(SdchManager::kMaxDictionaryCount, count);
-}
-
-TEST_F(SdchFilterTest, DictionaryNotTooLarge) {
- std::string dictionary_domain(".google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- dictionary_text.append(
- SdchManager::kMaxDictionarySize - dictionary_text.size(), ' ');
- EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://" + dictionary_domain)));
-}
-
-TEST_F(SdchFilterTest, DictionaryTooLarge) {
- std::string dictionary_domain(".google.com");
- std::string dictionary_text(NewSdchDictionary(dictionary_domain));
-
- dictionary_text.append(
- SdchManager::kMaxDictionarySize + 1 - dictionary_text.size(), ' ');
- EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text,
- GURL("http://" + dictionary_domain)));
-}
-
-TEST_F(SdchFilterTest, PathMatch) {
- bool (*PathMatch)(const std::string& path, const std::string& restriction) =
- SdchManager::Dictionary::PathMatch;
- // Perfect match is supported.
- EXPECT_TRUE(PathMatch("/search", "/search"));
- EXPECT_TRUE(PathMatch("/search/", "/search/"));
-
- // Prefix only works if last character of restriction is a slash, or first
- // character in path after a match is a slash. Validate each case separately.
-
- // Rely on the slash in the path (not at the end of the restriction).
- EXPECT_TRUE(PathMatch("/search/something", "/search"));
- EXPECT_TRUE(PathMatch("/search/s", "/search"));
- EXPECT_TRUE(PathMatch("/search/other", "/search"));
- EXPECT_TRUE(PathMatch("/search/something", "/search"));
-
- // Rely on the slash at the end of the restriction.
- EXPECT_TRUE(PathMatch("/search/something", "/search/"));
- EXPECT_TRUE(PathMatch("/search/s", "/search/"));
- EXPECT_TRUE(PathMatch("/search/other", "/search/"));
- EXPECT_TRUE(PathMatch("/search/something", "/search/"));
-
- // Make sure less that sufficient prefix match is false.
- EXPECT_FALSE(PathMatch("/sear", "/search"));
- EXPECT_FALSE(PathMatch("/", "/search"));
- EXPECT_FALSE(PathMatch(std::string(), "/search"));
-
- // Add examples with several levels of direcories in the restriction.
- EXPECT_FALSE(PathMatch("/search/something", "search/s"));
- EXPECT_FALSE(PathMatch("/search/", "/search/s"));
-
- // Make sure adding characters to path will also fail.
- EXPECT_FALSE(PathMatch("/searching", "/search/"));
- EXPECT_FALSE(PathMatch("/searching", "/search"));
-
- // Make sure we're case sensitive.
- EXPECT_FALSE(PathMatch("/ABC", "/abc"));
- EXPECT_FALSE(PathMatch("/abc", "/ABC"));
-}
-
-// The following are only applicable while we have a latency test in the code,
-// and can be removed when that functionality is stripped.
-TEST_F(SdchFilterTest, LatencyTestControls) {
- GURL url("http://www.google.com");
- GURL url2("http://www.google2.com");
-
- // First make sure we default to false.
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url));
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2));
-
- // That we can set each to true.
- sdch_manager_->SetAllowLatencyExperiment(url, true);
- EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url));
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2));
-
- sdch_manager_->SetAllowLatencyExperiment(url2, true);
- EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url));
- EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url2));
-
- // And can reset them to false.
- sdch_manager_->SetAllowLatencyExperiment(url, false);
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url));
- EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url2));
-
- sdch_manager_->SetAllowLatencyExperiment(url2, false);
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url));
- EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2));
-}
-
} // namespace net
diff --git a/chromium/net/ftp/ftp_auth_cache_unittest.cc b/chromium/net/ftp/ftp_auth_cache_unittest.cc
index 153d08be49f..bd47b647d60 100644
--- a/chromium/net/ftp/ftp_auth_cache_unittest.cc
+++ b/chromium/net/ftp/ftp_auth_cache_unittest.cc
@@ -11,6 +11,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
using net::FtpAuthCache;
namespace {
diff --git a/chromium/net/ftp/ftp_directory_listing_parser.cc b/chromium/net/ftp/ftp_directory_listing_parser.cc
index 03b77bb8205..d19e4a2c9fe 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser.cc
@@ -110,7 +110,7 @@ int DecodeAndParse(const std::string& text,
&converted_text)) {
for (size_t j = 0; j < arraysize(kNewlineSeparators); j++) {
int rv = ParseListing(converted_text,
- ASCIIToUTF16(kNewlineSeparators[j]),
+ base::ASCIIToUTF16(kNewlineSeparators[j]),
encodings[i],
current_time,
entries,
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
index 41e29b60a9b..2d23b6aecf5 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_ls.cc
@@ -135,7 +135,7 @@ bool ParseFtpDirectoryListingLs(
continue;
std::vector<base::string16> columns;
- base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+ base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns);
// Some FTP servers put a "total n" line at the beginning of the listing
// (n is an integer). Allow such a line, but only once, and only if it's
@@ -167,7 +167,7 @@ bool ParseFtpDirectoryListingLs(
// All those messages have in common is the string ".:",
// where "." means the current directory, and ":" separates it
// from the rest of the message, which may be empty.
- if (lines[i].find(ASCIIToUTF16(".:")) != base::string16::npos)
+ if (lines[i].find(base::ASCIIToUTF16(".:")) != base::string16::npos)
continue;
return false;
@@ -216,7 +216,8 @@ bool ParseFtpDirectoryListingLs(
column_offset + 1);
if (entry.type == FtpDirectoryListingEntry::SYMLINK) {
- base::string16::size_type pos = entry.name.rfind(ASCIIToUTF16(" -> "));
+ base::string16::size_type pos =
+ entry.name.rfind(base::ASCIIToUTF16(" -> "));
// We don't require the " -> " to be present. Some FTP servers don't send
// the symlink target, possibly for security reasons.
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_netware.cc b/chromium/net/ftp/ftp_directory_listing_parser_netware.cc
index 2306513fa60..20716ab9d64 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_netware.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_netware.cc
@@ -39,15 +39,17 @@ bool ParseFtpDirectoryListingNetware(
const std::vector<base::string16>& lines,
const base::Time& current_time,
std::vector<FtpDirectoryListingEntry>* entries) {
- if (!lines.empty() && !StartsWith(lines[0], ASCIIToUTF16("total "), true))
+ if (!lines.empty() &&
+ !StartsWith(lines[0], base::ASCIIToUTF16("total "), true)) {
return false;
+ }
for (size_t i = 1U; i < lines.size(); i++) {
if (lines[i].empty())
continue;
std::vector<base::string16> columns;
- base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+ base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns);
if (columns.size() < 8)
return false;
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_netware_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
index 4863713ca86..102552c43f6 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
@@ -36,7 +36,7 @@ TEST_F(FtpDirectoryListingParserNetwareTest, Good) {
GetSingleLineTestCase(good_cases[i].input));
// The parser requires a "total n" line before accepting regular input.
- lines.insert(lines.begin(), ASCIIToUTF16("total 1"));
+ lines.insert(lines.begin(), base::ASCIIToUTF16("total 1"));
std::vector<FtpDirectoryListingEntry> entries;
EXPECT_TRUE(ParseFtpDirectoryListingNetware(lines,
@@ -63,7 +63,7 @@ TEST_F(FtpDirectoryListingParserNetwareTest, Bad) {
std::vector<base::string16> lines(GetSingleLineTestCase(bad_cases[i]));
// The parser requires a "total n" line before accepting regular input.
- lines.insert(lines.begin(), ASCIIToUTF16("total 1"));
+ lines.insert(lines.begin(), base::ASCIIToUTF16("total 1"));
std::vector<FtpDirectoryListingEntry> entries;
EXPECT_FALSE(ParseFtpDirectoryListingNetware(lines,
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_os2.cc b/chromium/net/ftp/ftp_directory_listing_parser_os2.cc
index 9a0cce53c81..094a9bdb8d8 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_os2.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_os2.cc
@@ -23,7 +23,7 @@ bool ParseFtpDirectoryListingOS2(
continue;
std::vector<base::string16> columns;
- base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+ base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns);
// Every line of the listing consists of the following:
//
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
index f27007aef37..145b90cc7b2 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.cc
@@ -91,7 +91,7 @@ TEST_P(FtpDirectoryListingParserTest, Parse) {
ADD_FAILURE() << "invalid gold test data: " << type;
}
- EXPECT_EQ(UTF8ToUTF16(name), entry.name);
+ EXPECT_EQ(base::UTF8ToUTF16(name), entry.name);
EXPECT_EQ(size, entry.size);
base::Time::Exploded time_exploded;
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
index 9ac42d867b5..22c2cf90740 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
+++ b/chromium/net/ftp/ftp_directory_listing_parser_unittest.h
@@ -32,7 +32,7 @@ class FtpDirectoryListingParserTest : public testing::Test {
std::vector<base::string16> GetSingleLineTestCase(const std::string& text) {
std::vector<base::string16> lines;
- lines.push_back(UTF8ToUTF16(text));
+ lines.push_back(base::UTF8ToUTF16(text));
return lines;
}
@@ -43,7 +43,7 @@ class FtpDirectoryListingParserTest : public testing::Test {
FtpDirectoryListingEntry entry = entries[0];
EXPECT_EQ(test_case.type, entry.type);
- EXPECT_EQ(UTF8ToUTF16(test_case.filename), entry.name);
+ EXPECT_EQ(base::UTF8ToUTF16(test_case.filename), entry.name);
EXPECT_EQ(test_case.size, entry.size);
base::Time::Exploded time_exploded;
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
index 4b44d73b5b5..b52e3c807a6 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_vms.cc
@@ -54,7 +54,7 @@ bool ParseVmsFilename(const base::string16& raw_filename,
}
bool ParseVmsFilesize(const base::string16& input, int64* size) {
- if (ContainsOnlyChars(input, ASCIIToUTF16("*"))) {
+ if (base::ContainsOnlyChars(input, base::ASCIIToUTF16("*"))) {
// Response consisting of asterisks means unknown size.
*size = -1;
return true;
@@ -141,7 +141,7 @@ bool LooksLikeVMSError(const base::string16& text) {
};
for (size_t i = 0; i < arraysize(kPermissionDeniedMessages); i++) {
- if (text.find(ASCIIToUTF16(kPermissionDeniedMessages[i])) !=
+ if (text.find(base::ASCIIToUTF16(kPermissionDeniedMessages[i])) !=
base::string16::npos)
return true;
}
@@ -210,7 +210,7 @@ bool ParseFtpDirectoryListingVms(
if (lines[i].empty())
continue;
- if (StartsWith(lines[i], ASCIIToUTF16("Total of "), true)) {
+ if (StartsWith(lines[i], base::ASCIIToUTF16("Total of "), true)) {
// After the "total" line, all following lines must be empty.
for (size_t j = i + 1; j < lines.size(); j++)
if (!lines[j].empty())
@@ -230,7 +230,7 @@ bool ParseFtpDirectoryListingVms(
}
std::vector<base::string16> columns;
- base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+ base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns);
if (columns.size() == 1) {
// There can be no continuation if the current line is the last one.
@@ -248,8 +248,8 @@ bool ParseFtpDirectoryListingVms(
// Join the current and next line and split them into columns.
base::SplitString(
- CollapseWhitespace(lines[i - 1] + ASCIIToUTF16(" ") + lines[i],
- false),
+ base::CollapseWhitespace(
+ lines[i - 1] + base::ASCIIToUTF16(" ") + lines[i], false),
' ',
&columns);
}
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
index 3690f7e18c6..cef86c092e3 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
@@ -11,6 +11,8 @@
#include "base/strings/utf_string_conversions.h"
#include "net/ftp/ftp_directory_listing_parser_vms.h"
+using base::ASCIIToUTF16;
+
namespace net {
namespace {
diff --git a/chromium/net/ftp/ftp_directory_listing_parser_windows.cc b/chromium/net/ftp/ftp_directory_listing_parser_windows.cc
index 5ec4a523434..2a502205877 100644
--- a/chromium/net/ftp/ftp_directory_listing_parser_windows.cc
+++ b/chromium/net/ftp/ftp_directory_listing_parser_windows.cc
@@ -23,7 +23,7 @@ bool ParseFtpDirectoryListingWindows(
continue;
std::vector<base::string16> columns;
- base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+ base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns);
// Every line of the listing consists of the following:
//
diff --git a/chromium/net/ftp/ftp_network_transaction.cc b/chromium/net/ftp/ftp_network_transaction.cc
index 23a043247d5..78207156475 100644
--- a/chromium/net/ftp/ftp_network_transaction.cc
+++ b/chromium/net/ftp/ftp_network_transaction.cc
@@ -152,7 +152,7 @@ bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
return false;
std::string line(response.lines[0]);
- if (!IsStringASCII(line))
+ if (!base::IsStringASCII(line))
return false;
if (line.length() < 2)
return false;
@@ -256,8 +256,8 @@ int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
GetIdentityFromURL(request_->url, &username, &password);
credentials_.Set(username, password);
} else {
- credentials_.Set(ASCIIToUTF16("anonymous"),
- ASCIIToUTF16("chrome@example.com"));
+ credentials_.Set(base::ASCIIToUTF16("anonymous"),
+ base::ASCIIToUTF16("chrome@example.com"));
}
DetectTypecode();
@@ -706,7 +706,7 @@ int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
// connection when anonymous login is not permitted. For more details
// see http://crbug.com/25023.
if (command_sent_ == COMMAND_USER &&
- credentials_.username() == ASCIIToUTF16("anonymous")) {
+ credentials_.username() == base::ASCIIToUTF16("anonymous")) {
response_.needs_auth = true;
}
return Stop(ERR_EMPTY_RESPONSE);
@@ -753,7 +753,7 @@ int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
// USER Command.
int FtpNetworkTransaction::DoCtrlWriteUSER() {
- std::string command = "USER " + UTF16ToUTF8(credentials_.username());
+ std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
if (!IsValidFTPCommandString(command))
return Stop(ERR_MALFORMED_IDENTITY);
@@ -784,7 +784,7 @@ int FtpNetworkTransaction::ProcessResponseUSER(
// PASS command.
int FtpNetworkTransaction::DoCtrlWritePASS() {
- std::string command = "PASS " + UTF16ToUTF8(credentials_.password());
+ std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
if (!IsValidFTPCommandString(command))
return Stop(ERR_MALFORMED_IDENTITY);
@@ -830,7 +830,7 @@ int FtpNetworkTransaction::ProcessResponseSYST(
// The response should be ASCII, which allows us to do case-insensitive
// comparisons easily. If it is not ASCII, we leave the system type
// as unknown.
- if (IsStringASCII(line)) {
+ if (base::IsStringASCII(line)) {
line = StringToLowerASCII(line);
// Remove all whitespace, to correctly handle cases like fancy "V M S"
diff --git a/chromium/net/ftp/ftp_network_transaction_unittest.cc b/chromium/net/ftp/ftp_network_transaction_unittest.cc
index a244cc51c5a..537ce147e52 100644
--- a/chromium/net/ftp/ftp_network_transaction_unittest.cc
+++ b/chromium/net/ftp/ftp_network_transaction_unittest.cc
@@ -1311,8 +1311,8 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartUser) {
ASSERT_EQ(ERR_IO_PENDING,
transaction_.RestartWithAuth(
AuthCredentials(
- ASCIIToUTF16("foo\nownz0red"),
- ASCIIToUTF16("innocent")),
+ base::ASCIIToUTF16("foo\nownz0red"),
+ base::ASCIIToUTF16("innocent")),
callback_.callback()));
EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult());
}
@@ -1346,8 +1346,8 @@ TEST_P(FtpNetworkTransactionTest, EvilRestartPassword) {
mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
ASSERT_EQ(ERR_IO_PENDING,
transaction_.RestartWithAuth(
- AuthCredentials(ASCIIToUTF16("innocent"),
- ASCIIToUTF16("foo\nownz0red")),
+ AuthCredentials(base::ASCIIToUTF16("innocent"),
+ base::ASCIIToUTF16("foo\nownz0red")),
callback_.callback()));
EXPECT_EQ(ERR_MALFORMED_IDENTITY, callback_.WaitForResult());
}
diff --git a/chromium/net/ftp/ftp_util.cc b/chromium/net/ftp/ftp_util.cc
index e639c46edcc..b5580664736 100644
--- a/chromium/net/ftp/ftp_util.cc
+++ b/chromium/net/ftp/ftp_util.cc
@@ -22,6 +22,7 @@
#include "third_party/icu/source/i18n/unicode/datefmt.h"
#include "third_party/icu/source/i18n/unicode/dtfmtsym.h"
+using base::ASCIIToUTF16;
using base::StringPiece16;
// For examples of Unix<->VMS path conversions, see the unit test file. On VMS
@@ -365,7 +366,7 @@ base::string16 FtpUtil::GetStringPartAfterColumns(const base::string16& text,
}
base::string16 result(text.substr(iter.array_pos()));
- TrimWhitespace(result, TRIM_ALL, &result);
+ base::TrimWhitespace(result, base::TRIM_ALL, &result);
return result;
}
diff --git a/chromium/net/ftp/ftp_util_unittest.cc b/chromium/net/ftp/ftp_util_unittest.cc
index 2aab7f49f5c..5851e88a6ef 100644
--- a/chromium/net/ftp/ftp_util_unittest.cc
+++ b/chromium/net/ftp/ftp_util_unittest.cc
@@ -12,6 +12,9 @@
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+using base::UTF8ToUTF16;
+
namespace {
TEST(FtpUtilTest, UnixFilePathToVMS) {
diff --git a/chromium/net/http/disk_cache_based_quic_server_info.cc b/chromium/net/http/disk_cache_based_quic_server_info.cc
new file mode 100644
index 00000000000..4cf036de3ba
--- /dev/null
+++ b/chromium/net/http/disk_cache_based_quic_server_info.cc
@@ -0,0 +1,295 @@
+// Copyright 2014 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/http/disk_cache_based_quic_server_info.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+// Some APIs inside disk_cache take a handle that the caller must keep alive
+// until the API has finished its asynchronous execution.
+//
+// Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
+// operation completes causing a use-after-free.
+//
+// This data shim struct is meant to provide a location for the disk_cache
+// APIs to write into even if the originating DiskCacheBasedQuicServerInfo
+// object has been deleted. The lifetime for instances of this struct
+// should be bound to the CompletionCallback that is passed to the disk_cache
+// API. We do this by binding an instance of this struct to an unused
+// parameter for OnIOComplete() using base::Owned().
+//
+// This is a hack. A better fix is to make it so that the disk_cache APIs
+// take a Callback to a mutator for setting the output value rather than
+// writing into a raw handle. Then the caller can just pass in a Callback
+// bound to WeakPtr for itself. This callback would correctly "no-op" itself
+// when the DiskCacheBasedQuicServerInfo object is deleted.
+//
+// TODO(ajwong): Change disk_cache's API to return results via Callback.
+struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
+ CacheOperationDataShim() : backend(NULL), entry(NULL) {}
+
+ disk_cache::Backend* backend;
+ disk_cache::Entry* entry;
+};
+
+DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
+ const QuicServerId& server_id,
+ HttpCache* http_cache)
+ : QuicServerInfo(server_id),
+ weak_factory_(this),
+ data_shim_(new CacheOperationDataShim()),
+ io_callback_(
+ base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
+ weak_factory_.GetWeakPtr(),
+ base::Owned(data_shim_))), // Ownership assigned.
+ state_(GET_BACKEND),
+ ready_(false),
+ found_entry_(false),
+ server_id_(server_id),
+ http_cache_(http_cache),
+ backend_(NULL),
+ entry_(NULL) {
+}
+
+void DiskCacheBasedQuicServerInfo::Start() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(GET_BACKEND, state_);
+ DoLoop(OK);
+}
+
+int DiskCacheBasedQuicServerInfo::WaitForDataReady(
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(GET_BACKEND, state_);
+
+ if (ready_)
+ return OK;
+
+ if (!callback.is_null()) {
+ // Prevent a new callback for WaitForDataReady overwriting an existing
+ // pending callback (|user_callback_|).
+ if (!user_callback_.is_null())
+ return ERR_INVALID_ARGUMENT;
+ user_callback_ = callback;
+ }
+
+ return ERR_IO_PENDING;
+}
+
+bool DiskCacheBasedQuicServerInfo::IsDataReady() {
+ return ready_;
+}
+
+bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
+ // The data can be persisted if it has been loaded from the disk cache
+ // and there are no pending writes.
+ return ready_ && new_data_.empty();
+}
+
+void DiskCacheBasedQuicServerInfo::Persist() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(GET_BACKEND, state_);
+
+ DCHECK(new_data_.empty());
+ CHECK(ready_);
+ DCHECK(user_callback_.is_null());
+ new_data_ = Serialize();
+
+ if (!backend_)
+ return;
+
+ state_ = CREATE_OR_OPEN;
+ DoLoop(OK);
+}
+
+DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
+ DCHECK(user_callback_.is_null());
+ if (entry_)
+ entry_->Close();
+}
+
+std::string DiskCacheBasedQuicServerInfo::key() const {
+ return "quicserverinfo:" + server_id_.ToString();
+}
+
+void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
+ int rv) {
+ DCHECK_NE(NONE, state_);
+ rv = DoLoop(rv);
+ if (rv != ERR_IO_PENDING && !user_callback_.is_null()) {
+ CompletionCallback callback = user_callback_;
+ user_callback_.Reset();
+ callback.Run(rv);
+ }
+}
+
+int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
+ do {
+ switch (state_) {
+ case GET_BACKEND:
+ rv = DoGetBackend();
+ break;
+ case GET_BACKEND_COMPLETE:
+ rv = DoGetBackendComplete(rv);
+ break;
+ case OPEN:
+ rv = DoOpen();
+ break;
+ case OPEN_COMPLETE:
+ rv = DoOpenComplete(rv);
+ break;
+ case READ:
+ rv = DoRead();
+ break;
+ case READ_COMPLETE:
+ rv = DoReadComplete(rv);
+ break;
+ case WAIT_FOR_DATA_READY_DONE:
+ rv = DoWaitForDataReadyDone();
+ break;
+ case CREATE_OR_OPEN:
+ rv = DoCreateOrOpen();
+ break;
+ case CREATE_OR_OPEN_COMPLETE:
+ rv = DoCreateOrOpenComplete(rv);
+ break;
+ case WRITE:
+ rv = DoWrite();
+ break;
+ case WRITE_COMPLETE:
+ rv = DoWriteComplete(rv);
+ break;
+ case SET_DONE:
+ rv = DoSetDone();
+ break;
+ default:
+ rv = OK;
+ NOTREACHED();
+ }
+ } while (rv != ERR_IO_PENDING && state_ != NONE);
+
+ return rv;
+}
+
+int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
+ if (rv == OK) {
+ backend_ = data_shim_->backend;
+ state_ = OPEN;
+ } else {
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ }
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
+ if (rv == OK) {
+ entry_ = data_shim_->entry;
+ state_ = READ;
+ found_entry_ = true;
+ } else {
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ }
+
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
+ if (rv > 0)
+ data_.assign(read_buffer_->data(), rv);
+
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
+ state_ = SET_DONE;
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
+ if (rv != OK) {
+ state_ = SET_DONE;
+ } else {
+ entry_ = data_shim_->entry;
+ state_ = WRITE;
+ }
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoGetBackend() {
+ state_ = GET_BACKEND_COMPLETE;
+ return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoOpen() {
+ state_ = OPEN_COMPLETE;
+ return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoRead() {
+ const int32 size = entry_->GetDataSize(0 /* index */);
+ if (!size) {
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ return OK;
+ }
+
+ read_buffer_ = new IOBuffer(size);
+ state_ = READ_COMPLETE;
+ return entry_->ReadData(
+ 0 /* index */, 0 /* offset */, read_buffer_, size, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoWrite() {
+ write_buffer_ = new IOBuffer(new_data_.size());
+ memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
+ state_ = WRITE_COMPLETE;
+
+ return entry_->WriteData(
+ 0 /* index */, 0 /* offset */, write_buffer_, new_data_.size(),
+ io_callback_, true /* truncate */);
+}
+
+int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
+ DCHECK(entry_ == NULL);
+ state_ = CREATE_OR_OPEN_COMPLETE;
+ if (found_entry_) {
+ return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
+ }
+
+ return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
+ DCHECK(!ready_);
+ state_ = NONE;
+ ready_ = true;
+ // We close the entry because, if we shutdown before ::Persist is called,
+ // then we might leak a cache reference, which causes a DCHECK on shutdown.
+ if (entry_)
+ entry_->Close();
+ entry_ = NULL;
+ Parse(data_);
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoSetDone() {
+ if (entry_)
+ entry_->Close();
+ entry_ = NULL;
+ new_data_.clear();
+ state_ = NONE;
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/http/disk_cache_based_quic_server_info.h b/chromium/net/http/disk_cache_based_quic_server_info.h
new file mode 100644
index 00000000000..2a18ef5abc8
--- /dev/null
+++ b/chromium/net/http/disk_cache_based_quic_server_info.h
@@ -0,0 +1,106 @@
+// Copyright 2014 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_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_
+#define NET_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/base/completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/quic/crypto/quic_server_info.h"
+
+namespace net {
+
+class HttpCache;
+class IOBuffer;
+class QuicServerId;
+
+// DiskCacheBasedQuicServerInfo fetches information about a QUIC server from
+// our standard disk cache. Since the information is defined to be
+// non-sensitive, it's ok for us to keep it on disk.
+class NET_EXPORT_PRIVATE DiskCacheBasedQuicServerInfo
+ : public QuicServerInfo,
+ public NON_EXPORTED_BASE(base::NonThreadSafe) {
+ public:
+ DiskCacheBasedQuicServerInfo(const QuicServerId& server_id,
+ HttpCache* http_cache);
+
+ // QuicServerInfo implementation.
+ virtual void Start() OVERRIDE;
+ virtual int WaitForDataReady(const CompletionCallback& callback) OVERRIDE;
+ virtual bool IsDataReady() OVERRIDE;
+ virtual bool IsReadyToPersist() OVERRIDE;
+ virtual void Persist() OVERRIDE;
+
+ private:
+ struct CacheOperationDataShim;
+ enum State {
+ GET_BACKEND,
+ GET_BACKEND_COMPLETE,
+ OPEN,
+ OPEN_COMPLETE,
+ READ,
+ READ_COMPLETE,
+ WAIT_FOR_DATA_READY_DONE,
+ CREATE_OR_OPEN,
+ CREATE_OR_OPEN_COMPLETE,
+ WRITE,
+ WRITE_COMPLETE,
+ SET_DONE,
+ NONE,
+ };
+
+ virtual ~DiskCacheBasedQuicServerInfo();
+
+ std::string key() const;
+
+ // The |unused| parameter is a small hack so that we can have the
+ // CacheOperationDataShim object owned by the Callback that is created for
+ // this method. See comment above CacheOperationDataShim for details.
+ void OnIOComplete(CacheOperationDataShim* unused, int rv);
+
+ int DoLoop(int rv);
+
+ int DoGetBackendComplete(int rv);
+ int DoOpenComplete(int rv);
+ int DoReadComplete(int rv);
+ int DoWriteComplete(int rv);
+ int DoCreateOrOpenComplete(int rv);
+
+ int DoGetBackend();
+ int DoOpen();
+ int DoRead();
+ int DoWrite();
+ int DoCreateOrOpen();
+
+ // DoWaitForDataReadyDone is the terminal state of the read operation.
+ int DoWaitForDataReadyDone();
+
+ // DoSetDone is the terminal state of the write operation.
+ int DoSetDone();
+
+ base::WeakPtrFactory<DiskCacheBasedQuicServerInfo> weak_factory_;
+ CacheOperationDataShim* data_shim_; // Owned by |io_callback_|.
+ CompletionCallback io_callback_;
+ State state_;
+ bool ready_;
+ bool found_entry_; // Controls the behavior of DoCreateOrOpen.
+ std::string new_data_;
+ const QuicServerId server_id_;
+ HttpCache* const http_cache_;
+ disk_cache::Backend* backend_;
+ disk_cache::Entry* entry_;
+ CompletionCallback user_callback_;
+ scoped_refptr<IOBuffer> read_buffer_;
+ scoped_refptr<IOBuffer> write_buffer_;
+ std::string data_;
+};
+
+} // namespace net
+
+#endif // NET_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_
diff --git a/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc b/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc
new file mode 100644
index 00000000000..52b832dbbda
--- /dev/null
+++ b/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc
@@ -0,0 +1,282 @@
+// Copyright 2014 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/http/disk_cache_based_quic_server_info.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "net/base/net_errors.h"
+#include "net/http/mock_http_cache.h"
+#include "net/quic/crypto/quic_server_info.h"
+#include "net/quic/quic_server_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+// This is an empty transaction, needed to register the URL and the test mode.
+const MockTransaction kHostInfoTransaction1 = {
+ "quicserverinfo:https://www.google.com:443",
+ "",
+ base::Time(),
+ "",
+ LOAD_NORMAL,
+ "",
+ "",
+ base::Time(),
+ "",
+ TEST_MODE_NORMAL,
+ NULL,
+ 0
+};
+
+const MockTransaction kHostInfoTransaction2 = {
+ "quicserverinfo:http://www.google.com:80",
+ "",
+ base::Time(),
+ "",
+ LOAD_NORMAL,
+ "",
+ "",
+ base::Time(),
+ "",
+ TEST_MODE_NORMAL,
+ NULL,
+ 0
+};
+
+} // namespace
+
+// Tests that we can delete a DiskCacheBasedQuicServerInfo object in a
+// completion callback for DiskCacheBasedQuicServerInfo::WaitForDataReady.
+TEST(DiskCacheBasedQuicServerInfo, DeleteInCallback) {
+ // Use the blocking mock backend factory to force asynchronous completion
+ // of quic_server_info->WaitForDataReady(), so that the callback will run.
+ MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+ MockHttpCache cache(factory);
+ QuicServerId server_id("www.verisign.com", 443, true, PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicServerInfo> quic_server_info(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ quic_server_info->Start();
+ TestCompletionCallback callback;
+ int rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ // Now complete the backend creation and let the callback run.
+ factory->FinishCreation();
+ EXPECT_EQ(OK, callback.GetResult(rv));
+}
+
+// Tests the basic logic of storing, retrieving and updating data.
+TEST(DiskCacheBasedQuicServerInfo, Update) {
+ MockHttpCache cache;
+ AddMockTransaction(&kHostInfoTransaction1);
+ TestCompletionCallback callback;
+
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicServerInfo> quic_server_info(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ quic_server_info->Start();
+ int rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ QuicServerInfo::State* state = quic_server_info->mutable_state();
+ EXPECT_TRUE(state->certs.empty());
+ const string server_config_a = "server_config_a";
+ const string source_address_token_a = "source_address_token_a";
+ const string server_config_sig_a = "server_config_sig_a";
+ const string cert_a = "cert_a";
+ const string cert_b = "cert_b";
+
+ state->server_config = server_config_a;
+ state->source_address_token = source_address_token_a;
+ state->server_config_sig = server_config_sig_a;
+ state->certs.push_back(cert_a);
+ quic_server_info->Persist();
+
+ // Wait until Persist() does the work.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Open the stored QuicServerInfo.
+ quic_server_info.reset(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ quic_server_info->Start();
+ rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // And now update the data.
+ state = quic_server_info->mutable_state();
+ state->certs.push_back(cert_b);
+
+ // Fail instead of DCHECKing double creates.
+ cache.disk_cache()->set_double_create_check(false);
+ quic_server_info->Persist();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Verify that the state was updated.
+ quic_server_info.reset(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ quic_server_info->Start();
+ rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(quic_server_info->IsDataReady());
+
+ const QuicServerInfo::State& state1 = quic_server_info->state();
+ EXPECT_EQ(server_config_a, state1.server_config);
+ EXPECT_EQ(source_address_token_a, state1.source_address_token);
+ EXPECT_EQ(server_config_sig_a, state1.server_config_sig);
+ EXPECT_EQ(2U, state1.certs.size());
+ EXPECT_EQ(cert_a, state1.certs[0]);
+ EXPECT_EQ(cert_b, state1.certs[1]);
+
+ RemoveMockTransaction(&kHostInfoTransaction1);
+}
+
+// Test that demonstrates different info is returned when the ports differ.
+TEST(DiskCacheBasedQuicServerInfo, UpdateDifferentPorts) {
+ MockHttpCache cache;
+ AddMockTransaction(&kHostInfoTransaction1);
+ AddMockTransaction(&kHostInfoTransaction2);
+ TestCompletionCallback callback;
+
+ // Persist data for port 443.
+ QuicServerId server_id1("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicServerInfo> quic_server_info1(
+ new DiskCacheBasedQuicServerInfo(server_id1, cache.http_cache()));
+ quic_server_info1->Start();
+ int rv = quic_server_info1->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ QuicServerInfo::State* state1 = quic_server_info1->mutable_state();
+ EXPECT_TRUE(state1->certs.empty());
+ const string server_config_a = "server_config_a";
+ const string source_address_token_a = "source_address_token_a";
+ const string server_config_sig_a = "server_config_sig_a";
+ const string cert_a = "cert_a";
+
+ state1->server_config = server_config_a;
+ state1->source_address_token = source_address_token_a;
+ state1->server_config_sig = server_config_sig_a;
+ state1->certs.push_back(cert_a);
+ quic_server_info1->Persist();
+
+ // Wait until Persist() does the work.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Persist data for port 80.
+ QuicServerId server_id2("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicServerInfo> quic_server_info2(
+ new DiskCacheBasedQuicServerInfo(server_id2, cache.http_cache()));
+ quic_server_info2->Start();
+ rv = quic_server_info2->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ QuicServerInfo::State* state2 = quic_server_info2->mutable_state();
+ EXPECT_TRUE(state2->certs.empty());
+ const string server_config_b = "server_config_b";
+ const string source_address_token_b = "source_address_token_b";
+ const string server_config_sig_b = "server_config_sig_b";
+ const string cert_b = "cert_b";
+
+ state2->server_config = server_config_b;
+ state2->source_address_token = source_address_token_b;
+ state2->server_config_sig = server_config_sig_b;
+ state2->certs.push_back(cert_b);
+ quic_server_info2->Persist();
+
+ // Wait until Persist() does the work.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Verify the stored QuicServerInfo for port 443.
+ scoped_ptr<QuicServerInfo> quic_server_info(
+ new DiskCacheBasedQuicServerInfo(server_id1, cache.http_cache()));
+ quic_server_info->Start();
+ rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(quic_server_info->IsDataReady());
+
+ const QuicServerInfo::State& state_a = quic_server_info->state();
+ EXPECT_EQ(server_config_a, state_a.server_config);
+ EXPECT_EQ(source_address_token_a, state_a.source_address_token);
+ EXPECT_EQ(server_config_sig_a, state_a.server_config_sig);
+ EXPECT_EQ(1U, state_a.certs.size());
+ EXPECT_EQ(cert_a, state_a.certs[0]);
+
+ // Verify the stored QuicServerInfo for port 80.
+ quic_server_info.reset(
+ new DiskCacheBasedQuicServerInfo(server_id2, cache.http_cache()));
+ quic_server_info->Start();
+ rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(quic_server_info->IsDataReady());
+
+ const QuicServerInfo::State& state_b = quic_server_info->state();
+ EXPECT_EQ(server_config_b, state_b.server_config);
+ EXPECT_EQ(source_address_token_b, state_b.source_address_token);
+ EXPECT_EQ(server_config_sig_b, state_b.server_config_sig);
+ EXPECT_EQ(1U, state_b.certs.size());
+ EXPECT_EQ(cert_b, state_b.certs[0]);
+
+ RemoveMockTransaction(&kHostInfoTransaction2);
+ RemoveMockTransaction(&kHostInfoTransaction1);
+}
+
+// Test IsReadyToPersist when there is a pending write.
+TEST(DiskCacheBasedQuicServerInfo, IsReadyToPersist) {
+ MockHttpCache cache;
+ AddMockTransaction(&kHostInfoTransaction1);
+ TestCompletionCallback callback;
+
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicServerInfo> quic_server_info(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ EXPECT_FALSE(quic_server_info->IsDataReady());
+ quic_server_info->Start();
+ int rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(quic_server_info->IsDataReady());
+
+ QuicServerInfo::State* state = quic_server_info->mutable_state();
+ EXPECT_TRUE(state->certs.empty());
+ const string server_config_a = "server_config_a";
+ const string source_address_token_a = "source_address_token_a";
+ const string server_config_sig_a = "server_config_sig_a";
+ const string cert_a = "cert_a";
+
+ state->server_config = server_config_a;
+ state->source_address_token = source_address_token_a;
+ state->server_config_sig = server_config_sig_a;
+ state->certs.push_back(cert_a);
+ EXPECT_TRUE(quic_server_info->IsReadyToPersist());
+ quic_server_info->Persist();
+
+ // Once we call Persist, IsReadyToPersist should return false until Persist
+ // has completed.
+ EXPECT_FALSE(quic_server_info->IsReadyToPersist());
+
+ // Wait until Persist() does the work.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_TRUE(quic_server_info->IsReadyToPersist());
+
+ // Verify that the state was updated.
+ quic_server_info.reset(
+ new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache()));
+ quic_server_info->Start();
+ rv = quic_server_info->WaitForDataReady(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(quic_server_info->IsDataReady());
+
+ const QuicServerInfo::State& state1 = quic_server_info->state();
+ EXPECT_EQ(server_config_a, state1.server_config);
+ EXPECT_EQ(source_address_token_a, state1.source_address_token);
+ EXPECT_EQ(server_config_sig_a, state1.server_config_sig);
+ EXPECT_EQ(1U, state1.certs.size());
+ EXPECT_EQ(cert_a, state1.certs[0]);
+
+ RemoveMockTransaction(&kHostInfoTransaction1);
+}
+
+} // namespace net
diff --git a/chromium/net/http/failing_http_transaction_factory.cc b/chromium/net/http/failing_http_transaction_factory.cc
new file mode 100644
index 00000000000..d1d1612966d
--- /dev/null
+++ b/chromium/net/http/failing_http_transaction_factory.cc
@@ -0,0 +1,191 @@
+// Copyright 2014 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/http/failing_http_transaction_factory.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "net/base/load_timing_info.h"
+#include "net/base/upload_progress.h"
+
+namespace net {
+
+class AuthCredentials;
+class BoundNetLog;
+class HttpRequestHeaders;
+class IOBuffer;
+class X509Certificate;
+
+struct HttpRequestInfo;
+
+namespace {
+
+// A simple class to interpose between the cache and network http layers.
+// These transactions can be generated by the FailingHttpTransactionFactory
+// to test interactions between cache and network.
+class FailingHttpTransaction : public HttpTransaction {
+ public:
+ explicit FailingHttpTransaction(Error error);
+ virtual ~FailingHttpTransaction();
+
+ // HttpTransaction
+ virtual int Start(const HttpRequestInfo* request_info,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual int RestartIgnoringLastError(
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int RestartWithCertificate(
+ X509Certificate* client_cert,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int RestartWithAuth(
+ const AuthCredentials& credentials,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual bool IsReadyToRestartForAuth() OVERRIDE;
+ virtual int Read(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual void StopCaching() OVERRIDE;
+ virtual bool GetFullRequestHeaders(
+ HttpRequestHeaders* headers) const OVERRIDE;
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
+ virtual void DoneReading() OVERRIDE;
+ virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
+ virtual LoadState GetLoadState() const OVERRIDE;
+ virtual UploadProgress GetUploadProgress() const OVERRIDE;
+ virtual void SetQuicServerInfo(
+ net::QuicServerInfo* quic_server_info) OVERRIDE;
+ virtual bool GetLoadTimingInfo(
+ LoadTimingInfo* load_timing_info) const OVERRIDE;
+ virtual void SetPriority(RequestPriority priority) OVERRIDE;
+ virtual void SetWebSocketHandshakeStreamCreateHelper(
+ WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE;
+ virtual void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) OVERRIDE;
+ virtual int ResumeNetworkStart() OVERRIDE;
+
+ private:
+ Error error_;
+};
+
+FailingHttpTransaction::FailingHttpTransaction(Error error) : error_(error) {
+ DCHECK_LT(error, OK);
+}
+
+FailingHttpTransaction::~FailingHttpTransaction() {}
+
+int FailingHttpTransaction::Start(const HttpRequestInfo* request_info,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, error_));
+ return ERR_IO_PENDING;
+}
+
+int FailingHttpTransaction::RestartIgnoringLastError(
+ const CompletionCallback& callback) {
+ return ERR_FAILED;
+}
+
+int FailingHttpTransaction::RestartWithCertificate(
+ X509Certificate* client_cert,
+ const CompletionCallback& callback) {
+ return ERR_FAILED;
+}
+
+int FailingHttpTransaction::RestartWithAuth(
+ const AuthCredentials& credentials,
+ const CompletionCallback& callback) {
+ return ERR_FAILED;
+}
+
+bool FailingHttpTransaction::IsReadyToRestartForAuth() {
+ return false;
+}
+
+int FailingHttpTransaction::Read(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ NOTREACHED();
+ return ERR_FAILED;
+}
+
+void FailingHttpTransaction::StopCaching() {}
+
+bool FailingHttpTransaction::GetFullRequestHeaders(
+ HttpRequestHeaders* headers) const {
+ return false;
+}
+
+int64 FailingHttpTransaction::GetTotalReceivedBytes() const {
+ return 0;
+}
+
+void FailingHttpTransaction::DoneReading() {
+ NOTREACHED();
+}
+
+const HttpResponseInfo* FailingHttpTransaction::GetResponseInfo() const {
+ return NULL;
+}
+
+LoadState FailingHttpTransaction::GetLoadState() const {
+ return LOAD_STATE_IDLE;
+}
+
+UploadProgress FailingHttpTransaction::GetUploadProgress() const {
+ return UploadProgress();
+}
+
+void FailingHttpTransaction::SetQuicServerInfo(
+ net::QuicServerInfo* quic_server_info) {}
+
+bool FailingHttpTransaction::GetLoadTimingInfo(
+ LoadTimingInfo* load_timing_info) const {
+ return false;
+}
+
+void FailingHttpTransaction::SetPriority(RequestPriority priority) {}
+
+void FailingHttpTransaction::SetWebSocketHandshakeStreamCreateHelper(
+ WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
+ NOTREACHED();
+}
+
+void FailingHttpTransaction::SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) {
+}
+
+int FailingHttpTransaction::ResumeNetworkStart() {
+ NOTREACHED();
+ return ERR_FAILED;
+}
+
+} // namespace
+
+FailingHttpTransactionFactory::FailingHttpTransactionFactory(
+ HttpNetworkSession* session,
+ Error error) : session_(session), error_(error) {
+ DCHECK_LT(error, OK);
+}
+
+FailingHttpTransactionFactory::~FailingHttpTransactionFactory() {}
+
+// HttpTransactionFactory:
+int FailingHttpTransactionFactory::CreateTransaction(
+ RequestPriority priority,
+ scoped_ptr<HttpTransaction>* trans) {
+ trans->reset(new FailingHttpTransaction(error_));
+ return OK;
+}
+
+HttpCache* FailingHttpTransactionFactory::GetCache() {
+ return NULL;
+}
+
+HttpNetworkSession* FailingHttpTransactionFactory::GetSession() {
+ return session_;
+}
+
+} // namespace net
+
diff --git a/chromium/net/http/failing_http_transaction_factory.h b/chromium/net/http/failing_http_transaction_factory.h
new file mode 100644
index 00000000000..84d87a7a3d3
--- /dev/null
+++ b/chromium/net/http/failing_http_transaction_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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_FAILING_HTTP_TRANSACTION_FACTORY_H_
+#define NET_FAILING_HTTP_TRANSACTION_FACTORY_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_transaction.h"
+#include "net/http/http_transaction_factory.h"
+
+namespace net {
+
+class HttpCache;
+class HttpNetworkSession;
+
+// Creates transactions that always (asynchronously) return a specified
+// error. The error is returned asynchronously, just after the transaction is
+// started.
+class NET_EXPORT FailingHttpTransactionFactory : public HttpTransactionFactory {
+ public:
+ // The caller must guarantee that |session| outlives this object.
+ FailingHttpTransactionFactory(HttpNetworkSession* session, Error error);
+ virtual ~FailingHttpTransactionFactory();
+
+ // HttpTransactionFactory:
+ virtual int CreateTransaction(
+ RequestPriority priority,
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE;
+ virtual HttpCache* GetCache() OVERRIDE;
+ virtual HttpNetworkSession* GetSession() OVERRIDE;
+
+ private:
+ HttpNetworkSession* session_;
+ Error error_;
+};
+
+} // namespace net
+
+#endif // NET_FAILING_HTTP_TRANSACTION_FACTORY_H_
diff --git a/chromium/net/http/http_auth.cc b/chromium/net/http/http_auth.cc
index 3cc9db1a122..4c6d3e8dda3 100644
--- a/chromium/net/http/http_auth.cc
+++ b/chromium/net/http/http_auth.cc
@@ -10,6 +10,7 @@
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_request_headers.h"
@@ -74,7 +75,7 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse(
HttpAuth::AuthorizationResult authorization_result =
HttpAuth::AUTHORIZATION_RESULT_INVALID;
while (headers->EnumerateHeader(&iter, header_name, &challenge)) {
- HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
+ HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str()))
continue;
authorization_result = handler->HandleAnotherChallenge(&props);
@@ -87,56 +88,6 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse(
return HttpAuth::AUTHORIZATION_RESULT_REJECT;
}
-HttpAuth::ChallengeTokenizer::ChallengeTokenizer(
- std::string::const_iterator begin,
- std::string::const_iterator end)
- : begin_(begin),
- end_(end),
- scheme_begin_(begin),
- scheme_end_(begin),
- params_begin_(end),
- params_end_(end) {
- Init(begin, end);
-}
-
-HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs()
- const {
- return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
-}
-
-std::string HttpAuth::ChallengeTokenizer::base64_param() const {
- // Strip off any padding.
- // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
- //
- // Our base64 decoder requires that the length be a multiple of 4.
- int encoded_length = params_end_ - params_begin_;
- while (encoded_length > 0 && encoded_length % 4 != 0 &&
- params_begin_[encoded_length - 1] == '=') {
- --encoded_length;
- }
- return std::string(params_begin_, params_begin_ + encoded_length);
-}
-
-void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin,
- std::string::const_iterator end) {
- // The first space-separated token is the auth-scheme.
- // NOTE: we are more permissive than RFC 2617 which says auth-scheme
- // is separated by 1*SP.
- base::StringTokenizer tok(begin, end, HTTP_LWS);
- if (!tok.GetNext()) {
- // Default param and scheme iterators provide empty strings
- return;
- }
-
- // Save the scheme's position.
- scheme_begin_ = tok.token_begin();
- scheme_end_ = tok.token_end();
-
- params_begin_ = scheme_end_;
- params_end_ = end;
- HttpUtil::TrimLWS(&params_begin_, &params_end_);
-}
-
// static
std::string HttpAuth::GetChallengeHeaderName(Target target) {
switch (target) {
diff --git a/chromium/net/http/http_auth.h b/chromium/net/http/http_auth.h
index 1a03bb1fe1f..5a2c7ac6606 100644
--- a/chromium/net/http/http_auth.h
+++ b/chromium/net/http/http_auth.h
@@ -167,48 +167,6 @@ class NET_EXPORT_PRIVATE HttpAuth {
Target target,
const std::set<Scheme>& disabled_schemes,
std::string* challenge_used);
-
- // Breaks up a challenge string into the the auth scheme and parameter list,
- // according to RFC 2617 Sec 1.2:
- // challenge = auth-scheme 1*SP 1#auth-param
- //
- // Depending on the challenge scheme, it may be appropriate to interpret the
- // parameters as either a base-64 encoded string or a comma-delimited list
- // of name-value pairs. param_pairs() and base64_param() methods are provided
- // to support either usage.
- class NET_EXPORT_PRIVATE ChallengeTokenizer {
- public:
- ChallengeTokenizer(std::string::const_iterator begin,
- std::string::const_iterator end);
-
- // Get the original text.
- std::string challenge_text() const {
- return std::string(begin_, end_);
- }
-
- // Get the auth scheme of the challenge.
- std::string::const_iterator scheme_begin() const { return scheme_begin_; }
- std::string::const_iterator scheme_end() const { return scheme_end_; }
- std::string scheme() const {
- return std::string(scheme_begin_, scheme_end_);
- }
-
- HttpUtil::NameValuePairsIterator param_pairs() const;
- std::string base64_param() const;
-
- private:
- void Init(std::string::const_iterator begin,
- std::string::const_iterator end);
-
- std::string::const_iterator begin_;
- std::string::const_iterator end_;
-
- std::string::const_iterator scheme_begin_;
- std::string::const_iterator scheme_end_;
-
- std::string::const_iterator params_begin_;
- std::string::const_iterator params_end_;
- };
};
} // namespace net
diff --git a/chromium/net/http/http_auth_cache.cc b/chromium/net/http/http_auth_cache.cc
index 63bad076df1..d989830b2aa 100644
--- a/chromium/net/http/http_auth_cache.cc
+++ b/chromium/net/http/http_auth_cache.cc
@@ -5,6 +5,7 @@
#include "net/http/http_auth_cache.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
namespace {
@@ -57,6 +58,14 @@ struct IsEnclosedBy {
const std::string& path;
};
+void RecordLookupPosition(int position) {
+ UMA_HISTOGRAM_COUNTS_100("Net.HttpAuthCacheLookupPosition", position);
+}
+
+void RecordLookupByPathPosition(int position) {
+ UMA_HISTOGRAM_COUNTS_100("Net.HttpAuthCacheLookupByPathPosition", position);
+}
+
} // namespace
namespace net {
@@ -73,12 +82,18 @@ HttpAuthCache::Entry* HttpAuthCache::Lookup(const GURL& origin,
HttpAuth::Scheme scheme) {
CheckOriginIsValid(origin);
+ int entries_examined = 0;
// Linear scan through the realm entries.
for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
+ ++entries_examined;
if (it->origin() == origin && it->realm() == realm &&
- it->scheme() == scheme)
+ it->scheme() == scheme) {
+ it->last_use_time_ = base::TimeTicks::Now();
+ RecordLookupPosition(entries_examined);
return &(*it);
+ }
}
+ RecordLookupPosition(0);
return NULL; // No realm entry found.
}
@@ -89,6 +104,7 @@ HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin,
const std::string& path) {
HttpAuthCache::Entry* best_match = NULL;
size_t best_match_length = 0;
+ int best_match_position = 0;
CheckOriginIsValid(origin);
CheckPathIsValid(path);
@@ -98,15 +114,21 @@ HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin,
// within the protection space ...
std::string parent_dir = GetParentDirectory(path);
+ int entries_examined = 0;
// Linear scan through the realm entries.
for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
+ ++entries_examined;
size_t len = 0;
if (it->origin() == origin && it->HasEnclosingPath(parent_dir, &len) &&
(!best_match || len > best_match_length)) {
- best_match_length = len;
best_match = &(*it);
+ best_match_length = len;
+ best_match_position = entries_examined;
}
}
+ if (best_match)
+ best_match->last_use_time_ = base::TimeTicks::Now();
+ RecordLookupByPathPosition(best_match_position);
return best_match;
}
@@ -119,20 +141,30 @@ HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin,
CheckOriginIsValid(origin);
CheckPathIsValid(path);
+ base::TimeTicks now = base::TimeTicks::Now();
+
// Check for existing entry (we will re-use it if present).
HttpAuthCache::Entry* entry = Lookup(origin, realm, scheme);
if (!entry) {
+ bool evicted = false;
// Failsafe to prevent unbounded memory growth of the cache.
if (entries_.size() >= kMaxNumRealmEntries) {
LOG(WARNING) << "Num auth cache entries reached limit -- evicting";
+ UMA_HISTOGRAM_LONG_TIMES("Net.HttpAuthCacheAddEvictedCreation",
+ now - entries_.back().creation_time_);
+ UMA_HISTOGRAM_LONG_TIMES("Net.HttpAuthCacheAddEvictedLastUse",
+ now - entries_.back().last_use_time_);
entries_.pop_back();
+ evicted = true;
}
+ UMA_HISTOGRAM_BOOLEAN("Net.HttpAuthCacheAddEvicted", evicted);
entries_.push_front(Entry());
entry = &entries_.front();
entry->origin_ = origin;
entry->realm_ = realm;
entry->scheme_ = scheme;
+ entry->creation_time_ = now;
}
DCHECK_EQ(origin, entry->origin_);
DCHECK_EQ(realm, entry->realm_);
@@ -142,6 +174,7 @@ HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin,
entry->credentials_ = credentials;
entry->nonce_count_ = 1;
entry->AddPath(path);
+ entry->last_use_time_ = now;
return entry;
}
@@ -166,12 +199,15 @@ void HttpAuthCache::Entry::AddPath(const std::string& path) {
// Remove any entries that have been subsumed by the new entry.
paths_.remove_if(IsEnclosedBy(parent_dir));
+ bool evicted = false;
// Failsafe to prevent unbounded memory growth of the cache.
if (paths_.size() >= kMaxNumPathsPerRealmEntry) {
LOG(WARNING) << "Num path entries for " << origin()
<< " has grown too large -- evicting";
paths_.pop_back();
+ evicted = true;
}
+ UMA_HISTOGRAM_BOOLEAN("Net.HttpAuthCacheAddPathEvicted", evicted);
// Add new path.
paths_.push_front(parent_dir);
@@ -221,6 +257,7 @@ bool HttpAuthCache::UpdateStaleChallenge(const GURL& origin,
if (!entry)
return false;
entry->UpdateStaleChallenge(auth_challenge);
+ entry->last_use_time_ = base::TimeTicks::Now();
return true;
}
diff --git a/chromium/net/http/http_auth_cache.h b/chromium/net/http/http_auth_cache.h
index 75b379f25bd..707a571db96 100644
--- a/chromium/net/http/http_auth_cache.h
+++ b/chromium/net/http/http_auth_cache.h
@@ -10,6 +10,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
#include "net/base/net_export.h"
#include "net/http/http_auth.h"
#include "url/gurl.h"
@@ -96,6 +97,11 @@ class NET_EXPORT_PRIVATE HttpAuthCache {
// List of paths that define the realm's protection space.
PathList paths_;
+
+ // Times the entry was created and last used (by looking up, adding a path,
+ // or updating the challenge.)
+ base::TimeTicks creation_time_;
+ base::TimeTicks last_use_time_;
};
// Prevent unbounded memory growth. These are safeguards for abuse; it is
diff --git a/chromium/net/http/http_auth_cache_unittest.cc b/chromium/net/http/http_auth_cache_unittest.cc
index a4ea1c6da03..e925c714316 100644
--- a/chromium/net/http/http_auth_cache_unittest.cc
+++ b/chromium/net/http/http_auth_cache_unittest.cc
@@ -13,6 +13,8 @@
#include "net/http/http_auth_handler.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+
namespace net {
namespace {
@@ -31,12 +33,12 @@ class MockAuthHandler : public HttpAuthHandler {
}
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE {
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE {
return HttpAuth::AUTHORIZATION_RESULT_REJECT;
}
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE {
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE {
return false; // Unused.
}
diff --git a/chromium/net/http/http_auth_challenge_tokenizer.cc b/chromium/net/http/http_auth_challenge_tokenizer.cc
new file mode 100644
index 00000000000..528566c8664
--- /dev/null
+++ b/chromium/net/http/http_auth_challenge_tokenizer.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 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/http/http_auth_challenge_tokenizer.h"
+
+#include "base/strings/string_tokenizer.h"
+
+namespace net {
+
+HttpAuthChallengeTokenizer::HttpAuthChallengeTokenizer(
+ std::string::const_iterator begin,
+ std::string::const_iterator end)
+ : begin_(begin),
+ end_(end),
+ scheme_begin_(begin),
+ scheme_end_(begin),
+ params_begin_(end),
+ params_end_(end) {
+ Init(begin, end);
+}
+
+HttpUtil::NameValuePairsIterator HttpAuthChallengeTokenizer::param_pairs()
+ const {
+ return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
+}
+
+std::string HttpAuthChallengeTokenizer::base64_param() const {
+ // Strip off any padding.
+ // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
+ //
+ // Our base64 decoder requires that the length be a multiple of 4.
+ int encoded_length = params_end_ - params_begin_;
+ while (encoded_length > 0 && encoded_length % 4 != 0 &&
+ params_begin_[encoded_length - 1] == '=') {
+ --encoded_length;
+ }
+ return std::string(params_begin_, params_begin_ + encoded_length);
+}
+
+void HttpAuthChallengeTokenizer::Init(std::string::const_iterator begin,
+ std::string::const_iterator end) {
+ // The first space-separated token is the auth-scheme.
+ // NOTE: we are more permissive than RFC 2617 which says auth-scheme
+ // is separated by 1*SP.
+ base::StringTokenizer tok(begin, end, HTTP_LWS);
+ if (!tok.GetNext()) {
+ // Default param and scheme iterators provide empty strings
+ return;
+ }
+
+ // Save the scheme's position.
+ scheme_begin_ = tok.token_begin();
+ scheme_end_ = tok.token_end();
+
+ params_begin_ = scheme_end_;
+ params_end_ = end;
+ HttpUtil::TrimLWS(&params_begin_, &params_end_);
+}
+
+} // namespace net
diff --git a/chromium/net/http/http_auth_challenge_tokenizer.h b/chromium/net/http/http_auth_challenge_tokenizer.h
new file mode 100644
index 00000000000..a73f19205b6
--- /dev/null
+++ b/chromium/net/http/http_auth_challenge_tokenizer.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_
+#define NET_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_
+
+#include <string>
+
+#include "net/base/net_export.h"
+#include "net/http/http_util.h"
+
+namespace net {
+
+// Breaks up a challenge string into the the auth scheme and parameter list,
+// according to RFC 2617 Sec 1.2:
+// challenge = auth-scheme 1*SP 1#auth-param
+//
+// Depending on the challenge scheme, it may be appropriate to interpret the
+// parameters as either a base-64 encoded string or a comma-delimited list
+// of name-value pairs. param_pairs() and base64_param() methods are provided
+// to support either usage.
+class NET_EXPORT_PRIVATE HttpAuthChallengeTokenizer {
+ public:
+ HttpAuthChallengeTokenizer(std::string::const_iterator begin,
+ std::string::const_iterator end);
+
+ // Get the original text.
+ std::string challenge_text() const {
+ return std::string(begin_, end_);
+ }
+
+ // Get the auth scheme of the challenge.
+ std::string::const_iterator scheme_begin() const { return scheme_begin_; }
+ std::string::const_iterator scheme_end() const { return scheme_end_; }
+ std::string scheme() const {
+ return std::string(scheme_begin_, scheme_end_);
+ }
+
+ std::string::const_iterator params_begin() const { return params_begin_; }
+ std::string::const_iterator params_end() const { return params_end_; }
+ HttpUtil::NameValuePairsIterator param_pairs() const;
+ std::string base64_param() const;
+
+ private:
+ void Init(std::string::const_iterator begin,
+ std::string::const_iterator end);
+
+ std::string::const_iterator begin_;
+ std::string::const_iterator end_;
+
+ std::string::const_iterator scheme_begin_;
+ std::string::const_iterator scheme_end_;
+
+ std::string::const_iterator params_begin_;
+ std::string::const_iterator params_end_;
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_
diff --git a/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc b/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc
new file mode 100644
index 00000000000..2cf657a17f5
--- /dev/null
+++ b/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2014 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/http/http_auth_challenge_tokenizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(HttpAuthChallengeTokenizerTest, Basic) {
+ std::string challenge_str = "Basic realm=\"foobar\"";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Basic"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("foobar"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a name=value property with no quote marks.
+TEST(HttpAuthChallengeTokenizerTest, NoQuotes) {
+ std::string challenge_str = "Basic realm=foobar@baz.com";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Basic"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("foobar@baz.com"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a name=value property with mismatching quote marks.
+TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotes) {
+ std::string challenge_str = "Basic realm=\"foobar@baz.com";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Basic"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("foobar@baz.com"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a name= property without a value and with mismatching quote marks.
+TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesNoValue) {
+ std::string challenge_str = "Basic realm=\"";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Basic"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string(), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a name=value property with mismatching quote marks and spaces in the
+// value.
+TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesSpaces) {
+ std::string challenge_str = "Basic realm=\"foo bar";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Basic"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("foo bar"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use multiple name=value properties with mismatching quote marks in the last
+// value.
+TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesMultiple) {
+ std::string challenge_str = "Digest qop=auth-int, algorithm=md5, realm=\"foo";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Digest"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("qop"), parameters.name());
+ EXPECT_EQ(std::string("auth-int"), parameters.value());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("algorithm"), parameters.name());
+ EXPECT_EQ(std::string("md5"), parameters.value());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("foo"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a name= property which has no value.
+TEST(HttpAuthChallengeTokenizerTest, NoValue) {
+ std::string challenge_str = "Digest qop=";
+ HttpAuthChallengeTokenizer challenge(
+ challenge_str.begin(), challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Digest"), challenge.scheme());
+ EXPECT_FALSE(parameters.GetNext());
+ EXPECT_FALSE(parameters.valid());
+}
+
+// Specify multiple properties, comma separated.
+TEST(HttpAuthChallengeTokenizerTest, Multiple) {
+ std::string challenge_str =
+ "Digest algorithm=md5, realm=\"Oblivion\", qop=auth-int";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("Digest"), challenge.scheme());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("algorithm"), parameters.name());
+ EXPECT_EQ(std::string("md5"), parameters.value());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("realm"), parameters.name());
+ EXPECT_EQ(std::string("Oblivion"), parameters.value());
+ EXPECT_TRUE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("qop"), parameters.name());
+ EXPECT_EQ(std::string("auth-int"), parameters.value());
+ EXPECT_FALSE(parameters.GetNext());
+ EXPECT_TRUE(parameters.valid());
+}
+
+// Use a challenge which has no property.
+TEST(HttpAuthChallengeTokenizerTest, NoProperty) {
+ std::string challenge_str = "NTLM";
+ HttpAuthChallengeTokenizer challenge(
+ challenge_str.begin(), challenge_str.end());
+ HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
+
+ EXPECT_TRUE(parameters.valid());
+ EXPECT_EQ(std::string("NTLM"), challenge.scheme());
+ EXPECT_FALSE(parameters.GetNext());
+}
+
+// Use a challenge with Base64 encoded token.
+TEST(HttpAuthChallengeTokenizerTest, Base64) {
+ std::string challenge_str = "NTLM SGVsbG8sIFdvcmxkCg===";
+ HttpAuthChallengeTokenizer challenge(challenge_str.begin(),
+ challenge_str.end());
+
+ EXPECT_EQ(std::string("NTLM"), challenge.scheme());
+ // Notice the two equal statements below due to padding removal.
+ EXPECT_EQ(std::string("SGVsbG8sIFdvcmxkCg=="), challenge.base64_param());
+}
+
+} // namespace net
diff --git a/chromium/net/http/http_auth_controller_unittest.cc b/chromium/net/http/http_auth_controller_unittest.cc
index 2a18369c1f7..1a59b8d4f68 100644
--- a/chromium/net/http/http_auth_controller_unittest.cc
+++ b/chromium/net/http/http_auth_controller_unittest.cc
@@ -9,6 +9,7 @@
#include "net/base/net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/http/http_auth_cache.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_mock.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
@@ -127,7 +128,7 @@ TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) {
}
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE {
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE {
HttpAuthHandlerMock::Init(challenge);
set_allows_default_credentials(true);
set_allows_explicit_credentials(false);
@@ -224,7 +225,7 @@ TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) {
ASSERT_EQ(OK,
controller->HandleAuthChallenge(headers, false, false, dummy_log));
ASSERT_TRUE(controller->HaveAuthHandler());
- controller->ResetAuth(AuthCredentials(ASCIIToUTF16("Hello"),
+ controller->ResetAuth(AuthCredentials(base::ASCIIToUTF16("Hello"),
base::string16()));
EXPECT_TRUE(controller->HaveAuth());
EXPECT_TRUE(controller->IsAuthSchemeDisabled(HttpAuth::AUTH_SCHEME_MOCK));
diff --git a/chromium/net/http/http_auth_filter_win.h b/chromium/net/http/http_auth_filter_win.h
index 24305b511ec..d2c0af4a09d 100644
--- a/chromium/net/http/http_auth_filter_win.h
+++ b/chromium/net/http/http_auth_filter_win.h
@@ -20,13 +20,13 @@ enum RegistryHiveType {
namespace http_auth {
// The common path to all the registry keys containing domain zone information.
-extern const char16 kRegistryInternetSettings[];
-extern const char16 kSettingsMachineOnly[];
-extern const char16* kRegistryEntries[3]; // L"http", L"https", and L"*"
+extern const base::char16 kRegistryInternetSettings[];
+extern const base::char16 kSettingsMachineOnly[];
+extern const base::char16* kRegistryEntries[3]; // L"http", L"https", and L"*"
-extern const char16* GetRegistryWhitelistKey();
+extern const base::char16* GetRegistryWhitelistKey();
// Override the whitelist key. Passing in NULL restores the default value.
-extern void SetRegistryWhitelistKey(const char16* new_whitelist_key);
+extern void SetRegistryWhitelistKey(const base::char16* new_whitelist_key);
extern bool UseOnlyMachineSettings();
} // namespace http_auth
diff --git a/chromium/net/http/http_auth_gssapi_posix.cc b/chromium/net/http/http_auth_gssapi_posix.cc
index 41cbcdbcdc0..a4b8c0cd839 100644
--- a/chromium/net/http/http_auth_gssapi_posix.cc
+++ b/chromium/net/http/http_auth_gssapi_posix.cc
@@ -16,6 +16,7 @@
#include "base/threading/thread_restrictions.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
// These are defined for the GSSAPI library:
// Paraphrasing the comments from gssapi.h:
@@ -684,7 +685,7 @@ void HttpAuthGSSAPI::Delegate() {
}
HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok) {
+ HttpAuthChallengeTokenizer* tok) {
// Verify the challenge's auth-scheme.
if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
return HttpAuth::AUTHORIZATION_RESULT_INVALID;
diff --git a/chromium/net/http/http_auth_gssapi_posix.h b/chromium/net/http/http_auth_gssapi_posix.h
index db603f65da8..ddaf518736b 100644
--- a/chromium/net/http/http_auth_gssapi_posix.h
+++ b/chromium/net/http/http_auth_gssapi_posix.h
@@ -22,6 +22,8 @@
namespace net {
+class HttpAuthChallengeTokenizer;
+
// Mechanism OID for GSSAPI. We always use SPNEGO.
NET_EXPORT_PRIVATE extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC;
@@ -240,7 +242,7 @@ class NET_EXPORT_PRIVATE HttpAuthGSSAPI {
bool AllowsExplicitCredentials() const;
HttpAuth::AuthorizationResult ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok);
+ HttpAuthChallengeTokenizer* tok);
// Generates an authentication token.
// The return value is an error code. If it's not |OK|, the value of
diff --git a/chromium/net/http/http_auth_gssapi_posix_unittest.cc b/chromium/net/http/http_auth_gssapi_posix_unittest.cc
index 48d17a3ad64..6f933349d7e 100644
--- a/chromium/net/http/http_auth_gssapi_posix_unittest.cc
+++ b/chromium/net/http/http_auth_gssapi_posix_unittest.cc
@@ -9,6 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/native_library.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/mock_gssapi_library_posix.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -181,8 +182,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) {
HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
CHROME_GSS_SPNEGO_MECH_OID_DESC);
std::string challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
- challenge_text.end());
+ HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
+ challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_gssapi.ParseChallenge(&challenge));
}
@@ -194,8 +195,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
CHROME_GSS_SPNEGO_MECH_OID_DESC);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_gssapi.ParseChallenge(&first_challenge));
@@ -206,8 +207,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) {
&auth_token));
std::string second_challenge_text = "Negotiate Zm9vYmFy";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_gssapi.ParseChallenge(&second_challenge));
}
@@ -219,8 +220,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) {
HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
CHROME_GSS_SPNEGO_MECH_OID_DESC);
std::string challenge_text = "Negotiate Zm9vYmFy";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
- challenge_text.end());
+ HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
+ challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
auth_gssapi.ParseChallenge(&challenge));
}
@@ -232,8 +233,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
CHROME_GSS_SPNEGO_MECH_OID_DESC);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_gssapi.ParseChallenge(&first_challenge));
@@ -242,8 +243,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) {
EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
&auth_token));
std::string second_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
auth_gssapi.ParseChallenge(&second_challenge));
}
@@ -255,8 +256,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate",
CHROME_GSS_SPNEGO_MECH_OID_DESC);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_gssapi.ParseChallenge(&first_challenge));
@@ -265,8 +266,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) {
EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
&auth_token));
std::string second_challenge_text = "Negotiate =happyjoy=";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
auth_gssapi.ParseChallenge(&second_challenge));
}
diff --git a/chromium/net/http/http_auth_handler.cc b/chromium/net/http/http_auth_handler.cc
index ab809faefae..7369ea5425f 100644
--- a/chromium/net/http/http_auth_handler.cc
+++ b/chromium/net/http/http_auth_handler.cc
@@ -8,6 +8,7 @@
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
namespace net {
@@ -22,7 +23,7 @@ HttpAuthHandler::~HttpAuthHandler() {
}
bool HttpAuthHandler::InitFromChallenge(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
const BoundNetLog& net_log) {
diff --git a/chromium/net/http/http_auth_handler.h b/chromium/net/http/http_auth_handler.h
index 638bb44aed3..dfb50d43a60 100644
--- a/chromium/net/http/http_auth_handler.h
+++ b/chromium/net/http/http_auth_handler.h
@@ -14,6 +14,7 @@
namespace net {
+class HttpAuthChallengeTokenizer;
struct HttpRequestInfo;
// HttpAuthHandler is the interface for the authentication schemes
@@ -29,7 +30,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler {
// authentication scheme, but none of the tokens occurring after the
// authentication scheme. |target| and |origin| are both stored
// for later use, and are not part of the initial challenge.
- bool InitFromChallenge(HttpAuth::ChallengeTokenizer* challenge,
+ bool InitFromChallenge(HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
const BoundNetLog& net_log);
@@ -48,7 +49,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler {
// authentication scheme, but none of the tokens occurring after the
// authentication scheme.
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) = 0;
+ HttpAuthChallengeTokenizer* challenge) = 0;
// Generates an authentication token, potentially asynchronously.
//
@@ -151,7 +152,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler {
// authentication scheme.
// Implementations are expected to initialize the following members:
// scheme_, realm_, score_, properties_
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) = 0;
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) = 0;
// |GenerateAuthTokenImpl()} is the auth-scheme specific implementation
// of generating the next auth token. Callers should use |GenerateAuthToken()|
diff --git a/chromium/net/http/http_auth_handler_basic.cc b/chromium/net/http/http_auth_handler_basic.cc
index e445c93100c..48371389d1a 100644
--- a/chromium/net/http/http_auth_handler_basic.cc
+++ b/chromium/net/http/http_auth_handler_basic.cc
@@ -7,11 +7,12 @@
#include <string>
#include "base/base64.h"
-#include "base/i18n/icu_string_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
+#include "net/base/net_string_util.h"
#include "net/http/http_auth.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
namespace net {
@@ -33,7 +34,7 @@ namespace {
//
// TODO(cbentzel): Realm may need to be decoded using RFC 2047 rules as
// well, see http://crbug.com/25790.
-bool ParseRealm(const HttpAuth::ChallengeTokenizer& tokenizer,
+bool ParseRealm(const HttpAuthChallengeTokenizer& tokenizer,
std::string* realm) {
CHECK(realm);
realm->clear();
@@ -42,16 +43,17 @@ bool ParseRealm(const HttpAuth::ChallengeTokenizer& tokenizer,
if (!LowerCaseEqualsASCII(parameters.name(), "realm"))
continue;
- if (!base::ConvertToUtf8AndNormalize(
- parameters.value(), base::kCodepageLatin1, realm))
+ if (!net::ConvertToUtf8AndNormalize(parameters.value(), kCharsetLatin1,
+ realm)) {
return false;
+ }
}
return parameters.valid();
}
} // namespace
-bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) {
+bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) {
auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC;
score_ = 1;
properties_ = 0;
@@ -59,7 +61,7 @@ bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) {
}
bool HttpAuthHandlerBasic::ParseChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
// Verify the challenge's auth-scheme.
if (!LowerCaseEqualsASCII(challenge->scheme(), "basic"))
return false;
@@ -73,7 +75,7 @@ bool HttpAuthHandlerBasic::ParseChallenge(
}
HttpAuth::AuthorizationResult HttpAuthHandlerBasic::HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
// Basic authentication is always a single round, so any responses
// should be treated as a rejection. However, if the new challenge
// is for a different realm, then indicate the realm change.
@@ -91,8 +93,8 @@ int HttpAuthHandlerBasic::GenerateAuthTokenImpl(
DCHECK(credentials);
// TODO(eroman): is this the right encoding of username/password?
std::string base64_username_password;
- base::Base64Encode(UTF16ToUTF8(credentials->username()) + ":" +
- UTF16ToUTF8(credentials->password()),
+ base::Base64Encode(base::UTF16ToUTF8(credentials->username()) + ":" +
+ base::UTF16ToUTF8(credentials->password()),
&base64_username_password);
*auth_token = "Basic " + base64_username_password;
return OK;
@@ -105,7 +107,7 @@ HttpAuthHandlerBasic::Factory::~Factory() {
}
int HttpAuthHandlerBasic::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_basic.h b/chromium/net/http/http_auth_handler_basic.h
index ce56152b09f..5d786f9de86 100644
--- a/chromium/net/http/http_auth_handler_basic.h
+++ b/chromium/net/http/http_auth_handler_basic.h
@@ -22,7 +22,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler {
virtual ~Factory();
virtual int CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -32,10 +32,10 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler {
};
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE;
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials,
const HttpRequestInfo* request,
@@ -45,7 +45,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler {
private:
virtual ~HttpAuthHandlerBasic() {}
- bool ParseChallenge(HttpAuth::ChallengeTokenizer* challenge);
+ bool ParseChallenge(HttpAuthChallengeTokenizer* challenge);
};
} // namespace net
diff --git a/chromium/net/http/http_auth_handler_basic_unittest.cc b/chromium/net/http/http_auth_handler_basic_unittest.cc
index 5e05b76d8b5..6f112b1b32f 100644
--- a/chromium/net/http/http_auth_handler_basic_unittest.cc
+++ b/chromium/net/http/http_auth_handler_basic_unittest.cc
@@ -9,6 +9,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_basic.h"
#include "net/http/http_request_info.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,8 +37,8 @@ TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) {
scoped_ptr<HttpAuthHandler> basic;
EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(), &basic));
- AuthCredentials credentials(ASCIIToUTF16(tests[i].username),
- ASCIIToUTF16(tests[i].password));
+ AuthCredentials credentials(base::ASCIIToUTF16(tests[i].username),
+ base::ASCIIToUTF16(tests[i].password));
HttpRequestInfo request_info;
std::string auth_token;
int rv = basic->GenerateAuthToken(&credentials, &request_info,
@@ -91,8 +92,8 @@ TEST(HttpAuthHandlerBasicTest, HandleAnotherChallenge) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
std::string challenge(tests[i].challenge);
- HttpAuth::ChallengeTokenizer tok(challenge.begin(),
- challenge.end());
+ HttpAuthChallengeTokenizer tok(challenge.begin(),
+ challenge.end());
EXPECT_EQ(tests[i].expected_rv, basic->HandleAnotherChallenge(&tok));
}
}
diff --git a/chromium/net/http/http_auth_handler_digest.cc b/chromium/net/http/http_auth_handler_digest.cc
index a8e301a02e8..7dee081ddbe 100644
--- a/chromium/net/http/http_auth_handler_digest.cc
+++ b/chromium/net/http/http_auth_handler_digest.cc
@@ -6,7 +6,6 @@
#include <string>
-#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/md5.h"
#include "base/rand_util.h"
@@ -14,8 +13,10 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
+#include "net/base/net_string_util.h"
#include "net/base/net_util.h"
#include "net/http/http_auth.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_request_info.h"
#include "net/http/http_util.h"
#include "url/gurl.h"
@@ -89,7 +90,7 @@ void HttpAuthHandlerDigest::Factory::set_nonce_generator(
}
int HttpAuthHandlerDigest::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -107,7 +108,7 @@ int HttpAuthHandlerDigest::Factory::CreateAuthHandler(
}
HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
// Even though Digest is not connection based, a "second round" is parsed
// to differentiate between stale and rejected responses.
// Note that the state of the current handler is not mutated - this way if
@@ -133,7 +134,7 @@ HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge(
HttpAuth::AUTHORIZATION_RESULT_REJECT;
}
-bool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) {
+bool HttpAuthHandlerDigest::Init(HttpAuthChallengeTokenizer* challenge) {
return ParseChallenge(challenge);
}
@@ -186,7 +187,7 @@ HttpAuthHandlerDigest::~HttpAuthHandlerDigest() {
// send the realm (See http://crbug.com/20984 for an instance where a
// webserver was not sending the realm with a BASIC challenge).
bool HttpAuthHandlerDigest::ParseChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
auth_scheme_ = HttpAuth::AUTH_SCHEME_DIGEST;
score_ = 2;
properties_ = ENCRYPTS_IDENTITY;
@@ -226,7 +227,7 @@ bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name,
const std::string& value) {
if (LowerCaseEqualsASCII(name, "realm")) {
std::string realm;
- if (!base::ConvertToUtf8AndNormalize(value, base::kCodepageLatin1, &realm))
+ if (!net::ConvertToUtf8AndNormalize(value, kCharsetLatin1, &realm))
return false;
realm_ = realm;
original_realm_ = value;
@@ -322,9 +323,9 @@ std::string HttpAuthHandlerDigest::AssembleResponseDigest(
const std::string& nc) const {
// ha1 = MD5(A1)
// TODO(eroman): is this the right encoding?
- std::string ha1 = base::MD5String(UTF16ToUTF8(credentials.username()) + ":" +
- original_realm_ + ":" +
- UTF16ToUTF8(credentials.password()));
+ std::string ha1 = base::MD5String(base::UTF16ToUTF8(credentials.username()) +
+ ":" + original_realm_ + ":" +
+ base::UTF16ToUTF8(credentials.password()));
if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS)
ha1 = base::MD5String(ha1 + ":" + nonce_ + ":" + cnonce);
@@ -352,7 +353,7 @@ std::string HttpAuthHandlerDigest::AssembleCredentials(
// TODO(eroman): is this the right encoding?
std::string authorization = (std::string("Digest username=") +
HttpUtil::Quote(
- UTF16ToUTF8(credentials.username())));
+ base::UTF16ToUTF8(credentials.username())));
authorization += ", realm=" + HttpUtil::Quote(original_realm_);
authorization += ", nonce=" + HttpUtil::Quote(nonce_);
authorization += ", uri=" + HttpUtil::Quote(path);
diff --git a/chromium/net/http/http_auth_handler_digest.h b/chromium/net/http/http_auth_handler_digest.h
index 7edac166c6d..6a960d9cba7 100644
--- a/chromium/net/http/http_auth_handler_digest.h
+++ b/chromium/net/http/http_auth_handler_digest.h
@@ -65,7 +65,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler {
void set_nonce_generator(const NonceGenerator* nonce_generator);
virtual int CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -78,10 +78,10 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler {
};
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE;
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials,
const HttpRequestInfo* request,
@@ -124,7 +124,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler {
// Parse the challenge, saving the results into this instance.
// Returns true on success.
- bool ParseChallenge(HttpAuth::ChallengeTokenizer* challenge);
+ bool ParseChallenge(HttpAuthChallengeTokenizer* challenge);
// Parse an individual property. Returns true on success.
bool ParseChallengeProperty(const std::string& name,
diff --git a/chromium/net/http/http_auth_handler_digest_unittest.cc b/chromium/net/http/http_auth_handler_digest_unittest.cc
index dee44ebd5d0..5c3941c9efe 100644
--- a/chromium/net/http/http_auth_handler_digest_unittest.cc
+++ b/chromium/net/http/http_auth_handler_digest_unittest.cc
@@ -9,6 +9,7 @@
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_digest.h"
#include "net/http/http_request_info.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -67,7 +68,8 @@ bool RespondToChallenge(HttpAuth::Target target,
TestCompletionCallback callback;
scoped_ptr<HttpRequestInfo> request(new HttpRequestInfo());
request->url = GURL(request_url);
- AuthCredentials credentials(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"));
+ AuthCredentials credentials(base::ASCIIToUTF16("foo"),
+ base::ASCIIToUTF16("bar"));
int rv_generate = handler->GenerateAuthToken(
&credentials, request.get(), callback.callback(), token);
if (rv_generate != OK) {
@@ -530,8 +532,8 @@ TEST(HttpAuthHandlerDigestTest, AssembleCredentials) {
digest->AssembleCredentials(tests[i].req_method,
tests[i].req_path,
AuthCredentials(
- ASCIIToUTF16(tests[i].username),
- ASCIIToUTF16(tests[i].password)),
+ base::ASCIIToUTF16(tests[i].username),
+ base::ASCIIToUTF16(tests[i].password)),
tests[i].cnonce,
tests[i].nonce_count);
@@ -551,27 +553,27 @@ TEST(HttpAuthHandlerDigest, HandleAnotherChallenge) {
&handler);
EXPECT_EQ(OK, rv);
ASSERT_TRUE(handler.get() != NULL);
- HttpAuth::ChallengeTokenizer tok_default(default_challenge.begin(),
- default_challenge.end());
+ HttpAuthChallengeTokenizer tok_default(default_challenge.begin(),
+ default_challenge.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
handler->HandleAnotherChallenge(&tok_default));
std::string stale_challenge = default_challenge + ", stale=true";
- HttpAuth::ChallengeTokenizer tok_stale(stale_challenge.begin(),
- stale_challenge.end());
+ HttpAuthChallengeTokenizer tok_stale(stale_challenge.begin(),
+ stale_challenge.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_STALE,
handler->HandleAnotherChallenge(&tok_stale));
std::string stale_false_challenge = default_challenge + ", stale=false";
- HttpAuth::ChallengeTokenizer tok_stale_false(stale_false_challenge.begin(),
- stale_false_challenge.end());
+ HttpAuthChallengeTokenizer tok_stale_false(stale_false_challenge.begin(),
+ stale_false_challenge.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
handler->HandleAnotherChallenge(&tok_stale_false));
std::string realm_change_challenge =
"Digest realm=\"SomethingElse\", nonce=\"nonce-value2\"";
- HttpAuth::ChallengeTokenizer tok_realm_change(realm_change_challenge.begin(),
- realm_change_challenge.end());
+ HttpAuthChallengeTokenizer tok_realm_change(realm_change_challenge.begin(),
+ realm_change_challenge.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM,
handler->HandleAnotherChallenge(&tok_realm_change));
}
diff --git a/chromium/net/http/http_auth_handler_factory.cc b/chromium/net/http/http_auth_handler_factory.cc
index f94bd33d666..9867bb99c1b 100644
--- a/chromium/net/http/http_auth_handler_factory.cc
+++ b/chromium/net/http/http_auth_handler_factory.cc
@@ -7,6 +7,7 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_filter.h"
#include "net/http/http_auth_handler_basic.h"
#include "net/http/http_auth_handler_digest.h"
@@ -24,7 +25,7 @@ int HttpAuthHandlerFactory::CreateAuthHandlerFromString(
const GURL& origin,
const BoundNetLog& net_log,
scoped_ptr<HttpAuthHandler>* handler) {
- HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
+ HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
return CreateAuthHandler(&props, target, origin, CREATE_CHALLENGE, 1,
net_log, handler);
}
@@ -36,7 +37,7 @@ int HttpAuthHandlerFactory::CreatePreemptiveAuthHandlerFromString(
int digest_nonce_count,
const BoundNetLog& net_log,
scoped_ptr<HttpAuthHandler>* handler) {
- HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
+ HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
return CreateAuthHandler(&props, target, origin, CREATE_PREEMPTIVE,
digest_nonce_count, net_log, handler);
}
@@ -172,7 +173,7 @@ HttpAuthHandlerRegistryFactory* HttpAuthHandlerRegistryFactory::Create(
}
int HttpAuthHandlerRegistryFactory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_factory.h b/chromium/net/http/http_auth_handler_factory.h
index 27627514979..e712aafcb0a 100644
--- a/chromium/net/http/http_auth_handler_factory.h
+++ b/chromium/net/http/http_auth_handler_factory.h
@@ -20,6 +20,7 @@ namespace net {
class BoundNetLog;
class HostResolver;
+class HttpAuthChallengeTokenizer;
class HttpAuthHandler;
class HttpAuthHandlerRegistryFactory;
@@ -73,7 +74,7 @@ class NET_EXPORT HttpAuthHandlerFactory {
// NOTE: This will apply to ALL |origin| values if the filters are empty.
//
// |*challenge| should not be reused after a call to |CreateAuthHandler()|,
- virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+ virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason create_reason,
@@ -182,7 +183,7 @@ class NET_EXPORT HttpAuthHandlerRegistryFactory
// Creates an auth handler by dispatching out to the registered factories
// based on the first token in |challenge|.
- virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+ virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_factory_unittest.cc b/chromium/net/http/http_auth_handler_factory_unittest.cc
index 8e69919c0bf..06e8933f25a 100644
--- a/chromium/net/http/http_auth_handler_factory_unittest.cc
+++ b/chromium/net/http/http_auth_handler_factory_unittest.cc
@@ -21,7 +21,7 @@ class MockHttpAuthHandlerFactory : public HttpAuthHandlerFactory {
return_code_(return_code) {}
virtual ~MockHttpAuthHandlerFactory() {}
- virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+ virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_mock.cc b/chromium/net/http/http_auth_handler_mock.cc
index 826c3d6a12b..52990eef923 100644
--- a/chromium/net/http/http_auth_handler_mock.cc
+++ b/chromium/net/http/http_auth_handler_mock.cc
@@ -8,6 +8,7 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_request_info.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -77,7 +78,7 @@ void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) {
}
HttpAuth::AuthorizationResult HttpAuthHandlerMock::HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
// If we receive an empty challenge for a connection based scheme, or a second
// challenge for a non connection based scheme, assume it's a rejection.
if (!is_connection_based() || challenge->base64_param().empty())
@@ -99,7 +100,7 @@ bool HttpAuthHandlerMock::AllowsExplicitCredentials() {
return allows_explicit_credentials_;
}
-bool HttpAuthHandlerMock::Init(HttpAuth::ChallengeTokenizer* challenge) {
+bool HttpAuthHandlerMock::Init(HttpAuthChallengeTokenizer* challenge) {
auth_scheme_ = HttpAuth::AUTH_SCHEME_MOCK;
score_ = 1;
properties_ = connection_based_ ? IS_CONNECTION_BASED : 0;
@@ -164,7 +165,7 @@ void HttpAuthHandlerMock::Factory::AddMockHandler(
}
int HttpAuthHandlerMock::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_mock.h b/chromium/net/http/http_auth_handler_mock.h
index 7cc441a70cc..a4642dfe0f0 100644
--- a/chromium/net/http/http_auth_handler_mock.h
+++ b/chromium/net/http/http_auth_handler_mock.h
@@ -43,7 +43,7 @@ class HttpAuthHandlerMock : public HttpAuthHandler {
// HttpAuthHandlerFactory:
virtual int CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -88,13 +88,13 @@ class HttpAuthHandlerMock : public HttpAuthHandler {
// HttpAuthHandler:
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual bool NeedsIdentity() OVERRIDE;
virtual bool AllowsDefaultCredentials() OVERRIDE;
virtual bool AllowsExplicitCredentials() OVERRIDE;
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials,
const HttpRequestInfo* request,
diff --git a/chromium/net/http/http_auth_handler_negotiate.cc b/chromium/net/http/http_auth_handler_negotiate.cc
index 788b06750b6..422ddd729a2 100644
--- a/chromium/net/http/http_auth_handler_negotiate.cc
+++ b/chromium/net/http/http_auth_handler_negotiate.cc
@@ -37,7 +37,7 @@ void HttpAuthHandlerNegotiate::Factory::set_host_resolver(
}
int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -161,7 +161,7 @@ std::string HttpAuthHandlerNegotiate::CreateSPN(
}
HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
return auth_system_.ParseChallenge(challenge);
}
@@ -184,7 +184,7 @@ bool HttpAuthHandlerNegotiate::AllowsExplicitCredentials() {
// The Negotiate challenge header looks like:
// WWW-Authenticate: NEGOTIATE auth-data
-bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) {
+bool HttpAuthHandlerNegotiate::Init(HttpAuthChallengeTokenizer* challenge) {
#if defined(OS_POSIX)
if (!auth_system_.Init()) {
VLOG(1) << "can't initialize GSSAPI library";
diff --git a/chromium/net/http/http_auth_handler_negotiate.h b/chromium/net/http/http_auth_handler_negotiate.h
index d028178ed51..90bd16ce958 100644
--- a/chromium/net/http/http_auth_handler_negotiate.h
+++ b/chromium/net/http/http_auth_handler_negotiate.h
@@ -70,7 +70,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNegotiate : public HttpAuthHandler {
}
virtual int CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -107,13 +107,13 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNegotiate : public HttpAuthHandler {
// HttpAuthHandler:
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual bool NeedsIdentity() OVERRIDE;
virtual bool AllowsDefaultCredentials() OVERRIDE;
virtual bool AllowsExplicitCredentials() OVERRIDE;
protected:
- virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE;
virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials,
const HttpRequestInfo* request,
diff --git a/chromium/net/http/http_auth_handler_ntlm.cc b/chromium/net/http/http_auth_handler_ntlm.cc
index 922800c71e1..de0fe290a3c 100644
--- a/chromium/net/http/http_auth_handler_ntlm.cc
+++ b/chromium/net/http/http_auth_handler_ntlm.cc
@@ -12,15 +12,16 @@
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
namespace net {
HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) {
+ HttpAuthChallengeTokenizer* challenge) {
return ParseChallenge(challenge, false);
}
-bool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) {
+bool HttpAuthHandlerNTLM::Init(HttpAuthChallengeTokenizer* tok) {
auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM;
score_ = 3;
properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
@@ -100,7 +101,7 @@ int HttpAuthHandlerNTLM::GenerateAuthTokenImpl(
// The NTLM challenge header looks like:
// WWW-Authenticate: NTLM auth-data
HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok, bool initial_challenge) {
+ HttpAuthChallengeTokenizer* tok, bool initial_challenge) {
#if defined(NTLM_SSPI)
// auth_sspi_ contains state for whether or not this is the initial challenge.
return auth_sspi_.ParseChallenge(tok);
diff --git a/chromium/net/http/http_auth_handler_ntlm.h b/chromium/net/http/http_auth_handler_ntlm.h
index 98bd362d543..9e2abc6254a 100644
--- a/chromium/net/http/http_auth_handler_ntlm.h
+++ b/chromium/net/http/http_auth_handler_ntlm.h
@@ -42,7 +42,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler {
virtual ~Factory();
virtual int CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
@@ -109,14 +109,14 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler {
virtual bool AllowsDefaultCredentials() OVERRIDE;
virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
- HttpAuth::ChallengeTokenizer* challenge) OVERRIDE;
+ HttpAuthChallengeTokenizer* challenge) OVERRIDE;
protected:
// This function acquires a credentials handle in the SSPI implementation.
// It does nothing in the portable implementation.
int InitializeBeforeFirstChallenge();
- virtual bool Init(HttpAuth::ChallengeTokenizer* tok) OVERRIDE;
+ virtual bool Init(HttpAuthChallengeTokenizer* tok) OVERRIDE;
virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials,
const HttpRequestInfo* request,
@@ -135,7 +135,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler {
// Parse the challenge, saving the results into this instance.
HttpAuth::AuthorizationResult ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok, bool initial_challenge);
+ HttpAuthChallengeTokenizer* tok, bool initial_challenge);
// Given an input token received from the server, generate the next output
// token to be sent to the server.
diff --git a/chromium/net/http/http_auth_handler_ntlm_portable.cc b/chromium/net/http/http_auth_handler_ntlm_portable.cc
index d1fbf23c59f..035a6dc8017 100644
--- a/chromium/net/http/http_auth_handler_ntlm_portable.cc
+++ b/chromium/net/http/http_auth_handler_ntlm_portable.cc
@@ -70,12 +70,10 @@ namespace net {
*
* ***** END LICENSE BLOCK ***** */
-// Discover the endianness by testing processor architecture.
-#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64)\
- || defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPSEL)
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
#define IS_LITTLE_ENDIAN 1
#undef IS_BIG_ENDIAN
-#elif defined(ARCH_CPU_MIPSEB)
+#elif defined(ARCH_CPU_BIG_ENDIAN)
#define IS_BIG_ENDIAN 1
#undef IS_LITTLE_ENDIAN
#else
@@ -211,7 +209,8 @@ static void* WriteSecBuf(void* buf, uint16 length, uint32 offset) {
* to pass the same buffer as both input and output, which is a handy way to
* convert the unicode buffer to little-endian on big-endian platforms.
*/
-static void* WriteUnicodeLE(void* buf, const char16* str, uint32 str_len) {
+static void* WriteUnicodeLE(
+ void* buf, const base::char16* str, uint32 str_len) {
// Convert input string from BE to LE.
uint8* cursor = static_cast<uint8*>(buf);
const uint8* input = reinterpret_cast<const uint8*>(str);
@@ -257,7 +256,7 @@ static void LM_Hash(const base::string16& password, uint8* hash) {
// Convert password to OEM character set. We'll just use the native
// filesystem charset.
- std::string passbuf = base::SysWideToNativeMB(UTF16ToWide(password));
+ std::string passbuf = base::SysWideToNativeMB(base::UTF16ToWide(password));
StringToUpperASCII(&passbuf);
passbuf.resize(14, '\0');
@@ -480,14 +479,15 @@ static int GenerateType3Msg(const base::string16& domain,
ucs_domain_buf = domain;
domain_ptr = ucs_domain_buf.data();
domain_len = ucs_domain_buf.length() * 2;
- WriteUnicodeLE(const_cast<void*>(domain_ptr), (const char16*) domain_ptr,
+ WriteUnicodeLE(const_cast<void*>(domain_ptr),
+ (const base::char16*) domain_ptr,
ucs_domain_buf.length());
#else
domain_ptr = domain.data();
domain_len = domain.length() * 2;
#endif
} else {
- oem_domain_buf = base::SysWideToNativeMB(UTF16ToWide(domain));
+ oem_domain_buf = base::SysWideToNativeMB(base::UTF16ToWide(domain));
domain_ptr = oem_domain_buf.data();
domain_len = oem_domain_buf.length();
}
@@ -500,14 +500,14 @@ static int GenerateType3Msg(const base::string16& domain,
ucs_user_buf = username;
user_ptr = ucs_user_buf.data();
user_len = ucs_user_buf.length() * 2;
- WriteUnicodeLE(const_cast<void*>(user_ptr), (const char16*) user_ptr,
+ WriteUnicodeLE(const_cast<void*>(user_ptr), (const base::char16*) user_ptr,
ucs_user_buf.length());
#else
user_ptr = username.data();
user_len = username.length() * 2;
#endif
} else {
- oem_user_buf = base::SysWideToNativeMB(UTF16ToWide(username));
+ oem_user_buf = base::SysWideToNativeMB(base::UTF16ToWide(username));
user_ptr = oem_user_buf.data();
user_len = oem_user_buf.length();
}
@@ -521,7 +521,7 @@ static int GenerateType3Msg(const base::string16& domain,
host_ptr = ucs_host_buf.data();
host_len = ucs_host_buf.length() * 2;
#ifdef IS_BIG_ENDIAN
- WriteUnicodeLE(const_cast<void*>(host_ptr), (const char16*) host_ptr,
+ WriteUnicodeLE(const_cast<void*>(host_ptr), (const base::char16*) host_ptr,
ucs_host_buf.length());
#endif
} else {
@@ -707,7 +707,7 @@ int HttpAuthHandlerNTLM::GetNextToken(const void* in_token,
}
int HttpAuthHandlerNTLM::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_ntlm_win.cc b/chromium/net/http/http_auth_handler_ntlm_win.cc
index 041522f79e6..acc6fc15e2e 100644
--- a/chromium/net/http/http_auth_handler_ntlm_win.cc
+++ b/chromium/net/http/http_auth_handler_ntlm_win.cc
@@ -52,7 +52,7 @@ HttpAuthHandlerNTLM::Factory::~Factory() {
}
int HttpAuthHandlerNTLM::Factory::CreateAuthHandler(
- HttpAuth::ChallengeTokenizer* challenge,
+ HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const GURL& origin,
CreateReason reason,
diff --git a/chromium/net/http/http_auth_handler_unittest.cc b/chromium/net/http/http_auth_handler_unittest.cc
index f8928fcc428..a53573d6b47 100644
--- a/chromium/net/http/http_auth_handler_unittest.cc
+++ b/chromium/net/http/http_auth_handler_unittest.cc
@@ -10,6 +10,7 @@
#include "net/base/net_errors.h"
#include "net/base/net_log_unittest.h"
#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_mock.h"
#include "net/http/http_request_info.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +20,8 @@ namespace net {
TEST(HttpAuthHandlerTest, NetLog) {
GURL origin("http://www.example.com");
std::string challenge = "Mock asdf";
- AuthCredentials credentials(ASCIIToUTF16("user"), ASCIIToUTF16("pass"));
+ AuthCredentials credentials(base::ASCIIToUTF16("user"),
+ base::ASCIIToUTF16("pass"));
std::string auth_token;
HttpRequestInfo request;
@@ -33,7 +35,7 @@ TEST(HttpAuthHandlerTest, NetLog) {
(k == 0) ? HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER;
NetLog::EventType event_type =
(k == 0) ? NetLog::TYPE_AUTH_PROXY : NetLog::TYPE_AUTH_SERVER;
- HttpAuth::ChallengeTokenizer tokenizer(
+ HttpAuthChallengeTokenizer tokenizer(
challenge.begin(), challenge.end());
HttpAuthHandlerMock mock_handler;
CapturingNetLog capturing_net_log;
diff --git a/chromium/net/http/http_auth_sspi_win.cc b/chromium/net/http/http_auth_sspi_win.cc
index 5718f9ef753..8a0e49fff11 100644
--- a/chromium/net/http/http_auth_sspi_win.cc
+++ b/chromium/net/http/http_auth_sspi_win.cc
@@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
namespace net {
@@ -226,7 +227,7 @@ void HttpAuthSSPI::ResetSecurityContext() {
}
HttpAuth::AuthorizationResult HttpAuthSSPI::ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok) {
+ HttpAuthChallengeTokenizer* tok) {
// Verify the challenge's auth-scheme.
if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
return HttpAuth::AUTHORIZATION_RESULT_INVALID;
diff --git a/chromium/net/http/http_auth_sspi_win.h b/chromium/net/http/http_auth_sspi_win.h
index bc7da46c312..dd0b1772b16 100644
--- a/chromium/net/http/http_auth_sspi_win.h
+++ b/chromium/net/http/http_auth_sspi_win.h
@@ -22,6 +22,8 @@
namespace net {
+class HttpAuthChallengeTokenizer;
+
// SSPILibrary is introduced so unit tests can mock the calls to Windows' SSPI
// implementation. The default implementation simply passes the arguments on to
// the SSPI implementation provided by Secur32.dll.
@@ -134,7 +136,7 @@ class NET_EXPORT_PRIVATE HttpAuthSSPI {
bool AllowsExplicitCredentials() const;
HttpAuth::AuthorizationResult ParseChallenge(
- HttpAuth::ChallengeTokenizer* tok);
+ HttpAuthChallengeTokenizer* tok);
// Generates an authentication token for the service specified by the
// Service Principal Name |spn| and stores the value in |*auth_token|.
diff --git a/chromium/net/http/http_auth_sspi_win_unittest.cc b/chromium/net/http/http_auth_sspi_win_unittest.cc
index 09fe6fa25ef..586822d2ef7 100644
--- a/chromium/net/http/http_auth_sspi_win_unittest.cc
+++ b/chromium/net/http/http_auth_sspi_win_unittest.cc
@@ -4,6 +4,7 @@
#include "base/basictypes.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_sspi_win.h"
#include "net/http/mock_sspi_library_win.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,8 +63,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_FirstRound) {
HttpAuthSSPI auth_sspi(&mock_library, "Negotiate",
NEGOSSP_NAME, kMaxTokenLength);
std::string challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
- challenge_text.end());
+ HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
+ challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_sspi.ParseChallenge(&challenge));
}
@@ -75,8 +76,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_TwoRounds) {
HttpAuthSSPI auth_sspi(&mock_library, "Negotiate",
NEGOSSP_NAME, kMaxTokenLength);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_sspi.ParseChallenge(&first_challenge));
@@ -86,8 +87,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_TwoRounds) {
&auth_token));
std::string second_challenge_text = "Negotiate Zm9vYmFy";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_sspi.ParseChallenge(&second_challenge));
}
@@ -99,8 +100,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_UnexpectedTokenFirstRound) {
HttpAuthSSPI auth_sspi(&mock_library, "Negotiate",
NEGOSSP_NAME, kMaxTokenLength);
std::string challenge_text = "Negotiate Zm9vYmFy";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
- challenge_text.end());
+ HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
+ challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
auth_sspi.ParseChallenge(&challenge));
}
@@ -112,8 +113,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_MissingTokenSecondRound) {
HttpAuthSSPI auth_sspi(&mock_library, "Negotiate",
NEGOSSP_NAME, kMaxTokenLength);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_sspi.ParseChallenge(&first_challenge));
@@ -121,8 +122,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_MissingTokenSecondRound) {
EXPECT_EQ(OK, auth_sspi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
&auth_token));
std::string second_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
auth_sspi.ParseChallenge(&second_challenge));
}
@@ -134,8 +135,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_NonBase64EncodedToken) {
HttpAuthSSPI auth_sspi(&mock_library, "Negotiate",
NEGOSSP_NAME, kMaxTokenLength);
std::string first_challenge_text = "Negotiate";
- HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(),
- first_challenge_text.end());
+ HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(),
+ first_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
auth_sspi.ParseChallenge(&first_challenge));
@@ -143,8 +144,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_NonBase64EncodedToken) {
EXPECT_EQ(OK, auth_sspi.GenerateAuthToken(NULL, "HTTP/intranet.google.com",
&auth_token));
std::string second_challenge_text = "Negotiate =happyjoy=";
- HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(),
- second_challenge_text.end());
+ HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(),
+ second_challenge_text.end());
EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
auth_sspi.ParseChallenge(&second_challenge));
}
diff --git a/chromium/net/http/http_auth_unittest.cc b/chromium/net/http/http_auth_unittest.cc
index 6f1471d472a..cf68fa8039d 100644
--- a/chromium/net/http/http_auth_unittest.cc
+++ b/chromium/net/http/http_auth_unittest.cc
@@ -11,6 +11,7 @@
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_auth.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_filter.h"
#include "net/http/http_auth_handler.h"
#include "net/http/http_auth_handler_factory.h"
@@ -28,7 +29,7 @@ HttpAuthHandlerMock* CreateMockHandler(bool connection_based) {
HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock();
auth_handler->set_connection_based(connection_based);
std::string challenge_text = "Basic";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
+ HttpAuthChallengeTokenizer challenge(challenge_text.begin(),
challenge_text.end());
GURL origin("www.example.com");
EXPECT_TRUE(auth_handler->InitFromChallenge(&challenge,
@@ -245,173 +246,6 @@ TEST(HttpAuthTest, HandleChallengeResponse) {
EXPECT_EQ("Mock token_a", challenge_used);
}
-TEST(HttpAuthTest, ChallengeTokenizer) {
- std::string challenge_str = "Basic realm=\"foobar\"";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Basic"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("foobar"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a name=value property with no quote marks.
-TEST(HttpAuthTest, ChallengeTokenizerNoQuotes) {
- std::string challenge_str = "Basic realm=foobar@baz.com";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Basic"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("foobar@baz.com"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a name=value property with mismatching quote marks.
-TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotes) {
- std::string challenge_str = "Basic realm=\"foobar@baz.com";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Basic"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("foobar@baz.com"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a name= property without a value and with mismatching quote marks.
-TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesNoValue) {
- std::string challenge_str = "Basic realm=\"";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Basic"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string(), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a name=value property with mismatching quote marks and spaces in the
-// value.
-TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesSpaces) {
- std::string challenge_str = "Basic realm=\"foo bar";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Basic"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("foo bar"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use multiple name=value properties with mismatching quote marks in the last
-// value.
-TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesMultiple) {
- std::string challenge_str = "Digest qop=auth-int, algorithm=md5, realm=\"foo";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Digest"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("qop"), parameters.name());
- EXPECT_EQ(std::string("auth-int"), parameters.value());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("algorithm"), parameters.name());
- EXPECT_EQ(std::string("md5"), parameters.value());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("foo"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a name= property which has no value.
-TEST(HttpAuthTest, ChallengeTokenizerNoValue) {
- std::string challenge_str = "Digest qop=";
- HttpAuth::ChallengeTokenizer challenge(
- challenge_str.begin(), challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Digest"), challenge.scheme());
- EXPECT_FALSE(parameters.GetNext());
- EXPECT_FALSE(parameters.valid());
-}
-
-// Specify multiple properties, comma separated.
-TEST(HttpAuthTest, ChallengeTokenizerMultiple) {
- std::string challenge_str =
- "Digest algorithm=md5, realm=\"Oblivion\", qop=auth-int";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("Digest"), challenge.scheme());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("algorithm"), parameters.name());
- EXPECT_EQ(std::string("md5"), parameters.value());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("realm"), parameters.name());
- EXPECT_EQ(std::string("Oblivion"), parameters.value());
- EXPECT_TRUE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("qop"), parameters.name());
- EXPECT_EQ(std::string("auth-int"), parameters.value());
- EXPECT_FALSE(parameters.GetNext());
- EXPECT_TRUE(parameters.valid());
-}
-
-// Use a challenge which has no property.
-TEST(HttpAuthTest, ChallengeTokenizerNoProperty) {
- std::string challenge_str = "NTLM";
- HttpAuth::ChallengeTokenizer challenge(
- challenge_str.begin(), challenge_str.end());
- HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs();
-
- EXPECT_TRUE(parameters.valid());
- EXPECT_EQ(std::string("NTLM"), challenge.scheme());
- EXPECT_FALSE(parameters.GetNext());
-}
-
-// Use a challenge with Base64 encoded token.
-TEST(HttpAuthTest, ChallengeTokenizerBase64) {
- std::string challenge_str = "NTLM SGVsbG8sIFdvcmxkCg===";
- HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
- challenge_str.end());
-
- EXPECT_EQ(std::string("NTLM"), challenge.scheme());
- // Notice the two equal statements below due to padding removal.
- EXPECT_EQ(std::string("SGVsbG8sIFdvcmxkCg=="), challenge.base64_param());
-}
-
TEST(HttpAuthTest, GetChallengeHeaderName) {
std::string name;
diff --git a/chromium/net/http/http_basic_stream.cc b/chromium/net/http/http_basic_stream.cc
index 87f6469ad61..0d14e0f9a0e 100644
--- a/chromium/net/http/http_basic_stream.cc
+++ b/chromium/net/http/http_basic_stream.cc
@@ -42,10 +42,6 @@ int HttpBasicStream::ReadResponseHeaders(const CompletionCallback& callback) {
return parser()->ReadResponseHeaders(callback);
}
-const HttpResponseInfo* HttpBasicStream::GetResponseInfo() const {
- return parser()->GetResponseInfo();
-}
-
int HttpBasicStream::ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
@@ -86,7 +82,9 @@ bool HttpBasicStream::IsConnectionReusable() const {
}
int64 HttpBasicStream::GetTotalReceivedBytes() const {
- return parser()->received_bytes();
+ if (parser())
+ return parser()->received_bytes();
+ return 0;
}
bool HttpBasicStream::GetLoadTimingInfo(
diff --git a/chromium/net/http/http_basic_stream.h b/chromium/net/http/http_basic_stream.h
index 4a67ccaac00..00253c37db4 100644
--- a/chromium/net/http/http_basic_stream.h
+++ b/chromium/net/http/http_basic_stream.h
@@ -46,8 +46,6 @@ class HttpBasicStream : public HttpStream {
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
-
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
diff --git a/chromium/net/http/http_byte_range.h b/chromium/net/http/http_byte_range.h
index 98708721a4f..63c02f1ba5c 100644
--- a/chromium/net/http/http_byte_range.h
+++ b/chromium/net/http/http_byte_range.h
@@ -13,8 +13,8 @@
namespace net {
// A container class that represents a "range" specified for range request
-// specified by RFC 2616 Section 14.35.1.
-// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
+// specified by RFC 7233 Section 2.1.
+// https://tools.ietf.org/html/rfc7233#section-2.1
class NET_EXPORT HttpByteRange {
public:
HttpByteRange();
diff --git a/chromium/net/http/http_cache.cc b/chromium/net/http/http_cache.cc
index 49591da8ff4..3ae69c80a6f 100644
--- a/chromium/net/http/http_cache.cc
+++ b/chromium/net/http/http_cache.cc
@@ -33,6 +33,7 @@
#include "net/base/net_errors.h"
#include "net/base/upload_data_stream.h"
#include "net/disk_cache/disk_cache.h"
+#include "net/http/disk_cache_based_quic_server_info.h"
#include "net/http/http_cache_transaction.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
@@ -40,6 +41,7 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_util.h"
+#include "net/quic/crypto/quic_server_info.h"
namespace {
@@ -267,23 +269,44 @@ void HttpCache::MetadataWriter::OnIOComplete(int result) {
//-----------------------------------------------------------------------------
+class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
+ public:
+ QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
+ : http_cache_(http_cache) {
+ }
+
+ virtual QuicServerInfo* GetForServer(
+ const QuicServerId& server_id) OVERRIDE {
+ return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
+ }
+
+ private:
+ HttpCache* const http_cache_;
+};
+
+//-----------------------------------------------------------------------------
HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
BackendFactory* backend_factory)
: net_log_(params.net_log),
backend_factory_(backend_factory),
building_backend_(false),
mode_(NORMAL),
- network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))) {
+ network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
+ weak_factory_(this) {
+ SetupQuicServerInfoFactory(network_layer_->GetSession());
}
+// This call doesn't change the shared |session|'s QuicServerInfoFactory because
+// |session| is shared.
HttpCache::HttpCache(HttpNetworkSession* session,
BackendFactory* backend_factory)
: net_log_(session->net_log()),
backend_factory_(backend_factory),
building_backend_(false),
mode_(NORMAL),
- network_layer_(new HttpNetworkLayer(session)) {
+ network_layer_(new HttpNetworkLayer(session)),
+ weak_factory_(this) {
}
HttpCache::HttpCache(HttpTransactionFactory* network_layer,
@@ -293,10 +316,16 @@ HttpCache::HttpCache(HttpTransactionFactory* network_layer,
backend_factory_(backend_factory),
building_backend_(false),
mode_(NORMAL),
- network_layer_(network_layer) {
+ network_layer_(network_layer),
+ weak_factory_(this) {
+ SetupQuicServerInfoFactory(network_layer_->GetSession());
}
HttpCache::~HttpCache() {
+ // Transactions should see an invalid cache after this point; otherwise they
+ // could see an inconsistent object (half destroyed).
+ weak_factory_.InvalidateWeakPtrs();
+
// If we have any active entries remaining, then we need to deactivate them.
// We may have some pending calls to OnProcessPendingQueue, but since those
// won't run (due to our destruction), we can simply ignore the corresponding
@@ -379,7 +408,7 @@ void HttpCache::WriteMetadata(const GURL& url,
}
HttpCache::Transaction* trans =
- new HttpCache::Transaction(priority, this, NULL);
+ new HttpCache::Transaction(priority, this);
MetadataWriter* writer = new MetadataWriter(trans);
// The writer will self destruct when done.
@@ -387,17 +416,13 @@ void HttpCache::WriteMetadata(const GURL& url,
}
void HttpCache::CloseAllConnections() {
- net::HttpNetworkLayer* network =
- static_cast<net::HttpNetworkLayer*>(network_layer_.get());
- HttpNetworkSession* session = network->GetSession();
+ HttpNetworkSession* session = GetSession();
if (session)
session->CloseAllConnections();
}
void HttpCache::CloseIdleConnections() {
- net::HttpNetworkLayer* network =
- static_cast<net::HttpNetworkLayer*>(network_layer_.get());
- HttpNetworkSession* session = network->GetSession();
+ HttpNetworkSession* session = GetSession();
if (session)
session->CloseIdleConnections();
}
@@ -421,15 +446,14 @@ void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
}
int HttpCache::CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) {
+ scoped_ptr<HttpTransaction>* trans) {
// Do lazy initialization of disk cache if needed.
if (!disk_cache_.get()) {
// We don't care about the result.
CreateBackend(NULL, net::CompletionCallback());
}
- trans->reset(new HttpCache::Transaction(priority, this, delegate));
+ trans->reset(new HttpCache::Transaction(priority, this));
return OK;
}
@@ -438,9 +462,15 @@ HttpCache* HttpCache::GetCache() {
}
HttpNetworkSession* HttpCache::GetSession() {
- net::HttpNetworkLayer* network =
- static_cast<net::HttpNetworkLayer*>(network_layer_.get());
- return network->GetSession();
+ return network_layer_->GetSession();
+}
+
+scoped_ptr<HttpTransactionFactory>
+HttpCache::SetHttpNetworkTransactionFactoryForTesting(
+ scoped_ptr<HttpTransactionFactory> new_network_layer) {
+ scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
+ network_layer_ = new_network_layer.Pass();
+ return old_network_layer.Pass();
}
//-----------------------------------------------------------------------------
@@ -468,7 +498,7 @@ int HttpCache::CreateBackend(disk_cache::Backend** backend,
pending_op->writer = item.release();
pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
- AsWeakPtr(), pending_op);
+ GetWeakPtr(), pending_op);
int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
pending_op->callback);
@@ -583,7 +613,7 @@ int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
pending_op->writer = item;
pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
- AsWeakPtr(), pending_op);
+ GetWeakPtr(), pending_op);
int rv = disk_cache_->DoomEntry(key, pending_op->callback);
if (rv != ERR_IO_PENDING) {
@@ -723,7 +753,7 @@ int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
pending_op->writer = item;
pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
- AsWeakPtr(), pending_op);
+ GetWeakPtr(), pending_op);
int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
pending_op->callback);
@@ -752,7 +782,7 @@ int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
pending_op->writer = item;
pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
- AsWeakPtr(), pending_op);
+ GetWeakPtr(), pending_op);
int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
pending_op->callback);
@@ -968,6 +998,16 @@ bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
return false;
}
+void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
+ if (session && session->params().enable_quic_persist_server_info &&
+ !session->quic_stream_factory()->has_quic_server_info_factory()) {
+ DCHECK(!quic_server_info_factory_);
+ quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
+ session->quic_stream_factory()->set_quic_server_info_factory(
+ quic_server_info_factory_.get());
+ }
+}
+
void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
// Multiple readers may finish with an entry at once, so we want to batch up
// calls to OnProcessPendingQueue. This flag also tells us that we should
@@ -978,7 +1018,7 @@ void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&HttpCache::OnProcessPendingQueue, AsWeakPtr(), entry));
+ base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
}
void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
@@ -1135,8 +1175,8 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &HttpCache::OnBackendCreated, AsWeakPtr(), result, pending_op));
+ base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
+ result, pending_op));
} else {
building_backend_ = false;
DeletePendingOp(pending_op);
diff --git a/chromium/net/http/http_cache.h b/chromium/net/http/http_cache.h
index db2db6906d5..732a9c09c75 100644
--- a/chromium/net/http/http_cache.h
+++ b/chromium/net/http/http_cache.h
@@ -60,7 +60,6 @@ class ViewCacheHelper;
struct HttpRequestInfo;
class NET_EXPORT HttpCache : public HttpTransactionFactory,
- public base::SupportsWeakPtr<HttpCache>,
NON_EXPORTED_BASE(public base::NonThreadSafe) {
public:
// The cache mode of operation.
@@ -125,17 +124,16 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
HttpCache(const net::HttpNetworkSession::Params& params,
BackendFactory* backend_factory);
- // The disk cache is initialized lazily (by CreateTransaction) in this case.
+ // The disk cache is initialized lazily (by CreateTransaction) in this case.
// Provide an existing HttpNetworkSession, the cache can construct a
// network layer with a shared HttpNetworkSession in order for multiple
// network layers to share information (e.g. authentication data). The
// HttpCache takes ownership of the |backend_factory|.
HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory);
- // Initialize the cache from its component parts, which is useful for
- // testing. The lifetime of the network_layer and backend_factory are managed
- // by the HttpCache and will be destroyed using |delete| when the HttpCache is
- // destroyed.
+ // Initialize the cache from its component parts. The lifetime of the
+ // |network_layer| and |backend_factory| are managed by the HttpCache and
+ // will be destroyed using |delete| when the HttpCache is destroyed.
HttpCache(HttpTransactionFactory* network_layer,
NetLog* net_log,
BackendFactory* backend_factory);
@@ -191,12 +189,23 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
// HttpTransactionFactory implementation:
virtual int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) OVERRIDE;
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE;
virtual HttpCache* GetCache() OVERRIDE;
virtual HttpNetworkSession* GetSession() OVERRIDE;
- protected:
+ base::WeakPtr<HttpCache> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+
+ // Resets the network layer to allow for tests that probe
+ // network changes (e.g. host unreachable). The old network layer is
+ // returned to allow for filter patterns that only intercept
+ // some creation requests. Note ownership exchange.
+ scoped_ptr<HttpTransactionFactory>
+ SetHttpNetworkTransactionFactoryForTesting(
+ scoped_ptr<HttpTransactionFactory> new_network_layer);
+
+ private:
+ // Types --------------------------------------------------------------------
+
// Disk cache entry data indices.
enum {
kResponseInfoIndex = 0,
@@ -206,15 +215,13 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
// Must remain at the end of the enum.
kNumCacheEntryDataIndices
};
- friend class ViewCacheHelper;
-
- private:
- // Types --------------------------------------------------------------------
class MetadataWriter;
+ class QuicServerInfoFactoryAdaptor;
class Transaction;
class WorkItem;
friend class Transaction;
+ friend class ViewCacheHelper;
struct PendingOp; // Info for an entry under construction.
typedef std::list<Transaction*> TransactionList;
@@ -347,6 +354,9 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
Transaction* trans);
+ // Instantiates and sets QUIC server info factory.
+ void SetupQuicServerInfoFactory(HttpNetworkSession* session);
+
// Resumes processing the pending list of |entry|.
void ProcessPendingQueue(ActiveEntry* entry);
@@ -382,7 +392,10 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
Mode mode_;
- const scoped_ptr<HttpTransactionFactory> network_layer_;
+ scoped_ptr<QuicServerInfoFactoryAdaptor> quic_server_info_factory_;
+
+ scoped_ptr<HttpTransactionFactory> network_layer_;
+
scoped_ptr<disk_cache::Backend> disk_cache_;
// The set of active entries indexed by cache key.
@@ -396,6 +409,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
scoped_ptr<PlaybackCacheMap> playback_cache_map_;
+ base::WeakPtrFactory<HttpCache> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(HttpCache);
};
diff --git a/chromium/net/http/http_cache_transaction.cc b/chromium/net/http/http_cache_transaction.cc
index 88fb53a69c4..efb21e1df17 100644
--- a/chromium/net/http/http_cache_transaction.cc
+++ b/chromium/net/http/http_cache_transaction.cc
@@ -36,7 +36,6 @@
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_transaction.h"
-#include "net/http/http_transaction_delegate.h"
#include "net/http/http_util.h"
#include "net/http/partial_data.h"
#include "net/ssl/ssl_cert_request_info.h"
@@ -118,6 +117,15 @@ void RecordVaryHeaderHistogram(const net::HttpResponseInfo* response) {
UMA_HISTOGRAM_ENUMERATION("HttpCache.Vary", vary, VARY_MAX);
}
+void RecordNoStoreHeaderHistogram(int load_flags,
+ const net::HttpResponseInfo* response) {
+ if (load_flags & net::LOAD_MAIN_FRAME) {
+ UMA_HISTOGRAM_BOOLEAN(
+ "Net.MainFrameNoStore",
+ response->headers->HasHeaderValue("cache-control", "no-store"));
+ }
+}
+
} // namespace
namespace net {
@@ -184,12 +192,11 @@ static bool HeaderMatches(const HttpRequestHeaders& headers,
HttpCache::Transaction::Transaction(
RequestPriority priority,
- HttpCache* cache,
- HttpTransactionDelegate* transaction_delegate)
+ HttpCache* cache)
: next_state_(STATE_NONE),
request_(NULL),
priority_(priority),
- cache_(cache->AsWeakPtr()),
+ cache_(cache->GetWeakPtr()),
entry_(NULL),
new_entry_(NULL),
new_response_(NULL),
@@ -213,7 +220,7 @@ HttpCache::Transaction::Transaction(
io_callback_(base::Bind(&Transaction::OnIOComplete,
weak_factory_.GetWeakPtr())),
transaction_pattern_(PATTERN_UNDEFINED),
- transaction_delegate_(transaction_delegate),
+ total_received_bytes_(0),
websocket_handshake_stream_base_create_helper_(NULL) {
COMPILE_ASSERT(HttpCache::Transaction::kNumValidationHeaders ==
arraysize(kValidationHeaders),
@@ -225,8 +232,6 @@ HttpCache::Transaction::~Transaction() {
// after this point.
callback_.Reset();
- transaction_delegate_ = NULL;
-
if (cache_) {
if (entry_) {
bool cancel_request = reading_ && response_.headers;
@@ -450,6 +455,9 @@ void HttpCache::Transaction::StopCaching() {
// entry how it is (it will be marked as truncated at destruction), and let
// the next piece of code that executes know that we are now reading directly
// from the net.
+ // TODO(mmenke): This doesn't release the lock on the cache entry, so a
+ // future request for the resource will be blocked on this one.
+ // Fix this.
if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() &&
!is_sparse_ && !range_requested_) {
mode_ = NONE;
@@ -465,6 +473,13 @@ bool HttpCache::Transaction::GetFullRequestHeaders(
return false;
}
+int64 HttpCache::Transaction::GetTotalReceivedBytes() const {
+ int64 total_received_bytes = total_received_bytes_;
+ if (network_trans_)
+ total_received_bytes += network_trans_->GetTotalReceivedBytes();
+ return total_received_bytes;
+}
+
void HttpCache::Transaction::DoneReading() {
if (cache_.get() && entry_) {
DCHECK_NE(mode_, UPDATE);
@@ -507,6 +522,9 @@ UploadProgress HttpCache::Transaction::GetUploadProgress() const {
return final_upload_progress_;
}
+void HttpCache::Transaction::SetQuicServerInfo(
+ QuicServerInfo* quic_server_info) {}
+
bool HttpCache::Transaction::GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const {
if (network_trans_)
@@ -541,6 +559,18 @@ void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper(
network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper);
}
+void HttpCache::Transaction::SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) {
+ DCHECK(!network_trans_);
+ before_network_start_callback_ = callback;
+}
+
+int HttpCache::Transaction::ResumeNetworkStart() {
+ if (network_trans_)
+ return network_trans_->ResumeNetworkStart();
+ return ERR_UNEXPECTED;
+}
+
//-----------------------------------------------------------------------------
void HttpCache::Transaction::DoCallback(int rv) {
@@ -791,13 +821,11 @@ int HttpCache::Transaction::DoGetBackend() {
cache_pending_ = true;
next_state_ = STATE_GET_BACKEND_COMPLETE;
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_GET_BACKEND);
- ReportCacheActionStart();
return cache_->GetBackendForTransaction(this);
}
int HttpCache::Transaction::DoGetBackendComplete(int result) {
DCHECK(result == OK || result == ERR_FAILED);
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_GET_BACKEND,
result);
cache_pending_ = false;
@@ -860,10 +888,11 @@ int HttpCache::Transaction::DoSendRequest() {
send_request_since_ = TimeTicks::Now();
// Create a network transaction.
- int rv = cache_->network_layer_->CreateTransaction(
- priority_, &network_trans_, NULL);
+ int rv = cache_->network_layer_->CreateTransaction(priority_,
+ &network_trans_);
if (rv != OK)
return rv;
+ network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_);
// Old load timing information, if any, is now obsolete.
old_network_trans_load_timing_.reset();
@@ -872,15 +901,12 @@ int HttpCache::Transaction::DoSendRequest() {
network_trans_->SetWebSocketHandshakeStreamCreateHelper(
websocket_handshake_stream_base_create_helper_);
- ReportNetworkActionStart();
next_state_ = STATE_SEND_REQUEST_COMPLETE;
rv = network_trans_->Start(request_, io_callback_, net_log_);
return rv;
}
int HttpCache::Transaction::DoSendRequestComplete(int result) {
- ReportNetworkActionFinish();
-
if (!cache_.get())
return ERR_UNEXPECTED;
@@ -938,15 +964,37 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) {
int HttpCache::Transaction::DoSuccessfulSendRequest() {
DCHECK(!new_response_);
const HttpResponseInfo* new_response = network_trans_->GetResponseInfo();
+ bool authentication_failure = false;
if (new_response->headers->response_code() == 401 ||
new_response->headers->response_code() == 407) {
auth_response_ = *new_response;
- return OK;
+ if (!reading_)
+ return OK;
+
+ // We initiated a second request the caller doesn't know about. We should be
+ // able to authenticate this request because we should have authenticated
+ // this URL moments ago.
+ if (IsReadyToRestartForAuth()) {
+ DCHECK(!response_.auth_challenge.get());
+ next_state_ = STATE_SEND_REQUEST_COMPLETE;
+ // In theory we should check to see if there are new cookies, but there
+ // is no way to do that from here.
+ return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_);
+ }
+
+ // We have to perform cleanup at this point so that at least the next
+ // request can succeed.
+ authentication_failure = true;
+ if (entry_)
+ DoomPartialEntry(false);
+ mode_ = NONE;
+ partial_.reset();
}
new_response_ = new_response;
- if (!ValidatePartialResponse() && !auth_response_.headers.get()) {
+ if (authentication_failure ||
+ (!ValidatePartialResponse() && !auth_response_.headers.get())) {
// Something went wrong with this request and we have to restart it.
// If we have an authentication response, we are exposed to weird things
// hapenning if the user cancels the authentication before we receive
@@ -958,18 +1006,13 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
next_state_ = STATE_SEND_REQUEST;
return OK;
}
+
if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) {
// We have stored the full entry, but it changed and the server is
// sending a range. We have to delete the old entry.
UpdateTransactionPattern(PATTERN_NOT_COVERED);
DoneWritingToEntry(false);
}
- if (new_response_->headers->response_code() == 416 &&
- (request_->method == "GET" || request_->method == "POST")) {
- DCHECK_EQ(NONE, mode_);
- response_ = *new_response_;
- return OK;
- }
if (mode_ == WRITE &&
transaction_pattern_ != PATTERN_ENTRY_CANT_CONDITIONALIZE) {
@@ -993,6 +1036,14 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
}
RecordVaryHeaderHistogram(new_response);
+ RecordNoStoreHeaderHistogram(request_->load_flags, new_response);
+
+ if (new_response_->headers->response_code() == 416 &&
+ (request_->method == "GET" || request_->method == "POST")) {
+ // If there is an ective entry it may be destroyed with this transaction.
+ response_ = *new_response_;
+ return OK;
+ }
// Are we expecting a response to a conditional query?
if (mode_ == READ_WRITE || mode_ == UPDATE) {
@@ -1010,7 +1061,6 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
}
int HttpCache::Transaction::DoNetworkRead() {
- ReportNetworkActionStart();
next_state_ = STATE_NETWORK_READ_COMPLETE;
return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_);
}
@@ -1018,8 +1068,6 @@ int HttpCache::Transaction::DoNetworkRead() {
int HttpCache::Transaction::DoNetworkReadComplete(int result) {
DCHECK(mode_ & WRITE || mode_ == NONE);
- ReportNetworkActionFinish();
-
if (!cache_.get())
return ERR_UNEXPECTED;
@@ -1053,7 +1101,6 @@ int HttpCache::Transaction::DoOpenEntry() {
cache_pending_ = true;
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY);
first_cache_access_since_ = TimeTicks::Now();
- ReportCacheActionStart();
return cache_->OpenEntry(cache_key_, &new_entry_, this);
}
@@ -1061,7 +1108,6 @@ int HttpCache::Transaction::DoOpenEntryComplete(int result) {
// It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
// OK, otherwise the cache will end up with an active entry without any
// transaction attached.
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, result);
cache_pending_ = false;
if (result == OK) {
@@ -1105,7 +1151,6 @@ int HttpCache::Transaction::DoCreateEntry() {
next_state_ = STATE_CREATE_ENTRY_COMPLETE;
cache_pending_ = true;
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY);
- ReportCacheActionStart();
return cache_->CreateEntry(cache_key_, &new_entry_, this);
}
@@ -1113,7 +1158,6 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) {
// It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
// OK, otherwise the cache will end up with an active entry without any
// transaction attached.
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY,
result);
cache_pending_ = false;
@@ -1147,12 +1191,10 @@ int HttpCache::Transaction::DoDoomEntry() {
if (first_cache_access_since_.is_null())
first_cache_access_since_ = TimeTicks::Now();
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY);
- ReportCacheActionStart();
return cache_->DoomEntry(cache_key_, this);
}
int HttpCache::Transaction::DoDoomEntryComplete(int result) {
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, result);
next_state_ = STATE_CREATE_ENTRY;
cache_pending_ = false;
@@ -1337,20 +1379,18 @@ int HttpCache::Transaction::DoTruncateCachedData() {
next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE;
if (!entry_)
return OK;
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA);
- ReportCacheActionStart();
// Truncate the stream.
return WriteToEntry(kResponseContentIndex, 0, NULL, 0, io_callback_);
}
int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
if (entry_) {
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents()) {
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA,
- result);
- }
+ if (net_log_.IsLogging()) {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA,
+ result);
+ }
}
next_state_ = STATE_TRUNCATE_CACHED_METADATA;
@@ -1362,16 +1402,14 @@ int HttpCache::Transaction::DoTruncateCachedMetadata() {
if (!entry_)
return OK;
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO);
- ReportCacheActionStart();
return WriteToEntry(kMetadataIndex, 0, NULL, 0, io_callback_);
}
int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) {
if (entry_) {
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO,
result);
}
@@ -1412,13 +1450,11 @@ int HttpCache::Transaction::DoCacheReadResponse() {
read_buf_ = new IOBuffer(io_buf_len_);
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO);
- ReportCacheActionStart();
return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(),
io_buf_len_, io_callback_);
}
int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result);
if (result != io_buf_len_ ||
!HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_,
@@ -1465,18 +1501,16 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
int HttpCache::Transaction::DoCacheWriteResponse() {
if (entry_) {
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO);
- ReportCacheActionStart();
}
return WriteResponseInfoToEntry(false);
}
int HttpCache::Transaction::DoCacheWriteTruncatedResponse() {
if (entry_) {
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO);
- ReportCacheActionStart();
}
return WriteResponseInfoToEntry(true);
}
@@ -1486,8 +1520,7 @@ int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
target_state_ = STATE_NONE;
if (!entry_)
return OK;
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO,
result);
}
@@ -1509,7 +1542,6 @@ int HttpCache::Transaction::DoCacheReadMetadata() {
new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex));
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO);
- ReportCacheActionStart();
return entry_->disk_entry->ReadData(kMetadataIndex, 0,
response_.metadata.get(),
response_.metadata->size(),
@@ -1517,7 +1549,6 @@ int HttpCache::Transaction::DoCacheReadMetadata() {
}
int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) {
- ReportCacheActionFinish();
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result);
if (result != response_.metadata->size())
return OnCacheReadError(result, false);
@@ -1547,9 +1578,8 @@ int HttpCache::Transaction::DoCacheReadData() {
DCHECK(entry_);
next_state_ = STATE_CACHE_READ_DATA_COMPLETE;
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA);
- ReportCacheActionStart();
if (partial_.get()) {
return partial_->CacheRead(entry_->disk_entry, read_buf_.get(), io_buf_len_,
io_callback_);
@@ -1561,8 +1591,7 @@ int HttpCache::Transaction::DoCacheReadData() {
}
int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA,
result);
}
@@ -1593,9 +1622,8 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) {
next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE;
write_len_ = num_bytes;
if (entry_) {
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA);
- ReportCacheActionStart();
}
return AppendResponseDataToEntry(read_buf_.get(), num_bytes, io_callback_);
@@ -1603,8 +1631,7 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) {
int HttpCache::Transaction::DoCacheWriteDataComplete(int result) {
if (entry_) {
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA,
result);
}
@@ -1923,7 +1950,6 @@ int HttpCache::Transaction::RestartNetworkRequest() {
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
- ReportNetworkActionStart();
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartIgnoringLastError(io_callback_);
if (rv != ERR_IO_PENDING)
@@ -1937,7 +1963,6 @@ int HttpCache::Transaction::RestartNetworkRequestWithCertificate(
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
- ReportNetworkActionStart();
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartWithCertificate(client_cert, io_callback_);
if (rv != ERR_IO_PENDING)
@@ -1951,7 +1976,6 @@ int HttpCache::Transaction::RestartNetworkRequestWithAuth(
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
- ReportNetworkActionStart();
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartWithAuth(credentials, io_callback_);
if (rv != ERR_IO_PENDING)
@@ -2265,8 +2289,7 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) {
response_.headers->HasHeaderValue("cache-control", "no-store")) ||
net::IsCertStatusError(response_.ssl_info.cert_status)) {
DoneWritingToEntry(false);
- ReportCacheActionFinish();
- if (net_log_.IsLoggingAllEvents())
+ if (net_log_.IsLogging())
net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO);
return OK;
}
@@ -2387,6 +2410,7 @@ void HttpCache::Transaction::ResetNetworkTransaction() {
LoadTimingInfo load_timing;
if (network_trans_->GetLoadTimingInfo(&load_timing))
old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing));
+ total_received_bytes_ += network_trans_->GetTotalReceivedBytes();
network_trans_.reset();
}
@@ -2422,30 +2446,6 @@ bool HttpCache::Transaction::CanResume(bool has_data) {
return true;
}
-void HttpCache::Transaction::OnIOComplete(int result) {
- DoLoop(result);
-}
-
-void HttpCache::Transaction::ReportCacheActionStart() {
- if (transaction_delegate_)
- transaction_delegate_->OnCacheActionStart();
-}
-
-void HttpCache::Transaction::ReportCacheActionFinish() {
- if (transaction_delegate_)
- transaction_delegate_->OnCacheActionFinish();
-}
-
-void HttpCache::Transaction::ReportNetworkActionStart() {
- if (transaction_delegate_)
- transaction_delegate_->OnNetworkActionStart();
-}
-
-void HttpCache::Transaction::ReportNetworkActionFinish() {
- if (transaction_delegate_)
- transaction_delegate_->OnNetworkActionFinish();
-}
-
void HttpCache::Transaction::UpdateTransactionPattern(
TransactionPattern new_transaction_pattern) {
if (transaction_pattern_ == PATTERN_NOT_COVERED)
@@ -2532,4 +2532,8 @@ void HttpCache::Transaction::RecordHistograms() {
}
}
+void HttpCache::Transaction::OnIOComplete(int result) {
+ DoLoop(result);
+}
+
} // namespace net
diff --git a/chromium/net/http/http_cache_transaction.h b/chromium/net/http/http_cache_transaction.h
index 90c4db5a39c..aeef83e9b9c 100644
--- a/chromium/net/http/http_cache_transaction.h
+++ b/chromium/net/http/http_cache_transaction.h
@@ -23,7 +23,6 @@ namespace net {
class PartialData;
struct HttpRequestInfo;
-class HttpTransactionDelegate;
struct LoadTimingInfo;
// This is the transaction that is returned by the HttpCache transaction
@@ -60,8 +59,7 @@ class HttpCache::Transaction : public HttpTransaction {
};
Transaction(RequestPriority priority,
- HttpCache* cache,
- HttpTransactionDelegate* transaction_delegate);
+ HttpCache* cache);
virtual ~Transaction();
Mode mode() const { return mode_; }
@@ -123,15 +121,20 @@ class HttpCache::Transaction : public HttpTransaction {
virtual void StopCaching() OVERRIDE;
virtual bool GetFullRequestHeaders(
HttpRequestHeaders* headers) const OVERRIDE;
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
virtual void DoneReading() OVERRIDE;
virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual LoadState GetLoadState() const OVERRIDE;
virtual UploadProgress GetUploadProgress(void) const OVERRIDE;
+ virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) OVERRIDE;
virtual bool GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const OVERRIDE;
virtual void SetPriority(RequestPriority priority) OVERRIDE;
virtual void SetWebSocketHandshakeStreamCreateHelper(
net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE;
+ virtual void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) OVERRIDE;
+ virtual int ResumeNetworkStart() OVERRIDE;
private:
static const size_t kNumValidationHeaders = 2;
@@ -373,16 +376,12 @@ class HttpCache::Transaction : public HttpTransaction {
// data is considered for the result.
bool CanResume(bool has_data);
- // Called to signal completion of asynchronous IO.
- void OnIOComplete(int result);
-
- void ReportCacheActionStart();
- void ReportCacheActionFinish();
- void ReportNetworkActionStart();
- void ReportNetworkActionFinish();
void UpdateTransactionPattern(TransactionPattern new_transaction_pattern);
void RecordHistograms();
+ // Called to signal completion of asynchronous IO.
+ void OnIOComplete(int result);
+
State next_state_;
const HttpRequestInfo* request_;
RequestPriority priority_;
@@ -403,14 +402,14 @@ class HttpCache::Transaction : public HttpTransaction {
std::string cache_key_;
Mode mode_;
State target_state_;
- bool reading_; // We are already reading.
+ bool reading_; // We are already reading. Never reverts to false once set.
bool invalid_range_; // We may bypass the cache for this request.
bool truncated_; // We don't have all the response data.
bool is_sparse_; // The data is stored in sparse byte ranges.
bool range_requested_; // The user requested a byte range.
bool handling_206_; // We must deal with this 206 response.
bool cache_pending_; // We are waiting for the HttpCache.
- bool done_reading_;
+ bool done_reading_; // All available data was read.
bool vary_mismatch_; // The request doesn't match the stored vary data.
bool couldnt_conditionalize_request_;
scoped_refptr<IOBuffer> read_buf_;
@@ -429,7 +428,7 @@ class HttpCache::Transaction : public HttpTransaction {
base::TimeTicks first_cache_access_since_;
base::TimeTicks send_request_since_;
- HttpTransactionDelegate* transaction_delegate_;
+ int64 total_received_bytes_;
// Load timing information for the last network request, if any. Set in the
// 304 and 206 response cases, as the network transaction may be destroyed
@@ -443,6 +442,8 @@ class HttpCache::Transaction : public HttpTransaction {
WebSocketHandshakeStreamBase::CreateHelper*
websocket_handshake_stream_base_create_helper_;
+ BeforeNetworkStartCallback before_network_start_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Transaction);
};
diff --git a/chromium/net/http/http_cache_unittest.cc b/chromium/net/http/http_cache_unittest.cc
index aa6056a4177..c87fa1b7eec 100644
--- a/chromium/net/http/http_cache_unittest.cc
+++ b/chromium/net/http/http_cache_unittest.cc
@@ -4,6 +4,8 @@
#include "net/http/http_cache.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_vector.h"
@@ -27,8 +29,7 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_transaction.h"
-#include "net/http/http_transaction_delegate.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/http_util.h"
#include "net/http/mock_http_cache.h"
#include "net/socket/client_socket_handle.h"
@@ -108,60 +109,6 @@ class DeleteCacheCompletionCallback : public net::TestCompletionCallbackBase {
//-----------------------------------------------------------------------------
// helpers
-class TestHttpTransactionDelegate : public net::HttpTransactionDelegate {
- public:
- TestHttpTransactionDelegate(int num_cache_actions_to_observe,
- int num_network_actions_to_observe)
- : num_callbacks_observed_(0),
- num_remaining_cache_actions_to_observe_(num_cache_actions_to_observe),
- num_remaining_network_actions_to_observe_(
- num_network_actions_to_observe),
- cache_action_in_progress_(false),
- network_action_in_progress_(false) {
- }
- virtual ~TestHttpTransactionDelegate() {
- EXPECT_EQ(0, num_remaining_cache_actions_to_observe_);
- EXPECT_EQ(0, num_remaining_network_actions_to_observe_);
- EXPECT_FALSE(cache_action_in_progress_);
- EXPECT_FALSE(network_action_in_progress_);
- }
- virtual void OnCacheActionStart() OVERRIDE {
- num_callbacks_observed_++;
- EXPECT_FALSE(cache_action_in_progress_);
- EXPECT_FALSE(network_action_in_progress_);
- EXPECT_GT(num_remaining_cache_actions_to_observe_, 0);
- num_remaining_cache_actions_to_observe_--;
- cache_action_in_progress_ = true;
- }
- virtual void OnCacheActionFinish() OVERRIDE {
- num_callbacks_observed_++;
- EXPECT_TRUE(cache_action_in_progress_);
- cache_action_in_progress_ = false;
- }
- virtual void OnNetworkActionStart() OVERRIDE {
- num_callbacks_observed_++;
- EXPECT_FALSE(cache_action_in_progress_);
- EXPECT_FALSE(network_action_in_progress_);
- EXPECT_GT(num_remaining_network_actions_to_observe_, 0);
- num_remaining_network_actions_to_observe_--;
- network_action_in_progress_ = true;
- }
- virtual void OnNetworkActionFinish() OVERRIDE {
- num_callbacks_observed_++;
- EXPECT_TRUE(network_action_in_progress_);
- network_action_in_progress_ = false;
- }
-
- int num_callbacks_observed() { return num_callbacks_observed_; }
-
- private:
- int num_callbacks_observed_;
- int num_remaining_cache_actions_to_observe_;
- int num_remaining_network_actions_to_observe_;
- bool cache_action_in_progress_;
- bool network_action_in_progress_;
-};
-
void ReadAndVerifyTransaction(net::HttpTransaction* trans,
const MockTransaction& trans_info) {
std::string content;
@@ -172,31 +119,19 @@ void ReadAndVerifyTransaction(net::HttpTransaction* trans,
EXPECT_EQ(expected, content);
}
-const int kNoDelegateTransactionCheck = -1;
-
-void RunTransactionTestWithRequestAndDelegateAndGetTiming(
- net::HttpCache* cache,
- const MockTransaction& trans_info,
- const MockHttpRequest& request,
- net::HttpResponseInfo* response_info,
- int num_cache_delegate_actions,
- int num_network_delegate_actions,
- const net::BoundNetLog& net_log,
- net::LoadTimingInfo* load_timing_info) {
+void RunTransactionTestBase(net::HttpCache* cache,
+ const MockTransaction& trans_info,
+ const MockHttpRequest& request,
+ net::HttpResponseInfo* response_info,
+ const net::BoundNetLog& net_log,
+ net::LoadTimingInfo* load_timing_info,
+ int64* received_bytes) {
net::TestCompletionCallback callback;
// write to the cache
- scoped_ptr<TestHttpTransactionDelegate> delegate;
- if (num_cache_delegate_actions != kNoDelegateTransactionCheck &&
- num_network_delegate_actions != kNoDelegateTransactionCheck) {
- delegate.reset(
- new TestHttpTransactionDelegate(num_cache_delegate_actions,
- num_network_delegate_actions));
- }
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, delegate.get());
+ int rv = cache->CreateTransaction(net::DEFAULT_PRIORITY, &trans);
EXPECT_EQ(net::OK, rv);
ASSERT_TRUE(trans.get());
@@ -223,47 +158,25 @@ void RunTransactionTestWithRequestAndDelegateAndGetTiming(
}
ReadAndVerifyTransaction(trans.get(), trans_info);
-}
-void RunTransactionTestWithRequestAndDelegate(
- net::HttpCache* cache,
- const MockTransaction& trans_info,
- const MockHttpRequest& request,
- net::HttpResponseInfo* response_info,
- int num_cache_delegate_actions,
- int num_network_delegate_actions) {
- RunTransactionTestWithRequestAndDelegateAndGetTiming(
- cache, trans_info, request, response_info, num_cache_delegate_actions,
- num_network_delegate_actions, net::BoundNetLog(), NULL);
+ if (received_bytes)
+ *received_bytes = trans->GetTotalReceivedBytes();
}
void RunTransactionTestWithRequest(net::HttpCache* cache,
const MockTransaction& trans_info,
const MockHttpRequest& request,
net::HttpResponseInfo* response_info) {
- RunTransactionTestWithRequestAndDelegate(
- cache, trans_info, request, response_info, kNoDelegateTransactionCheck,
- kNoDelegateTransactionCheck);
+ RunTransactionTestBase(cache, trans_info, request, response_info,
+ net::BoundNetLog(), NULL, NULL);
}
-void RunTransactionTestAndGetTiming(
- net::HttpCache* cache,
- const MockTransaction& trans_info,
- const net::BoundNetLog& log,
- net::LoadTimingInfo* load_timing_info) {
- RunTransactionTestWithRequestAndDelegateAndGetTiming(
- cache, trans_info, MockHttpRequest(trans_info), NULL,
- kNoDelegateTransactionCheck, kNoDelegateTransactionCheck, log,
- load_timing_info);
-}
-
-void RunTransactionTestWithDelegate(net::HttpCache* cache,
+void RunTransactionTestAndGetTiming(net::HttpCache* cache,
const MockTransaction& trans_info,
- int num_cache_delegate_actions,
- int num_network_delegate_actions) {
- RunTransactionTestWithRequestAndDelegate(
- cache, trans_info, MockHttpRequest(trans_info), NULL,
- num_cache_delegate_actions, num_network_delegate_actions);
+ const net::BoundNetLog& log,
+ net::LoadTimingInfo* load_timing_info) {
+ RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info),
+ NULL, log, load_timing_info, NULL);
}
void RunTransactionTest(net::HttpCache* cache,
@@ -274,8 +187,8 @@ void RunTransactionTest(net::HttpCache* cache,
void RunTransactionTestWithResponseInfo(net::HttpCache* cache,
const MockTransaction& trans_info,
net::HttpResponseInfo* response) {
- RunTransactionTestWithRequest(
- cache, trans_info, MockHttpRequest(trans_info), response);
+ RunTransactionTestWithRequest(cache, trans_info, MockHttpRequest(trans_info),
+ response);
}
void RunTransactionTestWithResponseInfoAndGetTiming(
@@ -284,10 +197,8 @@ void RunTransactionTestWithResponseInfoAndGetTiming(
net::HttpResponseInfo* response,
const net::BoundNetLog& log,
net::LoadTimingInfo* load_timing_info) {
- RunTransactionTestWithRequestAndDelegateAndGetTiming(
- cache, trans_info, MockHttpRequest(trans_info), response,
- kNoDelegateTransactionCheck, kNoDelegateTransactionCheck, log,
- load_timing_info);
+ RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info),
+ response, log, load_timing_info, NULL);
}
void RunTransactionTestWithResponse(net::HttpCache* cache,
@@ -305,10 +216,8 @@ void RunTransactionTestWithResponseAndGetTiming(
const net::BoundNetLog& log,
net::LoadTimingInfo* load_timing_info) {
net::HttpResponseInfo response;
- RunTransactionTestWithRequestAndDelegateAndGetTiming(
- cache, trans_info, MockHttpRequest(trans_info), &response,
- kNoDelegateTransactionCheck, kNoDelegateTransactionCheck,
- log, load_timing_info);
+ RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info),
+ &response, log, load_timing_info, NULL);
response.headers->GetNormalizedHeaders(response_headers);
}
@@ -418,6 +327,13 @@ void RangeTransactionServer::RangeHandler(const net::HttpRequestInfo* request,
// We want to make sure we don't delete extra headers.
EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
+ if (request->extra_headers.HasHeader("X-Require-Mock-Auth") &&
+ !request->extra_headers.HasHeader("Authorization")) {
+ response_status->assign("HTTP/1.1 401 Unauthorized");
+ response_data->assign("WWW-Authenticate: Foo\n");
+ return;
+ }
+
if (not_modified_) {
response_status->assign("HTTP/1.1 304 Not Modified");
response_data->clear();
@@ -591,6 +507,31 @@ class FakeWebSocketHandshakeStreamCreateHelper
}
};
+// Returns true if |entry| is not one of the log types paid attention to in this
+// test. Note that TYPE_HTTP_CACHE_WRITE_INFO and TYPE_HTTP_CACHE_*_DATA are
+// ignored.
+bool ShouldIgnoreLogEntry(const net::CapturingNetLog::CapturedEntry& entry) {
+ switch (entry.type) {
+ case net::NetLog::TYPE_HTTP_CACHE_GET_BACKEND:
+ case net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY:
+ case net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY:
+ case net::NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY:
+ case net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY:
+ case net::NetLog::TYPE_HTTP_CACHE_READ_INFO:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// Modifies |entries| to only include log entries created by the cache layer and
+// asserted on in these tests.
+void FilterLogEntries(net::CapturingNetLog::CapturedEntryList* entries) {
+ entries->erase(std::remove_if(entries->begin(), entries->end(),
+ &ShouldIgnoreLogEntry),
+ entries->end());
+}
+
} // namespace
@@ -601,9 +542,7 @@ TEST(HttpCache, CreateThenDestroy) {
MockHttpCache cache;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ EXPECT_EQ(net::OK, cache.CreateTransaction(&trans));
ASSERT_TRUE(trans.get());
}
@@ -638,7 +577,6 @@ TEST(HttpCache, SimpleGETNoDiskCache) {
cache.disk_cache()->set_fail_requests();
net::CapturingBoundNetLog log;
- log.SetLogLevel(net::NetLog::LOG_BASIC);
net::LoadTimingInfo load_timing_info;
// Read from the network, and don't use the cache.
@@ -649,6 +587,7 @@ TEST(HttpCache, SimpleGETNoDiskCache) {
// (We attempted to both Open and Create entries, but both failed).
net::CapturingNetLog::CapturedEntryList entries;
log.GetEntries(&entries);
+ FilterLogEntries(&entries);
EXPECT_EQ(6u, entries.size());
EXPECT_TRUE(net::LogContainsBeginEvent(
@@ -693,15 +632,13 @@ TEST(HttpCache, ReleaseBuffer) {
MockHttpRequest request(kSimpleGET_Transaction);
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- ASSERT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
const int kBufferSize = 10;
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
net::ReleaseBufferCompletionCallback cb(buffer.get());
- rv = trans->Start(&request, cb.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, cb.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, cb.GetResult(rv));
rv = trans->Read(buffer.get(), kBufferSize, cb.callback());
@@ -736,9 +673,8 @@ TEST(HttpCache, SimpleGETWithDiskFailures2) {
MockHttpRequest request(kSimpleGET_Transaction);
scoped_ptr<Context> c(new Context());
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::ERR_IO_PENDING, rv);
@@ -782,9 +718,8 @@ TEST(HttpCache, SimpleGETWithDiskFailures3) {
// Now fail to read from the cache.
scoped_ptr<Context> c(new Context());
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
MockHttpRequest request(kSimpleGET_Transaction);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
@@ -808,10 +743,6 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
MockHttpCache cache;
net::CapturingBoundNetLog log;
-
- // This prevents a number of write events from being logged.
- log.SetLogLevel(net::NetLog::LOG_BASIC);
-
net::LoadTimingInfo load_timing_info;
// Write to the cache.
@@ -821,6 +752,7 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
// Check that the NetLog was filled as expected.
net::CapturingNetLog::CapturedEntryList entries;
log.GetEntries(&entries);
+ FilterLogEntries(&entries);
EXPECT_EQ(8u, entries.size());
EXPECT_TRUE(net::LogContainsBeginEvent(
@@ -853,6 +785,7 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
// Check that the NetLog was filled as expected.
log.GetEntries(&entries);
+ FilterLogEntries(&entries);
EXPECT_EQ(8u, entries.size());
EXPECT_TRUE(net::LogContainsBeginEvent(
@@ -889,12 +822,9 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Miss) {
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
rv = callback.WaitForResult();
ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -1042,11 +972,8 @@ TEST(HttpCache, SimpleGET_CacheOverride_Offline) {
MockHttpRequest request(transaction);
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- ASSERT_TRUE(trans.get());
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
const net::HttpResponseInfo* response_info = trans->GetResponseInfo();
@@ -1090,6 +1017,42 @@ TEST(HttpCache, SimpleGET_CacheOverride_NonOffline) {
RemoveMockTransaction(&transaction);
}
+// Tests that was_cached was set properly on a failure, even if the cached
+// response wasn't returned.
+TEST(HttpCache, SimpleGET_CacheSignal_Failure) {
+ MockHttpCache cache;
+
+ // Prime cache.
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.response_headers = "Cache-Control: no-cache\n";
+
+ AddMockTransaction(&transaction);
+ RunTransactionTest(cache.http_cache(), transaction);
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ RemoveMockTransaction(&transaction);
+
+ // Network failure with error; should fail but have was_cached set.
+ transaction.return_code = net::ERR_FAILED;
+ AddMockTransaction(&transaction);
+
+ MockHttpRequest request(transaction);
+ net::TestCompletionCallback callback;
+ scoped_ptr<net::HttpTransaction> trans;
+ int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, &trans);
+ EXPECT_EQ(net::OK, rv);
+ ASSERT_TRUE(trans.get());
+ rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ EXPECT_EQ(net::ERR_FAILED, callback.GetResult(rv));
+
+ const net::HttpResponseInfo* response_info = trans->GetResponseInfo();
+ ASSERT_TRUE(response_info);
+ EXPECT_TRUE(response_info->was_cached);
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+
+ RemoveMockTransaction(&transaction);
+}
+
// Confirm if we have an empty cache, a read is marked as network verified.
TEST(HttpCache, SimpleGET_NetworkAccessed_Network) {
MockHttpCache cache;
@@ -1138,9 +1101,6 @@ TEST(HttpCache, SimpleGET_LoadBypassCache) {
transaction.load_flags |= net::LOAD_BYPASS_CACHE;
net::CapturingBoundNetLog log;
-
- // This prevents a number of write events from being logged.
- log.SetLogLevel(net::NetLog::LOG_BASIC);
net::LoadTimingInfo load_timing_info;
// Write to the cache.
@@ -1150,6 +1110,7 @@ TEST(HttpCache, SimpleGET_LoadBypassCache) {
// Check that the NetLog was filled as expected.
net::CapturingNetLog::CapturedEntryList entries;
log.GetEntries(&entries);
+ FilterLogEntries(&entries);
EXPECT_EQ(8u, entries.size());
EXPECT_TRUE(net::LogContainsBeginEvent(
@@ -1317,9 +1278,8 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
EXPECT_EQ(net::LOAD_STATE_IDLE, c->trans->GetLoadState());
c->result = c->trans->Start(
@@ -1386,9 +1346,8 @@ TEST(HttpCache, SimpleGET_RacingReaders) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
MockHttpRequest* this_request = &request;
if (i == 1 || i == 2)
@@ -1472,9 +1431,8 @@ TEST(HttpCache, SimpleGET_DoomWithPending) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
MockHttpRequest* this_request = &request;
if (i == 3)
@@ -1521,9 +1479,8 @@ TEST(HttpCache, FastNoStoreGET_DoneWithPending) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1569,9 +1526,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1630,9 +1586,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelCreate) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1682,9 +1637,8 @@ TEST(HttpCache, SimpleGET_CancelCreate) {
Context* c = new Context();
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1713,9 +1667,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_BypassCache) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1756,10 +1709,8 @@ TEST(HttpCache, SimpleGET_AbandonedCacheRead) {
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
rv = callback.WaitForResult();
ASSERT_EQ(net::OK, rv);
@@ -1792,9 +1743,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_DeleteCache) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache->http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache->CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->result = c->trans->Start(
&request, c->callback.callback(), net::BoundNetLog());
@@ -1832,9 +1782,8 @@ TEST(HttpCache, SimpleGET_WaitForBackend) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
}
context_list[0]->result = context_list[0]->trans->Start(
@@ -1879,9 +1828,8 @@ TEST(HttpCache, SimpleGET_WaitForBackend_CancelCreate) {
context_list.push_back(new Context());
Context* c = context_list[i];
- c->result = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
}
context_list[0]->result = context_list[0]->trans->Start(
@@ -1926,9 +1874,8 @@ TEST(HttpCache, DeleteCacheWaitingForBackend) {
MockHttpRequest request(kSimpleGET_Transaction);
scoped_ptr<Context> c(new Context());
- c->result = cache->http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache->CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
@@ -1965,9 +1912,8 @@ TEST(HttpCache, DeleteCacheWaitingForBackend2) {
MockHttpRequest request(kSimpleGET_Transaction);
scoped_ptr<Context> c(new Context());
- c->result = cache->http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, c->result);
+ c->result = cache->CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, c->result);
c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
@@ -2776,12 +2722,10 @@ TEST(HttpCache, SimplePOST_LoadOnlyFromCache_Miss) {
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
ASSERT_TRUE(trans.get());
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
ASSERT_EQ(net::ERR_CACHE_MISS, callback.GetResult(rv));
trans.reset();
@@ -3383,6 +3327,23 @@ TEST(HttpCache, GET_Crazy206) {
RemoveMockTransaction(&transaction);
}
+// Tests that receiving 416 for a regular request is handled correctly.
+TEST(HttpCache, GET_Crazy416) {
+ MockHttpCache cache;
+
+ // Write to the cache.
+ MockTransaction transaction(kSimpleGET_Transaction);
+ AddMockTransaction(&transaction);
+ transaction.status = "HTTP/1.1 416 Requested Range Not Satisfiable";
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ RemoveMockTransaction(&transaction);
+}
+
// Tests that we don't cache partial responses that can't be validated.
TEST(HttpCache, RangeGET_NoStrongValidators) {
MockHttpCache cache;
@@ -4327,9 +4288,8 @@ TEST(HttpCache, RangeGET_Cancel) {
MockHttpRequest request(kRangeGET_TransactionOK);
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4367,9 +4327,8 @@ TEST(HttpCache, RangeGET_Cancel2) {
request.load_flags |= net::LOAD_VALIDATE_CACHE;
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4413,9 +4372,8 @@ TEST(HttpCache, RangeGET_Cancel3) {
request.load_flags |= net::LOAD_VALIDATE_CACHE;
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::ERR_IO_PENDING, rv);
@@ -4441,9 +4399,8 @@ TEST(HttpCache, RangeGET_Cancel3) {
// active entry (no open or create).
c = new Context();
- rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::ERR_IO_PENDING, rv);
@@ -4723,8 +4680,7 @@ TEST(HttpCache, RangeGET_OK_LoadOnlyFromCache) {
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
+ int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, &trans);
EXPECT_EQ(net::OK, rv);
ASSERT_TRUE(trans.get());
@@ -4802,9 +4758,8 @@ TEST(HttpCache, DoomOnDestruction) {
MockHttpRequest request(kSimpleGET_Transaction);
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4833,9 +4788,8 @@ TEST(HttpCache, DoomOnDestruction2) {
MockHttpRequest request(kSimpleGET_Transaction);
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4877,9 +4831,8 @@ TEST(HttpCache, DoomOnDestruction3) {
MockHttpRequest request(transaction);
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4921,15 +4874,9 @@ TEST(HttpCache, SetTruncatedFlag) {
MockHttpRequest request(transaction);
scoped_ptr<Context> c(new Context());
- // We use a test delegate to ensure that after initiating destruction
- // of the transaction, no further delegate callbacks happen.
- // We initialize the TestHttpTransactionDelegate with the correct number of
- // cache actions and network actions to be reported.
- scoped_ptr<TestHttpTransactionDelegate> delegate(
- new TestHttpTransactionDelegate(7, 3));
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, delegate.get());
- EXPECT_EQ(net::OK, rv);
+
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
@@ -4952,21 +4899,11 @@ TEST(HttpCache, SetTruncatedFlag) {
EXPECT_FALSE(c->callback.have_result());
MockHttpCache::SetTestMode(TEST_MODE_SYNC_ALL);
- int num_delegate_callbacks_before_destruction =
- delegate->num_callbacks_observed();
// Destroy the transaction.
c->trans.reset();
MockHttpCache::SetTestMode(0);
- // Ensure the delegate received no callbacks during destruction.
- EXPECT_EQ(num_delegate_callbacks_before_destruction,
- delegate->num_callbacks_observed());
-
- // Since the transaction was aborted in the middle of network I/O, we will
- // manually call the delegate so that its pending I/O operation will be
- // closed (which is what the test delegate is expecting).
- delegate->OnNetworkActionFinish();
// Make sure that we don't invoke the callback. We may have an issue if the
// UrlRequestJob is killed directly (without cancelling the UrlRequest) so we
@@ -4999,9 +4936,8 @@ TEST(HttpCache, DontSetTruncatedFlag) {
MockHttpRequest request(transaction);
scoped_ptr<Context> c(new Context());
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, c->callback.GetResult(rv));
@@ -5144,18 +5080,15 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) {
MockHttpRequest request(transaction);
Context* c = new Context();
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
// Queue another request to this transaction. We have to start this request
// before the first one gets the response from the server and dooms the entry,
// otherwise it will just create a new entry without being queued to the first
// request.
Context* pending = new Context();
- EXPECT_EQ(net::OK,
- cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &pending->trans, NULL));
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&pending->trans));
rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::ERR_IO_PENDING,
@@ -5239,12 +5172,11 @@ TEST(HttpCache, GET_IncompleteResource3) {
"rg: 50-59 rg: 60-69 rg: 70-79 ";
scoped_ptr<Context> c(new Context);
- EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL));
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
MockHttpRequest request(transaction);
- int rv = c->trans->Start(
- &request, c->callback.callback(), net::BoundNetLog());
+ rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, c->callback.GetResult(rv));
// We should have checked with the server before finishing Start().
@@ -5255,6 +5187,56 @@ TEST(HttpCache, GET_IncompleteResource3) {
RemoveMockTransaction(&kRangeGET_TransactionOK);
}
+// Tests that we handle 401s for truncated resources.
+TEST(HttpCache, GET_IncompleteResourceWithAuth) {
+ MockHttpCache cache;
+ AddMockTransaction(&kRangeGET_TransactionOK);
+
+ std::string raw_headers("HTTP/1.1 200 OK\n"
+ "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
+ "ETag: \"foo\"\n"
+ "Accept-Ranges: bytes\n"
+ "Content-Length: 80\n");
+ CreateTruncatedEntry(raw_headers, &cache);
+
+ // Now make a regular request.
+ MockTransaction transaction(kRangeGET_TransactionOK);
+ transaction.request_headers = "X-Require-Mock-Auth: dummy\r\n"
+ EXTRA_HEADER;
+ transaction.data = "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 rg: 40-49 "
+ "rg: 50-59 rg: 60-69 rg: 70-79 ";
+ RangeTransactionServer handler;
+
+ scoped_ptr<Context> c(new Context);
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
+
+ MockHttpRequest request(transaction);
+ rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
+ EXPECT_EQ(net::OK, c->callback.GetResult(rv));
+
+ const net::HttpResponseInfo* response = c->trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(401, response->headers->response_code());
+ rv = c->trans->RestartWithAuth(net::AuthCredentials(),
+ c->callback.callback());
+ EXPECT_EQ(net::OK, c->callback.GetResult(rv));
+ response = c->trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(200, response->headers->response_code());
+
+ ReadAndVerifyTransaction(c->trans.get(), transaction);
+ c.reset(); // The destructor could delete the entry.
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+
+ // Verify that the entry was not deleted.
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+ entry->Close();
+
+ RemoveMockTransaction(&kRangeGET_TransactionOK);
+}
+
// Tests that we cache a 200 response to the validation request.
TEST(HttpCache, GET_IncompleteResource4) {
MockHttpCache cache;
@@ -5312,11 +5294,10 @@ TEST(HttpCache, GET_CancelIncompleteResource) {
MockHttpRequest request(transaction);
Context* c = new Context();
- EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &c->trans, NULL));
+ int rv = cache.CreateTransaction(&c->trans);
+ ASSERT_EQ(net::OK, rv);
- int rv = c->trans->Start(
- &request, c->callback.callback(), net::BoundNetLog());
+ rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, c->callback.GetResult(rv));
// Read 20 bytes from the cache, and 10 from the net.
@@ -5441,12 +5422,9 @@ TEST(HttpCache, CachedRedirect) {
// Write to the cache.
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
rv = callback.WaitForResult();
ASSERT_EQ(net::OK, rv);
@@ -5478,12 +5456,9 @@ TEST(HttpCache, CachedRedirect) {
// Read from the cache.
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
rv = callback.WaitForResult();
ASSERT_EQ(net::OK, rv);
@@ -5662,12 +5637,9 @@ TEST(HttpCache, SimpleGET_SSLError) {
net::TestCompletionCallback callback;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
if (rv == net::ERR_IO_PENDING)
rv = callback.WaitForResult();
ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -5678,9 +5650,7 @@ TEST(HttpCache, OutlivedTransactions) {
MockHttpCache* cache = new MockHttpCache;
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache->http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ EXPECT_EQ(net::OK, cache->CreateTransaction(&trans));
delete cache;
trans.reset();
@@ -5927,12 +5897,10 @@ TEST(HttpCache, FilterCompletion) {
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
MockHttpRequest request(kSimpleGET_Transaction);
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256));
@@ -5965,12 +5933,10 @@ TEST(HttpCache, DoneReading) {
transaction.data = "";
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
MockHttpRequest request(transaction);
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
trans->DoneReading();
@@ -5995,11 +5961,9 @@ TEST(HttpCache, StopCachingDeletesEntry) {
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256));
@@ -6035,11 +5999,9 @@ TEST(HttpCache, StopCachingThenDoneReadingDeletesEntry) {
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256));
@@ -6080,11 +6042,9 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) {
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
trans->StopCaching();
@@ -6114,9 +6074,7 @@ TEST(HttpCache, StopCachingSavesEntry) {
{
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
// Force a response that can be resumed.
MockTransaction mock_transaction(kSimpleGET_Transaction);
@@ -6125,7 +6083,7 @@ TEST(HttpCache, StopCachingSavesEntry) {
"Content-Length: 42\n"
"Etag: \"foo\"\n";
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256));
@@ -6172,11 +6130,9 @@ TEST(HttpCache, StopCachingTruncatedEntry) {
{
// Now make a regular request.
scoped_ptr<net::HttpTransaction> trans;
- int rv = cache.http_cache()->CreateTransaction(
- net::DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(net::OK, rv);
+ ASSERT_EQ(net::OK, cache.CreateTransaction(&trans));
- rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ int rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
EXPECT_EQ(net::OK, callback.GetResult(rv));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256));
@@ -6253,34 +6209,13 @@ TEST(HttpCache, TruncatedByContentLength2) {
entry->Close();
}
-TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit_TransactionDelegate) {
- MockHttpCache cache;
-
- // Write to the cache.
- RunTransactionTestWithDelegate(cache.http_cache(),
- kSimpleGET_Transaction,
- 8,
- 3);
-
- // Force this transaction to read from the cache.
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.load_flags |= net::LOAD_ONLY_FROM_CACHE;
-
- RunTransactionTestWithDelegate(cache.http_cache(),
- kSimpleGET_Transaction,
- 5,
- 0);
-}
-
// Make sure that calling SetPriority on a cache transaction passes on
// its priority updates to its underlying network transaction.
TEST(HttpCache, SetPriority) {
MockHttpCache cache;
scoped_ptr<net::HttpTransaction> trans;
- EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(
- net::IDLE, &trans, NULL));
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.http_cache()->CreateTransaction(net::IDLE, &trans));
// Shouldn't crash, but doesn't do anything either.
trans->SetPriority(net::LOW);
@@ -6322,9 +6257,7 @@ TEST(HttpCache, SetWebSocketHandshakeStreamCreateHelper) {
FakeWebSocketHandshakeStreamCreateHelper create_helper;
scoped_ptr<net::HttpTransaction> trans;
- EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(
- net::IDLE, &trans, NULL));
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK, cache.http_cache()->CreateTransaction(net::IDLE, &trans));
EXPECT_FALSE(cache.network_layer()->last_transaction());
@@ -6365,9 +6298,8 @@ TEST(HttpCache, SetPriorityNewTransaction) {
"rg: 50-59 rg: 60-69 rg: 70-79 ";
scoped_ptr<net::HttpTransaction> trans;
- EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(
- net::MEDIUM, &trans, NULL));
- ASSERT_TRUE(trans.get());
+ ASSERT_EQ(net::OK,
+ cache.http_cache()->CreateTransaction(net::MEDIUM, &trans));
EXPECT_EQ(net::DEFAULT_PRIORITY,
cache.network_layer()->last_create_transaction_priority());
@@ -6390,3 +6322,97 @@ TEST(HttpCache, SetPriorityNewTransaction) {
RemoveMockTransaction(&kRangeGET_TransactionOK);
}
+
+int64 RunTransactionAndGetReceivedBytes(
+ MockHttpCache& cache,
+ const MockTransaction& trans_info) {
+ int64 received_bytes = -1;
+ RunTransactionTestBase(cache.http_cache(), trans_info,
+ MockHttpRequest(trans_info), NULL, net::BoundNetLog(),
+ NULL, &received_bytes);
+ return received_bytes;
+}
+
+int64 TransactionSize(const MockTransaction& transaction) {
+ return strlen(transaction.status) + strlen(transaction.response_headers) +
+ strlen(transaction.data);
+}
+
+TEST(HttpCache, ReceivedBytesCacheMissAndThenHit) {
+ MockHttpCache cache;
+
+ MockTransaction transaction(kSimpleGET_Transaction);
+ int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(TransactionSize(transaction), received_bytes);
+
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(0, received_bytes);
+}
+
+TEST(HttpCache, ReceivedBytesConditionalRequest304) {
+ MockHttpCache cache;
+
+ ScopedMockTransaction transaction(kETagGET_Transaction);
+ int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(TransactionSize(transaction), received_bytes);
+
+ transaction.load_flags = net::LOAD_VALIDATE_CACHE;
+ transaction.handler = ETagGet_ConditionalRequest_Handler;
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(TransactionSize(transaction), received_bytes);
+}
+
+TEST(HttpCache, ReceivedBytesConditionalRequest200) {
+ MockHttpCache cache;
+
+ MockTransaction transaction(kTypicalGET_Transaction);
+ transaction.request_headers = "Foo: bar\r\n";
+ transaction.response_headers =
+ "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
+ "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n"
+ "Etag: \"foopy\"\n"
+ "Cache-Control: max-age=0\n"
+ "Vary: Foo\n";
+ AddMockTransaction(&transaction);
+ int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(TransactionSize(transaction), received_bytes);
+
+ RevalidationServer server;
+ transaction.handler = server.Handler;
+ transaction.request_headers = "Foo: none\r\n";
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(TransactionSize(transaction), received_bytes);
+
+ RemoveMockTransaction(&transaction);
+}
+
+TEST(HttpCache, ReceivedBytesRange) {
+ MockHttpCache cache;
+ AddMockTransaction(&kRangeGET_TransactionOK);
+ MockTransaction transaction(kRangeGET_TransactionOK);
+
+ // Read bytes 40-49 from the network.
+ int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ int64 range_response_size = TransactionSize(transaction);
+ EXPECT_EQ(range_response_size, received_bytes);
+
+ // Read bytes 40-49 from the cache.
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(0, received_bytes);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Read bytes 30-39 from the network.
+ transaction.request_headers = "Range: bytes = 30-39\r\n" EXTRA_HEADER;
+ transaction.data = "rg: 30-39 ";
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(range_response_size, received_bytes);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Read bytes 20-29 and 50-59 from the network, bytes 30-49 from the cache.
+ transaction.request_headers = "Range: bytes = 20-59\r\n" EXTRA_HEADER;
+ transaction.data = "rg: 20-29 rg: 30-39 rg: 40-49 rg: 50-59 ";
+ received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction);
+ EXPECT_EQ(range_response_size * 2, received_bytes);
+
+ RemoveMockTransaction(&kRangeGET_TransactionOK);
+}
diff --git a/chromium/net/http/http_chunked_decoder.h b/chromium/net/http/http_chunked_decoder.h
index 9ee7400ad7d..23076f9b368 100644
--- a/chromium/net/http/http_chunked_decoder.h
+++ b/chromium/net/http/http_chunked_decoder.h
@@ -122,8 +122,7 @@ class NET_EXPORT_PRIVATE HttpChunkedDecoder {
// Set to true when FilterBuf encounters the final CRLF.
bool reached_eof_;
- // The number of unfiltered bytes after the final CRLF, either extraneous
- // data or the first part of the next response in a pipelined stream.
+ // The number of extraneous unfiltered bytes after the final CRLF.
int bytes_after_eof_;
};
diff --git a/chromium/net/http/http_content_disposition.cc b/chromium/net/http/http_content_disposition.cc
index 3dbf234b943..3a1dedeb788 100644
--- a/chromium/net/http/http_content_disposition.cc
+++ b/chromium/net/http/http_content_disposition.cc
@@ -5,15 +5,14 @@
#include "net/http/http_content_disposition.h"
#include "base/base64.h"
-#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "net/base/net_string_util.h"
#include "net/base/net_util.h"
#include "net/http/http_util.h"
-#include "third_party/icu/source/common/unicode/ucnv.h"
namespace {
@@ -65,32 +64,16 @@ bool DecodeBQEncoding(const std::string& part,
std::string* output) {
std::string decoded;
if (!((enc_type == B_ENCODING) ?
- base::Base64Decode(part, &decoded) : DecodeQEncoding(part, &decoded)))
+ base::Base64Decode(part, &decoded) : DecodeQEncoding(part, &decoded))) {
return false;
+ }
if (decoded.empty()) {
output->clear();
return true;
}
- UErrorCode err = U_ZERO_ERROR;
- UConverter* converter(ucnv_open(charset.c_str(), &err));
- if (U_FAILURE(err))
- return false;
-
- // A single byte in a legacy encoding can be expanded to 3 bytes in UTF-8.
- // A 'two-byte character' in a legacy encoding can be expanded to 4 bytes
- // in UTF-8. Therefore, the expansion ratio is 3 at most. Add one for a
- // trailing '\0'.
- size_t output_length = decoded.length() * 3 + 1;
- char* buf = WriteInto(output, output_length);
- output_length = ucnv_toAlgorithmic(UCNV_UTF8, converter, buf, output_length,
- decoded.data(), decoded.length(), &err);
- ucnv_close(converter);
- if (U_FAILURE(err))
- return false;
- output->resize(output_length);
- return true;
+ return net::ConvertToUtf8(decoded, charset.c_str(), output);
}
bool DecodeWord(const std::string& encoded_word,
@@ -103,19 +86,18 @@ bool DecodeWord(const std::string& encoded_word,
if (encoded_word.empty())
return true;
- if (!IsStringASCII(encoded_word)) {
+ if (!base::IsStringASCII(encoded_word)) {
// Try UTF-8, referrer_charset and the native OS default charset in turn.
- if (IsStringUTF8(encoded_word)) {
+ if (base::IsStringUTF8(encoded_word)) {
*output = encoded_word;
} else {
base::string16 utf16_output;
if (!referrer_charset.empty() &&
- base::CodepageToUTF16(encoded_word, referrer_charset.c_str(),
- base::OnStringConversionError::FAIL,
- &utf16_output)) {
- *output = UTF16ToUTF8(utf16_output);
+ net::ConvertToUTF16(encoded_word, referrer_charset.c_str(),
+ &utf16_output)) {
+ *output = base::UTF16ToUTF8(utf16_output);
} else {
- *output = WideToUTF8(base::SysNativeMBToWide(encoded_word));
+ *output = base::WideToUTF8(base::SysNativeMBToWide(encoded_word));
}
}
@@ -209,7 +191,7 @@ bool DecodeWord(const std::string& encoded_word,
if (decoded_word != encoded_word)
*parse_result_flags |=
net::HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS;
- if (IsStringUTF8(decoded_word)) {
+ if (base::IsStringUTF8(decoded_word)) {
output->swap(decoded_word);
return true;
// We can try either the OS default charset or 'origin charset' here,
@@ -335,7 +317,7 @@ bool DecodeExtValue(const std::string& param_value, std::string* decoded) {
return false;
// RFC 5987 value should be ASCII-only.
- if (!IsStringASCII(value)) {
+ if (!base::IsStringASCII(value)) {
decoded->clear();
return true;
}
@@ -343,7 +325,7 @@ bool DecodeExtValue(const std::string& param_value, std::string* decoded) {
std::string unescaped = net::UnescapeURLComponent(
value, net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS);
- return base::ConvertToUtf8AndNormalize(unescaped, charset, decoded);
+ return net::ConvertToUtf8AndNormalize(unescaped, charset.c_str(), decoded);
}
} // namespace
diff --git a/chromium/net/http/http_content_disposition_unittest.cc b/chromium/net/http/http_content_disposition_unittest.cc
index 62d95778c0b..43fef9dd0ea 100644
--- a/chromium/net/http/http_content_disposition_unittest.cc
+++ b/chromium/net/http/http_content_disposition_unittest.cc
@@ -198,7 +198,7 @@ TEST(HttpContentDispositionTest, Filename) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
HttpContentDisposition header(tests[i].header, tests[i].referrer_charset);
EXPECT_EQ(tests[i].expected,
- UTF8ToWide(header.filename()))
+ base::UTF8ToWide(header.filename()))
<< "Failed on input: " << tests[i].header;
}
}
@@ -507,7 +507,7 @@ TEST(HttpContentDispositionTest, tc2231) {
HttpContentDisposition header(tests[i].header, std::string());
EXPECT_EQ(tests[i].expected_type, header.type())
<< "Failed on input: " << tests[i].header;
- EXPECT_EQ(tests[i].expected_filename, UTF8ToWide(header.filename()))
+ EXPECT_EQ(tests[i].expected_filename, base::UTF8ToWide(header.filename()))
<< "Failed on input: " << tests[i].header;
}
}
diff --git a/chromium/net/http/http_log_util.cc b/chromium/net/http/http_log_util.cc
new file mode 100644
index 00000000000..ab6ebda74ac
--- /dev/null
+++ b/chromium/net/http/http_log_util.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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/http/http_log_util.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
+
+namespace net {
+
+namespace {
+
+bool ShouldRedactChallenge(HttpAuthChallengeTokenizer* challenge) {
+ // Ignore lines with commas, as they may contain lists of schemes, and
+ // the information we want to hide is Base64 encoded, so has no commas.
+ if (challenge->challenge_text().find(',') != std::string::npos)
+ return false;
+
+ std::string scheme = StringToLowerASCII(challenge->scheme());
+ // Invalid input.
+ if (scheme.empty())
+ return false;
+
+ // Ignore Basic and Digest authentication challenges, as they contain
+ // public information.
+ if (scheme == "basic" || scheme == "digest")
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+std::string ElideHeaderValueForNetLog(NetLog::LogLevel log_level,
+ const std::string& header,
+ const std::string& value) {
+#if defined(SPDY_PROXY_AUTH_ORIGIN)
+ if (!base::strcasecmp(header.c_str(), "proxy-authorization") ||
+ !base::strcasecmp(header.c_str(), "proxy-authenticate")) {
+ return "[elided]";
+ }
+#endif
+
+ if (log_level < NetLog::LOG_STRIP_PRIVATE_DATA)
+ return value;
+
+ // Note: this logic should be kept in sync with stripCookiesAndLoginInfo in
+ // chrome/browser/resources/net_internals/log_view_painter.js.
+
+ std::string::const_iterator redact_begin = value.begin();
+ std::string::const_iterator redact_end = value.begin();
+ if (!base::strcasecmp(header.c_str(), "set-cookie") ||
+ !base::strcasecmp(header.c_str(), "set-cookie2") ||
+ !base::strcasecmp(header.c_str(), "cookie") ||
+ !base::strcasecmp(header.c_str(), "authorization") ||
+ !base::strcasecmp(header.c_str(), "proxy-authorization")) {
+ redact_begin = value.begin();
+ redact_end = value.end();
+ } else if (!base::strcasecmp(header.c_str(), "www-authenticate") ||
+ !base::strcasecmp(header.c_str(), "proxy-authenticate")) {
+ // Look for authentication information from data received from the server in
+ // multi-round Negotiate authentication.
+ HttpAuthChallengeTokenizer challenge(value.begin(), value.end());
+ if (ShouldRedactChallenge(&challenge)) {
+ redact_begin = challenge.params_begin();
+ redact_end = challenge.params_end();
+ }
+ }
+
+ if (redact_begin == redact_end)
+ return value;
+
+ return std::string(value.begin(), redact_begin) +
+ base::StringPrintf("[%ld bytes were stripped]",
+ static_cast<long>(redact_end - redact_begin)) +
+ std::string(redact_end, value.end());
+}
+
+} // namespace net
diff --git a/chromium/net/http/http_log_util.h b/chromium/net/http/http_log_util.h
new file mode 100644
index 00000000000..f18c6e737b9
--- /dev/null
+++ b/chromium/net/http/http_log_util.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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_HTTP_HTTP_LOG_UTIL_
+#define NET_HTTP_HTTP_LOG_UTIL_
+
+#include <string>
+
+#include "net/base/net_export.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+// Given an HTTP header |header| with value |value|, returns the elided version
+// of the header value at |log_level|.
+NET_EXPORT_PRIVATE std::string ElideHeaderValueForNetLog(
+ NetLog::LogLevel log_level,
+ const std::string& header,
+ const std::string& value);
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_LOG_UTIL_
diff --git a/chromium/net/http/http_log_util_unittest.cc b/chromium/net/http/http_log_util_unittest.cc
new file mode 100644
index 00000000000..1b0e9dbbfa2
--- /dev/null
+++ b/chromium/net/http/http_log_util_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 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/http/http_log_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(HttpLogUtilTest, ElideHeaderValueForNetLog) {
+ // Only elide for appropriate log level.
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Cookie", "name=value"));
+ EXPECT_EQ("name=value", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_ALL_BUT_BYTES, "Cookie", "name=value"));
+
+ // Headers are compared case insensitively.
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "cOoKiE", "name=value"));
+
+ // These headers should be completely elided.
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie", "name=value"));
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie2", "name=value"));
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Authorization", "Basic 1234"));
+#if !defined(SPDY_PROXY_AUTH_ORIGIN)
+ EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA,
+ "Proxy-Authorization", "Basic 1234"));
+#endif
+
+ // Unknown headers should pass through.
+ EXPECT_EQ("value", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Boring", "value"));
+
+ // Basic and Digest auth challenges are public.
+ EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA,
+ "WWW-Authenticate", "Basic realm=test"));
+ EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA,
+ "WWW-Authenticate", "Digest realm=test"));
+#if !defined(SPDY_PROXY_AUTH_ORIGIN)
+ EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA,
+ "Proxy-Authenticate", "Basic realm=test"));
+ EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA,
+ "Proxy-Authenticate", "Digest realm=test"));
+#endif
+
+ // Multi-round mechanisms partially elided.
+ EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234"));
+#if !defined(SPDY_PROXY_AUTH_ORIGIN)
+ EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "Proxy-Authenticate", "NTLM 1234"));
+#endif
+
+ // Leave whitespace intact.
+ EXPECT_EQ("NTLM [4 bytes were stripped] ", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234 "));
+
+ // Extra elisions for SPDY_PROXY_AUTH_ORIGIN.
+#if defined(SPDY_PROXY_AUTH_ORIGIN)
+ EXPECT_EQ("[elided]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_ALL_BUT_BYTES,
+ "Proxy-Authenticate", "Basic realm=test"));
+ EXPECT_EQ("[elided]", ElideHeaderValueForNetLog(
+ net::NetLog::LOG_ALL_BUT_BYTES, "Proxy-Authorization", "Basic 1234"));
+#endif
+}
+
+} // namspace net
diff --git a/chromium/net/http/http_network_layer.cc b/chromium/net/http/http_network_layer.cc
index 7d3f1588006..0704de420de 100644
--- a/chromium/net/http/http_network_layer.cc
+++ b/chromium/net/http/http_network_layer.cc
@@ -60,8 +60,7 @@ void HttpNetworkLayer::ForceAlternateProtocol() {
//-----------------------------------------------------------------------------
int HttpNetworkLayer::CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) {
+ scoped_ptr<HttpTransaction>* trans) {
if (suspended_)
return ERR_NETWORK_IO_SUSPENDED;
diff --git a/chromium/net/http/http_network_layer.h b/chromium/net/http/http_network_layer.h
index c4c41aec43e..fc94d0a7234 100644
--- a/chromium/net/http/http_network_layer.h
+++ b/chromium/net/http/http_network_layer.h
@@ -44,8 +44,7 @@ class NET_EXPORT HttpNetworkLayer
// HttpTransactionFactory methods:
virtual int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) OVERRIDE;
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE;
virtual HttpCache* GetCache() OVERRIDE;
virtual HttpNetworkSession* GetSession() OVERRIDE;
diff --git a/chromium/net/http/http_network_layer_unittest.cc b/chromium/net/http/http_network_layer_unittest.cc
index 98b8d4eaf44..7225001e752 100644
--- a/chromium/net/http/http_network_layer_unittest.cc
+++ b/chromium/net/http/http_network_layer_unittest.cc
@@ -11,7 +11,7 @@
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties_impl.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
@@ -49,18 +49,6 @@ class HttpNetworkLayerTest : public PlatformTest {
factory_.reset(new HttpNetworkLayer(network_session_.get()));
}
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- std::string GetChromeProxy() {
- return HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)).ToString();
- }
-#endif
-
-#if defined(SPDY_PROXY_AUTH_ORIGIN) && defined(DATA_REDUCTION_FALLBACK_HOST)
- std::string GetChromeFallbackProxy() {
- return HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString();
- }
-#endif
-
void ExecuteRequestExpectingContentAndHeader(const std::string& method,
const std::string& content,
const std::string& header,
@@ -73,7 +61,7 @@ class HttpNetworkLayerTest : public PlatformTest {
request_info.load_flags = LOAD_NORMAL;
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
@@ -108,7 +96,7 @@ class HttpNetworkLayerTest : public PlatformTest {
// Simulates a request through a proxy which returns a bypass, which is then
// retried through a second proxy that doesn't bypass.
// Checks that the expected requests were issued, the expected content was
- // recieved, and the first proxy |bad_proxy| was marked as bad.
+ // received, and the first proxy |bad_proxy| was marked as bad.
void TestProxyFallback(const std::string& bad_proxy) {
MockRead data_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"
@@ -278,28 +266,28 @@ class HttpNetworkLayerTest : public PlatformTest {
TEST_F(HttpNetworkLayerTest, CreateAndDestroy) {
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
EXPECT_TRUE(trans.get() != NULL);
}
TEST_F(HttpNetworkLayerTest, Suspend) {
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
trans.reset();
factory_->OnSuspend();
- rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, rv);
ASSERT_TRUE(trans == NULL);
factory_->OnResume();
- rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
}
@@ -329,7 +317,7 @@ TEST_F(HttpNetworkLayerTest, GET) {
request_info.load_flags = LOAD_NORMAL;
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
@@ -342,401 +330,6 @@ TEST_F(HttpNetworkLayerTest, GET) {
EXPECT_EQ("hello world", contents);
}
-// Proxy bypass tests. These tests run through various server-induced
-// proxy bypass scenarios using both PAC file and fixed proxy params.
-// The test scenarios are:
-// - bypass with two proxies configured and the first but not the second
-// is bypassed.
-// - bypass with one proxy configured and an explicit fallback to direct
-// connections
-// - bypass with two proxies configured and both are bypassed
-// - bypass with one proxy configured which is bypassed with no defined
-// fallback
-
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-TEST_F(HttpNetworkLayerTest, ServerTwoProxyBypassPac) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy + "; PROXY good:8080"));
- TestProxyFallback(bad_proxy);
-}
-
-TEST_F(HttpNetworkLayerTest, ServerTwoProxyBypassFixed) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(
- ProxyService::CreateFixed(bad_proxy +", good:8080"));
- TestProxyFallback(bad_proxy);
-}
-
-TEST_F(HttpNetworkLayerTest, BypassAndRetryIdempotentMethods) {
- std::string bad_proxy = GetChromeProxy();
- const struct {
- std::string method;
- std::string content;
- bool expected_to_retry;
- } tests[] = {
- {
- "GET",
- "content",
- true,
- },
- {
- "OPTIONS",
- "content",
- true,
- },
- {
- "HEAD",
- "",
- true,
- },
- {
- "PUT",
- "",
- true,
- },
- {
- "DELETE",
- "content",
- true,
- },
- {
- "TRACE",
- "content",
- true,
- },
- {
- "POST",
- "Bypass message",
- false,
- },
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
- ConfigureTestDependencies(
- ProxyService::CreateFixed(bad_proxy +", good:8080"));
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Chrome-Proxy: bypass=0\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
- TestProxyFallbackByMethodWithMockReads(bad_proxy, "", data_reads,
- arraysize(data_reads),
- tests[i].method,
- tests[i].content,
- tests[i].expected_to_retry, 1u);
- }
-}
-
-TEST_F(HttpNetworkLayerTest, ServerOneProxyWithDirectBypassPac) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy + "; DIRECT"));
- TestProxyFallbackToDirect(bad_proxy);
-}
-
-TEST_F(HttpNetworkLayerTest, ServerOneProxyWithDirectBypassFixed) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(
- ProxyService::CreateFixed(bad_proxy + ", direct://"));
- TestProxyFallbackToDirect(bad_proxy);
-}
-
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
-TEST_F(HttpNetworkLayerTest, ServerTwoProxyDoubleBypassPac) {
- std::string bad_proxy = GetChromeProxy();
- std::string bad_proxy2 =
- HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy + "; PROXY " + bad_proxy2));
- TestProxyFallbackFail(2u, bad_proxy, bad_proxy2);
-}
-
-TEST_F(HttpNetworkLayerTest, ServerTwoProxyDoubleBypassFixed) {
- std::string bad_proxy = GetChromeProxy();
- std::string bad_proxy2 =
- HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString();
- ConfigureTestDependencies(ProxyService::CreateFixed(
- bad_proxy + ", " + bad_proxy2));
- TestProxyFallbackFail(2u, bad_proxy, bad_proxy2);
-}
-#endif
-
-TEST_F(HttpNetworkLayerTest, ServerOneProxyNoDirectBypassPac) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy));
- TestProxyFallbackFail(1u, bad_proxy, "");
-}
-
-TEST_F(HttpNetworkLayerTest, ServerOneProxyNoDirectBypassFixed) {
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixed(bad_proxy));
- TestProxyFallbackFail(1u, bad_proxy, "");
-}
-
-TEST_F(HttpNetworkLayerTest, ServerFallbackOn5xxError) {
- // Verify that "500 Internal Server Error", "502 Bad Gateway", and
- // "503 Service Unavailable" via the data reduction proxy induce proxy
- // fallback to a second proxy, if configured.
-
- // To configure this test, we need to wire up a custom proxy service to use
- // a pair of proxies. We'll induce fallback via the first and return
- // the expected data via the second.
- std::string data_reduction_proxy(
- HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)).ToString());
- std::string pac_string = base::StringPrintf(
- "PROXY %s; PROXY good:8080", data_reduction_proxy.data());
-
- std::string headers[] = {
- "HTTP/1.1 500 Internal Server Error\r\n\r\n",
- "HTTP/1.1 502 Bad Gateway\r\n\r\n",
- "HTTP/1.1 503 Service Unavailable\r\n\r\n"
- };
-
- for (size_t i = 0; i < arraysize(headers); ++i) {
- ConfigureTestDependencies(
- ProxyService::CreateFixedFromPacResult(pac_string));
-
- MockRead data_reads[] = {
- MockRead(headers[i].c_str()),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- MockWrite data_writes[] = {
- MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- };
-
- StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
- data_writes, arraysize(data_writes));
- mock_socket_factory_.AddSocketDataProvider(&data1);
-
- // Second data provider returns the expected content.
- MockRead data_reads2[] = {
- MockRead("HTTP/1.0 200 OK\r\n"
- "Server: not-proxy\r\n\r\n"),
- MockRead("content"),
- MockRead(SYNCHRONOUS, OK),
- };
- MockWrite data_writes2[] = {
- MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- };
-
- StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
- data_writes2, arraysize(data_writes2));
- mock_socket_factory_.AddSocketDataProvider(&data2);
-
- TestCompletionCallback callback;
-
- HttpRequestInfo request_info;
- request_info.url = GURL("http://www.google.com/");
- request_info.method = "GET";
- request_info.load_flags = LOAD_NORMAL;
-
- scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(OK, rv);
-
- rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- ASSERT_EQ(OK, rv);
-
- std::string contents;
- rv = ReadTransaction(trans.get(), &contents);
- EXPECT_EQ(OK, rv);
-
- // We should obtain content from the second socket provider write
- // corresponding to the fallback proxy.
- EXPECT_EQ("content", contents);
- // We also have a server header here that isn't set by the proxy.
- EXPECT_TRUE(trans->GetResponseInfo()->headers->HasHeaderValue(
- "server", "not-proxy"));
- // We should also observe the data reduction proxy in the retry list.
- ASSERT_EQ(1u, proxy_service_->proxy_retry_info().size());
- EXPECT_EQ(data_reduction_proxy,
- (*proxy_service_->proxy_retry_info().begin()).first);
- }
-}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
-
-TEST_F(HttpNetworkLayerTest, ProxyBypassIgnoredOnDirectConnectionPac) {
- // Verify that a Chrome-Proxy header is ignored when returned from a directly
- // connected origin server.
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Chrome-Proxy: bypass=0\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
- MockWrite data_writes[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
- data_writes, arraysize(data_writes));
- mock_socket_factory_.AddSocketDataProvider(&data1);
- TestCompletionCallback callback;
-
- HttpRequestInfo request_info;
- request_info.url = GURL("http://www.google.com/");
- request_info.method = "GET";
- request_info.load_flags = LOAD_NORMAL;
-
- scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
- EXPECT_EQ(OK, rv);
-
- rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- ASSERT_EQ(OK, rv);
-
- // We should have read the original page data.
- std::string contents;
- rv = ReadTransaction(trans.get(), &contents);
- EXPECT_EQ(OK, rv);
- EXPECT_EQ("Bypass message", contents);
-
- // We should have no entries in our bad proxy list.
- ASSERT_EQ(0u, proxy_service_->proxy_retry_info().size());
-}
-
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypass) {
- // Verify that a Chrome-Proxy: bypass=<seconds> header induces proxy
- // fallback to a second proxy, if configured.
- std::string bad_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy + "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Connection: keep-alive\r\n"
- "Chrome-Proxy: bypass=86400\r\n"
- "Via: 1.1 Chrome Compression Proxy\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackWithMockReads(bad_proxy, "", data_reads,
- arraysize(data_reads), 1u);
- EXPECT_EQ(base::TimeDelta::FromSeconds(86400),
- (*proxy_service_->proxy_retry_info().begin()).second.current_delay);
-}
-
-TEST_F(HttpNetworkLayerTest, ServerFallbackWithWrongViaHeader) {
- // Verify that a Via header that lacks the Chrome-Proxy induces proxy fallback
- // to a second proxy, if configured.
- std::string chrome_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + chrome_proxy + "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Connection: keep-alive\r\n"
- "Via: 1.0 some-other-proxy\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackWithMockReads(chrome_proxy, std::string(), data_reads,
- arraysize(data_reads), 1u);
-}
-
-TEST_F(HttpNetworkLayerTest, ServerFallbackWithNoViaHeader) {
- // Verify that the lack of a Via header induces proxy fallback to a second
- // proxy, if configured.
- std::string chrome_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + chrome_proxy + "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackWithMockReads(chrome_proxy, std::string(), data_reads,
- arraysize(data_reads), 1u);
-}
-
-TEST_F(HttpNetworkLayerTest, NoServerFallbackWith304Response) {
- // Verify that Chrome will not be induced to bypass the Chrome proxy when
- // the Chrome Proxy via header is absent on a 304.
- std::string chrome_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + chrome_proxy + "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 304 Not Modified\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackByMethodWithMockReads(chrome_proxy, std::string(),
- data_reads, arraysize(data_reads),
- "GET", std::string(), false, 0);
-}
-
-TEST_F(HttpNetworkLayerTest, NoServerFallbackWithChainedViaHeader) {
- // Verify that Chrome will not be induced to bypass the Chrome proxy when
- // the Chrome Proxy via header is present, even if that header is chained.
- std::string chrome_proxy = GetChromeProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + chrome_proxy + "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Connection: keep-alive\r\n"
- "Via: 1.1 Chrome Compression Proxy, 1.0 some-other-proxy\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackByMethodWithMockReads(chrome_proxy, std::string(),
- data_reads, arraysize(data_reads),
- "GET", "Bypass message", false, 0);
-}
-
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
-TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypassAll) {
- // Verify that a Chrome-Proxy: block=<seconds> header bypasses a
- // a configured Chrome-Proxy and fallback and induces proxy fallback to a
- // third proxy, if configured.
- std::string bad_proxy = GetChromeProxy();
- std::string fallback_proxy = GetChromeFallbackProxy();
- ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
- "PROXY " + bad_proxy + "; PROXY " + fallback_proxy +
- "; PROXY good:8080"));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 200 OK\r\n"
- "Connection: keep-alive\r\n"
- "Chrome-Proxy: block=86400\r\n"
- "Via: 1.1 Chrome Compression Proxy\r\n\r\n"),
- MockRead("Bypass message"),
- MockRead(SYNCHRONOUS, OK),
- };
-
- TestProxyFallbackWithMockReads(bad_proxy, fallback_proxy, data_reads,
- arraysize(data_reads), 2u);
- EXPECT_EQ(base::TimeDelta::FromSeconds(86400),
- (*proxy_service_->proxy_retry_info().begin()).second.current_delay);
-}
-#endif // defined(DATA_REDUCTION_FALLBACK_HOST)
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
-
TEST_F(HttpNetworkLayerTest, NetworkVerified) {
MockRead data_reads[] = {
MockRead("HTTP/1.0 200 OK\r\n\r\n"),
@@ -763,7 +356,7 @@ TEST_F(HttpNetworkLayerTest, NetworkVerified) {
request_info.load_flags = LOAD_NORMAL;
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
@@ -796,7 +389,7 @@ TEST_F(HttpNetworkLayerTest, NetworkUnVerified) {
request_info.load_flags = LOAD_NORMAL;
scoped_ptr<HttpTransaction> trans;
- int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL);
+ int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
EXPECT_EQ(OK, rv);
rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
diff --git a/chromium/net/http/http_network_session.cc b/chromium/net/http/http_network_session.cc
index 1be93fe5c3f..b4e373e5de7 100644
--- a/chromium/net/http/http_network_session.cc
+++ b/chromium/net/http/http_network_session.cc
@@ -24,6 +24,7 @@
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_pool_manager_impl.h"
#include "net/socket/next_proto.h"
+#include "net/spdy/hpack_huffman_aggregator.h"
#include "net/spdy/spdy_session_pool.h"
namespace {
@@ -66,13 +67,10 @@ HttpNetworkSession::Params::Params()
network_delegate(NULL),
net_log(NULL),
host_mapping_rules(NULL),
- force_http_pipelining(false),
ignore_certificate_errors(false),
- http_pipelining_enabled(false),
testing_fixed_http_port(0),
testing_fixed_https_port(0),
force_spdy_single_domain(false),
- enable_spdy_ip_pooling(true),
enable_spdy_compression(true),
enable_spdy_ping_based_connection_checking(true),
spdy_default_protocol(kProtoUnknown),
@@ -80,13 +78,22 @@ HttpNetworkSession::Params::Params()
spdy_initial_max_concurrent_streams(0),
spdy_max_concurrent_streams_limit(0),
time_func(&base::TimeTicks::Now),
+ force_spdy_over_ssl(true),
+ force_spdy_always(false),
+ use_alternate_protocols(false),
+ enable_websocket_over_spdy(false),
enable_quic(false),
enable_quic_https(false),
+ enable_quic_port_selection(true),
+ enable_quic_pacing(false),
+ enable_quic_time_based_loss_detection(false),
+ enable_quic_persist_server_info(true),
quic_clock(NULL),
quic_random(NULL),
quic_max_packet_length(kDefaultMaxPacketSize),
enable_user_alternate_protocol_ports(false),
quic_crypto_client_stream_factory(NULL) {
+ quic_supported_versions.push_back(QUIC_VERSION_18);
}
HttpNetworkSession::Params::~Params() {}
@@ -98,7 +105,6 @@ HttpNetworkSession::HttpNetworkSession(const Params& params)
http_server_properties_(params.http_server_properties),
cert_verifier_(params.cert_verifier),
http_auth_handler_factory_(params.http_auth_handler_factory),
- force_http_pipelining_(params.force_http_pipelining),
proxy_service_(params.proxy_service),
ssl_config_service_(params.ssl_config_service),
normal_socket_pool_manager_(
@@ -110,17 +116,22 @@ HttpNetworkSession::HttpNetworkSession(const Params& params)
params.client_socket_factory :
net::ClientSocketFactory::GetDefaultFactory(),
params.http_server_properties,
+ params.cert_verifier,
params.quic_crypto_client_stream_factory,
params.quic_random ? params.quic_random :
QuicRandom::GetInstance(),
params.quic_clock ? params. quic_clock :
new QuicClock(),
- params.quic_max_packet_length),
+ params.quic_max_packet_length,
+ params.quic_user_agent_id,
+ params.quic_supported_versions,
+ params.enable_quic_port_selection,
+ params.enable_quic_pacing,
+ params.enable_quic_time_based_loss_detection),
spdy_session_pool_(params.host_resolver,
params.ssl_config_service,
params.http_server_properties,
params.force_spdy_single_domain,
- params.enable_spdy_ip_pooling,
params.enable_spdy_compression,
params.enable_spdy_ping_based_connection_checking,
params.spdy_default_protocol,
@@ -136,6 +147,40 @@ HttpNetworkSession::HttpNetworkSession(const Params& params)
DCHECK(proxy_service_);
DCHECK(ssl_config_service_.get());
CHECK(http_server_properties_);
+
+ for (int i = ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION;
+ i <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION; ++i) {
+ enabled_protocols_[i - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = false;
+ }
+
+ // TODO(rtenneti): bug 116575 - consider combining the NextProto and
+ // AlternateProtocol.
+ for (std::vector<NextProto>::const_iterator it = params_.next_protos.begin();
+ it != params_.next_protos.end(); ++it) {
+ NextProto proto = *it;
+
+ // Add the protocol to the TLS next protocol list, except for QUIC
+ // since it uses UDP.
+ if (proto != kProtoQUIC1SPDY3) {
+ next_protos_.push_back(SSLClientSocket::NextProtoToString(proto));
+ }
+
+ // Enable the corresponding alternate protocol, except for HTTP
+ // which has not corresponding alternative.
+ if (proto != kProtoHTTP11) {
+ AlternateProtocol alternate = AlternateProtocolFromNextProto(proto);
+ if (!IsAlternateProtocolValid(alternate)) {
+ NOTREACHED() << "Invalid next proto: " << proto;
+ continue;
+ }
+ enabled_protocols_[alternate - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] =
+ true;
+ }
+ }
+
+ if (HpackHuffmanAggregator::UseAggregator()) {
+ huffman_aggregator_.reset(new HpackHuffmanAggregator());
+ }
}
HttpNetworkSession::~HttpNetworkSession() {
@@ -198,6 +243,14 @@ base::Value* HttpNetworkSession::QuicInfoToValue() const {
dict->Set("sessions", quic_stream_factory_.QuicStreamFactoryInfoToValue());
dict->SetBoolean("quic_enabled", params_.enable_quic);
dict->SetBoolean("quic_enabled_https", params_.enable_quic_https);
+ dict->SetBoolean("enable_quic_port_selection",
+ params_.enable_quic_port_selection);
+ dict->SetBoolean("enable_quic_pacing",
+ params_.enable_quic_pacing);
+ dict->SetBoolean("enable_quic_time_based_loss_detection",
+ params_.enable_quic_time_based_loss_detection);
+ dict->SetBoolean("enable_quic_persist_server_info",
+ params_.enable_quic_persist_server_info);
dict->SetString("origin_to_force_quic_on",
params_.origin_to_force_quic_on.ToString());
return dict;
@@ -216,6 +269,27 @@ void HttpNetworkSession::CloseIdleConnections() {
spdy_session_pool_.CloseCurrentIdleSessions();
}
+bool HttpNetworkSession::IsProtocolEnabled(AlternateProtocol protocol) const {
+ DCHECK(IsAlternateProtocolValid(protocol));
+ return enabled_protocols_[
+ protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION];
+}
+
+void HttpNetworkSession::GetNextProtos(
+ std::vector<std::string>* next_protos) const {
+ if (HttpStreamFactory::spdy_enabled()) {
+ *next_protos = next_protos_;
+ } else {
+ next_protos->clear();
+ }
+}
+
+bool HttpNetworkSession::HasSpdyExclusion(
+ HostPortPair host_port_pair) const {
+ return params_.forced_spdy_exclusions.find(host_port_pair) !=
+ params_.forced_spdy_exclusions.end();
+}
+
ClientSocketPoolManager* HttpNetworkSession::GetSocketPoolManager(
SocketPoolType pool_type) {
switch (pool_type) {
diff --git a/chromium/net/http/http_network_session.h b/chromium/net/http/http_network_session.h
index 471041220ec..b877b08081a 100644
--- a/chromium/net/http/http_network_session.h
+++ b/chromium/net/http/http_network_session.h
@@ -7,6 +7,7 @@
#include <set>
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
@@ -18,6 +19,7 @@
#include "net/http/http_auth_cache.h"
#include "net/http/http_stream_factory.h"
#include "net/quic/quic_stream_factory.h"
+#include "net/socket/next_proto.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/ssl/ssl_client_auth_cache.h"
@@ -30,7 +32,9 @@ namespace net {
class CertVerifier;
class ClientSocketFactory;
class ClientSocketPoolManager;
+class CTVerifier;
class HostResolver;
+class HpackHuffmanAggregator;
class HttpAuthHandlerFactory;
class HttpNetworkSessionPeer;
class HttpProxyClientSocketPool;
@@ -42,6 +46,7 @@ class ServerBoundCertService;
class ProxyService;
class QuicClock;
class QuicCryptoClientStreamFactory;
+class QuicServerInfoFactory;
class SOCKSClientSocketPool;
class SSLClientSocketPool;
class SSLConfigService;
@@ -71,29 +76,50 @@ class NET_EXPORT HttpNetworkSession
base::WeakPtr<HttpServerProperties> http_server_properties;
NetLog* net_log;
HostMappingRules* host_mapping_rules;
- bool force_http_pipelining;
bool ignore_certificate_errors;
- bool http_pipelining_enabled;
uint16 testing_fixed_http_port;
uint16 testing_fixed_https_port;
+
bool force_spdy_single_domain;
- bool enable_spdy_ip_pooling;
bool enable_spdy_compression;
bool enable_spdy_ping_based_connection_checking;
NextProto spdy_default_protocol;
+ // The protocols supported by NPN (next protocol negotiation) during the
+ // SSL handshake as well as by HTTP Alternate-Protocol.
+ // TODO(mmenke): This is currently empty by default, and alternate
+ // protocols are disabled. We should use some reasonable
+ // defaults.
+ NextProtoVector next_protos;
size_t spdy_stream_initial_recv_window_size;
size_t spdy_initial_max_concurrent_streams;
size_t spdy_max_concurrent_streams_limit;
SpdySessionPool::TimeFunc time_func;
std::string trusted_spdy_proxy;
+ // Controls whether or not ssl is used when in SPDY mode.
+ bool force_spdy_over_ssl;
+ // Controls whether or not SPDY is used without NPN.
+ bool force_spdy_always;
+ // URLs to exclude from forced SPDY.
+ std::set<HostPortPair> forced_spdy_exclusions;
+ // Noe: Using this in the case of NPN for HTTP only results in the browser
+ // trying SSL and then falling back to http.
+ bool use_alternate_protocols;
+ bool enable_websocket_over_spdy;
+
bool enable_quic;
bool enable_quic_https;
+ bool enable_quic_port_selection;
+ bool enable_quic_pacing;
+ bool enable_quic_time_based_loss_detection;
+ bool enable_quic_persist_server_info;
HostPortPair origin_to_force_quic_on;
QuicClock* quic_clock; // Will be owned by QuicStreamFactory.
QuicRandom* quic_random;
size_t quic_max_packet_length;
+ std::string quic_user_agent_id;
bool enable_user_alternate_protocol_ports;
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory;
+ QuicVersionVector quic_supported_versions;
};
enum SocketPoolType {
@@ -148,6 +174,9 @@ class NET_EXPORT HttpNetworkSession
NetLog* net_log() {
return net_log_;
}
+ HpackHuffmanAggregator* huffman_aggregator() {
+ return huffman_aggregator_.get();
+ }
// Creates a Value summary of the state of the socket pools. The caller is
// responsible for deleting the returned value.
@@ -164,14 +193,16 @@ class NET_EXPORT HttpNetworkSession
void CloseAllConnections();
void CloseIdleConnections();
- bool force_http_pipelining() const { return force_http_pipelining_; }
-
// Returns the original Params used to construct this session.
const Params& params() const { return params_; }
- void set_http_pipelining_enabled(bool enable) {
- params_.http_pipelining_enabled = enable;
- }
+ bool IsProtocolEnabled(AlternateProtocol protocol) const;
+
+ void GetNextProtos(std::vector<std::string>* next_protos) const;
+
+ // Convenience function for searching through |params_| for
+ // |forced_spdy_exclusions|.
+ bool HasSpdyExclusion(HostPortPair host_port_pair) const;
private:
friend class base::RefCounted<HttpNetworkSession>;
@@ -186,7 +217,6 @@ class NET_EXPORT HttpNetworkSession
const base::WeakPtr<HttpServerProperties> http_server_properties_;
CertVerifier* const cert_verifier_;
HttpAuthHandlerFactory* const http_auth_handler_factory_;
- bool force_http_pipelining_;
// Not const since it's modified by HttpNetworkSessionPeer for testing.
ProxyService* proxy_service_;
@@ -202,6 +232,12 @@ class NET_EXPORT HttpNetworkSession
scoped_ptr<HttpStreamFactory> http_stream_factory_for_websocket_;
std::set<HttpResponseBodyDrainer*> response_drainers_;
+ // TODO(jgraettinger): Remove when Huffman collection is complete.
+ scoped_ptr<HpackHuffmanAggregator> huffman_aggregator_;
+
+ std::vector<std::string> next_protos_;
+ bool enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS];
+
Params params_;
};
diff --git a/chromium/net/http/http_network_transaction.cc b/chromium/net/http/http_network_transaction.cc
index d7b3f41ff76..32af2dd1844 100644
--- a/chromium/net/http/http_network_transaction.cc
+++ b/chromium/net/http/http_network_transaction.cc
@@ -54,6 +54,7 @@
#include "net/socket/ssl_client_socket.h"
#include "net/socket/ssl_client_socket_pool.h"
#include "net/socket/transport_client_socket_pool.h"
+#include "net/spdy/hpack_huffman_aggregator.h"
#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
@@ -75,8 +76,7 @@ namespace net {
namespace {
void ProcessAlternateProtocol(
- HttpStreamFactory* factory,
- const base::WeakPtr<HttpServerProperties>& http_server_properties,
+ HttpNetworkSession* session,
const HttpResponseHeaders& headers,
const HostPortPair& http_host_port_pair) {
std::string alternate_protocol_str;
@@ -87,9 +87,11 @@ void ProcessAlternateProtocol(
return;
}
- factory->ProcessAlternateProtocol(http_server_properties,
- alternate_protocol_str,
- http_host_port_pair);
+ session->http_stream_factory()->ProcessAlternateProtocol(
+ session->http_server_properties(),
+ alternate_protocol_str,
+ http_host_port_pair,
+ *session);
}
// Returns true if |error| is a client certificate authentication error.
@@ -119,30 +121,6 @@ base::Value* NetLogSSLVersionFallbackCallback(
return dict;
}
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-// Returns true if |response_headers| contains the data reduction proxy Via
-// header value.
-bool IsChromeProxyResponse(const net::HttpResponseHeaders* response_headers) {
- if (!response_headers) {
- return false;
- }
- const char kDataReductionProxyViaValue[] = "1.1 Chrome Compression Proxy";
- size_t value_len = strlen(kDataReductionProxyViaValue);
- void* iter = NULL;
- std::string temp;
- while (response_headers->EnumerateHeader(&iter, "Via", &temp)) {
- std::string::const_iterator it =
- std::search(temp.begin(), temp.end(),
- kDataReductionProxyViaValue,
- kDataReductionProxyViaValue + value_len,
- base::CaseInsensitiveCompareASCII<char>());
- if (it != temp.end())
- return true;
- }
- return false;
-}
-#endif
-
} // namespace
//-----------------------------------------------------------------------------
@@ -160,14 +138,12 @@ HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority,
fallback_error_code_(ERR_SSL_INAPPROPRIATE_FALLBACK),
request_headers_(),
read_buf_len_(0),
+ total_received_bytes_(0),
next_state_(STATE_NONE),
establishing_tunnel_(false),
websocket_handshake_stream_base_create_helper_(NULL) {
session->ssl_config_service()->GetSSLConfig(&server_ssl_config_);
- if (session->http_stream_factory()->has_next_protos()) {
- server_ssl_config_.next_protos =
- session->http_stream_factory()->next_protos();
- }
+ session->GetNextProtos(&server_ssl_config_.next_protos);
proxy_ssl_config_ = server_ssl_config_;
}
@@ -216,13 +192,11 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
proxy_ssl_config_.rev_checking_enabled = false;
}
- // Channel ID is enabled unless --disable-tls-channel-id flag is set,
- // or if privacy mode is enabled.
- bool channel_id_enabled = server_ssl_config_.channel_id_enabled &&
- (request_->privacy_mode == kPrivacyModeDisabled);
- server_ssl_config_.channel_id_enabled = channel_id_enabled;
+ // Channel ID is disabled if privacy mode is enabled for this request.
+ if (request_->privacy_mode == PRIVACY_MODE_ENABLED)
+ server_ssl_config_.channel_id_enabled = false;
- next_state_ = STATE_CREATE_STREAM;
+ next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = callback;
@@ -331,6 +305,7 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
DCHECK(!stream_request_.get());
if (stream_.get()) {
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
HttpStream* new_stream = NULL;
if (keep_alive && stream_->IsConnectionReusable()) {
// We should call connection_->set_idle_time(), but this doesn't occur
@@ -347,6 +322,8 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
stream_->Close(true);
next_state_ = STATE_CREATE_STREAM;
} else {
+ // Renewed streams shouldn't carry over received bytes.
+ DCHECK_EQ(0, new_stream->GetTotalReceivedBytes());
next_state_ = STATE_INIT_STREAM;
}
stream_.reset(new_stream);
@@ -400,6 +377,8 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
return rv;
}
+void HttpNetworkTransaction::StopCaching() {}
+
bool HttpNetworkTransaction::GetFullRequestHeaders(
HttpRequestHeaders* headers) const {
// TODO(ttuttle): Make sure we've populated request_headers_.
@@ -407,6 +386,15 @@ bool HttpNetworkTransaction::GetFullRequestHeaders(
return true;
}
+int64 HttpNetworkTransaction::GetTotalReceivedBytes() const {
+ int64 total_received_bytes = total_received_bytes_;
+ if (stream_)
+ total_received_bytes += stream_->GetTotalReceivedBytes();
+ return total_received_bytes;
+}
+
+void HttpNetworkTransaction::DoneReading() {}
+
const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const {
return ((headers_valid_ && response_.headers.get()) ||
response_.ssl_info.cert.get() || response_.cert_request_info.get())
@@ -418,6 +406,8 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
// TODO(wtc): Define a new LoadState value for the
// STATE_INIT_CONNECTION_COMPLETE state, which delays the HTTP request.
switch (next_state_) {
+ case STATE_CREATE_STREAM:
+ return LOAD_STATE_WAITING_FOR_DELEGATE;
case STATE_CREATE_STREAM_COMPLETE:
return stream_request_->GetLoadState();
case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
@@ -441,6 +431,9 @@ UploadProgress HttpNetworkTransaction::GetUploadProgress() const {
return static_cast<HttpStream*>(stream_.get())->GetUploadProgress();
}
+void HttpNetworkTransaction::SetQuicServerInfo(
+ QuicServerInfo* quic_server_info) {}
+
bool HttpNetworkTransaction::GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const {
if (!stream_ || !stream_->GetLoadTimingInfo(load_timing_info))
@@ -467,12 +460,24 @@ void HttpNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper(
websocket_handshake_stream_base_create_helper_ = create_helper;
}
+void HttpNetworkTransaction::SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) {
+ before_network_start_callback_ = callback;
+}
+
+int HttpNetworkTransaction::ResumeNetworkStart() {
+ DCHECK_EQ(next_state_, STATE_CREATE_STREAM);
+ return DoLoop(OK);
+}
+
void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
HttpStreamBase* stream) {
DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_);
DCHECK(stream_request_.get());
+ if (stream_)
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
stream_.reset(stream);
server_ssl_config_ = used_ssl_config;
proxy_info_ = used_proxy_info;
@@ -481,7 +486,8 @@ void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config,
stream_request_->protocol_negotiated());
response_.was_fetched_via_spdy = stream_request_->using_spdy();
response_.was_fetched_via_proxy = !proxy_info_.is_direct();
-
+ if (response_.was_fetched_via_proxy && !proxy_info_.is_empty())
+ response_.proxy_server = proxy_info_.proxy_server().host_port_pair();
OnIOComplete(OK);
}
@@ -566,6 +572,8 @@ void HttpNetworkTransaction::OnHttpsProxyTunnelResponse(
response_ = response_info;
server_ssl_config_ = used_ssl_config;
proxy_info_ = used_proxy_info;
+ if (stream_)
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
stream_.reset(stream);
stream_request_.reset(); // we're done with the stream request
OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
@@ -599,6 +607,10 @@ int HttpNetworkTransaction::DoLoop(int result) {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
+ case STATE_NOTIFY_BEFORE_CREATE_STREAM:
+ DCHECK_EQ(OK, rv);
+ rv = DoNotifyBeforeCreateStream();
+ break;
case STATE_CREATE_STREAM:
DCHECK_EQ(OK, rv);
rv = DoCreateStream();
@@ -692,9 +704,18 @@ int HttpNetworkTransaction::DoLoop(int result) {
return rv;
}
+int HttpNetworkTransaction::DoNotifyBeforeCreateStream() {
+ next_state_ = STATE_CREATE_STREAM;
+ bool defer = false;
+ if (!before_network_start_callback_.is_null())
+ before_network_start_callback_.Run(&defer);
+ if (!defer)
+ return OK;
+ return ERR_IO_PENDING;
+}
+
int HttpNetworkTransaction::DoCreateStream() {
next_state_ = STATE_CREATE_STREAM_COMPLETE;
-
if (ForWebSocketHandshake()) {
stream_request_.reset(
session_->http_stream_factory_for_websocket()
@@ -755,6 +776,8 @@ int HttpNetworkTransaction::DoInitStreamComplete(int result) {
result = HandleIOError(result);
// The stream initialization failed, so this stream will never be useful.
+ if (stream_)
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
stream_.reset();
}
@@ -918,16 +941,6 @@ int HttpNetworkTransaction::DoReadHeaders() {
return stream_->ReadResponseHeaders(io_callback_);
}
-int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
- if (!response_.headers.get() && !stream_->IsConnectionReused()) {
- // The connection was closed before any data was sent. Likely an error
- // rather than empty HTTP/0.9 response.
- return ERR_EMPTY_RESPONSE;
- }
-
- return OK;
-}
-
int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
// We can get a certificate error or ERR_SSL_CLIENT_AUTH_CERT_NEEDED here
// due to SSL renegotiation.
@@ -954,108 +967,40 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
return OK;
}
- if (result < 0 && result != ERR_CONNECTION_CLOSED)
- return HandleIOError(result);
-
- if (result == ERR_CONNECTION_CLOSED && ShouldResendRequest(result)) {
- ResetConnectionAndRequestForResend();
- return OK;
- }
-
// After we call RestartWithAuth a new response_time will be recorded, and
// we need to be cautious about incorrectly logging the duration across the
// authentication activity.
if (result == OK)
LogTransactionConnectedMetrics();
- if (result == ERR_CONNECTION_CLOSED) {
- // For now, if we get at least some data, we do the best we can to make
- // sense of it and send it back up the stack.
- int rv = HandleConnectionClosedBeforeEndOfHeaders();
- if (rv != OK)
- return rv;
- }
- DCHECK(response_.headers.get());
+ // ERR_CONNECTION_CLOSED is treated differently at this point; if partial
+ // response headers were received, we do the best we can to make sense of it
+ // and send it back up the stack.
+ //
+ // TODO(davidben): Consider moving this to HttpBasicStream, It's a little
+ // bizarre for SPDY. Assuming this logic is useful at all.
+ // TODO(davidben): Bubble the error code up so we do not cache?
+ if (result == ERR_CONNECTION_CLOSED && response_.headers.get())
+ result = OK;
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- // Server-induced fallback; see: http://crbug.com/143712
- if (response_.was_fetched_via_proxy && response_.headers.get() != NULL) {
- ProxyService::DataReductionProxyBypassEventType proxy_bypass_event =
- ProxyService::BYPASS_EVENT_TYPE_MAX;
- net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info;
- bool chrome_proxy_used =
- proxy_info_.proxy_server().isDataReductionProxy();
- bool chrome_fallback_proxy_used = false;
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
- if (!chrome_proxy_used) {
- chrome_fallback_proxy_used =
- proxy_info_.proxy_server().isDataReductionProxyFallback();
- }
-#endif
+ if (result < 0)
+ return HandleIOError(result);
- if (chrome_proxy_used || chrome_fallback_proxy_used) {
- // A Via header might not be present in a 304. Since the goal of a 304
- // response is to minimize information transfer, a sender in general
- // should not generate representation metadata other than Cache-Control,
- // Content-Location, Date, ETag, Expires, and Vary.
- if (!IsChromeProxyResponse(response_.headers.get()) &&
- (response_.headers->response_code() != HTTP_NOT_MODIFIED)) {
- proxy_bypass_event = ProxyService::MISSING_VIA_HEADER;
- } else if (response_.headers->GetChromeProxyInfo(&chrome_proxy_info)) {
- if (chrome_proxy_info.bypass_duration < TimeDelta::FromMinutes(30))
- proxy_bypass_event = ProxyService::SHORT_BYPASS;
- else
- proxy_bypass_event = ProxyService::LONG_BYPASS;
- } else {
- // Additionally, fallback if a 500, 502 or 503 is returned via the data
- // reduction proxy. This is conservative, as the 500, 502 or 503 might
- // have been generated by the origin, and not the proxy.
- if (response_.headers->response_code() == HTTP_INTERNAL_SERVER_ERROR ||
- response_.headers->response_code() == HTTP_BAD_GATEWAY ||
- response_.headers->response_code() == HTTP_SERVICE_UNAVAILABLE) {
- proxy_bypass_event = ProxyService::INTERNAL_SERVER_ERROR_BYPASS;
- }
- }
+ DCHECK(response_.headers.get());
- if (proxy_bypass_event < ProxyService::BYPASS_EVENT_TYPE_MAX) {
- ProxyService* proxy_service = session_->proxy_service();
-
- proxy_service->RecordDataReductionProxyBypassInfo(
- chrome_proxy_used, proxy_info_.proxy_server(), proxy_bypass_event);
-
- ProxyServer proxy_server;
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
- if (chrome_proxy_used && chrome_proxy_info.bypass_all) {
- // TODO(bengr): Rename as DATA_REDUCTION_FALLBACK_ORIGIN.
- GURL proxy_url(DATA_REDUCTION_FALLBACK_HOST);
- if (proxy_url.SchemeIsHTTPOrHTTPS()) {
- proxy_server = ProxyServer(proxy_url.SchemeIs("http") ?
- ProxyServer::SCHEME_HTTP :
- ProxyServer::SCHEME_HTTPS,
- HostPortPair::FromURL(proxy_url));
- }
- }
-#endif
- if (proxy_service->MarkProxiesAsBad(proxy_info_,
- chrome_proxy_info.bypass_duration,
- proxy_server,
- net_log_)) {
- // Only retry idempotent methods. We don't want to resubmit a POST
- // if the proxy took some action.
- if (request_->method == "GET" ||
- request_->method == "OPTIONS" ||
- request_->method == "HEAD" ||
- request_->method == "PUT" ||
- request_->method == "DELETE" ||
- request_->method == "TRACE") {
- ResetConnectionAndRequestForResend();
- return OK;
- }
- }
- }
- }
+ // On a 408 response from the server ("Request Timeout") on a stale socket,
+ // retry the request.
+ // Headers can be NULL because of http://crbug.com/384554.
+ if (response_.headers.get() && response_.headers->response_code() == 408 &&
+ stream_->IsConnectionReused()) {
+ net_log_.AddEventWithNetErrorCode(
+ NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR,
+ response_.headers->response_code());
+ // This will close the socket - it would be weird to try and reuse it, even
+ // if the server doesn't actually close it.
+ ResetConnectionAndRequestForResend();
+ return OK;
}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
// Like Net.HttpResponseCode, but only for MAIN_FRAME loads.
if (request_->load_flags & LOAD_MAIN_FRAME) {
@@ -1091,8 +1036,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
HostPortPair endpoint = HostPortPair(request_->url.HostNoBrackets(),
request_->url.EffectiveIntPort());
- ProcessAlternateProtocol(session_->http_stream_factory(),
- session_->http_server_properties(),
+ ProcessAlternateProtocol(session_,
*response_.headers.get(),
endpoint);
@@ -1104,6 +1048,14 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
stream_->GetSSLInfo(&response_.ssl_info);
headers_valid_ = true;
+
+ if (session_->huffman_aggregator()) {
+ session_->huffman_aggregator()->AggregateTransactionCharacterCounts(
+ *request_,
+ request_headers_,
+ proxy_info_.proxy_server(),
+ *response_.headers);
+ }
return OK;
}
@@ -1280,6 +1232,7 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) {
// Since we already have a stream, we're being called as part of SSL
// renegotiation.
DCHECK(!stream_request_.get());
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
stream_->Close(true);
stream_.reset();
}
@@ -1328,7 +1281,7 @@ void HttpNetworkTransaction::HandleClientAuthError(int error) {
if (server_ssl_config_.send_client_cert &&
(error == ERR_SSL_PROTOCOL_ERROR || IsClientCertificateError(error))) {
session_->ssl_client_auth_cache()->Remove(
- GetHostAndPort(request_->url));
+ HostPortPair::FromURL(request_->url));
}
}
@@ -1428,15 +1381,11 @@ int HttpNetworkTransaction::HandleIOError(int error) {
// likely happen when trying to retrieve its IP address.
// See http://crbug.com/105824 for more details.
case ERR_SOCKET_NOT_CONNECTED:
- if (ShouldResendRequest(error)) {
- net_log_.AddEventWithNetErrorCode(
- NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error);
- ResetConnectionAndRequestForResend();
- error = OK;
- }
- break;
- case ERR_PIPELINE_EVICTION:
- if (!session_->force_http_pipelining()) {
+ // If a socket is closed on its initial request, HttpStreamParser returns
+ // ERR_EMPTY_RESPONSE. This may still be close/reuse race if the socket was
+ // preconnected but failed to be used before the server timed it out.
+ case ERR_EMPTY_RESPONSE:
+ if (ShouldResendRequest()) {
net_log_.AddEventWithNetErrorCode(
NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error);
ResetConnectionAndRequestForResend();
@@ -1457,6 +1406,8 @@ int HttpNetworkTransaction::HandleIOError(int error) {
void HttpNetworkTransaction::ResetStateForRestart() {
ResetStateForAuthRestart();
+ if (stream_)
+ total_received_bytes_ += stream_->GetTotalReceivedBytes();
stream_.reset();
}
@@ -1477,7 +1428,7 @@ HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const {
return response_.headers.get();
}
-bool HttpNetworkTransaction::ShouldResendRequest(int error) const {
+bool HttpNetworkTransaction::ShouldResendRequest() const {
bool connection_is_proven = stream_->IsConnectionReused();
bool has_received_headers = GetResponseHeaders() != NULL;
@@ -1580,6 +1531,7 @@ bool HttpNetworkTransaction::ForWebSocketHandshake() const {
std::string HttpNetworkTransaction::DescribeState(State state) {
std::string description;
switch (state) {
+ STATE_CASE(STATE_NOTIFY_BEFORE_CREATE_STREAM);
STATE_CASE(STATE_CREATE_STREAM);
STATE_CASE(STATE_CREATE_STREAM_COMPLETE);
STATE_CASE(STATE_INIT_REQUEST_BODY);
diff --git a/chromium/net/http/http_network_transaction.h b/chromium/net/http/http_network_transaction.h
index 3ae1725d1de..24ab95b8ac2 100644
--- a/chromium/net/http/http_network_transaction.h
+++ b/chromium/net/http/http_network_transaction.h
@@ -59,18 +59,23 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
virtual int Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
- virtual void StopCaching() OVERRIDE {}
+ virtual void StopCaching() OVERRIDE;
virtual bool GetFullRequestHeaders(
HttpRequestHeaders* headers) const OVERRIDE;
- virtual void DoneReading() OVERRIDE {}
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
+ virtual void DoneReading() OVERRIDE;
virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual LoadState GetLoadState() const OVERRIDE;
virtual UploadProgress GetUploadProgress() const OVERRIDE;
+ virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) OVERRIDE;
virtual bool GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const OVERRIDE;
virtual void SetPriority(RequestPriority priority) OVERRIDE;
virtual void SetWebSocketHandshakeStreamCreateHelper(
WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE;
+ virtual void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) OVERRIDE;
+ virtual int ResumeNetworkStart() OVERRIDE;
// HttpStreamRequest::Delegate methods:
virtual void OnStreamReady(const SSLConfig& used_ssl_config,
@@ -116,6 +121,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
FlowControlNegativeSendWindowSize);
enum State {
+ STATE_NOTIFY_BEFORE_CREATE_STREAM,
STATE_CREATE_STREAM,
STATE_CREATE_STREAM_COMPLETE,
STATE_INIT_STREAM,
@@ -151,6 +157,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
// argument receive the result from the previous state. If a method returns
// ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
// next state method as the result arg.
+ int DoNotifyBeforeCreateStream();
int DoCreateStream();
int DoCreateStreamComplete(int result);
int DoInitStream();
@@ -203,19 +210,14 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
// Gets the response headers from the HttpStream.
HttpResponseHeaders* GetResponseHeaders() const;
- // Called when we reached EOF or got an error. Returns true if we should
- // resend the request. |error| is OK when we reached EOF.
- bool ShouldResendRequest(int error) const;
+ // Called when the socket is unexpectedly closed. Returns true if the request
+ // should be resent in case of a socket reuse/close race.
+ bool ShouldResendRequest() const;
// Resets the connection and the request headers for resend. Called when
// ShouldResendRequest() is true.
void ResetConnectionAndRequestForResend();
- // Decides the policy when the connection is closed before the end of headers
- // has been read. This only applies to reading responses, and not writing
- // requests.
- int HandleConnectionClosedBeforeEndOfHeaders();
-
// Sets up the state machine to restart the transaction with auth.
void PrepareForAuthRestart(HttpAuth::Target target);
@@ -254,6 +256,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
// Debug helper.
static std::string DescribeState(State state);
+ void SetStream(HttpStreamBase* stream);
+
scoped_refptr<HttpAuthController>
auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
@@ -306,6 +310,9 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
scoped_refptr<IOBuffer> read_buf_;
int read_buf_len_;
+ // Total number of bytes received on streams for this transaction.
+ int64 total_received_bytes_;
+
// The time the Start method was called.
base::Time start_time_;
@@ -326,6 +333,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction
WebSocketHandshakeStreamBase::CreateHelper*
websocket_handshake_stream_base_create_helper_;
+ BeforeNetworkStartCallback before_network_start_callback_;
+
DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction);
};
diff --git a/chromium/net/http/http_network_transaction_unittest.cc b/chromium/net/http/http_network_transaction_unittest.cc
index dd9eefecf37..01756a19654 100644
--- a/chromium/net/http/http_network_transaction_unittest.cc
+++ b/chromium/net/http/http_network_transaction_unittest.cc
@@ -16,6 +16,7 @@
#include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_file_util.h"
@@ -35,6 +36,7 @@
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/host_cache.h"
#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_digest.h"
#include "net/http/http_auth_handler_mock.h"
#include "net/http/http_auth_handler_ntlm.h"
@@ -44,7 +46,7 @@
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_stream.h"
#include "net/http/http_stream_factory.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_resolver.h"
@@ -69,6 +71,8 @@
#include "testing/platform_test.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
+
//-----------------------------------------------------------------------------
namespace {
@@ -244,6 +248,7 @@ class HttpNetworkTransactionTest
int rv;
std::string status_line;
std::string response_data;
+ int64 totalReceivedBytes;
LoadTimingInfo load_timing_info;
};
@@ -260,8 +265,6 @@ class HttpNetworkTransactionTest
PlatformTest::TearDown();
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
base::MessageLoop::current()->RunUntilIdle();
- HttpStreamFactory::set_use_alternate_protocols(false);
- HttpStreamFactory::SetNextProtos(std::vector<NextProto>());
}
// This is the expected return from a current server advertising SPDY.
@@ -279,6 +282,14 @@ class HttpNetworkTransactionTest
void KeepAliveConnectionResendRequestTest(const MockWrite* write_failure,
const MockRead* read_failure);
+ // Either |write_failure| specifies a write failure or |read_failure|
+ // specifies a read failure when using a reused socket. In either case, the
+ // failure should cause the network transaction to resend the request, and the
+ // other argument should be NULL.
+ void PreconnectErrorResendRequestTest(const MockWrite* write_failure,
+ const MockRead* read_failure,
+ bool use_spdy);
+
SimpleGetHelperResult SimpleGetHelperForData(StaticSocketDataProvider* data[],
size_t data_count) {
SimpleGetHelperResult out;
@@ -300,7 +311,7 @@ class HttpNetworkTransactionTest
TestCompletionCallback callback;
- EXPECT_TRUE(log.bound().IsLoggingAllEvents());
+ EXPECT_TRUE(log.bound().IsLogging());
int rv = trans->Start(&request, callback.callback(), log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -356,6 +367,7 @@ class HttpNetworkTransactionTest
EXPECT_EQ("['Host: www.google.com','Connection: keep-alive']",
response_headers);
+ out.totalReceivedBytes = trans->GetTotalReceivedBytes();
return out;
}
@@ -366,6 +378,13 @@ class HttpNetworkTransactionTest
return SimpleGetHelperForData(data, 1);
}
+ int64 ReadsSize(MockRead data_reads[], size_t reads_count) {
+ int64 size = 0;
+ for (size_t i = 0; i < reads_count; ++i)
+ size += data_reads[i].data_len;
+ return size;
+ }
+
void ConnectStatusHelperWithExpectedStatus(const MockRead& status,
int expected_status);
@@ -388,11 +407,32 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
HttpNetworkTransactionTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
namespace {
+class BeforeNetworkStartHandler {
+ public:
+ explicit BeforeNetworkStartHandler(bool defer)
+ : defer_on_before_network_start_(defer),
+ observed_before_network_start_(false) {}
+
+ void OnBeforeNetworkStart(bool* defer) {
+ *defer = defer_on_before_network_start_;
+ observed_before_network_start_ = true;
+ }
+
+ bool observed_before_network_start() const {
+ return observed_before_network_start_;
+ }
+
+ private:
+ const bool defer_on_before_network_start_;
+ bool observed_before_network_start_;
+
+ DISALLOW_COPY_AND_ASSIGN(BeforeNetworkStartHandler);
+};
+
// Fill |str| with a long header list that consumes >= |size| bytes.
void FillLargeHeadersString(std::string* str, int size) {
const char* row =
@@ -584,6 +624,8 @@ TEST_P(HttpNetworkTransactionTest, SimpleGET) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
EXPECT_EQ("hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Response with no status line.
@@ -597,6 +639,8 @@ TEST_P(HttpNetworkTransactionTest, SimpleGETNoHeaders) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Allow up to 4 bytes of junk to precede status line.
@@ -610,6 +654,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk3Bytes) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Allow up to 4 bytes of junk to precede status line.
@@ -623,6 +669,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk4Bytes) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Beyond 4 bytes of slop and it should fail to find a status line.
@@ -636,6 +684,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk5Bytes) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("xxxxxHTTP/1.1 404 Not Found\nServer: blah", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Same as StatusLineJunk4Bytes, except the read chunks are smaller.
@@ -653,6 +703,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk4Bytes_Slow) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Close the connection before enough bytes to have a status line.
@@ -666,15 +718,18 @@ TEST_P(HttpNetworkTransactionTest, StatusLinePartial) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("HTT", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Simulate a 204 response, lacking a Content-Length header, sent over a
// persistent connection. The response should still terminate since a 204
// cannot have a response body.
TEST_P(HttpNetworkTransactionTest, StopsReading204) {
+ char junk[] = "junk";
MockRead data_reads[] = {
MockRead("HTTP/1.1 204 No Content\r\n\r\n"),
- MockRead("junk"), // Should not be read!!
+ MockRead(junk), // Should not be read!!
MockRead(SYNCHRONOUS, OK),
};
SimpleGetHelperResult out = SimpleGetHelper(data_reads,
@@ -682,18 +737,23 @@ TEST_P(HttpNetworkTransactionTest, StopsReading204) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 204 No Content", out.status_line);
EXPECT_EQ("", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ int64 response_size = reads_size - strlen(junk);
+ EXPECT_EQ(response_size, out.totalReceivedBytes);
}
// A simple request using chunked encoding with some extra data after.
-// (Like might be seen in a pipelined response.)
TEST_P(HttpNetworkTransactionTest, ChunkedEncoding) {
+ std::string final_chunk = "0\r\n\r\n";
+ std::string extra_data = "HTTP/1.1 200 OK\r\n";
+ std::string last_read = final_chunk + extra_data;
MockRead data_reads[] = {
MockRead("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"),
MockRead("5\r\nHello\r\n"),
MockRead("1\r\n"),
MockRead(" \r\n"),
MockRead("5\r\nworld\r\n"),
- MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"),
+ MockRead(last_read.data()),
MockRead(SYNCHRONOUS, OK),
};
SimpleGetHelperResult out = SimpleGetHelper(data_reads,
@@ -701,6 +761,9 @@ TEST_P(HttpNetworkTransactionTest, ChunkedEncoding) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
EXPECT_EQ("Hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ int64 response_size = reads_size - extra_data.size();
+ EXPECT_EQ(response_size, out.totalReceivedBytes);
}
// Next tests deal with http://crbug.com/56344.
@@ -883,6 +946,7 @@ TEST_P(HttpNetworkTransactionTest, TwoIdenticalLocationHeaders) {
std::string url;
EXPECT_TRUE(response->headers->IsRedirect(&url));
EXPECT_EQ("http://good.com/", url);
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
}
// Checks that two distinct Location headers result in an error.
@@ -945,6 +1009,7 @@ TEST_P(HttpNetworkTransactionTest, Head) {
EXPECT_TRUE(response->headers.get() != NULL);
EXPECT_EQ(1234, response->headers->GetContentLength());
EXPECT_EQ("HTTP/1.1 404 Not Found", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
std::string server_header;
void* iter = NULL;
@@ -1000,6 +1065,7 @@ TEST_P(HttpNetworkTransactionTest, ReuseConnection) {
EXPECT_TRUE(response->headers.get() != NULL);
EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
std::string response_data;
rv = ReadTransaction(trans.get(), &response_data);
@@ -1181,7 +1247,7 @@ void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest(
};
if (write_failure) {
- ASSERT_TRUE(!read_failure);
+ ASSERT_FALSE(read_failure);
data1_writes[1] = *write_failure;
} else {
ASSERT_TRUE(read_failure);
@@ -1240,6 +1306,126 @@ void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest(
}
}
+void HttpNetworkTransactionTest::PreconnectErrorResendRequestTest(
+ const MockWrite* write_failure,
+ const MockRead* read_failure,
+ bool use_spdy) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.foo.com/");
+ request.load_flags = 0;
+
+ CapturingNetLog net_log;
+ session_deps_.net_log = &net_log;
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ SSLSocketDataProvider ssl1(ASYNC, OK);
+ SSLSocketDataProvider ssl2(ASYNC, OK);
+ if (use_spdy) {
+ ssl1.SetNextProto(GetParam());
+ ssl2.SetNextProto(GetParam());
+ }
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl1);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2);
+
+ // SPDY versions of the request and response.
+ scoped_ptr<SpdyFrame> spdy_request(spdy_util_.ConstructSpdyGet(
+ request.url.spec().c_str(), false, 1, DEFAULT_PRIORITY));
+ scoped_ptr<SpdyFrame> spdy_response(
+ spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> spdy_data(
+ spdy_util_.ConstructSpdyBodyFrame(1, "hello", 5, true));
+
+ // HTTP/1.1 versions of the request and response.
+ const char kHttpRequest[] = "GET / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n\r\n";
+ const char kHttpResponse[] = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n";
+ const char kHttpData[] = "hello";
+
+ std::vector<MockRead> data1_reads;
+ std::vector<MockWrite> data1_writes;
+ if (write_failure) {
+ ASSERT_FALSE(read_failure);
+ data1_writes.push_back(*write_failure);
+ data1_reads.push_back(MockRead(ASYNC, OK));
+ } else {
+ ASSERT_TRUE(read_failure);
+ if (use_spdy) {
+ data1_writes.push_back(CreateMockWrite(*spdy_request));
+ } else {
+ data1_writes.push_back(MockWrite(kHttpRequest));
+ }
+ data1_reads.push_back(*read_failure);
+ }
+
+ StaticSocketDataProvider data1(&data1_reads[0], data1_reads.size(),
+ &data1_writes[0], data1_writes.size());
+ session_deps_.socket_factory->AddSocketDataProvider(&data1);
+
+ std::vector<MockRead> data2_reads;
+ std::vector<MockWrite> data2_writes;
+
+ if (use_spdy) {
+ data2_writes.push_back(CreateMockWrite(*spdy_request, 0, ASYNC));
+
+ data2_reads.push_back(CreateMockRead(*spdy_response, 1, ASYNC));
+ data2_reads.push_back(CreateMockRead(*spdy_data, 2, ASYNC));
+ data2_reads.push_back(MockRead(ASYNC, OK, 3));
+ } else {
+ data2_writes.push_back(
+ MockWrite(ASYNC, kHttpRequest, strlen(kHttpRequest), 0));
+
+ data2_reads.push_back(
+ MockRead(ASYNC, kHttpResponse, strlen(kHttpResponse), 1));
+ data2_reads.push_back(MockRead(ASYNC, kHttpData, strlen(kHttpData), 2));
+ data2_reads.push_back(MockRead(ASYNC, OK, 3));
+ }
+ OrderedSocketData data2(&data2_reads[0], data2_reads.size(),
+ &data2_writes[0], data2_writes.size());
+ session_deps_.socket_factory->AddSocketDataProvider(&data2);
+
+ // Preconnect a socket.
+ net::SSLConfig ssl_config;
+ session->ssl_config_service()->GetSSLConfig(&ssl_config);
+ session->GetNextProtos(&ssl_config.next_protos);
+ session->http_stream_factory()->PreconnectStreams(
+ 1, request, DEFAULT_PRIORITY, ssl_config, ssl_config);
+ // Wait for the preconnect to complete.
+ // TODO(davidben): Some way to wait for an idle socket count might be handy.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+
+ // Make the request.
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ LoadTimingInfo load_timing_info;
+ EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
+ TestLoadTimingNotReused(
+ load_timing_info,
+ CONNECT_TIMING_HAS_DNS_TIMES|CONNECT_TIMING_HAS_SSL_TIMES);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ(kHttpData, response_data);
+}
+
TEST_P(HttpNetworkTransactionTest,
KeepAliveConnectionNotConnectedOnWrite) {
MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
@@ -1256,6 +1442,71 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveConnectionEOF) {
KeepAliveConnectionResendRequestTest(NULL, &read_failure);
}
+// Make sure that on a 408 response (Request Timeout), the request is retried,
+// if the socket was a reused keep alive socket.
+TEST_P(HttpNetworkTransactionTest, KeepAlive408) {
+ MockRead read_failure(SYNCHRONOUS,
+ "HTTP/1.1 408 Request Timeout\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 6\r\n\r\n"
+ "Pickle");
+ KeepAliveConnectionResendRequestTest(NULL, &read_failure);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PreconnectErrorNotConnectedOnWrite) {
+ MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+ PreconnectErrorResendRequestTest(&write_failure, NULL, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorReset) {
+ MockRead read_failure(ASYNC, ERR_CONNECTION_RESET);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorEOF) {
+ MockRead read_failure(SYNCHRONOUS, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorAsyncEOF) {
+ MockRead read_failure(ASYNC, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+// Make sure that on a 408 response (Request Timeout), the request is retried,
+// if the socket was a preconnected (UNUSED_IDLE) socket.
+TEST_P(HttpNetworkTransactionTest, RetryOnIdle408) {
+ MockRead read_failure(SYNCHRONOUS,
+ "HTTP/1.1 408 Request Timeout\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 6\r\n\r\n"
+ "Pickle");
+ KeepAliveConnectionResendRequestTest(NULL, &read_failure);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ SpdyPreconnectErrorNotConnectedOnWrite) {
+ MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+ PreconnectErrorResendRequestTest(&write_failure, NULL, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorReset) {
+ MockRead read_failure(ASYNC, ERR_CONNECTION_RESET);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorEOF) {
+ MockRead read_failure(SYNCHRONOUS, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorAsyncEOF) {
+ MockRead read_failure(ASYNC, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) {
HttpRequestInfo request;
request.method = "GET";
@@ -1308,6 +1559,85 @@ TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionEOF) {
EXPECT_EQ(ERR_EMPTY_RESPONSE, out.rv);
}
+// Test that network access can be deferred and resumed.
+TEST_P(HttpNetworkTransactionTest, ThrottleBeforeNetworkStart) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Defer on OnBeforeNetworkStart.
+ BeforeNetworkStartHandler net_start_handler(true); // defer
+ trans->SetBeforeNetworkStartCallback(
+ base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart,
+ base::Unretained(&net_start_handler)));
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("hello"),
+ MockRead(SYNCHRONOUS, 0),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Should have deferred for network start.
+ EXPECT_TRUE(net_start_handler.observed_before_network_start());
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState());
+ EXPECT_TRUE(trans->GetResponseInfo() == NULL);
+
+ trans->ResumeNetworkStart();
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(trans->GetResponseInfo() != NULL);
+
+ scoped_refptr<IOBufferWithSize> io_buf(new IOBufferWithSize(100));
+ rv = trans->Read(io_buf.get(), io_buf->size(), callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(5, rv);
+ trans.reset();
+}
+
+// Test that network use can be deferred and canceled.
+TEST_P(HttpNetworkTransactionTest, ThrottleAndCancelBeforeNetworkStart) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Defer on OnBeforeNetworkStart.
+ BeforeNetworkStartHandler net_start_handler(true); // defer
+ trans->SetBeforeNetworkStartCallback(
+ base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart,
+ base::Unretained(&net_start_handler)));
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Should have deferred for network start.
+ EXPECT_TRUE(net_start_handler.observed_before_network_start());
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState());
+ EXPECT_TRUE(trans->GetResponseInfo() == NULL);
+}
+
// Next 2 cases (KeepAliveEarlyClose and KeepAliveEarlyClose2) are regression
// tests. There was a bug causing HttpNetworkTransaction to hang in the
// destructor in such situations.
@@ -1569,6 +1899,9 @@ TEST_P(HttpNetworkTransactionTest, BasicAuth) {
EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1));
TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES);
+ int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1));
+ EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes());
+
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
@@ -1591,6 +1924,9 @@ TEST_P(HttpNetworkTransactionTest, BasicAuth) {
load_timing_info2.connect_timing.connect_start);
EXPECT_NE(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
+ int64 reads_size2 = ReadsSize(data_reads2, arraysize(data_reads2));
+ EXPECT_EQ(reads_size1 + reads_size2, trans->GetTotalReceivedBytes());
+
response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
@@ -1633,6 +1969,9 @@ TEST_P(HttpNetworkTransactionTest, DoNotSendAuth) {
rv = callback.WaitForResult();
EXPECT_EQ(0, rv);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, trans->GetTotalReceivedBytes());
+
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
@@ -1730,6 +2069,12 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
EXPECT_EQ(5, response->headers->GetContentLength());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1));
+ EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes());
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
@@ -2683,6 +3028,82 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGet) {
EXPECT_EQ(kUploadData, response_data);
}
+// Verifies that a session which races and wins against the owning transaction
+// (completing prior to host resolution), doesn't fail the transaction.
+// Regression test for crbug.com/334413.
+TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithSessionRace) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ // Configure SPDY proxy server "proxy:70".
+ session_deps_.proxy_service.reset(
+ ProxyService::CreateFixed("https://proxy:70"));
+ CapturingBoundNetLog log;
+ session_deps_.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ // Fetch http://www.google.com/ through the SPDY proxy.
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false));
+ MockWrite spdy_writes[] = {CreateMockWrite(*req)};
+
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> data(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*resp), CreateMockRead(*data), MockRead(ASYNC, 0, 0),
+ };
+
+ DelayedSocketData spdy_data(
+ 1, // wait for one write to finish before reading.
+ spdy_reads,
+ arraysize(spdy_reads),
+ spdy_writes,
+ arraysize(spdy_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&spdy_data);
+
+ SSLSocketDataProvider ssl(ASYNC, OK);
+ ssl.SetNextProto(GetParam());
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback1;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Stall the hostname resolution begun by the transaction.
+ session_deps_.host_resolver->set_synchronous_mode(false);
+ session_deps_.host_resolver->set_ondemand_mode(true);
+
+ int rv = trans->Start(&request, callback1.callback(), log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Race a session to the proxy, which completes first.
+ session_deps_.host_resolver->set_ondemand_mode(false);
+ SpdySessionKey key(
+ HostPortPair("proxy", 70), ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+ base::WeakPtr<SpdySession> spdy_session =
+ CreateSecureSpdySession(session, key, log.bound());
+
+ // Unstall the resolution begun by the transaction.
+ session_deps_.host_resolver->set_ondemand_mode(true);
+ session_deps_.host_resolver->ResolveAllPending();
+
+ EXPECT_FALSE(callback1.have_result());
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ(kUploadData, response_data);
+}
+
// Test a SPDY get through an HTTPS Proxy.
TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) {
HttpRequestInfo request;
@@ -3064,23 +3485,15 @@ TEST_P(HttpNetworkTransactionTest,
spdy_util_.ConstructSpdyWindowUpdate(1, wrapped_get_resp1->size()));
// CONNECT to news.google.com:443 via SPDY.
- const char* const kConnectHeaders2[] = {
- spdy_util_.GetMethodKey(), "CONNECT",
- spdy_util_.GetPathKey(), "news.google.com:443",
- spdy_util_.GetHostKey(), "news.google.com",
- spdy_util_.GetVersionKey(), "HTTP/1.1",
- };
+ SpdySynStreamIR connect2_ir(3);
+ spdy_util_.SetPriority(LOWEST, &connect2_ir);
+ connect2_ir.SetHeader(spdy_util_.GetMethodKey(), "CONNECT");
+ connect2_ir.SetHeader(spdy_util_.GetPathKey(), "news.google.com:443");
+ connect2_ir.SetHeader(spdy_util_.GetHostKey(), "news.google.com");
+ spdy_util_.MaybeAddVersionHeader(&connect2_ir);
scoped_ptr<SpdyFrame> connect2(
- spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- /*compressed*/ false,
- 3,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- kConnectHeaders2,
- arraysize(kConnectHeaders2),
- 0));
+ spdy_util_.CreateFramer(false)->SerializeFrame(connect2_ir));
+
scoped_ptr<SpdyFrame> conn_resp2(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
@@ -3635,6 +4048,10 @@ TEST_P(HttpNetworkTransactionTest, ConnectStatus307) {
ConnectStatusHelper(MockRead("HTTP/1.1 307 Temporary Redirect\r\n"));
}
+TEST_P(HttpNetworkTransactionTest, ConnectStatus308) {
+ ConnectStatusHelper(MockRead("HTTP/1.1 308 Permanent Redirect\r\n"));
+}
+
TEST_P(HttpNetworkTransactionTest, ConnectStatus400) {
ConnectStatusHelper(MockRead("HTTP/1.1 400 Bad Request\r\n"));
}
@@ -7160,7 +7577,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForDirectConnections) {
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
@@ -7224,7 +7641,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
@@ -7295,7 +7712,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) {
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
@@ -7710,7 +8127,7 @@ TEST_P(HttpNetworkTransactionTest, UploadUnreadableFile) {
base::FilePath temp_file;
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file));
std::string temp_file_content("Unreadable file.");
- ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_content.c_str(),
+ ASSERT_TRUE(base::WriteFile(temp_file, temp_file_content.c_str(),
temp_file_content.length()));
ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file));
@@ -7961,8 +8378,8 @@ TEST_P(HttpNetworkTransactionTest, ChangeAuthRealms) {
}
TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.next_protos = SpdyNextProtos();
+ session_deps_.use_alternate_protocols = true;
std::string alternate_protocol_http_header =
GetAlternateProtocolHttpHeader();
@@ -7993,7 +8410,7 @@ TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
EXPECT_EQ(ERR_IO_PENDING, rv);
HostPortPair http_host_port_pair("www.google.com", 80);
- const HttpServerProperties& http_server_properties =
+ HttpServerProperties& http_server_properties =
*session->http_server_properties();
EXPECT_FALSE(
http_server_properties.HasAlternateProtocol(http_host_port_pair));
@@ -8022,7 +8439,7 @@ TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
TEST_P(HttpNetworkTransactionTest,
MarkBrokenAlternateProtocolAndFallback) {
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo request;
request.method = "GET";
@@ -8085,7 +8502,7 @@ TEST_P(HttpNetworkTransactionTest,
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo restricted_port_request;
restricted_port_request.method = "GET";
@@ -8135,7 +8552,7 @@ TEST_P(HttpNetworkTransactionTest,
// on a restricted port (port < 1024) if we set
// enable_user_alternate_protocol_ports.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
session_deps_.enable_user_alternate_protocol_ports = true;
HttpRequestInfo restricted_port_request;
@@ -8184,7 +8601,7 @@ TEST_P(HttpNetworkTransactionTest,
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo restricted_port_request;
restricted_port_request.method = "GET";
@@ -8233,7 +8650,7 @@ TEST_P(HttpNetworkTransactionTest,
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo unrestricted_port_request;
unrestricted_port_request.method = "GET";
@@ -8281,7 +8698,7 @@ TEST_P(HttpNetworkTransactionTest,
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo unrestricted_port_request;
unrestricted_port_request.method = "GET";
@@ -8323,12 +8740,11 @@ TEST_P(HttpNetworkTransactionTest,
EXPECT_EQ(OK, callback.WaitForResult());
}
-TEST_P(HttpNetworkTransactionTest,
- AlternateProtocolUnsafeBlocked) {
+TEST_P(HttpNetworkTransactionTest, AlternateProtocolUnsafeBlocked) {
// Ensure that we're not allowed to redirect traffic via an alternate
// protocol to an unsafe port, and that we resume the second
// HttpStreamFactoryImpl::Job once the alternate protocol request fails.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo request;
request.method = "GET";
@@ -8366,7 +8782,7 @@ TEST_P(HttpNetworkTransactionTest,
EXPECT_EQ(OK, callback.WaitForResult());
// Disable alternate protocol before the asserts.
- HttpStreamFactory::set_use_alternate_protocols(false);
+ // HttpStreamFactory::set_use_alternate_protocols(false);
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
@@ -8379,8 +8795,8 @@ TEST_P(HttpNetworkTransactionTest,
}
TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
@@ -8469,8 +8885,8 @@ TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
}
TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
@@ -8586,8 +9002,8 @@ TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) {
}
TEST_P(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
@@ -8705,8 +9121,8 @@ class CapturingProxyResolver : public ProxyResolver {
TEST_P(HttpNetworkTransactionTest,
UseAlternateProtocolForTunneledNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
ProxyConfig proxy_config;
proxy_config.set_auto_detect(true);
@@ -8827,8 +9243,8 @@ TEST_P(HttpNetworkTransactionTest,
TEST_P(HttpNetworkTransactionTest,
UseAlternateProtocolForNpnSpdyWithExistingSpdySession) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
@@ -8894,7 +9310,7 @@ TEST_P(HttpNetworkTransactionTest,
// Set up an initial SpdySession in the pool to reuse.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> spdy_session =
CreateSecureSpdySession(session, key, BoundNetLog());
@@ -9230,8 +9646,8 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) {
HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
std::string auth_challenge = "Mock realm=proxy";
GURL origin(test_config.proxy_url);
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_PROXY,
origin, BoundNetLog());
auth_handler->SetGenerateExpectation(
@@ -9244,8 +9660,8 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) {
HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
std::string auth_challenge = "Mock realm=server";
GURL origin(test_config.server_url);
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
origin, BoundNetLog());
auth_handler->SetGenerateExpectation(
@@ -9341,8 +9757,8 @@ TEST_P(HttpNetworkTransactionTest, MultiRoundAuth) {
auth_handler->set_connection_based(true);
std::string auth_challenge = "Mock realm=server";
GURL origin("http://www.example.com");
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
origin, BoundNetLog());
auth_factory->AddMockHandler(auth_handler, HttpAuth::AUTH_SERVER);
@@ -9518,10 +9934,11 @@ TEST_P(HttpNetworkTransactionTest, MultiRoundAuth) {
// This tests the case that a request is issued via http instead of spdy after
// npn is negotiated.
TEST_P(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
+ session_deps_.use_alternate_protocols = true;
+ NextProtoVector next_protos;
next_protos.push_back(kProtoHTTP11);
- HttpStreamFactory::SetNextProtos(next_protos);
+ session_deps_.next_protos = next_protos;
+
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
@@ -9582,8 +9999,8 @@ TEST_P(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) {
// Simulate the SSL handshake completing with an NPN negotiation
// followed by an immediate server closing of the socket.
// Fix crash: http://crbug.com/46369
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
@@ -9645,8 +10062,8 @@ class UrlRecordingHttpAuthHandlerMock : public HttpAuthHandlerMock {
TEST_P(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) {
// This test ensures that the URL passed into the proxy is upgraded
// to https when doing an Alternate Protocol upgrade.
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
session_deps_.proxy_service.reset(
ProxyService::CreateFixedFromPacResult("PROXY myproxy:70"));
@@ -9840,6 +10257,56 @@ TEST_P(HttpNetworkTransactionTest, SimpleCancel) {
base::MessageLoop::current()->RunUntilIdle();
}
+// Test that if a transaction is cancelled after receiving the headers, the
+// stream is drained properly and added back to the socket pool. The main
+// purpose of this test is to make sure that an HttpStreamParser can be read
+// from after the HttpNetworkTransaction and the objects it owns have been
+// deleted.
+// See http://crbug.com/368418
+TEST_P(HttpNetworkTransactionTest, CancelAfterHeaders) {
+ MockRead data_reads[] = {
+ MockRead(ASYNC, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, "Content-Length: 2\r\n"),
+ MockRead(ASYNC, "Connection: Keep-Alive\r\n\r\n"),
+ MockRead(ASYNC, "1"),
+ // 2 async reads are necessary to trigger a ReadResponseBody call after the
+ // HttpNetworkTransaction has been deleted.
+ MockRead(ASYNC, "2"),
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING), // Should never read this.
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ HttpNetworkTransaction trans(DEFAULT_PRIORITY, session);
+ TestCompletionCallback callback;
+
+ int rv = trans.Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ callback.WaitForResult();
+
+ const HttpResponseInfo* response = trans.GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ // The transaction and HttpRequestInfo are deleted.
+ }
+
+ // Let the HttpResponseBodyDrainer drain the socket.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Socket should now be idle, waiting to be reused.
+ EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session));
+}
+
// Test a basic GET request through a proxy.
TEST_P(HttpNetworkTransactionTest, ProxyGet) {
session_deps_.proxy_service.reset(
@@ -9887,6 +10354,8 @@ TEST_P(HttpNetworkTransactionTest, ProxyGet) {
EXPECT_EQ(200, response->headers->response_code());
EXPECT_EQ(100, response->headers->GetContentLength());
EXPECT_TRUE(response->was_fetched_via_proxy);
+ EXPECT_TRUE(
+ response->proxy_server.Equals(HostPortPair::FromString("myproxy:70")));
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
LoadTimingInfo load_timing_info;
@@ -9961,6 +10430,8 @@ TEST_P(HttpNetworkTransactionTest, ProxyTunnelGet) {
EXPECT_EQ(100, response->headers->GetContentLength());
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
EXPECT_TRUE(response->was_fetched_via_proxy);
+ EXPECT_TRUE(
+ response->proxy_server.Equals(HostPortPair::FromString("myproxy:70")));
LoadTimingInfo load_timing_info;
EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
@@ -10053,7 +10524,7 @@ TEST_P(HttpNetworkTransactionTest, PreconnectWithExistingSpdySession) {
// Set up an initial SpdySession in the pool to reuse.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> spdy_session =
CreateInsecureSpdySession(session, key, BoundNetLog());
@@ -10130,7 +10601,7 @@ TEST_P(HttpNetworkTransactionTest,
request_info.load_flags = net::LOAD_NORMAL;
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "www.example.com:443";
+ cert_request->host_and_port = HostPortPair("www.example.com", 443);
// [ssl_]data1 contains the data for the first SSL handshake. When a
// CertificateRequest is received for the first time, the handshake will
@@ -10210,8 +10681,8 @@ TEST_P(HttpNetworkTransactionTest,
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Restart the handshake. This will consume ssl_data2, which fails, and
@@ -10222,8 +10693,8 @@ TEST_P(HttpNetworkTransactionTest,
// Ensure that the client certificate is removed from the cache on a
// handshake failure.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
// Ensure that a client certificate is removed from the SSL client auth
@@ -10240,7 +10711,7 @@ TEST_P(HttpNetworkTransactionTest,
request_info.load_flags = net::LOAD_NORMAL;
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "www.example.com:443";
+ cert_request->host_and_port = HostPortPair("www.example.com", 443);
// When TLS False Start is used, SSLClientSocket::Connect() calls will
// return successfully after reading up to the peer's Certificate message.
@@ -10331,8 +10802,8 @@ TEST_P(HttpNetworkTransactionTest,
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Restart the handshake. This will consume ssl_data2, which fails, and
@@ -10343,8 +10814,8 @@ TEST_P(HttpNetworkTransactionTest,
// Ensure that the client certificate is removed from the cache on a
// handshake failure.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
// Ensure that a client certificate is removed from the SSL client auth
@@ -10362,7 +10833,7 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) {
session_deps_.net_log = log.bound().net_log();
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "proxy:70";
+ cert_request->host_and_port = HostPortPair("proxy", 70);
// See ClientAuthCertCache_Direct_NoFalseStart for the explanation of
// [ssl_]data[1-3]. Rather than represending the endpoint
@@ -10425,13 +10896,13 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) {
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("proxy", 70), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Ensure the certificate was NOT cached for the endpoint. This only
// applies to HTTPS requests, but is fine to check for HTTP requests.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
// Restart the handshake. This will consume ssl_data2, which fails, and
// then consume ssl_data3, which should also fail. The result code is
@@ -10441,10 +10912,10 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) {
// Now that the new handshake has failed, ensure that the client
// certificate was removed from the client auth cache.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("proxy:70",
- &client_cert));
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("proxy", 70), &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
}
@@ -10465,8 +10936,8 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) {
#define MAYBE_UseIPConnectionPooling UseIPConnectionPooling
#endif
WRAPPED_TEST_P(HttpNetworkTransactionTest, MAYBE_UseIPConnectionPooling) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a MockCachingHostResolver.
session_deps_.host_resolver.reset(new MockCachingHostResolver());
@@ -10568,8 +11039,8 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest, MAYBE_UseIPConnectionPooling) {
#undef MAYBE_UseIPConnectionPooling
TEST_P(HttpNetworkTransactionTest, UseIPConnectionPoolingAfterResolution) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a MockCachingHostResolver.
session_deps_.host_resolver.reset(new MockCachingHostResolver());
@@ -10710,10 +11181,9 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest,
// prefix doesn't work with parametrized tests).
#if defined(OS_WIN)
return;
-#endif
-
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+#else
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a OneTimeCachingHostResolver.
OneTimeCachingHostResolver host_resolver(HostPortPair("www.gmail.com", 443));
@@ -10813,56 +11283,10 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest,
EXPECT_TRUE(response->was_npn_negotiated);
ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data));
EXPECT_EQ("hello!", response_data);
+#endif
}
#undef MAYBE_UseIPConnectionPoolingWithHostCacheExpiration
-TEST_P(HttpNetworkTransactionTest, ReadPipelineEvictionFallback) {
- MockRead data_reads1[] = {
- MockRead(SYNCHRONOUS, ERR_PIPELINE_EVICTION),
- };
- MockRead data_reads2[] = {
- MockRead("HTTP/1.0 200 OK\r\n\r\n"),
- MockRead("hello world"),
- MockRead(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), NULL, 0);
- StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), NULL, 0);
- StaticSocketDataProvider* data[] = { &data1, &data2 };
-
- SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data));
-
- EXPECT_EQ(OK, out.rv);
- EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
- EXPECT_EQ("hello world", out.response_data);
-}
-
-TEST_P(HttpNetworkTransactionTest, SendPipelineEvictionFallback) {
- MockWrite data_writes1[] = {
- MockWrite(SYNCHRONOUS, ERR_PIPELINE_EVICTION),
- };
- MockWrite data_writes2[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead data_reads2[] = {
- MockRead("HTTP/1.0 200 OK\r\n\r\n"),
- MockRead("hello world"),
- MockRead(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data1(NULL, 0,
- data_writes1, arraysize(data_writes1));
- StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
- data_writes2, arraysize(data_writes2));
- StaticSocketDataProvider* data[] = { &data1, &data2 };
-
- SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data));
-
- EXPECT_EQ(OK, out.rv);
- EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
- EXPECT_EQ("hello world", out.response_data);
-}
-
TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttp) {
const std::string https_url = "https://www.google.com/";
const std::string http_url = "http://www.google.com:443/";
@@ -10953,20 +11377,21 @@ TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) {
LOWEST));
scoped_ptr<SpdyFrame> req1(
spdy_util_.ConstructSpdyGet(https_url.c_str(), false, 1, LOWEST));
-
- // SPDY GET for HTTP URL (through the proxy, but not the tunnel)
scoped_ptr<SpdyFrame> wrapped_req1(
spdy_util_.ConstructWrappedSpdyFrame(req1, 1));
- const char* const headers[] = {
- spdy_util_.GetMethodKey(), "GET",
- spdy_util_.GetPathKey(), spdy_util_.is_spdy2() ? http_url.c_str() : "/",
- spdy_util_.GetHostKey(), "www.google.com:443",
- spdy_util_.GetSchemeKey(), "http",
- spdy_util_.GetVersionKey(), "HTTP/1.1"
- };
- scoped_ptr<SpdyFrame> req2(spdy_util_.ConstructSpdyControlFrame(
- NULL, 0, false, 3, MEDIUM, SYN_STREAM, CONTROL_FLAG_FIN,
- headers, arraysize(headers), 0));
+
+ // SPDY GET for HTTP URL (through the proxy, but not the tunnel).
+ SpdySynStreamIR req2_ir(3);
+ spdy_util_.SetPriority(MEDIUM, &req2_ir);
+ req2_ir.set_fin(true);
+ req2_ir.SetHeader(spdy_util_.GetMethodKey(), "GET");
+ req2_ir.SetHeader(spdy_util_.GetPathKey(),
+ spdy_util_.is_spdy2() ? http_url.c_str() : "/");
+ req2_ir.SetHeader(spdy_util_.GetHostKey(), "www.google.com:443");
+ req2_ir.SetHeader(spdy_util_.GetSchemeKey(), "http");
+ spdy_util_.MaybeAddVersionHeader(&req2_ir);
+ scoped_ptr<SpdyFrame> req2(
+ spdy_util_.CreateFramer(false)->SerializeFrame(req2_ir));
MockWrite writes1[] = {
CreateMockWrite(*connect, 0),
@@ -11058,7 +11483,7 @@ TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) {
}
TEST_P(HttpNetworkTransactionTest, UseSpdySessionForHttpWhenForced) {
- HttpStreamFactory::set_force_spdy_always(true);
+ session_deps_.force_spdy_always = true;
const std::string https_url = "https://www.google.com/";
const std::string http_url = "http://www.google.com:443/";
@@ -11333,7 +11758,7 @@ TEST_P(HttpNetworkTransactionTest, ErrorSocketNotConnected) {
}
TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) {
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.next_protos = SpdyNextProtos();
ClientSocketPoolManager::set_max_sockets_per_group(
HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
ClientSocketPoolManager::set_max_sockets_per_pool(
@@ -11411,7 +11836,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) {
HostPortPair host_port_pair_a("www.a.com", 443);
SpdySessionKey spdy_session_key_a(
- host_port_pair_a, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_a, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_a));
@@ -11443,7 +11868,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) {
HostPortPair host_port_pair_b("www.b.com", 443);
SpdySessionKey spdy_session_key_b(
- host_port_pair_b, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_b, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_b));
HttpRequestInfo request2;
@@ -11471,7 +11896,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) {
HostPortPair host_port_pair_a1("www.a.com", 80);
SpdySessionKey spdy_session_key_a1(
- host_port_pair_a1, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_a1, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_a1));
HttpRequestInfo request3;
@@ -11779,11 +12204,6 @@ class FakeStream : public HttpStreamBase,
return ERR_UNEXPECTED;
}
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
- ADD_FAILURE();
- return NULL;
- }
-
virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
const CompletionCallback& callback) OVERRIDE {
ADD_FAILURE();
@@ -11973,11 +12393,6 @@ class FakeStreamFactory : public HttpStreamFactory {
ADD_FAILURE();
}
- virtual base::Value* PipelineInfoToValue() const OVERRIDE {
- ADD_FAILURE();
- return NULL;
- }
-
virtual const HostMappingRules* GetHostMappingRules() const OVERRIDE {
ADD_FAILURE();
return NULL;
@@ -12289,4 +12704,487 @@ TEST_P(HttpNetworkTransactionTest, CloseSSLSocketOnIdleForHttpRequest2) {
EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session));
}
+TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+// This test makes sure the retry logic doesn't trigger when reading an error
+// response from a server that rejected a POST with a CONNECTION_RESET.
+TEST_P(HttpNetworkTransactionTest,
+ PostReadsErrorResponseAfterResetOnReusedSocket) {
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 Peachy\r\n"
+ "Content-Length: 14\r\n\r\n"),
+ MockRead("first response"),
+ MockRead("HTTP/1.1 400 Not OK\r\n"
+ "Content-Length: 15\r\n\r\n"),
+ MockRead("second response"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+ HttpRequestInfo request1;
+ request1.method = "GET";
+ request1.url = GURL("http://www.foo.com/");
+ request1.load_flags = 0;
+
+ scoped_ptr<HttpTransaction> trans1(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ int rv = trans1->Start(&request1, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+ ASSERT_TRUE(response1 != NULL);
+
+ EXPECT_TRUE(response1->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 Peachy", response1->headers->GetStatusLine());
+
+ std::string response_data1;
+ rv = ReadTransaction(trans1.get(), &response_data1);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("first response", response_data1);
+ // Delete the transaction to release the socket back into the socket pool.
+ trans1.reset();
+
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request2;
+ request2.method = "POST";
+ request2.url = GURL("http://www.foo.com/");
+ request2.upload_data_stream = &upload_data_stream;
+ request2.load_flags = 0;
+
+ scoped_ptr<HttpTransaction> trans2(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ rv = trans2->Start(&request2, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response2 = trans2->GetResponseInfo();
+ ASSERT_TRUE(response2 != NULL);
+
+ EXPECT_TRUE(response2->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 400 Not OK", response2->headers->GetStatusLine());
+
+ std::string response_data2;
+ rv = ReadTransaction(trans2.get(), &response_data2);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("second response", response_data2);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PostReadsErrorResponseAfterResetPartialBodySent) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"
+ "fo"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+// This tests the more common case than the previous test, where headers and
+// body are not merged into a single request.
+TEST_P(HttpNetworkTransactionTest, ChunkedPostReadsErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Transfer-Encoding: chunked\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ // Make sure the headers are sent before adding a chunk. This ensures that
+ // they can't be merged with the body in a single send. Not currently
+ // necessary since a chunked body is never merged with headers, but this makes
+ // the test more future proof.
+ base::RunLoop().RunUntilIdle();
+
+ upload_data_stream.AppendChunk("last chunk", 10, true);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterResetAnd100) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 100 Continue\r\n\r\n"),
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresNonErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 Just Dandy\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PostIgnoresNonErrorResponseAfterResetAnd100) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 100 Continue\r\n\r\n"),
+ MockRead("HTTP/1.0 302 Redirect\r\n"),
+ MockRead("Location: http://somewhere-else.com/\r\n"),
+ MockRead("Content-Length: 0\r\n\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresHttp09ResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP 0.9 rocks!"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresPartial400HeadersAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not a Full Response\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
} // namespace net
diff --git a/chromium/net/http/http_pipelined_connection.h b/chromium/net/http/http_pipelined_connection.h
deleted file mode 100644
index d0d3599e3f1..00000000000
--- a/chromium/net/http/http_pipelined_connection.h
+++ /dev/null
@@ -1,93 +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_HTTP_HTTP_PIPELINED_CONNECTION_H_
-#define NET_HTTP_HTTP_PIPELINED_CONNECTION_H_
-
-#include "net/base/net_export.h"
-#include "net/base/net_log.h"
-#include "net/socket/ssl_client_socket.h"
-
-namespace net {
-
-class BoundNetLog;
-class ClientSocketHandle;
-class HostPortPair;
-class HttpPipelinedStream;
-class ProxyInfo;
-struct SSLConfig;
-
-class NET_EXPORT_PRIVATE HttpPipelinedConnection {
- public:
- enum Feedback {
- OK,
- PIPELINE_SOCKET_ERROR,
- OLD_HTTP_VERSION,
- MUST_CLOSE_CONNECTION,
- AUTHENTICATION_REQUIRED,
- };
-
- class Delegate {
- public:
- // Called when a pipeline has newly available capacity. This may be because
- // the first request has been sent and the pipeline is now active. Or, it
- // may be because a request successfully completed.
- virtual void OnPipelineHasCapacity(HttpPipelinedConnection* pipeline) = 0;
-
- // Called every time a pipeline receives headers. Lets the delegate know if
- // the headers indicate that pipelining can be used.
- virtual void OnPipelineFeedback(HttpPipelinedConnection* pipeline,
- Feedback feedback) = 0;
- };
-
- class Factory {
- public:
- virtual ~Factory() {}
-
- virtual HttpPipelinedConnection* CreateNewPipeline(
- ClientSocketHandle* connection,
- Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) = 0;
- };
-
- virtual ~HttpPipelinedConnection() {}
-
- // Returns a new stream that uses this pipeline.
- virtual HttpPipelinedStream* CreateNewStream() = 0;
-
- // The number of streams currently associated with this pipeline.
- virtual int depth() const = 0;
-
- // True if this pipeline can accept new HTTP requests. False if a fatal error
- // has occurred.
- virtual bool usable() const = 0;
-
- // True if this pipeline has bound one request and is ready for additional
- // requests.
- virtual bool active() const = 0;
-
- // The SSLConfig used to establish this connection.
- virtual const SSLConfig& used_ssl_config() const = 0;
-
- // The ProxyInfo used to establish this connection.
- virtual const ProxyInfo& used_proxy_info() const = 0;
-
- // The BoundNetLog of this pipelined connection.
- virtual const BoundNetLog& net_log() const = 0;
-
- // True if this connection was NPN negotiated.
- virtual bool was_npn_negotiated() const = 0;
-
- // Protocol negotiated with the server.
- virtual NextProto protocol_negotiated() const = 0;
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_CONNECTION_H_
diff --git a/chromium/net/http/http_pipelined_connection_impl.cc b/chromium/net/http/http_pipelined_connection_impl.cc
deleted file mode 100644
index 284b25406d8..00000000000
--- a/chromium/net/http/http_pipelined_connection_impl.cc
+++ /dev/null
@@ -1,845 +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/http/http_pipelined_connection_impl.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
-#include "base/stl_util.h"
-#include "base/values.h"
-#include "net/base/io_buffer.h"
-#include "net/http/http_pipelined_stream.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_response_body_drainer.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_stream_parser.h"
-#include "net/http/http_version.h"
-#include "net/socket/client_socket_handle.h"
-
-namespace net {
-
-namespace {
-
-base::Value* NetLogReceivedHeadersCallback(const NetLog::Source& source,
- const std::string* feedback,
- NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue;
- source.AddToEventParameters(dict);
- dict->SetString("feedback", *feedback);
- return dict;
-}
-
-base::Value* NetLogStreamClosedCallback(const NetLog::Source& source,
- bool not_reusable,
- NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue;
- source.AddToEventParameters(dict);
- dict->SetBoolean("not_reusable", not_reusable);
- return dict;
-}
-
-base::Value* NetLogHostPortPairCallback(const HostPortPair* host_port_pair,
- NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue;
- dict->SetString("host_and_port", host_port_pair->ToString());
- return dict;
-}
-
-} // anonymous namespace
-
-HttpPipelinedConnection*
-HttpPipelinedConnectionImpl::Factory::CreateNewPipeline(
- ClientSocketHandle* connection,
- HttpPipelinedConnection::Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) {
- return new HttpPipelinedConnectionImpl(connection, delegate, origin,
- used_ssl_config, used_proxy_info,
- net_log, was_npn_negotiated,
- protocol_negotiated);
-}
-
-HttpPipelinedConnectionImpl::HttpPipelinedConnectionImpl(
- ClientSocketHandle* connection,
- HttpPipelinedConnection::Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated)
- : delegate_(delegate),
- connection_(connection),
- used_ssl_config_(used_ssl_config),
- used_proxy_info_(used_proxy_info),
- net_log_(BoundNetLog::Make(net_log.net_log(),
- NetLog::SOURCE_HTTP_PIPELINED_CONNECTION)),
- was_npn_negotiated_(was_npn_negotiated),
- protocol_negotiated_(protocol_negotiated),
- read_buf_(new GrowableIOBuffer()),
- next_pipeline_id_(1),
- active_(false),
- usable_(true),
- completed_one_request_(false),
- weak_factory_(this),
- send_next_state_(SEND_STATE_NONE),
- send_still_on_call_stack_(false),
- read_next_state_(READ_STATE_NONE),
- active_read_id_(0),
- read_still_on_call_stack_(false) {
- CHECK(connection_.get());
- net_log_.BeginEvent(
- NetLog::TYPE_HTTP_PIPELINED_CONNECTION,
- base::Bind(&NetLogHostPortPairCallback, &origin));
-}
-
-HttpPipelinedConnectionImpl::~HttpPipelinedConnectionImpl() {
- CHECK_EQ(depth(), 0);
- CHECK(stream_info_map_.empty());
- CHECK(pending_send_request_queue_.empty());
- CHECK(request_order_.empty());
- CHECK_EQ(send_next_state_, SEND_STATE_NONE);
- CHECK_EQ(read_next_state_, READ_STATE_NONE);
- CHECK(!active_send_request_.get());
- CHECK(!active_read_id_);
- if (!usable_) {
- connection_->socket()->Disconnect();
- }
- connection_->Reset();
- net_log_.EndEvent(NetLog::TYPE_HTTP_PIPELINED_CONNECTION);
-}
-
-HttpPipelinedStream* HttpPipelinedConnectionImpl::CreateNewStream() {
- int pipeline_id = next_pipeline_id_++;
- CHECK(pipeline_id);
- HttpPipelinedStream* stream = new HttpPipelinedStream(this, pipeline_id);
- stream_info_map_.insert(std::make_pair(pipeline_id, StreamInfo()));
- return stream;
-}
-
-void HttpPipelinedConnectionImpl::InitializeParser(
- int pipeline_id,
- const HttpRequestInfo* request,
- const BoundNetLog& net_log) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(!stream_info_map_[pipeline_id].parser.get());
- stream_info_map_[pipeline_id].state = STREAM_BOUND;
- stream_info_map_[pipeline_id].parser.reset(new HttpStreamParser(
- connection_.get(), request, read_buf_.get(), net_log));
- stream_info_map_[pipeline_id].source = net_log.source();
-
- // In case our first stream doesn't SendRequest() immediately, we should still
- // allow others to use this pipeline.
- if (pipeline_id == 1) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&HttpPipelinedConnectionImpl::ActivatePipeline,
- weak_factory_.GetWeakPtr()));
- }
-}
-
-void HttpPipelinedConnectionImpl::ActivatePipeline() {
- if (!active_) {
- active_ = true;
- delegate_->OnPipelineHasCapacity(this);
- }
-}
-
-void HttpPipelinedConnectionImpl::OnStreamDeleted(int pipeline_id) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- Close(pipeline_id, false);
-
- if (stream_info_map_[pipeline_id].state != STREAM_CREATED &&
- stream_info_map_[pipeline_id].state != STREAM_UNUSED) {
- CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_CLOSED);
- CHECK(stream_info_map_[pipeline_id].parser.get());
- stream_info_map_[pipeline_id].parser.reset();
- }
- CHECK(!stream_info_map_[pipeline_id].parser.get());
- stream_info_map_.erase(pipeline_id);
-
- delegate_->OnPipelineHasCapacity(this);
-}
-
-int HttpPipelinedConnectionImpl::SendRequest(
- int pipeline_id,
- const std::string& request_line,
- const HttpRequestHeaders& headers,
- HttpResponseInfo* response,
- const CompletionCallback& callback) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_BOUND);
- if (!usable_) {
- return ERR_PIPELINE_EVICTION;
- }
-
- PendingSendRequest* send_request = new PendingSendRequest;
- send_request->pipeline_id = pipeline_id;
- send_request->request_line = request_line;
- send_request->headers = headers;
- send_request->response = response;
- send_request->callback = callback;
- pending_send_request_queue_.push(send_request);
-
- int rv;
- if (send_next_state_ == SEND_STATE_NONE) {
- send_next_state_ = SEND_STATE_START_IMMEDIATELY;
- rv = DoSendRequestLoop(OK);
- } else {
- rv = ERR_IO_PENDING;
- }
- ActivatePipeline();
- return rv;
-}
-
-int HttpPipelinedConnectionImpl::DoSendRequestLoop(int result) {
- int rv = result;
- do {
- SendRequestState state = send_next_state_;
- send_next_state_ = SEND_STATE_NONE;
- switch (state) {
- case SEND_STATE_START_IMMEDIATELY:
- rv = DoStartRequestImmediately(rv);
- break;
- case SEND_STATE_START_NEXT_DEFERRED_REQUEST:
- rv = DoStartNextDeferredRequest(rv);
- break;
- case SEND_STATE_SEND_ACTIVE_REQUEST:
- rv = DoSendActiveRequest(rv);
- break;
- case SEND_STATE_COMPLETE:
- rv = DoSendComplete(rv);
- break;
- case SEND_STATE_EVICT_PENDING_REQUESTS:
- rv = DoEvictPendingSendRequests(rv);
- break;
- default:
- CHECK(false) << "bad send state: " << state;
- rv = ERR_FAILED;
- break;
- }
- } while (rv != ERR_IO_PENDING && send_next_state_ != SEND_STATE_NONE);
- send_still_on_call_stack_ = false;
- return rv;
-}
-
-void HttpPipelinedConnectionImpl::OnSendIOCallback(int result) {
- CHECK(active_send_request_.get());
- DoSendRequestLoop(result);
-}
-
-int HttpPipelinedConnectionImpl::DoStartRequestImmediately(int result) {
- CHECK(!active_send_request_.get());
- CHECK_EQ(static_cast<size_t>(1), pending_send_request_queue_.size());
- // If SendRequest() completes synchronously, then we need to return the value
- // directly to the caller. |send_still_on_call_stack_| will track this.
- // Otherwise, asynchronous completions will notify the caller via callback.
- send_still_on_call_stack_ = true;
- active_send_request_.reset(pending_send_request_queue_.front());
- pending_send_request_queue_.pop();
- send_next_state_ = SEND_STATE_SEND_ACTIVE_REQUEST;
- return OK;
-}
-
-int HttpPipelinedConnectionImpl::DoStartNextDeferredRequest(int result) {
- CHECK(!send_still_on_call_stack_);
- CHECK(!active_send_request_.get());
-
- while (!pending_send_request_queue_.empty()) {
- scoped_ptr<PendingSendRequest> next_request(
- pending_send_request_queue_.front());
- pending_send_request_queue_.pop();
- CHECK(ContainsKey(stream_info_map_, next_request->pipeline_id));
- if (stream_info_map_[next_request->pipeline_id].state != STREAM_CLOSED) {
- active_send_request_.reset(next_request.release());
- send_next_state_ = SEND_STATE_SEND_ACTIVE_REQUEST;
- return OK;
- }
- }
-
- send_next_state_ = SEND_STATE_NONE;
- return OK;
-}
-
-int HttpPipelinedConnectionImpl::DoSendActiveRequest(int result) {
- CHECK(stream_info_map_[active_send_request_->pipeline_id].parser.get());
- int rv = stream_info_map_[active_send_request_->pipeline_id].parser->
- SendRequest(active_send_request_->request_line,
- active_send_request_->headers,
- active_send_request_->response,
- base::Bind(&HttpPipelinedConnectionImpl::OnSendIOCallback,
- base::Unretained(this)));
- stream_info_map_[active_send_request_->pipeline_id].state = STREAM_SENDING;
- send_next_state_ = SEND_STATE_COMPLETE;
- return rv;
-}
-
-int HttpPipelinedConnectionImpl::DoSendComplete(int result) {
- CHECK(active_send_request_.get());
- CHECK_EQ(STREAM_SENDING,
- stream_info_map_[active_send_request_->pipeline_id].state);
-
- request_order_.push(active_send_request_->pipeline_id);
- stream_info_map_[active_send_request_->pipeline_id].state = STREAM_SENT;
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_PIPELINED_CONNECTION_SENT_REQUEST,
- stream_info_map_[active_send_request_->pipeline_id].source.
- ToEventParametersCallback());
-
- if (result == ERR_SOCKET_NOT_CONNECTED && completed_one_request_) {
- result = ERR_PIPELINE_EVICTION;
- }
- if (result < OK) {
- usable_ = false;
- }
-
- if (!send_still_on_call_stack_) {
- QueueUserCallback(active_send_request_->pipeline_id,
- active_send_request_->callback, result, FROM_HERE);
- }
-
- active_send_request_.reset();
-
- if (send_still_on_call_stack_) {
- // It should be impossible for another request to appear on the queue while
- // this send was on the call stack.
- CHECK(pending_send_request_queue_.empty());
- send_next_state_ = SEND_STATE_NONE;
- } else if (!usable_) {
- send_next_state_ = SEND_STATE_EVICT_PENDING_REQUESTS;
- } else {
- send_next_state_ = SEND_STATE_START_NEXT_DEFERRED_REQUEST;
- }
-
- return result;
-}
-
-int HttpPipelinedConnectionImpl::DoEvictPendingSendRequests(int result) {
- while (!pending_send_request_queue_.empty()) {
- scoped_ptr<PendingSendRequest> evicted_send(
- pending_send_request_queue_.front());
- pending_send_request_queue_.pop();
- if (ContainsKey(stream_info_map_, evicted_send->pipeline_id) &&
- stream_info_map_[evicted_send->pipeline_id].state != STREAM_CLOSED) {
- evicted_send->callback.Run(ERR_PIPELINE_EVICTION);
- }
- }
- send_next_state_ = SEND_STATE_NONE;
- return result;
-}
-
-int HttpPipelinedConnectionImpl::ReadResponseHeaders(
- int pipeline_id, const CompletionCallback& callback) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK_EQ(STREAM_SENT, stream_info_map_[pipeline_id].state);
- CHECK(stream_info_map_[pipeline_id].read_headers_callback.is_null());
-
- if (!usable_)
- return ERR_PIPELINE_EVICTION;
-
- stream_info_map_[pipeline_id].state = STREAM_READ_PENDING;
- stream_info_map_[pipeline_id].read_headers_callback = callback;
- if (read_next_state_ == READ_STATE_NONE &&
- pipeline_id == request_order_.front()) {
- read_next_state_ = READ_STATE_START_IMMEDIATELY;
- return DoReadHeadersLoop(OK);
- }
- return ERR_IO_PENDING;
-}
-
-void HttpPipelinedConnectionImpl::StartNextDeferredRead() {
- if (read_next_state_ == READ_STATE_NONE) {
- read_next_state_ = READ_STATE_START_NEXT_DEFERRED_READ;
- DoReadHeadersLoop(OK);
- }
-}
-
-int HttpPipelinedConnectionImpl::DoReadHeadersLoop(int result) {
- int rv = result;
- do {
- ReadHeadersState state = read_next_state_;
- read_next_state_ = READ_STATE_NONE;
- switch (state) {
- case READ_STATE_START_IMMEDIATELY:
- rv = DoStartReadImmediately(rv);
- break;
- case READ_STATE_START_NEXT_DEFERRED_READ:
- rv = DoStartNextDeferredRead(rv);
- break;
- case READ_STATE_READ_HEADERS:
- rv = DoReadHeaders(rv);
- break;
- case READ_STATE_READ_HEADERS_COMPLETE:
- rv = DoReadHeadersComplete(rv);
- break;
- case READ_STATE_WAITING_FOR_CLOSE:
- // This is a holding state. We return instead of continuing to run hte
- // loop. The state will advance when the stream calls Close().
- rv = DoReadWaitForClose(rv);
- read_still_on_call_stack_ = false;
- return rv;
- case READ_STATE_STREAM_CLOSED:
- rv = DoReadStreamClosed();
- break;
- case READ_STATE_EVICT_PENDING_READS:
- rv = DoEvictPendingReadHeaders(rv);
- break;
- case READ_STATE_NONE:
- break;
- default:
- CHECK(false) << "bad read state";
- rv = ERR_FAILED;
- break;
- }
- } while (rv != ERR_IO_PENDING && read_next_state_ != READ_STATE_NONE);
- read_still_on_call_stack_ = false;
- return rv;
-}
-
-void HttpPipelinedConnectionImpl::OnReadIOCallback(int result) {
- DoReadHeadersLoop(result);
-}
-
-int HttpPipelinedConnectionImpl::DoStartReadImmediately(int result) {
- CHECK(!active_read_id_);
- CHECK(!read_still_on_call_stack_);
- CHECK(!request_order_.empty());
- // If ReadResponseHeaders() completes synchronously, then we need to return
- // the value directly to the caller. |read_still_on_call_stack_| will track
- // this. Otherwise, asynchronous completions will notify the caller via
- // callback.
- read_still_on_call_stack_ = true;
- read_next_state_ = READ_STATE_READ_HEADERS;
- active_read_id_ = request_order_.front();
- request_order_.pop();
- return OK;
-}
-
-int HttpPipelinedConnectionImpl::DoStartNextDeferredRead(int result) {
- CHECK(!active_read_id_);
- CHECK(!read_still_on_call_stack_);
-
- if (request_order_.empty()) {
- read_next_state_ = READ_STATE_NONE;
- return OK;
- }
-
- int next_id = request_order_.front();
- CHECK(ContainsKey(stream_info_map_, next_id));
- switch (stream_info_map_[next_id].state) {
- case STREAM_READ_PENDING:
- read_next_state_ = READ_STATE_READ_HEADERS;
- active_read_id_ = next_id;
- request_order_.pop();
- break;
-
- case STREAM_CLOSED:
- // Since nobody will read whatever data is on the pipeline associated with
- // this closed request, we must shut down the rest of the pipeline.
- read_next_state_ = READ_STATE_EVICT_PENDING_READS;
- break;
-
- case STREAM_SENT:
- read_next_state_ = READ_STATE_NONE;
- break;
-
- default:
- CHECK(false) << "Unexpected read state: "
- << stream_info_map_[next_id].state;
- }
-
- return OK;
-}
-
-int HttpPipelinedConnectionImpl::DoReadHeaders(int result) {
- CHECK(active_read_id_);
- CHECK(ContainsKey(stream_info_map_, active_read_id_));
- CHECK_EQ(STREAM_READ_PENDING, stream_info_map_[active_read_id_].state);
- stream_info_map_[active_read_id_].state = STREAM_ACTIVE;
- int rv = stream_info_map_[active_read_id_].parser->ReadResponseHeaders(
- base::Bind(&HttpPipelinedConnectionImpl::OnReadIOCallback,
- base::Unretained(this)));
- read_next_state_ = READ_STATE_READ_HEADERS_COMPLETE;
- return rv;
-}
-
-int HttpPipelinedConnectionImpl::DoReadHeadersComplete(int result) {
- CHECK(active_read_id_);
- CHECK(ContainsKey(stream_info_map_, active_read_id_));
- CHECK_EQ(STREAM_ACTIVE, stream_info_map_[active_read_id_].state);
-
- read_next_state_ = READ_STATE_WAITING_FOR_CLOSE;
- if (result < OK) {
- if (completed_one_request_ &&
- (result == ERR_CONNECTION_CLOSED ||
- result == ERR_EMPTY_RESPONSE ||
- result == ERR_SOCKET_NOT_CONNECTED)) {
- // These usually indicate that pipelining failed on the server side. In
- // that case, we should retry without pipelining.
- result = ERR_PIPELINE_EVICTION;
- }
- usable_ = false;
- }
-
- CheckHeadersForPipelineCompatibility(active_read_id_, result);
-
- if (!read_still_on_call_stack_) {
- QueueUserCallback(active_read_id_,
- stream_info_map_[active_read_id_].read_headers_callback,
- result, FROM_HERE);
- }
-
- return result;
-}
-
-int HttpPipelinedConnectionImpl::DoReadWaitForClose(int result) {
- read_next_state_ = READ_STATE_WAITING_FOR_CLOSE;
- return result;
-}
-
-int HttpPipelinedConnectionImpl::DoReadStreamClosed() {
- CHECK(active_read_id_);
- CHECK(ContainsKey(stream_info_map_, active_read_id_));
- CHECK_EQ(stream_info_map_[active_read_id_].state, STREAM_CLOSED);
- active_read_id_ = 0;
- if (!usable_) {
- // TODO(simonjam): Don't wait this long to evict.
- read_next_state_ = READ_STATE_EVICT_PENDING_READS;
- return OK;
- }
- completed_one_request_ = true;
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&HttpPipelinedConnectionImpl::StartNextDeferredRead,
- weak_factory_.GetWeakPtr()));
- read_next_state_ = READ_STATE_NONE;
- return OK;
-}
-
-int HttpPipelinedConnectionImpl::DoEvictPendingReadHeaders(int result) {
- while (!request_order_.empty()) {
- int evicted_id = request_order_.front();
- request_order_.pop();
- if (!ContainsKey(stream_info_map_, evicted_id)) {
- continue;
- }
- if (stream_info_map_[evicted_id].state == STREAM_READ_PENDING) {
- stream_info_map_[evicted_id].state = STREAM_READ_EVICTED;
- stream_info_map_[evicted_id].read_headers_callback.Run(
- ERR_PIPELINE_EVICTION);
- }
- }
- read_next_state_ = READ_STATE_NONE;
- return result;
-}
-
-void HttpPipelinedConnectionImpl::Close(int pipeline_id,
- bool not_reusable) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_PIPELINED_CONNECTION_STREAM_CLOSED,
- base::Bind(&NetLogStreamClosedCallback,
- stream_info_map_[pipeline_id].source, not_reusable));
- switch (stream_info_map_[pipeline_id].state) {
- case STREAM_CREATED:
- stream_info_map_[pipeline_id].state = STREAM_UNUSED;
- break;
-
- case STREAM_BOUND:
- stream_info_map_[pipeline_id].state = STREAM_CLOSED;
- break;
-
- case STREAM_SENDING:
- usable_ = false;
- stream_info_map_[pipeline_id].state = STREAM_CLOSED;
- active_send_request_.reset();
- send_next_state_ = SEND_STATE_EVICT_PENDING_REQUESTS;
- DoSendRequestLoop(OK);
- break;
-
- case STREAM_SENT:
- case STREAM_READ_PENDING:
- usable_ = false;
- stream_info_map_[pipeline_id].state = STREAM_CLOSED;
- if (!request_order_.empty() &&
- pipeline_id == request_order_.front() &&
- read_next_state_ == READ_STATE_NONE) {
- read_next_state_ = READ_STATE_EVICT_PENDING_READS;
- DoReadHeadersLoop(OK);
- }
- break;
-
- case STREAM_ACTIVE:
- stream_info_map_[pipeline_id].state = STREAM_CLOSED;
- if (not_reusable) {
- usable_ = false;
- }
- read_next_state_ = READ_STATE_STREAM_CLOSED;
- DoReadHeadersLoop(OK);
- break;
-
- case STREAM_READ_EVICTED:
- stream_info_map_[pipeline_id].state = STREAM_CLOSED;
- break;
-
- case STREAM_CLOSED:
- case STREAM_UNUSED:
- // TODO(simonjam): Why is Close() sometimes called twice?
- break;
-
- default:
- CHECK(false);
- break;
- }
-}
-
-int HttpPipelinedConnectionImpl::ReadResponseBody(
- int pipeline_id, IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK_EQ(active_read_id_, pipeline_id);
- CHECK(stream_info_map_[pipeline_id].parser.get());
- return stream_info_map_[pipeline_id].parser->ReadResponseBody(
- buf, buf_len, callback);
-}
-
-UploadProgress HttpPipelinedConnectionImpl::GetUploadProgress(
- int pipeline_id) const {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_.find(pipeline_id)->second.parser.get());
- return stream_info_map_.find(pipeline_id)->second.parser->GetUploadProgress();
-}
-
-HttpResponseInfo* HttpPipelinedConnectionImpl::GetResponseInfo(
- int pipeline_id) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_.find(pipeline_id)->second.parser.get());
- return stream_info_map_.find(pipeline_id)->second.parser->GetResponseInfo();
-}
-
-bool HttpPipelinedConnectionImpl::IsResponseBodyComplete(
- int pipeline_id) const {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_.find(pipeline_id)->second.parser.get());
- return stream_info_map_.find(pipeline_id)->second.parser->
- IsResponseBodyComplete();
-}
-
-bool HttpPipelinedConnectionImpl::CanFindEndOfResponse(int pipeline_id) const {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_.find(pipeline_id)->second.parser.get());
- return stream_info_map_.find(pipeline_id)->second.parser->
- CanFindEndOfResponse();
-}
-
-bool HttpPipelinedConnectionImpl::IsConnectionReused(int pipeline_id) const {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- if (pipeline_id > 1) {
- return true;
- }
- ClientSocketHandle::SocketReuseType reuse_type = connection_->reuse_type();
- return connection_->is_reused() ||
- reuse_type == ClientSocketHandle::UNUSED_IDLE;
-}
-
-void HttpPipelinedConnectionImpl::SetConnectionReused(int pipeline_id) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- connection_->set_is_reused(true);
-}
-
-int64 HttpPipelinedConnectionImpl::GetTotalReceivedBytes(
- int pipeline_id) const {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_.find(pipeline_id)->second.parser.get());
- return stream_info_map_.find(pipeline_id)->second.parser->received_bytes();
-}
-
-bool HttpPipelinedConnectionImpl::GetLoadTimingInfo(
- int pipeline_id, LoadTimingInfo* load_timing_info) const {
- return connection_->GetLoadTimingInfo(IsConnectionReused(pipeline_id),
- load_timing_info);
-}
-
-void HttpPipelinedConnectionImpl::GetSSLInfo(int pipeline_id,
- SSLInfo* ssl_info) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_[pipeline_id].parser.get());
- stream_info_map_[pipeline_id].parser->GetSSLInfo(ssl_info);
-}
-
-void HttpPipelinedConnectionImpl::GetSSLCertRequestInfo(
- int pipeline_id,
- SSLCertRequestInfo* cert_request_info) {
- CHECK(ContainsKey(stream_info_map_, pipeline_id));
- CHECK(stream_info_map_[pipeline_id].parser.get());
- stream_info_map_[pipeline_id].parser->GetSSLCertRequestInfo(
- cert_request_info);
-}
-
-void HttpPipelinedConnectionImpl::Drain(HttpPipelinedStream* stream,
- HttpNetworkSession* session) {
- HttpResponseHeaders* headers = stream->GetResponseInfo()->headers.get();
- if (!stream->CanFindEndOfResponse() || headers->IsChunkEncoded() ||
- !usable_) {
- // TODO(simonjam): Drain chunk-encoded responses if they're relatively
- // common.
- stream->Close(true);
- delete stream;
- return;
- }
- HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(stream);
- drainer->StartWithSize(session, headers->GetContentLength());
- // |drainer| will delete itself when done.
-}
-
-void HttpPipelinedConnectionImpl::CheckHeadersForPipelineCompatibility(
- int pipeline_id,
- int result) {
- if (result < OK) {
- switch (result) {
- // TODO(simonjam): Ignoring specific errors like this may not work.
- // Collect metrics to see if this code is useful.
- case ERR_ABORTED:
- case ERR_INTERNET_DISCONNECTED:
- case ERR_NETWORK_CHANGED:
- // These errors are no fault of the server.
- break;
-
- default:
- ReportPipelineFeedback(pipeline_id, PIPELINE_SOCKET_ERROR);
- break;
- }
- return;
- }
- HttpResponseInfo* info = GetResponseInfo(pipeline_id);
- const HttpVersion required_version(1, 1);
- if (info->headers->GetParsedHttpVersion() < required_version) {
- ReportPipelineFeedback(pipeline_id, OLD_HTTP_VERSION);
- return;
- }
- if (!info->headers->IsKeepAlive() || !CanFindEndOfResponse(pipeline_id)) {
- usable_ = false;
- ReportPipelineFeedback(pipeline_id, MUST_CLOSE_CONNECTION);
- return;
- }
- if (info->headers->HasHeader(
- HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_SERVER))) {
- ReportPipelineFeedback(pipeline_id, AUTHENTICATION_REQUIRED);
- return;
- }
- ReportPipelineFeedback(pipeline_id, OK);
-}
-
-void HttpPipelinedConnectionImpl::ReportPipelineFeedback(int pipeline_id,
- Feedback feedback) {
- std::string feedback_str;
- switch (feedback) {
- case OK:
- feedback_str = "OK";
- break;
-
- case PIPELINE_SOCKET_ERROR:
- feedback_str = "PIPELINE_SOCKET_ERROR";
- break;
-
- case OLD_HTTP_VERSION:
- feedback_str = "OLD_HTTP_VERSION";
- break;
-
- case MUST_CLOSE_CONNECTION:
- feedback_str = "MUST_CLOSE_CONNECTION";
- break;
-
- case AUTHENTICATION_REQUIRED:
- feedback_str = "AUTHENTICATION_REQUIRED";
- break;
-
- default:
- NOTREACHED();
- feedback_str = "UNKNOWN";
- break;
- }
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_PIPELINED_CONNECTION_RECEIVED_HEADERS,
- base::Bind(&NetLogReceivedHeadersCallback,
- stream_info_map_[pipeline_id].source, &feedback_str));
- delegate_->OnPipelineFeedback(this, feedback);
-}
-
-void HttpPipelinedConnectionImpl::QueueUserCallback(
- int pipeline_id, const CompletionCallback& callback, int rv,
- const tracked_objects::Location& from_here) {
- CHECK(stream_info_map_[pipeline_id].pending_user_callback.is_null());
- stream_info_map_[pipeline_id].pending_user_callback = callback;
- base::MessageLoop::current()->PostTask(
- from_here,
- base::Bind(&HttpPipelinedConnectionImpl::FireUserCallback,
- weak_factory_.GetWeakPtr(), pipeline_id, rv));
-}
-
-void HttpPipelinedConnectionImpl::FireUserCallback(int pipeline_id,
- int result) {
- if (ContainsKey(stream_info_map_, pipeline_id)) {
- CHECK(!stream_info_map_[pipeline_id].pending_user_callback.is_null());
- CompletionCallback callback =
- stream_info_map_[pipeline_id].pending_user_callback;
- stream_info_map_[pipeline_id].pending_user_callback.Reset();
- callback.Run(result);
- }
-}
-
-int HttpPipelinedConnectionImpl::depth() const {
- return stream_info_map_.size();
-}
-
-bool HttpPipelinedConnectionImpl::usable() const {
- return usable_;
-}
-
-bool HttpPipelinedConnectionImpl::active() const {
- return active_;
-}
-
-const SSLConfig& HttpPipelinedConnectionImpl::used_ssl_config() const {
- return used_ssl_config_;
-}
-
-const ProxyInfo& HttpPipelinedConnectionImpl::used_proxy_info() const {
- return used_proxy_info_;
-}
-
-const BoundNetLog& HttpPipelinedConnectionImpl::net_log() const {
- return net_log_;
-}
-
-bool HttpPipelinedConnectionImpl::was_npn_negotiated() const {
- return was_npn_negotiated_;
-}
-
-NextProto HttpPipelinedConnectionImpl::protocol_negotiated()
- const {
- return protocol_negotiated_;
-}
-
-HttpPipelinedConnectionImpl::PendingSendRequest::PendingSendRequest()
- : pipeline_id(0),
- response(NULL) {
-}
-
-HttpPipelinedConnectionImpl::PendingSendRequest::~PendingSendRequest() {
-}
-
-HttpPipelinedConnectionImpl::StreamInfo::StreamInfo()
- : state(STREAM_CREATED) {
-}
-
-HttpPipelinedConnectionImpl::StreamInfo::~StreamInfo() {
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_connection_impl.h b/chromium/net/http/http_pipelined_connection_impl.h
deleted file mode 100644
index d558e47eb0b..00000000000
--- a/chromium/net/http/http_pipelined_connection_impl.h
+++ /dev/null
@@ -1,330 +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_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_
-#define NET_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_
-
-#include <map>
-#include <queue>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/location.h"
-#include "base/memory/linked_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_export.h"
-#include "net/base/net_log.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_stream_parser.h"
-#include "net/proxy/proxy_info.h"
-#include "net/ssl/ssl_config_service.h"
-
-namespace net {
-
-class ClientSocketHandle;
-class GrowableIOBuffer;
-class HostPortPair;
-class HttpNetworkSession;
-class HttpRequestHeaders;
-class HttpResponseInfo;
-class IOBuffer;
-struct LoadTimingInfo;
-class SSLCertRequestInfo;
-class SSLInfo;
-
-// This class manages all of the state for a single pipelined connection. It
-// tracks the order that HTTP requests are sent and enforces that the
-// subsequent reads occur in the appropriate order.
-//
-// If an error occurs related to pipelining, ERR_PIPELINE_EVICTION will be
-// returned to the client. This indicates the client should retry the request
-// without pipelining.
-class NET_EXPORT_PRIVATE HttpPipelinedConnectionImpl
- : public HttpPipelinedConnection {
- public:
- class Factory : public HttpPipelinedConnection::Factory {
- public:
- virtual HttpPipelinedConnection* CreateNewPipeline(
- ClientSocketHandle* connection,
- HttpPipelinedConnection::Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) OVERRIDE;
- };
-
- HttpPipelinedConnectionImpl(ClientSocketHandle* connection,
- Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated);
- virtual ~HttpPipelinedConnectionImpl();
-
- // HttpPipelinedConnection interface.
-
- // Used by HttpStreamFactoryImpl and friends.
- virtual HttpPipelinedStream* CreateNewStream() OVERRIDE;
-
- // Used by HttpPipelinedHost.
- virtual int depth() const OVERRIDE;
- virtual bool usable() const OVERRIDE;
- virtual bool active() const OVERRIDE;
-
- // Used by HttpStreamFactoryImpl.
- virtual const SSLConfig& used_ssl_config() const OVERRIDE;
- virtual const ProxyInfo& used_proxy_info() const OVERRIDE;
- virtual const BoundNetLog& net_log() const OVERRIDE;
- virtual bool was_npn_negotiated() const OVERRIDE;
- virtual NextProto protocol_negotiated() const OVERRIDE;
-
- // Used by HttpPipelinedStream.
-
- // Notifies this pipeline that a stream is no longer using it.
- void OnStreamDeleted(int pipeline_id);
-
- // Effective implementation of HttpStream. Note that we don't directly
- // implement that interface. Instead, these functions will be called by the
- // pass-through methods in HttpPipelinedStream.
- void InitializeParser(int pipeline_id,
- const HttpRequestInfo* request,
- const BoundNetLog& net_log);
-
- int SendRequest(int pipeline_id,
- const std::string& request_line,
- const HttpRequestHeaders& headers,
- HttpResponseInfo* response,
- const CompletionCallback& callback);
-
- int ReadResponseHeaders(int pipeline_id,
- const CompletionCallback& callback);
-
- int ReadResponseBody(int pipeline_id,
- IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
-
- void Close(int pipeline_id,
- bool not_reusable);
-
- UploadProgress GetUploadProgress(int pipeline_id) const;
-
- HttpResponseInfo* GetResponseInfo(int pipeline_id);
-
- bool IsResponseBodyComplete(int pipeline_id) const;
-
- bool CanFindEndOfResponse(int pipeline_id) const;
-
- bool IsConnectionReused(int pipeline_id) const;
-
- void SetConnectionReused(int pipeline_id);
-
- int64 GetTotalReceivedBytes(int pipeline_id) const;
-
- bool GetLoadTimingInfo(int pipeline_id,
- LoadTimingInfo* load_timing_info) const;
-
- void GetSSLInfo(int pipeline_id, SSLInfo* ssl_info);
-
- void GetSSLCertRequestInfo(int pipeline_id,
- SSLCertRequestInfo* cert_request_info);
-
- // Attempts to drain the response body for |stream| so that the pipeline may
- // be reused.
- void Drain(HttpPipelinedStream* stream, HttpNetworkSession* session);
-
- private:
- enum StreamState {
- STREAM_CREATED,
- STREAM_BOUND,
- STREAM_SENDING,
- STREAM_SENT,
- STREAM_READ_PENDING,
- STREAM_ACTIVE,
- STREAM_CLOSED,
- STREAM_READ_EVICTED,
- STREAM_UNUSED,
- };
- enum SendRequestState {
- SEND_STATE_START_IMMEDIATELY,
- SEND_STATE_START_NEXT_DEFERRED_REQUEST,
- SEND_STATE_SEND_ACTIVE_REQUEST,
- SEND_STATE_COMPLETE,
- SEND_STATE_EVICT_PENDING_REQUESTS,
- SEND_STATE_NONE,
- };
- enum ReadHeadersState {
- READ_STATE_START_IMMEDIATELY,
- READ_STATE_START_NEXT_DEFERRED_READ,
- READ_STATE_READ_HEADERS,
- READ_STATE_READ_HEADERS_COMPLETE,
- READ_STATE_WAITING_FOR_CLOSE,
- READ_STATE_STREAM_CLOSED,
- READ_STATE_NONE,
- READ_STATE_EVICT_PENDING_READS,
- };
-
- struct PendingSendRequest {
- PendingSendRequest();
- ~PendingSendRequest();
-
- int pipeline_id;
- std::string request_line;
- HttpRequestHeaders headers;
- HttpResponseInfo* response;
- CompletionCallback callback;
- };
-
- struct StreamInfo {
- StreamInfo();
- ~StreamInfo();
-
- linked_ptr<HttpStreamParser> parser;
- CompletionCallback read_headers_callback;
- CompletionCallback pending_user_callback;
- StreamState state;
- NetLog::Source source;
- };
-
- typedef std::map<int, StreamInfo> StreamInfoMap;
-
- // Called after the first request is sent or in a task sometime after the
- // first stream is added to this pipeline. This gives the first request
- // priority to send, but doesn't hold up other requests if it doesn't.
- // When called the first time, notifies the |delegate_| that we can accept new
- // requests.
- void ActivatePipeline();
-
- // Responsible for sending one request at a time and waiting until each
- // comepletes.
- int DoSendRequestLoop(int result);
-
- // Called when an asynchronous Send() completes.
- void OnSendIOCallback(int result);
-
- // Activates the only request in |pending_send_request_queue_|. This should
- // only be called via SendRequest() when the send loop is idle.
- int DoStartRequestImmediately(int result);
-
- // Activates the first request in |pending_send_request_queue_| that hasn't
- // been closed, if any. This is called via DoSendComplete() after a prior
- // request complets.
- int DoStartNextDeferredRequest(int result);
-
- // Sends the active request.
- int DoSendActiveRequest(int result);
-
- // Notifies the user that the send has completed. This may be called directly
- // after SendRequest() for a synchronous request, or it may be called in
- // response to OnSendIOCallback for an asynchronous request.
- int DoSendComplete(int result);
-
- // Evicts all unsent deferred requests. This is called if there is a Send()
- // error or one of our streams informs us the connection is no longer
- // reusable.
- int DoEvictPendingSendRequests(int result);
-
- // Ensures that only the active request's HttpPipelinedSocket can read from
- // the underlying socket until it completes. A HttpPipelinedSocket informs us
- // that it's done by calling Close().
- int DoReadHeadersLoop(int result);
-
- // Called when the pending asynchronous ReadResponseHeaders() completes.
- void OnReadIOCallback(int result);
-
- // Invokes DoStartNextDeferredRead() if the read loop is idle. This is called
- // via a task queued when the previous |active_read_id_| closes its stream
- // after a succesful response.
- void StartNextDeferredRead();
-
- // Activates the next read request immediately. This is called via
- // ReadResponseHeaders() if that stream is at the front of |request_order_|
- // and the read loop is idle.
- int DoStartReadImmediately(int result);
-
- // Activates the next read request in |request_order_| if it's ready to go.
- // This is called via StartNextDeferredRead().
- int DoStartNextDeferredRead(int result);
-
- // Calls ReadResponseHeaders() on the active request's parser.
- int DoReadHeaders(int result);
-
- // Notifies the user that reading the headers has completed. This may happen
- // directly after DoReadNextHeaders() if the response is already available.
- // Otherwise, it is called in response to OnReadIOCallback().
- int DoReadHeadersComplete(int result);
-
- // Halts the read loop until Close() is called by the active stream.
- int DoReadWaitForClose(int result);
-
- // Cleans up the state associated with the active request. Invokes
- // DoReadNextHeaders() in a new task to start the next response. This is
- // called after the active request's HttpPipelinedSocket calls Close().
- int DoReadStreamClosed();
-
- // Removes all pending ReadResponseHeaders() requests from the queue. This may
- // happen if there is an error with the pipeline or one of our
- // HttpPipelinedSockets indicates the connection was suddenly closed.
- int DoEvictPendingReadHeaders(int result);
-
- // Determines if the response headers indicate pipelining will work. This is
- // called every time we receive headers.
- void CheckHeadersForPipelineCompatibility(int pipeline_id, int result);
-
- // Reports back to |delegate_| whether pipelining will work.
- void ReportPipelineFeedback(int pipeline_id, Feedback feedback);
-
- // Posts a task to fire the user's callback in response to SendRequest() or
- // ReadResponseHeaders() completing on an underlying parser. This might be
- // invoked in response to our own IO callbacks, or it may be invoked if the
- // underlying parser completes SendRequest() or ReadResponseHeaders()
- // synchronously, but we've already returned ERR_IO_PENDING to the user's
- // SendRequest() or ReadResponseHeaders() call into us.
- void QueueUserCallback(int pipeline_id,
- const CompletionCallback& callback,
- int rv,
- const tracked_objects::Location& from_here);
-
- // Invokes the callback queued in QueueUserCallback().
- void FireUserCallback(int pipeline_id, int result);
-
- Delegate* delegate_;
- scoped_ptr<ClientSocketHandle> connection_;
- SSLConfig used_ssl_config_;
- ProxyInfo used_proxy_info_;
- BoundNetLog net_log_;
- bool was_npn_negotiated_;
- // Protocol negotiated with the server.
- NextProto protocol_negotiated_;
- scoped_refptr<GrowableIOBuffer> read_buf_;
- int next_pipeline_id_;
- bool active_;
- bool usable_;
- bool completed_one_request_;
- base::WeakPtrFactory<HttpPipelinedConnectionImpl> weak_factory_;
-
- StreamInfoMap stream_info_map_;
-
- std::queue<int> request_order_;
-
- std::queue<PendingSendRequest*> pending_send_request_queue_;
- scoped_ptr<PendingSendRequest> active_send_request_;
- SendRequestState send_next_state_;
- bool send_still_on_call_stack_;
-
- ReadHeadersState read_next_state_;
- int active_read_id_;
- bool read_still_on_call_stack_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpPipelinedConnectionImpl);
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_
diff --git a/chromium/net/http/http_pipelined_connection_impl_unittest.cc b/chromium/net/http/http_pipelined_connection_impl_unittest.cc
deleted file mode 100644
index 296194ecd63..00000000000
--- a/chromium/net/http/http_pipelined_connection_impl_unittest.cc
+++ /dev/null
@@ -1,1597 +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/http/http_pipelined_connection_impl.h"
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "net/base/capturing_net_log.h"
-#include "net/base/io_buffer.h"
-#include "net/base/load_timing_info.h"
-#include "net/base/load_timing_info_test_util.h"
-#include "net/base/net_errors.h"
-#include "net/base/request_priority.h"
-#include "net/http/http_pipelined_stream.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
-#include "net/socket/socket_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::NiceMock;
-using testing::StrEq;
-
-namespace net {
-
-namespace {
-
-// Tests the load timing of a stream that's connected and is not the first
-// request sent on a connection.
-void TestLoadTimingReused(const HttpStream& stream) {
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
-
- EXPECT_TRUE(load_timing_info.socket_reused);
- EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
-
- ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
- ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
-}
-
-// Tests the load timing of a stream that's connected and using a fresh
-// connection.
-void TestLoadTimingNotReused(const HttpStream& stream) {
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
-
- EXPECT_FALSE(load_timing_info.socket_reused);
- EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
-
- ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
- CONNECT_TIMING_HAS_DNS_TIMES);
- ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
-}
-
-class MockPipelineDelegate : public HttpPipelinedConnection::Delegate {
- public:
- MOCK_METHOD1(OnPipelineHasCapacity, void(HttpPipelinedConnection* pipeline));
- MOCK_METHOD2(OnPipelineFeedback, void(
- HttpPipelinedConnection* pipeline,
- HttpPipelinedConnection::Feedback feedback));
-};
-
-class SuddenCloseObserver : public base::MessageLoop::TaskObserver {
- public:
- SuddenCloseObserver(HttpStream* stream, int close_before_task)
- : stream_(stream),
- close_before_task_(close_before_task),
- current_task_(0) { }
-
- virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
- ++current_task_;
- if (current_task_ == close_before_task_) {
- stream_->Close(false);
- base::MessageLoop::current()->RemoveTaskObserver(this);
- }
- }
-
- virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {}
-
- private:
- HttpStream* stream_;
- int close_before_task_;
- int current_task_;
-};
-
-class HttpPipelinedConnectionImplTest : public testing::Test {
- public:
- HttpPipelinedConnectionImplTest()
- : histograms_("a"),
- pool_(1, 1, &histograms_, &factory_),
- origin_("host", 123) {
- }
-
- void TearDown() {
- base::MessageLoop::current()->RunUntilIdle();
- }
-
- void Initialize(MockRead* reads, size_t reads_count,
- MockWrite* writes, size_t writes_count) {
- data_.reset(new DeterministicSocketData(reads, reads_count,
- writes, writes_count));
- data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
- if (reads_count || writes_count) {
- data_->StopAfter(reads_count + writes_count);
- }
- factory_.AddSocketDataProvider(data_.get());
- scoped_refptr<MockTransportSocketParams> params;
- ClientSocketHandle* connection = new ClientSocketHandle;
- // Only give the connection a real NetLog to make sure that LoadTiming uses
- // the connection's ID, rather than the pipeline's. Since pipelines are
- // destroyed when they've responded to all requests, but the connection
- // lives on, this is an important behavior.
- connection->Init("a", params, MEDIUM, CompletionCallback(), &pool_,
- net_log_.bound());
- pipeline_.reset(new HttpPipelinedConnectionImpl(
- connection, &delegate_, origin_, ssl_config_, proxy_info_,
- BoundNetLog(), false, kProtoUnknown));
- }
-
- HttpRequestInfo* GetRequestInfo(const std::string& filename) {
- HttpRequestInfo* request_info = new HttpRequestInfo;
- request_info->url = GURL("http://localhost/" + filename);
- request_info->method = "GET";
- request_info_vector_.push_back(request_info);
- return request_info;
- }
-
- HttpStream* NewTestStream(const std::string& filename) {
- HttpStream* stream = pipeline_->CreateNewStream();
- HttpRequestInfo* request_info = GetRequestInfo(filename);
- int rv = stream->InitializeStream(
- request_info, DEFAULT_PRIORITY, BoundNetLog(), CompletionCallback());
- DCHECK_EQ(OK, rv);
- return stream;
- }
-
- void ExpectResponse(const std::string& expected,
- scoped_ptr<HttpStream>& stream, bool async) {
- scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
-
- if (async) {
- EXPECT_EQ(ERR_IO_PENDING,
- stream->ReadResponseBody(buffer.get(), expected.size(),
- callback_.callback()));
- data_->RunFor(1);
- EXPECT_EQ(static_cast<int>(expected.size()), callback_.WaitForResult());
- } else {
- EXPECT_EQ(static_cast<int>(expected.size()),
- stream->ReadResponseBody(buffer.get(), expected.size(),
- callback_.callback()));
- }
- std::string actual(buffer->data(), expected.size());
- EXPECT_THAT(actual, StrEq(expected));
- }
-
- void TestSyncRequest(scoped_ptr<HttpStream>& stream,
- const std::string& filename) {
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse(filename, stream, false);
-
- stream->Close(false);
- }
-
- CapturingBoundNetLog net_log_;
- DeterministicMockClientSocketFactory factory_;
- ClientSocketPoolHistograms histograms_;
- MockTransportClientSocketPool pool_;
- scoped_ptr<DeterministicSocketData> data_;
-
- HostPortPair origin_;
- SSLConfig ssl_config_;
- ProxyInfo proxy_info_;
- NiceMock<MockPipelineDelegate> delegate_;
- TestCompletionCallback callback_;
- scoped_ptr<HttpPipelinedConnectionImpl> pipeline_;
- ScopedVector<HttpRequestInfo> request_info_vector_;
-};
-
-TEST_F(HttpPipelinedConnectionImplTest, PipelineNotUsed) {
- Initialize(NULL, 0, NULL, 0);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, StreamNotUsed) {
- Initialize(NULL, 0, NULL, 0);
-
- scoped_ptr<HttpStream> stream(pipeline_->CreateNewStream());
-
- stream->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, StreamBoundButNotUsed) {
- Initialize(NULL, 0, NULL, 0);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
-
- TestLoadTimingNotReused(*stream);
- stream->Close(false);
- TestLoadTimingNotReused(*stream);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, SyncSingleRequest) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- TestLoadTimingNotReused(*stream);
- TestSyncRequest(stream, "ok.html");
- TestLoadTimingNotReused(*stream);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AsyncSingleRequest) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(ASYNC, 3, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
- callback_.callback()));
- data_->RunFor(1);
- EXPECT_LE(OK, callback_.WaitForResult());
- TestLoadTimingNotReused(*stream);
-
- EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
- data_->RunFor(2);
- EXPECT_LE(OK, callback_.WaitForResult());
- TestLoadTimingNotReused(*stream);
-
- ExpectResponse("ok.html", stream, true);
- TestLoadTimingNotReused(*stream);
-
- stream->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, LockStepAsyncRequests) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(ASYNC, 4, "ok.html"),
- MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(ASYNC, 7, "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1,
- callback_.callback()));
- TestLoadTimingNotReused(*stream1);
-
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
- callback_.callback()));
- TestLoadTimingReused(*stream2);
-
- data_->RunFor(1);
- EXPECT_LE(OK, callback_.WaitForResult());
- data_->RunFor(1);
- EXPECT_LE(OK, callback_.WaitForResult());
-
- EXPECT_EQ(ERR_IO_PENDING, stream1->ReadResponseHeaders(callback_.callback()));
- EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback()));
-
- data_->RunFor(2);
- EXPECT_LE(OK, callback_.WaitForResult());
-
- ExpectResponse("ok.html", stream1, true);
-
- TestLoadTimingNotReused(*stream1);
- LoadTimingInfo load_timing_info1;
- EXPECT_TRUE(stream1->GetLoadTimingInfo(&load_timing_info1));
- stream1->Close(false);
-
- data_->RunFor(2);
- EXPECT_LE(OK, callback_.WaitForResult());
-
- ExpectResponse("ko.html", stream2, true);
-
- TestLoadTimingReused(*stream2);
- LoadTimingInfo load_timing_info2;
- EXPECT_TRUE(stream2->GetLoadTimingInfo(&load_timing_info2));
- EXPECT_EQ(load_timing_info1.socket_log_id,
- load_timing_info2.socket_log_id);
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, TwoResponsesInOnePacket) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 7\r\n\r\n"
- "ok.html"
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 7\r\n\r\n"
- "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", stream1, false);
- stream1->Close(false);
-
- EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ko.html", stream2, false);
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, SendOrderSwapped) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ko.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 4, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "ko.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- TestSyncRequest(stream2, "ko.html");
- TestSyncRequest(stream1, "ok.html");
- TestLoadTimingNotReused(*stream1);
- TestLoadTimingReused(*stream2);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, ReadOrderSwapped) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
-
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
- EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback()));
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", stream1, false);
-
- stream1->Close(false);
-
- EXPECT_LE(OK, callback_.WaitForResult());
- ExpectResponse("ko.html", stream2, false);
-
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, SendWhileReading) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
-
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
- ExpectResponse("ok.html", stream1, false);
- stream1->Close(false);
-
- EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ko.html", stream2, false);
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AsyncSendWhileAsyncReadBlocked) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, 3, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(ASYNC, 4, "ok.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- TestCompletionCallback callback1;
- std::string expected = "ok.html";
- scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
- EXPECT_EQ(ERR_IO_PENDING,
- stream1->ReadResponseBody(buffer.get(), expected.size(),
- callback1.callback()));
-
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- TestCompletionCallback callback2;
- EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
- callback2.callback()));
-
- data_->RunFor(1);
- EXPECT_LE(OK, callback2.WaitForResult());
- EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback2.callback()));
-
- data_->RunFor(1);
- EXPECT_EQ(static_cast<int>(expected.size()), callback1.WaitForResult());
- std::string actual(buffer->data(), expected.size());
- EXPECT_THAT(actual, StrEq(expected));
- stream1->Close(false);
-
- data_->StopAfter(8);
- EXPECT_LE(OK, callback2.WaitForResult());
- ExpectResponse("ko.html", stream2, false);
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, UnusedStreamAllowsLaterUse) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> unused_stream(NewTestStream("unused.html"));
- unused_stream->Close(false);
-
- scoped_ptr<HttpStream> later_stream(NewTestStream("ok.html"));
- TestSyncRequest(later_stream, "ok.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, UnsentStreamAllowsLaterUse) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 4, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
- MockRead(ASYNC, 3, "ok.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "ko.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
- callback_.callback()));
-
- scoped_ptr<HttpStream> unsent_stream(NewTestStream("unsent.html"));
- HttpRequestHeaders unsent_headers;
- HttpResponseInfo unsent_response;
- EXPECT_EQ(ERR_IO_PENDING, unsent_stream->SendRequest(unsent_headers,
- &unsent_response,
- callback_.callback()));
- unsent_stream->Close(false);
-
- data_->RunFor(1);
- EXPECT_LE(OK, callback_.WaitForResult());
-
- EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
- data_->RunFor(2);
- EXPECT_LE(OK, callback_.WaitForResult());
-
- ExpectResponse("ok.html", stream, true);
-
- stream->Close(false);
-
- data_->StopAfter(8);
- scoped_ptr<HttpStream> later_stream(NewTestStream("ko.html"));
- TestSyncRequest(later_stream, "ko.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FailedSend) {
- MockWrite writes[] = {
- MockWrite(ASYNC, ERR_FAILED),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- scoped_ptr<HttpStream> failed_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
- scoped_ptr<HttpStream> closed_stream(NewTestStream("closed.html"));
- scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- TestCompletionCallback failed_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- failed_stream->SendRequest(headers, &response,
- failed_callback.callback()));
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->SendRequest(headers, &response,
- evicted_callback.callback()));
- EXPECT_EQ(ERR_IO_PENDING, closed_stream->SendRequest(headers, &response,
- callback_.callback()));
- closed_stream->Close(false);
-
- data_->RunFor(1);
- EXPECT_EQ(ERR_FAILED, failed_callback.WaitForResult());
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- rejected_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- failed_stream->Close(true);
- evicted_stream->Close(true);
- rejected_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, ConnectionSuddenlyClosedAfterResponse) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /read_evicted.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 2, "GET /read_rejected.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(ASYNC, OK, 6), // Connection closed message. Not read before the
- // ERR_SOCKET_NOT_CONNECTED.
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> closed_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> read_evicted_stream(
- NewTestStream("read_evicted.html"));
- scoped_ptr<HttpStream> read_rejected_stream(
- NewTestStream("read_rejected.html"));
- scoped_ptr<HttpStream> send_closed_stream(
- NewTestStream("send_closed.html"));
- scoped_ptr<HttpStream> send_evicted_stream(
- NewTestStream("send_evicted.html"));
- scoped_ptr<HttpStream> send_rejected_stream(
- NewTestStream("send_rejected.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, closed_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, read_evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK, read_rejected_stream->SendRequest(headers, &response,
- callback_.callback()));
- TestCompletionCallback send_closed_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- send_closed_stream->SendRequest(headers, &response,
- send_closed_callback.callback()));
- TestCompletionCallback send_evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- send_evicted_stream->SendRequest(headers, &response,
- send_evicted_callback.callback()));
-
- TestCompletionCallback read_evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- read_evicted_stream->ReadResponseHeaders(
- read_evicted_callback.callback()));
-
- EXPECT_EQ(OK, closed_stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", closed_stream, false);
- closed_stream->Close(true);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION, read_evicted_callback.WaitForResult());
- read_evicted_stream->Close(true);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- read_rejected_stream->ReadResponseHeaders(callback_.callback()));
- read_rejected_stream->Close(true);
-
- data_->RunFor(1);
- EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, send_closed_callback.WaitForResult());
- send_closed_stream->Close(true);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION, send_evicted_callback.WaitForResult());
- send_evicted_stream->Close(true);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- send_rejected_stream->SendRequest(headers, &response,
- callback_.callback()));
- send_rejected_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSending) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- TestCompletionCallback aborted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- aborted_stream->SendRequest(headers, &response,
- aborted_callback.callback()));
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->SendRequest(headers, &response,
- evicted_callback.callback()));
-
- aborted_stream->Close(true);
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(true);
- EXPECT_FALSE(aborted_callback.have_result());
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendingSecondRequest) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, 1, "GET /aborts.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- TestCompletionCallback ok_callback;
- EXPECT_EQ(ERR_IO_PENDING, ok_stream->SendRequest(headers, &response,
- ok_callback.callback()));
- TestCompletionCallback aborted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- aborted_stream->SendRequest(headers, &response,
- aborted_callback.callback()));
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->SendRequest(headers, &response,
- evicted_callback.callback()));
-
- data_->RunFor(1);
- EXPECT_LE(OK, ok_callback.WaitForResult());
- base::MessageLoop::current()->RunUntilIdle();
- aborted_stream->Close(true);
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(true);
- EXPECT_FALSE(aborted_callback.have_result());
- ok_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AbortWhileReadingHeaders) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, ERR_FAILED, 2),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
- scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK,
- aborted_stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK,
- evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(ERR_IO_PENDING,
- aborted_stream->ReadResponseHeaders(callback_.callback()));
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- aborted_stream->Close(true);
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(true);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- rejected_stream->SendRequest(headers, &response,
- callback_.callback()));
- rejected_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, PendingResponseAbandoned) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /abandoned.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 2, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 4, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 5, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> abandoned_stream(NewTestStream("abandoned.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK, abandoned_stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
- TestCompletionCallback abandoned_callback;
- EXPECT_EQ(ERR_IO_PENDING, abandoned_stream->ReadResponseHeaders(
- abandoned_callback.callback()));
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- abandoned_stream->Close(false);
-
- ExpectResponse("ok.html", ok_stream, false);
- ok_stream->Close(false);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(true);
- EXPECT_FALSE(evicted_stream->IsConnectionReusable());
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, DisconnectedAfterOneRequestRecovery) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /rejected.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5),
- MockWrite(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 7),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 6),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> rejected_read_stream(NewTestStream("rejected.html"));
- scoped_ptr<HttpStream> evicted_send_stream(NewTestStream("evicted.html"));
- scoped_ptr<HttpStream> rejected_send_stream(NewTestStream("rejected.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, rejected_read_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", ok_stream, false);
- ok_stream->Close(false);
-
- TestCompletionCallback read_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_send_stream->SendRequest(headers, &response,
- read_callback.callback()));
- data_->RunFor(1);
- EXPECT_EQ(ERR_PIPELINE_EVICTION, read_callback.WaitForResult());
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- rejected_read_stream->ReadResponseHeaders(callback_.callback()));
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- rejected_send_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- rejected_read_stream->Close(true);
- rejected_send_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, DisconnectedPendingReadRecovery) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", ok_stream, false);
-
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- ok_stream->Close(false);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeNextReadLoop) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", ok_stream, false);
-
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- ok_stream->Close(false);
- evicted_stream->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeReadCallback) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", ok_stream, false);
-
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- ok_stream->Close(false);
-
- // The posted tasks should be:
- // 1. DoReadHeadersLoop, which will post:
- // 2. InvokeUserCallback
- SuddenCloseObserver observer(evicted_stream.get(), 2);
- base::MessageLoop::current()->AddTaskObserver(&observer);
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_FALSE(evicted_callback.have_result());
-}
-
-class StreamDeleter {
- public:
- StreamDeleter(HttpStream* stream)
- : stream_(stream),
- callback_(base::Bind(&StreamDeleter::OnIOComplete,
- base::Unretained(this))) {
- }
-
- ~StreamDeleter() {
- EXPECT_FALSE(stream_);
- }
-
- const CompletionCallback& callback() { return callback_; }
-
- private:
- void OnIOComplete(int result) {
- stream_->Close(true);
- stream_.reset();
- }
-
- scoped_ptr<HttpStream> stream_;
- CompletionCallback callback_;
-};
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringSendCallback) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- HttpStream* stream(NewTestStream("ok.html"));
-
- StreamDeleter deleter(stream);
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
- deleter.callback()));
- data_->RunFor(1);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringReadCallback) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- HttpStream* stream(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, stream->SendRequest(headers,
- &response, callback_.callback()));
-
- StreamDeleter deleter(stream);
- EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(deleter.callback()));
- data_->RunFor(1);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest,
- CloseCalledDuringReadCallbackWithPendingRead) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /failed.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- HttpStream* failed_stream(NewTestStream("failed.html"));
- HttpStream* evicted_stream(NewTestStream("evicted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, failed_stream->SendRequest(headers, &response,
- callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- StreamDeleter failed_deleter(failed_stream);
- EXPECT_EQ(ERR_IO_PENDING,
- failed_stream->ReadResponseHeaders(failed_deleter.callback()));
- StreamDeleter evicted_deleter(evicted_stream);
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_deleter.callback()));
- data_->RunFor(1);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseOtherDuringReadCallback) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /deleter.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /deleted.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> deleter_stream(NewTestStream("deleter.html"));
- HttpStream* deleted_stream(NewTestStream("deleted.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, deleter_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, deleted_stream->SendRequest(headers,
- &response, callback_.callback()));
-
- StreamDeleter deleter(deleted_stream);
- EXPECT_EQ(ERR_IO_PENDING,
- deleter_stream->ReadResponseHeaders(deleter.callback()));
- EXPECT_EQ(ERR_IO_PENDING,
- deleted_stream->ReadResponseHeaders(callback_.callback()));
- data_->RunFor(1);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeSendCallbackRuns) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, 1, "GET /dummy.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
- scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
-
- scoped_ptr<TestCompletionCallback> close_callback(
- new TestCompletionCallback);
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(ERR_IO_PENDING,
- close_stream->SendRequest(headers,
- &response, close_callback->callback()));
-
- data_->RunFor(1);
- EXPECT_FALSE(close_callback->have_result());
-
- close_stream->Close(false);
- close_stream.reset();
- close_callback.reset();
-
- base::MessageLoop::current()->RunUntilIdle();
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeReadCallbackRuns) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /dummy.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
- scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, close_stream->SendRequest(headers,
- &response, callback_.callback()));
-
- scoped_ptr<TestCompletionCallback> close_callback(
- new TestCompletionCallback);
- EXPECT_EQ(ERR_IO_PENDING,
- close_stream->ReadResponseHeaders(close_callback->callback()));
-
- data_->RunFor(1);
- EXPECT_FALSE(close_callback->have_result());
-
- close_stream->Close(false);
- close_stream.reset();
- close_callback.reset();
-
- base::MessageLoop::current()->RunUntilIdle();
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendQueued) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- TestCompletionCallback callback1;
- EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1,
- callback1.callback()));
-
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- TestCompletionCallback callback2;
- EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
- callback2.callback()));
-
- stream2.reset();
- stream1->Close(true);
-
- EXPECT_FALSE(callback2.have_result());
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, NoGapBetweenCloseAndEviction) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 2, "GET /dummy.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
- scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, close_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- TestCompletionCallback close_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- close_stream->ReadResponseHeaders(close_callback.callback()));
-
- EXPECT_EQ(OK, dummy_stream->SendRequest(headers, &response,
- callback_.callback()));
-
- TestCompletionCallback dummy_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- dummy_stream->ReadResponseHeaders(dummy_callback.callback()));
-
- close_stream->Close(true);
- close_stream.reset();
-
- EXPECT_TRUE(dummy_callback.have_result());
- EXPECT_EQ(ERR_PIPELINE_EVICTION, dummy_callback.WaitForResult());
- dummy_stream->Close(true);
- dummy_stream.reset();
- pipeline_.reset();
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, RecoverFromDrainOnRedirect) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2,
- "HTTP/1.1 302 OK\r\n"
- "Content-Length: 8\r\n\r\n"
- "redirect"),
- MockRead(SYNCHRONOUS, 3,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 7\r\n\r\n"
- "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- stream1.release()->Drain(NULL);
-
- EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
- ExpectResponse("ok.html", stream2, false);
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, EvictAfterDrainOfUnknownSize) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2,
- "HTTP/1.1 302 OK\r\n\r\n"
- "redirect"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- stream1.release()->Drain(NULL);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- stream2->ReadResponseHeaders(callback_.callback()));
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, EvictAfterFailedDrain) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2,
- "HTTP/1.1 302 OK\r\n"
- "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 3),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- stream1.release()->Drain(NULL);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- stream2->ReadResponseHeaders(callback_.callback()));
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, EvictIfDrainingChunkedEncoding) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 2,
- "HTTP/1.1 302 OK\r\n"
- "Transfer-Encoding: chunked\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3,
- "jibberish"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
- scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
-
- HttpRequestHeaders headers1;
- HttpResponseInfo response1;
- EXPECT_EQ(OK, stream1->SendRequest(headers1,
- &response1, callback_.callback()));
- HttpRequestHeaders headers2;
- HttpResponseInfo response2;
- EXPECT_EQ(OK, stream2->SendRequest(headers2,
- &response2, callback_.callback()));
-
-
- EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
- stream1.release()->Drain(NULL);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- stream2->ReadResponseHeaders(callback_.callback()));
- stream2->Close(false);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, EvictionDueToMissingContentLength) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 2, "GET /rejected.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- MockRead(SYNCHRONOUS, OK, 5),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
- scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
- scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
-
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, ok_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, evicted_stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(OK, rejected_stream->SendRequest(headers,
- &response, callback_.callback()));
-
- TestCompletionCallback ok_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- ok_stream->ReadResponseHeaders(ok_callback.callback()));
-
- TestCompletionCallback evicted_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
-
- data_->RunFor(1);
- EXPECT_LE(OK, ok_callback.WaitForResult());
- data_->StopAfter(10);
-
- ExpectResponse("ok.html", ok_stream, false);
- ok_stream->Close(false);
-
- EXPECT_EQ(ERR_PIPELINE_EVICTION,
- rejected_stream->ReadResponseHeaders(callback_.callback()));
- rejected_stream->Close(true);
- EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
- evicted_stream->Close(true);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnSocketError) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_FAILED, 1),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_,
- OnPipelineFeedback(
- pipeline_.get(),
- HttpPipelinedConnection::PIPELINE_SOCKET_ERROR))
- .Times(1);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(ERR_FAILED, stream->ReadResponseHeaders(callback_.callback()));
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoInternetConnection) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED, 1),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_, OnPipelineFeedback(_, _))
- .Times(0);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, stream->SendRequest(headers,
- &response, callback_.callback()));
- EXPECT_EQ(ERR_INTERNET_DISCONNECTED,
- stream->ReadResponseHeaders(callback_.callback()));
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnHttp10) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"),
- MockRead(SYNCHRONOUS, 3, "Connection: keep-alive\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_,
- OnPipelineFeedback(pipeline_.get(),
- HttpPipelinedConnection::OLD_HTTP_VERSION))
- .Times(1);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- TestSyncRequest(stream, "ok.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnMustClose) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"),
- MockRead(SYNCHRONOUS, 3, "Connection: close\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_,
- OnPipelineFeedback(
- pipeline_.get(),
- HttpPipelinedConnection::MUST_CLOSE_CONNECTION))
- .Times(1);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- TestSyncRequest(stream, "ok.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoContentLength) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, 2, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_,
- OnPipelineFeedback(
- pipeline_.get(),
- HttpPipelinedConnection::MUST_CLOSE_CONNECTION))
- .Times(1);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- TestSyncRequest(stream, "ok.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnAuthenticationRequired) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Unauthorized\r\n"),
- MockRead(SYNCHRONOUS, 2, "WWW-Authenticate: NTLM\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "ok.html"),
- };
- Initialize(reads, arraysize(reads), writes, arraysize(writes));
-
- EXPECT_CALL(delegate_,
- OnPipelineFeedback(
- pipeline_.get(),
- HttpPipelinedConnection::AUTHENTICATION_REQUIRED))
- .Times(1);
-
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
- TestSyncRequest(stream, "ok.html");
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacity) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
-
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
- HttpRequestHeaders headers;
- HttpResponseInfo response;
- EXPECT_EQ(OK, stream->SendRequest(headers,
- &response, callback_.callback()));
-
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
- base::MessageLoop::current()->RunUntilIdle();
-
- stream->Close(false);
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
- stream.reset(NULL);
-}
-
-TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacityWithoutSend) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
- };
- Initialize(NULL, 0, writes, arraysize(writes));
-
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
- scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
-
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
- base::MessageLoop::current()->RunUntilIdle();
-
- stream->Close(false);
- EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
- stream.reset(NULL);
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host.cc b/chromium/net/http/http_pipelined_host.cc
deleted file mode 100644
index ca477808e9b..00000000000
--- a/chromium/net/http/http_pipelined_host.cc
+++ /dev/null
@@ -1,17 +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/http/http_pipelined_host.h"
-
-namespace net {
-
-HttpPipelinedHost::Key::Key(const HostPortPair& origin)
- : origin_(origin) {
-}
-
-bool HttpPipelinedHost::Key::operator<(const Key& rhs) const {
- return origin_ < rhs.origin_;
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host.h b/chromium/net/http/http_pipelined_host.h
deleted file mode 100644
index b7732e60a8b..00000000000
--- a/chromium/net/http/http_pipelined_host.h
+++ /dev/null
@@ -1,102 +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_HTTP_HTTP_PIPELINED_HOST_H_
-#define NET_HTTP_HTTP_PIPELINED_HOST_H_
-
-#include "net/base/host_port_pair.h"
-#include "net/base/net_export.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host_capability.h"
-
-namespace base {
-class Value;
-}
-
-namespace net {
-
-class BoundNetLog;
-class ClientSocketHandle;
-class HostPortPair;
-class HttpPipelinedStream;
-class ProxyInfo;
-struct SSLConfig;
-
-// Manages all of the pipelining state for specific host with active pipelined
-// HTTP requests. Manages connection jobs, constructs pipelined streams, and
-// assigns requests to the least loaded pipelined connection.
-class NET_EXPORT_PRIVATE HttpPipelinedHost {
- public:
- class NET_EXPORT_PRIVATE Key {
- public:
- Key(const HostPortPair& origin);
-
- // The host and port associated with this key.
- const HostPortPair& origin() const { return origin_; }
-
- bool operator<(const Key& rhs) const;
-
- private:
- const HostPortPair origin_;
- };
-
- class Delegate {
- public:
- // Called when a pipelined host has no outstanding requests on any of its
- // pipelined connections.
- virtual void OnHostIdle(HttpPipelinedHost* host) = 0;
-
- // Called when a pipelined host has newly available pipeline capacity, like
- // when a request completes.
- virtual void OnHostHasAdditionalCapacity(HttpPipelinedHost* host) = 0;
-
- // Called when a host determines if pipelining can be used.
- virtual void OnHostDeterminedCapability(
- HttpPipelinedHost* host,
- HttpPipelinedHostCapability capability) = 0;
- };
-
- class Factory {
- public:
- virtual ~Factory() {}
-
- // Returns a new HttpPipelinedHost.
- virtual HttpPipelinedHost* CreateNewHost(
- Delegate* delegate, const Key& key,
- HttpPipelinedConnection::Factory* factory,
- HttpPipelinedHostCapability capability,
- bool force_pipelining) = 0;
- };
-
- virtual ~HttpPipelinedHost() {}
-
- // Constructs a new pipeline on |connection| and returns a new
- // HttpPipelinedStream that uses it.
- virtual HttpPipelinedStream* CreateStreamOnNewPipeline(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) = 0;
-
- // Tries to find an existing pipeline with capacity for a new request. If
- // successful, returns a new stream on that pipeline. Otherwise, returns NULL.
- virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() = 0;
-
- // Returns true if we have a pipelined connection that can accept new
- // requests.
- virtual bool IsExistingPipelineAvailable() const = 0;
-
- // Returns a Key that uniquely identifies this host.
- virtual const Key& GetKey() const = 0;
-
- // Creates a Value summary of this host's pipelines. Caller assumes
- // ownership of the returned Value.
- virtual base::Value* PipelineInfoToValue() const = 0;
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_HOST_H_
diff --git a/chromium/net/http/http_pipelined_host_capability.h b/chromium/net/http/http_pipelined_host_capability.h
deleted file mode 100644
index d03a20893ea..00000000000
--- a/chromium/net/http/http_pipelined_host_capability.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2011 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_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_
-#define NET_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_
-
-namespace net {
-
-// These values are serialized in Preferences. Do not change these values and
-// only add new ones at the end.
-enum HttpPipelinedHostCapability {
- PIPELINE_UNKNOWN = 0,
- PIPELINE_INCAPABLE = 1,
- PIPELINE_CAPABLE = 2,
- PIPELINE_PROBABLY_CAPABLE = 3, // We are using pipelining, but haven't
- // processed enough requests to record this
- // host as known to be capable.
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_
diff --git a/chromium/net/http/http_pipelined_host_forced.cc b/chromium/net/http/http_pipelined_host_forced.cc
deleted file mode 100644
index 8059d848d73..00000000000
--- a/chromium/net/http/http_pipelined_host_forced.cc
+++ /dev/null
@@ -1,103 +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/http/http_pipelined_host_forced.h"
-
-#include "base/values.h"
-#include "net/http/http_pipelined_connection_impl.h"
-#include "net/http/http_pipelined_stream.h"
-#include "net/socket/buffered_write_stream_socket.h"
-#include "net/socket/client_socket_handle.h"
-
-namespace net {
-
-HttpPipelinedHostForced::HttpPipelinedHostForced(
- HttpPipelinedHost::Delegate* delegate,
- const Key& key,
- HttpPipelinedConnection::Factory* factory)
- : delegate_(delegate),
- key_(key),
- factory_(factory) {
- if (!factory) {
- factory_.reset(new HttpPipelinedConnectionImpl::Factory());
- }
-}
-
-HttpPipelinedHostForced::~HttpPipelinedHostForced() {
- CHECK(!pipeline_.get());
-}
-
-HttpPipelinedStream* HttpPipelinedHostForced::CreateStreamOnNewPipeline(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) {
- CHECK(!pipeline_.get());
- scoped_ptr<BufferedWriteStreamSocket> buffered_socket(
- new BufferedWriteStreamSocket(connection->PassSocket()));
- connection->SetSocket(buffered_socket.PassAs<StreamSocket>());
- pipeline_.reset(factory_->CreateNewPipeline(
- connection, this, key_.origin(), used_ssl_config, used_proxy_info,
- net_log, was_npn_negotiated, protocol_negotiated));
- return pipeline_->CreateNewStream();
-}
-
-HttpPipelinedStream* HttpPipelinedHostForced::CreateStreamOnExistingPipeline() {
- if (!pipeline_.get()) {
- return NULL;
- }
- return pipeline_->CreateNewStream();
-}
-
-bool HttpPipelinedHostForced::IsExistingPipelineAvailable() const {
- return pipeline_.get() != NULL;
-}
-
-const HttpPipelinedHost::Key& HttpPipelinedHostForced::GetKey() const {
- return key_;
-}
-
-void HttpPipelinedHostForced::OnPipelineEmpty(
- HttpPipelinedConnection* pipeline) {
- CHECK_EQ(pipeline_.get(), pipeline);
- pipeline_.reset();
- delegate_->OnHostIdle(this);
- // WARNING: We'll probably be deleted here.
-}
-
-void HttpPipelinedHostForced::OnPipelineHasCapacity(
- HttpPipelinedConnection* pipeline) {
- CHECK_EQ(pipeline_.get(), pipeline);
- delegate_->OnHostHasAdditionalCapacity(this);
- if (!pipeline->depth()) {
- OnPipelineEmpty(pipeline);
- // WARNING: We might be deleted here.
- }
-}
-
-void HttpPipelinedHostForced::OnPipelineFeedback(
- HttpPipelinedConnection* pipeline,
- HttpPipelinedConnection::Feedback feedback) {
- // We don't care. We always pipeline.
-}
-
-base::Value* HttpPipelinedHostForced::PipelineInfoToValue() const {
- base::ListValue* list_value = new base::ListValue();
- if (pipeline_.get()) {
- base::DictionaryValue* pipeline_dict = new base::DictionaryValue;
- pipeline_dict->SetString("host", key_.origin().ToString());
- pipeline_dict->SetBoolean("forced", true);
- pipeline_dict->SetInteger("depth", pipeline_->depth());
- pipeline_dict->SetInteger("capacity", 1000);
- pipeline_dict->SetBoolean("usable", pipeline_->usable());
- pipeline_dict->SetBoolean("active", pipeline_->active());
- pipeline_dict->SetInteger("source_id", pipeline_->net_log().source().id);
- list_value->Append(pipeline_dict);
- }
- return list_value;
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_forced.h b/chromium/net/http/http_pipelined_host_forced.h
deleted file mode 100644
index 2c3c9159342..00000000000
--- a/chromium/net/http/http_pipelined_host_forced.h
+++ /dev/null
@@ -1,83 +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_HTTP_HTTP_PIPELINED_HOST_FORCED_H_
-#define NET_HTTP_HTTP_PIPELINED_HOST_FORCED_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/net_export.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_host_capability.h"
-
-namespace base {
-class Value;
-}
-
-namespace net {
-
-class BoundNetLog;
-class ClientSocketHandle;
-class HttpPipelinedStream;
-class ProxyInfo;
-struct SSLConfig;
-
-// Manages a single pipelined connection for requests to a host that are forced
-// to use pipelining. Note that this is normally not used. It is intended to
-// test the user's connection for pipelining compatibility.
-class NET_EXPORT_PRIVATE HttpPipelinedHostForced
- : public HttpPipelinedHost,
- public HttpPipelinedConnection::Delegate {
- public:
- HttpPipelinedHostForced(HttpPipelinedHost::Delegate* delegate,
- const Key& key,
- HttpPipelinedConnection::Factory* factory);
- virtual ~HttpPipelinedHostForced();
-
- // HttpPipelinedHost interface
- virtual HttpPipelinedStream* CreateStreamOnNewPipeline(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) OVERRIDE;
-
- virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() OVERRIDE;
-
- virtual bool IsExistingPipelineAvailable() const OVERRIDE;
-
- virtual const Key& GetKey() const OVERRIDE;
-
- virtual base::Value* PipelineInfoToValue() const OVERRIDE;
-
- // HttpPipelinedConnection::Delegate interface
-
- virtual void OnPipelineHasCapacity(
- HttpPipelinedConnection* pipeline) OVERRIDE;
-
- virtual void OnPipelineFeedback(
- HttpPipelinedConnection* pipeline,
- HttpPipelinedConnection::Feedback feedback) OVERRIDE;
-
- private:
- // Called when a pipeline is empty and there are no pending requests. Closes
- // the connection.
- void OnPipelineEmpty(HttpPipelinedConnection* pipeline);
-
- HttpPipelinedHost::Delegate* delegate_;
- const Key key_;
- scoped_ptr<HttpPipelinedConnection> pipeline_;
- scoped_ptr<HttpPipelinedConnection::Factory> factory_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostForced);
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_HOST_FORCED_H_
diff --git a/chromium/net/http/http_pipelined_host_forced_unittest.cc b/chromium/net/http/http_pipelined_host_forced_unittest.cc
deleted file mode 100644
index b86dd96d50a..00000000000
--- a/chromium/net/http/http_pipelined_host_forced_unittest.cc
+++ /dev/null
@@ -1,106 +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/http/http_pipelined_host_forced.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "net/http/http_pipelined_host_test_util.h"
-#include "net/proxy/proxy_info.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/ssl/ssl_config_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::NiceMock;
-using testing::Ref;
-using testing::Return;
-
-namespace net {
-
-namespace {
-
-HttpPipelinedStream* kDummyStream =
- reinterpret_cast<HttpPipelinedStream*>(24);
-
-class HttpPipelinedHostForcedTest : public testing::Test {
- public:
- HttpPipelinedHostForcedTest()
- : key_(HostPortPair("host", 123)),
- factory_(new MockPipelineFactory), // Owned by |host_|.
- host_(new HttpPipelinedHostForced(&delegate_, key_, factory_)) {
- }
-
- MockPipeline* AddTestPipeline() {
- MockPipeline* pipeline = new MockPipeline(0, true, true);
- EXPECT_CALL(*factory_, CreateNewPipeline(&connection_, host_.get(),
- MatchesOrigin(key_.origin()),
- Ref(ssl_config_), Ref(proxy_info_),
- Ref(net_log_), true,
- kProtoSPDY3))
- .Times(1)
- .WillOnce(Return(pipeline));
- EXPECT_CALL(*pipeline, CreateNewStream())
- .Times(1)
- .WillOnce(Return(kDummyStream));
- EXPECT_EQ(kDummyStream, host_->CreateStreamOnNewPipeline(
- &connection_, ssl_config_, proxy_info_, net_log_, true,
- kProtoSPDY3));
- return pipeline;
- }
-
- ClientSocketHandle connection_;
- NiceMock<MockHostDelegate> delegate_;
- HttpPipelinedHost::Key key_;
- MockPipelineFactory* factory_;
- scoped_ptr<HttpPipelinedHostForced> host_;
-
- SSLConfig ssl_config_;
- ProxyInfo proxy_info_;
- BoundNetLog net_log_;
-};
-
-TEST_F(HttpPipelinedHostForcedTest, Delegate) {
- EXPECT_TRUE(key_.origin().Equals(host_->GetKey().origin()));
-}
-
-TEST_F(HttpPipelinedHostForcedTest, SingleUser) {
- EXPECT_FALSE(host_->IsExistingPipelineAvailable());
-
- MockPipeline* pipeline = AddTestPipeline();
- EXPECT_TRUE(host_->IsExistingPipelineAvailable());
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(1);
- host_->OnPipelineHasCapacity(pipeline);
-}
-
-TEST_F(HttpPipelinedHostForcedTest, ReuseExisting) {
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- MockPipeline* pipeline = AddTestPipeline();
- EXPECT_CALL(*pipeline, CreateNewStream())
- .Times(1)
- .WillOnce(Return(kDummyStream));
- EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline());
-
- pipeline->SetState(1, true, true);
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(0);
- host_->OnPipelineHasCapacity(pipeline);
-
- pipeline->SetState(0, true, true);
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(1);
- host_->OnPipelineHasCapacity(pipeline);
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_impl.cc b/chromium/net/http/http_pipelined_host_impl.cc
deleted file mode 100644
index 2a41ca41a85..00000000000
--- a/chromium/net/http/http_pipelined_host_impl.cc
+++ /dev/null
@@ -1,210 +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/http/http_pipelined_host_impl.h"
-
-#include "base/stl_util.h"
-#include "base/values.h"
-#include "net/http/http_pipelined_connection_impl.h"
-#include "net/http/http_pipelined_stream.h"
-
-namespace net {
-
-// TODO(simonjam): Run experiments to see what value minimizes evictions without
-// costing too much performance. Until then, this is just a bad guess.
-static const int kNumKnownSuccessesThreshold = 3;
-
-HttpPipelinedHostImpl::HttpPipelinedHostImpl(
- HttpPipelinedHost::Delegate* delegate,
- const HttpPipelinedHost::Key& key,
- HttpPipelinedConnection::Factory* factory,
- HttpPipelinedHostCapability capability)
- : delegate_(delegate),
- key_(key),
- factory_(factory),
- capability_(capability) {
- if (!factory) {
- factory_.reset(new HttpPipelinedConnectionImpl::Factory());
- }
-}
-
-HttpPipelinedHostImpl::~HttpPipelinedHostImpl() {
- CHECK(pipelines_.empty());
-}
-
-HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnNewPipeline(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) {
- if (capability_ == PIPELINE_INCAPABLE) {
- return NULL;
- }
- HttpPipelinedConnection* pipeline = factory_->CreateNewPipeline(
- connection, this, key_.origin(), used_ssl_config, used_proxy_info,
- net_log, was_npn_negotiated, protocol_negotiated);
- PipelineInfo info;
- pipelines_.insert(std::make_pair(pipeline, info));
- return pipeline->CreateNewStream();
-}
-
-HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnExistingPipeline() {
- HttpPipelinedConnection* available_pipeline = NULL;
- for (PipelineInfoMap::iterator it = pipelines_.begin();
- it != pipelines_.end(); ++it) {
- if (CanPipelineAcceptRequests(it->first) &&
- (!available_pipeline ||
- it->first->depth() < available_pipeline->depth())) {
- available_pipeline = it->first;
- }
- }
- if (!available_pipeline) {
- return NULL;
- }
- return available_pipeline->CreateNewStream();
-}
-
-bool HttpPipelinedHostImpl::IsExistingPipelineAvailable() const {
- for (PipelineInfoMap::const_iterator it = pipelines_.begin();
- it != pipelines_.end(); ++it) {
- if (CanPipelineAcceptRequests(it->first)) {
- return true;
- }
- }
- return false;
-}
-
-const HttpPipelinedHost::Key& HttpPipelinedHostImpl::GetKey() const {
- return key_;
-}
-
-void HttpPipelinedHostImpl::OnPipelineEmpty(HttpPipelinedConnection* pipeline) {
- CHECK(ContainsKey(pipelines_, pipeline));
- pipelines_.erase(pipeline);
- delete pipeline;
- if (pipelines_.empty()) {
- delegate_->OnHostIdle(this);
- // WARNING: We'll probably be deleted here.
- }
-}
-
-void HttpPipelinedHostImpl::OnPipelineHasCapacity(
- HttpPipelinedConnection* pipeline) {
- CHECK(ContainsKey(pipelines_, pipeline));
- if (CanPipelineAcceptRequests(pipeline)) {
- delegate_->OnHostHasAdditionalCapacity(this);
- }
- if (!pipeline->depth()) {
- OnPipelineEmpty(pipeline);
- // WARNING: We might be deleted here.
- }
-}
-
-void HttpPipelinedHostImpl::OnPipelineFeedback(
- HttpPipelinedConnection* pipeline,
- HttpPipelinedConnection::Feedback feedback) {
- CHECK(ContainsKey(pipelines_, pipeline));
- switch (feedback) {
- case HttpPipelinedConnection::OK:
- ++pipelines_[pipeline].num_successes;
- if (capability_ == PIPELINE_UNKNOWN) {
- capability_ = PIPELINE_PROBABLY_CAPABLE;
- NotifyAllPipelinesHaveCapacity();
- } else if (capability_ == PIPELINE_PROBABLY_CAPABLE &&
- pipelines_[pipeline].num_successes >=
- kNumKnownSuccessesThreshold) {
- capability_ = PIPELINE_CAPABLE;
- delegate_->OnHostDeterminedCapability(this, PIPELINE_CAPABLE);
- }
- break;
-
- case HttpPipelinedConnection::PIPELINE_SOCKET_ERROR:
- // Socket errors on the initial request - when no other requests are
- // pipelined - can't be due to pipelining.
- if (pipelines_[pipeline].num_successes > 0 || pipeline->depth() > 1) {
- // TODO(simonjam): This may be needlessly harsh. For example, pogo.com
- // only returns a socket error once after the root document, but is
- // otherwise able to pipeline just fine. Consider being more persistent
- // and only give up on pipelining if we get a couple of failures.
- capability_ = PIPELINE_INCAPABLE;
- delegate_->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE);
- }
- break;
-
- case HttpPipelinedConnection::OLD_HTTP_VERSION:
- case HttpPipelinedConnection::AUTHENTICATION_REQUIRED:
- capability_ = PIPELINE_INCAPABLE;
- delegate_->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE);
- break;
-
- case HttpPipelinedConnection::MUST_CLOSE_CONNECTION:
- break;
- }
-}
-
-int HttpPipelinedHostImpl::GetPipelineCapacity() const {
- int capacity = 0;
- switch (capability_) {
- case PIPELINE_CAPABLE:
- case PIPELINE_PROBABLY_CAPABLE:
- capacity = max_pipeline_depth();
- break;
-
- case PIPELINE_INCAPABLE:
- CHECK(false);
-
- case PIPELINE_UNKNOWN:
- capacity = 1;
- break;
-
- default:
- CHECK(false) << "Unkown pipeline capability: " << capability_;
- }
- return capacity;
-}
-
-bool HttpPipelinedHostImpl::CanPipelineAcceptRequests(
- HttpPipelinedConnection* pipeline) const {
- return capability_ != PIPELINE_INCAPABLE &&
- pipeline->usable() &&
- pipeline->active() &&
- pipeline->depth() < GetPipelineCapacity();
-}
-
-void HttpPipelinedHostImpl::NotifyAllPipelinesHaveCapacity() {
- // Calling OnPipelineHasCapacity() can have side effects that include
- // deleting and removing entries from |pipelines_|.
- PipelineInfoMap pipelines_to_notify = pipelines_;
- for (PipelineInfoMap::iterator it = pipelines_to_notify.begin();
- it != pipelines_to_notify.end(); ++it) {
- if (pipelines_.find(it->first) != pipelines_.end()) {
- OnPipelineHasCapacity(it->first);
- }
- }
-}
-
-base::Value* HttpPipelinedHostImpl::PipelineInfoToValue() const {
- base::ListValue* list_value = new base::ListValue();
- for (PipelineInfoMap::const_iterator it = pipelines_.begin();
- it != pipelines_.end(); ++it) {
- base::DictionaryValue* pipeline_dict = new base::DictionaryValue;
- pipeline_dict->SetString("host", key_.origin().ToString());
- pipeline_dict->SetBoolean("forced", false);
- pipeline_dict->SetInteger("depth", it->first->depth());
- pipeline_dict->SetInteger("capacity", GetPipelineCapacity());
- pipeline_dict->SetBoolean("usable", it->first->usable());
- pipeline_dict->SetBoolean("active", it->first->active());
- pipeline_dict->SetInteger("source_id", it->first->net_log().source().id);
- list_value->Append(pipeline_dict);
- }
- return list_value;
-}
-
-HttpPipelinedHostImpl::PipelineInfo::PipelineInfo()
- : num_successes(0) {
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_impl.h b/chromium/net/http/http_pipelined_host_impl.h
deleted file mode 100644
index e2e53c93c37..00000000000
--- a/chromium/net/http/http_pipelined_host_impl.h
+++ /dev/null
@@ -1,117 +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_HTTP_HTTP_PIPELINED_HOST_IMPL_H_
-#define NET_HTTP_HTTP_PIPELINED_HOST_IMPL_H_
-
-#include <map>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/net_export.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_host_capability.h"
-
-namespace base {
-class Value;
-}
-
-namespace net {
-
-class BoundNetLog;
-class ClientSocketHandle;
-class HttpPipelinedStream;
-class ProxyInfo;
-struct SSLConfig;
-
-// Manages all of the pipelining state for specific host with active pipelined
-// HTTP requests. Manages connection jobs, constructs pipelined streams, and
-// assigns requests to the least loaded pipelined connection.
-class NET_EXPORT_PRIVATE HttpPipelinedHostImpl
- : public HttpPipelinedHost,
- public HttpPipelinedConnection::Delegate {
- public:
- HttpPipelinedHostImpl(HttpPipelinedHost::Delegate* delegate,
- const HttpPipelinedHost::Key& key,
- HttpPipelinedConnection::Factory* factory,
- HttpPipelinedHostCapability capability);
- virtual ~HttpPipelinedHostImpl();
-
- // HttpPipelinedHost interface
- virtual HttpPipelinedStream* CreateStreamOnNewPipeline(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) OVERRIDE;
-
- virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() OVERRIDE;
-
- virtual bool IsExistingPipelineAvailable() const OVERRIDE;
-
- // HttpPipelinedConnection::Delegate interface
-
- // Called when a pipelined connection completes a request. Adds a pending
- // request to the pipeline if the pipeline is still usable.
- virtual void OnPipelineHasCapacity(
- HttpPipelinedConnection* pipeline) OVERRIDE;
-
- virtual void OnPipelineFeedback(
- HttpPipelinedConnection* pipeline,
- HttpPipelinedConnection::Feedback feedback) OVERRIDE;
-
- virtual const Key& GetKey() const OVERRIDE;
-
- // Creates a Value summary of this host's |pipelines_|. Caller assumes
- // ownership of the returned Value.
- virtual base::Value* PipelineInfoToValue() const OVERRIDE;
-
- // Returns the maximum number of in-flight pipelined requests we'll allow on a
- // single connection.
- static int max_pipeline_depth() { return 3; }
-
- private:
- struct PipelineInfo {
- PipelineInfo();
-
- int num_successes;
- };
- typedef std::map<HttpPipelinedConnection*, PipelineInfo> PipelineInfoMap;
-
- // Called when a pipeline is empty and there are no pending requests. Closes
- // the connection.
- void OnPipelineEmpty(HttpPipelinedConnection* pipeline);
-
- // Adds the next pending request to the pipeline if it's still usuable.
- void AddRequestToPipeline(HttpPipelinedConnection* pipeline);
-
- // Returns the current pipeline capacity based on |capability_|. This should
- // not be called if |capability_| is INCAPABLE.
- int GetPipelineCapacity() const;
-
- // Returns true if |pipeline| can handle a new request. This is true if the
- // |pipeline| is active, usable, has capacity, and |capability_| is
- // sufficient.
- bool CanPipelineAcceptRequests(HttpPipelinedConnection* pipeline) const;
-
- // Called when |this| moves from UNKNOWN |capability_| to PROBABLY_CAPABLE.
- // Causes all pipelines to increase capacity to start pipelining.
- void NotifyAllPipelinesHaveCapacity();
-
- HttpPipelinedHost::Delegate* delegate_;
- const Key key_;
- PipelineInfoMap pipelines_;
- scoped_ptr<HttpPipelinedConnection::Factory> factory_;
- HttpPipelinedHostCapability capability_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostImpl);
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_HOST_IMPL_H_
diff --git a/chromium/net/http/http_pipelined_host_impl_unittest.cc b/chromium/net/http/http_pipelined_host_impl_unittest.cc
deleted file mode 100644
index 2658472138d..00000000000
--- a/chromium/net/http/http_pipelined_host_impl_unittest.cc
+++ /dev/null
@@ -1,308 +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/http/http_pipelined_host_impl.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host_test_util.h"
-#include "net/proxy/proxy_info.h"
-#include "net/ssl/ssl_config_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::NiceMock;
-using testing::Ref;
-using testing::Return;
-using testing::ReturnNull;
-
-namespace net {
-
-namespace {
-
-ClientSocketHandle* kDummyConnection =
- reinterpret_cast<ClientSocketHandle*>(84);
-HttpPipelinedStream* kDummyStream =
- reinterpret_cast<HttpPipelinedStream*>(42);
-
-class HttpPipelinedHostImplTest : public testing::Test {
- public:
- HttpPipelinedHostImplTest()
- : key_(HostPortPair("host", 123)),
- factory_(new MockPipelineFactory), // Owned by host_.
- host_(new HttpPipelinedHostImpl(&delegate_, key_, factory_,
- PIPELINE_CAPABLE)) {
- }
-
- void SetCapability(HttpPipelinedHostCapability capability) {
- factory_ = new MockPipelineFactory;
- host_.reset(new HttpPipelinedHostImpl(
- &delegate_, key_, factory_, capability));
- }
-
- MockPipeline* AddTestPipeline(int depth, bool usable, bool active) {
- MockPipeline* pipeline = new MockPipeline(depth, usable, active);
- EXPECT_CALL(*factory_, CreateNewPipeline(kDummyConnection, host_.get(),
- MatchesOrigin(key_.origin()),
- Ref(ssl_config_), Ref(proxy_info_),
- Ref(net_log_), true,
- kProtoSPDY3))
- .Times(1)
- .WillOnce(Return(pipeline));
- EXPECT_CALL(*pipeline, CreateNewStream())
- .Times(1)
- .WillOnce(Return(kDummyStream));
- EXPECT_EQ(kDummyStream, host_->CreateStreamOnNewPipeline(
- kDummyConnection, ssl_config_, proxy_info_, net_log_, true,
- kProtoSPDY3));
- return pipeline;
- }
-
- void ClearTestPipeline(MockPipeline* pipeline) {
- pipeline->SetState(0, true, true);
- host_->OnPipelineHasCapacity(pipeline);
- }
-
- NiceMock<MockHostDelegate> delegate_;
- HttpPipelinedHost::Key key_;
- MockPipelineFactory* factory_;
- scoped_ptr<HttpPipelinedHostImpl> host_;
-
- SSLConfig ssl_config_;
- ProxyInfo proxy_info_;
- BoundNetLog net_log_;
-};
-
-TEST_F(HttpPipelinedHostImplTest, Delegate) {
- EXPECT_TRUE(key_.origin().Equals(host_->GetKey().origin()));
-}
-
-TEST_F(HttpPipelinedHostImplTest, OnUnusablePipelineHasCapacity) {
- MockPipeline* pipeline = AddTestPipeline(0, false, true);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(0);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(1);
- host_->OnPipelineHasCapacity(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, OnUsablePipelineHasCapacity) {
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(0);
-
- host_->OnPipelineHasCapacity(pipeline);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_, OnHostIdle(host_.get()))
- .Times(1);
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, IgnoresUnusablePipeline) {
- MockPipeline* pipeline = AddTestPipeline(1, false, true);
-
- EXPECT_FALSE(host_->IsExistingPipelineAvailable());
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, IgnoresInactivePipeline) {
- MockPipeline* pipeline = AddTestPipeline(1, true, false);
-
- EXPECT_FALSE(host_->IsExistingPipelineAvailable());
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, IgnoresFullPipeline) {
- MockPipeline* pipeline = AddTestPipeline(
- HttpPipelinedHostImpl::max_pipeline_depth(), true, true);
-
- EXPECT_FALSE(host_->IsExistingPipelineAvailable());
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, PicksLeastLoadedPipeline) {
- MockPipeline* full_pipeline = AddTestPipeline(
- HttpPipelinedHostImpl::max_pipeline_depth(), true, true);
- MockPipeline* usable_pipeline = AddTestPipeline(
- HttpPipelinedHostImpl::max_pipeline_depth() - 1, true, true);
- MockPipeline* empty_pipeline = AddTestPipeline(0, true, true);
-
- EXPECT_TRUE(host_->IsExistingPipelineAvailable());
- EXPECT_CALL(*empty_pipeline, CreateNewStream())
- .Times(1)
- .WillOnce(ReturnNull());
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- ClearTestPipeline(full_pipeline);
- ClearTestPipeline(usable_pipeline);
- ClearTestPipeline(empty_pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, OpensUpOnPipelineSuccess) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK);
-
- EXPECT_CALL(*pipeline, CreateNewStream())
- .Times(1)
- .WillOnce(Return(kDummyStream));
- EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline());
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, OpensAllPipelinesOnPipelineSuccess) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline1 = AddTestPipeline(1, false, true);
- MockPipeline* pipeline2 = AddTestPipeline(1, true, true);
-
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- host_->OnPipelineFeedback(pipeline1, HttpPipelinedConnection::OK);
-
- EXPECT_CALL(*pipeline2, CreateNewStream())
- .Times(1)
- .WillOnce(Return(kDummyStream));
- EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline());
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(2);
- ClearTestPipeline(pipeline1);
- ClearTestPipeline(pipeline2);
-}
-
-TEST_F(HttpPipelinedHostImplTest, ShutsDownOnOldVersion) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(0);
- EXPECT_CALL(delegate_,
- OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE))
- .Times(1);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::OLD_HTTP_VERSION);
-
- ClearTestPipeline(pipeline);
- EXPECT_EQ(NULL, host_->CreateStreamOnNewPipeline(
- kDummyConnection, ssl_config_, proxy_info_, net_log_, true,
- kProtoSPDY3));
-}
-
-TEST_F(HttpPipelinedHostImplTest, ShutsDownOnAuthenticationRequired) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(0);
- EXPECT_CALL(delegate_,
- OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE))
- .Times(1);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::AUTHENTICATION_REQUIRED);
-
- ClearTestPipeline(pipeline);
- EXPECT_EQ(NULL, host_->CreateStreamOnNewPipeline(
- kDummyConnection, ssl_config_, proxy_info_, net_log_, true,
- kProtoSPDY3));
-}
-
-TEST_F(HttpPipelinedHostImplTest, ConnectionCloseHasNoEffect) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(0);
- EXPECT_CALL(delegate_, OnHostDeterminedCapability(host_.get(), _))
- .Times(0);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::MUST_CLOSE_CONNECTION);
- EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline());
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, SuccessesLeadToCapable) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- EXPECT_CALL(delegate_,
- OnHostDeterminedCapability(host_.get(), PIPELINE_CAPABLE))
- .Times(1);
- host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK);
-
- pipeline->SetState(3, true, true);
- host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK);
- host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, IgnoresSocketErrorOnFirstRequest) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(1, true, true);
-
- EXPECT_CALL(delegate_, OnHostDeterminedCapability(host_.get(), _))
- .Times(0);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::PIPELINE_SOCKET_ERROR);
-
- EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get()))
- .Times(1);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::OK);
-
- EXPECT_CALL(delegate_,
- OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE))
- .Times(1);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::PIPELINE_SOCKET_ERROR);
-
- ClearTestPipeline(pipeline);
-}
-
-TEST_F(HttpPipelinedHostImplTest, HeedsSocketErrorOnFirstRequestWithPipeline) {
- SetCapability(PIPELINE_UNKNOWN);
- MockPipeline* pipeline = AddTestPipeline(2, true, true);
-
- EXPECT_CALL(delegate_,
- OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE))
- .Times(1);
- host_->OnPipelineFeedback(pipeline,
- HttpPipelinedConnection::PIPELINE_SOCKET_ERROR);
-
- ClearTestPipeline(pipeline);
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_pool.cc b/chromium/net/http/http_pipelined_host_pool.cc
deleted file mode 100644
index ee37e74c29d..00000000000
--- a/chromium/net/http/http_pipelined_host_pool.cc
+++ /dev/null
@@ -1,145 +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/http/http_pipelined_host_pool.h"
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/values.h"
-#include "net/http/http_pipelined_host_capability.h"
-#include "net/http/http_pipelined_host_forced.h"
-#include "net/http/http_pipelined_host_impl.h"
-#include "net/http/http_server_properties.h"
-
-namespace net {
-
-class HttpPipelinedHostImplFactory : public HttpPipelinedHost::Factory {
- public:
- virtual HttpPipelinedHost* CreateNewHost(
- HttpPipelinedHost::Delegate* delegate,
- const HttpPipelinedHost::Key& key,
- HttpPipelinedConnection::Factory* factory,
- HttpPipelinedHostCapability capability,
- bool force_pipelining) OVERRIDE {
- if (force_pipelining) {
- return new HttpPipelinedHostForced(delegate, key, factory);
- } else {
- return new HttpPipelinedHostImpl(delegate, key, factory, capability);
- }
- }
-};
-
-HttpPipelinedHostPool::HttpPipelinedHostPool(
- Delegate* delegate,
- HttpPipelinedHost::Factory* factory,
- const base::WeakPtr<HttpServerProperties>& http_server_properties,
- bool force_pipelining)
- : delegate_(delegate),
- factory_(factory),
- http_server_properties_(http_server_properties),
- force_pipelining_(force_pipelining) {
- if (!factory) {
- factory_.reset(new HttpPipelinedHostImplFactory);
- }
-}
-
-HttpPipelinedHostPool::~HttpPipelinedHostPool() {
- CHECK(host_map_.empty());
-}
-
-bool HttpPipelinedHostPool::IsKeyEligibleForPipelining(
- const HttpPipelinedHost::Key& key) {
- HttpPipelinedHostCapability capability =
- http_server_properties_->GetPipelineCapability(key.origin());
- return capability != PIPELINE_INCAPABLE;
-}
-
-HttpPipelinedStream* HttpPipelinedHostPool::CreateStreamOnNewPipeline(
- const HttpPipelinedHost::Key& key,
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated) {
- HttpPipelinedHost* host = GetPipelinedHost(key, true);
- if (!host) {
- return NULL;
- }
- return host->CreateStreamOnNewPipeline(connection, used_ssl_config,
- used_proxy_info, net_log,
- was_npn_negotiated,
- protocol_negotiated);
-}
-
-HttpPipelinedStream* HttpPipelinedHostPool::CreateStreamOnExistingPipeline(
- const HttpPipelinedHost::Key& key) {
- HttpPipelinedHost* host = GetPipelinedHost(key, false);
- if (!host) {
- return NULL;
- }
- return host->CreateStreamOnExistingPipeline();
-}
-
-bool HttpPipelinedHostPool::IsExistingPipelineAvailableForKey(
- const HttpPipelinedHost::Key& key) {
- HttpPipelinedHost* host = GetPipelinedHost(key, false);
- if (!host) {
- return false;
- }
- return host->IsExistingPipelineAvailable();
-}
-
-HttpPipelinedHost* HttpPipelinedHostPool::GetPipelinedHost(
- const HttpPipelinedHost::Key& key, bool create_if_not_found) {
- HostMap::iterator host_it = host_map_.find(key);
- if (host_it != host_map_.end()) {
- CHECK(host_it->second);
- return host_it->second;
- } else if (!create_if_not_found) {
- return NULL;
- }
-
- HttpPipelinedHostCapability capability =
- http_server_properties_->GetPipelineCapability(key.origin());
- if (capability == PIPELINE_INCAPABLE) {
- return NULL;
- }
-
- HttpPipelinedHost* host = factory_->CreateNewHost(
- this, key, NULL, capability, force_pipelining_);
- host_map_[key] = host;
- return host;
-}
-
-void HttpPipelinedHostPool::OnHostIdle(HttpPipelinedHost* host) {
- const HttpPipelinedHost::Key& key = host->GetKey();
- CHECK(ContainsKey(host_map_, key));
- host_map_.erase(key);
- delete host;
-}
-
-void HttpPipelinedHostPool::OnHostHasAdditionalCapacity(
- HttpPipelinedHost* host) {
- delegate_->OnHttpPipelinedHostHasAdditionalCapacity(host);
-}
-
-void HttpPipelinedHostPool::OnHostDeterminedCapability(
- HttpPipelinedHost* host,
- HttpPipelinedHostCapability capability) {
- http_server_properties_->SetPipelineCapability(host->GetKey().origin(),
- capability);
-}
-
-base::Value* HttpPipelinedHostPool::PipelineInfoToValue() const {
- base::ListValue* list = new base::ListValue();
- for (HostMap::const_iterator it = host_map_.begin();
- it != host_map_.end(); ++it) {
- base::Value* value = it->second->PipelineInfoToValue();
- list->Append(value);
- }
- return list;
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_pool.h b/chromium/net/http/http_pipelined_host_pool.h
deleted file mode 100644
index 45cb38cedf6..00000000000
--- a/chromium/net/http/http_pipelined_host_pool.h
+++ /dev/null
@@ -1,102 +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_HTTP_HTTP_PIPELINED_HOST_POOL_H_
-#define NET_HTTP_HTTP_PIPELINED_HOST_POOL_H_
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_host_capability.h"
-
-namespace base {
-class Value;
-}
-
-namespace net {
-
-class HostPortPair;
-class HttpPipelinedStream;
-class HttpServerProperties;
-
-// Manages all of the pipelining state for specific host with active pipelined
-// HTTP requests. Manages connection jobs, constructs pipelined streams, and
-// assigns requests to the least loaded pipelined connection.
-class NET_EXPORT_PRIVATE HttpPipelinedHostPool
- : public HttpPipelinedHost::Delegate {
- public:
- class Delegate {
- public:
- // Called when a HttpPipelinedHost has new capacity. Attempts to allocate
- // any pending pipeline-capable requests to pipelines.
- virtual void OnHttpPipelinedHostHasAdditionalCapacity(
- HttpPipelinedHost* host) = 0;
- };
-
- HttpPipelinedHostPool(
- Delegate* delegate,
- HttpPipelinedHost::Factory* factory,
- const base::WeakPtr<HttpServerProperties>& http_server_properties,
- bool force_pipelining);
- virtual ~HttpPipelinedHostPool();
-
- // Returns true if pipelining might work for |key|. Generally, this returns
- // true, unless |key| is known to have failed pipelining recently.
- bool IsKeyEligibleForPipelining(const HttpPipelinedHost::Key& key);
-
- // Constructs a new pipeline on |connection| and returns a new
- // HttpPipelinedStream that uses it.
- HttpPipelinedStream* CreateStreamOnNewPipeline(
- const HttpPipelinedHost::Key& key,
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated);
-
- // Tries to find an existing pipeline with capacity for a new request. If
- // successful, returns a new stream on that pipeline. Otherwise, returns NULL.
- HttpPipelinedStream* CreateStreamOnExistingPipeline(
- const HttpPipelinedHost::Key& key);
-
- // Returns true if a pipelined connection already exists for |key| and
- // can accept new requests.
- bool IsExistingPipelineAvailableForKey(const HttpPipelinedHost::Key& key);
-
- // Callbacks for HttpPipelinedHost.
- virtual void OnHostIdle(HttpPipelinedHost* host) OVERRIDE;
-
- virtual void OnHostHasAdditionalCapacity(HttpPipelinedHost* host) OVERRIDE;
-
- virtual void OnHostDeterminedCapability(
- HttpPipelinedHost* host,
- HttpPipelinedHostCapability capability) OVERRIDE;
-
- // Creates a Value summary of this pool's |host_map_|. Caller assumes
- // ownership of the returned Value.
- base::Value* PipelineInfoToValue() const;
-
- private:
- typedef std::map<HttpPipelinedHost::Key, HttpPipelinedHost*> HostMap;
-
- HttpPipelinedHost* GetPipelinedHost(const HttpPipelinedHost::Key& key,
- bool create_if_not_found);
-
- Delegate* delegate_;
- scoped_ptr<HttpPipelinedHost::Factory> factory_;
- HostMap host_map_;
- const base::WeakPtr<HttpServerProperties> http_server_properties_;
- bool force_pipelining_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostPool);
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_HOST_POOL_H_
diff --git a/chromium/net/http/http_pipelined_host_pool_unittest.cc b/chromium/net/http/http_pipelined_host_pool_unittest.cc
deleted file mode 100644
index fa8d93facb1..00000000000
--- a/chromium/net/http/http_pipelined_host_pool_unittest.cc
+++ /dev/null
@@ -1,262 +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/http/http_pipelined_host_pool.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "base/rand_util.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_host_capability.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/proxy/proxy_info.h"
-#include "net/ssl/ssl_config_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::Ref;
-using testing::Return;
-using testing::ReturnNull;
-
-namespace net {
-
-namespace {
-
-ClientSocketHandle* kDummyConnection =
- reinterpret_cast<ClientSocketHandle*>(188);
-HttpPipelinedStream* kDummyStream =
- reinterpret_cast<HttpPipelinedStream*>(99);
-
-class MockPoolDelegate : public HttpPipelinedHostPool::Delegate {
- public:
- MOCK_METHOD1(OnHttpPipelinedHostHasAdditionalCapacity,
- void(HttpPipelinedHost* host));
-};
-
-class MockHostFactory : public HttpPipelinedHost::Factory {
- public:
- MOCK_METHOD5(CreateNewHost, HttpPipelinedHost*(
- HttpPipelinedHost::Delegate* delegate,
- const HttpPipelinedHost::Key& key,
- HttpPipelinedConnection::Factory* factory,
- HttpPipelinedHostCapability capability,
- bool force_pipelining));
-};
-
-class MockHost : public HttpPipelinedHost {
- public:
- MockHost(const Key& key)
- : key_(key) {
- }
-
- MOCK_METHOD6(CreateStreamOnNewPipeline, HttpPipelinedStream*(
- ClientSocketHandle* connection,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated));
- MOCK_METHOD0(CreateStreamOnExistingPipeline, HttpPipelinedStream*());
- MOCK_CONST_METHOD0(IsExistingPipelineAvailable, bool());
- MOCK_CONST_METHOD0(PipelineInfoToValue, base::Value*());
-
- virtual const Key& GetKey() const OVERRIDE { return key_; }
-
- private:
- Key key_;
-};
-
-class HttpPipelinedHostPoolTest : public testing::Test {
- public:
- HttpPipelinedHostPoolTest()
- : key_(HostPortPair("host", 123)),
- factory_(new MockHostFactory), // Owned by pool_.
- host_(new MockHost(key_)), // Owned by pool_.
- http_server_properties_(new HttpServerPropertiesImpl()),
- pool_(new HttpPipelinedHostPool(
- &delegate_, factory_,
- http_server_properties_->GetWeakPtr(), false)),
- was_npn_negotiated_(false),
- protocol_negotiated_(kProtoUnknown) {
- }
-
- void CreateDummyStream(const HttpPipelinedHost::Key& key,
- ClientSocketHandle* connection,
- HttpPipelinedStream* stream,
- MockHost* host) {
- EXPECT_CALL(*host, CreateStreamOnNewPipeline(connection,
- Ref(ssl_config_),
- Ref(proxy_info_),
- Ref(net_log_),
- was_npn_negotiated_,
- protocol_negotiated_))
- .Times(1)
- .WillOnce(Return(stream));
- EXPECT_EQ(stream,
- pool_->CreateStreamOnNewPipeline(key, connection,
- ssl_config_, proxy_info_,
- net_log_, was_npn_negotiated_,
- protocol_negotiated_));
- }
-
- MockHost* CreateDummyHost(const HttpPipelinedHost::Key& key) {
- MockHost* mock_host = new MockHost(key);
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(mock_host));
- ClientSocketHandle* dummy_connection =
- reinterpret_cast<ClientSocketHandle*>(base::RandUint64());
- HttpPipelinedStream* dummy_stream =
- reinterpret_cast<HttpPipelinedStream*>(base::RandUint64());
- CreateDummyStream(key, dummy_connection, dummy_stream, mock_host);
- return mock_host;
- }
-
- HttpPipelinedHost::Key key_;
- MockPoolDelegate delegate_;
- MockHostFactory* factory_;
- MockHost* host_;
- scoped_ptr<HttpServerPropertiesImpl> http_server_properties_;
- scoped_ptr<HttpPipelinedHostPool> pool_;
-
- const SSLConfig ssl_config_;
- const ProxyInfo proxy_info_;
- const BoundNetLog net_log_;
- bool was_npn_negotiated_;
- NextProto protocol_negotiated_;
-};
-
-TEST_F(HttpPipelinedHostPoolTest, DefaultUnknown) {
- EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_));
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostIdle(host_);
-}
-
-TEST_F(HttpPipelinedHostPoolTest, RemembersIncapable) {
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostDeterminedCapability(host_, PIPELINE_INCAPABLE);
- pool_->OnHostIdle(host_);
- EXPECT_FALSE(pool_->IsKeyEligibleForPipelining(key_));
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_INCAPABLE, false))
- .Times(0);
- EXPECT_EQ(NULL,
- pool_->CreateStreamOnNewPipeline(key_, kDummyConnection,
- ssl_config_, proxy_info_, net_log_,
- was_npn_negotiated_,
- protocol_negotiated_));
-}
-
-TEST_F(HttpPipelinedHostPoolTest, RemembersCapable) {
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE);
- pool_->OnHostIdle(host_);
- EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_));
-
- host_ = new MockHost(key_);
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_CAPABLE, false))
- .Times(1)
- .WillOnce(Return(host_));
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostIdle(host_);
-}
-
-TEST_F(HttpPipelinedHostPoolTest, IncapableIsSticky) {
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE);
- pool_->OnHostDeterminedCapability(host_, PIPELINE_INCAPABLE);
- pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE);
- pool_->OnHostIdle(host_);
- EXPECT_FALSE(pool_->IsKeyEligibleForPipelining(key_));
-}
-
-TEST_F(HttpPipelinedHostPoolTest, RemainsUnknownWithoutFeedback) {
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostIdle(host_);
- EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_));
-
- host_ = new MockHost(key_);
- EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _,
- PIPELINE_UNKNOWN, false))
- .Times(1)
- .WillOnce(Return(host_));
-
- CreateDummyStream(key_, kDummyConnection, kDummyStream, host_);
- pool_->OnHostIdle(host_);
-}
-
-TEST_F(HttpPipelinedHostPoolTest, PopulatesServerProperties) {
- EXPECT_EQ(PIPELINE_UNKNOWN,
- http_server_properties_->GetPipelineCapability(
- host_->GetKey().origin()));
- pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE);
- EXPECT_EQ(PIPELINE_CAPABLE,
- http_server_properties_->GetPipelineCapability(
- host_->GetKey().origin()));
- delete host_; // Must manually delete, because it's never added to |pool_|.
-}
-
-TEST_F(HttpPipelinedHostPoolTest, MultipleKeys) {
- HttpPipelinedHost::Key key1(HostPortPair("host", 123));
- HttpPipelinedHost::Key key2(HostPortPair("host", 456));
- HttpPipelinedHost::Key key3(HostPortPair("other", 456));
- HttpPipelinedHost::Key key4(HostPortPair("other", 789));
- MockHost* host1 = CreateDummyHost(key1);
- MockHost* host2 = CreateDummyHost(key2);
- MockHost* host3 = CreateDummyHost(key3);
-
- EXPECT_CALL(*host1, IsExistingPipelineAvailable())
- .Times(1)
- .WillOnce(Return(true));
- EXPECT_TRUE(pool_->IsExistingPipelineAvailableForKey(key1));
-
- EXPECT_CALL(*host2, IsExistingPipelineAvailable())
- .Times(1)
- .WillOnce(Return(false));
- EXPECT_FALSE(pool_->IsExistingPipelineAvailableForKey(key2));
-
- EXPECT_CALL(*host3, IsExistingPipelineAvailable())
- .Times(1)
- .WillOnce(Return(true));
- EXPECT_TRUE(pool_->IsExistingPipelineAvailableForKey(key3));
-
- EXPECT_FALSE(pool_->IsExistingPipelineAvailableForKey(key4));
-
- pool_->OnHostIdle(host1);
- pool_->OnHostIdle(host2);
- pool_->OnHostIdle(host3);
-
- delete host_; // Must manually delete, because it's never added to |pool_|.
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_test_util.cc b/chromium/net/http/http_pipelined_host_test_util.cc
deleted file mode 100644
index 5162e983708..00000000000
--- a/chromium/net/http/http_pipelined_host_test_util.cc
+++ /dev/null
@@ -1,33 +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/http/http_pipelined_host_test_util.h"
-
-#include "net/proxy/proxy_info.h"
-#include "net/ssl/ssl_config_service.h"
-
-namespace net {
-
-MockHostDelegate::MockHostDelegate() {
-}
-
-MockHostDelegate::~MockHostDelegate() {
-}
-
-MockPipelineFactory::MockPipelineFactory() {
-}
-
-MockPipelineFactory::~MockPipelineFactory() {
-}
-
-MockPipeline::MockPipeline(int depth, bool usable, bool active)
- : depth_(depth),
- usable_(usable),
- active_(active) {
-}
-
-MockPipeline::~MockPipeline() {
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_host_test_util.h b/chromium/net/http/http_pipelined_host_test_util.h
deleted file mode 100644
index 245ff02c4d5..00000000000
--- a/chromium/net/http/http_pipelined_host_test_util.h
+++ /dev/null
@@ -1,70 +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/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace net {
-
-class MockHostDelegate : public HttpPipelinedHost::Delegate {
- public:
- MockHostDelegate();
- virtual ~MockHostDelegate();
-
- MOCK_METHOD1(OnHostIdle, void(HttpPipelinedHost* host));
- MOCK_METHOD1(OnHostHasAdditionalCapacity, void(HttpPipelinedHost* host));
- MOCK_METHOD2(OnHostDeterminedCapability,
- void(HttpPipelinedHost* host,
- HttpPipelinedHostCapability capability));
-};
-
-class MockPipelineFactory : public HttpPipelinedConnection::Factory {
- public:
- MockPipelineFactory();
- virtual ~MockPipelineFactory();
-
- MOCK_METHOD8(CreateNewPipeline, HttpPipelinedConnection*(
- ClientSocketHandle* connection,
- HttpPipelinedConnection::Delegate* delegate,
- const HostPortPair& origin,
- const SSLConfig& used_ssl_config,
- const ProxyInfo& used_proxy_info,
- const BoundNetLog& net_log,
- bool was_npn_negotiated,
- NextProto protocol_negotiated));
-};
-
-class MockPipeline : public HttpPipelinedConnection {
- public:
- MockPipeline(int depth, bool usable, bool active);
- virtual ~MockPipeline();
-
- void SetState(int depth, bool usable, bool active) {
- depth_ = depth;
- usable_ = usable;
- active_ = active;
- }
-
- virtual int depth() const OVERRIDE { return depth_; }
- virtual bool usable() const OVERRIDE { return usable_; }
- virtual bool active() const OVERRIDE { return active_; }
-
- MOCK_METHOD0(CreateNewStream, HttpPipelinedStream*());
- MOCK_METHOD1(OnStreamDeleted, void(int pipeline_id));
- MOCK_CONST_METHOD0(used_ssl_config, const SSLConfig&());
- MOCK_CONST_METHOD0(used_proxy_info, const ProxyInfo&());
- MOCK_CONST_METHOD0(net_log, const BoundNetLog&());
- MOCK_CONST_METHOD0(was_npn_negotiated, bool());
- MOCK_CONST_METHOD0(protocol_negotiated, NextProto());
-
- private:
- int depth_;
- bool usable_;
- bool active_;
-};
-
-MATCHER_P(MatchesOrigin, expected, "") { return expected.Equals(arg); }
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_network_transaction_unittest.cc b/chromium/net/http/http_pipelined_network_transaction_unittest.cc
deleted file mode 100644
index 80b74fd554b..00000000000
--- a/chromium/net/http/http_pipelined_network_transaction_unittest.cc
+++ /dev/null
@@ -1,1035 +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 <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "net/base/address_list.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_util.h"
-#include "net/base/request_priority.h"
-#include "net/dns/host_cache.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/http/http_auth_handler_mock.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_network_transaction.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/proxy/proxy_config_service.h"
-#include "net/proxy/proxy_service.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
-#include "net/socket/client_socket_pool_manager.h"
-#include "net/socket/socket_test_util.h"
-#include "net/ssl/ssl_config_service_defaults.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::StrEq;
-
-namespace net {
-
-namespace {
-
-class SimpleProxyConfigService : public ProxyConfigService {
- public:
- virtual void AddObserver(Observer* observer) OVERRIDE {
- observer_ = observer;
- }
-
- virtual void RemoveObserver(Observer* observer) OVERRIDE {
- if (observer_ == observer) {
- observer_ = NULL;
- }
- }
-
- virtual ConfigAvailability GetLatestProxyConfig(
- ProxyConfig* config) OVERRIDE {
- *config = config_;
- return CONFIG_VALID;
- }
-
- void IncrementConfigId() {
- config_.set_id(config_.id() + 1);
- observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID);
- }
-
- private:
- ProxyConfig config_;
- Observer* observer_;
-};
-
-class HttpPipelinedNetworkTransactionTest : public testing::Test {
- public:
- HttpPipelinedNetworkTransactionTest()
- : histograms_("a"),
- pool_(1, 1, &histograms_, &factory_) {
- }
-
- void Initialize(bool force_http_pipelining) {
- // Normally, this code could just go in SetUp(). For a few of these tests,
- // we change the default number of sockets per group. That needs to be done
- // before we construct the HttpNetworkSession.
- proxy_config_service_ = new SimpleProxyConfigService();
- proxy_service_.reset(new ProxyService(proxy_config_service_, NULL, NULL));
- ssl_config_ = new SSLConfigServiceDefaults;
- auth_handler_factory_.reset(new HttpAuthHandlerMock::Factory());
-
- HttpNetworkSession::Params session_params;
- session_params.client_socket_factory = &factory_;
- session_params.proxy_service = proxy_service_.get();
- session_params.host_resolver = &mock_resolver_;
- session_params.ssl_config_service = ssl_config_.get();
- session_params.http_auth_handler_factory = auth_handler_factory_.get();
- session_params.http_server_properties =
- http_server_properties_.GetWeakPtr();
- session_params.force_http_pipelining = force_http_pipelining;
- session_params.http_pipelining_enabled = true;
- session_ = new HttpNetworkSession(session_params);
- }
-
- void AddExpectedConnection(MockRead* reads, size_t reads_count,
- MockWrite* writes, size_t writes_count) {
- DeterministicSocketData* data = new DeterministicSocketData(
- reads, reads_count, writes, writes_count);
- data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
- if (reads_count || writes_count) {
- data->StopAfter(reads_count + writes_count);
- }
- factory_.AddSocketDataProvider(data);
- data_vector_.push_back(data);
- }
-
- enum RequestInfoOptions {
- REQUEST_DEFAULT,
- REQUEST_MAIN_RESOURCE,
- };
-
- HttpRequestInfo* GetRequestInfo(
- const char* filename, RequestInfoOptions options = REQUEST_DEFAULT) {
- std::string url = base::StringPrintf("http://localhost/%s", filename);
- HttpRequestInfo* request_info = new HttpRequestInfo;
- request_info->url = GURL(url);
- request_info->method = "GET";
- if (options == REQUEST_MAIN_RESOURCE) {
- request_info->load_flags = LOAD_MAIN_FRAME;
- }
- request_info_vector_.push_back(request_info);
- return request_info;
- }
-
- void ExpectResponse(const std::string& expected,
- HttpNetworkTransaction& transaction,
- IoMode io_mode) {
- scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
- if (io_mode == ASYNC) {
- EXPECT_EQ(ERR_IO_PENDING, transaction.Read(buffer.get(), expected.size(),
- callback_.callback()));
- data_vector_[0]->RunFor(1);
- EXPECT_EQ(static_cast<int>(expected.length()), callback_.WaitForResult());
- } else {
- EXPECT_EQ(static_cast<int>(expected.size()),
- transaction.Read(buffer.get(), expected.size(),
- callback_.callback()));
- }
- std::string actual(buffer->data(), expected.size());
- EXPECT_THAT(actual, StrEq(expected));
- EXPECT_EQ(OK, transaction.Read(buffer.get(), expected.size(),
- callback_.callback()));
- }
-
- void CompleteTwoRequests(int data_index, int stop_at_step) {
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
- EXPECT_EQ(OK, one_callback.WaitForResult());
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- TestCompletionCallback one_read_callback;
- scoped_refptr<IOBuffer> buffer(new IOBuffer(8));
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Read(buffer.get(), 8,
- one_read_callback.callback()));
-
- data_vector_[data_index]->SetStop(stop_at_step);
- data_vector_[data_index]->Run();
- EXPECT_EQ(8, one_read_callback.WaitForResult());
- data_vector_[data_index]->SetStop(10);
- std::string actual(buffer->data(), 8);
- EXPECT_THAT(actual, StrEq("one.html"));
- EXPECT_EQ(OK, one_transaction->Read(buffer.get(), 8,
- one_read_callback.callback()));
-
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
- }
-
- void CompleteFourRequests(RequestInfoOptions options) {
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html", options),
- one_callback.callback(), BoundNetLog()));
- EXPECT_EQ(OK, one_callback.WaitForResult());
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html", options),
- two_callback.callback(), BoundNetLog()));
-
- HttpNetworkTransaction three_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback three_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- three_transaction.Start(GetRequestInfo("three.html", options),
- three_callback.callback(),
- BoundNetLog()));
-
- HttpNetworkTransaction four_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback four_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- four_transaction.Start(GetRequestInfo("four.html", options),
- four_callback.callback(), BoundNetLog()));
-
- ExpectResponse("one.html", *one_transaction.get(), SYNCHRONOUS);
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
- EXPECT_EQ(OK, three_callback.WaitForResult());
- ExpectResponse("three.html", three_transaction, SYNCHRONOUS);
-
- one_transaction.reset();
- EXPECT_EQ(OK, four_callback.WaitForResult());
- ExpectResponse("four.html", four_transaction, SYNCHRONOUS);
- }
-
- DeterministicMockClientSocketFactory factory_;
- ClientSocketPoolHistograms histograms_;
- MockTransportClientSocketPool pool_;
- ScopedVector<DeterministicSocketData> data_vector_;
- TestCompletionCallback callback_;
- ScopedVector<HttpRequestInfo> request_info_vector_;
-
- SimpleProxyConfigService* proxy_config_service_;
- scoped_ptr<ProxyService> proxy_service_;
- MockHostResolver mock_resolver_;
- scoped_refptr<SSLConfigService> ssl_config_;
- scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory_;
- HttpServerPropertiesImpl http_server_properties_;
- scoped_refptr<HttpNetworkSession> session_;
-};
-
-TEST_F(HttpPipelinedNetworkTransactionTest, OneRequest) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /test.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 9\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "test.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
- EXPECT_EQ(ERR_IO_PENDING,
- transaction.Start(GetRequestInfo("test.html"), callback_.callback(),
- BoundNetLog()));
- EXPECT_EQ(OK, callback_.WaitForResult());
- ExpectResponse("test.html", transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ReusePipeline) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 4, "one.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "two.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- CompleteTwoRequests(0, 5);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ReusesOnSpaceAvailable) {
- int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL);
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "one.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 8, "two.html"),
- MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
- MockRead(SYNCHRONOUS, 11, "three.html"),
- MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
- MockRead(SYNCHRONOUS, 15, "four.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- CompleteFourRequests(REQUEST_DEFAULT);
-
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, WontPipelineMainResource) {
- int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL);
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 8, "GET /three.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "one.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "two.html"),
- MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
- MockRead(SYNCHRONOUS, 11, "three.html"),
- MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
- MockRead(SYNCHRONOUS, 15, "four.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- CompleteFourRequests(REQUEST_MAIN_RESOURCE);
-
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, UnknownSizeEvictsToNewPipeline) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(ASYNC, 2, "one.html"),
- MockRead(SYNCHRONOUS, OK, 3),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "two.html"),
- };
- AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
-
- CompleteTwoRequests(0, 3);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ConnectionCloseEvictToNewPipeline) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 4, "one.html"),
- MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "two.html"),
- };
- AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
-
- CompleteTwoRequests(0, 5);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ErrorEvictsToNewPipeline) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, ERR_FAILED, 2),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "two.html"),
- };
- AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
-
- HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction.Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
- EXPECT_EQ(OK, one_callback.WaitForResult());
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- scoped_refptr<IOBuffer> buffer(new IOBuffer(1));
- EXPECT_EQ(ERR_FAILED,
- one_transaction.Read(buffer.get(), 1, callback_.callback()));
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, SendErrorEvictsToNewPipeline) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(ASYNC, ERR_FAILED, 0),
- };
- AddExpectedConnection(NULL, 0, writes, arraysize(writes));
-
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "two.html"),
- };
- AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
-
- HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction.Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- data_vector_[0]->RunFor(1);
- EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
-
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, RedirectDrained) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 4, "redirect"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "two.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("redirect.html"),
- one_callback.callback(), BoundNetLog()));
- EXPECT_EQ(OK, one_callback.WaitForResult());
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- one_transaction.reset();
- data_vector_[0]->RunFor(2);
- data_vector_[0]->SetStop(10);
-
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, BasicHttpAuthentication) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 5, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n"
- "Authorization: auth_token\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Authentication Required\r\n"),
- MockRead(SYNCHRONOUS, 2,
- "WWW-Authenticate: Basic realm=\"Secure Area\"\r\n"),
- MockRead(SYNCHRONOUS, 3, "Content-Length: 20\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "needs authentication"),
- MockRead(SYNCHRONOUS, 6, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 7, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 8, "one.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- HttpAuthHandlerMock* mock_auth = new HttpAuthHandlerMock;
- std::string challenge_text = "Basic";
- HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
- challenge_text.end());
- GURL origin("localhost");
- EXPECT_TRUE(mock_auth->InitFromChallenge(&challenge,
- HttpAuth::AUTH_SERVER,
- origin,
- BoundNetLog()));
- auth_handler_factory_->AddMockHandler(mock_auth, HttpAuth::AUTH_SERVER);
-
- HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
- EXPECT_EQ(ERR_IO_PENDING,
- transaction.Start(GetRequestInfo("one.html"),
- callback_.callback(),
- BoundNetLog()));
- EXPECT_EQ(OK, callback_.WaitForResult());
-
- AuthCredentials credentials(ASCIIToUTF16("user"), ASCIIToUTF16("pass"));
- EXPECT_EQ(OK, transaction.RestartWithAuth(credentials, callback_.callback()));
-
- ExpectResponse("one.html", transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, OldVersionDisablesPipelining) {
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /pipelined.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 14\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "pipelined.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 3, "one.html"),
- MockRead(SYNCHRONOUS, OK, 4),
- };
- AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
-
- MockWrite writes3[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads3[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "two.html"),
- MockRead(SYNCHRONOUS, OK, 4),
- };
- AddExpectedConnection(reads3, arraysize(reads3), writes3, arraysize(writes3));
-
- HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction.Start(GetRequestInfo("pipelined.html"),
- one_callback.callback(), BoundNetLog()));
- EXPECT_EQ(OK, one_callback.WaitForResult());
- ExpectResponse("pipelined.html", one_transaction, SYNCHRONOUS);
-
- CompleteTwoRequests(1, 4);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, PipelinesImmediatelyIfKnownGood) {
- // The first request gets us an HTTP/1.1. The next 3 test pipelining. When the
- // 3rd request completes, we know pipelining is safe. After the first 4
- // complete, the 5th and 6th should then be immediately sent pipelined on a
- // new HttpPipelinedConnection.
- int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL);
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 16, "GET /second-pipeline-one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 17, "GET /second-pipeline-two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3, "one.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 8, "two.html"),
- MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
- MockRead(SYNCHRONOUS, 11, "three.html"),
- MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
- MockRead(SYNCHRONOUS, 15, "four.html"),
- MockRead(ASYNC, 18, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 19, "Content-Length: 24\r\n\r\n"),
- MockRead(SYNCHRONOUS, 20, "second-pipeline-one.html"),
- MockRead(SYNCHRONOUS, 21, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 22, "Content-Length: 24\r\n\r\n"),
- MockRead(SYNCHRONOUS, 23, "second-pipeline-two.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- CompleteFourRequests(REQUEST_DEFAULT);
-
- HttpNetworkTransaction second_one_transaction(
- DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback second_one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- second_one_transaction.Start(
- GetRequestInfo("second-pipeline-one.html"),
- second_one_callback.callback(), BoundNetLog()));
- base::MessageLoop::current()->RunUntilIdle();
-
- HttpNetworkTransaction second_two_transaction(
- DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback second_two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- second_two_transaction.Start(
- GetRequestInfo("second-pipeline-two.html"),
- second_two_callback.callback(), BoundNetLog()));
-
- data_vector_[0]->RunFor(3);
- EXPECT_EQ(OK, second_one_callback.WaitForResult());
- data_vector_[0]->StopAfter(100);
- ExpectResponse("second-pipeline-one.html", second_one_transaction,
- SYNCHRONOUS);
- EXPECT_EQ(OK, second_two_callback.WaitForResult());
- ExpectResponse("second-pipeline-two.html", second_two_transaction,
- SYNCHRONOUS);
-
- ClientSocketPoolManager::set_max_sockets_per_group(
- HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
-}
-
-class DataRunnerObserver : public base::MessageLoop::TaskObserver {
- public:
- DataRunnerObserver(DeterministicSocketData* data, int run_before_task)
- : data_(data),
- run_before_task_(run_before_task),
- current_task_(0) { }
-
- virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
- ++current_task_;
- if (current_task_ == run_before_task_) {
- data_->Run();
- base::MessageLoop::current()->RemoveTaskObserver(this);
- }
- }
-
- virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {}
-
- private:
- DeterministicSocketData* data_;
- int run_before_task_;
- int current_task_;
-};
-
-TEST_F(HttpPipelinedNetworkTransactionTest, OpenPipelinesWhileBinding) {
- // There was a racy crash in the pipelining code. This test recreates that
- // race. The steps are:
- // 1. The first request starts a pipeline and requests headers.
- // 2. HttpStreamFactoryImpl::Job tries to bind a pending request to a new
- // pipeline and queues a task to do so.
- // 3. Before that task runs, the first request receives its headers and
- // determines this host is probably capable of pipelining.
- // 4. All of the hosts' pipelines are notified they have capacity in a loop.
- // 5. On the first iteration, the first pipeline is opened up to accept new
- // requests and steals the request from step #2.
- // 6. The pipeline from #2 is deleted because it has no streams.
- // 7. On the second iteration, the host tries to notify the pipeline from step
- // #2 that it has capacity. This is a use-after-free.
- Initialize(false);
-
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- MockWrite(ASYNC, 3, "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 4, "one.html"),
- MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
- MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
- MockRead(SYNCHRONOUS, 7, "two.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- AddExpectedConnection(NULL, 0, NULL, 0);
-
- HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction.Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- data_vector_[0]->SetStop(2);
- data_vector_[0]->Run();
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
- // Posted tasks should be:
- // 1. MockHostResolverBase::ResolveNow
- // 2. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 1
- // 3. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 2
- //
- // We need to make sure that the response that triggers OnPipelineFeedback(OK)
- // is called in between when task #3 is scheduled and when it runs. The
- // DataRunnerObserver does that.
- DataRunnerObserver observer(data_vector_[0], 3);
- base::MessageLoop::current()->AddTaskObserver(&observer);
- data_vector_[0]->SetStop(4);
- base::MessageLoop::current()->RunUntilIdle();
- data_vector_[0]->SetStop(10);
-
- EXPECT_EQ(OK, one_callback.WaitForResult());
- ExpectResponse("one.html", one_transaction, SYNCHRONOUS);
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ProxyChangesWhileConnecting) {
- Initialize(false);
-
- DeterministicSocketData data(NULL, 0, NULL, 0);
- data.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED));
- factory_.AddSocketDataProvider(&data);
-
- DeterministicSocketData data2(NULL, 0, NULL, 0);
- data2.set_connect_data(MockConnect(ASYNC, ERR_FAILED));
- factory_.AddSocketDataProvider(&data2);
-
- HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
- EXPECT_EQ(ERR_IO_PENDING,
- transaction.Start(GetRequestInfo("test.html"), callback_.callback(),
- BoundNetLog()));
-
- proxy_config_service_->IncrementConfigId();
-
- EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineSharesConnection) {
- Initialize(true);
-
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 3, "one.html"),
- MockRead(ASYNC, 4, "HTTP/1.1 200 OK\r\n"),
- MockRead(ASYNC, 5, "Content-Length: 8\r\n\r\n"),
- MockRead(ASYNC, 6, "two.html"),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- data_vector_[0]->RunFor(3); // Send + 2 lines of headers.
- EXPECT_EQ(OK, one_callback.WaitForResult());
- ExpectResponse("one.html", *one_transaction.get(), ASYNC);
- one_transaction.reset();
-
- data_vector_[0]->RunFor(2); // 2 lines of headers.
- EXPECT_EQ(OK, two_callback.WaitForResult());
- ExpectResponse("two.html", two_transaction, ASYNC);
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest,
- ForcedPipelineConnectionErrorFailsBoth) {
- Initialize(true);
-
- DeterministicSocketData data(NULL, 0, NULL, 0);
- data.set_connect_data(MockConnect(ASYNC, ERR_FAILED));
- factory_.AddSocketDataProvider(&data);
-
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- data.Run();
- EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
- EXPECT_EQ(ERR_FAILED, two_callback.WaitForResult());
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineEvictionIsFatal) {
- Initialize(true);
-
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads[] = {
- MockRead(ASYNC, ERR_FAILED, 1),
- };
- AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
-
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction.Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- data_vector_[0]->RunFor(2);
- EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
- one_transaction.reset();
- EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult());
-}
-
-TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineOrder) {
- Initialize(true);
-
- MockWrite writes[] = {
- MockWrite(ASYNC, 0,
- "GET /one.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- "GET /two.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- "GET /three.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- "GET /four.html HTTP/1.1\r\n"
- "Host: localhost\r\n"
- "Connection: keep-alive\r\n\r\n"
- ),
- };
- MockRead reads[] = {
- MockRead(ASYNC, ERR_FAILED, 1),
- };
- DeterministicSocketData data(
- reads, arraysize(reads), writes, arraysize(writes));
- data.set_connect_data(MockConnect(ASYNC, OK));
- factory_.AddSocketDataProvider(&data);
-
- scoped_ptr<HttpNetworkTransaction> one_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback one_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- one_transaction->Start(GetRequestInfo("one.html"),
- one_callback.callback(), BoundNetLog()));
-
- scoped_ptr<HttpNetworkTransaction> two_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback two_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- two_transaction->Start(GetRequestInfo("two.html"),
- two_callback.callback(), BoundNetLog()));
-
- scoped_ptr<HttpNetworkTransaction> three_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback three_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- three_transaction->Start(GetRequestInfo("three.html"),
- three_callback.callback(), BoundNetLog()));
-
- scoped_ptr<HttpNetworkTransaction> four_transaction(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
- TestCompletionCallback four_callback;
- EXPECT_EQ(ERR_IO_PENDING,
- four_transaction->Start(GetRequestInfo("four.html"),
- four_callback.callback(), BoundNetLog()));
-
- data.RunFor(3);
- EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
- one_transaction.reset();
- EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult());
- two_transaction.reset();
- EXPECT_EQ(ERR_PIPELINE_EVICTION, three_callback.WaitForResult());
- three_transaction.reset();
- EXPECT_EQ(ERR_PIPELINE_EVICTION, four_callback.WaitForResult());
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_stream.cc b/chromium/net/http/http_pipelined_stream.cc
deleted file mode 100644
index cc267e2510e..00000000000
--- a/chromium/net/http/http_pipelined_stream.cc
+++ /dev/null
@@ -1,153 +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/http/http_pipelined_stream.h"
-
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_pipelined_connection_impl.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_util.h"
-
-namespace net {
-
-HttpPipelinedStream::HttpPipelinedStream(HttpPipelinedConnectionImpl* pipeline,
- int pipeline_id)
- : pipeline_(pipeline),
- pipeline_id_(pipeline_id),
- request_info_(NULL) {
-}
-
-HttpPipelinedStream::~HttpPipelinedStream() {
- pipeline_->OnStreamDeleted(pipeline_id_);
-}
-
-int HttpPipelinedStream::InitializeStream(
- const HttpRequestInfo* request_info,
- RequestPriority priority,
- const BoundNetLog& net_log,
- const CompletionCallback& callback) {
- request_info_ = request_info;
- pipeline_->InitializeParser(pipeline_id_, request_info, net_log);
- return OK;
-}
-
-
-int HttpPipelinedStream::SendRequest(const HttpRequestHeaders& headers,
- HttpResponseInfo* response,
- const CompletionCallback& callback) {
- CHECK(pipeline_id_);
- CHECK(request_info_);
- // TODO(simonjam): Proxy support will be needed here.
- const std::string path = HttpUtil::PathForRequest(request_info_->url);
- std::string request_line_ = base::StringPrintf("%s %s HTTP/1.1\r\n",
- request_info_->method.c_str(),
- path.c_str());
- return pipeline_->SendRequest(pipeline_id_, request_line_, headers, response,
- callback);
-}
-
-UploadProgress HttpPipelinedStream::GetUploadProgress() const {
- return pipeline_->GetUploadProgress(pipeline_id_);
-}
-
-int HttpPipelinedStream::ReadResponseHeaders(
- const CompletionCallback& callback) {
- return pipeline_->ReadResponseHeaders(pipeline_id_, callback);
-}
-
-const HttpResponseInfo* HttpPipelinedStream::GetResponseInfo() const {
- return pipeline_->GetResponseInfo(pipeline_id_);
-}
-
-int HttpPipelinedStream::ReadResponseBody(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- return pipeline_->ReadResponseBody(pipeline_id_, buf, buf_len, callback);
-}
-
-void HttpPipelinedStream::Close(bool not_reusable) {
- pipeline_->Close(pipeline_id_, not_reusable);
-}
-
-HttpStream* HttpPipelinedStream::RenewStreamForAuth() {
- if (pipeline_->usable()) {
- return pipeline_->CreateNewStream();
- }
- return NULL;
-}
-
-bool HttpPipelinedStream::IsResponseBodyComplete() const {
- return pipeline_->IsResponseBodyComplete(pipeline_id_);
-}
-
-bool HttpPipelinedStream::CanFindEndOfResponse() const {
- return pipeline_->CanFindEndOfResponse(pipeline_id_);
-}
-
-bool HttpPipelinedStream::IsConnectionReused() const {
- return pipeline_->IsConnectionReused(pipeline_id_);
-}
-
-void HttpPipelinedStream::SetConnectionReused() {
- pipeline_->SetConnectionReused(pipeline_id_);
-}
-
-bool HttpPipelinedStream::IsConnectionReusable() const {
- return pipeline_->usable();
-}
-
-int64 HttpPipelinedStream::GetTotalReceivedBytes() const {
- return pipeline_->GetTotalReceivedBytes(pipeline_id_);
-}
-
-bool HttpPipelinedStream::GetLoadTimingInfo(
- LoadTimingInfo* load_timing_info) const {
- return pipeline_->GetLoadTimingInfo(pipeline_id_, load_timing_info);
-}
-
-void HttpPipelinedStream::GetSSLInfo(SSLInfo* ssl_info) {
- pipeline_->GetSSLInfo(pipeline_id_, ssl_info);
-}
-
-void HttpPipelinedStream::GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) {
- pipeline_->GetSSLCertRequestInfo(pipeline_id_, cert_request_info);
-}
-
-bool HttpPipelinedStream::IsSpdyHttpStream() const {
- return false;
-}
-
-void HttpPipelinedStream::Drain(HttpNetworkSession* session) {
- pipeline_->Drain(this, session);
-}
-
-void HttpPipelinedStream::SetPriority(RequestPriority priority) {
- // TODO(akalin): Plumb this through to |pipeline_| and its
- // underlying ClientSocketHandle.
-}
-
-const SSLConfig& HttpPipelinedStream::used_ssl_config() const {
- return pipeline_->used_ssl_config();
-}
-
-const ProxyInfo& HttpPipelinedStream::used_proxy_info() const {
- return pipeline_->used_proxy_info();
-}
-
-const BoundNetLog& HttpPipelinedStream::net_log() const {
- return pipeline_->net_log();
-}
-
-bool HttpPipelinedStream::was_npn_negotiated() const {
- return pipeline_->was_npn_negotiated();
-}
-
-NextProto HttpPipelinedStream::protocol_negotiated() const {
- return pipeline_->protocol_negotiated();
-}
-
-} // namespace net
diff --git a/chromium/net/http/http_pipelined_stream.h b/chromium/net/http/http_pipelined_stream.h
deleted file mode 100644
index 7a853abc1a7..00000000000
--- a/chromium/net/http/http_pipelined_stream.h
+++ /dev/null
@@ -1,115 +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_HTTP_HTTP_PIPELINED_STREAM_H_
-#define NET_HTTP_HTTP_PIPELINED_STREAM_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "net/base/net_log.h"
-#include "net/http/http_stream.h"
-#include "net/socket/ssl_client_socket.h"
-
-namespace net {
-
-class BoundNetLog;
-class HttpPipelinedConnectionImpl;
-class HttpResponseInfo;
-class HttpRequestHeaders;
-struct HttpRequestInfo;
-class IOBuffer;
-class ProxyInfo;
-struct SSLConfig;
-
-// HttpPipelinedStream is the pipelined implementation of HttpStream. It has
-// very little code in it. Instead, it serves as the client's interface to the
-// pipelined connection, where all the work happens.
-//
-// In the case of pipelining failures, these functions may return
-// ERR_PIPELINE_EVICTION. In that case, the client should retry the HTTP
-// request without pipelining.
-class HttpPipelinedStream : public HttpStream {
- public:
- HttpPipelinedStream(HttpPipelinedConnectionImpl* pipeline,
- int pipeline_id);
- virtual ~HttpPipelinedStream();
-
- // HttpStream methods:
- virtual int InitializeStream(const HttpRequestInfo* request_info,
- RequestPriority priority,
- const BoundNetLog& net_log,
- const CompletionCallback& callback) OVERRIDE;
-
- virtual int SendRequest(const HttpRequestHeaders& headers,
- HttpResponseInfo* response,
- const CompletionCallback& callback) OVERRIDE;
-
- virtual UploadProgress GetUploadProgress() const OVERRIDE;
-
- virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
-
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
-
- virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) OVERRIDE;
-
- virtual void Close(bool not_reusable) OVERRIDE;
-
- virtual HttpStream* RenewStreamForAuth() OVERRIDE;
-
- virtual bool IsResponseBodyComplete() const OVERRIDE;
-
- virtual bool CanFindEndOfResponse() const OVERRIDE;
-
- virtual bool IsConnectionReused() const OVERRIDE;
-
- virtual void SetConnectionReused() OVERRIDE;
-
- virtual bool IsConnectionReusable() const OVERRIDE;
-
- virtual int64 GetTotalReceivedBytes() const OVERRIDE;
-
- virtual bool GetLoadTimingInfo(
- LoadTimingInfo* load_timing_info) const OVERRIDE;
-
- virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
-
- virtual void GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) OVERRIDE;
-
- virtual bool IsSpdyHttpStream() const OVERRIDE;
-
- virtual void Drain(HttpNetworkSession* session) OVERRIDE;
-
- virtual void SetPriority(RequestPriority priority) OVERRIDE;
-
- // The SSLConfig used to establish this stream's pipeline.
- const SSLConfig& used_ssl_config() const;
-
- // The ProxyInfo used to establish this this stream's pipeline.
- const ProxyInfo& used_proxy_info() const;
-
- // The BoundNetLog of this stream's pipelined connection.
- const BoundNetLog& net_log() const;
-
- // True if this stream's pipeline was NPN negotiated.
- bool was_npn_negotiated() const;
-
- // Protocol negotiated with the server.
- NextProto protocol_negotiated() const;
-
- private:
- HttpPipelinedConnectionImpl* pipeline_;
-
- int pipeline_id_;
-
- const HttpRequestInfo* request_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpPipelinedStream);
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_PIPELINED_STREAM_H_
diff --git a/chromium/net/http/http_proxy_client_socket.cc b/chromium/net/http/http_proxy_client_socket.cc
index 7037616ae5a..3d9eadd4835 100644
--- a/chromium/net/http/http_proxy_client_socket.cc
+++ b/chromium/net/http/http_proxy_client_socket.cc
@@ -237,11 +237,11 @@ int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
return transport_->socket()->Write(buf, buf_len, callback);
}
-bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
+int HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool HttpProxyClientSocket::SetSendBufferSize(int32 size) {
+int HttpProxyClientSocket::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
@@ -276,7 +276,7 @@ int HttpProxyClientSocket::PrepareForAuthRestart() {
int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
next_state_ = STATE_GENERATE_AUTH_TOKEN;
- transport_->set_is_reused(true);
+ transport_->set_reuse_type(ClientSocketHandle::REUSED_IDLE);
} else {
// This assumes that the underlying transport socket is a TCP socket,
// since only TCP sockets are restartable.
diff --git a/chromium/net/http/http_proxy_client_socket.h b/chromium/net/http/http_proxy_client_socket.h
index fca054f9c44..a2682c505b1 100644
--- a/chromium/net/http/http_proxy_client_socket.h
+++ b/chromium/net/http/http_proxy_client_socket.h
@@ -82,8 +82,8 @@ class HttpProxyClientSocket : public ProxyClientSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
diff --git a/chromium/net/http/http_proxy_client_socket_pool.cc b/chromium/net/http/http_proxy_client_socket_pool.cc
index 8d691b7dd80..829a5e78314 100644
--- a/chromium/net/http/http_proxy_client_socket_pool.cc
+++ b/chromium/net/http/http_proxy_client_socket_pool.cc
@@ -206,7 +206,7 @@ int HttpProxyConnectJob::DoSSLConnect() {
if (params_->tunnel()) {
SpdySessionKey key(params_->destination().host_port_pair(),
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) {
using_spdy_ = true;
next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
@@ -237,6 +237,13 @@ int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
return ERR_PROXY_CERTIFICATE_INVALID;
}
}
+ // A SPDY session to the proxy completed prior to resolving the proxy
+ // hostname. Surface this error, and allow the delegate to retry.
+ // See crbug.com/334413.
+ if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
+ DCHECK(!transport_socket_handle_->socket());
+ return ERR_SPDY_SESSION_ALREADY_EXISTS;
+ }
if (result < 0) {
if (transport_socket_handle_->socket())
transport_socket_handle_->socket()->Disconnect();
@@ -302,7 +309,7 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
DCHECK(params_->tunnel());
SpdySessionKey key(params_->destination().host_port_pair(),
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
SpdySessionPool* spdy_pool = params_->spdy_session_pool();
base::WeakPtr<SpdySession> spdy_session =
spdy_pool->FindAvailableSession(key, net_log());
@@ -315,11 +322,11 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
}
} else {
// Create a session direct to the proxy itself
- int rv = spdy_pool->CreateAvailableSessionFromSocket(
- key, transport_socket_handle_.Pass(),
- net_log(), OK, &spdy_session, /*using_ssl_*/ true);
- if (rv < 0)
- return rv;
+ spdy_session =
+ spdy_pool->CreateAvailableSessionFromSocket(
+ key, transport_socket_handle_.Pass(),
+ net_log(), OK, /*using_ssl_*/ true);
+ DCHECK(spdy_session);
}
next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
diff --git a/chromium/net/http/http_proxy_client_socket_pool_unittest.cc b/chromium/net/http/http_proxy_client_socket_pool_unittest.cc
index a70fe6ab067..d7955d2c058 100644
--- a/chromium/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/chromium/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -100,8 +100,8 @@ class HttpProxyClientSocketPoolTest
}
void AddAuthToCache() {
- const base::string16 kFoo(ASCIIToUTF16("foo"));
- const base::string16 kBar(ASCIIToUTF16("bar"));
+ const base::string16 kFoo(base::ASCIIToUTF16("foo"));
+ const base::string16 kBar(base::ASCIIToUTF16("bar"));
GURL proxy_url(GetParam().proxy_type == HTTP ?
(std::string("http://") + kHttpProxyHost) :
(std::string("https://") + kHttpsProxyHost));
@@ -135,7 +135,7 @@ class HttpProxyClientSocketPoolTest
NULL,
HostPortPair(kHttpsProxyHost, 443),
SSLConfig(),
- kPrivacyModeDisabled,
+ PRIVACY_MODE_DISABLED,
0,
false,
false);
@@ -248,12 +248,9 @@ INSTANTIATE_TEST_CASE_P(
HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31),
HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31),
HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31),
- HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4a2),
- HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4a2),
- HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4a2),
- HttpProxyClientSocketPoolTestParams(HTTP, kProtoHTTP2Draft04),
- HttpProxyClientSocketPoolTestParams(HTTPS, kProtoHTTP2Draft04),
- HttpProxyClientSocketPoolTestParams(SPDY, kProtoHTTP2Draft04)));
+ HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4),
+ HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4),
+ HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4)));
TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) {
Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
diff --git a/chromium/net/http/http_request_headers.cc b/chromium/net/http/http_request_headers.cc
index 8c9c4289336..9348e3e2778 100644
--- a/chromium/net/http/http_request_headers.cc
+++ b/chromium/net/http/http_request_headers.cc
@@ -9,20 +9,9 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
+#include "net/http/http_log_util.h"
#include "net/http/http_util.h"
-namespace {
-
-bool ShouldShowHttpHeaderValue(const std::string& header_name) {
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- if (header_name == "Proxy-Authorization")
- return false;
-#endif
- return true;
-}
-
-} // namespace
-
namespace net {
const char HttpRequestHeaders::kGetMethod[] = "GET";
@@ -197,17 +186,17 @@ std::string HttpRequestHeaders::ToString() const {
base::Value* HttpRequestHeaders::NetLogCallback(
const std::string* request_line,
- NetLog::LogLevel /* log_level */) const {
+ NetLog::LogLevel log_level) const {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("line", *request_line);
base::ListValue* headers = new base::ListValue();
for (HeaderVector::const_iterator it = headers_.begin();
it != headers_.end(); ++it) {
+ std::string log_value = ElideHeaderValueForNetLog(
+ log_level, it->key, it->value);
headers->Append(new base::StringValue(
base::StringPrintf("%s: %s",
- it->key.c_str(),
- (ShouldShowHttpHeaderValue(it->key) ?
- it->value.c_str() : "[elided]"))));
+ it->key.c_str(), log_value.c_str())));
}
dict->Set("headers", headers);
return dict;
diff --git a/chromium/net/http/http_request_info.cc b/chromium/net/http/http_request_info.cc
index bffc96ccfae..794aa1c3a7c 100644
--- a/chromium/net/http/http_request_info.cc
+++ b/chromium/net/http/http_request_info.cc
@@ -10,7 +10,7 @@ HttpRequestInfo::HttpRequestInfo()
: upload_data_stream(NULL),
load_flags(0),
motivation(NORMAL_MOTIVATION),
- privacy_mode(kPrivacyModeDisabled) {
+ privacy_mode(PRIVACY_MODE_DISABLED) {
}
HttpRequestInfo::~HttpRequestInfo() {}
diff --git a/chromium/net/http/http_response_body_drainer.cc b/chromium/net/http/http_response_body_drainer.cc
index a1ba35ad31e..91dbbf70984 100644
--- a/chromium/net/http/http_response_body_drainer.cc
+++ b/chromium/net/http/http_response_body_drainer.cc
@@ -14,8 +14,7 @@
namespace net {
HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* stream)
- : read_size_(0),
- stream_(stream),
+ : stream_(stream),
next_state_(STATE_NONE),
total_read_(0),
session_(NULL) {}
@@ -23,25 +22,7 @@ HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* stream)
HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
- StartWithSize(session, kDrainBodyBufferSize);
-}
-
-void HttpResponseBodyDrainer::StartWithSize(HttpNetworkSession* session,
- int num_bytes_to_drain) {
- DCHECK_LE(0, num_bytes_to_drain);
- // TODO(simonjam): Consider raising this limit if we're pipelining. If we have
- // a bunch of responses in the pipeline, we should be less willing to give up
- // while draining.
- if (num_bytes_to_drain > kDrainBodyBufferSize) {
- Finish(ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN);
- return;
- } else if (num_bytes_to_drain == 0) {
- Finish(OK);
- return;
- }
-
- read_size_ = num_bytes_to_drain;
- read_buf_ = new IOBuffer(read_size_);
+ read_buf_ = new IOBuffer(kDrainBodyBufferSize);
next_state_ = STATE_DRAIN_RESPONSE_BODY;
int rv = DoLoop(OK);
@@ -88,7 +69,7 @@ int HttpResponseBodyDrainer::DoDrainResponseBody() {
return stream_->ReadResponseBody(
read_buf_.get(),
- read_size_ - total_read_,
+ kDrainBodyBufferSize - total_read_,
base::Bind(&HttpResponseBodyDrainer::OnIOComplete,
base::Unretained(this)));
}
diff --git a/chromium/net/http/http_response_body_drainer.h b/chromium/net/http/http_response_body_drainer.h
index 284d08a99cd..5c7713e254b 100644
--- a/chromium/net/http/http_response_body_drainer.h
+++ b/chromium/net/http/http_response_body_drainer.h
@@ -35,9 +35,6 @@ class NET_EXPORT_PRIVATE HttpResponseBodyDrainer {
// doesn't complete immediately, it will add itself to |session|.
void Start(HttpNetworkSession* session);
- // As above, but stop reading once |num_bytes_to_drain| has been reached.
- void StartWithSize(HttpNetworkSession* session, int num_bytes_to_drain);
-
private:
enum State {
STATE_DRAIN_RESPONSE_BODY,
@@ -54,7 +51,6 @@ class NET_EXPORT_PRIVATE HttpResponseBodyDrainer {
void OnTimerFired();
void Finish(int result);
- int read_size_;
scoped_refptr<IOBuffer> read_buf_;
const scoped_ptr<HttpStreamBase> stream_;
State next_state_;
diff --git a/chromium/net/http/http_response_body_drainer_unittest.cc b/chromium/net/http/http_response_body_drainer_unittest.cc
index f587b908529..42efa57f919 100644
--- a/chromium/net/http/http_response_body_drainer_unittest.cc
+++ b/chromium/net/http/http_response_body_drainer_unittest.cc
@@ -93,9 +93,6 @@ class MockHttpStream : public HttpStream {
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE {
return ERR_UNEXPECTED;
}
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
- return NULL;
- }
virtual bool CanFindEndOfResponse() const OVERRIDE { return true; }
virtual bool IsConnectionReused() const OVERRIDE { return false; }
@@ -305,22 +302,6 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) {
EXPECT_TRUE(result_waiter_.WaitForResult());
}
-TEST_F(HttpResponseBodyDrainerTest, StartBodyTooLarge) {
- int too_many_chunks =
- HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize;
- too_many_chunks += 1; // Now it's too large.
-
- mock_stream_->set_num_chunks(0);
- drainer_->StartWithSize(session_.get(), too_many_chunks * kMagicChunkSize);
- EXPECT_TRUE(result_waiter_.WaitForResult());
-}
-
-TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) {
- mock_stream_->set_num_chunks(0);
- drainer_->StartWithSize(session_.get(), 0);
- EXPECT_FALSE(result_waiter_.WaitForResult());
-}
-
} // namespace
} // namespace net
diff --git a/chromium/net/http/http_response_headers.cc b/chromium/net/http/http_response_headers.cc
index 289facd4f9e..b7ef98a5980 100644
--- a/chromium/net/http/http_response_headers.cc
+++ b/chromium/net/http/http_response_headers.cc
@@ -11,6 +11,7 @@
#include <algorithm>
+#include "base/format_macros.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/pickle.h"
@@ -21,6 +22,8 @@
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/escape.h"
+#include "net/http/http_byte_range.h"
+#include "net/http/http_log_util.h"
#include "net/http/http_util.h"
using base::StringPiece;
@@ -113,16 +116,10 @@ void CheckDoesNotHaveEmbededNulls(const std::string& str) {
CHECK(str.find('\0') == std::string::npos);
}
-bool ShouldShowHttpHeaderValue(const std::string& header_name) {
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- if (header_name == "Proxy-Authenticate")
- return false;
-#endif
- return true;
-}
-
} // namespace
+const char HttpResponseHeaders::kContentRange[] = "Content-Range";
+
struct HttpResponseHeaders::ParsedHeader {
// A header "continuation" contains only a subsequent value for the
// preceding header. (Header values are comma separated.)
@@ -372,6 +369,32 @@ void HttpResponseHeaders::ReplaceStatusLine(const std::string& new_status) {
MergeWithHeaders(new_raw_headers, empty_to_remove);
}
+void HttpResponseHeaders::UpdateWithNewRange(
+ const HttpByteRange& byte_range,
+ int64 resource_size,
+ bool replace_status_line) {
+ DCHECK(byte_range.IsValid());
+ DCHECK(byte_range.HasFirstBytePosition());
+ DCHECK(byte_range.HasLastBytePosition());
+
+ const char kLengthHeader[] = "Content-Length";
+ const char kRangeHeader[] = "Content-Range";
+
+ RemoveHeader(kLengthHeader);
+ RemoveHeader(kRangeHeader);
+
+ int64 start = byte_range.first_byte_position();
+ int64 end = byte_range.last_byte_position();
+ int64 range_len = end - start + 1;
+
+ if (replace_status_line)
+ ReplaceStatusLine("HTTP/1.1 206 Partial Content");
+
+ AddHeader(base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
+ kRangeHeader, start, end, resource_size));
+ AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
+}
+
void HttpResponseHeaders::Parse(const std::string& raw_input) {
raw_headers_.reserve(raw_input.size());
@@ -827,7 +850,7 @@ void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) {
}
void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) {
- result->insert("content-range");
+ result->insert(kContentRange);
}
void HttpResponseHeaders::AddSecurityStateHeaders(HeaderSet* result) {
@@ -894,7 +917,8 @@ bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) {
return (response_code == 301 ||
response_code == 302 ||
response_code == 303 ||
- response_code == 307);
+ response_code == 307 ||
+ response_code == 308);
}
// From RFC 2616 section 13.2.4:
@@ -993,6 +1017,9 @@ TimeDelta HttpResponseHeaders::GetFreshnessLifetime(
// time, if, based solely on the origin server's Expires or max-age value,
// the cached response is stale.)
//
+ // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an
+ // experimental RFC that adds 308 permanent redirect as well, for which "any
+ // future references ... SHOULD use one of the returned URIs."
if ((response_code_ == 200 || response_code_ == 203 ||
response_code_ == 206) &&
!HasHeaderValue("cache-control", "must-revalidate")) {
@@ -1006,8 +1033,10 @@ TimeDelta HttpResponseHeaders::GetFreshnessLifetime(
}
// These responses are implicitly fresh (unless otherwise overruled):
- if (response_code_ == 300 || response_code_ == 301 || response_code_ == 410)
- return TimeDelta::FromMicroseconds(kint64max);
+ if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 ||
+ response_code_ == 410) {
+ return TimeDelta::Max();
+ }
return TimeDelta(); // not fresh
}
@@ -1207,7 +1236,7 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position,
void* iter = NULL;
std::string content_range_spec;
*first_byte_position = *last_byte_position = *instance_length = -1;
- if (!EnumerateHeader(&iter, "content-range", &content_range_spec))
+ if (!EnumerateHeader(&iter, kContentRange, &content_range_spec))
return false;
// If the header value is empty, we have an invalid header.
@@ -1308,7 +1337,7 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position,
}
base::Value* HttpResponseHeaders::NetLogCallback(
- NetLog::LogLevel /* log_level */) const {
+ NetLog::LogLevel log_level) const {
base::DictionaryValue* dict = new base::DictionaryValue();
base::ListValue* headers = new base::ListValue();
headers->Append(new base::StringValue(GetStatusLine()));
@@ -1316,12 +1345,10 @@ base::Value* HttpResponseHeaders::NetLogCallback(
std::string name;
std::string value;
while (EnumerateHeaderLines(&iterator, &name, &value)) {
+ std::string log_value = ElideHeaderValueForNetLog(log_level, name, value);
headers->Append(
new base::StringValue(
- base::StringPrintf("%s: %s",
- name.c_str(),
- (ShouldShowHttpHeaderValue(name) ?
- value.c_str() : "[elided]"))));
+ base::StringPrintf("%s: %s", name.c_str(), log_value.c_str())));
}
dict->Set("headers", headers);
return dict;
@@ -1364,59 +1391,4 @@ bool HttpResponseHeaders::IsChunkEncoded() const {
HasHeaderValue("Transfer-Encoding", "chunked");
}
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-bool HttpResponseHeaders::GetChromeProxyBypassDuration(
- const std::string& action_prefix,
- base::TimeDelta* duration) const {
- void* iter = NULL;
- std::string value;
- std::string name = "chrome-proxy";
-
- while (EnumerateHeader(&iter, name, &value)) {
- if (value.size() > action_prefix.size()) {
- if (LowerCaseEqualsASCII(value.begin(),
- value.begin() + action_prefix.size(),
- action_prefix.c_str())) {
- int64 seconds;
- if (!base::StringToInt64(
- StringPiece(value.begin() + action_prefix.size(), value.end()),
- &seconds) || seconds < 0) {
- continue; // In case there is a well formed instruction.
- }
- *duration = TimeDelta::FromSeconds(seconds);
- return true;
- }
- }
- }
- return false;
-}
-
-bool HttpResponseHeaders::GetChromeProxyInfo(
- ChromeProxyInfo* proxy_info) const {
- DCHECK(proxy_info);
- proxy_info->bypass_all = false;
- proxy_info->bypass_duration = base::TimeDelta();
-
- // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
- // <duration> is the number of seconds to wait before retrying
- // the proxy. If the duration is 0, then the default proxy retry delay
- // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used.
- // 'bypass' instructs Chrome to bypass the currently connected Chrome proxy,
- // whereas 'block' instructs Chrome to bypass all available Chrome proxies.
-
- // 'block' takes precedence over 'bypass', so look for it first.
- // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
- if (GetChromeProxyBypassDuration("block=", &proxy_info->bypass_duration)) {
- proxy_info->bypass_all = true;
- return true;
- }
-
- // Next, look for 'bypass'.
- if (GetChromeProxyBypassDuration("bypass=", &proxy_info->bypass_duration))
- return true;
-
- return false;
-}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
-
} // namespace net
diff --git a/chromium/net/http/http_response_headers.h b/chromium/net/http/http_response_headers.h
index e54ac5df669..2d306c95ca0 100644
--- a/chromium/net/http/http_response_headers.h
+++ b/chromium/net/http/http_response_headers.h
@@ -26,6 +26,8 @@ class TimeDelta;
namespace net {
+class HttpByteRange;
+
// HttpResponseHeaders: parses and holds HTTP response headers.
class NET_EXPORT HttpResponseHeaders
: public base::RefCountedThreadSafe<HttpResponseHeaders> {
@@ -41,6 +43,8 @@ class NET_EXPORT HttpResponseHeaders
static const PersistOptions PERSIST_SANS_RANGES = 1 << 4;
static const PersistOptions PERSIST_SANS_SECURITY_STATE = 1 << 5;
+ static const char kContentRange[];
+
// Parses the given raw_headers. raw_headers should be formatted thus:
// includes the http status response line, each line is \0-terminated, and
// it's terminated by an empty line (ie, 2 \0s in a row).
@@ -81,6 +85,15 @@ class NET_EXPORT HttpResponseHeaders
// not have any EOL).
void ReplaceStatusLine(const std::string& new_status);
+ // Updates headers (Content-Length and Content-Range) in the |headers| to
+ // include the right content length and range for |byte_range|. This also
+ // updates HTTP status line if |replace_status_line| is true.
+ // |byte_range| must have a valid, bounded range (i.e. coming from a valid
+ // response or should be usable for a response).
+ void UpdateWithNewRange(const HttpByteRange& byte_range,
+ int64 resource_size,
+ bool replace_status_line);
+
// Creates a normalized header string. The output will be formatted exactly
// like so:
// HTTP/<version> <status_code> <status_text>\n
@@ -250,28 +263,6 @@ class NET_EXPORT HttpResponseHeaders
// Returns true if the response is chunk-encoded.
bool IsChunkEncoded() const;
-#if defined (SPDY_PROXY_AUTH_ORIGIN)
- // Contains instructions contained in the Chrome-Proxy header.
- struct ChromeProxyInfo {
- ChromeProxyInfo() : bypass_all(false) {}
-
- // True if Chrome should bypass all available Chrome proxies. False if only
- // the currently connected Chrome proxy should be bypassed.
- bool bypass_all;
-
- // Amount of time to bypass the Chrome proxy or proxies.
- base::TimeDelta bypass_duration;
- };
-
- // Returns true if the Chrome-Proxy header is present and contains a bypass
- // delay. Sets |proxy_info->bypass_duration| to the specified delay if greater
- // than 0, and to 0 otherwise to indicate that the default proxy delay
- // (as specified in |ProxyList::UpdateRetryInfoOnFallback|) should be used.
- // If all available Chrome proxies should by bypassed, |bypass_all| is set to
- // true. |proxy_info| must be non-NULL.
- bool GetChromeProxyInfo(ChromeProxyInfo* proxy_info) const;
-#endif
-
// Creates a Value for use with the NetLog containing the response headers.
base::Value* NetLogCallback(NetLog::LogLevel log_level) const;
@@ -370,13 +361,6 @@ class NET_EXPORT HttpResponseHeaders
// Adds the set of transport security state headers.
static void AddSecurityStateHeaders(HeaderSet* header_names);
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- // Searches for the specified Chrome-Proxy action, and if present interprets
- // its value as a duration in seconds.
- bool GetChromeProxyBypassDuration(const std::string& action_prefix,
- base::TimeDelta* duration) const;
-#endif
-
// We keep a list of ParsedHeader objects. These tell us where to locate the
// header-value pairs within raw_headers_.
HeaderList parsed_;
diff --git a/chromium/net/http/http_response_headers_unittest.cc b/chromium/net/http/http_response_headers_unittest.cc
index 4be74783b74..cc236d183e8 100644
--- a/chromium/net/http/http_response_headers_unittest.cc
+++ b/chromium/net/http/http_response_headers_unittest.cc
@@ -9,6 +9,7 @@
#include "base/pickle.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "net/http/http_byte_range.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -807,6 +808,11 @@ TEST(HttpResponseHeadersTest, RequiresValidation) {
"\n",
false
},
+ // another cached permanent redirect
+ { "HTTP/1.1 308 Permanent Redirect\n"
+ "\n",
+ false
+ },
// cached redirect: not reusable even though by default it would be
{ "HTTP/1.1 300 Multiple Choices\n"
"Cache-Control: no-cache\n"
@@ -1851,6 +1857,58 @@ TEST(HttpResponseHeadersTest, ReplaceStatus) {
}
}
+TEST(HttpResponseHeadersTest, UpdateWithNewRange) {
+ const struct {
+ const char* orig_headers;
+ const char* expected_headers;
+ const char* expected_headers_with_replaced_status;
+ } tests[] = {
+ { "HTTP/1.1 200 OK\n"
+ "Content-Length: 450\n",
+
+ "HTTP/1.1 200 OK\n"
+ "Content-Range: bytes 3-5/450\n"
+ "Content-Length: 3\n",
+
+ "HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes 3-5/450\n"
+ "Content-Length: 3\n",
+ },
+ { "HTTP/1.1 200 OK\n"
+ "Content-Length: 5\n",
+
+ "HTTP/1.1 200 OK\n"
+ "Content-Range: bytes 3-5/5\n"
+ "Content-Length: 3\n",
+
+ "HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes 3-5/5\n"
+ "Content-Length: 3\n",
+ },
+ };
+ const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ std::string orig_headers(tests[i].orig_headers);
+ std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
+ scoped_refptr<net::HttpResponseHeaders> parsed(
+ new net::HttpResponseHeaders(orig_headers + '\0'));
+ int64 content_size = parsed->GetContentLength();
+ std::string resulting_headers;
+
+ // Update headers without replacing status line.
+ parsed->UpdateWithNewRange(range, content_size, false);
+ parsed->GetNormalizedHeaders(&resulting_headers);
+ EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
+
+ // Replace status line too.
+ parsed->UpdateWithNewRange(range, content_size, true);
+ parsed->GetNormalizedHeaders(&resulting_headers);
+ EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status),
+ resulting_headers);
+ }
+}
+
TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
std::string headers("HTTP/1.1 404\n"
"Content-Length: 450\n"
@@ -1877,156 +1935,3 @@ TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
parsed->GetNormalizedHeaders(&normalized_recreated);
EXPECT_EQ(normalized_parsed, normalized_recreated);
}
-
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-TEST(HttpResponseHeadersTest, GetProxyBypassInfo) {
- const struct {
- const char* headers;
- bool expected_result;
- int64 expected_retry_delay;
- bool expected_bypass_all;
- } tests[] = {
- { "HTTP/1.1 200 OK\n"
- "Content-Length: 999\n",
- false,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Content-Length: 999\n",
- false,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=0\n"
- "Content-Length: 999\n",
- true,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=-1\n"
- "Content-Length: 999\n",
- false,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=xyz\n"
- "Content-Length: 999\n",
- false,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass\n"
- "Content-Length: 999\n",
- false,
- 0,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: foo=abc, bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=86400, bar=abc\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=3600\n"
- "Chrome-Proxy: bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 3600,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=3600, bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 3600,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=, bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass\n"
- "Chrome-Proxy: bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: block=, block=3600\n"
- "Content-Length: 999\n",
- true,
- 3600,
- true,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Chrome-Proxy: bypass=86400, block=3600\n"
- "Content-Length: 999\n",
- true,
- 3600,
- true,
- },
- { "HTTP/1.1 200 OK\n"
- "connection: proxy-bypass\n"
- "Chrome-Proxy: block=, bypass=86400\n"
- "Content-Length: 999\n",
- true,
- 86400,
- false,
- },
- };
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
- std::string headers(tests[i].headers);
- HeadersToRaw(&headers);
- scoped_refptr<net::HttpResponseHeaders> parsed(
- new net::HttpResponseHeaders(headers));
-
- net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info;
- EXPECT_EQ(tests[i].expected_result,
- parsed->GetChromeProxyInfo(&chrome_proxy_info));
- EXPECT_EQ(tests[i].expected_retry_delay,
- chrome_proxy_info.bypass_duration.InSeconds());
- EXPECT_EQ(tests[i].expected_bypass_all,
- chrome_proxy_info.bypass_all);
- }
-}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
diff --git a/chromium/net/http/http_response_info.cc b/chromium/net/http/http_response_info.cc
index 4f9e014cfb2..83896736a12 100644
--- a/chromium/net/http/http_response_info.cc
+++ b/chromium/net/http/http_response_info.cc
@@ -113,6 +113,7 @@ HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs)
was_fetched_via_spdy(rhs.was_fetched_via_spdy),
was_npn_negotiated(rhs.was_npn_negotiated),
was_fetched_via_proxy(rhs.was_fetched_via_proxy),
+ proxy_server(rhs.proxy_server),
did_use_http_auth(rhs.did_use_http_auth),
socket_address(rhs.socket_address),
npn_negotiated_protocol(rhs.npn_negotiated_protocol),
@@ -135,6 +136,7 @@ HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) {
server_data_unavailable = rhs.server_data_unavailable;
network_accessed = rhs.network_accessed;
was_fetched_via_spdy = rhs.was_fetched_via_spdy;
+ proxy_server = rhs.proxy_server;
was_npn_negotiated = rhs.was_npn_negotiated;
was_fetched_via_proxy = rhs.was_fetched_via_proxy;
did_use_http_auth = rhs.did_use_http_auth;
@@ -340,8 +342,8 @@ void HttpResponseInfo::Persist(Pickle* pickle,
for (SignedCertificateTimestampAndStatusList::const_iterator it =
ssl_info.signed_certificate_timestamps.begin(); it !=
ssl_info.signed_certificate_timestamps.end(); ++it) {
- it->sct_->Persist(pickle);
- pickle->WriteUInt16(it->status_);
+ it->sct->Persist(pickle);
+ pickle->WriteUInt16(it->status);
}
}
}
@@ -367,10 +369,8 @@ HttpResponseInfo::ConnectionInfo HttpResponseInfo::ConnectionInfoFromNextProto(
case kProtoSPDY3:
case kProtoSPDY31:
return CONNECTION_INFO_SPDY3;
- case kProtoSPDY4a2:
- return CONNECTION_INFO_SPDY4A2;
- case kProtoHTTP2Draft04:
- return CONNECTION_INFO_HTTP2_DRAFT_04;
+ case kProtoSPDY4:
+ return CONNECTION_INFO_SPDY4;
case kProtoQUIC1SPDY3:
return CONNECTION_INFO_QUIC1_SPDY3;
@@ -395,10 +395,10 @@ std::string HttpResponseInfo::ConnectionInfoToString(
return "spdy/2";
case CONNECTION_INFO_SPDY3:
return "spdy/3";
- case CONNECTION_INFO_SPDY4A2:
- return "spdy/4a2";
- case CONNECTION_INFO_HTTP2_DRAFT_04:
- return "HTTP-draft-04/2.0";
+ case CONNECTION_INFO_SPDY4:
+ // This is the HTTP/2 draft 12 identifier. For internal
+ // consistency, HTTP/2 is named SPDY4 within Chromium.
+ return "h2-12";
case CONNECTION_INFO_QUIC1_SPDY3:
return "quic/1+spdy/3";
case NUM_OF_CONNECTION_INFOS:
diff --git a/chromium/net/http/http_response_info.h b/chromium/net/http/http_response_info.h
index f0908b0ab46..efd76fe7b51 100644
--- a/chromium/net/http/http_response_info.h
+++ b/chromium/net/http/http_response_info.h
@@ -37,9 +37,8 @@ class NET_EXPORT HttpResponseInfo {
CONNECTION_INFO_HTTP1 = 1,
CONNECTION_INFO_DEPRECATED_SPDY2 = 2,
CONNECTION_INFO_SPDY3 = 3,
- CONNECTION_INFO_SPDY4A2 = 4,
+ CONNECTION_INFO_SPDY4 = 4,
CONNECTION_INFO_QUIC1_SPDY3 = 5,
- CONNECTION_INFO_HTTP2_DRAFT_04 = 6,
NUM_OF_CONNECTION_INFOS,
};
@@ -59,12 +58,14 @@ class NET_EXPORT HttpResponseInfo {
bool response_truncated) const;
// The following is only defined if the request_time member is set.
- // If this response was resurrected from cache, then this bool is set, and
+ // If this resource was found in the cache, then this bool is set, and
// request_time may corresponds to a time "far" in the past. Note that
// stale content (perhaps un-cacheable) may be fetched from cache subject to
// the load flags specified on the request info. For example, this is done
// when a user presses the back button to re-render pages, or at startup,
// when reloading previously visited pages (without going over the network).
+ // Note also that under normal circumstances, was_cached is set to the correct
+ // value even if the request fails.
bool was_cached;
// True if the request was fetched from cache rather than the network
@@ -84,8 +85,10 @@ class NET_EXPORT HttpResponseInfo {
// True if the request was fetched via an explicit proxy. The proxy could
// be any type of proxy, HTTP or SOCKS. Note, we do not know if a
- // transparent proxy may have been involved.
+ // transparent proxy may have been involved. If true, |proxy_server| contains
+ // the name of the proxy server that was used.
bool was_fetched_via_proxy;
+ HostPortPair proxy_server;
// Whether the request use http proxy or server authentication.
bool did_use_http_auth;
diff --git a/chromium/net/http/http_security_headers.cc b/chromium/net/http/http_security_headers.cc
index 0c3305f6e42..8d0c1465307 100644
--- a/chromium/net/http/http_security_headers.cc
+++ b/chromium/net/http/http_security_headers.cc
@@ -253,6 +253,7 @@ bool ParseHSTSHeader(const std::string& value,
}
switch (state) {
+ case DIRECTIVE_END:
case AFTER_MAX_AGE:
case AFTER_INCLUDE_SUBDOMAINS:
case AFTER_UNKNOWN_LABEL:
@@ -260,7 +261,6 @@ bool ParseHSTSHeader(const std::string& value,
*include_subdomains = include_subdomains_candidate;
return true;
case START:
- case DIRECTIVE_END:
case AFTER_MAX_AGE_LABEL:
case AFTER_MAX_AGE_EQUALS:
return false;
diff --git a/chromium/net/http/http_security_headers_unittest.cc b/chromium/net/http/http_security_headers_unittest.cc
index 42a5ee98960..ce919ff81f3 100644
--- a/chromium/net/http/http_security_headers_unittest.cc
+++ b/chromium/net/http/http_security_headers_unittest.cc
@@ -90,8 +90,6 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) {
&include_subdomains));
EXPECT_FALSE(ParseHSTSHeader("max-age=-3488923", &max_age,
&include_subdomains));
- EXPECT_FALSE(ParseHSTSHeader("max-age=3488923;", &max_age,
- &include_subdomains));
EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 e", &max_age,
&include_subdomains));
EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 includesubdomain",
@@ -114,6 +112,16 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) {
&max_age, &include_subdomains));
EXPECT_FALSE(ParseHSTSHeader("max-age=34889 includesubdomains",
&max_age, &include_subdomains));
+ EXPECT_FALSE(ParseHSTSHeader(";;;; ;;;",
+ &max_age, &include_subdomains));
+ EXPECT_FALSE(ParseHSTSHeader(";;;; includeSubDomains;;;",
+ &max_age, &include_subdomains));
+ EXPECT_FALSE(ParseHSTSHeader(" includeSubDomains; ",
+ &max_age, &include_subdomains));
+ EXPECT_FALSE(ParseHSTSHeader(";",
+ &max_age, &include_subdomains));
+ EXPECT_FALSE(ParseHSTSHeader("max-age; ;",
+ &max_age, &include_subdomains));
// Check the out args were not updated by checking the default
// values for its predictable fields.
@@ -223,6 +231,9 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) {
EXPECT_EQ(expect_max_age, max_age);
EXPECT_FALSE(include_subdomains);
+ EXPECT_TRUE(ParseHSTSHeader("max-age=3488923;", &max_age,
+ &include_subdomains));
+
EXPECT_TRUE(ParseHSTSHeader(" Max-agE = 567", &max_age,
&include_subdomains));
expect_max_age = base::TimeDelta::FromSeconds(567);
@@ -311,6 +322,46 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) {
EXPECT_TRUE(include_subdomains);
EXPECT_TRUE(ParseHSTSHeader(
+ "max-age=394082038 ; incLudesUbdOmains;", &max_age,
+ &include_subdomains));
+ expect_max_age = base::TimeDelta::FromSeconds(
+ std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038))));
+ EXPECT_EQ(expect_max_age, max_age);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(ParseHSTSHeader(
+ ";; max-age=394082038 ; incLudesUbdOmains; ;", &max_age,
+ &include_subdomains));
+ expect_max_age = base::TimeDelta::FromSeconds(
+ std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038))));
+ EXPECT_EQ(expect_max_age, max_age);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(ParseHSTSHeader(
+ ";; max-age=394082038 ;", &max_age,
+ &include_subdomains));
+ expect_max_age = base::TimeDelta::FromSeconds(
+ std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038))));
+ EXPECT_EQ(expect_max_age, max_age);
+ EXPECT_FALSE(include_subdomains);
+
+ EXPECT_TRUE(ParseHSTSHeader(
+ ";; ; ; max-age=394082038;;; includeSubdomains ;; ;", &max_age,
+ &include_subdomains));
+ expect_max_age = base::TimeDelta::FromSeconds(
+ std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038))));
+ EXPECT_EQ(expect_max_age, max_age);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(ParseHSTSHeader(
+ "incLudesUbdOmains ; max-age=394082038 ;;", &max_age,
+ &include_subdomains));
+ expect_max_age = base::TimeDelta::FromSeconds(
+ std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038))));
+ EXPECT_EQ(expect_max_age, max_age);
+ EXPECT_TRUE(include_subdomains);
+
+ EXPECT_TRUE(ParseHSTSHeader(
" max-age=0 ; incLudesUbdOmains ", &max_age,
&include_subdomains));
expect_max_age = base::TimeDelta::FromSeconds(0);
@@ -450,13 +501,15 @@ TEST_F(HttpSecurityHeadersTest, ValidPKPHeadersSHA256) {
TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) {
TransportSecurityState state;
- TransportSecurityState::DomainState domain_state;
+ TransportSecurityState::DomainState static_domain_state;
// docs.google.com has preloaded pins.
+ const bool sni_enabled = true;
std::string domain = "docs.google.com";
- EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state));
- EXPECT_GT(domain_state.static_spki_hashes.size(), 1UL);
- HashValueVector saved_hashes = domain_state.static_spki_hashes;
+ EXPECT_TRUE(
+ state.GetStaticDomainState(domain, sni_enabled, &static_domain_state));
+ EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL);
+ HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes;
// Add a header, which should only update the dynamic state.
HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1);
@@ -471,49 +524,185 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) {
ssl_info.public_key_hashes.push_back(saved_hashes[0]);
EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info));
- // Expect the preloaded state to remain unchanged.
- std::string canonicalized_host = TransportSecurityState::CanonicalizeHost(
- domain);
- TransportSecurityState::DomainState static_domain_state;
- EXPECT_TRUE(state.GetStaticDomainState(canonicalized_host,
- true,
- &static_domain_state));
+ // Expect the static state to remain unchanged.
+ TransportSecurityState::DomainState new_static_domain_state;
+ EXPECT_TRUE(state.GetStaticDomainState(
+ domain, sni_enabled, &new_static_domain_state));
for (size_t i = 0; i < saved_hashes.size(); ++i) {
- EXPECT_TRUE(HashValuesEqual(
- saved_hashes[i])(static_domain_state.static_spki_hashes[i]));
+ EXPECT_TRUE(HashValuesEqual(saved_hashes[i])(
+ new_static_domain_state.pkp.spki_hashes[i]));
}
// Expect the dynamic state to reflect the header.
TransportSecurityState::DomainState dynamic_domain_state;
EXPECT_TRUE(state.GetDynamicDomainState(domain, &dynamic_domain_state));
- EXPECT_EQ(2UL, dynamic_domain_state.dynamic_spki_hashes.size());
+ EXPECT_EQ(2UL, dynamic_domain_state.pkp.spki_hashes.size());
- HashValueVector::const_iterator hash = std::find_if(
- dynamic_domain_state.dynamic_spki_hashes.begin(),
- dynamic_domain_state.dynamic_spki_hashes.end(),
- HashValuesEqual(good_hash));
- EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash);
+ HashValueVector::const_iterator hash =
+ std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(),
+ dynamic_domain_state.pkp.spki_hashes.end(),
+ HashValuesEqual(good_hash));
+ EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash);
- hash = std::find_if(
- dynamic_domain_state.dynamic_spki_hashes.begin(),
- dynamic_domain_state.dynamic_spki_hashes.end(),
- HashValuesEqual(backup_hash));
- EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash);
+ hash = std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(),
+ dynamic_domain_state.pkp.spki_hashes.end(),
+ HashValuesEqual(backup_hash));
+ EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash);
// Expect the overall state to reflect the header, too.
- EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state));
- EXPECT_EQ(2UL, domain_state.dynamic_spki_hashes.size());
+ EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled));
+ HashValueVector hashes;
+ hashes.push_back(good_hash);
+ std::string failure_log;
+ EXPECT_TRUE(
+ state.CheckPublicKeyPins(domain, sni_enabled, hashes, &failure_log));
- hash = std::find_if(domain_state.dynamic_spki_hashes.begin(),
- domain_state.dynamic_spki_hashes.end(),
+ TransportSecurityState::DomainState new_dynamic_domain_state;
+ EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state));
+ EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size());
+
+ hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(),
+ new_dynamic_domain_state.pkp.spki_hashes.end(),
HashValuesEqual(good_hash));
- EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash);
+ EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash);
+
+ hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(),
+ new_dynamic_domain_state.pkp.spki_hashes.end(),
+ HashValuesEqual(backup_hash));
+ EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash);
+}
+
+// Failing on win_chromium_rel. crbug.com/375538
+#if defined(OS_WIN)
+#define MAYBE_UpdateDynamicPKPMaxAge0 DISABLED_UpdateDynamicPKPMaxAge0
+#else
+#define MAYBE_UpdateDynamicPKPMaxAge0 UpdateDynamicPKPMaxAge0
+#endif
+TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) {
+ TransportSecurityState state;
+ TransportSecurityState::DomainState static_domain_state;
+
+ // docs.google.com has preloaded pins.
+ const bool sni_enabled = true;
+ std::string domain = "docs.google.com";
+ ASSERT_TRUE(
+ state.GetStaticDomainState(domain, sni_enabled, &static_domain_state));
+ EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL);
+ HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes;
+
+ // Add a header, which should only update the dynamic state.
+ HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1);
+ std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1);
+ std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1);
+ std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin;
+
+ // Construct a fake SSLInfo that will pass AddHPKPHeader's checks.
+ SSLInfo ssl_info;
+ ssl_info.public_key_hashes.push_back(good_hash);
+ ssl_info.public_key_hashes.push_back(saved_hashes[0]);
+ EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info));
+
+ // Expect the static state to remain unchanged.
+ TransportSecurityState::DomainState new_static_domain_state;
+ EXPECT_TRUE(state.GetStaticDomainState(
+ domain, sni_enabled, &new_static_domain_state));
+ EXPECT_EQ(saved_hashes.size(),
+ new_static_domain_state.pkp.spki_hashes.size());
+ for (size_t i = 0; i < saved_hashes.size(); ++i) {
+ EXPECT_TRUE(HashValuesEqual(saved_hashes[i])(
+ new_static_domain_state.pkp.spki_hashes[i]));
+ }
- hash = std::find_if(
- domain_state.dynamic_spki_hashes.begin(),
- domain_state.dynamic_spki_hashes.end(),
- HashValuesEqual(backup_hash));
- EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash);
+ // Expect the dynamic state to have pins.
+ TransportSecurityState::DomainState new_dynamic_domain_state;
+ EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state));
+ EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size());
+ EXPECT_TRUE(new_dynamic_domain_state.HasPublicKeyPins());
+
+ // Now set another header with max-age=0, and check that the pins are
+ // cleared in the dynamic state only.
+ header = "max-age = 0; " + good_pin + "; " + backup_pin;
+ EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info));
+
+ // Expect the static state to remain unchanged.
+ TransportSecurityState::DomainState new_static_domain_state2;
+ EXPECT_TRUE(state.GetStaticDomainState(
+ domain, sni_enabled, &new_static_domain_state2));
+ EXPECT_EQ(saved_hashes.size(),
+ new_static_domain_state2.pkp.spki_hashes.size());
+ for (size_t i = 0; i < saved_hashes.size(); ++i) {
+ EXPECT_TRUE(HashValuesEqual(saved_hashes[i])(
+ new_static_domain_state2.pkp.spki_hashes[i]));
+ }
+
+ // Expect the dynamic pins to be gone.
+ TransportSecurityState::DomainState new_dynamic_domain_state2;
+ EXPECT_FALSE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state2));
+
+ // Expect the exact-matching static policy to continue to apply, even
+ // though dynamic policy has been removed. (This policy may change in the
+ // future, in which case this test must be updated.)
+ EXPECT_TRUE(state.HasPublicKeyPins(domain, true));
+ EXPECT_TRUE(state.ShouldSSLErrorsBeFatal(domain, true));
+ std::string failure_log;
+ // Damage the hashes to cause a pin validation failure.
+ new_static_domain_state2.pkp.spki_hashes[0].data()[0] ^= 0x80;
+ new_static_domain_state2.pkp.spki_hashes[1].data()[0] ^= 0x80;
+ EXPECT_FALSE(state.CheckPublicKeyPins(
+ domain, true, new_static_domain_state2.pkp.spki_hashes, &failure_log));
+ EXPECT_NE(0UL, failure_log.length());
+}
+#undef MAYBE_UpdateDynamicPKPMaxAge0
+
+// Tests that when a static HSTS and a static HPKP entry are present, adding a
+// dynamic HSTS header does not clobber the static HPKP entry. Further, adding a
+// dynamic HPKP entry could not affect the HSTS entry for the site.
+TEST_F(HttpSecurityHeadersTest, NoClobberPins) {
+ TransportSecurityState state;
+ TransportSecurityState::DomainState domain_state;
+
+ // accounts.google.com has preloaded pins.
+ std::string domain = "accounts.google.com";
+
+ // Retrieve the DomainState as it is by default, including its known good
+ // pins.
+ const bool sni_enabled = true;
+ EXPECT_TRUE(state.GetStaticDomainState(domain, sni_enabled, &domain_state));
+ HashValueVector saved_hashes = domain_state.pkp.spki_hashes;
+ EXPECT_TRUE(domain_state.ShouldUpgradeToSSL());
+ EXPECT_TRUE(domain_state.HasPublicKeyPins());
+ EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled));
+ EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled));
+
+ // Add a dynamic HSTS header. CheckPublicKeyPins should still pass when given
+ // the original |saved_hashes|, indicating that the static PKP data is still
+ // configured for the domain.
+ EXPECT_TRUE(state.AddHSTSHeader(domain, "includesubdomains; max-age=10000"));
+ EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled));
+ std::string failure_log;
+ EXPECT_TRUE(state.CheckPublicKeyPins(
+ domain, sni_enabled, saved_hashes, &failure_log));
+
+ // Add an HPKP header, which should only update the dynamic state.
+ HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1);
+ std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1);
+ std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1);
+ std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin;
+
+ // Construct a fake SSLInfo that will pass AddHPKPHeader's checks.
+ SSLInfo ssl_info;
+ ssl_info.public_key_hashes.push_back(good_hash);
+ ssl_info.public_key_hashes.push_back(saved_hashes[0]);
+ EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info));
+
+ EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info));
+ // HSTS should still be configured for this domain.
+ EXPECT_TRUE(domain_state.ShouldUpgradeToSSL());
+ EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled));
+ // The dynamic pins, which do not match |saved_hashes|, should take
+ // precedence over the static pins and cause the check to fail.
+ EXPECT_FALSE(state.CheckPublicKeyPins(
+ domain, sni_enabled, saved_hashes, &failure_log));
}
}; // namespace net
diff --git a/chromium/net/http/http_server_properties.cc b/chromium/net/http/http_server_properties.cc
index a10d5060c06..19b334c1fb3 100644
--- a/chromium/net/http/http_server_properties.cc
+++ b/chromium/net/http/http_server_properties.cc
@@ -5,6 +5,7 @@
#include "net/http/http_server_properties.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
namespace net {
@@ -19,8 +20,7 @@ const char* const kAlternateProtocolStrings[] = {
"npn-spdy/2",
"npn-spdy/3",
"npn-spdy/3.1",
- "npn-spdy/4a2",
- "npn-HTTP-draft-04/2.0",
+ "npn-h2-12", // HTTP/2 draft 12. Called SPDY4 internally.
"quic"
};
const char kBrokenAlternateProtocol[] = "Broken";
@@ -31,6 +31,28 @@ COMPILE_ASSERT(
} // namespace
+void HistogramAlternateProtocolUsage(
+ AlternateProtocolUsage usage,
+ AlternateProtocolExperiment alternate_protocol_experiment) {
+ UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage", usage,
+ ALTERNATE_PROTOCOL_USAGE_MAX);
+ if (alternate_protocol_experiment ==
+ ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS) {
+ UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage.200Truncated", usage,
+ ALTERNATE_PROTOCOL_USAGE_MAX);
+ } else if (alternate_protocol_experiment ==
+ ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS) {
+ UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage.1000Truncated", usage,
+ ALTERNATE_PROTOCOL_USAGE_MAX);
+ }
+}
+
+void HistogramBrokenAlternateProtocolLocation(
+ BrokenAlternateProtocolLocation location){
+ UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolBrokenLocation", location,
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX);
+}
+
bool IsAlternateProtocolValid(AlternateProtocol protocol) {
return protocol >= ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION &&
protocol <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION;
@@ -41,8 +63,7 @@ const char* AlternateProtocolToString(AlternateProtocol protocol) {
case DEPRECATED_NPN_SPDY_2:
case NPN_SPDY_3:
case NPN_SPDY_3_1:
- case NPN_SPDY_4A2:
- case NPN_HTTP2_DRAFT_04:
+ case NPN_SPDY_4:
case QUIC:
DCHECK(IsAlternateProtocolValid(protocol));
return kAlternateProtocolStrings[
@@ -76,10 +97,8 @@ AlternateProtocol AlternateProtocolFromNextProto(NextProto next_proto) {
return NPN_SPDY_3;
case kProtoSPDY31:
return NPN_SPDY_3_1;
- case kProtoSPDY4a2:
- return NPN_SPDY_4A2;
- case kProtoHTTP2Draft04:
- return NPN_HTTP2_DRAFT_04;
+ case kProtoSPDY4:
+ return NPN_SPDY_4;
case kProtoQUIC1SPDY3:
return QUIC;
diff --git a/chromium/net/http/http_server_properties.h b/chromium/net/http/http_server_properties.h
index 72fda4355b1..88d1f3cefba 100644
--- a/chromium/net/http/http_server_properties.h
+++ b/chromium/net/http/http_server_properties.h
@@ -8,25 +8,67 @@
#include <map>
#include <string>
#include "base/basictypes.h"
+#include "base/containers/mru_cache.h"
#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
-#include "net/http/http_pipelined_host_capability.h"
#include "net/socket/next_proto.h"
#include "net/spdy/spdy_framer.h" // TODO(willchan): Reconsider this.
namespace net {
+enum AlternateProtocolExperiment {
+ // 200 alternate_protocol servers are loaded (persisted 200 MRU servers).
+ ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT = 0,
+ // 200 alternate_protocol servers are loaded (persisted 1000 MRU servers).
+ ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS,
+ // 1000 alternate_protocol servers are loaded (persisted 1000 MRU servers).
+ ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS,
+};
+
+enum AlternateProtocolUsage {
+ // Alternate Protocol was used without racing a normal connection.
+ ALTERNATE_PROTOCOL_USAGE_NO_RACE = 0,
+ // Alternate Protocol was used by winning a race with a normal connection.
+ ALTERNATE_PROTOCOL_USAGE_WON_RACE = 1,
+ // Alternate Protocol was not used by losing a race with a normal connection.
+ ALTERNATE_PROTOCOL_USAGE_LOST_RACE = 2,
+ // Alternate Protocol was not used because no Alternate-Protocol information
+ // was available when the request was issued, but an Alternate-Protocol header
+ // was present in the response.
+ ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING = 3,
+ // Alternate Protocol was not used because it was marked broken.
+ ALTERNATE_PROTOCOL_USAGE_BROKEN = 4,
+ // Maximum value for the enum.
+ ALTERNATE_PROTOCOL_USAGE_MAX,
+};
+
+// Log a histogram to reflect |usage| and |alternate_protocol_experiment|.
+NET_EXPORT void HistogramAlternateProtocolUsage(
+ AlternateProtocolUsage usage,
+ AlternateProtocolExperiment alternate_protocol_experiment);
+
+enum BrokenAlternateProtocolLocation {
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB = 0,
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY = 1,
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT = 2,
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN = 3,
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX,
+};
+
+// Log a histogram to reflect |location|.
+NET_EXPORT void HistogramBrokenAlternateProtocolLocation(
+ BrokenAlternateProtocolLocation location);
+
enum AlternateProtocol {
DEPRECATED_NPN_SPDY_2 = 0,
ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION = DEPRECATED_NPN_SPDY_2,
NPN_SPDY_MINIMUM_VERSION = DEPRECATED_NPN_SPDY_2,
NPN_SPDY_3,
NPN_SPDY_3_1,
- NPN_SPDY_4A2,
- // We lump in HTTP/2 with the SPDY protocols for now.
- NPN_HTTP2_DRAFT_04,
- NPN_SPDY_MAXIMUM_VERSION = NPN_HTTP2_DRAFT_04,
+ NPN_SPDY_4, // SPDY4 is HTTP/2.
+ NPN_SPDY_MAXIMUM_VERSION = NPN_SPDY_4,
QUIC,
ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION = QUIC,
ALTERNATE_PROTOCOL_BROKEN, // The alternate protocol is known to be broken.
@@ -61,10 +103,9 @@ struct NET_EXPORT PortAlternateProtocolPair {
AlternateProtocol protocol;
};
-typedef std::map<HostPortPair, PortAlternateProtocolPair> AlternateProtocolMap;
-typedef std::map<HostPortPair, SettingsMap> SpdySettingsMap;
-typedef std::map<HostPortPair,
- HttpPipelinedHostCapability> PipelineCapabilityMap;
+typedef base::MRUCache<
+ HostPortPair, PortAlternateProtocolPair> AlternateProtocolMap;
+typedef base::MRUCache<HostPortPair, SettingsMap> SpdySettingsMap;
extern const char kAlternateProtocolHeader[];
@@ -75,6 +116,11 @@ extern const char kAlternateProtocolHeader[];
// * Spdy Settings (like CWND ID field)
class NET_EXPORT HttpServerProperties {
public:
+ struct NetworkStats {
+ base::TimeDelta srtt;
+ uint64 bandwidth_estimate;
+ };
+
HttpServerProperties() {}
virtual ~HttpServerProperties() {}
@@ -85,7 +131,7 @@ class NET_EXPORT HttpServerProperties {
virtual void Clear() = 0;
// Returns true if |server| supports SPDY.
- virtual bool SupportsSpdy(const HostPortPair& server) const = 0;
+ virtual bool SupportsSpdy(const HostPortPair& server) = 0;
// Add |server| into the persistent store. Should only be called from IO
// thread.
@@ -93,12 +139,12 @@ class NET_EXPORT HttpServerProperties {
bool support_spdy) = 0;
// Returns true if |server| has an Alternate-Protocol header.
- virtual bool HasAlternateProtocol(const HostPortPair& server) const = 0;
+ virtual bool HasAlternateProtocol(const HostPortPair& server) = 0;
// Returns the Alternate-Protocol and port for |server|.
// HasAlternateProtocol(server) must be true.
virtual PortAlternateProtocolPair GetAlternateProtocol(
- const HostPortPair& server) const = 0;
+ const HostPortPair& server) = 0;
// Sets the Alternate-Protocol for |server|.
virtual void SetAlternateProtocol(const HostPortPair& server,
@@ -108,13 +154,29 @@ class NET_EXPORT HttpServerProperties {
// Sets the Alternate-Protocol for |server| to be BROKEN.
virtual void SetBrokenAlternateProtocol(const HostPortPair& server) = 0;
+ // Returns true if Alternate-Protocol for |server| was recently BROKEN.
+ virtual bool WasAlternateProtocolRecentlyBroken(
+ const HostPortPair& server) = 0;
+
+ // Confirms that Alternate-Protocol for |server| is working.
+ virtual void ConfirmAlternateProtocol(const HostPortPair& server) = 0;
+
+ // Clears the Alternate-Protocol for |server|.
+ virtual void ClearAlternateProtocol(const HostPortPair& server) = 0;
+
// Returns all Alternate-Protocol mappings.
virtual const AlternateProtocolMap& alternate_protocol_map() const = 0;
+ virtual void SetAlternateProtocolExperiment(
+ AlternateProtocolExperiment experiment) = 0;
+
+ virtual AlternateProtocolExperiment GetAlternateProtocolExperiment()
+ const = 0;
+
// Gets a reference to the SettingsMap stored for a host.
// If no settings are stored, returns an empty SettingsMap.
virtual const SettingsMap& GetSpdySettings(
- const HostPortPair& host_port_pair) const = 0;
+ const HostPortPair& host_port_pair) = 0;
// Saves an individual SPDY setting for a host. Returns true if SPDY setting
// is to be persisted.
@@ -132,16 +194,11 @@ class NET_EXPORT HttpServerProperties {
// Returns all persistent SPDY settings.
virtual const SpdySettingsMap& spdy_settings_map() const = 0;
- virtual HttpPipelinedHostCapability GetPipelineCapability(
- const HostPortPair& origin) = 0;
-
- virtual void SetPipelineCapability(
- const HostPortPair& origin,
- HttpPipelinedHostCapability capability) = 0;
+ virtual void SetServerNetworkStats(const HostPortPair& host_port_pair,
+ NetworkStats stats) = 0;
- virtual void ClearPipelineCapabilities() = 0;
-
- virtual PipelineCapabilityMap GetPipelineCapabilityMap() const = 0;
+ virtual const NetworkStats* GetServerNetworkStats(
+ const HostPortPair& host_port_pair) const = 0;
private:
DISALLOW_COPY_AND_ASSIGN(HttpServerProperties);
diff --git a/chromium/net/http/http_server_properties_impl.cc b/chromium/net/http/http_server_properties_impl.cc
index a4ac6dc3804..1def87f71a0 100644
--- a/chromium/net/http/http_server_properties_impl.cc
+++ b/chromium/net/http/http_server_properties_impl.cc
@@ -4,23 +4,32 @@
#include "net/http/http_server_properties_impl.h"
+#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
+#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "net/http/http_pipelined_host_capability.h"
namespace net {
-// TODO(simonjam): Run experiments with different values of this to see what
-// value is good at avoiding evictions without eating too much memory. Until
-// then, this is just a bad guess.
-static const int kDefaultNumHostsToRemember = 200;
+namespace {
+
+const uint64 kBrokenAlternateProtocolDelaySecs = 300;
+
+} // namespace
HttpServerPropertiesImpl::HttpServerPropertiesImpl()
- : pipeline_capability_map_(
- new CachedPipelineCapabilityMap(kDefaultNumHostsToRemember)),
+ : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
+ alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT),
+ alternate_protocol_experiment_(
+ ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT),
+ spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
weak_ptr_factory_(this) {
+ canoncial_suffixes_.push_back(".c.youtube.com");
+ canoncial_suffixes_.push_back(".googlevideo.com");
+ canoncial_suffixes_.push_back(".googleusercontent.com");
}
HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
@@ -30,59 +39,84 @@ void HttpServerPropertiesImpl::InitializeSpdyServers(
std::vector<std::string>* spdy_servers,
bool support_spdy) {
DCHECK(CalledOnValidThread());
- spdy_servers_table_.clear();
if (!spdy_servers)
return;
- for (std::vector<std::string>::iterator it = spdy_servers->begin();
- it != spdy_servers->end(); ++it) {
- spdy_servers_table_[*it] = support_spdy;
+ // Add the entries from persisted data.
+ for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
+ it != spdy_servers->rend(); ++it) {
+ spdy_servers_map_.Put(*it, support_spdy);
}
}
void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
AlternateProtocolMap* alternate_protocol_map) {
- // First swap, and then add back all the ALTERNATE_PROTOCOL_BROKEN ones since
- // those don't get persisted.
- alternate_protocol_map_.swap(*alternate_protocol_map);
- for (AlternateProtocolMap::const_iterator it =
- alternate_protocol_map->begin();
- it != alternate_protocol_map->end(); ++it) {
- if (it->second.protocol == ALTERNATE_PROTOCOL_BROKEN)
- alternate_protocol_map_[it->first] = it->second;
+ // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't
+ // get persisted.
+ for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin();
+ it != alternate_protocol_map_.end();) {
+ AlternateProtocolMap::iterator old_it = it;
+ ++it;
+ if (old_it->second.protocol != ALTERNATE_PROTOCOL_BROKEN) {
+ alternate_protocol_map_.Erase(old_it);
+ }
}
-}
-void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
- SpdySettingsMap* spdy_settings_map) {
- spdy_settings_map_.swap(*spdy_settings_map);
-}
+ // Add the entries from persisted data.
+ for (AlternateProtocolMap::reverse_iterator it =
+ alternate_protocol_map->rbegin();
+ it != alternate_protocol_map->rend(); ++it) {
+ alternate_protocol_map_.Put(it->first, it->second);
+ }
-void HttpServerPropertiesImpl::InitializePipelineCapabilities(
- const PipelineCapabilityMap* pipeline_capability_map) {
- PipelineCapabilityMap::const_iterator it;
- pipeline_capability_map_->Clear();
- for (it = pipeline_capability_map->begin();
- it != pipeline_capability_map->end(); ++it) {
- pipeline_capability_map_->Put(it->first, it->second);
+ // Attempt to find canonical servers.
+ int canonical_ports[] = { 80, 443 };
+ for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
+ std::string canonical_suffix = canoncial_suffixes_[i];
+ for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
+ HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
+ // If we already have a valid canonical server, we're done.
+ if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
+ (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[
+ canonical_host]) != alternate_protocol_map_.end())) {
+ continue;
+ }
+ // Now attempt to find a server which matches this origin and set it as
+ // canonical .
+ for (AlternateProtocolMap::const_iterator it =
+ alternate_protocol_map_.begin();
+ it != alternate_protocol_map_.end(); ++it) {
+ if (EndsWith(it->first.host(), canoncial_suffixes_[i], false)) {
+ canonical_host_to_origin_map_[canonical_host] = it->first;
+ break;
+ }
+ }
+ }
}
}
-void HttpServerPropertiesImpl::SetNumPipelinedHostsToRemember(int max_size) {
- DCHECK(pipeline_capability_map_->empty());
- pipeline_capability_map_.reset(new CachedPipelineCapabilityMap(max_size));
+void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
+ SpdySettingsMap* spdy_settings_map) {
+ for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
+ it != spdy_settings_map->rend(); ++it) {
+ spdy_settings_map_.Put(it->first, it->second);
+ }
}
void HttpServerPropertiesImpl::GetSpdyServerList(
- base::ListValue* spdy_server_list) const {
+ base::ListValue* spdy_server_list,
+ size_t max_size) const {
DCHECK(CalledOnValidThread());
DCHECK(spdy_server_list);
spdy_server_list->Clear();
+ size_t count = 0;
// Get the list of servers (host/port) that support SPDY.
- for (SpdyServerHostPortTable::const_iterator it = spdy_servers_table_.begin();
- it != spdy_servers_table_.end(); ++it) {
+ for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
+ it != spdy_servers_map_.end() && count < max_size; ++it) {
const std::string spdy_server_host_port = it->first;
- if (it->second)
+ if (it->second) {
spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
+ ++count;
+ }
}
}
@@ -119,22 +153,21 @@ base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
void HttpServerPropertiesImpl::Clear() {
DCHECK(CalledOnValidThread());
- spdy_servers_table_.clear();
- alternate_protocol_map_.clear();
- spdy_settings_map_.clear();
- pipeline_capability_map_->Clear();
+ spdy_servers_map_.Clear();
+ alternate_protocol_map_.Clear();
+ spdy_settings_map_.Clear();
}
bool HttpServerPropertiesImpl::SupportsSpdy(
- const net::HostPortPair& host_port_pair) const {
+ const net::HostPortPair& host_port_pair) {
DCHECK(CalledOnValidThread());
if (host_port_pair.host().empty())
return false;
std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
- SpdyServerHostPortTable::const_iterator spdy_host_port =
- spdy_servers_table_.find(spdy_server);
- if (spdy_host_port != spdy_servers_table_.end())
+ SpdyServerHostPortMap::iterator spdy_host_port =
+ spdy_servers_map_.Get(spdy_server);
+ if (spdy_host_port != spdy_servers_map_.end())
return spdy_host_port->second;
return false;
}
@@ -147,33 +180,53 @@ void HttpServerPropertiesImpl::SetSupportsSpdy(
return;
std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
- SpdyServerHostPortTable::iterator spdy_host_port =
- spdy_servers_table_.find(spdy_server);
- if ((spdy_host_port != spdy_servers_table_.end()) &&
+ SpdyServerHostPortMap::iterator spdy_host_port =
+ spdy_servers_map_.Get(spdy_server);
+ if ((spdy_host_port != spdy_servers_map_.end()) &&
(spdy_host_port->second == support_spdy)) {
return;
}
// Cache the data.
- spdy_servers_table_[spdy_server] = support_spdy;
+ spdy_servers_map_.Put(spdy_server, support_spdy);
}
bool HttpServerPropertiesImpl::HasAlternateProtocol(
- const HostPortPair& server) const {
- return ContainsKey(alternate_protocol_map_, server) ||
- g_forced_alternate_protocol;
+ const HostPortPair& server) {
+ if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() ||
+ g_forced_alternate_protocol)
+ return true;
+
+ return GetCanonicalHost(server) != canonical_host_to_origin_map_.end();
+}
+
+std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
+ const HostPortPair& server) {
+ // If this host ends with a canonical suffix, then return the canonical
+ // suffix.
+ for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
+ std::string canonical_suffix = canoncial_suffixes_[i];
+ if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
+ return canonical_suffix;
+ }
+ }
+ return std::string();
}
PortAlternateProtocolPair
HttpServerPropertiesImpl::GetAlternateProtocol(
- const HostPortPair& server) const {
+ const HostPortPair& server) {
DCHECK(HasAlternateProtocol(server));
// First check the map.
- AlternateProtocolMap::const_iterator it =
- alternate_protocol_map_.find(server);
+ AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
if (it != alternate_protocol_map_.end())
return it->second;
+ // Next check the canonical host.
+ CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server);
+ if (canonical_host != canonical_host_to_origin_map_.end())
+ return alternate_protocol_map_.Get(canonical_host->second)->second;
+
// We must be forcing an alternate.
DCHECK(g_forced_alternate_protocol);
return *g_forced_alternate_protocol;
@@ -210,14 +263,68 @@ void HttpServerPropertiesImpl::SetAlternateProtocol(
<< ", Protocol: " << alternate_protocol
<< "].";
}
+ } else {
+ // TODO(rch): Consider the case where multiple requests are started
+ // before the first completes. In this case, only one of the jobs
+ // would reach this code, whereas all of them should should have.
+ HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING,
+ alternate_protocol_experiment_);
}
- alternate_protocol_map_[server] = alternate;
+ alternate_protocol_map_.Put(server, alternate);
+
+ // If this host ends with a canonical suffix, then set it as the
+ // canonical host.
+ for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
+ std::string canonical_suffix = canoncial_suffixes_[i];
+ if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
+ HostPortPair canonical_host(canonical_suffix, server.port());
+ canonical_host_to_origin_map_[canonical_host] = server;
+ break;
+ }
+ }
}
void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
const HostPortPair& server) {
- alternate_protocol_map_[server].protocol = ALTERNATE_PROTOCOL_BROKEN;
+ AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
+ if (it != alternate_protocol_map_.end()) {
+ it->second.protocol = ALTERNATE_PROTOCOL_BROKEN;
+ } else {
+ PortAlternateProtocolPair alternate;
+ alternate.protocol = ALTERNATE_PROTOCOL_BROKEN;
+ alternate_protocol_map_.Put(server, alternate);
+ }
+ int count = ++broken_alternate_protocol_map_[server];
+ base::TimeDelta delay =
+ base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs);
+ BrokenAlternateProtocolEntry entry;
+ entry.server = server;
+ entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1));
+ broken_alternate_protocol_list_.push_back(entry);
+ // If this is the only entry in the list, schedule an expiration task.
+ // Otherwse it will be rescheduled automatically when the pending
+ // task runs.
+ if (broken_alternate_protocol_list_.size() == 1) {
+ ScheduleBrokenAlternateProtocolMappingsExpiration();
+ }
+}
+
+bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
+ const HostPortPair& server) {
+ return ContainsKey(broken_alternate_protocol_map_, server);
+}
+
+void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
+ const HostPortPair& server) {
+ broken_alternate_protocol_map_.erase(server);
+}
+
+void HttpServerPropertiesImpl::ClearAlternateProtocol(
+ const HostPortPair& server) {
+ AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server);
+ if (it != alternate_protocol_map_.end())
+ alternate_protocol_map_.Erase(it);
}
const AlternateProtocolMap&
@@ -225,9 +332,19 @@ HttpServerPropertiesImpl::alternate_protocol_map() const {
return alternate_protocol_map_;
}
+void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
+ AlternateProtocolExperiment experiment) {
+ alternate_protocol_experiment_ = experiment;
+}
+
+AlternateProtocolExperiment
+HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
+ return alternate_protocol_experiment_;
+}
+
const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
- const HostPortPair& host_port_pair) const {
- SpdySettingsMap::const_iterator it = spdy_settings_map_.find(host_port_pair);
+ const HostPortPair& host_port_pair) {
+ SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
if (it == spdy_settings_map_.end()) {
CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
return kEmptySettingsMap;
@@ -243,19 +360,28 @@ bool HttpServerPropertiesImpl::SetSpdySetting(
if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
return false;
- SettingsMap& settings_map = spdy_settings_map_[host_port_pair];
SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
- settings_map[id] = flags_and_value;
+ SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
+ if (it == spdy_settings_map_.end()) {
+ SettingsMap settings_map;
+ settings_map[id] = flags_and_value;
+ spdy_settings_map_.Put(host_port_pair, settings_map);
+ } else {
+ SettingsMap& settings_map = it->second;
+ settings_map[id] = flags_and_value;
+ }
return true;
}
void HttpServerPropertiesImpl::ClearSpdySettings(
const HostPortPair& host_port_pair) {
- spdy_settings_map_.erase(host_port_pair);
+ SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
+ if (it != spdy_settings_map_.end())
+ spdy_settings_map_.Erase(it);
}
void HttpServerPropertiesImpl::ClearAllSpdySettings() {
- spdy_settings_map_.clear();
+ spdy_settings_map_.Clear();
}
const SpdySettingsMap&
@@ -263,41 +389,65 @@ HttpServerPropertiesImpl::spdy_settings_map() const {
return spdy_settings_map_;
}
-HttpPipelinedHostCapability HttpServerPropertiesImpl::GetPipelineCapability(
- const HostPortPair& origin) {
- HttpPipelinedHostCapability capability = PIPELINE_UNKNOWN;
- CachedPipelineCapabilityMap::const_iterator it =
- pipeline_capability_map_->Get(origin);
- if (it != pipeline_capability_map_->end()) {
- capability = it->second;
+void HttpServerPropertiesImpl::SetServerNetworkStats(
+ const HostPortPair& host_port_pair,
+ NetworkStats stats) {
+ server_network_stats_map_[host_port_pair] = stats;
+}
+
+const HttpServerProperties::NetworkStats*
+HttpServerPropertiesImpl::GetServerNetworkStats(
+ const HostPortPair& host_port_pair) const {
+ ServerNetworkStatsMap::const_iterator it =
+ server_network_stats_map_.find(host_port_pair);
+ if (it == server_network_stats_map_.end()) {
+ return NULL;
}
- return capability;
+ return &it->second;
}
-void HttpServerPropertiesImpl::SetPipelineCapability(
- const HostPortPair& origin,
- HttpPipelinedHostCapability capability) {
- CachedPipelineCapabilityMap::iterator it =
- pipeline_capability_map_->Peek(origin);
- if (it == pipeline_capability_map_->end() ||
- it->second != PIPELINE_INCAPABLE) {
- pipeline_capability_map_->Put(origin, capability);
+HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
+HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
+ for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
+ std::string canonical_suffix = canoncial_suffixes_[i];
+ if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
+ HostPortPair canonical_host(canonical_suffix, server.port());
+ return canonical_host_to_origin_map_.find(canonical_host);
+ }
}
+
+ return canonical_host_to_origin_map_.end();
}
-void HttpServerPropertiesImpl::ClearPipelineCapabilities() {
- pipeline_capability_map_->Clear();
+void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ while (!broken_alternate_protocol_list_.empty()) {
+ BrokenAlternateProtocolEntry entry =
+ broken_alternate_protocol_list_.front();
+ if (now < entry.when) {
+ break;
+ }
+
+ ClearAlternateProtocol(entry.server);
+ broken_alternate_protocol_list_.pop_front();
+ }
+ ScheduleBrokenAlternateProtocolMappingsExpiration();
}
-PipelineCapabilityMap
-HttpServerPropertiesImpl::GetPipelineCapabilityMap() const {
- PipelineCapabilityMap result;
- CachedPipelineCapabilityMap::const_iterator it;
- for (it = pipeline_capability_map_->begin();
- it != pipeline_capability_map_->end(); ++it) {
- result[it->first] = it->second;
+void
+HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
+ if (broken_alternate_protocol_list_.empty()) {
+ return;
}
- return result;
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeTicks when = broken_alternate_protocol_list_.front().when;
+ base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
+ weak_ptr_factory_.GetWeakPtr()),
+ delay);
}
} // namespace net
diff --git a/chromium/net/http/http_server_properties_impl.h b/chromium/net/http/http_server_properties_impl.h
index cf96b7d4f32..389c961f491 100644
--- a/chromium/net/http/http_server_properties_impl.h
+++ b/chromium/net/http/http_server_properties_impl.h
@@ -11,13 +11,11 @@
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
-#include "base/containers/mru_cache.h"
#include "base/gtest_prod_util.h"
#include "base/threading/non_thread_safe.h"
#include "base/values.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
-#include "net/http/http_pipelined_host_capability.h"
#include "net/http/http_server_properties.h"
namespace base {
@@ -34,7 +32,7 @@ class NET_EXPORT HttpServerPropertiesImpl
HttpServerPropertiesImpl();
virtual ~HttpServerPropertiesImpl();
- // Initializes |spdy_servers_table_| with the servers (host/port) from
+ // Initializes |spdy_servers_map_| with the servers (host/port) from
// |spdy_servers| that either support SPDY or not.
void InitializeSpdyServers(std::vector<std::string>* spdy_servers,
bool support_spdy);
@@ -44,13 +42,10 @@ class NET_EXPORT HttpServerPropertiesImpl
void InitializeSpdySettingsServers(SpdySettingsMap* spdy_settings_map);
- // Initializes |pipeline_capability_map_| with the servers (host/port) from
- // |pipeline_capability_map| that either support HTTP pipelining or not.
- void InitializePipelineCapabilities(
- const PipelineCapabilityMap* pipeline_capability_map);
-
- // Get the list of servers (host/port) that support SPDY.
- void GetSpdyServerList(base::ListValue* spdy_server_list) const;
+ // Get the list of servers (host/port) that support SPDY. The max_size is the
+ // number of MRU servers that support SPDY that are to be returned.
+ void GetSpdyServerList(base::ListValue* spdy_server_list,
+ size_t max_size) const;
// Returns flattened string representation of the |host_port_pair|. Used by
// unittests.
@@ -63,11 +58,9 @@ class NET_EXPORT HttpServerPropertiesImpl
static void ForceAlternateProtocol(const PortAlternateProtocolPair& pair);
static void DisableForcedAlternateProtocol();
- // Changes the number of host/port pairs we remember pipelining capability
- // for. A larger number means we're more likely to be able to pipeline
- // immediately if a host is known good, but uses more memory. This function
- // can only be called if |pipeline_capability_map_| is empty.
- void SetNumPipelinedHostsToRemember(int max_size);
+ // Returns the canonical host suffix for |server|, or std::string() if none
+ // exists.
+ std::string GetCanonicalSuffix(const net::HostPortPair& server);
// -----------------------------
// HttpServerProperties methods:
@@ -80,19 +73,19 @@ class NET_EXPORT HttpServerPropertiesImpl
virtual void Clear() OVERRIDE;
// Returns true if |server| supports SPDY.
- virtual bool SupportsSpdy(const HostPortPair& server) const OVERRIDE;
+ virtual bool SupportsSpdy(const HostPortPair& server) OVERRIDE;
// Add |server| into the persistent store.
virtual void SetSupportsSpdy(const HostPortPair& server,
bool support_spdy) OVERRIDE;
// Returns true if |server| has an Alternate-Protocol header.
- virtual bool HasAlternateProtocol(const HostPortPair& server) const OVERRIDE;
+ virtual bool HasAlternateProtocol(const HostPortPair& server) OVERRIDE;
// Returns the Alternate-Protocol and port for |server|.
// HasAlternateProtocol(server) must be true.
virtual PortAlternateProtocolPair GetAlternateProtocol(
- const HostPortPair& server) const OVERRIDE;
+ const HostPortPair& server) OVERRIDE;
// Sets the Alternate-Protocol for |server|.
virtual void SetAlternateProtocol(
@@ -103,13 +96,29 @@ class NET_EXPORT HttpServerPropertiesImpl
// Sets the Alternate-Protocol for |server| to be BROKEN.
virtual void SetBrokenAlternateProtocol(const HostPortPair& server) OVERRIDE;
+ // Returns true if Alternate-Protocol for |server| was recently BROKEN.
+ virtual bool WasAlternateProtocolRecentlyBroken(
+ const HostPortPair& server) OVERRIDE;
+
+ // Confirms that Alternate-Protocol for |server| is working.
+ virtual void ConfirmAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
+ // Clears the Alternate-Protocol for |server|.
+ virtual void ClearAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
// Returns all Alternate-Protocol mappings.
virtual const AlternateProtocolMap& alternate_protocol_map() const OVERRIDE;
+ virtual void SetAlternateProtocolExperiment(
+ AlternateProtocolExperiment experiment) OVERRIDE;
+
+ virtual AlternateProtocolExperiment GetAlternateProtocolExperiment()
+ const OVERRIDE;
+
// Gets a reference to the SettingsMap stored for a host.
// If no settings are stored, returns an empty SettingsMap.
virtual const SettingsMap& GetSpdySettings(
- const HostPortPair& host_port_pair) const OVERRIDE;
+ const HostPortPair& host_port_pair) OVERRIDE;
// Saves an individual SPDY setting for a host. Returns true if SPDY setting
// is to be persisted.
@@ -127,29 +136,52 @@ class NET_EXPORT HttpServerPropertiesImpl
// Returns all persistent SPDY settings.
virtual const SpdySettingsMap& spdy_settings_map() const OVERRIDE;
- virtual HttpPipelinedHostCapability GetPipelineCapability(
- const HostPortPair& origin) OVERRIDE;
-
- virtual void SetPipelineCapability(
- const HostPortPair& origin,
- HttpPipelinedHostCapability capability) OVERRIDE;
-
- virtual void ClearPipelineCapabilities() OVERRIDE;
+ virtual void SetServerNetworkStats(const HostPortPair& host_port_pair,
+ NetworkStats stats) OVERRIDE;
- virtual PipelineCapabilityMap GetPipelineCapabilityMap() const OVERRIDE;
+ virtual const NetworkStats* GetServerNetworkStats(
+ const HostPortPair& host_port_pair) const OVERRIDE;
private:
- typedef base::MRUCache<
- HostPortPair, HttpPipelinedHostCapability> CachedPipelineCapabilityMap;
- // |spdy_servers_table_| has flattened representation of servers (host/port
- // pair) that either support or not support SPDY protocol.
- typedef base::hash_map<std::string, bool> SpdyServerHostPortTable;
-
- SpdyServerHostPortTable spdy_servers_table_;
+ // |spdy_servers_map_| has flattened representation of servers (host, port)
+ // that either support or not support SPDY protocol.
+ typedef base::MRUCache<std::string, bool> SpdyServerHostPortMap;
+ typedef std::map<HostPortPair, NetworkStats> ServerNetworkStatsMap;
+ typedef std::map<HostPortPair, HostPortPair> CanonicalHostMap;
+ typedef std::vector<std::string> CanonicalSufficList;
+ // List of broken host:ports and the times when they can be expired.
+ struct BrokenAlternateProtocolEntry {
+ HostPortPair server;
+ base::TimeTicks when;
+ };
+ typedef std::list<BrokenAlternateProtocolEntry>
+ BrokenAlternateProtocolList;
+ // Map from host:port to the number of times alternate protocol has
+ // been marked broken.
+ typedef std::map<HostPortPair, int> BrokenAlternateProtocolMap;
+
+ // Return the canonical host for |server|, or end if none exists.
+ CanonicalHostMap::const_iterator GetCanonicalHost(HostPortPair server) const;
+
+ void ExpireBrokenAlternateProtocolMappings();
+ void ScheduleBrokenAlternateProtocolMappingsExpiration();
+
+ SpdyServerHostPortMap spdy_servers_map_;
AlternateProtocolMap alternate_protocol_map_;
+ BrokenAlternateProtocolList broken_alternate_protocol_list_;
+ BrokenAlternateProtocolMap broken_alternate_protocol_map_;
+ AlternateProtocolExperiment alternate_protocol_experiment_;
+
SpdySettingsMap spdy_settings_map_;
- scoped_ptr<CachedPipelineCapabilityMap> pipeline_capability_map_;
+ ServerNetworkStatsMap server_network_stats_map_;
+ // Contains a map of servers which could share the same alternate protocol.
+ // Map from a Canonical host/port (host is some postfix of host names) to an
+ // actual origin, which has a plausible alternate protocol mapping.
+ CanonicalHostMap canonical_host_to_origin_map_;
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com", ".googleusercontent.com") of canoncial hostnames.
+ CanonicalSufficList canoncial_suffixes_;
base::WeakPtrFactory<HttpServerPropertiesImpl> weak_ptr_factory_;
diff --git a/chromium/net/http/http_server_properties_impl_unittest.cc b/chromium/net/http/http_server_properties_impl_unittest.cc
index cf3a4643f9d..c8014741d7a 100644
--- a/chromium/net/http/http_server_properties_impl_unittest.cc
+++ b/chromium/net/http/http_server_properties_impl_unittest.cc
@@ -21,6 +21,8 @@ class ListValue;
namespace net {
+const int kMaxSupportsSpdyServerHosts = 500;
+
namespace {
class HttpServerPropertiesImplTest : public testing::Test {
@@ -60,6 +62,17 @@ TEST_F(SpdyServerPropertiesTest, Initialize) {
spdy_servers2.push_back(spdy_server_g);
spdy_servers2.push_back(spdy_server_d);
impl_.InitializeSpdyServers(&spdy_servers2, true);
+
+ // Verify spdy_server_g and spdy_server_d are in the list in the same order.
+ base::ListValue spdy_server_list;
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
+ EXPECT_EQ(2U, spdy_server_list.GetSize());
+ std::string string_value_g;
+ ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g));
+ ASSERT_EQ(spdy_server_g, string_value_g);
+ std::string string_value_d;
+ ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_d));
+ ASSERT_EQ(spdy_server_d, string_value_d);
EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_google));
EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_docs));
}
@@ -130,13 +143,13 @@ TEST_F(SpdyServerPropertiesTest, GetSpdyServerList) {
base::ListValue spdy_server_list;
// Check there are no spdy_servers.
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
EXPECT_EQ(0U, spdy_server_list.GetSize());
// Check empty server is not added.
HostPortPair spdy_server_empty(std::string(), 443);
impl_.SetSupportsSpdy(spdy_server_empty, true);
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
EXPECT_EQ(0U, spdy_server_list.GetSize());
std::string string_value_g;
@@ -150,38 +163,79 @@ TEST_F(SpdyServerPropertiesTest, GetSpdyServerList) {
// Add www.google.com:443 as not supporting SPDY.
impl_.SetSupportsSpdy(spdy_server_google, false);
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
EXPECT_EQ(0U, spdy_server_list.GetSize());
// Add www.google.com:443 as supporting SPDY.
impl_.SetSupportsSpdy(spdy_server_google, true);
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
ASSERT_EQ(1U, spdy_server_list.GetSize());
ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g));
ASSERT_EQ(spdy_server_g, string_value_g);
// Add mail.google.com:443 as not supporting SPDY.
impl_.SetSupportsSpdy(spdy_server_mail, false);
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
ASSERT_EQ(1U, spdy_server_list.GetSize());
ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g));
ASSERT_EQ(spdy_server_g, string_value_g);
// Add mail.google.com:443 as supporting SPDY.
impl_.SetSupportsSpdy(spdy_server_mail, true);
- impl_.GetSpdyServerList(&spdy_server_list);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
ASSERT_EQ(2U, spdy_server_list.GetSize());
// Verify www.google.com:443 and mail.google.com:443 are in the list.
+ ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m));
+ ASSERT_EQ(spdy_server_m, string_value_m);
+ ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_g));
+ ASSERT_EQ(spdy_server_g, string_value_g);
+
+ // Request for only one server and verify that we get only one server.
+ impl_.GetSpdyServerList(&spdy_server_list, 1);
+ ASSERT_EQ(1U, spdy_server_list.GetSize());
+ ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m));
+ ASSERT_EQ(spdy_server_m, string_value_m);
+}
+
+TEST_F(SpdyServerPropertiesTest, MRUOfGetSpdyServerList) {
+ base::ListValue spdy_server_list;
+
+ std::string string_value_g;
+ std::string string_value_m;
+ HostPortPair spdy_server_google("www.google.com", 443);
+ std::string spdy_server_g =
+ HttpServerPropertiesImpl::GetFlattenedSpdyServer(spdy_server_google);
+ HostPortPair spdy_server_mail("mail.google.com", 443);
+ std::string spdy_server_m =
+ HttpServerPropertiesImpl::GetFlattenedSpdyServer(spdy_server_mail);
+
+ // Add www.google.com:443 as supporting SPDY.
+ impl_.SetSupportsSpdy(spdy_server_google, true);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
+ ASSERT_EQ(1U, spdy_server_list.GetSize());
+ ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g));
+ ASSERT_EQ(spdy_server_g, string_value_g);
+
+ // Add mail.google.com:443 as supporting SPDY. Verify mail.google.com:443 and
+ // www.google.com:443 are in the list.
+ impl_.SetSupportsSpdy(spdy_server_mail, true);
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
+ ASSERT_EQ(2U, spdy_server_list.GetSize());
+ ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m));
+ ASSERT_EQ(spdy_server_m, string_value_m);
+ ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_g));
+ ASSERT_EQ(spdy_server_g, string_value_g);
+
+ // Get www.google.com:443 should reorder SpdyServerHostPortMap. Verify that it
+ // is www.google.com:443 is the MRU server.
+ EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_google));
+ impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts);
+ ASSERT_EQ(2U, spdy_server_list.GetSize());
ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g));
+ ASSERT_EQ(spdy_server_g, string_value_g);
ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_m));
- if (string_value_g.compare(spdy_server_g) == 0) {
- ASSERT_EQ(spdy_server_g, string_value_g);
- ASSERT_EQ(spdy_server_m, string_value_m);
- } else {
- ASSERT_EQ(spdy_server_g, string_value_m);
- ASSERT_EQ(spdy_server_m, string_value_g);
- }
+ ASSERT_EQ(spdy_server_m, string_value_m);
}
typedef HttpServerPropertiesImplTest AlternateProtocolServerPropertiesTest;
@@ -206,13 +260,27 @@ TEST_F(AlternateProtocolServerPropertiesTest, Initialize) {
HostPortPair test_host_port_pair2("foo2", 80);
impl_.SetAlternateProtocol(test_host_port_pair2, 443, NPN_SPDY_3);
- AlternateProtocolMap alternate_protocol_map;
+ AlternateProtocolMap alternate_protocol_map(
+ AlternateProtocolMap::NO_AUTO_EVICT);
PortAlternateProtocolPair port_alternate_protocol_pair;
port_alternate_protocol_pair.port = 123;
port_alternate_protocol_pair.protocol = NPN_SPDY_3;
- alternate_protocol_map[test_host_port_pair2] = port_alternate_protocol_pair;
+ alternate_protocol_map.Put(test_host_port_pair2,
+ port_alternate_protocol_pair);
+ HostPortPair test_host_port_pair3("foo3", 80);
+ port_alternate_protocol_pair.port = 1234;
+ alternate_protocol_map.Put(test_host_port_pair3,
+ port_alternate_protocol_pair);
impl_.InitializeAlternateProtocolServers(&alternate_protocol_map);
+ // Verify test_host_port_pair3 is the MRU server.
+ const net::AlternateProtocolMap& map = impl_.alternate_protocol_map();
+ net::AlternateProtocolMap::const_iterator it = map.begin();
+ it = map.begin();
+ EXPECT_TRUE(it->first.Equals(test_host_port_pair3));
+ EXPECT_EQ(1234, it->second.port);
+ EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
+
ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair1));
ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair2));
port_alternate_protocol_pair =
@@ -224,6 +292,49 @@ TEST_F(AlternateProtocolServerPropertiesTest, Initialize) {
EXPECT_EQ(NPN_SPDY_3, port_alternate_protocol_pair.protocol);
}
+TEST_F(AlternateProtocolServerPropertiesTest, MRUOfHasAlternateProtocol) {
+ HostPortPair test_host_port_pair1("foo1", 80);
+ impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3);
+ HostPortPair test_host_port_pair2("foo2", 80);
+ impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3);
+
+ const net::AlternateProtocolMap& map = impl_.alternate_protocol_map();
+ net::AlternateProtocolMap::const_iterator it = map.begin();
+ EXPECT_TRUE(it->first.Equals(test_host_port_pair2));
+ EXPECT_EQ(1234, it->second.port);
+ EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
+
+ // HasAlternateProtocol should reoder the AlternateProtocol map.
+ ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair1));
+ it = map.begin();
+ EXPECT_TRUE(it->first.Equals(test_host_port_pair1));
+ EXPECT_EQ(443, it->second.port);
+ EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
+}
+
+TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternateProtocol) {
+ HostPortPair test_host_port_pair1("foo1", 80);
+ impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3);
+ HostPortPair test_host_port_pair2("foo2", 80);
+ impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3);
+
+ const net::AlternateProtocolMap& map = impl_.alternate_protocol_map();
+ net::AlternateProtocolMap::const_iterator it = map.begin();
+ EXPECT_TRUE(it->first.Equals(test_host_port_pair2));
+ EXPECT_EQ(1234, it->second.port);
+ EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
+
+ // GetAlternateProtocol should reoder the AlternateProtocol map.
+ PortAlternateProtocolPair alternate =
+ impl_.GetAlternateProtocol(test_host_port_pair1);
+ EXPECT_EQ(443, alternate.port);
+ EXPECT_EQ(NPN_SPDY_3, alternate.protocol);
+ it = map.begin();
+ EXPECT_TRUE(it->first.Equals(test_host_port_pair1));
+ EXPECT_EQ(443, it->second.port);
+ EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
+}
+
TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) {
HostPortPair test_host_port_pair("foo", 80);
impl_.SetBrokenAlternateProtocol(test_host_port_pair);
@@ -241,6 +352,17 @@ TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) {
<< "Second attempt should be ignored.";
}
+TEST_F(AlternateProtocolServerPropertiesTest, ClearBroken) {
+ HostPortPair test_host_port_pair("foo", 80);
+ impl_.SetBrokenAlternateProtocol(test_host_port_pair);
+ ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
+ PortAlternateProtocolPair alternate =
+ impl_.GetAlternateProtocol(test_host_port_pair);
+ EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
+ impl_.ClearAlternateProtocol(test_host_port_pair);
+ EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
+}
+
TEST_F(AlternateProtocolServerPropertiesTest, Forced) {
// Test forced alternate protocols.
@@ -273,13 +395,39 @@ TEST_F(AlternateProtocolServerPropertiesTest, Forced) {
EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair2));
}
+TEST_F(AlternateProtocolServerPropertiesTest, Canonical) {
+ HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
+ EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
+
+ HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
+ EXPECT_FALSE(impl_.HasAlternateProtocol(canonical_port_pair));
+
+ PortAlternateProtocolPair canonical_protocol;
+ canonical_protocol.port = 1234;
+ canonical_protocol.protocol = QUIC;
+
+ impl_.SetAlternateProtocol(canonical_port_pair,
+ canonical_protocol.port,
+ canonical_protocol.protocol);
+ // Verify the forced protocol.
+ ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
+ PortAlternateProtocolPair alternate =
+ impl_.GetAlternateProtocol(test_host_port_pair);
+ EXPECT_EQ(canonical_protocol.port, alternate.port);
+ EXPECT_EQ(canonical_protocol.protocol, alternate.protocol);
+
+ // Verify the canonical suffix.
+ EXPECT_EQ(".c.youtube.com", impl_.GetCanonicalSuffix(test_host_port_pair));
+ EXPECT_EQ(".c.youtube.com", impl_.GetCanonicalSuffix(canonical_port_pair));
+}
+
typedef HttpServerPropertiesImplTest SpdySettingsServerPropertiesTest;
TEST_F(SpdySettingsServerPropertiesTest, Initialize) {
HostPortPair spdy_server_google("www.google.com", 443);
// Check by initializing empty spdy settings.
- SpdySettingsMap spdy_settings_map;
+ SpdySettingsMap spdy_settings_map(SpdySettingsMap::NO_AUTO_EVICT);
impl_.InitializeSpdySettingsServers(&spdy_settings_map);
EXPECT_TRUE(impl_.GetSpdySettings(spdy_server_google).empty());
@@ -290,7 +438,7 @@ TEST_F(SpdySettingsServerPropertiesTest, Initialize) {
const uint32 value = 31337;
SettingsFlagsAndValue flags_and_value(flags, value);
settings_map[id] = flags_and_value;
- spdy_settings_map[spdy_server_google] = settings_map;
+ spdy_settings_map.Put(spdy_server_google, settings_map);
impl_.InitializeSpdySettingsServers(&spdy_settings_map);
const SettingsMap& settings_map2 = impl_.GetSpdySettings(spdy_server_google);
@@ -411,6 +559,55 @@ TEST_F(SpdySettingsServerPropertiesTest, Clear) {
EXPECT_EQ(0U, impl_.GetSpdySettings(spdy_server_docs).size());
}
+TEST_F(SpdySettingsServerPropertiesTest, MRUOfGetSpdySettings) {
+ // Add www.google.com:443 as persisting.
+ HostPortPair spdy_server_google("www.google.com", 443);
+ const SpdySettingsIds id1 = SETTINGS_UPLOAD_BANDWIDTH;
+ const SpdySettingsFlags flags1 = SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value1 = 31337;
+ EXPECT_TRUE(impl_.SetSpdySetting(spdy_server_google, id1, flags1, value1));
+
+ // Add docs.google.com:443 as persisting
+ HostPortPair spdy_server_docs("docs.google.com", 443);
+ const SpdySettingsIds id2 = SETTINGS_ROUND_TRIP_TIME;
+ const SpdySettingsFlags flags2 = SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value2 = 93997;
+ EXPECT_TRUE(impl_.SetSpdySetting(spdy_server_docs, id2, flags2, value2));
+
+ // Verify the first element is docs.google.com:443.
+ const net::SpdySettingsMap& map = impl_.spdy_settings_map();
+ net::SpdySettingsMap::const_iterator it = map.begin();
+ EXPECT_TRUE(it->first.Equals(spdy_server_docs));
+ const SettingsMap& settings_map2_ret = it->second;
+ ASSERT_EQ(1U, settings_map2_ret.size());
+ SettingsMap::const_iterator it2_ret = settings_map2_ret.find(id2);
+ EXPECT_TRUE(it2_ret != settings_map2_ret.end());
+ SettingsFlagsAndValue flags_and_value2_ret = it2_ret->second;
+ EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value2_ret.first);
+ EXPECT_EQ(value2, flags_and_value2_ret.second);
+
+ // GetSpdySettings should reoder the SpdySettingsMap.
+ const SettingsMap& settings_map1_ret =
+ impl_.GetSpdySettings(spdy_server_google);
+ ASSERT_EQ(1U, settings_map1_ret.size());
+ SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_ret.end());
+ SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+
+ // Check the first entry is spdy_server_google by accessing it via iterator.
+ it = map.begin();
+ EXPECT_TRUE(it->first.Equals(spdy_server_google));
+ const SettingsMap& settings_map1_it_ret = it->second;
+ ASSERT_EQ(1U, settings_map1_it_ret.size());
+ it1_ret = settings_map1_it_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_it_ret.end());
+ flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/http/http_stream.h b/chromium/net/http/http_stream.h
index 3680db3f862..22362e28991 100644
--- a/chromium/net/http/http_stream.h
+++ b/chromium/net/http/http_stream.h
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// HttpStream provides an abstraction for a basic http streams, http pipelining
-// implementations, and SPDY. The HttpStream subtype is expected to manage the
-// underlying transport appropriately. For example, a non-pipelined HttpStream
-// would return the transport socket to the pool for reuse. SPDY streams on the
-// other hand leave the transport socket management to the SpdySession.
+// HttpStream provides an abstraction for a basic http streams, SPDY, and QUIC.
+// The HttpStream subtype is expected to manage the underlying transport
+// appropriately. For example, a basic http stream will return the transport
+// socket to the pool for reuse. SPDY streams on the other hand leave the
+// transport socket management to the SpdySession.
#ifndef NET_HTTP_HTTP_STREAM_H_
#define NET_HTTP_HTTP_STREAM_H_
diff --git a/chromium/net/http/http_stream_base.h b/chromium/net/http/http_stream_base.h
index f5dcc29409e..76e57ef8ff8 100644
--- a/chromium/net/http/http_stream_base.h
+++ b/chromium/net/http/http_stream_base.h
@@ -48,21 +48,30 @@ class NET_EXPORT_PRIVATE HttpStreamBase {
// ERR_IO_PENDING is returned if the operation could not be completed
// synchronously, in which case the result will be passed to the callback
// when available. Returns OK on success.
- // |response| must outlive the HttpStreamBase.
+ //
+ // The callback will only be invoked once the first full set of headers have
+ // been received, at which point |response| will have been populated with that
+ // set of headers, and is safe to read, until/unless ReadResponseHeaders is
+ // called.
+ //
+ // |response| must remain valid until all sets of headers has been read, or
+ // the HttpStreamBase is destroyed. There's typically only one set of
+ // headers, except in the case of 1xx responses (See ReadResponseHeaders).
virtual int SendRequest(const HttpRequestHeaders& request_headers,
HttpResponseInfo* response,
const CompletionCallback& callback) = 0;
- // Reads from the underlying socket until the response headers have been
- // completely received. ERR_IO_PENDING is returned if the operation could
- // not be completed synchronously, in which case the result will be passed
- // to the callback when available. Returns OK on success. The response
- // headers are available in the HttpResponseInfo returned by GetResponseInfo
+ // Reads from the underlying socket until the next set of response headers
+ // have been completely received. This may only be called on 1xx responses
+ // after SendRequest has completed successfully, to read the next set of
+ // headers.
+ //
+ // ERR_IO_PENDING is returned if the operation could not be completed
+ // synchronously, in which case the result will be passed to the callback when
+ // available. Returns OK on success. The response headers are available in
+ // the HttpResponseInfo passed in to original call to SendRequest.
virtual int ReadResponseHeaders(const CompletionCallback& callback) = 0;
- // Provides access to HttpResponseInfo (owned by HttpStream).
- virtual const HttpResponseInfo* GetResponseInfo() const = 0;
-
// Reads response body data, up to |buf_len| bytes. |buf_len| should be a
// reasonable size (<2MB). The number of bytes read is returned, or an
// error is returned upon failure. 0 indicates that the request has been
diff --git a/chromium/net/http/http_stream_factory.cc b/chromium/net/http/http_stream_factory.cc
index e88046fba3c..70d1101d07b 100644
--- a/chromium/net/http/http_stream_factory.cc
+++ b/chromium/net/http/http_stream_factory.cc
@@ -1,261 +1,99 @@
-// 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/http/http_stream_factory.h"
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "net/base/host_mapping_rules.h"
-#include "net/base/host_port_pair.h"
-#include "url/gurl.h"
-
-namespace net {
-
-// WARNING: If you modify or add any static flags, you must keep them in sync
-// with |ResetStaticSettingsToInit|. This is critical for unit test isolation.
-
-// static
-std::vector<std::string>* HttpStreamFactory::next_protos_ = NULL;
-// static
-bool HttpStreamFactory::enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS];
-// static
-bool HttpStreamFactory::spdy_enabled_ = true;
-// static
-bool HttpStreamFactory::use_alternate_protocols_ = false;
-// static
-bool HttpStreamFactory::force_spdy_over_ssl_ = true;
-// static
-bool HttpStreamFactory::force_spdy_always_ = false;
-// static
-std::list<HostPortPair>* HttpStreamFactory::forced_spdy_exclusions_ = NULL;
-
-HttpStreamFactory::~HttpStreamFactory() {}
-
-// static
-bool HttpStreamFactory::IsProtocolEnabled(AlternateProtocol protocol) {
- DCHECK(IsAlternateProtocolValid(protocol));
- return enabled_protocols_[
- protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION];
-}
-
-// static
-void HttpStreamFactory::SetProtocolEnabled(AlternateProtocol protocol) {
- DCHECK(IsAlternateProtocolValid(protocol));
- enabled_protocols_[
- protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = true;
-}
-
-// static
-void HttpStreamFactory::ResetEnabledProtocols() {
- for (int i = ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION;
- i <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION; ++i) {
- enabled_protocols_[i - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = false;
- }
-}
-
-// static
-void HttpStreamFactory::ResetStaticSettingsToInit() {
- // WARNING: These must match the initializers above.
- delete next_protos_;
- delete forced_spdy_exclusions_;
- next_protos_ = NULL;
- spdy_enabled_ = true;
- use_alternate_protocols_ = false;
- force_spdy_over_ssl_ = true;
- force_spdy_always_ = false;
- forced_spdy_exclusions_ = NULL;
- ResetEnabledProtocols();
-}
-
-void HttpStreamFactory::ProcessAlternateProtocol(
- const base::WeakPtr<HttpServerProperties>& http_server_properties,
- const std::string& alternate_protocol_str,
- const HostPortPair& http_host_port_pair) {
- std::vector<std::string> port_protocol_vector;
- base::SplitString(alternate_protocol_str, ':', &port_protocol_vector);
- if (port_protocol_vector.size() != 2) {
- DVLOG(1) << kAlternateProtocolHeader
- << " header has too many tokens: "
- << alternate_protocol_str;
- return;
- }
-
- int port;
- if (!base::StringToInt(port_protocol_vector[0], &port) ||
- port <= 0 || port >= 1 << 16) {
- DVLOG(1) << kAlternateProtocolHeader
- << " header has unrecognizable port: "
- << port_protocol_vector[0];
- return;
- }
-
- AlternateProtocol protocol =
- AlternateProtocolFromString(port_protocol_vector[1]);
- if (IsAlternateProtocolValid(protocol) && !IsProtocolEnabled(protocol)) {
- protocol = ALTERNATE_PROTOCOL_BROKEN;
- }
-
- if (protocol == ALTERNATE_PROTOCOL_BROKEN) {
- DVLOG(1) << kAlternateProtocolHeader
- << " header has unrecognized protocol: "
- << port_protocol_vector[1];
- return;
- }
-
- HostPortPair host_port(http_host_port_pair);
- const HostMappingRules* mapping_rules = GetHostMappingRules();
- if (mapping_rules)
- mapping_rules->RewriteHost(&host_port);
-
- if (http_server_properties->HasAlternateProtocol(host_port)) {
- const PortAlternateProtocolPair existing_alternate =
- http_server_properties->GetAlternateProtocol(host_port);
- // If we think the alternate protocol is broken, don't change it.
- if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN)
- return;
- }
-
- http_server_properties->SetAlternateProtocol(host_port, port, protocol);
-}
-
-GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url,
- HostPortPair* endpoint) {
- const HostMappingRules* mapping_rules = GetHostMappingRules();
- if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
- url_canon::Replacements<char> replacements;
- const std::string port_str = base::IntToString(endpoint->port());
- replacements.SetPort(port_str.c_str(),
- url_parse::Component(0, port_str.size()));
- replacements.SetHost(endpoint->host().c_str(),
- url_parse::Component(0, endpoint->host().size()));
- return url.ReplaceComponents(replacements);
- }
- return url;
-}
-
-// static
-void HttpStreamFactory::add_forced_spdy_exclusion(const std::string& value) {
- HostPortPair pair = HostPortPair::FromURL(GURL(value));
- if (!forced_spdy_exclusions_)
- forced_spdy_exclusions_ = new std::list<HostPortPair>();
- forced_spdy_exclusions_->push_back(pair);
-}
-
-// static
-bool HttpStreamFactory::HasSpdyExclusion(const HostPortPair& endpoint) {
- std::list<HostPortPair>* exclusions = forced_spdy_exclusions_;
- if (!exclusions)
- return false;
-
- std::list<HostPortPair>::const_iterator it;
- for (it = exclusions->begin(); it != exclusions->end(); ++it)
- if (it->Equals(endpoint))
- return true;
- return false;
-}
-
-// static
-void HttpStreamFactory::EnableNpnHttpOnly() {
- // Avoid alternate protocol in this case. Otherwise, browser will try SSL
- // and then fallback to http. This introduces extra load.
- set_use_alternate_protocols(false);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::EnableNpnSpdy3() {
- set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- next_protos.push_back(kProtoQUIC1SPDY3);
- next_protos.push_back(kProtoSPDY3);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::EnableNpnSpdy31() {
- set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- next_protos.push_back(kProtoQUIC1SPDY3);
- next_protos.push_back(kProtoSPDY3);
- next_protos.push_back(kProtoSPDY31);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::EnableNpnSpdy31WithSpdy2() {
- set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- next_protos.push_back(kProtoQUIC1SPDY3);
- next_protos.push_back(kProtoDeprecatedSPDY2);
- next_protos.push_back(kProtoSPDY3);
- next_protos.push_back(kProtoSPDY31);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::EnableNpnSpdy4a2() {
- set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- next_protos.push_back(kProtoQUIC1SPDY3);
- next_protos.push_back(kProtoSPDY3);
- next_protos.push_back(kProtoSPDY31);
- next_protos.push_back(kProtoSPDY4a2);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::EnableNpnHttp2Draft04() {
- set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
- next_protos.push_back(kProtoHTTP11);
- next_protos.push_back(kProtoQUIC1SPDY3);
- next_protos.push_back(kProtoSPDY3);
- next_protos.push_back(kProtoSPDY31);
- next_protos.push_back(kProtoSPDY4a2);
- next_protos.push_back(kProtoHTTP2Draft04);
- SetNextProtos(next_protos);
-}
-
-// static
-void HttpStreamFactory::SetNextProtos(const std::vector<NextProto>& value) {
- if (!next_protos_)
- next_protos_ = new std::vector<std::string>;
-
- next_protos_->clear();
-
- ResetEnabledProtocols();
-
- // TODO(rtenneti): bug 116575 - consider combining the NextProto and
- // AlternateProtocol.
- for (uint32 i = 0; i < value.size(); ++i) {
- NextProto proto = value[i];
- // Add the protocol to the TLS next protocol list, except for QUIC
- // since it uses UDP.
- if (proto != kProtoQUIC1SPDY3) {
- next_protos_->push_back(SSLClientSocket::NextProtoToString(proto));
- }
-
- // Enable the corresponding alternate protocol, except for HTTP
- // which has not corresponding alternative.
- if (proto != kProtoHTTP11) {
- AlternateProtocol alternate = AlternateProtocolFromNextProto(proto);
- if (!IsAlternateProtocolValid(alternate)) {
- NOTREACHED() << "Invalid next proto: " << proto;
- continue;
- }
- SetProtocolEnabled(alternate);
- }
- }
-}
-
-HttpStreamFactory::HttpStreamFactory() {}
-
-} // namespace net
+// 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/http/http_stream_factory.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/host_port_pair.h"
+#include "net/http/http_network_session.h"
+#include "url/gurl.h"
+
+namespace net {
+
+// WARNING: If you modify or add any static flags, you must keep them in sync
+// with |ResetStaticSettingsToInit|. This is critical for unit test isolation.
+
+// static
+bool HttpStreamFactory::spdy_enabled_ = true;
+
+HttpStreamFactory::~HttpStreamFactory() {}
+
+// static
+void HttpStreamFactory::ResetStaticSettingsToInit() {
+ spdy_enabled_ = true;
+}
+
+void HttpStreamFactory::ProcessAlternateProtocol(
+ const base::WeakPtr<HttpServerProperties>& http_server_properties,
+ const std::string& alternate_protocol_str,
+ const HostPortPair& http_host_port_pair,
+ const HttpNetworkSession& session) {
+ std::vector<std::string> port_protocol_vector;
+ base::SplitString(alternate_protocol_str, ':', &port_protocol_vector);
+ if (port_protocol_vector.size() != 2) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has too many tokens: "
+ << alternate_protocol_str;
+ return;
+ }
+
+ int port;
+ if (!base::StringToInt(port_protocol_vector[0], &port) ||
+ port <= 0 || port >= 1 << 16) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has unrecognizable port: "
+ << port_protocol_vector[0];
+ return;
+ }
+
+ AlternateProtocol protocol =
+ AlternateProtocolFromString(port_protocol_vector[1]);
+ if (IsAlternateProtocolValid(protocol) &&
+ !session.IsProtocolEnabled(protocol)) {
+ protocol = ALTERNATE_PROTOCOL_BROKEN;
+ }
+
+ if (protocol == ALTERNATE_PROTOCOL_BROKEN) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has unrecognized protocol: "
+ << port_protocol_vector[1];
+ return;
+ }
+
+ HostPortPair host_port(http_host_port_pair);
+ const HostMappingRules* mapping_rules = GetHostMappingRules();
+ if (mapping_rules)
+ mapping_rules->RewriteHost(&host_port);
+
+ if (http_server_properties->HasAlternateProtocol(host_port)) {
+ const PortAlternateProtocolPair existing_alternate =
+ http_server_properties->GetAlternateProtocol(host_port);
+ // If we think the alternate protocol is broken, don't change it.
+ if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN)
+ return;
+ }
+
+ http_server_properties->SetAlternateProtocol(host_port, port, protocol);
+}
+
+GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url,
+ HostPortPair* endpoint) {
+ const HostMappingRules* mapping_rules = GetHostMappingRules();
+ if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
+ url::Replacements<char> replacements;
+ const std::string port_str = base::IntToString(endpoint->port());
+ replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
+ replacements.SetHost(endpoint->host().c_str(),
+ url::Component(0, endpoint->host().size()));
+ return url.ReplaceComponents(replacements);
+ }
+ return url;
+}
+
+HttpStreamFactory::HttpStreamFactory() {}
+
+} // namespace net
diff --git a/chromium/net/http/http_stream_factory.h b/chromium/net/http/http_stream_factory.h
index 0d854a5dd9d..f3b0203ccfb 100644
--- a/chromium/net/http/http_stream_factory.h
+++ b/chromium/net/http/http_stream_factory.h
@@ -7,7 +7,6 @@
#include <list>
#include <string>
-#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
@@ -17,7 +16,6 @@
#include "net/base/net_export.h"
#include "net/base/request_priority.h"
#include "net/http/http_server_properties.h"
-#include "net/socket/ssl_client_socket.h"
// This file can be included from net/http even though
// it is in net/websockets because it doesn't
// introduce any link dependency to net/websockets.
@@ -36,6 +34,7 @@ class BoundNetLog;
class HostMappingRules;
class HostPortPair;
class HttpAuthController;
+class HttpNetworkSession;
class HttpResponseInfo;
class HttpServerProperties;
class HttpStreamBase;
@@ -181,7 +180,8 @@ class NET_EXPORT HttpStreamFactory {
void ProcessAlternateProtocol(
const base::WeakPtr<HttpServerProperties>& http_server_properties,
const std::string& alternate_protocol_str,
- const HostPortPair& http_host_port_pair);
+ const HostPortPair& http_host_port_pair,
+ const HttpNetworkSession& session);
GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint);
@@ -216,11 +216,6 @@ class NET_EXPORT HttpStreamFactory {
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config) = 0;
- // If pipelining is supported, creates a Value summary of the currently active
- // pipelines. Caller assumes ownership of the returned value. Otherwise,
- // returns an empty Value.
- virtual base::Value* PipelineInfoToValue() const = 0;
-
virtual const HostMappingRules* GetHostMappingRules() const = 0;
// Static settings
@@ -229,87 +224,18 @@ class NET_EXPORT HttpStreamFactory {
static void ResetStaticSettingsToInit();
// Turns spdy on or off.
+ // TODO(mmenke): Figure out if this can be made a property of the
+ // HttpNetworkSession.
static void set_spdy_enabled(bool value) {
spdy_enabled_ = value;
- if (!spdy_enabled_) {
- delete next_protos_;
- next_protos_ = NULL;
- }
}
static bool spdy_enabled() { return spdy_enabled_; }
- // Controls whether or not we use the Alternate-Protocol header.
- static void set_use_alternate_protocols(bool value) {
- use_alternate_protocols_ = value;
- }
- static bool use_alternate_protocols() { return use_alternate_protocols_; }
-
- // Controls whether or not we use ssl when in spdy mode.
- static void set_force_spdy_over_ssl(bool value) {
- force_spdy_over_ssl_ = value;
- }
- static bool force_spdy_over_ssl() {
- return force_spdy_over_ssl_;
- }
-
- // Controls whether or not we use spdy without npn.
- static void set_force_spdy_always(bool value) {
- force_spdy_always_ = value;
- }
- static bool force_spdy_always() { return force_spdy_always_; }
-
- // Add a URL to exclude from forced SPDY.
- static void add_forced_spdy_exclusion(const std::string& value);
- // Check if a HostPortPair is excluded from using spdy.
- static bool HasSpdyExclusion(const HostPortPair& endpoint);
-
- // Sets http/1.1 as the only protocol supported via NPN or Alternate-Protocol.
- static void EnableNpnHttpOnly();
-
- // Sets http/1.1, quic, and spdy/3 as the protocols supported via
- // NPN or Alternate-Protocol.
- static void EnableNpnSpdy3();
-
- // Sets http/1.1, quic, spdy/3, and spdy/3.1 as the protocols
- // supported via NPN or Alternate-Protocol.
- static void EnableNpnSpdy31();
-
- // Sets http/1.1, quic, spdy/2, spdy/3, and spdy/3.1 as the
- // protocols supported via NPN or Alternate-Protocol.
- static void EnableNpnSpdy31WithSpdy2();
-
- // Sets http/1.1, quic, spdy/3, spdy/3.1, and spdy/4a2 as the
- // protocols supported via NPN or Alternate-Protocol.
- static void EnableNpnSpdy4a2();
-
- // Sets http/1.1, quic, spdy/3, spdy/3.1, spdy/4a2, and http/2 draft
- // 04 as the protocols supported via NPN or Alternate-Protocol.
- static void EnableNpnHttp2Draft04();
-
- // Sets the protocols supported by NPN (next protocol negotiation) during the
- // SSL handshake as well as by HTTP Alternate-Protocol.
- static void SetNextProtos(const std::vector<NextProto>& value);
- static bool has_next_protos() { return next_protos_ != NULL; }
- static const std::vector<std::string>& next_protos() {
- return *next_protos_;
- }
-
protected:
HttpStreamFactory();
private:
- // |protocol| must be a valid protocol value.
- static bool IsProtocolEnabled(AlternateProtocol protocol);
- static void SetProtocolEnabled(AlternateProtocol protocol);
- static void ResetEnabledProtocols();
-
- static std::vector<std::string>* next_protos_;
- static bool enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS];
static bool spdy_enabled_;
- static bool use_alternate_protocols_;
- static bool force_spdy_over_ssl_;
- static bool force_spdy_always_;
- static std::list<HostPortPair>* forced_spdy_exclusions_;
DISALLOW_COPY_AND_ASSIGN(HttpStreamFactory);
};
diff --git a/chromium/net/http/http_stream_factory_impl.cc b/chromium/net/http/http_stream_factory_impl.cc
index 056a0f6b879..4340e62fb6a 100644
--- a/chromium/net/http/http_stream_factory_impl.cc
+++ b/chromium/net/http/http_stream_factory_impl.cc
@@ -6,14 +6,12 @@
#include <string>
+#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/http/http_network_session.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_stream.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_stream_factory_impl_job.h"
#include "net/http/http_stream_factory_impl_request.h"
@@ -44,15 +42,11 @@ GURL UpgradeUrlToHttps(const GURL& original_url, int port) {
HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session,
bool for_websockets)
: session_(session),
- http_pipelined_host_pool_(this, NULL,
- session_->http_server_properties(),
- session_->force_http_pipelining()),
for_websockets_(for_websockets) {}
HttpStreamFactoryImpl::~HttpStreamFactoryImpl() {
DCHECK(request_map_.empty());
DCHECK(spdy_session_request_map_.empty());
- DCHECK(http_pipelining_request_map_.empty());
std::set<const Job*> tmp_job_set;
tmp_job_set.swap(orphaned_job_set_);
@@ -178,18 +172,14 @@ void HttpStreamFactoryImpl::PreconnectStreams(
job->Preconnect(num_streams);
}
-base::Value* HttpStreamFactoryImpl::PipelineInfoToValue() const {
- return http_pipelined_host_pool_.PipelineInfoToValue();
-}
-
const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const {
return session_->params().host_mapping_rules;
}
PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
const GURL& original_url,
- GURL* alternate_url) const {
- if (!use_alternate_protocols())
+ GURL* alternate_url) {
+ if (!session_->params().use_alternate_protocols)
return kNoAlternateProtocol;
if (original_url.SchemeIs("ftp"))
@@ -198,15 +188,19 @@ PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
HostPortPair origin = HostPortPair(original_url.HostNoBrackets(),
original_url.EffectiveIntPort());
- const HttpServerProperties& http_server_properties =
+ HttpServerProperties& http_server_properties =
*session_->http_server_properties();
if (!http_server_properties.HasAlternateProtocol(origin))
return kNoAlternateProtocol;
PortAlternateProtocolPair alternate =
http_server_properties.GetAlternateProtocol(origin);
- if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN)
+ if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
+ HistogramAlternateProtocolUsage(
+ ALTERNATE_PROTOCOL_USAGE_BROKEN,
+ http_server_properties.GetAlternateProtocolExperiment());
return kNoAlternateProtocol;
+ }
if (!IsAlternateProtocolValid(alternate.protocol)) {
NOTREACHED();
@@ -228,10 +222,10 @@ PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
origin.set_port(alternate.port);
if (alternate.protocol >= NPN_SPDY_MINIMUM_VERSION &&
alternate.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
- if (!spdy_enabled())
+ if (!HttpStreamFactory::spdy_enabled())
return kNoAlternateProtocol;
- if (HttpStreamFactory::HasSpdyExclusion(origin))
+ if (session_->HasSpdyExclusion(origin))
return kNoAlternateProtocol;
*alternate_url = UpgradeUrlToHttps(original_url, alternate.port);
@@ -290,15 +284,9 @@ void HttpStreamFactoryImpl::OnNewSpdySessionReady(
using_spdy,
net_log);
if (for_websockets_) {
- WebSocketHandshakeStreamBase::CreateHelper* create_helper =
- request->websocket_handshake_stream_create_helper();
- DCHECK(create_helper);
- bool use_relative_url = direct || request->url().SchemeIs("wss");
- request->OnWebSocketHandshakeStreamReady(
- NULL,
- used_ssl_config,
- used_proxy_info,
- create_helper->CreateSpdyStream(spdy_session, use_relative_url));
+ // TODO(ricea): Restore this code path when WebSocket over SPDY
+ // implementation is ready.
+ NOTREACHED();
} else {
bool use_relative_url = direct || request->url().SchemeIs("https");
request->OnStreamReady(
@@ -322,40 +310,4 @@ void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) {
OnPreconnectsCompleteInternal();
}
-void HttpStreamFactoryImpl::OnHttpPipelinedHostHasAdditionalCapacity(
- HttpPipelinedHost* host) {
- while (ContainsKey(http_pipelining_request_map_, host->GetKey())) {
- HttpPipelinedStream* stream =
- http_pipelined_host_pool_.CreateStreamOnExistingPipeline(
- host->GetKey());
- if (!stream) {
- break;
- }
-
- Request* request = *http_pipelining_request_map_[host->GetKey()].begin();
- request->Complete(stream->was_npn_negotiated(),
- stream->protocol_negotiated(),
- false, // not using_spdy
- stream->net_log());
- request->OnStreamReady(NULL,
- stream->used_ssl_config(),
- stream->used_proxy_info(),
- stream);
- }
-}
-
-void HttpStreamFactoryImpl::AbortPipelinedRequestsWithKey(
- const Job* job, const HttpPipelinedHost::Key& key, int status,
- const SSLConfig& used_ssl_config) {
- RequestVector requests_to_fail = http_pipelining_request_map_[key];
- for (RequestVector::const_iterator it = requests_to_fail.begin();
- it != requests_to_fail.end(); ++it) {
- Request* request = *it;
- if (request == request_map_[job]) {
- continue;
- }
- request->OnStreamFailed(NULL, status, used_ssl_config);
- }
-}
-
} // namespace net
diff --git a/chromium/net/http/http_stream_factory_impl.h b/chromium/net/http/http_stream_factory_impl.h
index 4824dece14f..8a6fdaf4fc3 100644
--- a/chromium/net/http/http_stream_factory_impl.h
+++ b/chromium/net/http/http_stream_factory_impl.h
@@ -13,7 +13,6 @@
#include "base/memory/ref_counted.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_log.h"
-#include "net/http/http_pipelined_host_pool.h"
#include "net/http/http_stream_factory.h"
#include "net/proxy/proxy_server.h"
#include "net/socket/ssl_client_socket.h"
@@ -22,12 +21,9 @@
namespace net {
class HttpNetworkSession;
-class HttpPipelinedHost;
class SpdySession;
-class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
- public HttpStreamFactory,
- public HttpPipelinedHostPool::Delegate {
+class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : public HttpStreamFactory {
public:
// RequestStream may only be called if |for_websockets| is false.
// RequestWebSocketHandshakeStream may only be called if |for_websockets|
@@ -58,13 +54,8 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
RequestPriority priority,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config) OVERRIDE;
- virtual base::Value* PipelineInfoToValue() const OVERRIDE;
virtual const HostMappingRules* GetHostMappingRules() const OVERRIDE;
- // HttpPipelinedHostPool::Delegate interface
- virtual void OnHttpPipelinedHostHasAdditionalCapacity(
- HttpPipelinedHost* host) OVERRIDE;
-
size_t num_orphaned_jobs() const { return orphaned_job_set_.size(); }
private:
@@ -74,10 +65,7 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
class NET_EXPORT_PRIVATE Job;
typedef std::set<Request*> RequestSet;
- typedef std::vector<Request*> RequestVector;
typedef std::map<SpdySessionKey, RequestSet> SpdySessionRequestMap;
- typedef std::map<HttpPipelinedHost::Key,
- RequestVector> HttpPipeliningRequestMap;
HttpStreamRequest* RequestStreamInternal(
const HttpRequestInfo& info,
@@ -90,7 +78,7 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
PortAlternateProtocolPair GetAlternateProtocolRequestFor(
const GURL& original_url,
- GURL* alternate_url) const;
+ GURL* alternate_url);
// Detaches |job| from |request|.
void OrphanJob(Job* job, const Request* request);
@@ -121,11 +109,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
// Called when the Preconnect completes. Used for testing.
virtual void OnPreconnectsCompleteInternal() {}
- void AbortPipelinedRequestsWithKey(const Job* job,
- const HttpPipelinedHost::Key& key,
- int status,
- const SSLConfig& used_ssl_config);
-
HttpNetworkSession* const session_;
// All Requests are handed out to clients. By the time HttpStreamFactoryImpl
@@ -134,9 +117,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl :
std::map<const Job*, Request*> request_map_;
SpdySessionRequestMap spdy_session_request_map_;
- HttpPipeliningRequestMap http_pipelining_request_map_;
-
- HttpPipelinedHostPool http_pipelined_host_pool_;
// These jobs correspond to jobs orphaned by Requests and now owned by
// HttpStreamFactoryImpl. Since they are no longer tied to Requests, they will
diff --git a/chromium/net/http/http_stream_factory_impl_job.cc b/chromium/net/http/http_stream_factory_impl_job.cc
index c86bee7495b..79a95361d43 100644
--- a/chromium/net/http/http_stream_factory_impl_job.cc
+++ b/chromium/net/http/http_stream_factory_impl_job.cc
@@ -20,10 +20,6 @@
#include "net/base/net_util.h"
#include "net/http/http_basic_stream.h"
#include "net/http/http_network_session.h"
-#include "net/http/http_pipelined_connection.h"
-#include "net/http/http_pipelined_host.h"
-#include "net/http/http_pipelined_host_pool.h"
-#include "net/http/http_pipelined_stream.h"
#include "net/http/http_proxy_client_socket.h"
#include "net/http/http_proxy_client_socket_pool.h"
#include "net/http/http_request_info.h"
@@ -98,15 +94,15 @@ HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory,
using_spdy_(false),
using_quic_(false),
quic_request_(session_->quic_stream_factory()),
- force_spdy_always_(HttpStreamFactory::force_spdy_always()),
- force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()),
+ using_existing_quic_session_(false),
spdy_certificate_error_(OK),
establishing_tunnel_(false),
was_npn_negotiated_(false),
protocol_negotiated_(kProtoUnknown),
num_streams_(0),
spdy_session_direct_(false),
- existing_available_pipeline_(false),
+ job_status_(STATUS_RUNNING),
+ other_job_status_(STATUS_RUNNING),
ptr_factory_(this) {
DCHECK(stream_factory);
DCHECK(session);
@@ -217,15 +213,17 @@ void HttpStreamFactoryImpl::Job::Orphan(const Request* request) {
blocking_job_->waiting_job_ = NULL;
blocking_job_ = NULL;
if (stream_factory_->for_websockets_ &&
- connection_ && connection_->socket())
+ connection_ && connection_->socket()) {
connection_->socket()->Disconnect();
+ }
stream_factory_->OnOrphanedJobComplete(this);
} else if (stream_factory_->for_websockets_) {
// We cancel this job because a WebSocketHandshakeStream can't be created
// without a WebSocketHandshakeStreamBase::CreateHelper which is stored in
// the Request class and isn't accessible from this job.
- if (connection_ && connection_->socket())
+ if (connection_ && connection_->socket()) {
connection_->socket()->Disconnect();
+ }
stream_factory_->OnOrphanedJobComplete(this);
}
}
@@ -291,10 +289,11 @@ bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const {
// The only time we can use an existing session is if the request URL is
// https (the normal case) or if we're connection to a SPDY proxy, or
// if we're running with force_spdy_always_. crbug.com/133176
+ // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is
+ // working.
return request_info_.url.SchemeIs("https") ||
- request_info_.url.SchemeIs("wss") ||
proxy_info_.proxy_server().is_https() ||
- force_spdy_always_;
+ session_->params().force_spdy_always;
}
void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
@@ -333,20 +332,26 @@ void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() {
}
void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() {
- DCHECK(!stream_.get());
+ DCHECK(stream_.get());
DCHECK(!IsPreconnecting());
DCHECK(using_spdy());
- if (!new_spdy_session_)
- return;
+ // Note: an event loop iteration has passed, so |new_spdy_session_| may be
+ // NULL at this point if the SpdySession closed immediately after creation.
base::WeakPtr<SpdySession> spdy_session = new_spdy_session_;
new_spdy_session_.reset();
+
+ // TODO(jgraettinger): Notify the factory, and let that notify |request_|,
+ // rather than notifying |request_| directly.
if (IsOrphaned()) {
- stream_factory_->OnNewSpdySessionReady(
- spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_,
- was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_);
+ if (spdy_session) {
+ stream_factory_->OnNewSpdySessionReady(
+ spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_,
+ was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_);
+ }
stream_factory_->OnOrphanedJobComplete(this);
} else {
- request_->OnNewSpdySessionReady(this, spdy_session, spdy_session_direct_);
+ request_->OnNewSpdySessionReady(
+ this, stream_.Pass(), spdy_session, spdy_session_direct_);
}
// |this| may be deleted after this call.
}
@@ -451,9 +456,8 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) {
if (IsPreconnecting()) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &HttpStreamFactoryImpl::Job::OnPreconnectsComplete,
- ptr_factory_.GetWeakPtr()));
+ base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete,
+ ptr_factory_.GetWeakPtr()));
return ERR_IO_PENDING;
}
@@ -464,64 +468,57 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) {
next_state_ = STATE_WAITING_USER_ACTION;
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
- ptr_factory_.GetWeakPtr(),
- result, ssl_info_));
+ base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
+ ptr_factory_.GetWeakPtr(), result, ssl_info_));
return ERR_IO_PENDING;
}
switch (result) {
- case ERR_PROXY_AUTH_REQUESTED:
- {
- DCHECK(connection_.get());
- DCHECK(connection_->socket());
- DCHECK(establishing_tunnel_);
-
- ProxyClientSocket* proxy_socket =
- static_cast<ProxyClientSocket*>(connection_->socket());
- const HttpResponseInfo* tunnel_auth_response =
- proxy_socket->GetConnectResponseInfo();
-
- next_state_ = STATE_WAITING_USER_ACTION;
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(
- &Job::OnNeedsProxyAuthCallback,
- ptr_factory_.GetWeakPtr(),
- *tunnel_auth_response,
- proxy_socket->GetAuthController()));
- }
+ case ERR_PROXY_AUTH_REQUESTED: {
+ UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection",
+ connection_.get() != NULL);
+ if (!connection_.get())
+ return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION;
+ CHECK(connection_->socket());
+ CHECK(establishing_tunnel_);
+
+ next_state_ = STATE_WAITING_USER_ACTION;
+ ProxyClientSocket* proxy_socket =
+ static_cast<ProxyClientSocket*>(connection_->socket());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(),
+ *proxy_socket->GetConnectResponseInfo(),
+ proxy_socket->GetAuthController()));
return ERR_IO_PENDING;
+ }
case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &Job::OnNeedsClientAuthCallback,
- ptr_factory_.GetWeakPtr(),
- connection_->ssl_error_response_info().cert_request_info));
+ base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
+ connection_->ssl_error_response_info().cert_request_info));
return ERR_IO_PENDING;
- case ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
- {
- DCHECK(connection_.get());
- DCHECK(connection_->socket());
- DCHECK(establishing_tunnel_);
+ case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: {
+ DCHECK(connection_.get());
+ DCHECK(connection_->socket());
+ DCHECK(establishing_tunnel_);
- ProxyClientSocket* proxy_socket =
- static_cast<ProxyClientSocket*>(connection_->socket());
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(
- &Job::OnHttpsProxyTunnelResponseCallback,
- ptr_factory_.GetWeakPtr(),
- *proxy_socket->GetConnectResponseInfo(),
- proxy_socket->CreateConnectResponseStream()));
- return ERR_IO_PENDING;
- }
+ ProxyClientSocket* proxy_socket =
+ static_cast<ProxyClientSocket*>(connection_->socket());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Job::OnHttpsProxyTunnelResponseCallback,
+ ptr_factory_.GetWeakPtr(),
+ *proxy_socket->GetConnectResponseInfo(),
+ proxy_socket->CreateConnectResponseStream()));
+ return ERR_IO_PENDING;
+ }
case OK:
+ job_status_ = STATUS_SUCCEEDED;
+ MaybeMarkAlternateProtocolBroken();
next_state_ = STATE_DONE;
if (new_spdy_session_.get()) {
base::MessageLoop::current()->PostTask(
@@ -538,22 +535,22 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) {
DCHECK(stream_.get());
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &Job::OnStreamReadyCallback,
- ptr_factory_.GetWeakPtr()));
+ base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr()));
}
return ERR_IO_PENDING;
default:
+ if (job_status_ != STATUS_BROKEN) {
+ DCHECK_EQ(STATUS_RUNNING, job_status_);
+ job_status_ = STATUS_FAILED;
+ MaybeMarkAlternateProtocolBroken();
+ }
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(
- &Job::OnStreamFailedCallback,
- ptr_factory_.GetWeakPtr(),
- result));
+ base::Bind(&Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(),
+ result));
return ERR_IO_PENDING;
}
- return result;
}
int HttpStreamFactoryImpl::Job::DoLoop(int result) {
@@ -627,7 +624,6 @@ int HttpStreamFactoryImpl::Job::DoStart() {
origin_ = HostPortPair(request_info_.url.HostNoBrackets(), port);
origin_url_ = stream_factory_->ApplyHostMappingRules(
request_info_.url, &origin_);
- http_pipelining_key_.reset(new HttpPipelinedHost::Key(origin_));
net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB,
base::Bind(&NetLogHttpStreamJobCallback,
@@ -674,7 +670,7 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
if (result == OK) {
// Remove unsupported proxies from the list.
proxy_info_.RemoveProxiesWithoutScheme(
- ProxyServer::SCHEME_DIRECT |
+ ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC |
ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
@@ -682,6 +678,11 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
// No proxies/direct to choose from. This happens when we don't support
// any of the proxies in the returned list.
result = ERR_NO_SUPPORTED_PROXIES;
+ } else if (using_quic_ &&
+ (!proxy_info_.is_quic() && !proxy_info_.is_direct())) {
+ // QUIC can not be spoken to non-QUIC proxies. This error should not be
+ // user visible, because the non-alternate job should be resumed.
+ result = ERR_NO_SUPPORTED_PROXIES;
}
}
@@ -701,13 +702,15 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
}
bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const {
- bool rv = force_spdy_always_ && force_spdy_over_ssl_;
- return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
+ bool rv = session_->params().force_spdy_always &&
+ session_->params().force_spdy_over_ssl;
+ return rv && !session_->HasSpdyExclusion(origin_);
}
bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const {
- bool rv = force_spdy_always_ && !force_spdy_over_ssl_;
- return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
+ bool rv = session_->params().force_spdy_always &&
+ !session_->params().force_spdy_over_ssl;
+ return rv && !session_->HasSpdyExclusion(origin_);
}
bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const {
@@ -742,19 +745,26 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() {
if (ShouldForceQuic())
using_quic_ = true;
+ if (proxy_info_.is_quic())
+ using_quic_ = true;
+
if (using_quic_) {
DCHECK(session_->params().enable_quic);
- if (!proxy_info_.is_direct()) {
+ if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) {
NOTREACHED();
- // TODO(rch): support QUIC proxies.
+ // TODO(rch): support QUIC proxies for HTTPS urls.
return ERR_NOT_IMPLEMENTED;
}
+ HostPortPair destination = proxy_info_.is_quic() ?
+ proxy_info_.proxy_server().host_port_pair() : origin_;
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
- const ProxyServer& proxy_server = proxy_info_.proxy_server();
- int rv = quic_request_.Request(HostPortProxyPair(origin_, proxy_server),
- using_ssl_, session_->cert_verifier(),
- net_log_, io_callback_);
- if (rv != OK) {
+ bool secure_quic = using_ssl_ || proxy_info_.is_quic();
+ int rv = quic_request_.Request(
+ destination, secure_quic, request_info_.privacy_mode,
+ request_info_.method, net_log_, io_callback_);
+ if (rv == OK) {
+ using_existing_quic_session_ = true;
+ } else {
// OK, there's no available QUIC session. Let |waiting_job_| resume
// if it's paused.
if (waiting_job_) {
@@ -780,29 +790,10 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() {
next_state_ = STATE_CREATE_STREAM;
existing_spdy_session_ = spdy_session;
return OK;
- } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) {
+ } else if (request_ && !request_->HasSpdySessionKey() &&
+ (using_ssl_ || ShouldForceSpdyWithoutSSL())) {
// Update the spdy session key for the request that launched this job.
request_->SetSpdySessionKey(spdy_session_key);
- } else if (IsRequestEligibleForPipelining()) {
- // TODO(simonjam): With pipelining, we might be better off using fewer
- // connections and thus should make fewer preconnections. Explore
- // preconnecting fewer than the requested num_connections.
- //
- // Separate note: A forced pipeline is always available if one exists for
- // this key. This is different than normal pipelines, which may be
- // unavailable or unusable. So, there is no need to worry about a race
- // between when a pipeline becomes available and when this job blocks.
- existing_available_pipeline_ = stream_factory_->http_pipelined_host_pool_.
- IsExistingPipelineAvailableForKey(*http_pipelining_key_.get());
- if (existing_available_pipeline_) {
- return OK;
- } else {
- bool was_new_key = request_->SetHttpPipeliningKey(
- *http_pipelining_key_.get());
- if (!was_new_key && session_->force_http_pipelining()) {
- return ERR_IO_PENDING;
- }
- }
}
// OK, there's no available SPDY session. Let |waiting_job_| resume if it's
@@ -847,28 +838,32 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() {
request_info_.privacy_mode,
net_log_,
num_streams_);
- } else {
- // If we can't use a SPDY session, don't both checking for one after
- // the hostname is resolved.
- OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ?
- base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(),
- GetSpdySessionKey()) :
- OnHostResolutionCallback();
- if (stream_factory_->for_websockets_) {
- return InitSocketHandleForWebSocketRequest(
- origin_url_, request_info_.extra_headers, request_info_.load_flags,
- priority_, session_, proxy_info_, ShouldForceSpdySSL(),
- want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_,
- request_info_.privacy_mode, net_log_,
- connection_.get(), resolution_callback, io_callback_);
- }
- return InitSocketHandleForHttpRequest(
+ }
+
+ // If we can't use a SPDY session, don't both checking for one after
+ // the hostname is resolved.
+ OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ?
+ base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(),
+ GetSpdySessionKey()) :
+ OnHostResolutionCallback();
+ if (stream_factory_->for_websockets_) {
+ // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported.
+ SSLConfig websocket_server_ssl_config = server_ssl_config_;
+ websocket_server_ssl_config.next_protos.clear();
+ return InitSocketHandleForWebSocketRequest(
origin_url_, request_info_.extra_headers, request_info_.load_flags,
priority_, session_, proxy_info_, ShouldForceSpdySSL(),
- want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_,
+ want_spdy_over_npn, websocket_server_ssl_config, proxy_ssl_config_,
request_info_.privacy_mode, net_log_,
connection_.get(), resolution_callback, io_callback_);
}
+
+ return InitSocketHandleForHttpRequest(
+ origin_url_, request_info_.extra_headers, request_info_.load_flags,
+ priority_, session_, proxy_info_, ShouldForceSpdySSL(),
+ want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_,
+ request_info_.privacy_mode, net_log_,
+ connection_.get(), resolution_callback, io_callback_);
}
int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
@@ -903,11 +898,6 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
waiting_job_ = NULL;
}
- if (result < 0 && session_->force_http_pipelining()) {
- stream_factory_->AbortPipelinedRequestsWithKey(
- this, *http_pipelining_key_.get(), result, server_ssl_config_);
- }
-
// |result| may be the result of any of the stacked pools. The following
// logic is used when determining how to interpret an error.
// If |result| < 0:
@@ -976,15 +966,17 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
}
if (!ssl_started && result < 0 && original_url_.get()) {
- // Mark the alternate protocol as broken and fallback.
- session_->http_server_properties()->SetBrokenAlternateProtocol(
- HostPortPair::FromURL(*original_url_));
+ job_status_ = STATUS_BROKEN;
+ MaybeMarkAlternateProtocolBroken();
return result;
}
if (using_quic_) {
- if (result < 0)
+ if (result < 0) {
+ job_status_ = STATUS_BROKEN;
+ MaybeMarkAlternateProtocolBroken();
return result;
+ }
stream_ = quic_request_.ReleaseStream();
next_state_ = STATE_NONE;
return OK;
@@ -1039,8 +1031,7 @@ int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) {
}
int HttpStreamFactoryImpl::Job::DoCreateStream() {
- DCHECK(connection_->socket() || existing_spdy_session_.get() ||
- existing_available_pipeline_ || using_quic_);
+ DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_);
next_state_ = STATE_CREATE_STREAM_COMPLETE;
@@ -1055,31 +1046,12 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() {
bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
(request_info_.url.SchemeIs("http") ||
request_info_.url.SchemeIs("ftp"));
- if (stream_factory_->http_pipelined_host_pool_.
- IsExistingPipelineAvailableForKey(*http_pipelining_key_.get())) {
- DCHECK(!stream_factory_->for_websockets_);
- stream_.reset(stream_factory_->http_pipelined_host_pool_.
- CreateStreamOnExistingPipeline(
- *http_pipelining_key_.get()));
- CHECK(stream_.get());
- } else if (stream_factory_->for_websockets_) {
+ if (stream_factory_->for_websockets_) {
DCHECK(request_);
DCHECK(request_->websocket_handshake_stream_create_helper());
websocket_stream_.reset(
request_->websocket_handshake_stream_create_helper()
->CreateBasicStream(connection_.Pass(), using_proxy));
- } else if (!using_proxy && IsRequestEligibleForPipelining()) {
- // TODO(simonjam): Support proxies.
- stream_.reset(
- stream_factory_->http_pipelined_host_pool_.CreateStreamOnNewPipeline(
- *http_pipelining_key_.get(),
- connection_.release(),
- server_ssl_config_,
- proxy_info_,
- net_log_,
- was_npn_negotiated_,
- protocol_negotiated_));
- CHECK(stream_.get());
} else {
stream_.reset(new HttpBasicStream(connection_.release(), using_proxy));
}
@@ -1098,7 +1070,7 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() {
// We never use privacy mode for connection to proxy server.
spdy_session_key = SpdySessionKey(proxy_server.host_port_pair(),
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
direct = false;
}
@@ -1113,21 +1085,34 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() {
SpdySessionPool* spdy_pool = session_->spdy_session_pool();
spdy_session = spdy_pool->FindAvailableSession(spdy_session_key, net_log_);
if (!spdy_session) {
- int error =
+ base::WeakPtr<SpdySession> new_spdy_session =
spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key,
connection_.Pass(),
net_log_,
spdy_certificate_error_,
- &new_spdy_session_,
using_ssl_);
- if (error != OK)
- return error;
+ if (!new_spdy_session->HasAcceptableTransportSecurity()) {
+ new_spdy_session->CloseSessionOnError(
+ ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, "");
+ return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY;
+ }
+
+ new_spdy_session_ = new_spdy_session;
+ spdy_session_direct_ = direct;
const HostPortPair& host_port_pair = spdy_session_key.host_port_pair();
base::WeakPtr<HttpServerProperties> http_server_properties =
session_->http_server_properties();
if (http_server_properties)
http_server_properties->SetSupportsSpdy(host_port_pair, true);
- spdy_session_direct_ = direct;
+
+ // Create a SpdyHttpStream attached to the session;
+ // OnNewSpdySessionReadyCallback is not called until an event loop
+ // iteration later, so if the SpdySession is closed between then, allow
+ // reuse state from the underlying socket, sampled by SpdyHttpStream,
+ // bubble up to the request.
+ bool use_relative_url = direct || request_info_.url.SchemeIs("https");
+ stream_.reset(new SpdyHttpStream(new_spdy_session_, use_relative_url));
+
return OK;
}
}
@@ -1140,12 +1125,8 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() {
// will know when SpdySessions become available.
if (stream_factory_->for_websockets_) {
- DCHECK(request_);
- DCHECK(request_->websocket_handshake_stream_create_helper());
- bool use_relative_url = direct || request_info_.url.SchemeIs("wss");
- websocket_stream_.reset(
- request_->websocket_handshake_stream_create_helper()->CreateSpdyStream(
- spdy_session, use_relative_url));
+ // TODO(ricea): Restore this code when WebSockets over SPDY is implemented.
+ NOTREACHED();
} else {
bool use_relative_url = direct || request_info_.url.SchemeIs("https");
stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url));
@@ -1194,10 +1175,8 @@ void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection(
connection_->socket()->Disconnect();
connection_->Reset();
- if (request_) {
+ if (request_)
request_->RemoveRequestFromSpdySessionRequestMap();
- request_->RemoveRequestFromHttpPipeliningRequestMap();
- }
next_state_ = STATE_INIT_CONNECTION;
}
@@ -1282,7 +1261,7 @@ void HttpStreamFactoryImpl::Job::InitSSLConfig(
ssl_config->verify_ev_cert = true;
// Disable Channel ID if privacy mode is enabled.
- if (request_info_.privacy_mode == kPrivacyModeEnabled)
+ if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED)
ssl_config->channel_id_enabled = false;
}
@@ -1338,21 +1317,20 @@ int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) {
if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) {
session_->ssl_client_auth_cache()->Remove(
- proxy_info_.proxy_server().host_port_pair().ToString());
+ proxy_info_.proxy_server().host_port_pair());
}
int rv = session_->proxy_service()->ReconsiderProxyAfterError(
- request_info_.url, &proxy_info_, io_callback_, &pac_request_, net_log_);
+ request_info_.url, error, &proxy_info_, io_callback_, &pac_request_,
+ net_log_);
if (rv == OK || rv == ERR_IO_PENDING) {
// If the error was during connection setup, there is no socket to
// disconnect.
if (connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
- if (request_) {
+ if (request_)
request_->RemoveRequestFromSpdySessionRequestMap();
- request_->RemoveRequestFromHttpPipeliningRequestMap();
- }
next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
} else {
// If ReconsiderProxyAfterError() failed synchronously, it means
@@ -1446,33 +1424,59 @@ bool HttpStreamFactoryImpl::Job::IsOrphaned() const {
return !IsPreconnecting() && !request_;
}
-bool HttpStreamFactoryImpl::Job::IsRequestEligibleForPipelining() {
- if (IsPreconnecting() || !request_) {
- return false;
- }
- if (stream_factory_->for_websockets_) {
- return false;
- }
- if (session_->force_http_pipelining()) {
- return true;
- }
- if (!session_->params().http_pipelining_enabled) {
- return false;
+void HttpStreamFactoryImpl::Job::ReportJobSuccededForRequest() {
+ net::AlternateProtocolExperiment alternate_protocol_experiment =
+ ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT;
+ base::WeakPtr<HttpServerProperties> http_server_properties =
+ session_->http_server_properties();
+ if (http_server_properties) {
+ alternate_protocol_experiment =
+ http_server_properties->GetAlternateProtocolExperiment();
}
- if (using_ssl_) {
- return false;
+ if (using_existing_quic_session_) {
+ // If an existing session was used, then no TCP connection was
+ // started.
+ HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE,
+ alternate_protocol_experiment);
+ } else if (original_url_) {
+ // This job was the alternate protocol job, and hence won the race.
+ HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE,
+ alternate_protocol_experiment);
+ } else {
+ // This job was the normal job, and hence the alternate protocol job lost
+ // the race.
+ HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE,
+ alternate_protocol_experiment);
}
- if (request_info_.method != "GET" && request_info_.method != "HEAD") {
- return false;
+}
+
+void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job& job) {
+ DCHECK_EQ(STATUS_RUNNING, other_job_status_);
+ other_job_status_ = job.job_status_;
+ MaybeMarkAlternateProtocolBroken();
+}
+
+void HttpStreamFactoryImpl::Job::MaybeMarkAlternateProtocolBroken() {
+ if (job_status_ == STATUS_RUNNING || other_job_status_ == STATUS_RUNNING)
+ return;
+
+ bool is_alternate_protocol_job = original_url_.get() != NULL;
+ if (is_alternate_protocol_job) {
+ if (job_status_ == STATUS_BROKEN && other_job_status_ == STATUS_SUCCEEDED) {
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT);
+ session_->http_server_properties()->SetBrokenAlternateProtocol(
+ HostPortPair::FromURL(*original_url_));
+ }
+ return;
}
- if (request_info_.load_flags &
- (net::LOAD_MAIN_FRAME | net::LOAD_SUB_FRAME | net::LOAD_PREFETCH |
- net::LOAD_IS_DOWNLOAD)) {
- // Avoid pipelining resources that may be streamed for a long time.
- return false;
+
+ if (job_status_ == STATUS_SUCCEEDED && other_job_status_ == STATUS_BROKEN) {
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN);
+ session_->http_server_properties()->SetBrokenAlternateProtocol(
+ HostPortPair::FromURL(request_info_.url));
}
- return stream_factory_->http_pipelined_host_pool_.IsKeyEligibleForPipelining(
- *http_pipelining_key_.get());
}
} // namespace net
diff --git a/chromium/net/http/http_stream_factory_impl_job.h b/chromium/net/http/http_stream_factory_impl_job.h
index 1970b5be08b..9659d45060a 100644
--- a/chromium/net/http/http_stream_factory_impl_job.h
+++ b/chromium/net/http/http_stream_factory_impl_job.h
@@ -13,7 +13,6 @@
#include "net/base/request_priority.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_controller.h"
-#include "net/http/http_pipelined_host.h"
#include "net/http/http_request_info.h"
#include "net/http/http_stream_factory_impl.h"
#include "net/proxy/proxy_service.h"
@@ -92,6 +91,13 @@ class HttpStreamFactoryImpl::Job {
// Indicates whether or not this Job has been orphaned by a Request.
bool IsOrphaned() const;
+ // Called to indicate that this job succeeded, and some other jobs
+ // will be orphaned.
+ void ReportJobSuccededForRequest();
+
+ // Marks that the other |job| has completed.
+ void MarkOtherJobComplete(const Job& job);
+
private:
enum State {
STATE_START,
@@ -129,6 +135,13 @@ class HttpStreamFactoryImpl::Job {
STATE_NONE
};
+ enum JobStatus {
+ STATUS_RUNNING,
+ STATUS_FAILED,
+ STATUS_BROKEN,
+ STATUS_SUCCEEDED
+ };
+
void OnStreamReadyCallback();
void OnWebSocketHandshakeStreamReadyCallback();
// This callback function is called when a new SPDY session is created.
@@ -216,7 +229,7 @@ class HttpStreamFactoryImpl::Job {
// Should we force QUIC for this stream request.
bool ShouldForceQuic() const;
- bool IsRequestEligibleForPipelining();
+ void MaybeMarkAlternateProtocolBroken();
// Record histograms of latency until Connect() completes.
static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
@@ -277,11 +290,8 @@ class HttpStreamFactoryImpl::Job {
bool using_quic_;
QuicStreamRequest quic_request_;
- // Force spdy for all connections.
- bool force_spdy_always_;
-
- // Force spdy only for SSL connections.
- bool force_spdy_over_ssl_;
+ // True if this job used an existing QUIC session.
+ bool using_existing_quic_session_;
// Force quic for a specific port.
int force_quic_port_;
@@ -318,11 +328,8 @@ class HttpStreamFactoryImpl::Job {
// Only used if |new_spdy_session_| is non-NULL.
bool spdy_session_direct_;
- // Key used to identify the HttpPipelinedHost for |request_|.
- scoped_ptr<HttpPipelinedHost::Key> http_pipelining_key_;
-
- // True if an existing pipeline can handle this job's request.
- bool existing_available_pipeline_;
+ JobStatus job_status_;
+ JobStatus other_job_status_;
base::WeakPtrFactory<Job> ptr_factory_;
diff --git a/chromium/net/http/http_stream_factory_impl_request.cc b/chromium/net/http/http_stream_factory_impl_request.cc
index d731d414b71..2cc497f2112 100644
--- a/chromium/net/http/http_stream_factory_impl_request.cc
+++ b/chromium/net/http/http_stream_factory_impl_request.cc
@@ -48,14 +48,13 @@ HttpStreamFactoryImpl::Request::~Request() {
factory_->request_map_.erase(*it);
RemoveRequestFromSpdySessionRequestMap();
- RemoveRequestFromHttpPipeliningRequestMap();
STLDeleteElements(&jobs_);
}
void HttpStreamFactoryImpl::Request::SetSpdySessionKey(
const SpdySessionKey& spdy_session_key) {
- DCHECK(!spdy_session_key_.get());
+ CHECK(!spdy_session_key_.get());
spdy_session_key_.reset(new SpdySessionKey(spdy_session_key));
RequestSet& request_set =
factory_->spdy_session_request_map_[spdy_session_key];
@@ -63,18 +62,6 @@ void HttpStreamFactoryImpl::Request::SetSpdySessionKey(
request_set.insert(this);
}
-bool HttpStreamFactoryImpl::Request::SetHttpPipeliningKey(
- const HttpPipelinedHost::Key& http_pipelining_key) {
- CHECK(!http_pipelining_key_.get());
- http_pipelining_key_.reset(new HttpPipelinedHost::Key(http_pipelining_key));
- bool was_new_key = !ContainsKey(factory_->http_pipelining_request_map_,
- http_pipelining_key);
- RequestVector& request_vector =
- factory_->http_pipelining_request_map_[http_pipelining_key];
- request_vector.push_back(this);
- return was_new_key;
-}
-
void HttpStreamFactoryImpl::Request::AttachJob(Job* job) {
DCHECK(job);
jobs_.insert(job);
@@ -131,21 +118,16 @@ void HttpStreamFactoryImpl::Request::OnStreamFailed(
int status,
const SSLConfig& used_ssl_config) {
DCHECK_NE(OK, status);
- // |job| should only be NULL if we're being canceled by a late bound
- // HttpPipelinedConnection (one that was not created by a job in our |jobs_|
- // set).
- if (!job) {
- DCHECK(!bound_job_.get());
- DCHECK(!jobs_.empty());
- // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because
- // we *WANT* to cancel the unnecessary Jobs from other requests if another
- // Job completes first.
- } else if (!bound_job_.get()) {
+ DCHECK(job);
+ if (!bound_job_.get()) {
// Hey, we've got other jobs! Maybe one of them will succeed, let's just
// ignore this failure.
if (jobs_.size() > 1) {
jobs_.erase(job);
factory_->request_map_.erase(job);
+ // Notify all the other jobs that this one failed.
+ for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
+ (*it)->MarkOtherJobComplete(*job);
delete job;
return;
} else {
@@ -268,34 +250,29 @@ HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() {
}
}
-void
-HttpStreamFactoryImpl::Request::RemoveRequestFromHttpPipeliningRequestMap() {
- if (http_pipelining_key_.get()) {
- HttpPipeliningRequestMap& http_pipelining_request_map =
- factory_->http_pipelining_request_map_;
- DCHECK(ContainsKey(http_pipelining_request_map, *http_pipelining_key_));
- RequestVector& request_vector =
- http_pipelining_request_map[*http_pipelining_key_];
- for (RequestVector::iterator it = request_vector.begin();
- it != request_vector.end(); ++it) {
- if (*it == this) {
- request_vector.erase(it);
- break;
- }
- }
- if (request_vector.empty())
- http_pipelining_request_map.erase(*http_pipelining_key_);
- http_pipelining_key_.reset();
- }
+bool HttpStreamFactoryImpl::Request::HasSpdySessionKey() const {
+ return spdy_session_key_.get() != NULL;
}
+// TODO(jgraettinger): Currently, HttpStreamFactoryImpl::Job notifies a
+// Request that the session is ready, which in turn notifies it's delegate,
+// and then it notifies HttpStreamFactoryImpl so that /other/ requests may
+// be woken, but only if the spdy_session is still okay. This is tough to grok.
+// Instead, see if Job can notify HttpStreamFactoryImpl only, and have one
+// path for notifying any requests waiting for the session (including the
+// request which spawned it).
void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady(
Job* job,
+ scoped_ptr<HttpStream> stream,
const base::WeakPtr<SpdySession>& spdy_session,
bool direct) {
DCHECK(job);
DCHECK(job->using_spdy());
+ // Note: |spdy_session| may be NULL. In that case, |delegate_| should still
+ // receive |stream| so the error propogates up correctly, however there is no
+ // point in broadcasting |spdy_session| to other requests.
+
// The first case is the usual case.
if (!bound_job_.get()) {
OrphanJobsExcept(job);
@@ -318,29 +295,24 @@ void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady(
// Cache this so we can still use it if the request is deleted.
HttpStreamFactoryImpl* factory = factory_;
if (factory->for_websockets_) {
- DCHECK(websocket_handshake_stream_create_helper_);
- bool use_relative_url = direct || url().SchemeIs("wss");
- delegate_->OnWebSocketHandshakeStreamReady(
- job->server_ssl_config(),
- job->proxy_info(),
- websocket_handshake_stream_create_helper_->CreateSpdyStream(
- spdy_session, use_relative_url));
+ // TODO(ricea): Re-instate this code when WebSockets over SPDY is
+ // implemented.
+ NOTREACHED();
} else {
- bool use_relative_url = direct || url().SchemeIs("https");
- delegate_->OnStreamReady(
- job->server_ssl_config(),
- job->proxy_info(),
- new SpdyHttpStream(spdy_session, use_relative_url));
+ delegate_->OnStreamReady(job->server_ssl_config(), job->proxy_info(),
+ stream.release());
}
// |this| may be deleted after this point.
- factory->OnNewSpdySessionReady(spdy_session,
- direct,
- used_ssl_config,
- used_proxy_info,
- was_npn_negotiated,
- protocol_negotiated,
- using_spdy,
- net_log);
+ if (spdy_session && spdy_session->IsAvailable()) {
+ factory->OnNewSpdySessionReady(spdy_session,
+ direct,
+ used_ssl_config,
+ used_proxy_info,
+ was_npn_negotiated,
+ protocol_negotiated,
+ using_spdy,
+ net_log);
+ }
}
void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) {
@@ -356,7 +328,6 @@ void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) {
void HttpStreamFactoryImpl::Request::OrphanJobs() {
RemoveRequestFromSpdySessionRequestMap();
- RemoveRequestFromHttpPipeliningRequestMap();
std::set<Job*> tmp;
tmp.swap(jobs_);
@@ -367,8 +338,7 @@ void HttpStreamFactoryImpl::Request::OrphanJobs() {
void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) {
// |job| should only be NULL if we're being serviced by a late bound
- // SpdySession or HttpPipelinedConnection (one that was not created by a job
- // in our |jobs_| set).
+ // SpdySession (one that was not created by a job in our |jobs_| set).
if (!job) {
DCHECK(!bound_job_.get());
DCHECK(!jobs_.empty());
@@ -380,13 +350,23 @@ void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) {
// they complete? Or do we want to prevent connecting a new SpdySession if
// we've already got one available for a different hostname where the ip
// address matches up?
- } else if (!bound_job_.get()) {
+ return;
+ }
+ if (!bound_job_.get()) {
+ if (jobs_.size() > 1)
+ job->ReportJobSuccededForRequest();
+ // Notify all the other jobs that this one succeeded.
+ for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) {
+ if (*it != job) {
+ (*it)->MarkOtherJobComplete(*job);
+ }
+ }
// We may have other jobs in |jobs_|. For example, if we start multiple jobs
// for Alternate-Protocol.
OrphanJobsExcept(job);
- } else {
- DCHECK(jobs_.empty());
+ return;
}
+ DCHECK(jobs_.empty());
}
} // namespace net
diff --git a/chromium/net/http/http_stream_factory_impl_request.h b/chromium/net/http/http_stream_factory_impl_request.h
index 3d3b2c8bd63..eafc138925f 100644
--- a/chromium/net/http/http_stream_factory_impl_request.h
+++ b/chromium/net/http/http_stream_factory_impl_request.h
@@ -16,6 +16,7 @@
namespace net {
class ClientSocketHandle;
+class HttpStream;
class SpdySession;
class HttpStreamFactoryImpl::Request : public HttpStreamRequest {
@@ -36,12 +37,7 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest {
// for this SpdySessionKey, since we may need to wait for NPN to complete
// before knowing if SPDY is available.
void SetSpdySessionKey(const SpdySessionKey& spdy_session_key);
-
- // Called when the Job determines the appropriate |http_pipelining_key| for
- // the Request. Registers this Request with the factory, so that if an
- // existing pipeline becomes available, this Request can be late bound to it.
- // Returns true if this is this key was new to the factory.
- bool SetHttpPipeliningKey(const HttpPipelinedHost::Key& http_pipelining_key);
+ bool HasSpdySessionKey() const;
// Attaches |job| to this request. Does not mean that Request will use |job|,
// but Request will own |job|.
@@ -58,12 +54,9 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest {
// SpdySessionRequestMap.
void RemoveRequestFromSpdySessionRequestMap();
- // If this Request has a |http_pipelining_key_|, remove this session from the
- // HttpPipeliningRequestMap.
- void RemoveRequestFromHttpPipeliningRequestMap();
-
// Called by an attached Job if it sets up a SpdySession.
void OnNewSpdySessionReady(Job* job,
+ scoped_ptr<HttpStream> stream,
const base::WeakPtr<SpdySession>& spdy_session,
bool direct);
@@ -135,7 +128,6 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest {
scoped_ptr<Job> bound_job_;
std::set<HttpStreamFactoryImpl::Job*> jobs_;
scoped_ptr<const SpdySessionKey> spdy_session_key_;
- scoped_ptr<const HttpPipelinedHost::Key> http_pipelining_key_;
bool completed_;
bool was_npn_negotiated_;
diff --git a/chromium/net/http/http_stream_factory_impl_request_unittest.cc b/chromium/net/http/http_stream_factory_impl_request_unittest.cc
index b3b12bfa5a3..14c06ef7107 100644
--- a/chromium/net/http/http_stream_factory_impl_request_unittest.cc
+++ b/chromium/net/http/http_stream_factory_impl_request_unittest.cc
@@ -20,8 +20,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
HttpStreamFactoryImplRequestTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
namespace {
diff --git a/chromium/net/http/http_stream_factory_impl_unittest.cc b/chromium/net/http/http_stream_factory_impl_unittest.cc
index e3169b65993..e98edbad576 100644
--- a/chromium/net/http/http_stream_factory_impl_unittest.cc
+++ b/chromium/net/http/http_stream_factory_impl_unittest.cc
@@ -43,20 +43,6 @@ namespace net {
namespace {
-class UseAlternateProtocolsScopedSetter {
- public:
- explicit UseAlternateProtocolsScopedSetter(bool use_alternate_protocols)
- : use_alternate_protocols_(HttpStreamFactory::use_alternate_protocols()) {
- HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols);
- }
- ~UseAlternateProtocolsScopedSetter() {
- HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols_);
- }
-
- private:
- bool use_alternate_protocols_;
-};
-
class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
public:
enum StreamType {
@@ -87,9 +73,6 @@ class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE {
return ERR_IO_PENDING;
}
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
- return NULL;
- }
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE {
@@ -420,19 +403,19 @@ CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool(
CertVerifier* cert_verifier)
: SSLClientSocketPool(0,
0,
- NULL,
+ NULL, // ssl_histograms
host_resolver,
cert_verifier,
+ NULL, // server_bound_cert_store
+ NULL, // transport_security_state
+ NULL, // cert_transparency_verifier
+ std::string(), // ssl_session_cache_shard
+ NULL, // deterministic_socket_factory
+ NULL, // transport_socket_pool
NULL,
NULL,
- NULL,
- std::string(),
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL),
+ NULL, // ssl_config_service
+ NULL), // net_log
last_num_streams_(-1) {}
class HttpStreamFactoryTest : public ::testing::Test,
@@ -443,8 +426,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
HttpStreamFactoryTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(HttpStreamFactoryTest, PreconnectDirect) {
for (size_t i = 0; i < arraysize(kTests); ++i) {
@@ -546,7 +528,7 @@ TEST_P(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) {
// Put a SpdySession in the pool.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
ignore_result(CreateFakeSpdySession(session->spdy_session_pool(), key));
CapturePreconnectsTransportSocketPool* transport_conn_pool =
@@ -657,13 +639,13 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeDisablesChannelId) {
// Set an existing SpdySession in the pool.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeEnabled);
+ PRIVACY_MODE_ENABLED);
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
- request_info.privacy_mode = kPrivacyModeDisabled;
+ request_info.privacy_mode = PRIVACY_MODE_DISABLED;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
@@ -716,7 +698,7 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) {
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
- request_info.privacy_mode = kPrivacyModeDisabled;
+ request_info.privacy_mode = PRIVACY_MODE_DISABLED;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
@@ -737,7 +719,7 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) {
EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1);
- request_info.privacy_mode = kPrivacyModeEnabled;
+ request_info.privacy_mode = PRIVACY_MODE_ENABLED;
scoped_ptr<HttpStreamRequest> request3(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
@@ -1127,7 +1109,64 @@ TEST_P(HttpStreamFactoryTest, RequestSpdyHttpStream) {
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
-TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStream) {
+// TODO(ricea): This test can be removed once the new WebSocket stack supports
+// SPDY. Currently, even if we connect to a SPDY-supporting server, we need to
+// use plain SSL.
+TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStreamButGetSSL) {
+ SpdySessionDependencies session_deps(GetParam(),
+ ProxyService::CreateDirect());
+
+ MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING);
+ StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0);
+ socket_data.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps.socket_factory->AddSocketDataProvider(&socket_data);
+
+ SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
+ session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
+
+ HostPortPair host_port_pair("www.google.com", 80);
+ scoped_refptr<HttpNetworkSession>
+ session(SpdySessionDependencies::SpdyCreateSession(&session_deps));
+
+ // Now request a stream.
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("wss://www.google.com");
+ request_info.load_flags = 0;
+
+ SSLConfig ssl_config;
+ StreamRequestWaiter waiter1;
+ WebSocketStreamCreateHelper create_helper;
+ scoped_ptr<HttpStreamRequest> request1(
+ session->http_stream_factory_for_websocket()
+ ->RequestWebSocketHandshakeStream(request_info,
+ DEFAULT_PRIORITY,
+ ssl_config,
+ ssl_config,
+ &waiter1,
+ &create_helper,
+ BoundNetLog()));
+ waiter1.WaitForStream();
+ EXPECT_TRUE(waiter1.stream_done());
+ ASSERT_TRUE(NULL != waiter1.websocket_stream());
+ EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
+ waiter1.websocket_stream()->type());
+ EXPECT_TRUE(NULL == waiter1.stream());
+
+ EXPECT_EQ(0, GetSocketPoolGroupCount(
+ session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
+ EXPECT_EQ(0, GetSocketPoolGroupCount(
+ session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
+ EXPECT_EQ(1, GetSocketPoolGroupCount(
+ session->GetTransportSocketPool(
+ HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
+ EXPECT_EQ(1, GetSocketPoolGroupCount(
+ session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
+ EXPECT_TRUE(waiter1.used_proxy_info().is_direct());
+}
+
+// TODO(ricea): Re-enable once WebSocket-over-SPDY is implemented.
+TEST_P(HttpStreamFactoryTest, DISABLED_RequestWebSocketSpdyHandshakeStream) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
@@ -1203,10 +1242,11 @@ TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStream) {
EXPECT_TRUE(waiter1.used_proxy_info().is_direct());
}
-TEST_P(HttpStreamFactoryTest, OrphanedWebSocketStream) {
- UseAlternateProtocolsScopedSetter use_alternate_protocols(true);
+// TODO(ricea): Re-enable once WebSocket over SPDY is implemented.
+TEST_P(HttpStreamFactoryTest, DISABLED_OrphanedWebSocketStream) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
+ session_deps.use_alternate_protocols = true;
MockRead mock_read(ASYNC, OK);
DeterministicSocketData socket_data(&mock_read, 1, NULL, 0);
diff --git a/chromium/net/http/http_stream_parser.cc b/chromium/net/http/http_stream_parser.cc
index 0821c848652..86b188b6a1f 100644
--- a/chromium/net/http/http_stream_parser.cc
+++ b/chromium/net/http/http_stream_parser.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "net/base/io_buffer.h"
@@ -19,12 +20,14 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
+namespace net {
+
namespace {
const size_t kMaxMergedHeaderAndBodySize = 1400;
const size_t kRequestBodyBufferSize = 1 << 14; // 16KB
-std::string GetResponseHeaderLines(const net::HttpResponseHeaders& headers) {
+std::string GetResponseHeaderLines(const HttpResponseHeaders& headers) {
std::string raw_headers = headers.raw_headers();
const char* null_separated_headers = raw_headers.c_str();
const char* header_line = null_separated_headers;
@@ -39,9 +42,8 @@ std::string GetResponseHeaderLines(const net::HttpResponseHeaders& headers) {
// Return true if |headers| contain multiple |field_name| fields with different
// values.
-bool HeadersContainMultipleCopiesOfField(
- const net::HttpResponseHeaders& headers,
- const std::string& field_name) {
+bool HeadersContainMultipleCopiesOfField(const HttpResponseHeaders& headers,
+ const std::string& field_name) {
void* it = NULL;
std::string field_value;
if (!headers.EnumerateHeader(&it, field_name, &field_value))
@@ -56,11 +58,10 @@ bool HeadersContainMultipleCopiesOfField(
return false;
}
-base::Value* NetLogSendRequestBodyCallback(
- int length,
- bool is_chunked,
- bool did_merge,
- net::NetLog::LogLevel /* log_level */) {
+base::Value* NetLogSendRequestBodyCallback(int length,
+ bool is_chunked,
+ bool did_merge,
+ NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("length", length);
dict->SetBoolean("is_chunked", is_chunked);
@@ -68,9 +69,14 @@ base::Value* NetLogSendRequestBodyCallback(
return dict;
}
-} // namespace
+// Returns true if |error_code| is an error for which we give the server a
+// chance to send a body containing error information, if the error was received
+// while trying to upload a request body.
+bool ShouldTryReadingOnUploadError(int error_code) {
+ return (error_code == ERR_CONNECTION_RESET);
+}
-namespace net {
+} // namespace
// Similar to DrainableIOBuffer(), but this version comes with its own
// storage. The motivation is to avoid repeated allocations of
@@ -101,7 +107,7 @@ namespace net {
// // size() == BytesRemaining() == BytesConsumed() == 0.
// // data() points to the beginning of the buffer.
//
-class HttpStreamParser::SeekableIOBuffer : public net::IOBuffer {
+class HttpStreamParser::SeekableIOBuffer : public IOBuffer {
public:
explicit SeekableIOBuffer(int capacity)
: IOBuffer(capacity),
@@ -176,6 +182,7 @@ HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection,
: io_state_(STATE_NONE),
request_(request),
request_headers_(NULL),
+ request_headers_length_(0),
read_buf_(read_buffer),
read_buf_unused_offset_(0),
response_header_start_offset_(-1),
@@ -187,6 +194,7 @@ HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection,
connection_(connection),
net_log_(net_log),
sent_last_chunk_(false),
+ upload_error_(OK),
weak_ptr_factory_(this) {
io_callback_ = base::Bind(&HttpStreamParser::OnIOComplete,
weak_ptr_factory_.GetWeakPtr());
@@ -223,6 +231,7 @@ int HttpStreamParser::SendRequest(const std::string& request_line,
response_->socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
std::string request = request_line + headers.ToString();
+ request_headers_length_ = request.size();
if (request_->upload_data_stream != NULL) {
request_body_send_buf_ = new SeekableIOBuffer(kRequestBodyBufferSize);
@@ -237,13 +246,14 @@ int HttpStreamParser::SendRequest(const std::string& request_line,
}
}
- io_state_ = STATE_SENDING_HEADERS;
+ io_state_ = STATE_SEND_HEADERS;
// If we have a small request body, then we'll merge with the headers into a
// single write.
bool did_merge = false;
if (ShouldMergeRequestHeadersAndBody(request, request_->upload_data_stream)) {
- size_t merged_size = request.size() + request_->upload_data_stream->size();
+ size_t merged_size =
+ request_headers_length_ + request_->upload_data_stream->size();
scoped_refptr<IOBuffer> merged_request_headers_and_body(
new IOBuffer(merged_size));
// We'll repurpose |request_headers_| to store the merged headers and
@@ -251,8 +261,8 @@ int HttpStreamParser::SendRequest(const std::string& request_line,
request_headers_ = new DrainableIOBuffer(
merged_request_headers_and_body.get(), merged_size);
- memcpy(request_headers_->data(), request.data(), request.size());
- request_headers_->DidConsume(request.size());
+ memcpy(request_headers_->data(), request.data(), request_headers_length_);
+ request_headers_->DidConsume(request_headers_length_);
size_t todo = request_->upload_data_stream->size();
while (todo) {
@@ -291,7 +301,7 @@ int HttpStreamParser::SendRequest(const std::string& request_line,
}
int HttpStreamParser::ReadResponseHeaders(const CompletionCallback& callback) {
- DCHECK(io_state_ == STATE_REQUEST_SENT || io_state_ == STATE_DONE);
+ DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
DCHECK(callback_.is_null());
DCHECK(!callback.is_null());
DCHECK_EQ(0, read_buf_unused_offset_);
@@ -327,7 +337,7 @@ void HttpStreamParser::Close(bool not_reusable) {
int HttpStreamParser::ReadResponseBody(IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
- DCHECK(io_state_ == STATE_BODY_PENDING || io_state_ == STATE_DONE);
+ DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
DCHECK(callback_.is_null());
DCHECK(!callback.is_null());
DCHECK_LE(buf_len, kMaxBufSize);
@@ -359,30 +369,33 @@ void HttpStreamParser::OnIOComplete(int result) {
}
int HttpStreamParser::DoLoop(int result) {
- bool can_do_more = true;
do {
- switch (io_state_) {
- case STATE_SENDING_HEADERS:
- if (result < 0)
- can_do_more = false;
- else
- result = DoSendHeaders(result);
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK_NE(STATE_DONE, io_state_);
+ DCHECK_NE(STATE_NONE, io_state_);
+ State state = io_state_;
+ io_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_SEND_HEADERS:
+ DCHECK_EQ(OK, result);
+ result = DoSendHeaders();
break;
- case STATE_SENDING_BODY:
- if (result < 0)
- can_do_more = false;
- else
- result = DoSendBody(result);
+ case STATE_SEND_HEADERS_COMPLETE:
+ result = DoSendHeadersComplete(result);
break;
- case STATE_SEND_REQUEST_READING_BODY:
- result = DoSendRequestReadingBody(result);
+ case STATE_SEND_BODY:
+ DCHECK_EQ(OK, result);
+ result = DoSendBody();
break;
- case STATE_REQUEST_SENT:
- DCHECK(result != ERR_IO_PENDING);
- can_do_more = false;
+ case STATE_SEND_BODY_COMPLETE:
+ result = DoSendBodyComplete(result);
+ break;
+ case STATE_SEND_REQUEST_READ_BODY_COMPLETE:
+ result = DoSendRequestReadBodyComplete(result);
break;
case STATE_READ_HEADERS:
net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS);
+ DCHECK_GE(result, 0);
result = DoReadHeaders();
break;
case STATE_READ_HEADERS_COMPLETE:
@@ -390,68 +403,79 @@ int HttpStreamParser::DoLoop(int result) {
net_log_.EndEventWithNetErrorCode(
NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, result);
break;
- case STATE_BODY_PENDING:
- DCHECK(result != ERR_IO_PENDING);
- can_do_more = false;
- break;
case STATE_READ_BODY:
+ DCHECK_GE(result, 0);
result = DoReadBody();
- // DoReadBodyComplete handles error conditions.
break;
case STATE_READ_BODY_COMPLETE:
result = DoReadBodyComplete(result);
break;
- case STATE_DONE:
- DCHECK(result != ERR_IO_PENDING);
- can_do_more = false;
- break;
default:
NOTREACHED();
- can_do_more = false;
break;
}
- } while (result != ERR_IO_PENDING && can_do_more);
+ } while (result != ERR_IO_PENDING &&
+ (io_state_ != STATE_DONE && io_state_ != STATE_NONE));
return result;
}
-int HttpStreamParser::DoSendHeaders(int result) {
- request_headers_->DidConsume(result);
+int HttpStreamParser::DoSendHeaders() {
int bytes_remaining = request_headers_->BytesRemaining();
- if (bytes_remaining > 0) {
- // Record our best estimate of the 'request time' as the time when we send
- // out the first bytes of the request headers.
- if (bytes_remaining == request_headers_->size()) {
- response_->request_time = base::Time::Now();
+ DCHECK_GT(bytes_remaining, 0);
+
+ // Record our best estimate of the 'request time' as the time when we send
+ // out the first bytes of the request headers.
+ if (bytes_remaining == request_headers_->size())
+ response_->request_time = base::Time::Now();
+
+ io_state_ = STATE_SEND_HEADERS_COMPLETE;
+ return connection_->socket()
+ ->Write(request_headers_.get(), bytes_remaining, io_callback_);
+}
+
+int HttpStreamParser::DoSendHeadersComplete(int result) {
+ if (result < 0) {
+ // In the unlikely case that the headers and body were merged, all the
+ // the headers were sent, but not all of the body way, and |result| is
+ // an error that this should try reading after, stash the error for now and
+ // act like the request was successfully sent.
+ if (request_headers_->BytesConsumed() >= request_headers_length_ &&
+ ShouldTryReadingOnUploadError(result)) {
+ upload_error_ = result;
+ return OK;
}
- result = connection_->socket()
- ->Write(request_headers_.get(), bytes_remaining, io_callback_);
- } else if (request_->upload_data_stream != NULL &&
- (request_->upload_data_stream->is_chunked() ||
- // !IsEOF() indicates that the body wasn't merged.
- (request_->upload_data_stream->size() > 0 &&
- !request_->upload_data_stream->IsEOF()))) {
+ return result;
+ }
+
+ request_headers_->DidConsume(result);
+ if (request_headers_->BytesRemaining() > 0) {
+ io_state_ = STATE_SEND_HEADERS;
+ return OK;
+ }
+
+ if (request_->upload_data_stream != NULL &&
+ (request_->upload_data_stream->is_chunked() ||
+ // !IsEOF() indicates that the body wasn't merged.
+ (request_->upload_data_stream->size() > 0 &&
+ !request_->upload_data_stream->IsEOF()))) {
net_log_.AddEvent(
NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_BODY,
base::Bind(&NetLogSendRequestBodyCallback,
request_->upload_data_stream->size(),
request_->upload_data_stream->is_chunked(),
false /* not merged */));
- io_state_ = STATE_SENDING_BODY;
- result = OK;
- } else {
- io_state_ = STATE_REQUEST_SENT;
+ io_state_ = STATE_SEND_BODY;
+ return OK;
}
- return result;
-}
-int HttpStreamParser::DoSendBody(int result) {
- // |result| is the number of bytes sent from the last call to
- // DoSendBody(), or 0 (i.e. OK).
+ // Finished sending the request.
+ return OK;
+}
- // Send the remaining data in the request body buffer.
- request_body_send_buf_->DidConsume(result);
+int HttpStreamParser::DoSendBody() {
if (request_body_send_buf_->BytesRemaining() > 0) {
+ io_state_ = STATE_SEND_BODY_COMPLETE;
return connection_->socket()
->Write(request_body_send_buf_.get(),
request_body_send_buf_->BytesRemaining(),
@@ -459,18 +483,35 @@ int HttpStreamParser::DoSendBody(int result) {
}
if (request_->upload_data_stream->is_chunked() && sent_last_chunk_) {
- io_state_ = STATE_REQUEST_SENT;
+ // Finished sending the request.
return OK;
}
request_body_read_buf_->Clear();
- io_state_ = STATE_SEND_REQUEST_READING_BODY;
+ io_state_ = STATE_SEND_REQUEST_READ_BODY_COMPLETE;
return request_->upload_data_stream->Read(request_body_read_buf_.get(),
request_body_read_buf_->capacity(),
io_callback_);
}
-int HttpStreamParser::DoSendRequestReadingBody(int result) {
+int HttpStreamParser::DoSendBodyComplete(int result) {
+ if (result < 0) {
+ // If |result| is an error that this should try reading after, stash the
+ // error for now and act like the request was successfully sent.
+ if (ShouldTryReadingOnUploadError(result)) {
+ upload_error_ = result;
+ return OK;
+ }
+ return result;
+ }
+
+ request_body_send_buf_->DidConsume(result);
+
+ io_state_ = STATE_SEND_BODY;
+ return OK;
+}
+
+int HttpStreamParser::DoSendRequestReadBodyComplete(int result) {
// |result| is the result of read from the request body from the last call to
// DoSendBody().
DCHECK_GE(result, 0); // There won't be errors.
@@ -494,11 +535,11 @@ int HttpStreamParser::DoSendRequestReadingBody(int result) {
// chunked. (i.e. No need to send the terminal chunk.)
DCHECK(request_->upload_data_stream->IsEOF());
DCHECK(!request_->upload_data_stream->is_chunked());
- io_state_ = STATE_REQUEST_SENT;
+ // Finished sending the request.
} else if (result > 0) {
request_body_send_buf_->DidAppend(result);
result = 0;
- io_state_ = STATE_SENDING_BODY;
+ io_state_ = STATE_SEND_BODY;
}
return result;
}
@@ -519,110 +560,50 @@ int HttpStreamParser::DoReadHeaders() {
}
int HttpStreamParser::DoReadHeadersComplete(int result) {
- DCHECK_EQ(0, read_buf_unused_offset_);
+ result = HandleReadHeaderResult(result);
- if (result == 0)
- result = ERR_CONNECTION_CLOSED;
+ // TODO(mmenke): The code below is ugly and hacky. A much better and more
+ // flexible long term solution would be to separate out the read and write
+ // loops, though this would involve significant changes, both here and
+ // elsewhere (WebSockets, for instance).
- if (result < 0 && result != ERR_CONNECTION_CLOSED) {
- io_state_ = STATE_DONE;
+ // If still reading the headers, or there was no error uploading the request
+ // body, just return the result.
+ if (io_state_ == STATE_READ_HEADERS || upload_error_ == OK)
return result;
- }
- // If we've used the connection before, then we know it is not a HTTP/0.9
- // response and return ERR_CONNECTION_CLOSED.
- if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 &&
- connection_->is_reused()) {
+
+ // If the result is ERR_IO_PENDING, |io_state_| should be STATE_READ_HEADERS.
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ // On errors, use the original error received when sending the request.
+ // The main cases where these are different is when there's a header-related
+ // error code, or when there's an ERR_CONNECTION_CLOSED, which can result in
+ // special handling of partial responses and HTTP/0.9 responses.
+ if (result < 0) {
+ // Nothing else to do. In the HTTP/0.9 or only partial headers received
+ // cases, can normally go to other states after an error reading headers.
io_state_ = STATE_DONE;
- return result;
+ // Don't let caller see the headers.
+ response_->headers = NULL;
+ return upload_error_;
}
- // Record our best estimate of the 'response time' as the time when we read
- // the first bytes of the response headers.
- if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED)
- response_->response_time = base::Time::Now();
-
- if (result == ERR_CONNECTION_CLOSED) {
- // The connection closed before we detected the end of the headers.
- if (read_buf_->offset() == 0) {
- // The connection was closed before any data was sent. Likely an error
- // rather than empty HTTP/0.9 response.
- io_state_ = STATE_DONE;
- return ERR_EMPTY_RESPONSE;
- } else if (request_->url.SchemeIsSecure()) {
- // The connection was closed in the middle of the headers. For HTTPS we
- // don't parse partial headers. Return a different error code so that we
- // know that we shouldn't attempt to retry the request.
- io_state_ = STATE_DONE;
- return ERR_RESPONSE_HEADERS_TRUNCATED;
- }
- // Parse things as well as we can and let the caller decide what to do.
- int end_offset;
- if (response_header_start_offset_ >= 0) {
- io_state_ = STATE_READ_BODY_COMPLETE;
- end_offset = read_buf_->offset();
- } else {
- io_state_ = STATE_BODY_PENDING;
- end_offset = 0;
- }
- int rv = DoParseResponseHeaders(end_offset);
- if (rv < 0)
- return rv;
+ // Skip over 1xx responses as usual, and allow 4xx/5xx error responses to
+ // override the error received while uploading the body.
+ int response_code_class = response_->headers->response_code() / 100;
+ if (response_code_class == 1 || response_code_class == 4 ||
+ response_code_class == 5) {
return result;
}
- read_buf_->set_offset(read_buf_->offset() + result);
- DCHECK_LE(read_buf_->offset(), read_buf_->capacity());
- DCHECK_GE(result, 0);
-
- int end_of_header_offset = ParseResponseHeaders();
-
- // Note: -1 is special, it indicates we haven't found the end of headers.
- // Anything less than -1 is a net::Error, so we bail out.
- if (end_of_header_offset < -1)
- return end_of_header_offset;
-
- if (end_of_header_offset == -1) {
- io_state_ = STATE_READ_HEADERS;
- // Prevent growing the headers buffer indefinitely.
- if (read_buf_->offset() >= kMaxHeaderBufSize) {
- io_state_ = STATE_DONE;
- return ERR_RESPONSE_HEADERS_TOO_BIG;
- }
- } else {
- CalculateResponseBodySize();
- // If the body is zero length, the caller may not call ReadResponseBody,
- // which is where any extra data is copied to read_buf_, so we move the
- // data here.
- if (response_body_length_ == 0) {
- int extra_bytes = read_buf_->offset() - end_of_header_offset;
- if (extra_bytes) {
- CHECK_GT(extra_bytes, 0);
- memmove(read_buf_->StartOfBuffer(),
- read_buf_->StartOfBuffer() + end_of_header_offset,
- extra_bytes);
- }
- read_buf_->SetCapacity(extra_bytes);
- if (response_->headers->response_code() / 100 == 1) {
- // After processing a 1xx response, the caller will ask for the next
- // header, so reset state to support that. We don't completely ignore a
- // 1xx response because it cannot be returned in reply to a CONNECT
- // request so we return OK here, which lets the caller inspect the
- // response and reject it in the event that we're setting up a CONNECT
- // tunnel.
- response_header_start_offset_ = -1;
- response_body_length_ = -1;
- io_state_ = STATE_REQUEST_SENT;
- } else {
- io_state_ = STATE_DONE;
- }
- return OK;
- }
+ // All other status codes are not allowed after an error during upload, to
+ // make sure the consumer has some indication there was an error.
- // Note where the headers stop.
- read_buf_unused_offset_ = end_of_header_offset;
- io_state_ = STATE_BODY_PENDING;
- }
- return result;
+ // Nothing else to do.
+ io_state_ = STATE_DONE;
+ // Don't let caller see the headers.
+ response_->headers = NULL;
+ return upload_error_;
}
int HttpStreamParser::DoReadBody() {
@@ -751,7 +732,7 @@ int HttpStreamParser::DoReadBodyComplete(int result) {
}
read_buf_unused_offset_ = 0;
} else {
- io_state_ = STATE_BODY_PENDING;
+ // Now waiting for more of the body to be read.
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
}
@@ -759,6 +740,113 @@ int HttpStreamParser::DoReadBodyComplete(int result) {
return result;
}
+int HttpStreamParser::HandleReadHeaderResult(int result) {
+ DCHECK_EQ(0, read_buf_unused_offset_);
+
+ if (result == 0)
+ result = ERR_CONNECTION_CLOSED;
+
+ if (result < 0 && result != ERR_CONNECTION_CLOSED) {
+ io_state_ = STATE_DONE;
+ return result;
+ }
+ // If we've used the connection before, then we know it is not a HTTP/0.9
+ // response and return ERR_CONNECTION_CLOSED.
+ if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 &&
+ connection_->is_reused()) {
+ io_state_ = STATE_DONE;
+ return result;
+ }
+
+ // Record our best estimate of the 'response time' as the time when we read
+ // the first bytes of the response headers.
+ if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED)
+ response_->response_time = base::Time::Now();
+
+ if (result == ERR_CONNECTION_CLOSED) {
+ // The connection closed before we detected the end of the headers.
+ if (read_buf_->offset() == 0) {
+ // The connection was closed before any data was sent. Likely an error
+ // rather than empty HTTP/0.9 response.
+ io_state_ = STATE_DONE;
+ return ERR_EMPTY_RESPONSE;
+ } else if (request_->url.SchemeIsSecure()) {
+ // The connection was closed in the middle of the headers. For HTTPS we
+ // don't parse partial headers. Return a different error code so that we
+ // know that we shouldn't attempt to retry the request.
+ io_state_ = STATE_DONE;
+ return ERR_RESPONSE_HEADERS_TRUNCATED;
+ }
+ // Parse things as well as we can and let the caller decide what to do.
+ int end_offset;
+ if (response_header_start_offset_ >= 0) {
+ io_state_ = STATE_READ_BODY_COMPLETE;
+ end_offset = read_buf_->offset();
+ } else {
+ // Now waiting for the body to be read.
+ end_offset = 0;
+ }
+ int rv = DoParseResponseHeaders(end_offset);
+ if (rv < 0)
+ return rv;
+ return result;
+ }
+
+ read_buf_->set_offset(read_buf_->offset() + result);
+ DCHECK_LE(read_buf_->offset(), read_buf_->capacity());
+ DCHECK_GE(result, 0);
+
+ int end_of_header_offset = ParseResponseHeaders();
+
+ // Note: -1 is special, it indicates we haven't found the end of headers.
+ // Anything less than -1 is a net::Error, so we bail out.
+ if (end_of_header_offset < -1)
+ return end_of_header_offset;
+
+ if (end_of_header_offset == -1) {
+ io_state_ = STATE_READ_HEADERS;
+ // Prevent growing the headers buffer indefinitely.
+ if (read_buf_->offset() >= kMaxHeaderBufSize) {
+ io_state_ = STATE_DONE;
+ return ERR_RESPONSE_HEADERS_TOO_BIG;
+ }
+ } else {
+ CalculateResponseBodySize();
+ // If the body is zero length, the caller may not call ReadResponseBody,
+ // which is where any extra data is copied to read_buf_, so we move the
+ // data here.
+ if (response_body_length_ == 0) {
+ int extra_bytes = read_buf_->offset() - end_of_header_offset;
+ if (extra_bytes) {
+ CHECK_GT(extra_bytes, 0);
+ memmove(read_buf_->StartOfBuffer(),
+ read_buf_->StartOfBuffer() + end_of_header_offset,
+ extra_bytes);
+ }
+ read_buf_->SetCapacity(extra_bytes);
+ if (response_->headers->response_code() / 100 == 1) {
+ // After processing a 1xx response, the caller will ask for the next
+ // header, so reset state to support that. We don't completely ignore a
+ // 1xx response because it cannot be returned in reply to a CONNECT
+ // request so we return OK here, which lets the caller inspect the
+ // response and reject it in the event that we're setting up a CONNECT
+ // tunnel.
+ response_header_start_offset_ = -1;
+ response_body_length_ = -1;
+ // Now waiting for the second set of headers to be read.
+ } else {
+ io_state_ = STATE_DONE;
+ }
+ return OK;
+ }
+
+ // Note where the headers stop.
+ read_buf_unused_offset_ = end_of_header_offset;
+ // Now waiting for the body to be read.
+ }
+ return result;
+}
+
int HttpStreamParser::ParseResponseHeaders() {
int end_offset = -1;
DCHECK_EQ(0, read_buf_unused_offset_);
@@ -876,10 +964,6 @@ UploadProgress HttpStreamParser::GetUploadProgress() const {
request_->upload_data_stream->size());
}
-HttpResponseInfo* HttpStreamParser::GetResponseInfo() {
- return response_;
-}
-
bool HttpStreamParser::IsResponseBodyComplete() const {
if (chunked_decoder_.get())
return chunked_decoder_->reached_eof();
@@ -904,7 +988,7 @@ bool HttpStreamParser::IsConnectionReused() const {
}
void HttpStreamParser::SetConnectionReused() {
- connection_->set_is_reused(true);
+ connection_->set_reuse_type(ClientSocketHandle::REUSED_IDLE);
}
bool HttpStreamParser::IsConnectionReusable() const {
diff --git a/chromium/net/http/http_stream_parser.h b/chromium/net/http/http_stream_parser.h
index e3690322bbd..a8d0447a086 100644
--- a/chromium/net/http/http_stream_parser.h
+++ b/chromium/net/http/http_stream_parser.h
@@ -63,8 +63,6 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
// zero, but position will not be.
UploadProgress GetUploadProgress() const;
- HttpResponseInfo* GetResponseInfo();
-
bool IsResponseBodyComplete() const;
bool CanFindEndOfResponse() const;
@@ -110,17 +108,16 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
// FOO_COMPLETE states implement the second half of potentially asynchronous
// operations and don't necessarily mean that FOO is complete.
enum State {
+ // STATE_NONE indicates that this is waiting on an external call before
+ // continuing.
STATE_NONE,
- STATE_SENDING_HEADERS,
- // If the request comes with a body, either of the following two
- // states will be executed, depending on whether the body is chunked
- // or not.
- STATE_SENDING_BODY,
- STATE_SEND_REQUEST_READING_BODY,
- STATE_REQUEST_SENT,
+ STATE_SEND_HEADERS,
+ STATE_SEND_HEADERS_COMPLETE,
+ STATE_SEND_BODY,
+ STATE_SEND_BODY_COMPLETE,
+ STATE_SEND_REQUEST_READ_BODY_COMPLETE,
STATE_READ_HEADERS,
STATE_READ_HEADERS_COMPLETE,
- STATE_BODY_PENDING,
STATE_READ_BODY,
STATE_READ_BODY_COMPLETE,
STATE_DONE
@@ -146,14 +143,19 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
int DoLoop(int result);
// The implementations of each state of the state machine.
- int DoSendHeaders(int result);
- int DoSendBody(int result);
- int DoSendRequestReadingBody(int result);
+ int DoSendHeaders();
+ int DoSendHeadersComplete(int result);
+ int DoSendBody();
+ int DoSendBodyComplete(int result);
+ int DoSendRequestReadBodyComplete(int result);
int DoReadHeaders();
int DoReadHeadersComplete(int result);
int DoReadBody();
int DoReadBodyComplete(int result);
+ // This handles most of the logic for DoReadHeadersComplete.
+ int HandleReadHeaderResult(int result);
+
// Examines |read_buf_| to find the start and end of the headers. If they are
// found, parse them with DoParseResponseHeaders(). Return the offset for
// the end of the headers, or -1 if the complete headers were not found, or
@@ -167,15 +169,19 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
// Examine the parsed headers to try to determine the response body size.
void CalculateResponseBodySize();
- // Current state of the request.
+ // Next state of the request, when the current one completes.
State io_state_;
// The request to send.
const HttpRequestInfo* request_;
- // The request header data.
+ // The request header data. May include a merged request body.
scoped_refptr<DrainableIOBuffer> request_headers_;
+ // Size of just the request headers. May be less than the length of
+ // |request_headers_| if the body was merged with the headers.
+ int request_headers_length_;
+
// Temporary buffer for reading.
scoped_refptr<GrowableIOBuffer> read_buf_;
@@ -192,7 +198,10 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
// value may be bigger than final.
int64 received_bytes_;
- // The parsed response headers. Owned by the caller.
+ // The parsed response headers. Owned by the caller of SendRequest. This
+ // cannot be safely accessed after reading the final set of headers, as the
+ // caller of SendRequest may have been destroyed - this happens in the case an
+ // HttpResponseBodyDrainer is used.
HttpResponseInfo* response_;
// Indicates the content length. If this value is less than zero
@@ -235,6 +244,9 @@ class NET_EXPORT_PRIVATE HttpStreamParser {
scoped_refptr<SeekableIOBuffer> request_body_send_buf_;
bool sent_last_chunk_;
+ // Error received when uploading the body, if any.
+ int upload_error_;
+
base::WeakPtrFactory<HttpStreamParser> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HttpStreamParser);
diff --git a/chromium/net/http/http_stream_parser_unittest.cc b/chromium/net/http/http_stream_parser_unittest.cc
index dcaf1f3e9c3..33910395cbc 100644
--- a/chromium/net/http/http_stream_parser_unittest.cc
+++ b/chromium/net/http/http_stream_parser_unittest.cc
@@ -804,6 +804,63 @@ TEST(HttpStreamParser, ReceivedBytesIncludesContinueHeader) {
EXPECT_EQ(response_size, get_runner.parser()->received_bytes());
}
+// Test that an HttpStreamParser can be read from after it's received headers
+// and data structures owned by its owner have been deleted. This happens
+// when a ResponseBodyDrainer is used.
+TEST(HttpStreamParser, ReadAfterUnownedObjectsDestroyed) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0,
+ "GET /foo.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "1"),
+ };
+
+ const int kBodySize = 1;
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 1\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Connection: Keep-Alive\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "1"),
+ MockRead(SYNCHRONOUS, 0, 8), // EOF
+ };
+
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+ data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+
+ scoped_ptr<MockTCPClientSocket> transport(
+ new MockTCPClientSocket(AddressList(), NULL, &data));
+
+ TestCompletionCallback callback;
+ ASSERT_EQ(OK, transport->Connect(callback.callback()));
+
+ scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+ socket_handle->SetSocket(transport.PassAs<StreamSocket>());
+
+ scoped_ptr<HttpRequestInfo> request_info(new HttpRequestInfo());
+ request_info->method = "GET";
+ request_info->url = GURL("http://somewhere/foo.html");
+
+ scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+ HttpStreamParser parser(socket_handle.get(), request_info.get(),
+ read_buffer.get(), BoundNetLog());
+
+ scoped_ptr<HttpRequestHeaders> request_headers(new HttpRequestHeaders());
+ scoped_ptr<HttpResponseInfo> response_info(new HttpResponseInfo());
+ ASSERT_EQ(OK, parser.SendRequest("GET /foo.html HTTP/1.1\r\n",
+ *request_headers, response_info.get(), callback.callback()));
+ ASSERT_EQ(OK, parser.ReadResponseHeaders(callback.callback()));
+
+ // If the object that owns the HttpStreamParser is deleted, it takes the
+ // objects passed to the HttpStreamParser with it.
+ request_info.reset();
+ request_headers.reset();
+ response_info.reset();
+
+ scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+ ASSERT_EQ(kBodySize, parser.ReadResponseBody(
+ body_buffer.get(), kBodySize, callback.callback()));
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/http/http_transaction.h b/chromium/net/http/http_transaction.h
index 849d03af50c..ed1b20dac2f 100644
--- a/chromium/net/http/http_transaction.h
+++ b/chromium/net/http/http_transaction.h
@@ -21,6 +21,7 @@ struct HttpRequestInfo;
class HttpResponseInfo;
class IOBuffer;
struct LoadTimingInfo;
+class QuicServerInfo;
class X509Certificate;
// Represents a single HTTP transaction (i.e., a single request/response pair).
@@ -28,6 +29,10 @@ class X509Certificate;
// answered. Cookies are assumed to be managed by the caller.
class NET_EXPORT_PRIVATE HttpTransaction {
public:
+ // If |*defer| is set to true, the transaction will wait until
+ // ResumeNetworkStart is called before establishing a connection.
+ typedef base::Callback<void(bool* defer)> BeforeNetworkStartCallback;
+
// Stops any pending IO and destroys the transaction object.
virtual ~HttpTransaction() {}
@@ -106,6 +111,9 @@ class NET_EXPORT_PRIVATE HttpTransaction {
// otherwise, returns false and does not modify headers.
virtual bool GetFullRequestHeaders(HttpRequestHeaders* headers) const = 0;
+ // Get the number of bytes received from network.
+ virtual int64 GetTotalReceivedBytes() const = 0;
+
// Called to tell the transaction that we have successfully reached the end
// 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
@@ -126,6 +134,10 @@ class NET_EXPORT_PRIVATE HttpTransaction {
// zero will be returned. This does not include the request headers.
virtual UploadProgress GetUploadProgress() const = 0;
+ // SetQuicServerInfo sets a object which reads and writes public information
+ // about a QUIC server.
+ virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) = 0;
+
// Populates all of load timing, except for request start times and receive
// headers time.
// |load_timing_info| must have all null times when called. Returns false and
@@ -141,6 +153,13 @@ class NET_EXPORT_PRIVATE HttpTransaction {
// Start(). Ownership of |create_helper| remains with the caller.
virtual void SetWebSocketHandshakeStreamCreateHelper(
WebSocketHandshakeStreamBase::CreateHelper* create_helper) = 0;
+
+ // Set the callback to receive notification just before network use.
+ virtual void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) = 0;
+
+ // Resumes the transaction after being deferred.
+ virtual int ResumeNetworkStart() = 0;
};
} // namespace net
diff --git a/chromium/net/http/http_transaction_delegate.h b/chromium/net/http/http_transaction_delegate.h
deleted file mode 100644
index 18493733bff..00000000000
--- a/chromium/net/http/http_transaction_delegate.h
+++ /dev/null
@@ -1,26 +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_HTTP_HTTP_TRANSACTION_DELEGATE_H_
-#define NET_HTTP_HTTP_TRANSACTION_DELEGATE_H_
-
-namespace net {
-
-// Delegate class receiving notifications when cache or network actions start
-// and finish, i.e. when the object starts and finishes waiting on an
-// underlying cache or the network. The owner of a HttpTransaction can use
-// this to register a delegate to receive notifications when these events
-// happen.
-class HttpTransactionDelegate {
- public:
- virtual ~HttpTransactionDelegate() {}
- virtual void OnCacheActionStart() = 0;
- virtual void OnCacheActionFinish() = 0;
- virtual void OnNetworkActionStart() = 0;
- virtual void OnNetworkActionFinish() = 0;
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_TRANSACTION_DELEGATE_H_
diff --git a/chromium/net/http/http_transaction_factory.h b/chromium/net/http/http_transaction_factory.h
index d9870be914b..673a6f921e3 100644
--- a/chromium/net/http/http_transaction_factory.h
+++ b/chromium/net/http/http_transaction_factory.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_HTTP_HTTP_TRANSACTION_FACTORY_H__
-#define NET_HTTP_HTTP_TRANSACTION_FACTORY_H__
+#ifndef NET_HTTP_HTTP_TRANSACTION_FACTORY_H_
+#define NET_HTTP_HTTP_TRANSACTION_FACTORY_H_
#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
@@ -14,7 +14,6 @@ namespace net {
class HttpCache;
class HttpNetworkSession;
class HttpTransaction;
-class HttpTransactionDelegate;
// An interface to a class that can create HttpTransaction objects.
class NET_EXPORT HttpTransactionFactory {
@@ -24,8 +23,7 @@ class NET_EXPORT HttpTransactionFactory {
// Creates a HttpTransaction object. On success, saves the new
// transaction to |*trans| and returns OK.
virtual int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) = 0;
+ scoped_ptr<HttpTransaction>* trans) = 0;
// Returns the associated cache if any (may be NULL).
virtual HttpCache* GetCache() = 0;
@@ -36,4 +34,4 @@ class NET_EXPORT HttpTransactionFactory {
} // namespace net
-#endif // NET_HTTP_HTTP_TRANSACTION_FACTORY_H__
+#endif // NET_HTTP_HTTP_TRANSACTION_FACTORY_H_
diff --git a/chromium/net/http/http_transaction_unittest.cc b/chromium/net/http/http_transaction_test_util.cc
index e161ecac506..f3d086c5133 100644
--- a/chromium/net/http/http_transaction_unittest.cc
+++ b/chromium/net/http/http_transaction_test_util.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 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/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include <algorithm>
@@ -157,7 +157,7 @@ TestTransactionConsumer::TestTransactionConsumer(
net::HttpTransactionFactory* factory)
: state_(IDLE), error_(net::OK) {
// Disregard the error code.
- factory->CreateTransaction(priority, &trans_, NULL);
+ factory->CreateTransaction(priority, &trans_);
++quit_counter_;
}
@@ -226,10 +226,12 @@ MockNetworkTransaction::MockNetworkTransaction(
net::RequestPriority priority,
MockNetworkLayer* factory)
: weak_factory_(this),
+ request_(NULL),
data_cursor_(0),
priority_(priority),
websocket_handshake_stream_create_helper_(NULL),
transaction_factory_(factory->AsWeakPtr()),
+ received_bytes_(0),
socket_log_id_(net::NetLog::Source::kInvalidId) {
}
@@ -238,54 +240,11 @@ MockNetworkTransaction::~MockNetworkTransaction() {}
int MockNetworkTransaction::Start(const net::HttpRequestInfo* request,
const net::CompletionCallback& callback,
const net::BoundNetLog& net_log) {
- const MockTransaction* t = FindMockTransaction(request->url);
- if (!t)
+ if (request_)
return net::ERR_FAILED;
- test_mode_ = t->test_mode;
-
- // Return immediately if we're returning an error.
- if (net::OK != t->return_code) {
- if (test_mode_ & TEST_MODE_SYNC_NET_START)
- return t->return_code;
- CallbackLater(callback, t->return_code);
- return net::ERR_IO_PENDING;
- }
-
- std::string resp_status = t->status;
- std::string resp_headers = t->response_headers;
- std::string resp_data = t->data;
- if (t->handler)
- (t->handler)(request, &resp_status, &resp_headers, &resp_data);
-
- std::string header_data = base::StringPrintf(
- "%s\n%s\n", resp_status.c_str(), resp_headers.c_str());
- std::replace(header_data.begin(), header_data.end(), '\n', '\0');
-
- response_.request_time = base::Time::Now();
- if (!t->request_time.is_null())
- response_.request_time = t->request_time;
-
- response_.was_cached = false;
- response_.network_accessed = true;
-
- response_.response_time = base::Time::Now();
- if (!t->response_time.is_null())
- response_.response_time = t->response_time;
-
- response_.headers = new net::HttpResponseHeaders(header_data);
- response_.vary_data.Init(*request, *response_.headers.get());
- response_.ssl_info.cert_status = t->cert_status;
- data_ = resp_data;
-
- if (net_log.net_log())
- socket_log_id_ = net_log.net_log()->NextID();
-
- if (test_mode_ & TEST_MODE_SYNC_NET_START)
- return net::OK;
-
- CallbackLater(callback, net::OK);
- return net::ERR_IO_PENDING;
+ request_ = request;
+ return StartInternal(request, callback, net_log);
}
int MockNetworkTransaction::RestartIgnoringLastError(
@@ -302,17 +261,32 @@ int MockNetworkTransaction::RestartWithCertificate(
int MockNetworkTransaction::RestartWithAuth(
const net::AuthCredentials& credentials,
const net::CompletionCallback& callback) {
- return net::ERR_FAILED;
+ if (!IsReadyToRestartForAuth())
+ return net::ERR_FAILED;
+
+ net::HttpRequestInfo auth_request_info = *request_;
+ auth_request_info.extra_headers.AddHeaderFromString("Authorization: Bar");
+
+ // Let the MockTransactionHandler worry about this: the only way for this
+ // test to succeed is by using an explicit handler for the transaction so
+ // that server behavior can be simulated.
+ return StartInternal(&auth_request_info, callback, net::BoundNetLog());
}
bool MockNetworkTransaction::IsReadyToRestartForAuth() {
- return false;
+ if (!request_)
+ return false;
+
+ // Only mock auth when the test asks for it.
+ return request_->extra_headers.HasHeader("X-Require-Mock-Auth");
}
int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback) {
int data_len = static_cast<int>(data_.size());
int num = std::min(buf_len, data_len - data_cursor_);
+ if (test_mode_ & TEST_MODE_SLOW_READ)
+ num = std::min(num, 1);
if (num) {
memcpy(buf->data(), data_.data() + data_cursor_, num);
data_cursor_ += num;
@@ -324,13 +298,20 @@ int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len,
return net::ERR_IO_PENDING;
}
-void MockNetworkTransaction::StopCaching() {}
+void MockNetworkTransaction::StopCaching() {
+ if (transaction_factory_.get())
+ transaction_factory_->TransactionStopCaching();
+}
bool MockNetworkTransaction::GetFullRequestHeaders(
net::HttpRequestHeaders* headers) const {
return false;
}
+int64 MockNetworkTransaction::GetTotalReceivedBytes() const {
+ return received_bytes_;
+}
+
void MockNetworkTransaction::DoneReading() {
if (transaction_factory_.get())
transaction_factory_->TransactionDoneReading();
@@ -350,6 +331,9 @@ net::UploadProgress MockNetworkTransaction::GetUploadProgress() const {
return net::UploadProgress();
}
+void MockNetworkTransaction::SetQuicServerInfo(
+ net::QuicServerInfo* quic_server_info) {}
+
bool MockNetworkTransaction::GetLoadTimingInfo(
net::LoadTimingInfo* load_timing_info) const {
if (socket_log_id_ != net::NetLog::Source::kInvalidId) {
@@ -381,6 +365,70 @@ void MockNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper(
websocket_handshake_stream_create_helper_ = create_helper;
}
+int MockNetworkTransaction::StartInternal(
+ const net::HttpRequestInfo* request,
+ const net::CompletionCallback& callback,
+ const net::BoundNetLog& net_log) {
+ const MockTransaction* t = FindMockTransaction(request->url);
+ if (!t)
+ return net::ERR_FAILED;
+
+ test_mode_ = t->test_mode;
+
+ // Return immediately if we're returning an error.
+ if (net::OK != t->return_code) {
+ if (test_mode_ & TEST_MODE_SYNC_NET_START)
+ return t->return_code;
+ CallbackLater(callback, t->return_code);
+ return net::ERR_IO_PENDING;
+ }
+
+ std::string resp_status = t->status;
+ std::string resp_headers = t->response_headers;
+ std::string resp_data = t->data;
+ received_bytes_ = resp_status.size() + resp_headers.size() + resp_data.size();
+ if (t->handler)
+ (t->handler)(request, &resp_status, &resp_headers, &resp_data);
+
+ std::string header_data = base::StringPrintf(
+ "%s\n%s\n", resp_status.c_str(), resp_headers.c_str());
+ std::replace(header_data.begin(), header_data.end(), '\n', '\0');
+
+ response_.request_time = base::Time::Now();
+ if (!t->request_time.is_null())
+ response_.request_time = t->request_time;
+
+ response_.was_cached = false;
+ response_.network_accessed = true;
+
+ response_.response_time = base::Time::Now();
+ if (!t->response_time.is_null())
+ response_.response_time = t->response_time;
+
+ response_.headers = new net::HttpResponseHeaders(header_data);
+ response_.vary_data.Init(*request, *response_.headers.get());
+ response_.ssl_info.cert_status = t->cert_status;
+ data_ = resp_data;
+
+ if (net_log.net_log())
+ socket_log_id_ = net_log.net_log()->NextID();
+
+ if (test_mode_ & TEST_MODE_SYNC_NET_START)
+ return net::OK;
+
+ CallbackLater(callback, net::OK);
+ return net::ERR_IO_PENDING;
+}
+
+void MockNetworkTransaction::SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) {
+}
+
+int MockNetworkTransaction::ResumeNetworkStart() {
+ // Should not get here.
+ return net::ERR_FAILED;
+}
+
void MockNetworkTransaction::CallbackLater(
const net::CompletionCallback& callback, int result) {
base::MessageLoop::current()->PostTask(
@@ -396,6 +444,7 @@ void MockNetworkTransaction::RunCallback(
MockNetworkLayer::MockNetworkLayer()
: transaction_count_(0),
done_reading_called_(false),
+ stop_caching_called_(false),
last_create_transaction_priority_(net::DEFAULT_PRIORITY) {}
MockNetworkLayer::~MockNetworkLayer() {}
@@ -404,10 +453,13 @@ void MockNetworkLayer::TransactionDoneReading() {
done_reading_called_ = true;
}
+void MockNetworkLayer::TransactionStopCaching() {
+ stop_caching_called_ = true;
+}
+
int MockNetworkLayer::CreateTransaction(
net::RequestPriority priority,
- scoped_ptr<net::HttpTransaction>* trans,
- net::HttpTransactionDelegate* delegate) {
+ scoped_ptr<net::HttpTransaction>* trans) {
transaction_count_++;
last_create_transaction_priority_ = priority;
scoped_ptr<MockNetworkTransaction> mock_transaction(
diff --git a/chromium/net/http/http_transaction_unittest.h b/chromium/net/http/http_transaction_test_util.h
index 3d6bb0287fb..f123794529b 100644
--- a/chromium/net/http/http_transaction_unittest.h
+++ b/chromium/net/http/http_transaction_test_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
@@ -43,7 +43,8 @@ enum {
TEST_MODE_SYNC_CACHE_WRITE = 1 << 4,
TEST_MODE_SYNC_ALL = (TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ |
TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ |
- TEST_MODE_SYNC_CACHE_WRITE)
+ TEST_MODE_SYNC_CACHE_WRITE),
+ TEST_MODE_SLOW_READ = 1 << 5
};
typedef void (*MockTransactionHandler)(const net::HttpRequestInfo* request,
@@ -193,6 +194,8 @@ class MockNetworkTransaction
virtual bool GetFullRequestHeaders(
net::HttpRequestHeaders* headers) const OVERRIDE;
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
+
virtual void DoneReading() OVERRIDE;
virtual const net::HttpResponseInfo* GetResponseInfo() const OVERRIDE;
@@ -201,6 +204,9 @@ class MockNetworkTransaction
virtual net::UploadProgress GetUploadProgress() const OVERRIDE;
+ virtual void SetQuicServerInfo(
+ net::QuicServerInfo* quic_server_info) OVERRIDE;
+
virtual bool GetLoadTimingInfo(
net::LoadTimingInfo* load_timing_info) const OVERRIDE;
@@ -209,16 +215,25 @@ class MockNetworkTransaction
virtual void SetWebSocketHandshakeStreamCreateHelper(
CreateHelper* create_helper) OVERRIDE;
+ virtual void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) OVERRIDE;
+
+ virtual int ResumeNetworkStart() OVERRIDE;
+
CreateHelper* websocket_handshake_stream_create_helper() {
return websocket_handshake_stream_create_helper_;
}
net::RequestPriority priority() const { return priority_; }
private:
+ int StartInternal(const net::HttpRequestInfo* request,
+ const net::CompletionCallback& callback,
+ const net::BoundNetLog& net_log);
void CallbackLater(const net::CompletionCallback& callback, int result);
void RunCallback(const net::CompletionCallback& callback, int result);
base::WeakPtrFactory<MockNetworkTransaction> weak_factory_;
+ const net::HttpRequestInfo* request_;
net::HttpResponseInfo response_;
std::string data_;
int data_cursor_;
@@ -226,6 +241,7 @@ class MockNetworkTransaction
net::RequestPriority priority_;
CreateHelper* websocket_handshake_stream_create_helper_;
base::WeakPtr<MockNetworkLayer> transaction_factory_;
+ int64 received_bytes_;
// NetLog ID of the fake / non-existent underlying socket used by the
// connection. Requires Start() be passed a BoundNetLog with a real NetLog to
@@ -241,7 +257,9 @@ class MockNetworkLayer : public net::HttpTransactionFactory,
int transaction_count() const { return transaction_count_; }
bool done_reading_called() const { return done_reading_called_; }
+ bool stop_caching_called() const { return stop_caching_called_; }
void TransactionDoneReading();
+ void TransactionStopCaching();
// Returns the last priority passed to CreateTransaction, or
// DEFAULT_PRIORITY if it hasn't been called yet.
@@ -267,14 +285,14 @@ class MockNetworkLayer : public net::HttpTransactionFactory,
// net::HttpTransactionFactory:
virtual int CreateTransaction(
net::RequestPriority priority,
- scoped_ptr<net::HttpTransaction>* trans,
- net::HttpTransactionDelegate* delegate) OVERRIDE;
+ scoped_ptr<net::HttpTransaction>* trans) OVERRIDE;
virtual net::HttpCache* GetCache() OVERRIDE;
virtual net::HttpNetworkSession* GetSession() OVERRIDE;
private:
int transaction_count_;
bool done_reading_called_;
+ bool stop_caching_called_;
net::RequestPriority last_create_transaction_priority_;
base::WeakPtr<MockNetworkTransaction> last_transaction_;
};
diff --git a/chromium/net/http/http_util.cc b/chromium/net/http/http_util.cc
index 77f498133bc..f4f994af605 100644
--- a/chromium/net/http/http_util.cc
+++ b/chromium/net/http/http_util.cc
@@ -18,54 +18,40 @@
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-using std::string;
namespace net {
-//-----------------------------------------------------------------------------
+// Helpers --------------------------------------------------------------------
-// Return the index of the closing quote of the string, if any.
-static size_t FindStringEnd(const string& line, size_t start, char delim) {
- DCHECK(start < line.length() && line[start] == delim &&
- (delim == '"' || delim == '\''));
+// Returns the index of the closing quote of the string, if any. |start| points
+// at the opening quote.
+static size_t FindStringEnd(const std::string& line, size_t start, char delim) {
+ DCHECK_LT(start, line.length());
+ DCHECK_EQ(line[start], delim);
+ DCHECK((delim == '"') || (delim == '\''));
const char set[] = { delim, '\\', '\0' };
- for (;;) {
- // start points to either the start quote or the last
- // escaped char (the char following a '\\')
-
- size_t end = line.find_first_of(set, start + 1);
- if (end == string::npos)
- return line.length();
-
- if (line[end] == '\\') {
- // Hit a backslash-escaped char. Need to skip over it.
- start = end + 1;
- if (start == line.length())
- return start;
-
- // Go back to looking for the next escape or the string end
- continue;
- }
-
- return end;
+ for (size_t end = line.find_first_of(set, start + 1);
+ end != std::string::npos; end = line.find_first_of(set, end + 2)) {
+ if (line[end] != '\\')
+ return end;
}
-
- NOTREACHED();
return line.length();
}
-//-----------------------------------------------------------------------------
+
+// HttpUtil -------------------------------------------------------------------
// static
-size_t HttpUtil::FindDelimiter(const string& line, size_t search_start,
+size_t HttpUtil::FindDelimiter(const std::string& line,
+ size_t search_start,
char delimiter) {
do {
// search_start points to the spot from which we should start looking
// for the delimiter.
const char delim_str[] = { delimiter, '"', '\'', '\0' };
size_t cur_delim_pos = line.find_first_of(delim_str, search_start);
- if (cur_delim_pos == string::npos)
+ if (cur_delim_pos == std::string::npos)
return line.length();
char ch = line[cur_delim_pos];
@@ -91,12 +77,12 @@ size_t HttpUtil::FindDelimiter(const string& line, size_t search_start,
}
// static
-void HttpUtil::ParseContentType(const string& content_type_str,
- string* mime_type,
- string* charset,
+void HttpUtil::ParseContentType(const std::string& content_type_str,
+ std::string* mime_type,
+ std::string* charset,
bool* had_charset,
- string* boundary) {
- const string::const_iterator begin = content_type_str.begin();
+ std::string* boundary) {
+ const std::string::const_iterator begin = content_type_str.begin();
// Trim leading and trailing whitespace from type. We include '(' in
// the trailing trim set to catch media-type comments, which are not at all
@@ -104,7 +90,7 @@ void HttpUtil::ParseContentType(const string& content_type_str,
size_t type_val = content_type_str.find_first_not_of(HTTP_LWS);
type_val = std::min(type_val, content_type_str.length());
size_t type_end = content_type_str.find_first_of(HTTP_LWS ";(", type_val);
- if (string::npos == type_end)
+ if (type_end == std::string::npos)
type_end = content_type_str.length();
size_t charset_val = 0;
@@ -113,22 +99,22 @@ void HttpUtil::ParseContentType(const string& content_type_str,
// Iterate over parameters
size_t param_start = content_type_str.find_first_of(';', type_end);
- if (param_start != string::npos) {
+ if (param_start != std::string::npos) {
base::StringTokenizer tokenizer(begin + param_start, content_type_str.end(),
";");
tokenizer.set_quote_chars("\"");
while (tokenizer.GetNext()) {
- string::const_iterator equals_sign =
+ std::string::const_iterator equals_sign =
std::find(tokenizer.token_begin(), tokenizer.token_end(), '=');
if (equals_sign == tokenizer.token_end())
continue;
- string::const_iterator param_name_begin = tokenizer.token_begin();
- string::const_iterator param_name_end = equals_sign;
+ std::string::const_iterator param_name_begin = tokenizer.token_begin();
+ std::string::const_iterator param_name_end = equals_sign;
TrimLWS(&param_name_begin, &param_name_end);
- string::const_iterator param_value_begin = equals_sign + 1;
- string::const_iterator param_value_end = tokenizer.token_end();
+ std::string::const_iterator param_value_begin = equals_sign + 1;
+ std::string::const_iterator param_value_end = tokenizer.token_end();
DCHECK(param_value_begin <= tokenizer.token_end());
TrimLWS(&param_value_begin, &param_value_end);
@@ -172,7 +158,7 @@ void HttpUtil::ParseContentType(const string& content_type_str,
// include a comma, so this check makes us a bit more tolerant.
if (content_type_str.length() != 0 &&
content_type_str != "*/*" &&
- content_type_str.find_first_of('/') != string::npos) {
+ content_type_str.find_first_of('/') != std::string::npos) {
// Common case here is that mime_type is empty
bool eq = !mime_type->empty() && LowerCaseEqualsASCII(begin + type_val,
begin + type_end,
@@ -291,7 +277,7 @@ bool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier,
// static
bool HttpUtil::HasHeader(const std::string& headers, const char* name) {
size_t name_len = strlen(name);
- string::const_iterator it =
+ std::string::const_iterator it =
std::search(headers.begin(),
headers.end(),
name,
@@ -379,8 +365,8 @@ std::string HttpUtil::StripHeaders(const std::string& headers,
}
// static
-bool HttpUtil::IsNonCoalescingHeader(string::const_iterator name_begin,
- string::const_iterator name_end) {
+bool HttpUtil::IsNonCoalescingHeader(std::string::const_iterator name_begin,
+ std::string::const_iterator name_end) {
// NOTE: "set-cookie2" headers do not support expires attributes, so we don't
// have to list them here.
const char* kNonCoalescingHeaders[] = {
@@ -409,8 +395,8 @@ bool HttpUtil::IsLWS(char c) {
return strchr(HTTP_LWS, c) != NULL;
}
-void HttpUtil::TrimLWS(string::const_iterator* begin,
- string::const_iterator* end) {
+void HttpUtil::TrimLWS(std::string::const_iterator* begin,
+ std::string::const_iterator* end) {
// leading whitespace
while (*begin < *end && IsLWS((*begin)[0]))
++(*begin);
@@ -427,8 +413,8 @@ bool HttpUtil::IsQuote(char c) {
}
// See RFC 2616 Sec 2.2 for the definition of |token|.
-bool HttpUtil::IsToken(string::const_iterator begin,
- string::const_iterator end) {
+bool HttpUtil::IsToken(std::string::const_iterator begin,
+ std::string::const_iterator end) {
if (begin == end)
return false;
for (std::string::const_iterator iter = begin; iter != end; ++iter) {
@@ -764,9 +750,10 @@ int HttpUtil::MapStatusCodeForHistogram(int code) {
// of token, separators, and quoted-string>
//
-HttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin,
- string::const_iterator headers_end,
- const std::string& line_delimiter)
+HttpUtil::HeadersIterator::HeadersIterator(
+ std::string::const_iterator headers_begin,
+ std::string::const_iterator headers_end,
+ const std::string& line_delimiter)
: lines_(headers_begin, headers_end, line_delimiter) {
}
@@ -778,7 +765,7 @@ bool HttpUtil::HeadersIterator::GetNext() {
name_begin_ = lines_.token_begin();
values_end_ = lines_.token_end();
- string::const_iterator colon = std::find(name_begin_, values_end_, ':');
+ std::string::const_iterator colon(std::find(name_begin_, values_end_, ':'));
if (colon == values_end_)
continue; // skip malformed header
@@ -818,10 +805,10 @@ bool HttpUtil::HeadersIterator::AdvanceTo(const char* name) {
}
HttpUtil::ValuesIterator::ValuesIterator(
- string::const_iterator values_begin,
- string::const_iterator values_end,
+ std::string::const_iterator values_begin,
+ std::string::const_iterator values_end,
char delimiter)
- : values_(values_begin, values_end, string(1, delimiter)) {
+ : values_(values_begin, values_end, std::string(1, delimiter)) {
values_.set_quote_chars("\'\"");
}
@@ -842,8 +829,8 @@ bool HttpUtil::ValuesIterator::GetNext() {
}
HttpUtil::NameValuePairsIterator::NameValuePairsIterator(
- string::const_iterator begin,
- string::const_iterator end,
+ std::string::const_iterator begin,
+ std::string::const_iterator end,
char delimiter)
: props_(begin, end, delimiter),
valid_(true),
diff --git a/chromium/net/http/http_util.h b/chromium/net/http/http_util.h
index ae65146e6e4..14117c00289 100644
--- a/chromium/net/http/http_util.h
+++ b/chromium/net/http/http_util.h
@@ -56,8 +56,8 @@ class NET_EXPORT HttpUtil {
// if "Range" exists and the first one of it is well formatted then returns
// true, |ranges| will contain a list of valid ranges. If return
// value is false then values in |ranges| should not be used. The format of
- // "Range" header is defined in RFC 2616 Section 14.35.1.
- // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
+ // "Range" header is defined in RFC 7233 Section 2.1.
+ // https://tools.ietf.org/html/rfc7233#section-2.1
static bool ParseRanges(const std::string& headers,
std::vector<HttpByteRange>* ranges);
diff --git a/chromium/net/http/mock_http_cache.cc b/chromium/net/http/mock_http_cache.cc
index a3d55b17e6b..5dbd486df0f 100644
--- a/chromium/net/http/mock_http_cache.cc
+++ b/chromium/net/http/mock_http_cache.cc
@@ -512,6 +512,10 @@ MockDiskCache* MockHttpCache::disk_cache() {
return (rv == net::OK) ? static_cast<MockDiskCache*>(backend) : NULL;
}
+int MockHttpCache::CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) {
+ return http_cache_.CreateTransaction(net::DEFAULT_PRIORITY, trans);
+}
+
bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
net::HttpResponseInfo* response_info,
bool* response_truncated) {
diff --git a/chromium/net/http/mock_http_cache.h b/chromium/net/http/mock_http_cache.h
index 90399ec4ff0..0fd192e597e 100644
--- a/chromium/net/http/mock_http_cache.h
+++ b/chromium/net/http/mock_http_cache.h
@@ -13,7 +13,7 @@
#include "base/containers/hash_tables.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
//-----------------------------------------------------------------------------
// Mock disk cache (a very basic memory cache implementation).
@@ -174,6 +174,9 @@ class MockHttpCache {
}
MockDiskCache* disk_cache();
+ // Wrapper around http_cache()->CreateTransaction(net::DEFAULT_PRIORITY...)
+ int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans);
+
// Helper function for reading response info from the disk cache.
static bool ReadResponseInfo(disk_cache::Entry* disk_entry,
net::HttpResponseInfo* response_info,
diff --git a/chromium/net/http/partial_data.cc b/chromium/net/http/partial_data.cc
index ee3678b5ea0..a05c2189754 100644
--- a/chromium/net/http/partial_data.cc
+++ b/chromium/net/http/partial_data.cc
@@ -382,40 +382,26 @@ void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
if (truncated_)
return;
+ if (byte_range_.IsValid() && success) {
+ headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
+ return;
+ }
+
headers->RemoveHeader(kLengthHeader);
headers->RemoveHeader(kRangeHeader);
- int64 range_len, start, end;
if (byte_range_.IsValid()) {
- if (success) {
- if (!sparse_entry_)
- headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
-
- DCHECK(byte_range_.HasFirstBytePosition());
- DCHECK(byte_range_.HasLastBytePosition());
- start = byte_range_.first_byte_position();
- end = byte_range_.last_byte_position();
- range_len = end - start + 1;
- } else {
- headers->ReplaceStatusLine(
- "HTTP/1.1 416 Requested Range Not Satisfiable");
- start = 0;
- end = 0;
- range_len = 0;
- }
-
- headers->AddHeader(
- base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
- kRangeHeader, start, end, resource_size_));
+ headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
+ headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
+ kRangeHeader, resource_size_));
+ headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
} else {
// TODO(rvargas): Is it safe to change the protocol version?
headers->ReplaceStatusLine("HTTP/1.1 200 OK");
DCHECK_NE(resource_size_, 0);
- range_len = resource_size_;
+ headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
+ resource_size_));
}
-
- headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
- range_len));
}
void PartialData::FixContentLength(HttpResponseHeaders* headers) {
diff --git a/chromium/net/http/proxy_connect_redirect_http_stream.cc b/chromium/net/http/proxy_connect_redirect_http_stream.cc
index 0857a7451ca..b859d37de63 100644
--- a/chromium/net/http/proxy_connect_redirect_http_stream.cc
+++ b/chromium/net/http/proxy_connect_redirect_http_stream.cc
@@ -43,12 +43,6 @@ int ProxyConnectRedirectHttpStream::ReadResponseHeaders(
return OK;
}
-const HttpResponseInfo*
-ProxyConnectRedirectHttpStream::GetResponseInfo() const {
- NOTREACHED();
- return NULL;
-}
-
int ProxyConnectRedirectHttpStream::ReadResponseBody(
IOBuffer* buf,
int buf_len,
diff --git a/chromium/net/http/proxy_connect_redirect_http_stream.h b/chromium/net/http/proxy_connect_redirect_http_stream.h
index 0547654ab92..f848c64fcd1 100644
--- a/chromium/net/http/proxy_connect_redirect_http_stream.h
+++ b/chromium/net/http/proxy_connect_redirect_http_stream.h
@@ -33,7 +33,6 @@ class ProxyConnectRedirectHttpStream : public HttpStream {
HttpResponseInfo* response,
const CompletionCallback& callback) OVERRIDE;
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
diff --git a/chromium/net/http/transport_security_persister.cc b/chromium/net/http/transport_security_persister.cc
index 771d07dc1bc..7310b590f7e 100644
--- a/chromium/net/http/transport_security_persister.cc
+++ b/chromium/net/http/transport_security_persister.cc
@@ -26,14 +26,15 @@ using net::TransportSecurityState;
namespace {
-ListValue* SPKIHashesToListValue(const HashValueVector& hashes) {
- ListValue* pins = new ListValue;
+base::ListValue* SPKIHashesToListValue(const HashValueVector& hashes) {
+ base::ListValue* pins = new base::ListValue;
for (size_t i = 0; i != hashes.size(); i++)
- pins->Append(new StringValue(hashes[i].ToString()));
+ pins->Append(new base::StringValue(hashes[i].ToString()));
return pins;
}
-void SPKIHashesFromListValue(const ListValue& pins, HashValueVector* hashes) {
+void SPKIHashesFromListValue(const base::ListValue& pins,
+ HashValueVector* hashes) {
size_t num_pins = pins.GetSize();
for (size_t i = 0; i < num_pins; ++i) {
std::string type_and_base64;
@@ -71,14 +72,14 @@ const char kPkpIncludeSubdomains[] = "pkp_include_subdomains";
const char kMode[] = "mode";
const char kExpiry[] = "expiry";
const char kDynamicSPKIHashesExpiry[] = "dynamic_spki_hashes_expiry";
-const char kStaticSPKIHashes[] = "static_spki_hashes";
-const char kPreloadedSPKIHashes[] = "preloaded_spki_hashes";
const char kDynamicSPKIHashes[] = "dynamic_spki_hashes";
const char kForceHTTPS[] = "force-https";
const char kStrict[] = "strict";
const char kDefault[] = "default";
const char kPinningOnly[] = "pinning-only";
const char kCreated[] = "created";
+const char kStsObserved[] = "sts_observed";
+const char kPkpObserved[] = "pkp_observed";
std::string LoadState(const base::FilePath& path) {
std::string result;
@@ -135,7 +136,7 @@ void TransportSecurityPersister::StateIsDirty(
bool TransportSecurityPersister::SerializeData(std::string* output) {
DCHECK(foreground_runner_->RunsTasksOnCurrentThread());
- DictionaryValue toplevel;
+ base::DictionaryValue toplevel;
base::Time now = base::Time::Now();
TransportSecurityState::Iterator state(*transport_security_state_);
for (; state.HasNext(); state.Advance()) {
@@ -143,17 +144,20 @@ bool TransportSecurityPersister::SerializeData(std::string* output) {
const TransportSecurityState::DomainState& domain_state =
state.domain_state();
- DictionaryValue* serialized = new DictionaryValue;
+ base::DictionaryValue* serialized = new base::DictionaryValue;
serialized->SetBoolean(kStsIncludeSubdomains,
- domain_state.sts_include_subdomains);
+ domain_state.sts.include_subdomains);
serialized->SetBoolean(kPkpIncludeSubdomains,
- domain_state.pkp_include_subdomains);
- serialized->SetDouble(kCreated, domain_state.created.ToDoubleT());
- serialized->SetDouble(kExpiry, domain_state.upgrade_expiry.ToDoubleT());
+ domain_state.pkp.include_subdomains);
+ serialized->SetDouble(kStsObserved,
+ domain_state.sts.last_observed.ToDoubleT());
+ serialized->SetDouble(kPkpObserved,
+ domain_state.pkp.last_observed.ToDoubleT());
+ serialized->SetDouble(kExpiry, domain_state.sts.expiry.ToDoubleT());
serialized->SetDouble(kDynamicSPKIHashesExpiry,
- domain_state.dynamic_spki_hashes_expiry.ToDoubleT());
+ domain_state.pkp.expiry.ToDoubleT());
- switch (domain_state.upgrade_mode) {
+ switch (domain_state.sts.upgrade_mode) {
case TransportSecurityState::DomainState::MODE_FORCE_HTTPS:
serialized->SetString(kMode, kForceHTTPS);
break;
@@ -166,12 +170,9 @@ bool TransportSecurityPersister::SerializeData(std::string* output) {
continue;
}
- serialized->Set(kStaticSPKIHashes,
- SPKIHashesToListValue(domain_state.static_spki_hashes));
-
- if (now < domain_state.dynamic_spki_hashes_expiry) {
+ if (now < domain_state.pkp.expiry) {
serialized->Set(kDynamicSPKIHashes,
- SPKIHashesToListValue(domain_state.dynamic_spki_hashes));
+ SPKIHashesToListValue(domain_state.pkp.spki_hashes));
}
toplevel.Set(HashedDomainToExternalString(hostname), serialized);
@@ -195,23 +196,23 @@ bool TransportSecurityPersister::LoadEntries(const std::string& serialized,
bool TransportSecurityPersister::Deserialize(const std::string& serialized,
bool* dirty,
TransportSecurityState* state) {
- scoped_ptr<Value> value(base::JSONReader::Read(serialized));
- DictionaryValue* dict_value = NULL;
+ scoped_ptr<base::Value> value(base::JSONReader::Read(serialized));
+ base::DictionaryValue* dict_value = NULL;
if (!value.get() || !value->GetAsDictionary(&dict_value))
return false;
const base::Time current_time(base::Time::Now());
bool dirtied = false;
- for (DictionaryValue::Iterator i(*dict_value); !i.IsAtEnd(); i.Advance()) {
- const DictionaryValue* parsed = NULL;
+ for (base::DictionaryValue::Iterator i(*dict_value);
+ !i.IsAtEnd(); i.Advance()) {
+ const base::DictionaryValue* parsed = NULL;
if (!i.value().GetAsDictionary(&parsed)) {
LOG(WARNING) << "Could not parse entry " << i.key() << "; skipping entry";
continue;
}
std::string mode_string;
- double created;
double expiry;
double dynamic_spki_hashes_expiry = 0.0;
TransportSecurityState::DomainState domain_state;
@@ -222,14 +223,14 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized,
bool include_subdomains = false;
bool parsed_include_subdomains = parsed->GetBoolean(kIncludeSubdomains,
&include_subdomains);
- domain_state.sts_include_subdomains = include_subdomains;
- domain_state.pkp_include_subdomains = include_subdomains;
+ domain_state.sts.include_subdomains = include_subdomains;
+ domain_state.pkp.include_subdomains = include_subdomains;
if (parsed->GetBoolean(kStsIncludeSubdomains, &include_subdomains)) {
- domain_state.sts_include_subdomains = include_subdomains;
+ domain_state.sts.include_subdomains = include_subdomains;
parsed_include_subdomains = true;
}
if (parsed->GetBoolean(kPkpIncludeSubdomains, &include_subdomains)) {
- domain_state.pkp_include_subdomains = include_subdomains;
+ domain_state.pkp.include_subdomains = include_subdomains;
parsed_include_subdomains = true;
}
@@ -245,21 +246,16 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized,
parsed->GetDouble(kDynamicSPKIHashesExpiry,
&dynamic_spki_hashes_expiry);
- const ListValue* pins_list = NULL;
- // preloaded_spki_hashes is a legacy synonym for static_spki_hashes.
- if (parsed->GetList(kStaticSPKIHashes, &pins_list))
- SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes);
- else if (parsed->GetList(kPreloadedSPKIHashes, &pins_list))
- SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes);
-
- if (parsed->GetList(kDynamicSPKIHashes, &pins_list))
- SPKIHashesFromListValue(*pins_list, &domain_state.dynamic_spki_hashes);
+ const base::ListValue* pins_list = NULL;
+ if (parsed->GetList(kDynamicSPKIHashes, &pins_list)) {
+ SPKIHashesFromListValue(*pins_list, &domain_state.pkp.spki_hashes);
+ }
if (mode_string == kForceHTTPS || mode_string == kStrict) {
- domain_state.upgrade_mode =
+ domain_state.sts.upgrade_mode =
TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
} else if (mode_string == kDefault || mode_string == kPinningOnly) {
- domain_state.upgrade_mode =
+ domain_state.sts.upgrade_mode =
TransportSecurityState::DomainState::MODE_DEFAULT;
} else {
LOG(WARNING) << "Unknown TransportSecurityState mode string "
@@ -268,20 +264,34 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized,
continue;
}
- domain_state.upgrade_expiry = base::Time::FromDoubleT(expiry);
- domain_state.dynamic_spki_hashes_expiry =
+ domain_state.sts.expiry = base::Time::FromDoubleT(expiry);
+ domain_state.pkp.expiry =
base::Time::FromDoubleT(dynamic_spki_hashes_expiry);
- if (parsed->GetDouble(kCreated, &created)) {
- domain_state.created = base::Time::FromDoubleT(created);
+
+ double sts_observed;
+ double pkp_observed;
+ if (parsed->GetDouble(kStsObserved, &sts_observed)) {
+ domain_state.sts.last_observed = base::Time::FromDoubleT(sts_observed);
+ } else if (parsed->GetDouble(kCreated, &sts_observed)) {
+ // kCreated is a legacy synonym for both kStsObserved and kPkpObserved.
+ domain_state.sts.last_observed = base::Time::FromDoubleT(sts_observed);
} else {
- // We're migrating an old entry with no creation date. Make sure we
+ // We're migrating an old entry with no observation date. Make sure we
// write the new date back in a reasonable time frame.
dirtied = true;
- domain_state.created = base::Time::Now();
+ domain_state.sts.last_observed = base::Time::Now();
+ }
+ if (parsed->GetDouble(kPkpObserved, &pkp_observed)) {
+ domain_state.pkp.last_observed = base::Time::FromDoubleT(pkp_observed);
+ } else if (parsed->GetDouble(kCreated, &pkp_observed)) {
+ domain_state.pkp.last_observed = base::Time::FromDoubleT(pkp_observed);
+ } else {
+ dirtied = true;
+ domain_state.pkp.last_observed = base::Time::Now();
}
- if (domain_state.upgrade_expiry <= current_time &&
- domain_state.dynamic_spki_hashes_expiry <= current_time) {
+ if (domain_state.sts.expiry <= current_time &&
+ domain_state.pkp.expiry <= current_time) {
// Make sure we dirty the state if we drop an entry.
dirtied = true;
continue;
diff --git a/chromium/net/http/transport_security_persister_unittest.cc b/chromium/net/http/transport_security_persister_unittest.cc
index 8c41f9e81da..a30edae94f7 100644
--- a/chromium/net/http/transport_security_persister_unittest.cc
+++ b/chromium/net/http/transport_security_persister_unittest.cc
@@ -57,7 +57,8 @@ TEST_F(TransportSecurityPersisterTest, SerializeData2) {
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
static const char kYahooDomain[] = "yahoo.com";
- EXPECT_FALSE(state_.GetDomainState(kYahooDomain, true, &domain_state));
+ EXPECT_FALSE(state_.GetStaticDomainState(kYahooDomain, true, &domain_state));
+ EXPECT_FALSE(state_.GetDynamicDomainState(kYahooDomain, &domain_state));
bool include_subdomains = true;
state_.AddHSTS(kYahooDomain, expiry, include_subdomains);
@@ -67,20 +68,20 @@ TEST_F(TransportSecurityPersisterTest, SerializeData2) {
EXPECT_TRUE(persister_->SerializeData(&output));
EXPECT_TRUE(persister_->LoadEntries(output, &dirty));
- EXPECT_TRUE(state_.GetDomainState(kYahooDomain, true, &domain_state));
- EXPECT_EQ(domain_state.upgrade_mode,
+ EXPECT_TRUE(state_.GetDynamicDomainState(kYahooDomain, &domain_state));
+ EXPECT_EQ(domain_state.sts.upgrade_mode,
TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
- EXPECT_TRUE(state_.GetDomainState("foo.yahoo.com", true, &domain_state));
- EXPECT_EQ(domain_state.upgrade_mode,
+ EXPECT_TRUE(state_.GetDynamicDomainState("foo.yahoo.com", &domain_state));
+ EXPECT_EQ(domain_state.sts.upgrade_mode,
TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
- EXPECT_TRUE(state_.GetDomainState("foo.bar.yahoo.com", true, &domain_state));
- EXPECT_EQ(domain_state.upgrade_mode,
+ EXPECT_TRUE(state_.GetDynamicDomainState("foo.bar.yahoo.com", &domain_state));
+ EXPECT_EQ(domain_state.sts.upgrade_mode,
TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
- EXPECT_TRUE(state_.GetDomainState("foo.bar.baz.yahoo.com", true,
- &domain_state));
- EXPECT_EQ(domain_state.upgrade_mode,
+ EXPECT_TRUE(
+ state_.GetDynamicDomainState("foo.bar.baz.yahoo.com", &domain_state));
+ EXPECT_EQ(domain_state.sts.upgrade_mode,
TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
- EXPECT_FALSE(state_.GetDomainState("com", true, &domain_state));
+ EXPECT_FALSE(state_.GetStaticDomainState("com", true, &domain_state));
}
TEST_F(TransportSecurityPersisterTest, SerializeData3) {
@@ -127,8 +128,7 @@ TEST_F(TransportSecurityPersisterTest, SerializeData3) {
// than block.) Use a different basename just for cleanliness.
base::FilePath path =
temp_dir_.path().AppendASCII("TransportSecurityPersisterTest");
- EXPECT_TRUE(file_util::WriteFile(path, serialized.c_str(),
- serialized.size()));
+ EXPECT_TRUE(base::WriteFile(path, serialized.c_str(), serialized.size()));
// Read the data back.
std::string persisted;
@@ -167,35 +167,40 @@ TEST_F(TransportSecurityPersisterTest, SerializeDataOld) {
TEST_F(TransportSecurityPersisterTest, PublicKeyHashes) {
TransportSecurityState::DomainState domain_state;
static const char kTestDomain[] = "example.com";
- EXPECT_FALSE(state_.GetDomainState(kTestDomain, false, &domain_state));
+ EXPECT_FALSE(state_.GetDynamicDomainState(kTestDomain, &domain_state));
net::HashValueVector hashes;
- EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes));
+ std::string failure_log;
+ EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log));
net::HashValue sha1(net::HASH_VALUE_SHA1);
memset(sha1.data(), '1', sha1.size());
- domain_state.dynamic_spki_hashes.push_back(sha1);
+ domain_state.pkp.spki_hashes.push_back(sha1);
- EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes));
+ EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log));
hashes.push_back(sha1);
- EXPECT_TRUE(domain_state.CheckPublicKeyPins(hashes));
+ EXPECT_TRUE(domain_state.CheckPublicKeyPins(hashes, &failure_log));
hashes[0].data()[0] = '2';
- EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes));
+ EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log));
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
bool include_subdomains = false;
state_.AddHSTS(kTestDomain, expiry, include_subdomains);
- state_.AddHPKP(kTestDomain, expiry, include_subdomains,
- domain_state.dynamic_spki_hashes);
- std::string ser;
- EXPECT_TRUE(persister_->SerializeData(&ser));
+ state_.AddHPKP(
+ kTestDomain, expiry, include_subdomains, domain_state.pkp.spki_hashes);
+ std::string serialized;
+ EXPECT_TRUE(persister_->SerializeData(&serialized));
bool dirty;
- EXPECT_TRUE(persister_->LoadEntries(ser, &dirty));
- EXPECT_TRUE(state_.GetDomainState(kTestDomain, false, &domain_state));
- EXPECT_EQ(1u, domain_state.dynamic_spki_hashes.size());
- EXPECT_EQ(sha1.tag, domain_state.dynamic_spki_hashes[0].tag);
- EXPECT_EQ(0, memcmp(domain_state.dynamic_spki_hashes[0].data(), sha1.data(),
- sha1.size()));
+ EXPECT_TRUE(persister_->LoadEntries(serialized, &dirty));
+
+ TransportSecurityState::DomainState new_domain_state;
+ EXPECT_TRUE(state_.GetDynamicDomainState(kTestDomain, &new_domain_state));
+ EXPECT_EQ(1u, new_domain_state.pkp.spki_hashes.size());
+ EXPECT_EQ(sha1.tag, new_domain_state.pkp.spki_hashes[0].tag);
+ EXPECT_EQ(0,
+ memcmp(new_domain_state.pkp.spki_hashes[0].data(),
+ sha1.data(),
+ sha1.size()));
}
diff --git a/chromium/net/http/transport_security_state.cc b/chromium/net/http/transport_security_state.cc
index f9ba807ff79..f50b26483d1 100644
--- a/chromium/net/http/transport_security_state.cc
+++ b/chromium/net/http/transport_security_state.cc
@@ -95,6 +95,61 @@ TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
TransportSecurityState::Iterator::~Iterator() {}
+bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host,
+ bool sni_enabled) {
+ DomainState state;
+ if (GetStaticDomainState(host, sni_enabled, &state))
+ return true;
+ return GetDynamicDomainState(host, &state);
+}
+
+bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host,
+ bool sni_enabled) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.ShouldUpgradeToSSL();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, sni_enabled, &static_state) &&
+ static_state.ShouldUpgradeToSSL()) {
+ return true;
+ }
+
+ return false;
+}
+
+bool TransportSecurityState::CheckPublicKeyPins(const std::string& host,
+ bool sni_enabled,
+ const HashValueVector& hashes,
+ std::string* failure_log) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.CheckPublicKeyPins(hashes, failure_log);
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, sni_enabled, &static_state) &&
+ static_state.CheckPublicKeyPins(hashes, failure_log)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool TransportSecurityState::HasPublicKeyPins(const std::string& host,
+ bool sni_enabled) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.HasPublicKeyPins();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, sni_enabled, &static_state)) {
+ if (static_state.HasPublicKeyPins())
+ return true;
+ }
+
+ return false;
+}
+
void TransportSecurityState::SetDelegate(
TransportSecurityState::Delegate* delegate) {
DCHECK(CalledOnValidThread());
@@ -135,61 +190,6 @@ bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
return false;
}
-bool TransportSecurityState::GetDomainState(const std::string& host,
- bool sni_enabled,
- DomainState* result) {
- DCHECK(CalledOnValidThread());
-
- DomainState state;
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return false;
-
- bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled,
- &state);
- std::string canonicalized_preload = CanonicalizeHost(state.domain);
- GetDynamicDomainState(host, &state);
-
- base::Time current_time(base::Time::Now());
-
- for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
- std::string host_sub_chunk(&canonicalized_host[i],
- canonicalized_host.size() - i);
- // Exact match of a preload always wins.
- if (has_preload && host_sub_chunk == canonicalized_preload) {
- *result = state;
- return true;
- }
-
- DomainStateMap::iterator j =
- enabled_hosts_.find(HashHost(host_sub_chunk));
- if (j == enabled_hosts_.end())
- continue;
-
- if (current_time > j->second.upgrade_expiry &&
- current_time > j->second.dynamic_spki_hashes_expiry) {
- enabled_hosts_.erase(j);
- DirtyNotify();
- continue;
- }
-
- state = j->second;
- state.domain = DNSDomainToString(host_sub_chunk);
-
- // Succeed if we matched the domain exactly or if subdomain matches are
- // allowed.
- if (i == 0 || j->second.sts_include_subdomains ||
- j->second.pkp_include_subdomains) {
- *result = state;
- return true;
- }
-
- return false;
- }
-
- return false;
-}
-
void TransportSecurityState::ClearDynamicData() {
DCHECK(CalledOnValidThread());
enabled_hosts_.clear();
@@ -199,15 +199,24 @@ void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
DCHECK(CalledOnValidThread());
bool dirtied = false;
-
DomainStateMap::iterator i = enabled_hosts_.begin();
while (i != enabled_hosts_.end()) {
- if (i->second.created >= time) {
+ if (i->second.sts.last_observed >= time &&
+ i->second.pkp.last_observed >= time) {
dirtied = true;
enabled_hosts_.erase(i++);
- } else {
- i++;
+ continue;
+ }
+
+ if (i->second.sts.last_observed >= time) {
+ dirtied = true;
+ i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
+ } else if (i->second.pkp.last_observed >= time) {
+ dirtied = true;
+ i->second.pkp.spki_hashes.clear();
+ i->second.pkp.expiry = base::Time();
}
+ ++i;
}
if (dirtied)
@@ -515,6 +524,7 @@ enum SecondLevelDomainName {
DOMAIN_LAVABIT_COM,
DOMAIN_GOOGLETAGMANAGER_COM,
+ DOMAIN_GOOGLETAGSERVICES_COM,
// Boundary value for UMA_HISTOGRAM_ENUMERATION:
DOMAIN_NUM_EVENTS
@@ -547,22 +557,27 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
if (!entries[j].include_subdomains && i != 0) {
*ret = false;
} else {
- out->sts_include_subdomains = entries[j].include_subdomains;
- out->pkp_include_subdomains = entries[j].include_subdomains;
+ out->sts.include_subdomains = entries[j].include_subdomains;
+ out->sts.last_observed = base::GetBuildTime();
+ out->pkp.include_subdomains = entries[j].include_subdomains;
+ out->pkp.last_observed = base::GetBuildTime();
*ret = true;
+ out->sts.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
if (!entries[j].https_required)
- out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT;
+ out->sts.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_DEFAULT;
if (entries[j].pins.required_hashes) {
const char* const* sha1_hash = entries[j].pins.required_hashes;
while (*sha1_hash) {
- AddHash(*sha1_hash, &out->static_spki_hashes);
+ AddHash(*sha1_hash, &out->pkp.spki_hashes);
sha1_hash++;
}
}
if (entries[j].pins.excluded_hashes) {
const char* const* sha1_hash = entries[j].pins.excluded_hashes;
while (*sha1_hash) {
- AddHash(*sha1_hash, &out->bad_static_spki_hashes);
+ AddHash(*sha1_hash, &out->pkp.bad_spki_hashes);
sha1_hash++;
}
}
@@ -610,14 +625,14 @@ bool TransportSecurityState::AddHSTSHeader(const std::string& host,
base::TimeDelta max_age;
TransportSecurityState::DomainState domain_state;
GetDynamicDomainState(host, &domain_state);
- if (ParseHSTSHeader(value, &max_age, &domain_state.sts_include_subdomains)) {
- // Handle max-age == 0
+ if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) {
+ // Handle max-age == 0.
if (max_age.InSeconds() == 0)
- domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
+ domain_state.sts.upgrade_mode = DomainState::MODE_DEFAULT;
else
- domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
- domain_state.created = now;
- domain_state.upgrade_expiry = now + max_age;
+ domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
+ domain_state.sts.last_observed = now;
+ domain_state.sts.expiry = now + max_age;
EnableHost(host, domain_state);
return true;
}
@@ -633,12 +648,16 @@ bool TransportSecurityState::AddHPKPHeader(const std::string& host,
base::TimeDelta max_age;
TransportSecurityState::DomainState domain_state;
GetDynamicDomainState(host, &domain_state);
- if (ParseHPKPHeader(value, ssl_info.public_key_hashes,
- &max_age, &domain_state.pkp_include_subdomains,
- &domain_state.dynamic_spki_hashes)) {
- // TODO(palmer): http://crbug.com/243865 handle max-age == 0.
- domain_state.created = now;
- domain_state.dynamic_spki_hashes_expiry = now + max_age;
+ if (ParseHPKPHeader(value,
+ ssl_info.public_key_hashes,
+ &max_age,
+ &domain_state.pkp.include_subdomains,
+ &domain_state.pkp.spki_hashes)) {
+ // Handle max-age == 0.
+ if (max_age.InSeconds() == 0)
+ domain_state.pkp.spki_hashes.clear();
+ domain_state.pkp.last_observed = now;
+ domain_state.pkp.expiry = now + max_age;
EnableHost(host, domain_state);
return true;
}
@@ -659,10 +678,10 @@ bool TransportSecurityState::AddHSTS(const std::string& host,
if (i != enabled_hosts_.end())
domain_state = i->second;
- domain_state.created = base::Time::Now();
- domain_state.sts_include_subdomains = include_subdomains;
- domain_state.upgrade_expiry = expiry;
- domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
+ domain_state.sts.last_observed = base::Time::Now();
+ domain_state.sts.include_subdomains = include_subdomains;
+ domain_state.sts.expiry = expiry;
+ domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
EnableHost(host, domain_state);
return true;
}
@@ -682,10 +701,10 @@ bool TransportSecurityState::AddHPKP(const std::string& host,
if (i != enabled_hosts_.end())
domain_state = i->second;
- domain_state.created = base::Time::Now();
- domain_state.pkp_include_subdomains = include_subdomains;
- domain_state.dynamic_spki_hashes_expiry = expiry;
- domain_state.dynamic_spki_hashes = hashes;
+ domain_state.pkp.last_observed = base::Time::Now();
+ domain_state.pkp.include_subdomains = include_subdomains;
+ domain_state.pkp.expiry = expiry;
+ domain_state.pkp.spki_hashes = hashes;
EnableHost(host, domain_state);
return true;
}
@@ -742,15 +761,16 @@ bool TransportSecurityState::IsBuildTimely() {
return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
}
-bool TransportSecurityState::GetStaticDomainState(
- const std::string& canonicalized_host,
- bool sni_enabled,
- DomainState* out) {
+bool TransportSecurityState::GetStaticDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* out) const {
DCHECK(CalledOnValidThread());
- out->upgrade_mode = DomainState::MODE_FORCE_HTTPS;
- out->sts_include_subdomains = false;
- out->pkp_include_subdomains = false;
+ const std::string canonicalized_host = CanonicalizeHost(host);
+
+ out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
+ out->sts.include_subdomains = false;
+ out->pkp.include_subdomains = false;
const bool is_build_timely = IsBuildTimely();
@@ -794,8 +814,8 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host,
if (j == enabled_hosts_.end())
continue;
- if (current_time > j->second.upgrade_expiry &&
- current_time > j->second.dynamic_spki_hashes_expiry) {
+ if (current_time > j->second.sts.expiry &&
+ current_time > j->second.pkp.expiry) {
enabled_hosts_.erase(j);
DirtyNotify();
continue;
@@ -806,8 +826,8 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host,
// Succeed if we matched the domain exactly or if subdomain matches are
// allowed.
- if (i == 0 || j->second.sts_include_subdomains ||
- j->second.pkp_include_subdomains) {
+ if (i == 0 || j->second.sts.include_subdomains ||
+ j->second.pkp.include_subdomains) {
*result = state;
return true;
}
@@ -818,60 +838,57 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host,
return false;
}
-
void TransportSecurityState::AddOrUpdateEnabledHosts(
const std::string& hashed_host, const DomainState& state) {
DCHECK(CalledOnValidThread());
enabled_hosts_[hashed_host] = state;
}
-TransportSecurityState::DomainState::DomainState()
- : upgrade_mode(MODE_DEFAULT),
- created(base::Time::Now()),
- sts_include_subdomains(false),
- pkp_include_subdomains(false) {
+TransportSecurityState::DomainState::DomainState() {
+ sts.upgrade_mode = MODE_DEFAULT;
+ sts.include_subdomains = false;
+ pkp.include_subdomains = false;
}
TransportSecurityState::DomainState::~DomainState() {
}
bool TransportSecurityState::DomainState::CheckPublicKeyPins(
- const HashValueVector& hashes) const {
+ const HashValueVector& hashes, std::string* failure_log) const {
// Validate that hashes is not empty. By the time this code is called (in
// production), that should never happen, but it's good to be defensive.
// And, hashes *can* be empty in some test scenarios.
if (hashes.empty()) {
- LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned "
- "domain " << domain;
+ failure_log->append(
+ "Rejecting empty public key chain for public-key-pinned domains: " +
+ domain);
return false;
}
- if (HashesIntersect(bad_static_spki_hashes, hashes)) {
- LOG(ERROR) << "Rejecting public key chain for domain " << domain
- << ". Validated chain: " << HashesToBase64String(hashes)
- << ", matches one or more bad hashes: "
- << HashesToBase64String(bad_static_spki_hashes);
+ if (HashesIntersect(pkp.bad_spki_hashes, hashes)) {
+ failure_log->append("Rejecting public key chain for domain " + domain +
+ ". Validated chain: " + HashesToBase64String(hashes) +
+ ", matches one or more bad hashes: " +
+ HashesToBase64String(pkp.bad_spki_hashes));
return false;
}
// If there are no pins, then any valid chain is acceptable.
- if (dynamic_spki_hashes.empty() && static_spki_hashes.empty())
+ if (pkp.spki_hashes.empty())
return true;
- if (HashesIntersect(dynamic_spki_hashes, hashes) ||
- HashesIntersect(static_spki_hashes, hashes)) {
+ if (HashesIntersect(pkp.spki_hashes, hashes)) {
return true;
}
- LOG(ERROR) << "Rejecting public key chain for domain " << domain
- << ". Validated chain: " << HashesToBase64String(hashes)
- << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
- << " or: " << HashesToBase64String(static_spki_hashes);
+ failure_log->append("Rejecting public key chain for domain " + domain +
+ ". Validated chain: " + HashesToBase64String(hashes) +
+ ", expected: " + HashesToBase64String(pkp.spki_hashes));
return false;
}
bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const {
- return upgrade_mode == MODE_FORCE_HTTPS;
+ return sts.upgrade_mode == MODE_FORCE_HTTPS;
}
bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
@@ -879,9 +896,13 @@ bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
}
bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
- return static_spki_hashes.size() > 0 ||
- bad_static_spki_hashes.size() > 0 ||
- dynamic_spki_hashes.size() > 0;
+ return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0;
+}
+
+TransportSecurityState::DomainState::PKPState::PKPState() {
+}
+
+TransportSecurityState::DomainState::PKPState::~PKPState() {
}
} // namespace
diff --git a/chromium/net/http/transport_security_state.h b/chromium/net/http/transport_security_state.h
index 97b4d7c1fc8..36459379145 100644
--- a/chromium/net/http/transport_security_state.h
+++ b/chromium/net/http/transport_security_state.h
@@ -61,6 +61,44 @@ class NET_EXPORT TransportSecurityState
DomainState();
~DomainState();
+ struct STSState {
+ // The absolute time (UTC) when the |upgrade_mode| (and other state) was
+ // observed.
+ base::Time last_observed;
+
+ // The absolute time (UTC) when the |upgrade_mode|, if set to
+ // MODE_FORCE_HTTPS, downgrades to MODE_DEFAULT.
+ base::Time expiry;
+
+ UpgradeMode upgrade_mode;
+
+ // Are subdomains subject to this policy state?
+ bool include_subdomains;
+ };
+
+ struct PKPState {
+ PKPState();
+ ~PKPState();
+
+ // The absolute time (UTC) when the |spki_hashes| (and other state) were
+ // observed.
+ base::Time last_observed;
+
+ // The absolute time (UTC) when the |spki_hashes| expire.
+ base::Time expiry;
+
+ // Optional; hashes of pinned SubjectPublicKeyInfos.
+ HashValueVector spki_hashes;
+
+ // Optional; hashes of static known-bad SubjectPublicKeyInfos which MUST
+ // NOT intersect with the set of SPKIs in the TLS server's certificate
+ // chain.
+ HashValueVector bad_spki_hashes;
+
+ // Are subdomains subject to this policy state?
+ bool include_subdomains;
+ };
+
// Takes a set of SubjectPublicKeyInfo |hashes| and returns true if:
// 1) |bad_static_spki_hashes| does not intersect |hashes|; AND
// 2) Both |static_spki_hashes| and |dynamic_spki_hashes| are empty
@@ -77,67 +115,29 @@ class NET_EXPORT TransportSecurityState
//
// |bad_static_spki_hashes| contains public keys that we don't want to
// trust.
- bool CheckPublicKeyPins(const HashValueVector& hashes) const;
+ bool CheckPublicKeyPins(const HashValueVector& hashes,
+ std::string* failure_log) const;
// Returns true if any of the HashValueVectors |static_spki_hashes|,
// |bad_static_spki_hashes|, or |dynamic_spki_hashes| contains any
// items.
bool HasPublicKeyPins() const;
- // ShouldUpgradeToSSL returns true iff, given the |mode| of this
- // DomainState, HTTP requests should be internally redirected to HTTPS
- // (also if the "ws" WebSocket request should be upgraded to "wss")
+ // ShouldUpgradeToSSL returns true iff HTTP requests should be internally
+ // redirected to HTTPS (also if WS should be upgraded to WSS).
bool ShouldUpgradeToSSL() const;
// ShouldSSLErrorsBeFatal returns true iff HTTPS errors should cause
- // hard-fail behavior (e.g. if HSTS is set for the domain)
+ // hard-fail behavior (e.g. if HSTS is set for the domain).
bool ShouldSSLErrorsBeFatal() const;
- UpgradeMode upgrade_mode;
-
- // The absolute time (UTC) when this DomainState was first created.
- //
- // Static entries do not have a created time.
- base::Time created;
-
- // The absolute time (UTC) when the |upgrade_mode|, if set to
- // UPGRADE_ALWAYS, downgrades to UPGRADE_NEVER.
- base::Time upgrade_expiry;
-
- // Are subdomains subject to this DomainState, for the purposes of
- // upgrading to HTTPS?
- bool sts_include_subdomains;
-
- // Are subdomains subject to this DomainState, for the purposes of
- // Pin Validation?
- bool pkp_include_subdomains;
-
- // Optional; hashes of static pinned SubjectPublicKeyInfos. Unless both
- // are empty, at least one of |static_spki_hashes| and
- // |dynamic_spki_hashes| MUST intersect with the set of SPKIs in the TLS
- // server's certificate chain.
- //
- // |dynamic_spki_hashes| take precedence over |static_spki_hashes|.
- // That is, |IsChainOfPublicKeysPermitted| first checks dynamic pins and
- // then checks static pins.
- HashValueVector static_spki_hashes;
-
- // Optional; hashes of dynamically pinned SubjectPublicKeyInfos.
- HashValueVector dynamic_spki_hashes;
-
- // The absolute time (UTC) when the |dynamic_spki_hashes| expire.
- base::Time dynamic_spki_hashes_expiry;
-
- // Optional; hashes of static known-bad SubjectPublicKeyInfos which
- // MUST NOT intersect with the set of SPKIs in the TLS server's
- // certificate chain.
- HashValueVector bad_static_spki_hashes;
+ STSState sts;
+ PKPState pkp;
// The following members are not valid when stored in |enabled_hosts_|:
// The domain which matched during a search for this DomainState entry.
- // Updated by |GetDomainState|, |GetDynamicDomainState|, and
- // |GetStaticDomainState|.
+ // Updated by |GetDynamicDomainState| and |GetStaticDomainState|.
std::string domain;
};
@@ -156,6 +156,17 @@ class NET_EXPORT TransportSecurityState
std::map<std::string, DomainState>::const_iterator end_;
};
+ // These functions search for static and dynamic DomainStates, and invoke the
+ // functions of the same name on them. These functions are the primary public
+ // interface; direct access to DomainStates is best left to tests.
+ bool ShouldSSLErrorsBeFatal(const std::string& host, bool sni_enabled);
+ bool ShouldUpgradeToSSL(const std::string& host, bool sni_enabled);
+ bool CheckPublicKeyPins(const std::string& host,
+ bool sni_enabled,
+ const HashValueVector& hashes,
+ std::string* failure_log);
+ bool HasPublicKeyPins(const std::string& host, bool sni_enabled);
+
// Assign a |Delegate| for persisting the transport security state. If
// |NULL|, state will not be persisted. The caller retains
// ownership of |delegate|.
@@ -195,20 +206,31 @@ class NET_EXPORT TransportSecurityState
// the Delegate (if any).
bool DeleteDynamicDataForHost(const std::string& host);
- // Returns true and updates |*result| iff there is a DomainState for
- // |host|.
+ // Returns true and updates |*result| iff there is a static (built-in)
+ // DomainState for |host|.
//
- // If |sni_enabled| is true, searches the static pins defined for
- // SNI-using hosts as well as the rest of the pins.
+ // If |sni_enabled| is true, searches the static pins defined for SNI-using
+ // hosts as well as the rest of the pins.
//
- // If |host| matches both an exact entry and is a subdomain of another
- // entry, the exact match determines the return value.
+ // If |host| matches both an exact entry and is a subdomain of another entry,
+ // the exact match determines the return value.
//
// Note that this method is not const because it opportunistically removes
// entries that have expired.
- bool GetDomainState(const std::string& host,
- bool sni_enabled,
- DomainState* result);
+ bool GetStaticDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* result) const;
+
+ // Returns true and updates |*result| iff there is a dynamic DomainState
+ // (learned from HSTS or HPKP headers, or set by the user, or other means) for
+ // |host|.
+ //
+ // If |host| matches both an exact entry and is a subdomain of another entry,
+ // the exact match determines the return value.
+ //
+ // Note that this method is not const because it opportunistically removes
+ // entries that have expired.
+ bool GetDynamicDomainState(const std::string& host, DomainState* result);
// Processes an HSTS header value from the host, adding entries to
// dynamic state if necessary.
@@ -282,39 +304,6 @@ class NET_EXPORT TransportSecurityState
// the result.
static std::string CanonicalizeHost(const std::string& hostname);
- // Returns true and updates |*result| iff there is a static DomainState for
- // |host|.
- //
- // |GetStaticDomainState| is identical to |GetDomainState| except that it
- // searches only the statically-defined transport security state, ignoring
- // all dynamically-added DomainStates.
- //
- // If |sni_enabled| is true, searches the static pins defined for
- // SNI-using hosts as well as the rest of the pins.
- //
- // If |host| matches both an exact entry and is a subdomain of another
- // entry, the exact match determines the return value.
- //
- // Note that this method is not const because it opportunistically removes
- // entries that have expired.
- bool GetStaticDomainState(const std::string& host,
- bool sni_enabled,
- DomainState* result);
-
- // Returns true and updates |*result| iff there is a dynamic DomainState for
- // |host|.
- //
- // |GetDynamicDomainState| is identical to |GetDomainState| except that it
- // searches only the dynamically-added transport security state, ignoring
- // all statically-defined DomainStates.
- //
- // If |host| matches both an exact entry and is a subdomain of another
- // entry, the exact match determines the return value.
- //
- // Note that this method is not const because it opportunistically removes
- // entries that have expired.
- bool GetDynamicDomainState(const std::string& host, DomainState* result);
-
// The set of hosts that have enabled TransportSecurity.
DomainStateMap enabled_hosts_;
diff --git a/chromium/net/http/transport_security_state_static.h b/chromium/net/http/transport_security_state_static.h
index 6a28ca4562b..3723deafb3d 100644
--- a/chromium/net/http/transport_security_state_static.h
+++ b/chromium/net/http/transport_security_state_static.h
@@ -389,7 +389,7 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{17, true, "\004docs\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{18, true, "\005sites\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{25, true, "\014spreadsheets\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
- {22, false, "\011appengine\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
+ {22, true, "\011appengine\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{22, true, "\011encrypted\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{21, true, "\010accounts\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{21, true, "\010profiles\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
@@ -405,6 +405,7 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{17, true, "\004goto\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{18, true, "\005cloud\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{18, true, "\005glass\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
+ {18, true, "\005admin\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{17, false, "\004play\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{20, true, "\006market\007android\003com", true, kGooglePins, DOMAIN_ANDROID_COM },
{26, true, "\003ssl\020google-analytics\003com", true, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM },
@@ -420,6 +421,21 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{16, true, "\012googlecode\003com", false, kGooglePins, DOMAIN_GOOGLECODE_COM },
{15, true, "\002dl\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
{26, true, "\011translate\012googleapis\003com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM },
+ {24, true, "\012webfilings\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {35, true, "\025webfilings-mirror-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {27, true, "\015webfilings-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {34, true, "\024webfilings-eu-mirror\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {24, true, "\012wf-demo-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {25, true, "\013wf-demo-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {24, true, "\012wf-pentest\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {26, true, "\014wf-trial-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {25, true, "\013xbrlsuccess\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {25, true, "\013w-spotlight\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {29, true, "\017wf-training-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {30, true, "\020wf-bigsky-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {27, true, "\015wf-staging-hr\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {32, true, "\022wf-training-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
+ {28, true, "\016wf-dogfood-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM },
{23, true, "\005chart\004apis\006google\003com", false, kGooglePins, DOMAIN_GOOGLE_COM },
{11, true, "\005ytimg\003com", false, kGooglePins, DOMAIN_YTIMG_COM },
{23, true, "\021googleusercontent\003com", false, kGooglePins, DOMAIN_GOOGLEUSERCONTENT_COM },
@@ -437,6 +453,7 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{8, true, "\003goo\002gl", false, kGooglePins, DOMAIN_GOO_GL },
{6, true, "\001g\002co", false, kGooglePins, DOMAIN_G_CO },
{22, true, "\020googletagmanager\003com", false, kGooglePins, DOMAIN_GOOGLETAGMANAGER_COM },
+ {23, true, "\021googletagservices\003com", false, kGooglePins, DOMAIN_GOOGLETAGSERVICES_COM },
{11, true, "\006google\002ac", false, kGooglePins, DOMAIN_GOOGLE_AC },
{11, true, "\006google\002ad", false, kGooglePins, DOMAIN_GOOGLE_AD },
{11, true, "\006google\002ae", false, kGooglePins, DOMAIN_GOOGLE_AE },
@@ -647,7 +664,6 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{11, true, "\006google\002tm", false, kGooglePins, DOMAIN_GOOGLE_TM },
{11, true, "\006google\002tn", false, kGooglePins, DOMAIN_GOOGLE_TN },
{11, true, "\006google\002to", false, kGooglePins, DOMAIN_GOOGLE_TO },
- {11, true, "\006google\002tp", false, kGooglePins, DOMAIN_GOOGLE_TP },
{11, true, "\006google\002tt", false, kGooglePins, DOMAIN_GOOGLE_TT },
{11, true, "\006google\002us", false, kGooglePins, DOMAIN_GOOGLE_US },
{11, true, "\006google\002uz", false, kGooglePins, DOMAIN_GOOGLE_UZ },
@@ -760,7 +776,7 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{17, false, "\003www\010intercom\002io", true, kNoPins, DOMAIN_NOT_PINNED },
{17, true, "\010fatzebra\003com\002au", true, kNoPins, DOMAIN_NOT_PINNED },
{18, true, "\007csawctf\004poly\003edu", true, kNoPins, DOMAIN_NOT_PINNED },
- {18, false, "\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, true, "\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED },
{22, false, "\003www\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED },
{16, true, "\003iop\006intuit\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{14, false, "\010surfeasy\003com", true, kNoPins, DOMAIN_NOT_PINNED },
@@ -776,6 +792,12 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{17, true, "\003faq\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{22, true, "\010platform\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{19, true, "\005email\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\003app\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\003api\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {23, true, "\011keymaster\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {23, true, "\011discovery\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, true, "\014mobilethreat\003net", true, kNoPins, DOMAIN_NOT_PINNED },
+ {25, true, "\023mobilethreatnetwork\003net", true, kNoPins, DOMAIN_NOT_PINNED },
{15, true, "\011itriskltd\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{15, true, "\012stocktrade\002de", true, kNoPins, DOMAIN_NOT_PINNED },
{22, true, "\011openshift\006redhat\003com", true, kNoPins, DOMAIN_NOT_PINNED },
@@ -884,6 +906,91 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{14, true, "\003api\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{9, true, "\003eff\003org", true, kNoPins, DOMAIN_NOT_PINNED },
{9, true, "\004mail\002de", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, false, "\010passport\006yandex\002ru", true, kNoPins, DOMAIN_NOT_PINNED },
+ {21, false, "\010passport\006yandex\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, false, "\010passport\006yandex\002ua", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, false, "\010passport\006yandex\002by", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, false, "\010passport\006yandex\002kz", true, kNoPins, DOMAIN_NOT_PINNED },
+ {24, false, "\010passport\006yandex\003com\002tr", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, true, "\006mnsure\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, false, "\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, false, "\003www\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {23, true, "\020matteomarescotti\004name", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, true, "\003www\011heliosnet\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, false, "\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\003www\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, true, "\007f-droid\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, false, "\003www\010evernote\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, false, "\003app\010yinxiang\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, false, "\011neilwynne\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, false, "\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {24, false, "\003www\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, true, "\011blacklane\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {16, true, "\012boxcryptor\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {10, false, "\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, false, "\003www\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, true, "\007prodpad\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, true, "\007mailbox\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, false, "\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED },
+ {16, false, "\003www\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED },
+ {10, true, "\005fiken\002no", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, true, "\010fairbill\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, true, "\005nexth\003net", true, kNoPins, DOMAIN_NOT_PINNED },
+ {10, true, "\005nexth\002us", true, kNoPins, DOMAIN_NOT_PINNED },
+ {10, true, "\005nexth\002de", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, true, "\006souyar\003net", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, true, "\006souyar\002de", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, true, "\006souyar\002us", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, false, "\003www\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, false, "\003mbp\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, false, "\007feedbin\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {9, true, "\004heha\002co", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\013passwordbox\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, false, "\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\004pypi\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {16, true, "\003www\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\004docs\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\013encircleapp\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, true, "\010onedrive\004live\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, true, "\010onedrive\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, true, "\016keepersecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, true, "\011keeperapp\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, true, "\006donmez\002ws", true, kNoPins, DOMAIN_NOT_PINNED },
+ {23, false, "\010activiti\010alfresco\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, true, "\011cloudcert\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, true, "\010seifried\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, false, "\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, false, "\003www\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, false, "\006static\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, false, "\005stage\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, false, "\011vmoagents\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, true, "\007adsfund\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {9, false, "\004pult\002co", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, true, "\014dillonkorman\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, true, "\006edmodo\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {23, false, "\003www\013eternalgoth\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\003app\007manilla\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {16, true, "\012harvestapp\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, true, "\007anycoin\002me", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, true, "\010noexpect\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, false, "\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {16, true, "\003www\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {10, false, "\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {14, false, "\003www\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, false, "\006mobile\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {12, true, "\007subrosa\002io", true, kNoPins, DOMAIN_NOT_PINNED },
+ {15, false, "\011detectify\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, true, "\005crbug\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {20, true, "\016manageprojects\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {21, false, "\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {25, false, "\003www\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {11, false, "\006imouto\002my", true, kNoPins, DOMAIN_NOT_PINNED },
+ {13, true, "\010vocaloid\002my", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\006sakaki\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED },
+ {18, true, "\007reviews\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\004miku\007hatsune\002my", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, true, "\012webcollect\003org\002uk", true, kNoPins, DOMAIN_NOT_PINNED },
+ {24, false, "\003www\016capitainetrain\003com", true, kNoPins, DOMAIN_NOT_PINNED },
};
static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
@@ -894,6 +1001,8 @@ static const struct HSTSPreload kPreloadedSNISTS[] = {
{20, false, "\003www\012googlemail\003com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM },
{22, true, "\020google-analytics\003com", false, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM },
{18, true, "\014googlegroups\003com", false, kGooglePins, DOMAIN_GOOGLEGROUPS_COM },
+ {13, true, "\007mykolab\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\013semenkovich\003com", true, kNoPins, DOMAIN_NOT_PINNED },
};
static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
diff --git a/chromium/net/http/transport_security_state_static.json b/chromium/net/http/transport_security_state_static.json
index 500e1782110..35c14248a68 100644
--- a/chromium/net/http/transport_security_state_static.json
+++ b/chromium/net/http/transport_security_state_static.json
@@ -168,7 +168,7 @@
{ "name": "docs.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "sites.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "spreadsheets.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
- { "name": "appengine.google.com", "mode": "force-https", "pins": "google" },
+ { "name": "appengine.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "encrypted.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "accounts.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "profiles.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
@@ -184,6 +184,7 @@
{ "name": "goto.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "cloud.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "glass.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "admin.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
// play.google.com doesn't have include_subdomains because of crbug.com/327834.
{ "name": "play.google.com", "mode": "force-https", "pins": "google" },
@@ -203,6 +204,22 @@
{ "name": "dl.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
{ "name": "translate.googleapis.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "webfilings.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "webfilings-mirror-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "webfilings-eu.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "webfilings-eu-mirror.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-demo-eu.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-demo-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-pentest.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-trial-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "xbrlsuccess.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "w-spotlight.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-training-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-bigsky-master.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-staging-hr.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-training-master.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+ { "name": "wf-dogfood-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
+
// chart.apis.google.com is *not* HSTS because the certificate doesn't match
// and there are lots of links out there that still use the name. The correct
// hostname for this is chart.googleapis.com.
@@ -226,6 +243,7 @@
{ "name": "goo.gl", "include_subdomains": true, "pins": "google" },
{ "name": "g.co", "include_subdomains": true, "pins": "google" },
{ "name": "googletagmanager.com", "include_subdomains": true, "pins": "google" },
+ { "name": "googletagservices.com", "include_subdomains": true, "pins": "google" },
{ "name": "google.ac", "include_subdomains": true, "pins": "google" },
{ "name": "google.ad", "include_subdomains": true, "pins": "google" },
{ "name": "google.ae", "include_subdomains": true, "pins": "google" },
@@ -436,7 +454,6 @@
{ "name": "google.tm", "include_subdomains": true, "pins": "google" },
{ "name": "google.tn", "include_subdomains": true, "pins": "google" },
{ "name": "google.to", "include_subdomains": true, "pins": "google" },
- { "name": "google.tp", "include_subdomains": true, "pins": "google" },
{ "name": "google.tt", "include_subdomains": true, "pins": "google" },
{ "name": "google.us", "include_subdomains": true, "pins": "google" },
{ "name": "google.uz", "include_subdomains": true, "pins": "google" },
@@ -553,7 +570,7 @@
{ "name": "www.intercom.io", "mode": "force-https" },
{ "name": "fatzebra.com.au", "include_subdomains": true, "mode": "force-https" },
{ "name": "csawctf.poly.edu", "include_subdomains": true, "mode": "force-https" },
- { "name": "makeyourlaws.org", "mode": "force-https" },
+ { "name": "makeyourlaws.org", "include_subdomains": true, "mode": "force-https" },
{ "name": "www.makeyourlaws.org", "mode": "force-https" },
{ "name": "iop.intuit.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "surfeasy.com", "mode": "force-https" },
@@ -569,6 +586,12 @@
{ "name": "faq.lookout.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "platform.lookout.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "email.lookout.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "app.lookout.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "api.lookout.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "keymaster.lookout.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "discovery.lookout.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "mobilethreat.net", "include_subdomains": true, "mode": "force-https" },
+ { "name": "mobilethreatnetwork.net", "include_subdomains": true, "mode": "force-https" },
{ "name": "itriskltd.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "stocktrade.de", "include_subdomains": true, "mode": "force-https" },
{ "name": "openshift.redhat.com", "include_subdomains": true, "mode": "force-https" },
@@ -677,6 +700,91 @@
{ "name": "api.xero.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "eff.org", "include_subdomains": true, "mode": "force-https" },
{ "name": "mail.de", "include_subdomains": true, "mode": "force-https" },
+ { "name": "passport.yandex.ru", "mode": "force-https" },
+ { "name": "passport.yandex.com", "mode": "force-https" },
+ { "name": "passport.yandex.ua", "mode": "force-https" },
+ { "name": "passport.yandex.by", "mode": "force-https" },
+ { "name": "passport.yandex.kz", "mode": "force-https" },
+ { "name": "passport.yandex.com.tr", "mode": "force-https" },
+ { "name": "mnsure.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "getcloak.com", "mode": "force-https" },
+ { "name": "www.getcloak.com", "mode": "force-https" },
+ { "name": "matteomarescotti.name", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.heliosnet.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "opsmate.com", "mode": "force-https" },
+ { "name": "www.opsmate.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "f-droid.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.evernote.com", "mode": "force-https" },
+ { "name": "app.yinxiang.com", "mode": "force-https" },
+ { "name": "neilwynne.com", "mode": "force-https" },
+ { "name": "calyxinstitute.org", "mode": "force-https" },
+ { "name": "www.calyxinstitute.org", "mode": "force-https" },
+ { "name": "blacklane.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "boxcryptor.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "aclu.org", "mode": "force-https" },
+ { "name": "www.aclu.org", "mode": "force-https" },
+ { "name": "prodpad.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "mailbox.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "roddis.net", "mode": "force-https" },
+ { "name": "www.roddis.net", "mode": "force-https" },
+ { "name": "fiken.no", "include_subdomains": true, "mode": "force-https" },
+ { "name": "fairbill.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "nexth.net", "include_subdomains": true, "mode": "force-https" },
+ { "name": "nexth.us", "include_subdomains": true, "mode": "force-https" },
+ { "name": "nexth.de", "include_subdomains": true, "mode": "force-https" },
+ { "name": "souyar.net", "include_subdomains": true, "mode": "force-https" },
+ { "name": "souyar.de", "include_subdomains": true, "mode": "force-https" },
+ { "name": "souyar.us", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.banking.co.at", "mode": "force-https" },
+ { "name": "mbp.banking.co.at", "mode": "force-https" },
+ { "name": "feedbin.com", "mode": "force-https" },
+ { "name": "heha.co", "include_subdomains": true, "mode": "force-https" },
+ { "name": "passwordbox.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "python.org", "mode": "force-https" },
+ { "name": "pypi.python.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.python.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "docs.python.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "encircleapp.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "onedrive.live.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "onedrive.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "keepersecurity.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "keeperapp.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "donmez.ws", "include_subdomains": true, "mode": "force-https" },
+ { "name": "activiti.alfresco.com", "mode": "force-https" },
+ { "name": "cloudcert.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "seifried.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "wepay.com", "mode": "force-https" },
+ { "name": "www.wepay.com", "mode": "force-https" },
+ { "name": "static.wepay.com", "mode": "force-https" },
+ { "name": "stage.wepay.com", "mode": "force-https" },
+ { "name": "vmoagents.com", "mode": "force-https" },
+ { "name": "adsfund.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "pult.co", "mode": "force-https" },
+ { "name": "dillonkorman.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "edmodo.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.eternalgoth.co.uk", "mode": "force-https" },
+ { "name": "app.manilla.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "harvestapp.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "anycoin.me", "include_subdomains": true, "mode": "force-https" },
+ { "name": "noexpect.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "airbnb.com", "mode": "force-https" },
+ { "name": "www.airbnb.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "usaa.com", "mode": "force-https" },
+ { "name": "www.usaa.com", "mode": "force-https" },
+ { "name": "mobile.usaa.com", "mode": "force-https" },
+ { "name": "subrosa.io", "include_subdomains": true, "mode": "force-https" },
+ { "name": "detectify.com", "mode": "force-https" },
+ { "name": "crbug.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "manageprojects.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "tinfoilsecurity.com", "mode": "force-https" },
+ { "name": "www.tinfoilsecurity.com", "mode": "force-https" },
+ { "name": "imouto.my", "mode": "force-https" },
+ { "name": "vocaloid.my", "include_subdomains": true, "mode": "force-https" },
+ { "name": "sakaki.anime.my", "include_subdomains": true, "mode": "force-https" },
+ { "name": "reviews.anime.my", "include_subdomains": true, "mode": "force-https" },
+ { "name": "miku.hatsune.my", "include_subdomains": true, "mode": "force-https" },
+ { "name": "webcollect.org.uk", "include_subdomains": true, "mode": "force-https" },
+ { "name": "www.capitainetrain.com", "mode": "force-https" },
// Entries that are only valid if the client supports SNI.
{ "name": "gmail.com", "mode": "force-https", "pins": "google", "snionly": true },
@@ -684,6 +792,8 @@
{ "name": "www.gmail.com", "mode": "force-https", "pins": "google", "snionly": true },
{ "name": "www.googlemail.com", "mode": "force-https", "pins": "google", "snionly": true },
{ "name": "google-analytics.com", "include_subdomains": true, "pins": "google", "snionly": true },
- { "name": "googlegroups.com", "include_subdomains": true, "pins": "google", "snionly": true }
+ { "name": "googlegroups.com", "include_subdomains": true, "pins": "google", "snionly": true },
+ { "name": "mykolab.com", "include_subdomains": true, "mode": "force-https", "snionly": true },
+ { "name": "semenkovich.com", "include_subdomains": true, "mode": "force-https", "snionly": true }
]
}
diff --git a/chromium/net/http/transport_security_state_unittest.cc b/chromium/net/http/transport_security_state_unittest.cc
index c3d15da29f9..476ae94bd68 100644
--- a/chromium/net/http/transport_security_state_unittest.cc
+++ b/chromium/net/http/transport_security_state_unittest.cc
@@ -46,10 +46,6 @@ class TransportSecurityStateTest : public testing::Test {
}
protected:
- std::string CanonicalizeHost(const std::string& host) {
- return TransportSecurityState::CanonicalizeHost(host);
- }
-
bool GetStaticDomainState(TransportSecurityState* state,
const std::string& host,
bool sni_enabled,
@@ -70,10 +66,10 @@ TEST_F(TransportSecurityStateTest, SimpleMatches) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
bool include_subdomains = false;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
}
TEST_F(TransportSecurityStateTest, MatchesCase1) {
@@ -82,10 +78,10 @@ TEST_F(TransportSecurityStateTest, MatchesCase1) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
bool include_subdomains = false;
state.AddHSTS("YAhoo.coM", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
}
TEST_F(TransportSecurityStateTest, MatchesCase2) {
@@ -94,10 +90,10 @@ TEST_F(TransportSecurityStateTest, MatchesCase2) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("YAhoo.coM", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("YAhoo.coM", &domain_state));
bool include_subdomains = false;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("YAhoo.coM", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("YAhoo.coM", &domain_state));
}
TEST_F(TransportSecurityStateTest, SubdomainMatches) {
@@ -106,15 +102,15 @@ TEST_F(TransportSecurityStateTest, SubdomainMatches) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
bool include_subdomains = true;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
- EXPECT_TRUE(state.GetDomainState("foo.yahoo.com", true, &domain_state));
- EXPECT_TRUE(state.GetDomainState("foo.bar.yahoo.com", true, &domain_state));
- EXPECT_TRUE(state.GetDomainState("foo.bar.baz.yahoo.com", true,
- &domain_state));
- EXPECT_FALSE(state.GetDomainState("com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("foo.yahoo.com", &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("foo.bar.yahoo.com", &domain_state));
+ EXPECT_TRUE(
+ state.GetDynamicDomainState("foo.bar.baz.yahoo.com", &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("com", &domain_state));
}
TEST_F(TransportSecurityStateTest, InvalidDomains) {
@@ -123,11 +119,12 @@ TEST_F(TransportSecurityStateTest, InvalidDomains) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
bool include_subdomains = true;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("www-.foo.yahoo.com", true, &domain_state));
- EXPECT_TRUE(state.GetDomainState("2\x01.foo.yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("www-.foo.yahoo.com", &domain_state));
+ EXPECT_TRUE(
+ state.GetDynamicDomainState("2\x01.foo.yahoo.com", &domain_state));
}
TEST_F(TransportSecurityStateTest, DeleteAllDynamicDataSince) {
@@ -137,14 +134,18 @@ TEST_F(TransportSecurityStateTest, DeleteAllDynamicDataSince) {
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
const base::Time older = current_time - base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
bool include_subdomains = false;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
state.DeleteAllDynamicDataSince(expiry);
- EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
+ EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS,
+ domain_state.sts.upgrade_mode);
state.DeleteAllDynamicDataSince(older);
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
+ EXPECT_EQ(TransportSecurityState::DomainState::MODE_DEFAULT,
+ domain_state.sts.upgrade_mode);
}
TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) {
@@ -155,28 +156,28 @@ TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) {
bool include_subdomains = false;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
- EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
- EXPECT_FALSE(state.GetDomainState("example.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("example.com", &domain_state));
EXPECT_TRUE(state.DeleteDynamicDataForHost("yahoo.com"));
- EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state));
}
TEST_F(TransportSecurityStateTest, IsPreloaded) {
- const std::string paypal = CanonicalizeHost("paypal.com");
- const std::string www_paypal = CanonicalizeHost("www.paypal.com");
- const std::string foo_paypal = CanonicalizeHost("foo.paypal.com");
- const std::string a_www_paypal = CanonicalizeHost("a.www.paypal.com");
- const std::string abc_paypal = CanonicalizeHost("a.b.c.paypal.com");
- const std::string example = CanonicalizeHost("example.com");
- const std::string aypal = CanonicalizeHost("aypal.com");
+ const std::string paypal = "paypal.com";
+ const std::string www_paypal = "www.paypal.com";
+ const std::string foo_paypal = "foo.paypal.com";
+ const std::string a_www_paypal = "a.www.paypal.com";
+ const std::string abc_paypal = "a.b.c.paypal.com";
+ const std::string example = "example.com";
+ const std::string aypal = "aypal.com";
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
EXPECT_TRUE(GetStaticDomainState(&state, paypal, true, &domain_state));
EXPECT_TRUE(GetStaticDomainState(&state, www_paypal, true, &domain_state));
- EXPECT_FALSE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ EXPECT_FALSE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
EXPECT_FALSE(GetStaticDomainState(&state, a_www_paypal, true, &domain_state));
EXPECT_FALSE(GetStaticDomainState(&state, abc_paypal, true, &domain_state));
EXPECT_FALSE(GetStaticDomainState(&state, example, true, &domain_state));
@@ -189,48 +190,49 @@ TEST_F(TransportSecurityStateTest, PreloadedDomainSet) {
// The domain wasn't being set, leading to a blank string in the
// chrome://net-internals/#hsts UI. So test that.
- EXPECT_TRUE(state.GetDomainState("market.android.com", true, &domain_state));
+ EXPECT_TRUE(
+ state.GetStaticDomainState("market.android.com", true, &domain_state));
EXPECT_EQ(domain_state.domain, "market.android.com");
- EXPECT_TRUE(state.GetDomainState("sub.market.android.com", true,
- &domain_state));
+ EXPECT_TRUE(state.GetStaticDomainState(
+ "sub.market.android.com", true, &domain_state));
EXPECT_EQ(domain_state.domain, "market.android.com");
}
-static bool ShouldRedirect(const char* hostname) {
+static bool StaticShouldRedirect(const char* hostname) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.GetDomainState(hostname, true /* SNI ok */, &domain_state) &&
+ return state.GetStaticDomainState(
+ hostname, true /* SNI ok */, &domain_state) &&
domain_state.ShouldUpgradeToSSL();
}
-static bool HasState(const char* hostname) {
+static bool HasStaticState(const char* hostname) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.GetDomainState(hostname, true /* SNI ok */, &domain_state);
+ return state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state);
}
-static bool HasPublicKeyPins(const char* hostname, bool sni_enabled) {
+static bool HasStaticPublicKeyPins(const char* hostname, bool sni_enabled) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- if (!state.GetDomainState(hostname, sni_enabled, &domain_state))
+ if (!state.GetStaticDomainState(hostname, sni_enabled, &domain_state))
return false;
return domain_state.HasPublicKeyPins();
}
-static bool HasPublicKeyPins(const char* hostname) {
- return HasPublicKeyPins(hostname, true);
+static bool HasStaticPublicKeyPins(const char* hostname) {
+ return HasStaticPublicKeyPins(hostname, true);
}
-static bool OnlyPinning(const char *hostname) {
+static bool OnlyPinningInStaticState(const char* hostname) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- if (!state.GetDomainState(hostname, true /* SNI ok */, &domain_state))
+ if (!state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state))
return false;
- return (domain_state.static_spki_hashes.size() > 0 ||
- domain_state.bad_static_spki_hashes.size() > 0 ||
- domain_state.dynamic_spki_hashes.size() > 0) &&
+ return (domain_state.pkp.spki_hashes.size() > 0 ||
+ domain_state.pkp.bad_spki_hashes.size() > 0) &&
!domain_state.ShouldUpgradeToSSL();
}
@@ -239,240 +241,245 @@ TEST_F(TransportSecurityStateTest, Preloaded) {
TransportSecurityState::DomainState domain_state;
// We do more extensive checks for the first domain.
- EXPECT_TRUE(state.GetDomainState("www.paypal.com", true, &domain_state));
- EXPECT_EQ(domain_state.upgrade_mode,
+ EXPECT_TRUE(
+ state.GetStaticDomainState("www.paypal.com", true, &domain_state));
+ EXPECT_EQ(domain_state.sts.upgrade_mode,
TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
- EXPECT_FALSE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ EXPECT_FALSE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
- EXPECT_TRUE(HasState("paypal.com"));
- EXPECT_FALSE(HasState("www2.paypal.com"));
- EXPECT_FALSE(HasState("www2.paypal.com"));
+ EXPECT_TRUE(HasStaticState("paypal.com"));
+ EXPECT_FALSE(HasStaticState("www2.paypal.com"));
+ EXPECT_FALSE(HasStaticState("www2.paypal.com"));
// Google hosts:
- EXPECT_TRUE(ShouldRedirect("chrome.google.com"));
- EXPECT_TRUE(ShouldRedirect("checkout.google.com"));
- EXPECT_TRUE(ShouldRedirect("wallet.google.com"));
- EXPECT_TRUE(ShouldRedirect("docs.google.com"));
- EXPECT_TRUE(ShouldRedirect("sites.google.com"));
- EXPECT_TRUE(ShouldRedirect("drive.google.com"));
- EXPECT_TRUE(ShouldRedirect("spreadsheets.google.com"));
- EXPECT_TRUE(ShouldRedirect("appengine.google.com"));
- EXPECT_TRUE(ShouldRedirect("market.android.com"));
- EXPECT_TRUE(ShouldRedirect("encrypted.google.com"));
- EXPECT_TRUE(ShouldRedirect("accounts.google.com"));
- EXPECT_TRUE(ShouldRedirect("profiles.google.com"));
- EXPECT_TRUE(ShouldRedirect("mail.google.com"));
- EXPECT_TRUE(ShouldRedirect("chatenabled.mail.google.com"));
- EXPECT_TRUE(ShouldRedirect("talkgadget.google.com"));
- EXPECT_TRUE(ShouldRedirect("hostedtalkgadget.google.com"));
- EXPECT_TRUE(ShouldRedirect("talk.google.com"));
- EXPECT_TRUE(ShouldRedirect("plus.google.com"));
- EXPECT_TRUE(ShouldRedirect("groups.google.com"));
- EXPECT_TRUE(ShouldRedirect("apis.google.com"));
- EXPECT_FALSE(ShouldRedirect("chart.apis.google.com"));
- EXPECT_TRUE(ShouldRedirect("ssl.google-analytics.com"));
- EXPECT_TRUE(ShouldRedirect("gmail.com"));
- EXPECT_TRUE(ShouldRedirect("www.gmail.com"));
- EXPECT_TRUE(ShouldRedirect("googlemail.com"));
- EXPECT_TRUE(ShouldRedirect("www.googlemail.com"));
- EXPECT_TRUE(ShouldRedirect("googleplex.com"));
- EXPECT_TRUE(ShouldRedirect("www.googleplex.com"));
- EXPECT_FALSE(HasState("m.gmail.com"));
- EXPECT_FALSE(HasState("m.googlemail.com"));
-
- EXPECT_TRUE(OnlyPinning("www.google.com"));
- EXPECT_TRUE(OnlyPinning("foo.google.com"));
- EXPECT_TRUE(OnlyPinning("google.com"));
- EXPECT_TRUE(OnlyPinning("www.youtube.com"));
- EXPECT_TRUE(OnlyPinning("youtube.com"));
- EXPECT_TRUE(OnlyPinning("i.ytimg.com"));
- EXPECT_TRUE(OnlyPinning("ytimg.com"));
- EXPECT_TRUE(OnlyPinning("googleusercontent.com"));
- EXPECT_TRUE(OnlyPinning("www.googleusercontent.com"));
- EXPECT_TRUE(OnlyPinning("www.google-analytics.com"));
- EXPECT_TRUE(OnlyPinning("googleapis.com"));
- EXPECT_TRUE(OnlyPinning("googleadservices.com"));
- EXPECT_TRUE(OnlyPinning("googlecode.com"));
- EXPECT_TRUE(OnlyPinning("appspot.com"));
- EXPECT_TRUE(OnlyPinning("googlesyndication.com"));
- EXPECT_TRUE(OnlyPinning("doubleclick.net"));
- EXPECT_TRUE(OnlyPinning("googlegroups.com"));
+ EXPECT_TRUE(StaticShouldRedirect("chrome.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("checkout.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("wallet.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("docs.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("sites.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("drive.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("spreadsheets.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("appengine.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("market.android.com"));
+ EXPECT_TRUE(StaticShouldRedirect("encrypted.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("accounts.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("profiles.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("mail.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("chatenabled.mail.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("talkgadget.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("hostedtalkgadget.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("talk.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("plus.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("groups.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("apis.google.com"));
+ EXPECT_FALSE(StaticShouldRedirect("chart.apis.google.com"));
+ EXPECT_TRUE(StaticShouldRedirect("ssl.google-analytics.com"));
+ EXPECT_TRUE(StaticShouldRedirect("gmail.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.gmail.com"));
+ EXPECT_TRUE(StaticShouldRedirect("googlemail.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.googlemail.com"));
+ EXPECT_TRUE(StaticShouldRedirect("googleplex.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.googleplex.com"));
+ EXPECT_FALSE(HasStaticState("m.gmail.com"));
+ EXPECT_FALSE(HasStaticState("m.googlemail.com"));
+
+ EXPECT_TRUE(OnlyPinningInStaticState("www.google.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("google.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("youtube.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("appspot.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com"));
+ EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net"));
+ EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com"));
// Tests for domains that don't work without SNI.
- EXPECT_FALSE(state.GetDomainState("gmail.com", false, &domain_state));
- EXPECT_FALSE(state.GetDomainState("www.gmail.com", false, &domain_state));
- EXPECT_FALSE(state.GetDomainState("m.gmail.com", false, &domain_state));
- EXPECT_FALSE(state.GetDomainState("googlemail.com", false, &domain_state));
- EXPECT_FALSE(state.GetDomainState("www.googlemail.com", false,
- &domain_state));
- EXPECT_FALSE(state.GetDomainState("m.googlemail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState("gmail.com", false, &domain_state));
+ EXPECT_FALSE(
+ state.GetStaticDomainState("www.gmail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState("m.gmail.com", false, &domain_state));
+ EXPECT_FALSE(
+ state.GetStaticDomainState("googlemail.com", false, &domain_state));
+ EXPECT_FALSE(
+ state.GetStaticDomainState("www.googlemail.com", false, &domain_state));
+ EXPECT_FALSE(
+ state.GetStaticDomainState("m.googlemail.com", false, &domain_state));
// Other hosts:
- EXPECT_TRUE(ShouldRedirect("aladdinschools.appspot.com"));
-
- EXPECT_TRUE(ShouldRedirect("ottospora.nl"));
- EXPECT_TRUE(ShouldRedirect("www.ottospora.nl"));
+ EXPECT_TRUE(StaticShouldRedirect("aladdinschools.appspot.com"));
- EXPECT_TRUE(ShouldRedirect("www.paycheckrecords.com"));
+ EXPECT_TRUE(StaticShouldRedirect("ottospora.nl"));
+ EXPECT_TRUE(StaticShouldRedirect("www.ottospora.nl"));
- EXPECT_TRUE(ShouldRedirect("lastpass.com"));
- EXPECT_TRUE(ShouldRedirect("www.lastpass.com"));
- EXPECT_FALSE(HasState("blog.lastpass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.paycheckrecords.com"));
- EXPECT_TRUE(ShouldRedirect("keyerror.com"));
- EXPECT_TRUE(ShouldRedirect("www.keyerror.com"));
+ EXPECT_TRUE(StaticShouldRedirect("lastpass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.lastpass.com"));
+ EXPECT_FALSE(HasStaticState("blog.lastpass.com"));
- EXPECT_TRUE(ShouldRedirect("entropia.de"));
- EXPECT_TRUE(ShouldRedirect("www.entropia.de"));
- EXPECT_FALSE(HasState("foo.entropia.de"));
+ EXPECT_TRUE(StaticShouldRedirect("keyerror.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.keyerror.com"));
- EXPECT_TRUE(ShouldRedirect("www.elanex.biz"));
- EXPECT_FALSE(HasState("elanex.biz"));
- EXPECT_FALSE(HasState("foo.elanex.biz"));
+ EXPECT_TRUE(StaticShouldRedirect("entropia.de"));
+ EXPECT_TRUE(StaticShouldRedirect("www.entropia.de"));
+ EXPECT_FALSE(HasStaticState("foo.entropia.de"));
- EXPECT_TRUE(ShouldRedirect("sunshinepress.org"));
- EXPECT_TRUE(ShouldRedirect("www.sunshinepress.org"));
- EXPECT_TRUE(ShouldRedirect("a.b.sunshinepress.org"));
+ EXPECT_TRUE(StaticShouldRedirect("www.elanex.biz"));
+ EXPECT_FALSE(HasStaticState("elanex.biz"));
+ EXPECT_FALSE(HasStaticState("foo.elanex.biz"));
- EXPECT_TRUE(ShouldRedirect("www.noisebridge.net"));
- EXPECT_FALSE(HasState("noisebridge.net"));
- EXPECT_FALSE(HasState("foo.noisebridge.net"));
+ EXPECT_TRUE(StaticShouldRedirect("sunshinepress.org"));
+ EXPECT_TRUE(StaticShouldRedirect("www.sunshinepress.org"));
+ EXPECT_TRUE(StaticShouldRedirect("a.b.sunshinepress.org"));
- EXPECT_TRUE(ShouldRedirect("neg9.org"));
- EXPECT_FALSE(HasState("www.neg9.org"));
+ EXPECT_TRUE(StaticShouldRedirect("www.noisebridge.net"));
+ EXPECT_FALSE(HasStaticState("noisebridge.net"));
+ EXPECT_FALSE(HasStaticState("foo.noisebridge.net"));
- EXPECT_TRUE(ShouldRedirect("riseup.net"));
- EXPECT_TRUE(ShouldRedirect("foo.riseup.net"));
+ EXPECT_TRUE(StaticShouldRedirect("neg9.org"));
+ EXPECT_FALSE(HasStaticState("www.neg9.org"));
- EXPECT_TRUE(ShouldRedirect("factor.cc"));
- EXPECT_FALSE(HasState("www.factor.cc"));
+ EXPECT_TRUE(StaticShouldRedirect("riseup.net"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.riseup.net"));
- EXPECT_TRUE(ShouldRedirect("members.mayfirst.org"));
- EXPECT_TRUE(ShouldRedirect("support.mayfirst.org"));
- EXPECT_TRUE(ShouldRedirect("id.mayfirst.org"));
- EXPECT_TRUE(ShouldRedirect("lists.mayfirst.org"));
- EXPECT_FALSE(HasState("www.mayfirst.org"));
+ EXPECT_TRUE(StaticShouldRedirect("factor.cc"));
+ EXPECT_FALSE(HasStaticState("www.factor.cc"));
- EXPECT_TRUE(ShouldRedirect("romab.com"));
- EXPECT_TRUE(ShouldRedirect("www.romab.com"));
- EXPECT_TRUE(ShouldRedirect("foo.romab.com"));
+ EXPECT_TRUE(StaticShouldRedirect("members.mayfirst.org"));
+ EXPECT_TRUE(StaticShouldRedirect("support.mayfirst.org"));
+ EXPECT_TRUE(StaticShouldRedirect("id.mayfirst.org"));
+ EXPECT_TRUE(StaticShouldRedirect("lists.mayfirst.org"));
+ EXPECT_FALSE(HasStaticState("www.mayfirst.org"));
- EXPECT_TRUE(ShouldRedirect("logentries.com"));
- EXPECT_TRUE(ShouldRedirect("www.logentries.com"));
- EXPECT_FALSE(HasState("foo.logentries.com"));
+ EXPECT_TRUE(StaticShouldRedirect("romab.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.romab.com"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.romab.com"));
- EXPECT_TRUE(ShouldRedirect("stripe.com"));
- EXPECT_TRUE(ShouldRedirect("foo.stripe.com"));
+ EXPECT_TRUE(StaticShouldRedirect("logentries.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.logentries.com"));
+ EXPECT_FALSE(HasStaticState("foo.logentries.com"));
- EXPECT_TRUE(ShouldRedirect("cloudsecurityalliance.org"));
- EXPECT_TRUE(ShouldRedirect("foo.cloudsecurityalliance.org"));
+ EXPECT_TRUE(StaticShouldRedirect("stripe.com"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.stripe.com"));
- EXPECT_TRUE(ShouldRedirect("login.sapo.pt"));
- EXPECT_TRUE(ShouldRedirect("foo.login.sapo.pt"));
+ EXPECT_TRUE(StaticShouldRedirect("cloudsecurityalliance.org"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.cloudsecurityalliance.org"));
- EXPECT_TRUE(ShouldRedirect("mattmccutchen.net"));
- EXPECT_TRUE(ShouldRedirect("foo.mattmccutchen.net"));
+ EXPECT_TRUE(StaticShouldRedirect("login.sapo.pt"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.login.sapo.pt"));
- EXPECT_TRUE(ShouldRedirect("betnet.fr"));
- EXPECT_TRUE(ShouldRedirect("foo.betnet.fr"));
+ EXPECT_TRUE(StaticShouldRedirect("mattmccutchen.net"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.mattmccutchen.net"));
- EXPECT_TRUE(ShouldRedirect("uprotect.it"));
- EXPECT_TRUE(ShouldRedirect("foo.uprotect.it"));
+ EXPECT_TRUE(StaticShouldRedirect("betnet.fr"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.betnet.fr"));
- EXPECT_TRUE(ShouldRedirect("squareup.com"));
- EXPECT_FALSE(HasState("foo.squareup.com"));
+ EXPECT_TRUE(StaticShouldRedirect("uprotect.it"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.uprotect.it"));
- EXPECT_TRUE(ShouldRedirect("cert.se"));
- EXPECT_TRUE(ShouldRedirect("foo.cert.se"));
+ EXPECT_TRUE(StaticShouldRedirect("squareup.com"));
+ EXPECT_FALSE(HasStaticState("foo.squareup.com"));
- EXPECT_TRUE(ShouldRedirect("crypto.is"));
- EXPECT_TRUE(ShouldRedirect("foo.crypto.is"));
+ EXPECT_TRUE(StaticShouldRedirect("cert.se"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.cert.se"));
- EXPECT_TRUE(ShouldRedirect("simon.butcher.name"));
- EXPECT_TRUE(ShouldRedirect("foo.simon.butcher.name"));
+ EXPECT_TRUE(StaticShouldRedirect("crypto.is"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.crypto.is"));
- EXPECT_TRUE(ShouldRedirect("linx.net"));
- EXPECT_TRUE(ShouldRedirect("foo.linx.net"));
+ EXPECT_TRUE(StaticShouldRedirect("simon.butcher.name"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.simon.butcher.name"));
- EXPECT_TRUE(ShouldRedirect("dropcam.com"));
- EXPECT_TRUE(ShouldRedirect("www.dropcam.com"));
- EXPECT_FALSE(HasState("foo.dropcam.com"));
+ EXPECT_TRUE(StaticShouldRedirect("linx.net"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.linx.net"));
- EXPECT_TRUE(state.GetDomainState("torproject.org", false, &domain_state));
- EXPECT_FALSE(domain_state.static_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState("www.torproject.org", false,
- &domain_state));
- EXPECT_FALSE(domain_state.static_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState("check.torproject.org", false,
- &domain_state));
- EXPECT_FALSE(domain_state.static_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState("blog.torproject.org", false,
- &domain_state));
- EXPECT_FALSE(domain_state.static_spki_hashes.empty());
- EXPECT_TRUE(ShouldRedirect("ebanking.indovinabank.com.vn"));
- EXPECT_TRUE(ShouldRedirect("foo.ebanking.indovinabank.com.vn"));
+ EXPECT_TRUE(StaticShouldRedirect("dropcam.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.dropcam.com"));
+ EXPECT_FALSE(HasStaticState("foo.dropcam.com"));
- EXPECT_TRUE(ShouldRedirect("epoxate.com"));
- EXPECT_FALSE(HasState("foo.epoxate.com"));
+ EXPECT_TRUE(
+ state.GetStaticDomainState("torproject.org", false, &domain_state));
+ EXPECT_FALSE(domain_state.pkp.spki_hashes.empty());
+ EXPECT_TRUE(
+ state.GetStaticDomainState("www.torproject.org", false, &domain_state));
+ EXPECT_FALSE(domain_state.pkp.spki_hashes.empty());
+ EXPECT_TRUE(
+ state.GetStaticDomainState("check.torproject.org", false, &domain_state));
+ EXPECT_FALSE(domain_state.pkp.spki_hashes.empty());
+ EXPECT_TRUE(
+ state.GetStaticDomainState("blog.torproject.org", false, &domain_state));
+ EXPECT_FALSE(domain_state.pkp.spki_hashes.empty());
+ EXPECT_TRUE(StaticShouldRedirect("ebanking.indovinabank.com.vn"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.ebanking.indovinabank.com.vn"));
- EXPECT_TRUE(HasPublicKeyPins("torproject.org"));
- EXPECT_TRUE(HasPublicKeyPins("www.torproject.org"));
- EXPECT_TRUE(HasPublicKeyPins("check.torproject.org"));
- EXPECT_TRUE(HasPublicKeyPins("blog.torproject.org"));
- EXPECT_FALSE(HasState("foo.torproject.org"));
+ EXPECT_TRUE(StaticShouldRedirect("epoxate.com"));
+ EXPECT_FALSE(HasStaticState("foo.epoxate.com"));
- EXPECT_TRUE(ShouldRedirect("www.moneybookers.com"));
- EXPECT_FALSE(HasState("moneybookers.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org"));
+ EXPECT_FALSE(HasStaticState("foo.torproject.org"));
- EXPECT_TRUE(ShouldRedirect("ledgerscope.net"));
- EXPECT_TRUE(ShouldRedirect("www.ledgerscope.net"));
- EXPECT_FALSE(HasState("status.ledgerscope.net"));
+ EXPECT_TRUE(StaticShouldRedirect("www.moneybookers.com"));
+ EXPECT_FALSE(HasStaticState("moneybookers.com"));
- EXPECT_TRUE(ShouldRedirect("foo.app.recurly.com"));
- EXPECT_TRUE(ShouldRedirect("foo.api.recurly.com"));
+ EXPECT_TRUE(StaticShouldRedirect("ledgerscope.net"));
+ EXPECT_TRUE(StaticShouldRedirect("www.ledgerscope.net"));
+ EXPECT_FALSE(HasStaticState("status.ledgerscope.net"));
- EXPECT_TRUE(ShouldRedirect("greplin.com"));
- EXPECT_TRUE(ShouldRedirect("www.greplin.com"));
- EXPECT_FALSE(HasState("foo.greplin.com"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.app.recurly.com"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.api.recurly.com"));
- EXPECT_TRUE(ShouldRedirect("luneta.nearbuysystems.com"));
- EXPECT_TRUE(ShouldRedirect("foo.luneta.nearbuysystems.com"));
+ EXPECT_TRUE(StaticShouldRedirect("greplin.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.greplin.com"));
+ EXPECT_FALSE(HasStaticState("foo.greplin.com"));
- EXPECT_TRUE(ShouldRedirect("ubertt.org"));
- EXPECT_TRUE(ShouldRedirect("foo.ubertt.org"));
+ EXPECT_TRUE(StaticShouldRedirect("luneta.nearbuysystems.com"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.luneta.nearbuysystems.com"));
- EXPECT_TRUE(ShouldRedirect("pixi.me"));
- EXPECT_TRUE(ShouldRedirect("www.pixi.me"));
+ EXPECT_TRUE(StaticShouldRedirect("ubertt.org"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.ubertt.org"));
- EXPECT_TRUE(ShouldRedirect("grepular.com"));
- EXPECT_TRUE(ShouldRedirect("www.grepular.com"));
+ EXPECT_TRUE(StaticShouldRedirect("pixi.me"));
+ EXPECT_TRUE(StaticShouldRedirect("www.pixi.me"));
- EXPECT_TRUE(ShouldRedirect("mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("www.mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.www.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("developer.mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.developer.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("www.developer.mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.www.developer.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("sandbox.mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.sandbox.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("www.sandbox.mydigipass.com"));
- EXPECT_FALSE(ShouldRedirect("foo.www.sandbox.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("grepular.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.grepular.com"));
+
+ EXPECT_TRUE(StaticShouldRedirect("mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.www.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("developer.mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.developer.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.developer.mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.www.developer.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("sandbox.mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.sandbox.mydigipass.com"));
+ EXPECT_TRUE(StaticShouldRedirect("www.sandbox.mydigipass.com"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.www.sandbox.mydigipass.com"));
- EXPECT_TRUE(ShouldRedirect("crypto.cat"));
- EXPECT_FALSE(ShouldRedirect("foo.crypto.cat"));
+ EXPECT_TRUE(StaticShouldRedirect("crypto.cat"));
+ EXPECT_FALSE(StaticShouldRedirect("foo.crypto.cat"));
- EXPECT_TRUE(ShouldRedirect("bigshinylock.minazo.net"));
- EXPECT_TRUE(ShouldRedirect("foo.bigshinylock.minazo.net"));
+ EXPECT_TRUE(StaticShouldRedirect("bigshinylock.minazo.net"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.bigshinylock.minazo.net"));
- EXPECT_TRUE(ShouldRedirect("crate.io"));
- EXPECT_TRUE(ShouldRedirect("foo.crate.io"));
+ EXPECT_TRUE(StaticShouldRedirect("crate.io"));
+ EXPECT_TRUE(StaticShouldRedirect("foo.crate.io"));
- EXPECT_TRUE(HasPublicKeyPins("www.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com"));
}
TEST_F(TransportSecurityStateTest, LongNames) {
@@ -482,59 +489,62 @@ TEST_F(TransportSecurityStateTest, LongNames) {
"WaveletIdDomainAndBlipBlipid";
TransportSecurityState::DomainState domain_state;
// Just checks that we don't hit a NOTREACHED.
- EXPECT_FALSE(state.GetDomainState(kLongName, true, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState(kLongName, true, &domain_state));
+ EXPECT_FALSE(state.GetDynamicDomainState(kLongName, &domain_state));
}
TEST_F(TransportSecurityStateTest, BuiltinCertPins) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(state.GetDomainState("chrome.google.com", true, &domain_state));
- EXPECT_TRUE(HasPublicKeyPins("chrome.google.com"));
+ EXPECT_TRUE(
+ state.GetStaticDomainState("chrome.google.com", true, &domain_state));
+ EXPECT_TRUE(HasStaticPublicKeyPins("chrome.google.com"));
HashValueVector hashes;
+ std::string failure_log;
// Checks that a built-in list does exist.
- EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes));
- EXPECT_FALSE(HasPublicKeyPins("www.paypal.com"));
-
- EXPECT_TRUE(HasPublicKeyPins("docs.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("1.docs.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("sites.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("drive.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("spreadsheets.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("wallet.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("checkout.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("appengine.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("market.android.com"));
- EXPECT_TRUE(HasPublicKeyPins("encrypted.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("accounts.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("profiles.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("mail.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("chatenabled.mail.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("talkgadget.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("hostedtalkgadget.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("talk.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("plus.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("groups.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("apis.google.com"));
-
- EXPECT_TRUE(HasPublicKeyPins("ssl.gstatic.com"));
- EXPECT_TRUE(HasPublicKeyPins("gstatic.com"));
- EXPECT_TRUE(HasPublicKeyPins("www.gstatic.com"));
- EXPECT_TRUE(HasPublicKeyPins("ssl.google-analytics.com"));
- EXPECT_TRUE(HasPublicKeyPins("www.googleplex.com"));
+ EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log));
+ EXPECT_FALSE(HasStaticPublicKeyPins("www.paypal.com"));
+
+ EXPECT_TRUE(HasStaticPublicKeyPins("docs.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("1.docs.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("sites.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("drive.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("spreadsheets.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("wallet.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("checkout.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("appengine.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("market.android.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("encrypted.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("accounts.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("profiles.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("mail.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("chatenabled.mail.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("talkgadget.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("hostedtalkgadget.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("talk.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("plus.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("groups.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("apis.google.com"));
+
+ EXPECT_TRUE(HasStaticPublicKeyPins("ssl.gstatic.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("gstatic.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.gstatic.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("ssl.google-analytics.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.googleplex.com"));
// Disabled in order to help track down pinning failures --agl
- EXPECT_TRUE(HasPublicKeyPins("twitter.com"));
- EXPECT_FALSE(HasPublicKeyPins("foo.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("www.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("api.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("oauth.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("mobile.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("dev.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("business.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("platform.twitter.com"));
- EXPECT_TRUE(HasPublicKeyPins("si0.twimg.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("twitter.com"));
+ EXPECT_FALSE(HasStaticPublicKeyPins("foo.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("api.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("oauth.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("mobile.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("dev.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("business.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("platform.twitter.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("si0.twimg.com"));
}
static bool AddHash(const std::string& type_and_base64,
@@ -576,54 +586,56 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(state.GetDomainState("blog.torproject.org", true, &domain_state));
+ EXPECT_TRUE(
+ state.GetStaticDomainState("blog.torproject.org", true, &domain_state));
EXPECT_TRUE(domain_state.HasPublicKeyPins());
- EXPECT_TRUE(domain_state.CheckPublicKeyPins(good_hashes));
- EXPECT_FALSE(domain_state.CheckPublicKeyPins(bad_hashes));
+ std::string failure_log;
+ EXPECT_TRUE(domain_state.CheckPublicKeyPins(good_hashes, &failure_log));
+ EXPECT_FALSE(domain_state.CheckPublicKeyPins(bad_hashes, &failure_log));
}
TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) {
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_FALSE(ShouldRedirect("www.google-analytics.com"));
-
- EXPECT_FALSE(HasPublicKeyPins("www.google-analytics.com", false));
- EXPECT_TRUE(HasPublicKeyPins("www.google-analytics.com"));
- EXPECT_TRUE(HasPublicKeyPins("google.com"));
- EXPECT_TRUE(HasPublicKeyPins("www.google.com"));
- EXPECT_TRUE(HasPublicKeyPins("mail-attachment.googleusercontent.com"));
- EXPECT_TRUE(HasPublicKeyPins("www.youtube.com"));
- EXPECT_TRUE(HasPublicKeyPins("i.ytimg.com"));
- EXPECT_TRUE(HasPublicKeyPins("googleapis.com"));
- EXPECT_TRUE(HasPublicKeyPins("ajax.googleapis.com"));
- EXPECT_TRUE(HasPublicKeyPins("googleadservices.com"));
- EXPECT_TRUE(HasPublicKeyPins("pagead2.googleadservices.com"));
- EXPECT_TRUE(HasPublicKeyPins("googlecode.com"));
- EXPECT_TRUE(HasPublicKeyPins("kibbles.googlecode.com"));
- EXPECT_TRUE(HasPublicKeyPins("appspot.com"));
- EXPECT_TRUE(HasPublicKeyPins("googlesyndication.com"));
- EXPECT_TRUE(HasPublicKeyPins("doubleclick.net"));
- EXPECT_TRUE(HasPublicKeyPins("ad.doubleclick.net"));
- EXPECT_FALSE(HasPublicKeyPins("learn.doubleclick.net"));
- EXPECT_TRUE(HasPublicKeyPins("a.googlegroups.com"));
- EXPECT_FALSE(HasPublicKeyPins("a.googlegroups.com", false));
+ EXPECT_FALSE(StaticShouldRedirect("www.google-analytics.com"));
+
+ EXPECT_FALSE(HasStaticPublicKeyPins("www.google-analytics.com", false));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.google-analytics.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("mail-attachment.googleusercontent.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("www.youtube.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("i.ytimg.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("googleapis.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("ajax.googleapis.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("googleadservices.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("pagead2.googleadservices.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("googlecode.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("kibbles.googlecode.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("appspot.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("googlesyndication.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("doubleclick.net"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("ad.doubleclick.net"));
+ EXPECT_FALSE(HasStaticPublicKeyPins("learn.doubleclick.net"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("a.googlegroups.com"));
+ EXPECT_FALSE(HasStaticPublicKeyPins("a.googlegroups.com", false));
}
TEST_F(TransportSecurityStateTest, OverrideBuiltins) {
- EXPECT_TRUE(HasPublicKeyPins("google.com"));
- EXPECT_FALSE(ShouldRedirect("google.com"));
- EXPECT_FALSE(ShouldRedirect("www.google.com"));
+ EXPECT_TRUE(HasStaticPublicKeyPins("google.com"));
+ EXPECT_FALSE(StaticShouldRedirect("google.com"));
+ EXPECT_FALSE(StaticShouldRedirect("www.google.com"));
TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- domain_state.upgrade_expiry = expiry;
+ domain_state.sts.expiry = expiry;
EnableHost(&state, "www.google.com", domain_state);
- EXPECT_TRUE(state.GetDomainState("www.google.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDynamicDomainState("www.google.com", &domain_state));
}
TEST_F(TransportSecurityStateTest, GooglePinnedProperties) {
diff --git a/chromium/net/http/url_security_manager_win.cc b/chromium/net/http/url_security_manager_win.cc
index cb3c66ef0f0..4e2d938963e 100644
--- a/chromium/net/http/url_security_manager_win.cc
+++ b/chromium/net/http/url_security_manager_win.cc
@@ -53,7 +53,7 @@ bool URLSecurityManagerWin::CanUseDefaultCredentials(
if (!const_cast<URLSecurityManagerWin*>(this)->EnsureSystemSecurityManager())
return false;
- std::wstring url_w = ASCIIToWide(auth_origin.spec());
+ std::wstring url_w = base::ASCIIToWide(auth_origin.spec());
DWORD policy = 0;
HRESULT hr;
hr = security_manager_->ProcessUrlAction(url_w.c_str(),
diff --git a/chromium/net/net.gyp b/chromium/net/net.gyp
index c2d618ef220..84184237d07 100644
--- a/chromium/net/net.gyp
+++ b/chromium/net/net.gyp
@@ -41,9 +41,46 @@
},
'includes': [
'../build/win_precompile.gypi',
+ 'net.gypi',
],
'targets': [
{
+ 'target_name': 'net_derived_sources',
+ 'type': 'none',
+ 'sources': [
+ 'base/registry_controlled_domains/effective_tld_names.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest1.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest2.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest3.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest4.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest5.gperf',
+ 'base/registry_controlled_domains/effective_tld_names_unittest6.gperf',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'dafsa',
+ 'extension': 'gperf',
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/net/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)-inc.cc',
+ ],
+ 'inputs': [
+ 'tools/tld_cleanup/make_dafsa.py',
+ ],
+ 'action': [
+ 'python',
+ 'tools/tld_cleanup/make_dafsa.py',
+ '<(RULE_INPUT_PATH)',
+ '<(SHARED_INTERMEDIATE_DIR)/net/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)-inc.cc',
+ ],
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)'
+ ],
+ },
+ },
+ {
'target_name': 'net',
'type': '<(component)',
'variables': { 'enable_wexit_time_destructors': 1, },
@@ -57,1145 +94,12 @@
'../third_party/icu/icu.gyp:icuuc',
'../third_party/zlib/zlib.gyp:zlib',
'../url/url.gyp:url_lib',
+ 'net_derived_sources',
'net_resources',
],
'sources': [
- 'android/cert_verify_result_android.h',
- 'android/cert_verify_result_android_list.h',
- 'android/gurl_utils.cc',
- 'android/gurl_utils.h',
- 'android/keystore.cc',
- 'android/keystore.h',
- 'android/keystore_openssl.cc',
- 'android/keystore_openssl.h',
- 'android/net_jni_registrar.cc',
- 'android/net_jni_registrar.h',
- 'android/network_change_notifier_android.cc',
- 'android/network_change_notifier_android.h',
- 'android/network_change_notifier_delegate_android.cc',
- 'android/network_change_notifier_delegate_android.h',
- 'android/network_change_notifier_factory_android.cc',
- 'android/network_change_notifier_factory_android.h',
- 'android/network_library.cc',
- 'android/network_library.h',
- 'base/address_family.h',
- 'base/address_list.cc',
- 'base/address_list.h',
- 'base/address_tracker_linux.cc',
- 'base/address_tracker_linux.h',
- 'base/auth.cc',
- 'base/auth.h',
- 'base/backoff_entry.cc',
- 'base/backoff_entry.h',
- 'base/bandwidth_metrics.cc',
- 'base/bandwidth_metrics.h',
- 'base/big_endian.cc',
- 'base/big_endian.h',
- 'base/cache_type.h',
- 'base/completion_callback.h',
- 'base/connection_type_histograms.cc',
- 'base/connection_type_histograms.h',
- 'base/crypto_module.h',
- 'base/crypto_module_nss.cc',
- 'base/crypto_module_openssl.cc',
- 'base/data_url.cc',
- 'base/data_url.h',
- 'base/directory_lister.cc',
- 'base/directory_lister.h',
- 'base/dns_reloader.cc',
- 'base/dns_reloader.h',
- 'base/dns_util.cc',
- 'base/dns_util.h',
- 'base/escape.cc',
- 'base/escape.h',
- 'base/expiring_cache.h',
- 'base/file_stream.cc',
- 'base/file_stream.h',
- 'base/file_stream_context.cc',
- 'base/file_stream_context.h',
- 'base/file_stream_context_posix.cc',
- 'base/file_stream_context_win.cc',
- 'base/file_stream_metrics.cc',
- 'base/file_stream_metrics.h',
- 'base/file_stream_metrics_posix.cc',
- 'base/file_stream_metrics_win.cc',
- 'base/file_stream_net_log_parameters.cc',
- 'base/file_stream_net_log_parameters.h',
- 'base/file_stream_whence.h',
- 'base/filter.cc',
- 'base/filter.h',
- 'base/int128.cc',
- 'base/int128.h',
- 'base/gzip_filter.cc',
- 'base/gzip_filter.h',
- 'base/gzip_header.cc',
- 'base/gzip_header.h',
- 'base/hash_value.cc',
- 'base/hash_value.h',
- 'base/host_mapping_rules.cc',
- 'base/host_mapping_rules.h',
- 'base/host_port_pair.cc',
- 'base/host_port_pair.h',
- 'base/io_buffer.cc',
- 'base/io_buffer.h',
- 'base/iovec.h',
- 'base/ip_endpoint.cc',
- 'base/ip_endpoint.h',
- 'base/keygen_handler.cc',
- 'base/keygen_handler.h',
- 'base/keygen_handler_mac.cc',
- 'base/keygen_handler_nss.cc',
- 'base/keygen_handler_openssl.cc',
- 'base/keygen_handler_win.cc',
- 'base/linked_hash_map.h',
- 'base/load_flags.h',
- 'base/load_flags_list.h',
- 'base/load_states.h',
- 'base/load_states_list.h',
- 'base/load_timing_info.cc',
- 'base/load_timing_info.h',
- 'base/mime_sniffer.cc',
- 'base/mime_sniffer.h',
- 'base/mime_util.cc',
- 'base/mime_util.h',
- 'base/net_error_list.h',
- 'base/net_errors.cc',
- 'base/net_errors.h',
- 'base/net_errors_posix.cc',
- 'base/net_errors_win.cc',
- 'base/net_export.h',
- 'base/net_log.cc',
- 'base/net_log.h',
- 'base/net_log_logger.cc',
- 'base/net_log_logger.h',
- 'base/net_log_event_type_list.h',
- 'base/net_log_source_type_list.h',
- 'base/net_module.cc',
- 'base/net_module.h',
- 'base/net_util.cc',
- 'base/net_util.h',
- 'base/net_util_posix.cc',
- 'base/net_util_win.cc',
- 'base/network_change_notifier.cc',
- 'base/network_change_notifier.h',
- 'base/network_change_notifier_factory.h',
- 'base/network_change_notifier_linux.cc',
- 'base/network_change_notifier_linux.h',
- 'base/network_change_notifier_mac.cc',
- 'base/network_change_notifier_mac.h',
- 'base/network_change_notifier_win.cc',
- 'base/network_change_notifier_win.h',
- 'base/network_config_watcher_mac.cc',
- 'base/network_config_watcher_mac.h',
- 'base/network_delegate.cc',
- 'base/network_delegate.h',
- 'base/network_time_notifier.cc',
- 'base/network_time_notifier.h',
- 'base/nss_memio.c',
- 'base/nss_memio.h',
- 'base/openssl_private_key_store.h',
- 'base/openssl_private_key_store_android.cc',
- 'base/openssl_private_key_store_memory.cc',
- 'base/platform_mime_util.h',
- # TODO(tc): gnome-vfs? xdgmime? /etc/mime.types?
- 'base/platform_mime_util_linux.cc',
- 'base/platform_mime_util_mac.mm',
- 'base/platform_mime_util_win.cc',
- 'base/prioritized_dispatcher.cc',
- 'base/prioritized_dispatcher.h',
- 'base/priority_queue.h',
- 'base/rand_callback.h',
- 'base/registry_controlled_domains/registry_controlled_domain.cc',
- 'base/registry_controlled_domains/registry_controlled_domain.h',
- 'base/request_priority.cc',
- 'base/request_priority.h',
- 'base/sdch_filter.cc',
- 'base/sdch_filter.h',
- 'base/sdch_manager.cc',
- 'base/sdch_manager.h',
- 'base/static_cookie_policy.cc',
- 'base/static_cookie_policy.h',
- 'base/sys_addrinfo.h',
- 'base/test_data_stream.cc',
- 'base/test_data_stream.h',
- 'base/upload_bytes_element_reader.cc',
- 'base/upload_bytes_element_reader.h',
- 'base/upload_data.cc',
- 'base/upload_data.h',
- 'base/upload_data_stream.cc',
- 'base/upload_data_stream.h',
- 'base/upload_element.cc',
- 'base/upload_element.h',
- 'base/upload_element_reader.cc',
- 'base/upload_element_reader.h',
- 'base/upload_file_element_reader.cc',
- 'base/upload_file_element_reader.h',
- 'base/upload_progress.h',
- 'base/url_util.cc',
- 'base/url_util.h',
- 'base/winsock_init.cc',
- 'base/winsock_init.h',
- 'base/winsock_util.cc',
- 'base/winsock_util.h',
- 'base/zap.cc',
- 'base/zap.h',
- 'cert/asn1_util.cc',
- 'cert/asn1_util.h',
- 'cert/cert_database.cc',
- 'cert/cert_database.h',
- 'cert/cert_database_android.cc',
- 'cert/cert_database_ios.cc',
- 'cert/cert_database_mac.cc',
- 'cert/cert_database_nss.cc',
- 'cert/cert_database_openssl.cc',
- 'cert/cert_database_win.cc',
- 'cert/cert_status_flags.cc',
- 'cert/cert_status_flags.h',
- 'cert/cert_trust_anchor_provider.h',
- 'cert/cert_verifier.cc',
- 'cert/cert_verifier.h',
- 'cert/cert_verify_proc.cc',
- 'cert/cert_verify_proc.h',
- 'cert/cert_verify_proc_android.cc',
- 'cert/cert_verify_proc_android.h',
- 'cert/cert_verify_proc_mac.cc',
- 'cert/cert_verify_proc_mac.h',
- 'cert/cert_verify_proc_nss.cc',
- 'cert/cert_verify_proc_nss.h',
- 'cert/cert_verify_proc_openssl.cc',
- 'cert/cert_verify_proc_openssl.h',
- 'cert/cert_verify_proc_win.cc',
- 'cert/cert_verify_proc_win.h',
- 'cert/cert_verify_result.cc',
- 'cert/cert_verify_result.h',
- 'cert/crl_set.cc',
- 'cert/crl_set.h',
- 'cert/ct_known_logs.cc',
- 'cert/ct_known_logs.h',
- 'cert/ct_log_verifier.cc',
- 'cert/ct_log_verifier.h',
- 'cert/ct_log_verifier_nss.cc',
- 'cert/ct_log_verifier_openssl.cc',
- 'cert/ct_objects_extractor.h',
- 'cert/ct_objects_extractor_nss.cc',
- 'cert/ct_objects_extractor_openssl.cc',
- 'cert/ct_serialization.cc',
- 'cert/ct_serialization.h',
- 'cert/ct_signed_certificate_timestamp_log_param.cc',
- 'cert/ct_signed_certificate_timestamp_log_param.h',
- 'cert/ct_verifier.h',
- 'cert/ct_verify_result.cc',
- 'cert/ct_verify_result.h',
- 'cert/ev_root_ca_metadata.cc',
- 'cert/ev_root_ca_metadata.h',
- 'cert/jwk_serializer_nss.cc',
- 'cert/jwk_serializer_openssl.cc',
- 'cert/jwk_serializer.h',
- 'cert/multi_log_ct_verifier.cc',
- 'cert/multi_log_ct_verifier.h',
- 'cert/multi_threaded_cert_verifier.cc',
- 'cert/multi_threaded_cert_verifier.h',
- 'cert/nss_cert_database.cc',
- 'cert/nss_cert_database.h',
- 'cert/pem_tokenizer.cc',
- 'cert/pem_tokenizer.h',
- 'cert/scoped_nss_types.h',
- 'cert/sct_status_flags.h',
- 'cert/signed_certificate_timestamp.cc',
- 'cert/signed_certificate_timestamp.h',
- 'cert/single_request_cert_verifier.cc',
- 'cert/single_request_cert_verifier.h',
- 'cert/test_root_certs.cc',
- 'cert/test_root_certs.h',
- 'cert/test_root_certs_android.cc',
- 'cert/test_root_certs_mac.cc',
- 'cert/test_root_certs_nss.cc',
- 'cert/test_root_certs_openssl.cc',
- 'cert/test_root_certs_win.cc',
- 'cert/x509_cert_types.cc',
- 'cert/x509_cert_types.h',
- 'cert/x509_cert_types_mac.cc',
- 'cert/x509_cert_types_win.cc',
- 'cert/x509_certificate.cc',
- 'cert/x509_certificate.h',
- 'cert/x509_certificate_ios.cc',
- 'cert/x509_certificate_mac.cc',
- 'cert/x509_certificate_net_log_param.cc',
- 'cert/x509_certificate_net_log_param.h',
- 'cert/x509_certificate_nss.cc',
- 'cert/x509_certificate_openssl.cc',
- 'cert/x509_certificate_win.cc',
- 'cert/x509_util.cc',
- 'cert/x509_util.h',
- 'cert/x509_util_android.cc',
- 'cert/x509_util_android.h',
- 'cert/x509_util_ios.cc',
- 'cert/x509_util_ios.h',
- 'cert/x509_util_mac.cc',
- 'cert/x509_util_mac.h',
- 'cert/x509_util_nss.cc',
- 'cert/x509_util_nss.h',
- 'cert/x509_util_openssl.cc',
- 'cert/x509_util_openssl.h',
- 'cookies/canonical_cookie.cc',
- 'cookies/canonical_cookie.h',
- 'cookies/cookie_constants.cc',
- 'cookies/cookie_constants.h',
- 'cookies/cookie_monster.cc',
- 'cookies/cookie_monster.h',
- 'cookies/cookie_options.h',
- 'cookies/cookie_store.cc',
- 'cookies/cookie_store.h',
- 'cookies/cookie_util.cc',
- 'cookies/cookie_util.h',
- 'cookies/parsed_cookie.cc',
- 'cookies/parsed_cookie.h',
- 'disk_cache/addr.cc',
- 'disk_cache/addr.h',
- 'disk_cache/backend_impl.cc',
- 'disk_cache/backend_impl.h',
- 'disk_cache/bitmap.cc',
- 'disk_cache/bitmap.h',
- 'disk_cache/block_files.cc',
- 'disk_cache/block_files.h',
- 'disk_cache/cache_creator.cc',
- 'disk_cache/cache_util.h',
- 'disk_cache/cache_util.cc',
- 'disk_cache/cache_util_posix.cc',
- 'disk_cache/cache_util_win.cc',
- 'disk_cache/disk_cache.h',
- 'disk_cache/disk_format.cc',
- 'disk_cache/disk_format.h',
- 'disk_cache/disk_format_base.h',
- 'disk_cache/entry_impl.cc',
- 'disk_cache/entry_impl.h',
- 'disk_cache/errors.h',
- 'disk_cache/eviction.cc',
- 'disk_cache/eviction.h',
- 'disk_cache/experiments.h',
- 'disk_cache/file.cc',
- 'disk_cache/file.h',
- 'disk_cache/file_block.h',
- 'disk_cache/file_ios.cc',
- 'disk_cache/file_lock.cc',
- 'disk_cache/file_lock.h',
- 'disk_cache/file_posix.cc',
- 'disk_cache/file_win.cc',
- 'disk_cache/histogram_macros.h',
- 'disk_cache/in_flight_backend_io.cc',
- 'disk_cache/in_flight_backend_io.h',
- 'disk_cache/in_flight_io.cc',
- 'disk_cache/in_flight_io.h',
- 'disk_cache/mapped_file.cc',
- 'disk_cache/mapped_file.h',
- 'disk_cache/mapped_file_posix.cc',
- 'disk_cache/mapped_file_avoid_mmap_posix.cc',
- 'disk_cache/mapped_file_win.cc',
- 'disk_cache/mem_backend_impl.cc',
- 'disk_cache/mem_backend_impl.h',
- 'disk_cache/mem_entry_impl.cc',
- 'disk_cache/mem_entry_impl.h',
- 'disk_cache/mem_rankings.cc',
- 'disk_cache/mem_rankings.h',
- 'disk_cache/net_log_parameters.cc',
- 'disk_cache/net_log_parameters.h',
- 'disk_cache/rankings.cc',
- 'disk_cache/rankings.h',
- 'disk_cache/sparse_control.cc',
- 'disk_cache/sparse_control.h',
- 'disk_cache/stats.cc',
- 'disk_cache/stats.h',
- 'disk_cache/stats_histogram.cc',
- 'disk_cache/stats_histogram.h',
- 'disk_cache/storage_block-inl.h',
- 'disk_cache/storage_block.h',
- 'disk_cache/stress_support.h',
- 'disk_cache/trace.cc',
- 'disk_cache/trace.h',
- 'disk_cache/tracing_cache_backend.cc',
- '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',
- 'disk_cache/simple/simple_entry_operation.h',
- 'disk_cache/simple/simple_histogram_macros.h' ,
- 'disk_cache/simple/simple_index.cc',
- 'disk_cache/simple/simple_index.h',
- 'disk_cache/simple/simple_index_delegate.h',
- 'disk_cache/simple/simple_index_file.cc',
- 'disk_cache/simple/simple_index_file.h',
- 'disk_cache/simple/simple_index_file_posix.cc',
- 'disk_cache/simple/simple_index_file_win.cc',
- 'disk_cache/simple/simple_net_log_parameters.cc',
- 'disk_cache/simple/simple_net_log_parameters.h',
- 'disk_cache/simple/simple_synchronous_entry.cc',
- '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',
- 'disk_cache/flash/internal_entry.cc',
- 'disk_cache/flash/internal_entry.h',
- 'disk_cache/flash/log_store.cc',
- 'disk_cache/flash/log_store.h',
- 'disk_cache/flash/log_store_entry.cc',
- 'disk_cache/flash/log_store_entry.h',
- 'disk_cache/flash/segment.cc',
- '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',
- 'dns/address_sorter_posix.h',
- 'dns/address_sorter_win.cc',
- 'dns/dns_client.cc',
- 'dns/dns_client.h',
- 'dns/dns_config_service.cc',
- 'dns/dns_config_service.h',
- 'dns/dns_config_service_posix.cc',
- 'dns/dns_config_service_posix.h',
- 'dns/dns_config_service_win.cc',
- 'dns/dns_config_service_win.h',
- 'dns/dns_config_watcher_mac.cc',
- 'dns/dns_config_watcher_mac.h',
- 'dns/dns_hosts.cc',
- 'dns/dns_hosts.h',
- 'dns/dns_protocol.h',
- 'dns/dns_query.cc',
- 'dns/dns_query.h',
- 'dns/dns_response.cc',
- 'dns/dns_response.h',
- 'dns/dns_session.cc',
- 'dns/dns_session.h',
- 'dns/dns_socket_pool.cc',
- 'dns/dns_socket_pool.h',
- 'dns/dns_transaction.cc',
- 'dns/dns_transaction.h',
- 'dns/host_cache.cc',
- 'dns/host_cache.h',
- 'dns/host_resolver.cc',
- 'dns/host_resolver.h',
- 'dns/host_resolver_impl.cc',
- 'dns/host_resolver_impl.h',
- 'dns/host_resolver_proc.cc',
- 'dns/host_resolver_proc.h',
- 'dns/mapped_host_resolver.cc',
- 'dns/mapped_host_resolver.h',
- 'dns/mdns_cache.cc',
- 'dns/mdns_cache.h',
- 'dns/mdns_client.cc',
- 'dns/mdns_client.h',
- 'dns/mdns_client_impl.cc',
- 'dns/mdns_client_impl.h',
- 'dns/notify_watcher_mac.cc',
- 'dns/notify_watcher_mac.h',
- 'dns/record_parsed.cc',
- 'dns/record_parsed.h',
- 'dns/record_rdata.cc',
- 'dns/record_rdata.h',
- 'dns/serial_worker.cc',
- 'dns/serial_worker.h',
- 'dns/single_request_host_resolver.cc',
- 'dns/single_request_host_resolver.h',
- 'ftp/ftp_auth_cache.cc',
- 'ftp/ftp_auth_cache.h',
- 'ftp/ftp_ctrl_response_buffer.cc',
- 'ftp/ftp_ctrl_response_buffer.h',
- 'ftp/ftp_directory_listing_parser.cc',
- 'ftp/ftp_directory_listing_parser.h',
- 'ftp/ftp_directory_listing_parser_ls.cc',
- 'ftp/ftp_directory_listing_parser_ls.h',
- 'ftp/ftp_directory_listing_parser_netware.cc',
- 'ftp/ftp_directory_listing_parser_netware.h',
- 'ftp/ftp_directory_listing_parser_os2.cc',
- 'ftp/ftp_directory_listing_parser_os2.h',
- 'ftp/ftp_directory_listing_parser_vms.cc',
- 'ftp/ftp_directory_listing_parser_vms.h',
- 'ftp/ftp_directory_listing_parser_windows.cc',
- 'ftp/ftp_directory_listing_parser_windows.h',
- 'ftp/ftp_network_layer.cc',
- 'ftp/ftp_network_layer.h',
- 'ftp/ftp_network_session.cc',
- 'ftp/ftp_network_session.h',
- 'ftp/ftp_network_transaction.cc',
- 'ftp/ftp_network_transaction.h',
- 'ftp/ftp_request_info.h',
- 'ftp/ftp_response_info.cc',
- 'ftp/ftp_response_info.h',
- 'ftp/ftp_server_type_histograms.cc',
- 'ftp/ftp_server_type_histograms.h',
- 'ftp/ftp_transaction.h',
- 'ftp/ftp_transaction_factory.h',
- 'ftp/ftp_util.cc',
- 'ftp/ftp_util.h',
- 'http/des.cc',
- 'http/des.h',
- 'http/http_atom_list.h',
- 'http/http_auth.cc',
- 'http/http_auth.h',
- 'http/http_auth_cache.cc',
- 'http/http_auth_cache.h',
- 'http/http_auth_controller.cc',
- 'http/http_auth_controller.h',
- 'http/http_auth_filter.cc',
- 'http/http_auth_filter.h',
- 'http/http_auth_filter_win.h',
- 'http/http_auth_gssapi_posix.cc',
- 'http/http_auth_gssapi_posix.h',
- 'http/http_auth_handler.cc',
- 'http/http_auth_handler.h',
- 'http/http_auth_handler_basic.cc',
- 'http/http_auth_handler_basic.h',
- 'http/http_auth_handler_digest.cc',
- 'http/http_auth_handler_digest.h',
- 'http/http_auth_handler_factory.cc',
- 'http/http_auth_handler_factory.h',
- 'http/http_auth_handler_negotiate.cc',
- 'http/http_auth_handler_negotiate.h',
- 'http/http_auth_handler_ntlm.cc',
- 'http/http_auth_handler_ntlm.h',
- 'http/http_auth_handler_ntlm_portable.cc',
- 'http/http_auth_handler_ntlm_win.cc',
- 'http/http_auth_sspi_win.cc',
- 'http/http_auth_sspi_win.h',
- 'http/http_basic_state.cc',
- 'http/http_basic_state.h',
- 'http/http_basic_stream.cc',
- 'http/http_basic_stream.h',
- 'http/http_byte_range.cc',
- 'http/http_byte_range.h',
- 'http/http_cache.cc',
- 'http/http_cache.h',
- 'http/http_cache_transaction.cc',
- 'http/http_cache_transaction.h',
- 'http/http_content_disposition.cc',
- 'http/http_content_disposition.h',
- 'http/http_chunked_decoder.cc',
- 'http/http_chunked_decoder.h',
- 'http/http_network_layer.cc',
- 'http/http_network_layer.h',
- 'http/http_network_session.cc',
- 'http/http_network_session.h',
- 'http/http_network_session_peer.cc',
- 'http/http_network_session_peer.h',
- 'http/http_network_transaction.cc',
- 'http/http_network_transaction.h',
- 'http/http_pipelined_connection.h',
- 'http/http_pipelined_connection_impl.cc',
- 'http/http_pipelined_connection_impl.h',
- 'http/http_pipelined_host.cc',
- 'http/http_pipelined_host.h',
- 'http/http_pipelined_host_capability.h',
- 'http/http_pipelined_host_forced.cc',
- 'http/http_pipelined_host_forced.h',
- 'http/http_pipelined_host_impl.cc',
- 'http/http_pipelined_host_impl.h',
- 'http/http_pipelined_host_pool.cc',
- 'http/http_pipelined_host_pool.h',
- 'http/http_pipelined_stream.cc',
- 'http/http_pipelined_stream.h',
- 'http/http_proxy_client_socket.cc',
- 'http/http_proxy_client_socket.h',
- 'http/http_proxy_client_socket_pool.cc',
- 'http/http_proxy_client_socket_pool.h',
- 'http/http_request_headers.cc',
- 'http/http_request_headers.h',
- 'http/http_request_info.cc',
- 'http/http_request_info.h',
- 'http/http_response_body_drainer.cc',
- 'http/http_response_body_drainer.h',
- 'http/http_response_headers.cc',
- 'http/http_response_headers.h',
- 'http/http_response_info.cc',
- 'http/http_response_info.h',
- 'http/http_security_headers.cc',
- 'http/http_security_headers.h',
- 'http/http_server_properties.cc',
- 'http/http_server_properties.h',
- 'http/http_server_properties_impl.cc',
- 'http/http_server_properties_impl.h',
- 'http/http_status_code.cc',
- 'http/http_status_code.h',
- 'http/http_stream.h',
- 'http/http_stream_base.h',
- 'http/http_stream_factory.cc',
- 'http/http_stream_factory.h',
- 'http/http_stream_factory_impl.cc',
- 'http/http_stream_factory_impl.h',
- 'http/http_stream_factory_impl_job.cc',
- 'http/http_stream_factory_impl_job.h',
- 'http/http_stream_factory_impl_request.cc',
- 'http/http_stream_factory_impl_request.h',
- 'http/http_stream_parser.cc',
- 'http/http_stream_parser.h',
- 'http/http_transaction.h',
- 'http/http_transaction_delegate.h',
- 'http/http_transaction_factory.h',
- 'http/http_util.cc',
- 'http/http_util.h',
- 'http/http_util_icu.cc',
- 'http/http_vary_data.cc',
- 'http/http_vary_data.h',
- 'http/http_version.h',
- 'http/md4.cc',
- 'http/md4.h',
- 'http/partial_data.cc',
- 'http/partial_data.h',
- 'http/proxy_client_socket.h',
- 'http/proxy_client_socket.cc',
- 'http/proxy_connect_redirect_http_stream.h',
- 'http/proxy_connect_redirect_http_stream.cc',
- 'http/transport_security_persister.cc',
- 'http/transport_security_persister.h',
- 'http/transport_security_state.cc',
- 'http/transport_security_state.h',
- 'http/transport_security_state_static.h',
- 'http/url_security_manager.cc',
- 'http/url_security_manager.h',
- 'http/url_security_manager_posix.cc',
- 'http/url_security_manager_win.cc',
- 'ocsp/nss_ocsp.cc',
- 'ocsp/nss_ocsp.h',
- 'proxy/dhcp_proxy_script_adapter_fetcher_win.cc',
- 'proxy/dhcp_proxy_script_adapter_fetcher_win.h',
- 'proxy/dhcp_proxy_script_fetcher.cc',
- 'proxy/dhcp_proxy_script_fetcher.h',
- 'proxy/dhcp_proxy_script_fetcher_factory.cc',
- 'proxy/dhcp_proxy_script_fetcher_factory.h',
- 'proxy/dhcp_proxy_script_fetcher_win.cc',
- 'proxy/dhcp_proxy_script_fetcher_win.h',
- 'proxy/dhcpcsvc_init_win.cc',
- 'proxy/dhcpcsvc_init_win.h',
- 'proxy/multi_threaded_proxy_resolver.cc',
- 'proxy/multi_threaded_proxy_resolver.h',
- 'proxy/network_delegate_error_observer.cc',
- 'proxy/network_delegate_error_observer.h',
- 'proxy/polling_proxy_config_service.cc',
- 'proxy/polling_proxy_config_service.h',
- 'proxy/proxy_bypass_rules.cc',
- 'proxy/proxy_bypass_rules.h',
- 'proxy/proxy_config.cc',
- 'proxy/proxy_config.h',
- 'proxy/proxy_config_service.h',
- 'proxy/proxy_config_service_android.cc',
- 'proxy/proxy_config_service_android.h',
- 'proxy/proxy_config_service_fixed.cc',
- 'proxy/proxy_config_service_fixed.h',
- 'proxy/proxy_config_service_ios.cc',
- 'proxy/proxy_config_service_ios.h',
- 'proxy/proxy_config_service_linux.cc',
- 'proxy/proxy_config_service_linux.h',
- 'proxy/proxy_config_service_mac.cc',
- 'proxy/proxy_config_service_mac.h',
- 'proxy/proxy_config_service_win.cc',
- 'proxy/proxy_config_service_win.h',
- 'proxy/proxy_config_source.cc',
- 'proxy/proxy_config_source.h',
- 'proxy/proxy_info.cc',
- 'proxy/proxy_info.h',
- 'proxy/proxy_list.cc',
- 'proxy/proxy_list.h',
- 'proxy/proxy_resolver.h',
- 'proxy/proxy_resolver_error_observer.h',
- 'proxy/proxy_resolver_mac.cc',
- 'proxy/proxy_resolver_mac.h',
- 'proxy/proxy_resolver_script.h',
- 'proxy/proxy_resolver_script_data.cc',
- 'proxy/proxy_resolver_script_data.h',
- 'proxy/proxy_resolver_winhttp.cc',
- 'proxy/proxy_resolver_winhttp.h',
- 'proxy/proxy_retry_info.h',
- 'proxy/proxy_script_decider.cc',
- 'proxy/proxy_script_decider.h',
- 'proxy/proxy_script_fetcher.h',
- 'proxy/proxy_script_fetcher_impl.cc',
- 'proxy/proxy_script_fetcher_impl.h',
- 'proxy/proxy_server.cc',
- 'proxy/proxy_server.h',
- 'proxy/proxy_server_mac.cc',
- 'proxy/proxy_service.cc',
- 'proxy/proxy_service.h',
- 'quic/congestion_control/available_channel_estimator.cc',
- 'quic/congestion_control/available_channel_estimator.h',
- 'quic/congestion_control/channel_estimator.cc',
- 'quic/congestion_control/channel_estimator.h',
- 'quic/congestion_control/cube_root.cc',
- 'quic/congestion_control/cube_root.h',
- 'quic/congestion_control/cubic.cc',
- 'quic/congestion_control/cubic.h',
- 'quic/congestion_control/fix_rate_receiver.cc',
- 'quic/congestion_control/fix_rate_receiver.h',
- 'quic/congestion_control/fix_rate_sender.cc',
- 'quic/congestion_control/fix_rate_sender.h',
- 'quic/congestion_control/hybrid_slow_start.cc',
- 'quic/congestion_control/hybrid_slow_start.h',
- 'quic/congestion_control/inter_arrival_bitrate_ramp_up.cc',
- 'quic/congestion_control/inter_arrival_bitrate_ramp_up.h',
- 'quic/congestion_control/inter_arrival_overuse_detector.cc',
- 'quic/congestion_control/inter_arrival_overuse_detector.h',
- 'quic/congestion_control/inter_arrival_probe.cc',
- 'quic/congestion_control/inter_arrival_probe.h',
- 'quic/congestion_control/inter_arrival_receiver.cc',
- 'quic/congestion_control/inter_arrival_receiver.h',
- 'quic/congestion_control/inter_arrival_sender.cc',
- 'quic/congestion_control/inter_arrival_sender.h',
- 'quic/congestion_control/inter_arrival_state_machine.cc',
- 'quic/congestion_control/inter_arrival_state_machine.h',
- 'quic/congestion_control/leaky_bucket.cc',
- 'quic/congestion_control/leaky_bucket.h',
- 'quic/congestion_control/paced_sender.cc',
- 'quic/congestion_control/paced_sender.h',
- 'quic/congestion_control/pacing_sender.cc',
- 'quic/congestion_control/pacing_sender.h',
- 'quic/congestion_control/quic_max_sized_map.h',
- 'quic/congestion_control/receive_algorithm_interface.cc',
- 'quic/congestion_control/receive_algorithm_interface.h',
- 'quic/congestion_control/send_algorithm_interface.cc',
- 'quic/congestion_control/send_algorithm_interface.h',
- 'quic/congestion_control/tcp_cubic_sender.cc',
- 'quic/congestion_control/tcp_cubic_sender.h',
- 'quic/congestion_control/tcp_receiver.cc',
- 'quic/congestion_control/tcp_receiver.h',
- 'quic/crypto/aes_128_gcm_12_decrypter.h',
- 'quic/crypto/aes_128_gcm_12_decrypter_nss.cc',
- 'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
- 'quic/crypto/aes_128_gcm_12_encrypter.h',
- 'quic/crypto/aes_128_gcm_12_encrypter_nss.cc',
- 'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc',
- 'quic/crypto/cert_compressor.cc',
- 'quic/crypto/cert_compressor.h',
- 'quic/crypto/channel_id.cc',
- 'quic/crypto/channel_id.h',
- 'quic/crypto/channel_id_nss.cc',
- 'quic/crypto/channel_id_openssl.cc',
- 'quic/crypto/common_cert_set.cc',
- 'quic/crypto/common_cert_set.h',
- 'quic/crypto/crypto_framer.cc',
- 'quic/crypto/crypto_framer.h',
- 'quic/crypto/crypto_handshake.cc',
- 'quic/crypto/crypto_handshake.h',
- 'quic/crypto/crypto_protocol.h',
- 'quic/crypto/crypto_secret_boxer.cc',
- 'quic/crypto/crypto_secret_boxer.h',
- 'quic/crypto/crypto_server_config_protobuf.cc',
- 'quic/crypto/crypto_server_config_protobuf.h',
- 'quic/crypto/crypto_utils.cc',
- 'quic/crypto/crypto_utils.h',
- 'quic/crypto/curve25519_key_exchange.cc',
- 'quic/crypto/curve25519_key_exchange.h',
- 'quic/crypto/ephemeral_key_source.h',
- 'quic/crypto/key_exchange.h',
- 'quic/crypto/local_strike_register_client.cc',
- 'quic/crypto/local_strike_register_client.h',
- 'quic/crypto/null_decrypter.cc',
- 'quic/crypto/null_decrypter.h',
- 'quic/crypto/null_encrypter.cc',
- 'quic/crypto/null_encrypter.h',
- 'quic/crypto/p256_key_exchange.h',
- 'quic/crypto/p256_key_exchange_nss.cc',
- 'quic/crypto/p256_key_exchange_openssl.cc',
- 'quic/crypto/proof_source.h',
- 'quic/crypto/proof_source_chromium.cc',
- 'quic/crypto/proof_source_chromium.h',
- 'quic/crypto/proof_verifier.cc',
- 'quic/crypto/proof_verifier_chromium.cc',
- 'quic/crypto/proof_verifier_chromium.h',
- 'quic/crypto/quic_crypto_client_config.cc',
- 'quic/crypto/quic_crypto_client_config.h',
- 'quic/crypto/quic_crypto_server_config.cc',
- 'quic/crypto/quic_crypto_server_config.h',
- 'quic/crypto/quic_decrypter.cc',
- 'quic/crypto/quic_decrypter.h',
- 'quic/crypto/quic_encrypter.cc',
- 'quic/crypto/quic_encrypter.h',
- 'quic/crypto/quic_random.cc',
- 'quic/crypto/quic_random.h',
- 'quic/crypto/scoped_evp_cipher_ctx.cc',
- 'quic/crypto/scoped_evp_cipher_ctx.h',
- 'quic/crypto/strike_register.cc',
- 'quic/crypto/strike_register.h',
- 'quic/crypto/strike_register_client.h',
- 'quic/crypto/source_address_token.cc',
- 'quic/crypto/source_address_token.h',
- 'quic/iovector.cc',
- 'quic/iovector.h',
- 'quic/port_suggester.cc',
- 'quic/port_suggester.h',
- 'quic/quic_ack_notifier.cc',
- 'quic/quic_ack_notifier.h',
- 'quic/quic_ack_notifier_manager.cc',
- 'quic/quic_ack_notifier_manager.h',
- 'quic/quic_alarm.cc',
- 'quic/quic_alarm.h',
- 'quic/quic_bandwidth.cc',
- 'quic/quic_bandwidth.h',
- 'quic/quic_blocked_writer_interface.h',
- 'quic/quic_client_session.cc',
- 'quic/quic_client_session.h',
- 'quic/quic_clock.cc',
- 'quic/quic_clock.h',
- 'quic/quic_config.cc',
- 'quic/quic_config.h',
- 'quic/quic_connection.cc',
- 'quic/quic_connection.h',
- 'quic/quic_connection_helper.cc',
- 'quic/quic_connection_helper.h',
- 'quic/quic_connection_logger.cc',
- 'quic/quic_connection_logger.h',
- 'quic/quic_connection_stats.cc',
- 'quic/quic_connection_stats.h',
- 'quic/quic_crypto_client_stream.cc',
- 'quic/quic_crypto_client_stream.h',
- 'quic/quic_crypto_client_stream_factory.h',
- 'quic/quic_crypto_server_stream.cc',
- 'quic/quic_crypto_server_stream.h',
- 'quic/quic_crypto_stream.cc',
- 'quic/quic_crypto_stream.h',
- 'quic/quic_data_reader.cc',
- 'quic/quic_data_reader.h',
- 'quic/quic_data_stream.cc',
- 'quic/quic_data_stream.h',
- 'quic/quic_data_writer.cc',
- 'quic/quic_data_writer.h',
- 'quic/quic_default_packet_writer.cc',
- 'quic/quic_default_packet_writer.h',
- 'quic/quic_fec_group.cc',
- 'quic/quic_fec_group.h',
- 'quic/quic_framer.cc',
- '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',
- 'quic/quic_packet_generator.h',
- 'quic/quic_packet_writer.h',
- 'quic/quic_protocol.cc',
- 'quic/quic_protocol.h',
- 'quic/quic_received_packet_manager.cc',
- 'quic/quic_received_packet_manager.h',
- 'quic/quic_reliable_client_stream.cc',
- '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',
- 'quic/quic_spdy_compressor.h',
- 'quic/quic_spdy_decompressor.cc',
- 'quic/quic_spdy_decompressor.h',
- 'quic/quic_stream_factory.cc',
- 'quic/quic_stream_factory.h',
- 'quic/quic_stream_sequencer.cc',
- 'quic/quic_stream_sequencer.h',
- 'quic/quic_time.cc',
- 'quic/quic_time.h',
- 'quic/quic_utils.cc',
- 'quic/quic_utils.h',
- 'quic/reliable_quic_stream.cc',
- 'quic/reliable_quic_stream.h',
- 'quic/spdy_utils.cc',
- 'quic/spdy_utils.h',
- 'socket/buffered_write_stream_socket.cc',
- 'socket/buffered_write_stream_socket.h',
- 'socket/client_socket_factory.cc',
- 'socket/client_socket_factory.h',
- 'socket/client_socket_handle.cc',
- 'socket/client_socket_handle.h',
- 'socket/client_socket_pool.cc',
- 'socket/client_socket_pool.h',
- 'socket/client_socket_pool_base.cc',
- 'socket/client_socket_pool_base.h',
- 'socket/client_socket_pool_histograms.cc',
- 'socket/client_socket_pool_histograms.h',
- 'socket/client_socket_pool_manager.cc',
- 'socket/client_socket_pool_manager.h',
- 'socket/client_socket_pool_manager_impl.cc',
- 'socket/client_socket_pool_manager_impl.h',
- 'socket/next_proto.h',
- 'socket/nss_ssl_util.cc',
- 'socket/nss_ssl_util.h',
- 'socket/server_socket.h',
- 'socket/socket_descriptor.cc',
- 'socket/socket_descriptor.h',
- 'socket/socket_net_log_params.cc',
- 'socket/socket_net_log_params.h',
- 'socket/socket.h',
- 'socket/socks5_client_socket.cc',
- 'socket/socks5_client_socket.h',
- 'socket/socks_client_socket.cc',
- 'socket/socks_client_socket.h',
- 'socket/socks_client_socket_pool.cc',
- 'socket/socks_client_socket_pool.h',
- 'socket/ssl_client_socket.cc',
- 'socket/ssl_client_socket.h',
- 'socket/ssl_client_socket_nss.cc',
- 'socket/ssl_client_socket_nss.h',
- 'socket/ssl_client_socket_openssl.cc',
- 'socket/ssl_client_socket_openssl.h',
- 'socket/ssl_client_socket_pool.cc',
- 'socket/ssl_client_socket_pool.h',
- 'socket/ssl_error_params.cc',
- 'socket/ssl_error_params.h',
- 'socket/ssl_server_socket.h',
- 'socket/ssl_server_socket_nss.cc',
- 'socket/ssl_server_socket_nss.h',
- 'socket/ssl_server_socket_openssl.cc',
- 'socket/ssl_session_cache_openssl.cc',
- 'socket/ssl_session_cache_openssl.h',
- 'socket/ssl_socket.h',
- 'socket/stream_listen_socket.cc',
- 'socket/stream_listen_socket.h',
- 'socket/stream_socket.cc',
- 'socket/stream_socket.h',
- 'socket/tcp_client_socket.cc',
- 'socket/tcp_client_socket.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',
- 'socket/tcp_socket_win.cc',
- 'socket/tcp_socket_win.h',
- 'socket/transport_client_socket_pool.cc',
- 'socket/transport_client_socket_pool.h',
- 'socket/unix_domain_socket_posix.cc',
- 'socket/unix_domain_socket_posix.h',
- 'socket_stream/socket_stream.cc',
- 'socket_stream/socket_stream.h',
- 'socket_stream/socket_stream_job.cc',
- 'socket_stream/socket_stream_job.h',
- 'socket_stream/socket_stream_job_manager.cc',
- 'socket_stream/socket_stream_job_manager.h',
- 'socket_stream/socket_stream_metrics.cc',
- 'socket_stream/socket_stream_metrics.h',
- 'spdy/buffered_spdy_framer.cc',
- 'spdy/buffered_spdy_framer.h',
- 'spdy/spdy_bitmasks.h',
- 'spdy/spdy_buffer.cc',
- 'spdy/spdy_buffer.h',
- 'spdy/spdy_buffer_producer.cc',
- 'spdy/spdy_buffer_producer.h',
- 'spdy/spdy_frame_builder.cc',
- 'spdy/spdy_frame_builder.h',
- 'spdy/spdy_frame_reader.cc',
- 'spdy/spdy_frame_reader.h',
- 'spdy/spdy_framer.cc',
- 'spdy/spdy_framer.h',
- 'spdy/spdy_header_block.cc',
- 'spdy/spdy_header_block.h',
- 'spdy/spdy_http_stream.cc',
- 'spdy/spdy_http_stream.h',
- 'spdy/spdy_http_utils.cc',
- 'spdy/spdy_http_utils.h',
- 'spdy/spdy_priority_forest.h',
- 'spdy/spdy_protocol.cc',
- 'spdy/spdy_protocol.h',
- 'spdy/spdy_proxy_client_socket.cc',
- 'spdy/spdy_proxy_client_socket.h',
- 'spdy/spdy_read_queue.cc',
- 'spdy/spdy_read_queue.h',
- 'spdy/spdy_session.cc',
- 'spdy/spdy_session.h',
- 'spdy/spdy_session_key.cc',
- 'spdy/spdy_session_key.h',
- 'spdy/spdy_session_pool.cc',
- 'spdy/spdy_session_pool.h',
- 'spdy/spdy_stream.cc',
- 'spdy/spdy_stream.h',
- 'spdy/spdy_websocket_stream.cc',
- 'spdy/spdy_websocket_stream.h',
- 'spdy/spdy_write_queue.cc',
- 'spdy/spdy_write_queue.h',
- 'spdy/write_blocked_list.h',
- 'ssl/client_cert_store.h',
- 'ssl/client_cert_store_mac.cc',
- 'ssl/client_cert_store_mac.h',
- 'ssl/client_cert_store_nss.cc',
- 'ssl/client_cert_store_nss.h',
- 'ssl/client_cert_store_win.cc',
- 'ssl/client_cert_store_win.h',
- 'ssl/default_server_bound_cert_store.cc',
- 'ssl/default_server_bound_cert_store.h',
- 'ssl/openssl_client_key_store.cc',
- 'ssl/openssl_client_key_store.h',
- 'ssl/server_bound_cert_service.cc',
- 'ssl/server_bound_cert_service.h',
- 'ssl/server_bound_cert_store.cc',
- 'ssl/server_bound_cert_store.h',
- 'ssl/signed_certificate_timestamp_and_status.cc',
- 'ssl/signed_certificate_timestamp_and_status.h',
- 'ssl/ssl_cert_request_info.cc',
- 'ssl/ssl_cert_request_info.h',
- 'ssl/ssl_cipher_suite_names.cc',
- 'ssl/ssl_cipher_suite_names.h',
- 'ssl/ssl_client_auth_cache.cc',
- 'ssl/ssl_client_auth_cache.h',
- 'ssl/ssl_client_cert_type.h',
- 'ssl/ssl_config_service.cc',
- 'ssl/ssl_config_service.h',
- 'ssl/ssl_config_service_defaults.cc',
- 'ssl/ssl_config_service_defaults.h',
- 'ssl/ssl_info.cc',
- 'ssl/ssl_info.h',
- 'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
- 'third_party/mozilla_security_manager/nsKeygenHandler.h',
- 'third_party/mozilla_security_manager/nsNSSCertificateDB.cpp',
- 'third_party/mozilla_security_manager/nsNSSCertificateDB.h',
- 'third_party/mozilla_security_manager/nsPKCS12Blob.cpp',
- 'third_party/mozilla_security_manager/nsPKCS12Blob.h',
- 'udp/datagram_client_socket.h',
- 'udp/datagram_server_socket.h',
- 'udp/datagram_socket.h',
- 'udp/udp_client_socket.cc',
- 'udp/udp_client_socket.h',
- 'udp/udp_net_log_parameters.cc',
- 'udp/udp_net_log_parameters.h',
- 'udp/udp_server_socket.cc',
- 'udp/udp_server_socket.h',
- 'udp/udp_socket.h',
- 'udp/udp_socket_libevent.cc',
- 'udp/udp_socket_libevent.h',
- 'udp/udp_socket_win.cc',
- 'udp/udp_socket_win.h',
- 'url_request/data_protocol_handler.cc',
- 'url_request/data_protocol_handler.h',
- 'url_request/file_protocol_handler.cc',
- 'url_request/file_protocol_handler.h',
- 'url_request/fraudulent_certificate_reporter.h',
- 'url_request/ftp_protocol_handler.cc',
- 'url_request/ftp_protocol_handler.h',
- 'url_request/http_user_agent_settings.h',
- 'url_request/protocol_intercept_job_factory.cc',
- 'url_request/protocol_intercept_job_factory.h',
- 'url_request/static_http_user_agent_settings.cc',
- 'url_request/static_http_user_agent_settings.h',
- 'url_request/url_fetcher.cc',
- 'url_request/url_fetcher.h',
- 'url_request/url_fetcher_core.cc',
- 'url_request/url_fetcher_core.h',
- 'url_request/url_fetcher_delegate.cc',
- 'url_request/url_fetcher_delegate.h',
- 'url_request/url_fetcher_factory.h',
- 'url_request/url_fetcher_impl.cc',
- 'url_request/url_fetcher_impl.h',
- 'url_request/url_fetcher_response_writer.cc',
- 'url_request/url_fetcher_response_writer.h',
- 'url_request/url_request.cc',
- 'url_request/url_request.h',
- 'url_request/url_request_about_job.cc',
- 'url_request/url_request_about_job.h',
- 'url_request/url_request_context.cc',
- 'url_request/url_request_context.h',
- 'url_request/url_request_context_builder.cc',
- 'url_request/url_request_context_builder.h',
- 'url_request/url_request_context_getter.cc',
- 'url_request/url_request_context_getter.h',
- 'url_request/url_request_context_storage.cc',
- 'url_request/url_request_context_storage.h',
- 'url_request/url_request_data_job.cc',
- 'url_request/url_request_data_job.h',
- 'url_request/url_request_error_job.cc',
- 'url_request/url_request_error_job.h',
- 'url_request/url_request_file_dir_job.cc',
- 'url_request/url_request_file_dir_job.h',
- 'url_request/url_request_file_job.cc',
- 'url_request/url_request_file_job.h',
- 'url_request/url_request_filter.cc',
- 'url_request/url_request_filter.h',
- 'url_request/url_request_ftp_job.cc',
- 'url_request/url_request_ftp_job.h',
- 'url_request/url_request_http_job.cc',
- 'url_request/url_request_http_job.h',
- 'url_request/url_request_job.cc',
- 'url_request/url_request_job.h',
- 'url_request/url_request_job_factory.cc',
- 'url_request/url_request_job_factory.h',
- 'url_request/url_request_job_factory_impl.cc',
- 'url_request/url_request_job_factory_impl.h',
- 'url_request/url_request_job_manager.cc',
- 'url_request/url_request_job_manager.h',
- 'url_request/url_request_netlog_params.cc',
- 'url_request/url_request_netlog_params.h',
- 'url_request/url_request_redirect_job.cc',
- 'url_request/url_request_redirect_job.h',
- 'url_request/url_request_simple_job.cc',
- 'url_request/url_request_simple_job.h',
- 'url_request/url_request_status.h',
- 'url_request/url_request_test_job.cc',
- 'url_request/url_request_test_job.h',
- 'url_request/url_request_throttler_entry.cc',
- 'url_request/url_request_throttler_entry.h',
- 'url_request/url_request_throttler_entry_interface.h',
- 'url_request/url_request_throttler_header_adapter.cc',
- 'url_request/url_request_throttler_header_adapter.h',
- 'url_request/url_request_throttler_header_interface.h',
- 'url_request/url_request_throttler_manager.cc',
- 'url_request/url_request_throttler_manager.h',
- 'url_request/view_cache_helper.cc',
- 'url_request/view_cache_helper.h',
- 'url_request/websocket_handshake_userdata_key.cc',
- 'url_request/websocket_handshake_userdata_key.h',
- 'websockets/websocket_basic_handshake_stream.cc',
- 'websockets/websocket_basic_handshake_stream.h',
- 'websockets/websocket_basic_stream.cc',
- 'websockets/websocket_basic_stream.h',
- 'websockets/websocket_channel.cc',
- 'websockets/websocket_channel.h',
- 'websockets/websocket_deflate_predictor.h',
- 'websockets/websocket_deflate_predictor_impl.cc',
- 'websockets/websocket_deflate_predictor_impl.h',
- 'websockets/websocket_deflate_stream.cc',
- 'websockets/websocket_deflate_stream.h',
- 'websockets/websocket_deflater.cc',
- 'websockets/websocket_deflater.h',
- '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',
- 'websockets/websocket_frame_parser.h',
- 'websockets/websocket_handshake_constants.cc',
- 'websockets/websocket_handshake_constants.h',
- 'websockets/websocket_handshake_handler.cc',
- 'websockets/websocket_handshake_handler.h',
- 'websockets/websocket_handshake_stream_base.h',
- 'websockets/websocket_handshake_stream_create_helper.cc',
- 'websockets/websocket_handshake_stream_create_helper.h',
- 'websockets/websocket_inflater.cc',
- 'websockets/websocket_inflater.h',
- 'websockets/websocket_job.cc',
- 'websockets/websocket_job.h',
- 'websockets/websocket_mux.h',
- 'websockets/websocket_net_log_params.cc',
- 'websockets/websocket_net_log_params.h',
- 'websockets/websocket_stream.cc',
- 'websockets/websocket_stream.h',
- 'websockets/websocket_throttle.cc',
- 'websockets/websocket_throttle.h',
+ '<@(net_nacl_common_sources)',
+ '<@(net_non_nacl_sources)',
],
'defines': [
'NET_IMPLEMENTATION',
@@ -1254,11 +158,26 @@
],
},
'sources!': [
- 'disk_cache/mapped_file_posix.cc',
+ 'disk_cache/blockfile/mapped_file_posix.cc',
],
}, { # else
'sources!': [
- 'disk_cache/mapped_file_avoid_mmap_posix.cc',
+ 'disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc',
+ ],
+ }],
+ ['disable_file_support==1', {
+ # TODO(mmenke): Should probably get rid of the dependency on
+ # net_resources in this case (It's used in net_util, to format
+ # directory listings. Also used outside of net/).
+ 'sources!': [
+ 'base/directory_lister.cc',
+ 'base/directory_lister.h',
+ 'url_request/url_request_file_dir_job.cc',
+ 'url_request/url_request_file_dir_job.h',
+ 'url_request/url_request_file_job.cc',
+ 'url_request/url_request_file_job.h',
+ 'url_request/file_protocol_handler.cc',
+ 'url_request/file_protocol_handler.h',
],
}],
['disable_ftp_support==1', {
@@ -1302,6 +221,10 @@
'cert/jwk_serializer_nss.cc',
'cert/nss_cert_database.cc',
'cert/nss_cert_database.h',
+ 'cert/nss_cert_database_chromeos.cc',
+ 'cert/nss_cert_database_chromeos.h',
+ 'cert/nss_profile_filter_chromeos.cc',
+ 'cert/nss_profile_filter_chromeos.h',
'cert/scoped_nss_types.h',
'cert/test_root_certs_nss.cc',
'cert/x509_certificate_nss.cc',
@@ -1309,8 +232,12 @@
'cert/x509_util_nss.h',
'ocsp/nss_ocsp.cc',
'ocsp/nss_ocsp.h',
+ 'quic/crypto/aead_base_decrypter_nss.cc',
+ 'quic/crypto/aead_base_encrypter_nss.cc',
'quic/crypto/aes_128_gcm_12_decrypter_nss.cc',
'quic/crypto/aes_128_gcm_12_encrypter_nss.cc',
+ 'quic/crypto/chacha20_poly1305_decrypter_nss.cc',
+ 'quic/crypto/chacha20_poly1305_encrypter_nss.cc',
'quic/crypto/channel_id_nss.cc',
'quic/crypto/p256_key_exchange_nss.cc',
'socket/nss_ssl_util.cc',
@@ -1326,40 +253,54 @@
'third_party/mozilla_security_manager/nsPKCS12Blob.cpp',
'third_party/mozilla_security_manager/nsPKCS12Blob.h',
],
+ 'dependencies': [
+ '../third_party/openssl/openssl.gyp:openssl',
+ ],
},
{ # else !use_openssl: remove the unneeded files
'sources!': [
'base/crypto_module_openssl.cc',
- 'base/keygen_handler_openssl.cc',
- 'base/openssl_private_key_store.h',
- 'base/openssl_private_key_store_android.cc',
- 'base/openssl_private_key_store_memory.cc',
- 'cert/cert_database_openssl.cc',
- 'cert/cert_verify_proc_openssl.cc',
- 'cert/cert_verify_proc_openssl.h',
'cert/ct_log_verifier_openssl.cc',
'cert/ct_objects_extractor_openssl.cc',
'cert/jwk_serializer_openssl.cc',
- 'cert/test_root_certs_openssl.cc',
- 'cert/x509_certificate_openssl.cc',
'cert/x509_util_openssl.cc',
'cert/x509_util_openssl.h',
+ 'quic/crypto/aead_base_decrypter_openssl.cc',
+ 'quic/crypto/aead_base_encrypter_openssl.cc',
'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc',
+ 'quic/crypto/chacha20_poly1305_decrypter_openssl.cc',
+ 'quic/crypto/chacha20_poly1305_encrypter_openssl.cc',
'quic/crypto/channel_id_openssl.cc',
'quic/crypto/p256_key_exchange_openssl.cc',
- 'quic/crypto/scoped_evp_cipher_ctx.cc',
- 'quic/crypto/scoped_evp_cipher_ctx.h',
+ 'quic/crypto/scoped_evp_aead_ctx.cc',
+ 'quic/crypto/scoped_evp_aead_ctx.h',
+ 'socket/openssl_ssl_util.cc',
+ 'socket/openssl_ssl_util.h',
'socket/ssl_client_socket_openssl.cc',
'socket/ssl_client_socket_openssl.h',
'socket/ssl_server_socket_openssl.cc',
+ 'socket/ssl_server_socket_openssl.h',
'socket/ssl_session_cache_openssl.cc',
'socket/ssl_session_cache_openssl.h',
- 'ssl/openssl_client_key_store.cc',
- 'ssl/openssl_client_key_store.h',
],
},
],
+ [ 'use_openssl_certs == 0', {
+ 'sources!': [
+ 'base/keygen_handler_openssl.cc',
+ 'base/openssl_private_key_store.h',
+ 'base/openssl_private_key_store_android.cc',
+ 'base/openssl_private_key_store_memory.cc',
+ 'cert/cert_database_openssl.cc',
+ 'cert/cert_verify_proc_openssl.cc',
+ 'cert/cert_verify_proc_openssl.h',
+ 'cert/test_root_certs_openssl.cc',
+ 'cert/x509_certificate_openssl.cc',
+ 'ssl/openssl_client_key_store.cc',
+ 'ssl/openssl_client_key_store.h',
+ ],
+ }],
[ 'use_glib == 1', {
'dependencies': [
'../build/linux/system.gyp:gconf',
@@ -1368,12 +309,8 @@
}],
[ 'desktop_linux == 1 or chromeos == 1', {
'conditions': [
- ['use_openssl==1', {
- 'dependencies': [
- '../third_party/openssl/openssl.gyp:openssl',
- ],
- },
- { # else use_openssl==0, use NSS
+ ['use_openssl == 0', {
+ # use NSS
'dependencies': [
'../build/linux/system.gyp:ssl',
],
@@ -1418,17 +355,14 @@
],
},
],
- [ 'toolkit_uses_gtk == 1', {
- 'dependencies': [
- '../build/linux/system.gyp:gdk',
- ],
- }],
[ 'use_nss != 1', {
'sources!': [
'cert/cert_verify_proc_nss.cc',
'cert/cert_verify_proc_nss.h',
'ssl/client_cert_store_nss.cc',
'ssl/client_cert_store_nss.h',
+ 'ssl/client_cert_store_chromeos.cc',
+ 'ssl/client_cert_store_chromeos.h',
],
}],
[ 'enable_websockets != 1', {
@@ -1482,10 +416,15 @@
},
],
[ 'OS == "mac"', {
- 'dependencies': [
- '../third_party/nss/nss.gyp:nspr',
- '../third_party/nss/nss.gyp:nss',
- 'third_party/nss/ssl.gyp:libssl',
+ 'conditions': [
+ [ 'use_openssl == 0', {
+ 'dependencies': [
+ # defaults to nss
+ '../third_party/nss/nss.gyp:nspr',
+ '../third_party/nss/nss.gyp:nss',
+ 'third_party/nss/ssl.gyp:libssl',
+ ],
+ }],
],
'link_settings': {
'libraries': [
@@ -1502,6 +441,9 @@
'../third_party/nss/nss.gyp:nss',
'third_party/nss/ssl.gyp:libssl',
],
+ 'sources!': [
+ 'disk_cache/blockfile/file_posix.cc',
+ ],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/CFNetwork.framework',
@@ -1510,9 +452,6 @@
'$(SDKROOT)/System/Library/Frameworks/SystemConfiguration.framework',
'$(SDKROOT)/usr/lib/libresolv.dylib',
],
- 'sources!': [
- 'disk_cache/file_posix.cc',
- ],
},
},
],
@@ -1539,6 +478,23 @@
],
},
],
+ [ 'use_icu_alternatives_on_android == 1', {
+ 'dependencies!': [
+ '../base/base.gyp:base_i18n',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'sources!': [
+ 'base/filename_util_icu.cc',
+ 'base/net_string_util_icu.cc',
+ 'base/net_util_icu.cc',
+ ],
+ 'sources': [
+ 'base/net_string_util_icu_alternatives_android.cc',
+ 'base/net_string_util_icu_alternatives_android.h',
+ ],
+ },
+ ],
],
'target_conditions': [
# These source files are excluded by default platform rules, but they
@@ -1548,6 +504,8 @@
['OS == "android"', {
'sources/': [
['include', '^base/platform_mime_util_linux\\.cc$'],
+ ['include', '^base/address_tracker_linux\\.cc$'],
+ ['include', '^base/address_tracker_linux\\.h$'],
],
}],
['OS == "ios"', {
@@ -1583,464 +541,24 @@
'../testing/gtest.gyp:gtest',
'../third_party/zlib/zlib.gyp:zlib',
'../url/url.gyp:url_lib',
+ 'balsa',
'http_server',
'net',
- 'net_test_support'
+ 'net_derived_sources',
+ 'net_test_support',
],
'sources': [
- 'android/keystore_unittest.cc',
- 'android/network_change_notifier_android_unittest.cc',
- 'base/address_list_unittest.cc',
- 'base/address_tracker_linux_unittest.cc',
- 'base/backoff_entry_unittest.cc',
- 'base/big_endian_unittest.cc',
- 'base/data_url_unittest.cc',
- 'base/directory_lister_unittest.cc',
- 'base/dns_util_unittest.cc',
- 'base/escape_unittest.cc',
- 'base/expiring_cache_unittest.cc',
- 'base/file_stream_unittest.cc',
- 'base/filter_unittest.cc',
- 'base/int128_unittest.cc',
- 'base/gzip_filter_unittest.cc',
- 'base/host_mapping_rules_unittest.cc',
- 'base/host_port_pair_unittest.cc',
- 'base/ip_endpoint_unittest.cc',
- 'base/keygen_handler_unittest.cc',
- 'base/mime_sniffer_unittest.cc',
- 'base/mime_util_unittest.cc',
- 'base/mock_filter_context.cc',
- 'base/mock_filter_context.h',
- 'base/net_log_logger_unittest.cc',
- 'base/net_log_unittest.cc',
- 'base/net_log_unittest.h',
- 'base/net_util_unittest.cc',
- 'base/network_change_notifier_win_unittest.cc',
- 'base/prioritized_dispatcher_unittest.cc',
- 'base/priority_queue_unittest.cc',
- 'base/registry_controlled_domains/registry_controlled_domain_unittest.cc',
- 'base/sdch_filter_unittest.cc',
- 'base/static_cookie_policy_unittest.cc',
- 'base/test_completion_callback_unittest.cc',
- 'base/upload_bytes_element_reader_unittest.cc',
- 'base/upload_data_stream_unittest.cc',
- 'base/upload_file_element_reader_unittest.cc',
- 'base/url_util_unittest.cc',
- 'cert/cert_verify_proc_unittest.cc',
- 'cert/crl_set_unittest.cc',
- 'cert/ct_log_verifier_unittest.cc',
- 'cert/ct_objects_extractor_unittest.cc',
- 'cert/ct_serialization_unittest.cc',
- 'cert/ev_root_ca_metadata_unittest.cc',
- 'cert/jwk_serializer_unittest.cc',
- 'cert/multi_log_ct_verifier_unittest.cc',
- 'cert/multi_threaded_cert_verifier_unittest.cc',
- 'cert/nss_cert_database_unittest.cc',
- 'cert/pem_tokenizer_unittest.cc',
- 'cert/signed_certificate_timestamp_unittest.cc',
- 'cert/test_root_certs_unittest.cc',
- 'cert/x509_certificate_unittest.cc',
- 'cert/x509_cert_types_unittest.cc',
- 'cert/x509_util_unittest.cc',
- 'cert/x509_util_nss_unittest.cc',
- 'cert/x509_util_openssl_unittest.cc',
- 'cookies/canonical_cookie_unittest.cc',
- 'cookies/cookie_constants_unittest.cc',
- 'cookies/cookie_monster_unittest.cc',
- 'cookies/cookie_store_unittest.h',
- 'cookies/cookie_util_unittest.cc',
- 'cookies/parsed_cookie_unittest.cc',
- 'disk_cache/addr_unittest.cc',
- 'disk_cache/backend_unittest.cc',
- 'disk_cache/bitmap_unittest.cc',
- 'disk_cache/block_files_unittest.cc',
- 'disk_cache/cache_util_unittest.cc',
- 'disk_cache/entry_unittest.cc',
- 'disk_cache/mapped_file_unittest.cc',
- 'disk_cache/simple/simple_index_file_unittest.cc',
- 'disk_cache/simple/simple_index_unittest.cc',
- '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',
- 'dns/dns_config_service_unittest.cc',
- 'dns/dns_config_service_win_unittest.cc',
- 'dns/dns_hosts_unittest.cc',
- 'dns/dns_query_unittest.cc',
- 'dns/dns_response_unittest.cc',
- 'dns/dns_session_unittest.cc',
- 'dns/dns_transaction_unittest.cc',
- 'dns/host_cache_unittest.cc',
- 'dns/host_resolver_impl_unittest.cc',
- 'dns/mapped_host_resolver_unittest.cc',
- 'dns/mdns_cache_unittest.cc',
- 'dns/mdns_client_unittest.cc',
- 'dns/serial_worker_unittest.cc',
- 'dns/record_parsed_unittest.cc',
- 'dns/record_rdata_unittest.cc',
- 'dns/single_request_host_resolver_unittest.cc',
- 'ftp/ftp_auth_cache_unittest.cc',
- 'ftp/ftp_ctrl_response_buffer_unittest.cc',
- 'ftp/ftp_directory_listing_parser_ls_unittest.cc',
- 'ftp/ftp_directory_listing_parser_netware_unittest.cc',
- 'ftp/ftp_directory_listing_parser_os2_unittest.cc',
- 'ftp/ftp_directory_listing_parser_unittest.cc',
- 'ftp/ftp_directory_listing_parser_unittest.h',
- 'ftp/ftp_directory_listing_parser_vms_unittest.cc',
- 'ftp/ftp_directory_listing_parser_windows_unittest.cc',
- 'ftp/ftp_network_transaction_unittest.cc',
- 'ftp/ftp_util_unittest.cc',
- 'http/des_unittest.cc',
- 'http/http_auth_cache_unittest.cc',
- 'http/http_auth_controller_unittest.cc',
- 'http/http_auth_filter_unittest.cc',
- 'http/http_auth_gssapi_posix_unittest.cc',
- 'http/http_auth_handler_basic_unittest.cc',
- 'http/http_auth_handler_digest_unittest.cc',
- 'http/http_auth_handler_factory_unittest.cc',
- 'http/http_auth_handler_mock.cc',
- 'http/http_auth_handler_mock.h',
- 'http/http_auth_handler_negotiate_unittest.cc',
- 'http/http_auth_handler_unittest.cc',
- 'http/http_auth_sspi_win_unittest.cc',
- 'http/http_auth_unittest.cc',
- 'http/http_basic_state_unittest.cc',
- 'http/http_byte_range_unittest.cc',
- 'http/http_cache_unittest.cc',
- 'http/http_chunked_decoder_unittest.cc',
- 'http/http_content_disposition_unittest.cc',
- 'http/http_network_layer_unittest.cc',
- 'http/http_network_transaction_ssl_unittest.cc',
- 'http/http_network_transaction_unittest.cc',
- 'http/http_pipelined_connection_impl_unittest.cc',
- 'http/http_pipelined_host_forced_unittest.cc',
- 'http/http_pipelined_host_impl_unittest.cc',
- 'http/http_pipelined_host_pool_unittest.cc',
- 'http/http_pipelined_host_test_util.cc',
- 'http/http_pipelined_host_test_util.h',
- 'http/http_pipelined_network_transaction_unittest.cc',
- 'http/http_proxy_client_socket_pool_unittest.cc',
- 'http/http_request_headers_unittest.cc',
- 'http/http_response_body_drainer_unittest.cc',
- 'http/http_response_headers_unittest.cc',
- 'http/http_security_headers_unittest.cc',
- 'http/http_server_properties_impl_unittest.cc',
- 'http/http_status_code_unittest.cc',
- 'http/http_stream_factory_impl_request_unittest.cc',
- 'http/http_stream_factory_impl_unittest.cc',
- 'http/http_stream_parser_unittest.cc',
- 'http/http_transaction_unittest.cc',
- 'http/http_transaction_unittest.h',
- 'http/http_util_unittest.cc',
- 'http/http_vary_data_unittest.cc',
- 'http/mock_allow_url_security_manager.cc',
- 'http/mock_allow_url_security_manager.h',
- 'http/mock_gssapi_library_posix.cc',
- 'http/mock_gssapi_library_posix.h',
- 'http/mock_http_cache.cc',
- 'http/mock_http_cache.h',
- 'http/mock_sspi_library_win.cc',
- 'http/mock_sspi_library_win.h',
- 'http/transport_security_persister_unittest.cc',
- 'http/transport_security_state_unittest.cc',
- 'http/url_security_manager_unittest.cc',
- 'ocsp/nss_ocsp_unittest.cc',
- 'proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc',
- 'proxy/dhcp_proxy_script_fetcher_factory_unittest.cc',
- 'proxy/dhcp_proxy_script_fetcher_win_unittest.cc',
- 'proxy/multi_threaded_proxy_resolver_unittest.cc',
- 'proxy/network_delegate_error_observer_unittest.cc',
- 'proxy/proxy_bypass_rules_unittest.cc',
- 'proxy/proxy_config_service_android_unittest.cc',
- 'proxy/proxy_config_service_linux_unittest.cc',
- 'proxy/proxy_config_service_win_unittest.cc',
- 'proxy/proxy_config_unittest.cc',
- 'proxy/proxy_info_unittest.cc',
- 'proxy/proxy_list_unittest.cc',
- 'proxy/proxy_resolver_v8_tracing_unittest.cc',
- 'proxy/proxy_resolver_v8_unittest.cc',
- 'proxy/proxy_script_decider_unittest.cc',
- 'proxy/proxy_script_fetcher_impl_unittest.cc',
- 'proxy/proxy_server_unittest.cc',
- 'proxy/proxy_service_unittest.cc',
- 'quic/congestion_control/available_channel_estimator_test.cc',
- 'quic/congestion_control/channel_estimator_test.cc',
- 'quic/congestion_control/cube_root_test.cc',
- 'quic/congestion_control/cubic_test.cc',
- 'quic/congestion_control/fix_rate_test.cc',
- 'quic/congestion_control/hybrid_slow_start_test.cc',
- 'quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc',
- 'quic/congestion_control/inter_arrival_overuse_detector_test.cc',
- 'quic/congestion_control/inter_arrival_probe_test.cc',
- 'quic/congestion_control/inter_arrival_receiver_test.cc',
- 'quic/congestion_control/inter_arrival_state_machine_test.cc',
- 'quic/congestion_control/inter_arrival_sender_test.cc',
- 'quic/congestion_control/leaky_bucket_test.cc',
- 'quic/congestion_control/paced_sender_test.cc',
- 'quic/congestion_control/pacing_sender_test.cc',
- 'quic/congestion_control/quic_max_sized_map_test.cc',
- 'quic/congestion_control/tcp_cubic_sender_test.cc',
- 'quic/congestion_control/tcp_receiver_test.cc',
- 'quic/crypto/aes_128_gcm_12_decrypter_test.cc',
- 'quic/crypto/aes_128_gcm_12_encrypter_test.cc',
- 'quic/crypto/cert_compressor_test.cc',
- 'quic/crypto/channel_id_test.cc',
- 'quic/crypto/common_cert_set_test.cc',
- 'quic/crypto/crypto_framer_test.cc',
- 'quic/crypto/crypto_secret_boxer_test.cc',
- 'quic/crypto/crypto_server_test.cc',
- 'quic/crypto/crypto_utils_test.cc',
- 'quic/crypto/curve25519_key_exchange_test.cc',
- 'quic/crypto/local_strike_register_client_test.cc',
- 'quic/crypto/null_decrypter_test.cc',
- 'quic/crypto/null_encrypter_test.cc',
- 'quic/crypto/p256_key_exchange_test.cc',
- 'quic/crypto/proof_test.cc',
- 'quic/crypto/quic_crypto_client_config_test.cc',
- 'quic/crypto/quic_crypto_server_config_test.cc',
- 'quic/crypto/quic_random_test.cc',
- 'quic/crypto/strike_register_test.cc',
- 'quic/iovector_test.cc',
- 'quic/port_suggester_unittest.cc',
- 'quic/test_tools/crypto_test_utils.cc',
- 'quic/test_tools/crypto_test_utils.h',
- 'quic/test_tools/crypto_test_utils_chromium.cc',
- 'quic/test_tools/crypto_test_utils_nss.cc',
- 'quic/test_tools/crypto_test_utils_openssl.cc',
- 'quic/test_tools/delayed_verify_strike_register_client.cc',
- 'quic/test_tools/delayed_verify_strike_register_client.h',
- 'quic/test_tools/mock_clock.cc',
- 'quic/test_tools/mock_clock.h',
- 'quic/test_tools/mock_crypto_client_stream.cc',
- 'quic/test_tools/mock_crypto_client_stream.h',
- 'quic/test_tools/mock_crypto_client_stream_factory.cc',
- 'quic/test_tools/mock_crypto_client_stream_factory.h',
- 'quic/test_tools/mock_random.cc',
- 'quic/test_tools/mock_random.h',
- 'quic/test_tools/quic_client_session_peer.cc',
- 'quic/test_tools/quic_client_session_peer.h',
- 'quic/test_tools/quic_connection_peer.cc',
- 'quic/test_tools/quic_connection_peer.h',
- 'quic/test_tools/quic_data_stream_peer.cc',
- 'quic/test_tools/quic_data_stream_peer.h',
- 'quic/test_tools/quic_framer_peer.cc',
- 'quic/test_tools/quic_framer_peer.h',
- 'quic/test_tools/quic_packet_creator_peer.cc',
- 'quic/test_tools/quic_packet_creator_peer.h',
- 'quic/test_tools/quic_received_packet_manager_peer.cc',
- 'quic/test_tools/quic_received_packet_manager_peer.h',
- 'quic/test_tools/quic_sent_packet_manager_peer.cc',
- 'quic/test_tools/quic_sent_packet_manager_peer.h',
- 'quic/test_tools/quic_session_peer.cc',
- 'quic/test_tools/quic_session_peer.h',
- 'quic/test_tools/quic_test_utils.cc',
- 'quic/test_tools/quic_test_utils.h',
- 'quic/test_tools/quic_test_writer.cc',
- 'quic/test_tools/quic_test_writer.h',
- 'quic/test_tools/reliable_quic_stream_peer.cc',
- 'quic/test_tools/reliable_quic_stream_peer.h',
- 'quic/test_tools/simple_quic_framer.cc',
- 'quic/test_tools/simple_quic_framer.h',
- 'quic/test_tools/test_task_runner.cc',
- 'quic/test_tools/test_task_runner.h',
- 'quic/quic_ack_notifier_test.cc',
- 'quic/quic_alarm_test.cc',
- 'quic/quic_bandwidth_test.cc',
- 'quic/quic_client_session_test.cc',
- 'quic/quic_clock_test.cc',
- 'quic/quic_config_test.cc',
- 'quic/quic_connection_helper_test.cc',
- 'quic/quic_connection_test.cc',
- 'quic/quic_crypto_client_stream_test.cc',
- 'quic/quic_crypto_server_stream_test.cc',
- 'quic/quic_crypto_stream_test.cc',
- 'quic/quic_data_stream_test.cc',
- 'quic/quic_data_writer_test.cc',
- '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',
- 'quic/quic_protocol_test.cc',
- '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',
- 'quic/quic_stream_factory_test.cc',
- 'quic/quic_stream_sequencer_test.cc',
- 'quic/quic_time_test.cc',
- 'quic/quic_utils_test.cc',
- 'quic/reliable_quic_stream_test.cc',
- 'server/http_server_response_info_unittest.cc',
- 'server/http_server_unittest.cc',
- 'socket/buffered_write_stream_socket_unittest.cc',
- 'socket/client_socket_pool_base_unittest.cc',
- 'socket/deterministic_socket_data_unittest.cc',
- 'socket/mock_client_socket_pool_manager.cc',
- 'socket/mock_client_socket_pool_manager.h',
- 'socket/socks5_client_socket_unittest.cc',
- 'socket/socks_client_socket_pool_unittest.cc',
- 'socket/socks_client_socket_unittest.cc',
- 'socket/ssl_client_socket_openssl_unittest.cc',
- 'socket/ssl_client_socket_pool_unittest.cc',
- 'socket/ssl_client_socket_unittest.cc',
- 'socket/ssl_server_socket_unittest.cc',
- 'socket/ssl_session_cache_openssl_unittest.cc',
- 'socket/tcp_client_socket_unittest.cc',
- 'socket/tcp_listen_socket_unittest.cc',
- 'socket/tcp_listen_socket_unittest.h',
- 'socket/tcp_server_socket_unittest.cc',
- 'socket/tcp_socket_unittest.cc',
- 'socket/transport_client_socket_pool_unittest.cc',
- 'socket/transport_client_socket_unittest.cc',
- 'socket/unix_domain_socket_posix_unittest.cc',
- 'socket_stream/socket_stream_metrics_unittest.cc',
- 'socket_stream/socket_stream_unittest.cc',
- 'spdy/buffered_spdy_framer_unittest.cc',
- 'spdy/spdy_buffer_unittest.cc',
- 'spdy/spdy_frame_builder_test.cc',
- 'spdy/spdy_frame_reader_test.cc',
- 'spdy/spdy_framer_test.cc',
- 'spdy/spdy_header_block_unittest.cc',
- 'spdy/spdy_http_stream_unittest.cc',
- 'spdy/spdy_http_utils_unittest.cc',
- 'spdy/spdy_network_transaction_unittest.cc',
- 'spdy/spdy_priority_forest_test.cc',
- 'spdy/spdy_protocol_test.cc',
- 'spdy/spdy_proxy_client_socket_unittest.cc',
- 'spdy/spdy_read_queue_unittest.cc',
- 'spdy/spdy_session_pool_unittest.cc',
- 'spdy/spdy_session_test_util.cc',
- 'spdy/spdy_session_test_util.h',
- 'spdy/spdy_session_unittest.cc',
- 'spdy/spdy_stream_test_util.cc',
- 'spdy/spdy_stream_test_util.h',
- 'spdy/spdy_stream_unittest.cc',
- 'spdy/spdy_test_util_common.cc',
- 'spdy/spdy_test_util_common.h',
- 'spdy/spdy_test_utils.cc',
- 'spdy/spdy_test_utils.h',
- 'spdy/spdy_websocket_stream_unittest.cc',
- 'spdy/spdy_websocket_test_util.cc',
- 'spdy/spdy_websocket_test_util.h',
- 'spdy/spdy_write_queue_unittest.cc',
- 'spdy/write_blocked_list_test.cc',
- 'ssl/client_cert_store_mac_unittest.cc',
- 'ssl/client_cert_store_nss_unittest.cc',
- 'ssl/client_cert_store_unittest-inl.h',
- 'ssl/client_cert_store_win_unittest.cc',
- 'ssl/default_server_bound_cert_store_unittest.cc',
- 'ssl/openssl_client_key_store_unittest.cc',
- 'ssl/server_bound_cert_service_unittest.cc',
- 'ssl/ssl_cipher_suite_names_unittest.cc',
- 'ssl/ssl_client_auth_cache_unittest.cc',
- 'ssl/ssl_config_service_unittest.cc',
- 'test/embedded_test_server/embedded_test_server_unittest.cc',
- 'test/embedded_test_server/http_request_unittest.cc',
- 'test/embedded_test_server/http_response_unittest.cc',
- 'test/python_utils_unittest.cc',
- 'test/run_all_unittests.cc',
- 'test/test_certificate_data.h',
- 'tools/dump_cache/url_to_filename_encoder.cc',
- 'tools/dump_cache/url_to_filename_encoder.h',
- 'tools/dump_cache/url_to_filename_encoder_unittest.cc',
- 'tools/dump_cache/url_utilities.h',
- 'tools/dump_cache/url_utilities.cc',
- 'tools/dump_cache/url_utilities_unittest.cc',
- 'tools/tld_cleanup/tld_cleanup_util_unittest.cc',
- 'udp/udp_socket_unittest.cc',
- 'url_request/url_fetcher_impl_unittest.cc',
- 'url_request/url_request_context_builder_unittest.cc',
- 'url_request/url_request_filter_unittest.cc',
- 'url_request/url_request_ftp_job_unittest.cc',
- 'url_request/url_request_http_job_unittest.cc',
- 'url_request/url_request_job_factory_impl_unittest.cc',
- 'url_request/url_request_job_unittest.cc',
- 'url_request/url_request_throttler_simulation_unittest.cc',
- 'url_request/url_request_throttler_test_support.cc',
- 'url_request/url_request_throttler_test_support.h',
- 'url_request/url_request_throttler_unittest.cc',
- 'url_request/url_request_unittest.cc',
- 'url_request/view_cache_helper_unittest.cc',
- 'websockets/websocket_basic_stream_test.cc',
- 'websockets/websocket_channel_test.cc',
- 'websockets/websocket_deflate_predictor_impl_test.cc',
- 'websockets/websocket_deflate_stream_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_spdy_test.cc',
- 'websockets/websocket_handshake_handler_test.cc',
- 'websockets/websocket_handshake_stream_create_helper_test.cc',
- 'websockets/websocket_inflater_test.cc',
- 'websockets/websocket_job_test.cc',
- 'websockets/websocket_net_log_params_test.cc',
- 'websockets/websocket_stream_test.cc',
- 'websockets/websocket_test_util.cc',
- 'websockets/websocket_test_util.h',
- 'websockets/websocket_throttle_test.cc',
+ '<@(net_test_sources)',
],
'conditions': [
['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', {
'dependencies': [
- 'balsa',
'epoll_server',
'flip_in_mem_edsm_server_base',
'quic_base',
],
'sources': [
- 'quic/quic_end_to_end_unittest.cc',
- 'tools/balsa/balsa_frame_test.cc',
- 'tools/balsa/balsa_headers_test.cc',
- 'tools/quic/end_to_end_test.cc',
- 'tools/quic/quic_client_session_test.cc',
- 'tools/quic/quic_dispatcher_test.cc',
- 'tools/quic/quic_epoll_clock_test.cc',
- 'tools/quic/quic_epoll_connection_helper_test.cc',
- 'tools/quic/quic_in_memory_cache_test.cc',
- 'tools/quic/quic_server_session_test.cc',
- 'tools/quic/quic_server_test.cc',
- 'tools/quic/quic_spdy_client_stream_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',
- 'tools/quic/test_tools/mock_epoll_server.h',
- 'tools/quic/test_tools/mock_quic_dispatcher.cc',
- 'tools/quic/test_tools/mock_quic_dispatcher.h',
- 'tools/quic/test_tools/packet_dropping_test_writer.cc',
- 'tools/quic/test_tools/packet_dropping_test_writer.h',
- 'tools/quic/test_tools/quic_client_peer.cc',
- 'tools/quic/test_tools/quic_client_peer.h',
- 'tools/quic/test_tools/quic_dispatcher_peer.cc',
- 'tools/quic/test_tools/quic_dispatcher_peer.h',
- 'tools/quic/test_tools/quic_in_memory_cache_peer.h',
- 'tools/quic/test_tools/quic_in_memory_cache_peer.cc',
- 'tools/quic/test_tools/quic_server_peer.cc',
- 'tools/quic/test_tools/quic_server_peer.h',
- 'tools/quic/test_tools/quic_test_client.cc',
- 'tools/quic/test_tools/quic_test_client.h',
- 'tools/quic/test_tools/quic_test_utils.cc',
- 'tools/quic/test_tools/quic_test_utils.h',
- 'tools/quic/test_tools/server_thread.h',
- 'tools/quic/test_tools/server_thread.cc',
+ '<@(net_linux_test_sources)',
],
}],
['chromeos==1', {
@@ -2051,6 +569,8 @@
}],
[ 'OS == "android"', {
'sources!': [
+ # See bug http://crbug.com/344533.
+ 'disk_cache/blockfile/index_table_v3_unittest.cc',
# No res_ninit() et al on Android, so this doesn't make a lot of
# sense.
'dns/dns_config_service_posix_unittest.cc',
@@ -2063,6 +583,7 @@
[ 'use_nss != 1', {
'sources!': [
'ssl/client_cert_store_nss_unittest.cc',
+ 'ssl/client_cert_store_chromeos_unittest.cc',
],
}],
[ 'use_openssl == 1', {
@@ -2083,15 +604,9 @@
}],
],
}],
- [ 'toolkit_uses_gtk == 1', {
- 'dependencies': [
- '../build/linux/system.gyp:gtk',
- ],
- },
- ],
[ 'os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
'conditions': [
- ['linux_use_tcmalloc==1', {
+ ['use_allocator!="none"', {
'dependencies': [
'../base/allocator/allocator.gyp:allocator',
],
@@ -2126,6 +641,8 @@
'cert/ct_objects_extractor_unittest.cc',
'cert/multi_log_ct_verifier_unittest.cc',
'cert/nss_cert_database_unittest.cc',
+ 'cert/nss_cert_database_chromeos_unittest.cc',
+ 'cert/nss_profile_filter_chromeos_unittest.cc',
'cert/x509_util_nss_unittest.cc',
'quic/test_tools/crypto_test_utils_nss.cc',
],
@@ -2135,10 +652,14 @@
'quic/test_tools/crypto_test_utils_openssl.cc',
'socket/ssl_client_socket_openssl_unittest.cc',
'socket/ssl_session_cache_openssl_unittest.cc',
- 'ssl/openssl_client_key_store_unittest.cc',
],
},
],
+ [ 'use_openssl_certs == 0', {
+ 'sources!': [
+ 'ssl/openssl_client_key_store_unittest.cc',
+ ],
+ }],
[ 'enable_websockets != 1', {
'sources/': [
['exclude', '^socket_stream/'],
@@ -2146,6 +667,12 @@
['exclude', '^spdy/spdy_websocket_stream_unittest\\.cc$'],
],
}],
+ ['disable_file_support==1', {
+ 'sources!': [
+ 'base/directory_lister_unittest.cc',
+ 'url_request/url_request_file_job_unittest.cc',
+ ],
+ }],
[ 'disable_ftp_support==1', {
'sources/': [
['exclude', '^ftp/'],
@@ -2162,7 +689,10 @@
],
},
],
- [ 'use_v8_in_net==1', {
+ # Always need use_v8_in_net to be 1 to run gyp on Android, so just
+ # remove net_unittest's dependency on v8 when using icu alternatives
+ # instead of setting use_v8_in_net to 0.
+ [ 'use_v8_in_net==1 and use_icu_alternatives_on_android==0', {
'dependencies': [
'net_with_v8',
],
@@ -2188,19 +718,25 @@
'dns/dns_config_service_posix_unittest.cc',
'http/http_auth_gssapi_posix_unittest.cc',
],
- # This is needed to trigger the dll copy step on windows.
- # TODO(mark): Specifying this here shouldn't be necessary.
'dependencies': [
- '../third_party/icu/icu.gyp:icudata',
'../third_party/nss/nss.gyp:nspr',
'../third_party/nss/nss.gyp:nss',
'third_party/nss/ssl.gyp:libssl',
],
+ 'conditions': [
+ [ 'icu_use_data_file_flag == 0', {
+ # This is needed to trigger the dll copy step on windows.
+ # TODO(mark): Specifying this here shouldn't be necessary.
+ 'dependencies': [
+ '../third_party/icu/icu.gyp:icudata',
+ ],
+ }],
+ ],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
},
],
- [ 'OS == "mac"', {
+ [ 'OS == "mac" and use_openssl == 0', {
'dependencies': [
'../third_party/nss/nss.gyp:nspr',
'../third_party/nss/nss.gyp:nss',
@@ -2231,11 +767,12 @@
# implementation is missing or incomplete.
# KeygenHandler::GenKeyAndSignChallenge() is not ported to iOS.
'base/keygen_handler_unittest.cc',
- # Need to read input data files.
- 'base/gzip_filter_unittest.cc',
'disk_cache/backend_unittest.cc',
- 'disk_cache/block_files_unittest.cc',
+ 'disk_cache/blockfile/block_files_unittest.cc',
+ # Need to read input data files.
+ 'filter/gzip_filter_unittest.cc',
'socket/ssl_server_socket_unittest.cc',
+ 'spdy/fuzzing/hpack_fuzz_util_test.cc',
# Need TestServer.
'proxy/proxy_script_fetcher_impl_unittest.cc',
'socket/ssl_client_socket_unittest.cc',
@@ -2248,6 +785,9 @@
# iOS.
# OS is not "linux" or "freebsd" or "openbsd".
'socket/unix_domain_socket_posix_unittest.cc',
+
+ # See bug http://crbug.com/344533.
+ 'disk_cache/blockfile/index_table_v3_unittest.cc',
],
}],
[ 'OS == "android"', {
@@ -2259,11 +799,34 @@
],
},
],
- ['OS == "android" and gtest_target_type == "shared_library"', {
+ ['OS == "android"', {
+ # TODO(mmenke): This depends on test_support_base, which depends on
+ # icu. Figure out a way to remove that dependency.
'dependencies': [
'../testing/android/native_test.gyp:native_test_native_code',
]
}],
+ [ 'use_icu_alternatives_on_android == 1', {
+ 'dependencies!': [
+ '../base/base.gyp:base_i18n',
+ ],
+ 'sources!': [
+ 'base/filename_util_unittest.cc',
+ 'base/net_util_icu_unittest.cc',
+ ],
+ },
+ ],
+ ],
+ 'target_conditions': [
+ # These source files are excluded by default platform rules, but they
+ # are needed in specific cases on other platforms. Re-including them can
+ # only be done in target_conditions as it is evaluated after the
+ # platform rules.
+ ['OS == "android"', {
+ 'sources/': [
+ ['include', '^base/address_tracker_linux_unittest\\.cc$'],
+ ],
+ }],
],
},
{
@@ -2280,7 +843,7 @@
],
'sources': [
'cookies/cookie_monster_perftest.cc',
- 'disk_cache/disk_cache_perftest.cc',
+ 'disk_cache/blockfile/disk_cache_perftest.cc',
'proxy/proxy_resolver_perftest.cc',
],
'conditions': [
@@ -2294,16 +857,19 @@
],
},
],
- # This is needed to trigger the dll copy step on windows.
- # TODO(mark): Specifying this here shouldn't be necessary.
[ 'OS == "win"', {
- 'dependencies': [
- '../third_party/icu/icu.gyp:icudata',
+ 'conditions': [
+ [ 'icu_use_data_file_flag == 0', {
+ # This is needed to trigger the dll copy step on windows.
+ # TODO(mark): Specifying this here shouldn't be necessary.
+ 'dependencies': [
+ '../third_party/icu/icu.gyp:icudata',
+ ],
+ }],
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
- },
- ],
+ }],
],
},
{
@@ -2320,6 +886,8 @@
],
'export_dependent_settings': [
'../base/base.gyp:base',
+ # TODO(mmenke): This depends on icu, figure out a way to build tests
+ # without icu.
'../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
'../testing/gmock.gyp:gmock',
@@ -2347,14 +915,14 @@
'disk_cache/disk_cache_test_base.h',
'disk_cache/disk_cache_test_util.cc',
'disk_cache/disk_cache_test_util.h',
- 'disk_cache/flash/flash_cache_test_base.h',
- 'disk_cache/flash/flash_cache_test_base.cc',
'dns/dns_test_util.cc',
'dns/dns_test_util.h',
'dns/mock_host_resolver.cc',
'dns/mock_host_resolver.h',
'dns/mock_mdns_socket_factory.cc',
'dns/mock_mdns_socket_factory.h',
+ 'http/http_transaction_test_util.cc',
+ 'http/http_transaction_test_util.h',
'proxy/mock_proxy_resolver.cc',
'proxy/mock_proxy_resolver.h',
'proxy/mock_proxy_script_fetcher.cc',
@@ -2416,7 +984,7 @@
}],
['os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
'conditions': [
- ['linux_use_tcmalloc==1', {
+ ['use_allocator!="none"', {
'dependencies': [
'../base/allocator/allocator.gyp:allocator',
],
@@ -2493,6 +1061,34 @@
'msvs_disabled_warnings': [4267, ],
},
{
+ 'target_name': 'balsa',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'net',
+ ],
+ 'sources': [
+ 'tools/balsa/balsa_enums.h',
+ 'tools/balsa/balsa_frame.cc',
+ 'tools/balsa/balsa_frame.h',
+ 'tools/balsa/balsa_headers.cc',
+ 'tools/balsa/balsa_headers.h',
+ 'tools/balsa/balsa_headers_token_utils.cc',
+ 'tools/balsa/balsa_headers_token_utils.h',
+ 'tools/balsa/balsa_visitor_interface.h',
+ 'tools/balsa/http_message_constants.cc',
+ 'tools/balsa/http_message_constants.h',
+ 'tools/balsa/noop_balsa_visitor.h',
+ 'tools/balsa/simple_buffer.cc',
+ 'tools/balsa/simple_buffer.h',
+ 'tools/balsa/split.cc',
+ 'tools/balsa/split.h',
+ 'tools/balsa/string_piece_utils.h',
+ 'tools/quic/spdy_utils.cc',
+ 'tools/quic/spdy_utils.h',
+ ],
+ },
+ {
'target_name': 'dump_cache',
'type': 'executable',
'dependencies': [
@@ -2528,6 +1124,7 @@
'variables': { 'enable_wexit_time_destructors': 1, },
'dependencies': [
'../base/base.gyp:base',
+ '../gin/gin.gyp:gin',
'../url/url.gyp:url_lib',
'../v8/tools/gyp/v8.gyp:v8',
'net'
@@ -2548,7 +1145,7 @@
},
],
}],
- ['OS != "ios"', {
+ ['OS != "ios" and OS != "android"', {
'targets': [
# iOS doesn't have the concept of simple executables, these targets
# can't be compiled on the platform.
@@ -2593,71 +1190,67 @@
'msvs_disabled_warnings': [4267, ],
},
{
- 'target_name': 'fetch_client',
+ 'target_name': 'gdig',
'type': 'executable',
- 'variables': { 'enable_wexit_time_destructors': 1, },
'dependencies': [
'../base/base.gyp:base',
- '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
- '../testing/gtest.gyp:gtest',
+ 'net',
+ ],
+ 'sources': [
+ 'tools/gdig/file_net_log.cc',
+ 'tools/gdig/gdig.cc',
+ ],
+ },
+ {
+ 'target_name': 'get_server_time',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
'../url/url.gyp:url_lib',
'net',
- 'net_with_v8',
],
'sources': [
- 'tools/fetch/fetch_client.cc',
+ 'tools/get_server_time/get_server_time.cc',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
},
{
- 'target_name': 'fetch_server',
+ 'target_name': 'hpack_example_generator',
'type': 'executable',
- 'variables': { 'enable_wexit_time_destructors': 1, },
'dependencies': [
'../base/base.gyp:base',
- '../url/url.gyp:url_lib',
'net',
],
'sources': [
- 'tools/fetch/fetch_server.cc',
- 'tools/fetch/http_listen_socket.cc',
- 'tools/fetch/http_listen_socket.h',
- 'tools/fetch/http_server.cc',
- 'tools/fetch/http_server.h',
- 'tools/fetch/http_server_request_info.cc',
- 'tools/fetch/http_server_request_info.h',
- 'tools/fetch/http_server_response_info.cc',
- 'tools/fetch/http_server_response_info.h',
- 'tools/fetch/http_session.cc',
- 'tools/fetch/http_session.h',
+ 'spdy/fuzzing/hpack_example_generator.cc',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
},
{
- 'target_name': 'gdig',
+ 'target_name': 'hpack_fuzz_mutator',
'type': 'executable',
'dependencies': [
'../base/base.gyp:base',
'net',
],
'sources': [
- 'tools/gdig/file_net_log.cc',
- 'tools/gdig/gdig.cc',
+ 'spdy/fuzzing/hpack_fuzz_mutator.cc',
],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
},
{
- 'target_name': 'get_server_time',
+ 'target_name': 'hpack_fuzz_wrapper',
'type': 'executable',
'dependencies': [
'../base/base.gyp:base',
- '../base/base.gyp:base_i18n',
- '../url/url.gyp:url_lib',
'net',
],
'sources': [
- 'tools/get_server_time/get_server_time.cc',
+ 'spdy/fuzzing/hpack_fuzz_wrapper.cc',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
@@ -2705,7 +1298,7 @@
'net_test_support',
],
'sources': [
- 'disk_cache/stress_cache.cc',
+ 'disk_cache/blockfile/stress_cache.cc',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
@@ -2729,32 +1322,6 @@
['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', {
'targets': [
{
- 'target_name': 'balsa',
- 'type': 'static_library',
- 'dependencies': [
- '../base/base.gyp:base',
- 'net',
- ],
- 'sources': [
- 'tools/balsa/balsa_enums.h',
- 'tools/balsa/balsa_frame.cc',
- 'tools/balsa/balsa_frame.h',
- 'tools/balsa/balsa_headers.cc',
- 'tools/balsa/balsa_headers.h',
- 'tools/balsa/balsa_headers_token_utils.cc',
- 'tools/balsa/balsa_headers_token_utils.h',
- 'tools/balsa/balsa_visitor_interface.h',
- 'tools/balsa/http_message_constants.cc',
- 'tools/balsa/http_message_constants.h',
- 'tools/balsa/noop_balsa_visitor.h',
- 'tools/balsa/simple_buffer.cc',
- 'tools/balsa/simple_buffer.h',
- 'tools/balsa/split.cc',
- 'tools/balsa/split.h',
- 'tools/balsa/string_piece_utils.h',
- ],
- },
- {
'target_name': 'epoll_server',
'type': 'static_library',
'dependencies': [
@@ -2854,8 +1421,6 @@
'dependencies': [
'../base/base.gyp:base',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
- '../crypto/crypto.gyp:crypto',
- '../third_party/openssl/openssl.gyp:openssl',
'../url/url.gyp:url_lib',
'balsa',
'epoll_server',
@@ -2876,6 +1441,8 @@
'tools/quic/quic_epoll_connection_helper.h',
'tools/quic/quic_in_memory_cache.cc',
'tools/quic/quic_in_memory_cache.h',
+ 'tools/quic/quic_packet_writer_wrapper.cc',
+ 'tools/quic/quic_packet_writer_wrapper.h',
'tools/quic/quic_server.cc',
'tools/quic/quic_server.h',
'tools/quic/quic_server_session.cc',
@@ -2888,8 +1455,6 @@
'tools/quic/quic_spdy_server_stream.h',
'tools/quic/quic_time_wait_list_manager.h',
'tools/quic/quic_time_wait_list_manager.cc',
- 'tools/quic/spdy_utils.cc',
- 'tools/quic/spdy_utils.h',
],
},
{
@@ -2897,7 +1462,6 @@
'type': 'executable',
'dependencies': [
'../base/base.gyp:base',
- '../third_party/openssl/openssl.gyp:openssl',
'net',
'quic_base',
],
@@ -2910,14 +1474,13 @@
'type': 'executable',
'dependencies': [
'../base/base.gyp:base',
- '../third_party/openssl/openssl.gyp:openssl',
'net',
'quic_base',
],
'sources': [
'tools/quic/quic_server_bin.cc',
],
- }
+ },
]
}],
['OS=="android"', {
@@ -2926,8 +1489,10 @@
'target_name': 'net_jni_headers',
'type': 'none',
'sources': [
+ 'android/java/src/org/chromium/net/AndroidCertVerifyResult.java',
'android/java/src/org/chromium/net/AndroidKeyStore.java',
'android/java/src/org/chromium/net/AndroidNetworkLibrary.java',
+ 'android/java/src/org/chromium/net/AndroidPrivateKey.java',
'android/java/src/org/chromium/net/GURLUtils.java',
'android/java/src/org/chromium/net/NetworkChangeNotifier.java',
'android/java/src/org/chromium/net/ProxyChangeListener.java',
@@ -2935,14 +1500,16 @@
],
'variables': {
'jni_gen_package': 'net',
- 'jni_generator_ptr_type': 'long',
- },
- 'direct_dependent_settings': {
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/net',
- ],
},
'includes': [ '../build/jni_generator.gypi' ],
+
+ 'conditions': [
+ ['use_icu_alternatives_on_android==1', {
+ 'sources': [
+ 'android/java/src/org/chromium/net/NetStringUtil.java',
+ ],
+ }],
+ ],
},
{
'target_name': 'net_test_jni_headers',
@@ -2952,12 +1519,6 @@
],
'variables': {
'jni_gen_package': 'net',
- 'jni_generator_ptr_type': 'long',
- },
- 'direct_dependent_settings': {
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/net',
- ],
},
'includes': [ '../build/jni_generator.gypi' ],
},
@@ -2969,14 +1530,29 @@
},
'dependencies': [
'../base/base.gyp:base',
- 'cert_verify_result_android_java',
+ 'cert_verify_status_android_java',
'certificate_mime_types_java',
'net_errors_java',
'private_key_types_java',
+ 'remote_android_keystore_aidl',
],
'includes': [ '../build/java.gypi' ],
},
{
+ # Processes the interface files for communication with an Android KeyStore
+ # running in a separate process.
+ 'target_name': 'remote_android_keystore_aidl',
+ 'type': 'none',
+ 'variables': {
+ 'aidl_interface_file': '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl',
+ },
+ 'sources': [
+ '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl',
+ '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl',
+ ],
+ 'includes': [ '../build/java_aidl.gypi' ],
+ },
+ {
'target_name': 'net_java_test_support',
'type': 'none',
'variables': {
@@ -3022,14 +1598,14 @@
'includes': [ '../build/android/java_cpp_template.gypi' ],
},
{
- 'target_name': 'cert_verify_result_android_java',
+ 'target_name': 'cert_verify_status_android_java',
'type': 'none',
'sources': [
- 'android/java/CertVerifyResultAndroid.template',
+ 'android/java/CertVerifyStatusAndroid.template',
],
'variables': {
'package_name': 'org/chromium/net',
- 'template_deps': ['android/cert_verify_result_android_list.h'],
+ 'template_deps': ['android/cert_verify_status_android_list.h'],
},
'includes': [ '../build/android/java_cpp_template.gypi' ],
},
@@ -3047,10 +1623,7 @@
},
],
}],
- # Special target to wrap a gtest_target_type==shared_library
- # net_unittests into an android apk for execution.
- # See base.gyp for TODO(jrg)s about this strategy.
- ['OS == "android" and gtest_target_type == "shared_library"', {
+ ['OS == "android"', {
'targets': [
{
'target_name': 'net_unittests_apk',
@@ -3062,7 +1635,6 @@
],
'variables': {
'test_suite_name': 'net_unittests',
- 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)net_unittests<(SHARED_LIB_SUFFIX)',
},
'includes': [ '../build/apk_test.gypi' ],
},
diff --git a/chromium/net/net.gypi b/chromium/net/net.gypi
new file mode 100644
index 00000000000..ffbdaa3c42e
--- /dev/null
+++ b/chromium/net/net.gypi
@@ -0,0 +1,1717 @@
+# Copyright 2014 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.
+
+# This file is shared between the regular GYP build, the NaCl GYP build, and
+# the GN build. For GN support, it must have no conditionals or anything like
+# that beyond the simple one-level-deep dictionary of values.
+{
+ 'variables': {
+ # Subset of net source files that are compiled for NaCl (net_nacl target).
+ # These files must not depend on files listed in sources list in the net
+ # target.
+ 'net_nacl_common_sources' : [
+ 'base/address_family.h',
+ 'base/address_list.cc',
+ 'base/address_list.h',
+ 'base/auth.cc',
+ 'base/auth.h',
+ 'base/completion_callback.h',
+ 'base/connection_type_histograms.cc',
+ 'base/connection_type_histograms.h',
+ 'base/dns_util.cc',
+ 'base/dns_util.h',
+ 'base/escape.cc',
+ 'base/escape.h',
+ 'base/hash_value.cc',
+ 'base/hash_value.h',
+ 'base/load_timing_info.cc',
+ 'base/load_timing_info.h',
+ 'base/host_port_pair.cc',
+ 'base/host_port_pair.h',
+ 'base/io_buffer.cc',
+ 'base/io_buffer.h',
+ 'base/ip_endpoint.cc',
+ 'base/ip_endpoint.h',
+ 'base/net_error_list.h',
+ 'base/net_errors.cc',
+ 'base/net_errors.h',
+ 'base/net_errors_posix.cc',
+ 'base/net_export.h',
+ 'base/net_log.cc',
+ 'base/net_log.h',
+ 'base/net_log_event_type_list.h',
+ 'base/net_log_source_type_list.h',
+ 'base/net_module.cc',
+ 'base/net_module.h',
+ 'base/net_string_util.h',
+ 'base/net_string_util_icu.cc',
+ 'base/net_util.cc',
+ 'base/net_util.h',
+ 'base/net_util_icu.cc',
+ 'base/net_util_posix.cc',
+ 'base/openssl_private_key_store.h',
+ 'base/openssl_private_key_store_android.cc',
+ 'base/openssl_private_key_store_memory.cc',
+ 'base/rand_callback.h',
+ 'base/registry_controlled_domains/registry_controlled_domain.cc',
+ 'base/registry_controlled_domains/registry_controlled_domain.h',
+ 'base/sys_addrinfo.h',
+ 'base/zap.cc',
+ 'base/zap.h',
+ 'cert/asn1_util.cc',
+ 'cert/asn1_util.h',
+ 'cert/cert_database.cc',
+ 'cert/cert_database.h',
+ 'cert/cert_database_openssl.cc',
+ 'cert/cert_status_flags.cc',
+ 'cert/cert_status_flags.h',
+ 'cert/cert_verifier.cc',
+ 'cert/cert_verifier.h',
+ 'cert/cert_verify_result.cc',
+ 'cert/cert_verify_result.h',
+ 'cert/pem_tokenizer.cc',
+ 'cert/pem_tokenizer.h',
+ 'cert/signed_certificate_timestamp.cc',
+ 'cert/signed_certificate_timestamp.h',
+ 'cert/single_request_cert_verifier.cc',
+ 'cert/single_request_cert_verifier.h',
+ 'cert/signed_tree_head.h',
+ 'cert/x509_cert_types.cc',
+ 'cert/x509_cert_types.h',
+ 'cert/x509_certificate.cc',
+ 'cert/x509_certificate.h',
+ 'cert/x509_certificate_net_log_param.cc',
+ 'cert/x509_certificate_net_log_param.h',
+ 'cert/x509_certificate_openssl.cc',
+ 'cert/x509_util.cc',
+ 'cert/x509_util.h',
+ 'cert/x509_util_openssl.cc',
+ 'cert/x509_util_openssl.h',
+ 'http/http_auth_challenge_tokenizer.cc',
+ 'http/http_auth_challenge_tokenizer.h',
+ 'http/http_byte_range.cc',
+ 'http/http_byte_range.h',
+ 'http/http_log_util.cc',
+ 'http/http_log_util.h',
+ 'http/http_request_headers.cc',
+ 'http/http_request_headers.h',
+ 'http/http_response_headers.cc',
+ 'http/http_response_headers.h',
+ 'http/http_response_info.cc',
+ 'http/http_response_info.h',
+ 'http/http_security_headers.cc',
+ 'http/http_security_headers.h',
+ 'http/http_util.cc',
+ 'http/http_util.h',
+ 'http/http_util_icu.cc',
+ 'http/http_vary_data.cc',
+ 'http/http_vary_data.h',
+ 'http/transport_security_state.cc',
+ 'http/transport_security_state.h',
+ 'socket/client_socket_handle.cc',
+ 'socket/client_socket_handle.h',
+ 'socket/client_socket_pool_histograms.cc',
+ 'socket/client_socket_pool_histograms.h',
+ 'socket/next_proto.cc',
+ 'socket/next_proto.h',
+ 'socket/openssl_ssl_util.cc',
+ 'socket/openssl_ssl_util.h',
+ 'socket/socket.h',
+ 'socket/ssl_client_socket.cc',
+ 'socket/ssl_client_socket.h',
+ 'socket/ssl_client_socket_openssl.cc',
+ 'socket/ssl_client_socket_openssl.h',
+ 'socket/ssl_client_socket_pool.cc',
+ 'socket/ssl_client_socket_pool.h',
+ 'socket/ssl_error_params.cc',
+ 'socket/ssl_error_params.h',
+ 'socket/ssl_session_cache_openssl.cc',
+ 'socket/ssl_session_cache_openssl.h',
+ 'socket/ssl_socket.h',
+ 'ssl/default_server_bound_cert_store.cc',
+ 'ssl/default_server_bound_cert_store.h',
+ 'ssl/openssl_client_key_store.cc',
+ 'ssl/openssl_client_key_store.h',
+ 'ssl/server_bound_cert_service.cc',
+ 'ssl/server_bound_cert_service.h',
+ 'ssl/server_bound_cert_store.cc',
+ 'ssl/server_bound_cert_store.h',
+ 'ssl/signed_certificate_timestamp_and_status.cc',
+ 'ssl/signed_certificate_timestamp_and_status.h',
+ 'ssl/ssl_cert_request_info.cc',
+ 'ssl/ssl_cert_request_info.h',
+ 'ssl/ssl_client_auth_cache.cc',
+ 'ssl/ssl_client_auth_cache.h',
+ 'ssl/ssl_client_cert_type.h',
+ 'ssl/ssl_config.cc',
+ 'ssl/ssl_config.h',
+ 'ssl/ssl_info.cc',
+ 'ssl/ssl_info.h',
+ ],
+ 'net_non_nacl_sources': [
+ 'android/cert_verify_result_android.cc',
+ 'android/cert_verify_result_android.h',
+ 'android/cert_verify_status_android_list.h',
+ 'android/gurl_utils.cc',
+ 'android/gurl_utils.h',
+ 'android/android_private_key.cc',
+ 'android/android_private_key.h',
+ 'android/keystore.cc',
+ 'android/keystore.h',
+ 'android/keystore_openssl.cc',
+ 'android/keystore_openssl.h',
+ 'android/net_jni_registrar.cc',
+ 'android/net_jni_registrar.h',
+ 'android/network_change_notifier_android.cc',
+ 'android/network_change_notifier_android.h',
+ 'android/network_change_notifier_delegate_android.cc',
+ 'android/network_change_notifier_delegate_android.h',
+ 'android/network_change_notifier_factory_android.cc',
+ 'android/network_change_notifier_factory_android.h',
+ 'android/network_library.cc',
+ 'android/network_library.h',
+ 'base/address_tracker_linux.cc',
+ 'base/address_tracker_linux.h',
+ 'base/backoff_entry.cc',
+ 'base/backoff_entry.h',
+ 'base/bandwidth_metrics.cc',
+ 'base/bandwidth_metrics.h',
+ 'base/cache_type.h',
+ 'base/crypto_module.h',
+ 'base/crypto_module_nss.cc',
+ 'base/crypto_module_openssl.cc',
+ 'base/data_url.cc',
+ 'base/data_url.h',
+ 'base/directory_lister.cc',
+ 'base/directory_lister.h',
+ 'base/dns_reloader.cc',
+ 'base/dns_reloader.h',
+ 'base/expiring_cache.h',
+ 'base/file_stream.cc',
+ 'base/file_stream.h',
+ 'base/file_stream_context.cc',
+ 'base/file_stream_context.h',
+ 'base/file_stream_context_posix.cc',
+ 'base/file_stream_context_win.cc',
+ 'base/file_stream_whence.h',
+ 'base/filename_util.cc',
+ 'base/filename_util.h',
+ 'base/filename_util_icu.cc',
+ 'base/filename_util_internal.cc',
+ 'base/filename_util_internal.h',
+ 'base/filename_util_unsafe.cc',
+ 'base/filename_util_unsafe.h',
+ 'base/host_mapping_rules.cc',
+ 'base/host_mapping_rules.h',
+ 'base/int128.cc',
+ 'base/int128.h',
+ 'base/iovec.h',
+ 'base/ip_pattern.cc',
+ 'base/ip_pattern.h',
+ 'base/keygen_handler.cc',
+ 'base/keygen_handler.h',
+ 'base/keygen_handler_mac.cc',
+ 'base/keygen_handler_nss.cc',
+ 'base/keygen_handler_openssl.cc',
+ 'base/keygen_handler_win.cc',
+ 'base/linked_hash_map.h',
+ 'base/load_flags.h',
+ 'base/load_flags_list.h',
+ 'base/load_states.h',
+ 'base/load_states_list.h',
+ 'base/mime_sniffer.cc',
+ 'base/mime_sniffer.h',
+ 'base/mime_util.cc',
+ 'base/mime_util.h',
+ 'base/net_errors_win.cc',
+ 'base/net_log_logger.cc',
+ 'base/net_log_logger.h',
+ 'base/net_util_win.cc',
+ 'base/network_change_notifier.cc',
+ 'base/network_change_notifier.h',
+ 'base/network_change_notifier_factory.h',
+ 'base/network_change_notifier_linux.cc',
+ 'base/network_change_notifier_linux.h',
+ 'base/network_change_notifier_mac.cc',
+ 'base/network_change_notifier_mac.h',
+ 'base/network_change_notifier_win.cc',
+ 'base/network_change_notifier_win.h',
+ 'base/network_config_watcher_mac.cc',
+ 'base/network_config_watcher_mac.h',
+ 'base/network_delegate.cc',
+ 'base/network_delegate.h',
+ 'base/nss_memio.c',
+ 'base/nss_memio.h',
+ 'base/platform_mime_util.h',
+ # TODO(tc): gnome-vfs? xdgmime? /etc/mime.types?
+ 'base/platform_mime_util_linux.cc',
+ 'base/platform_mime_util_mac.mm',
+ 'base/platform_mime_util_win.cc',
+ 'base/prioritized_dispatcher.cc',
+ 'base/prioritized_dispatcher.h',
+ 'base/priority_queue.h',
+ 'base/request_priority.cc',
+ 'base/request_priority.h',
+ 'base/sdch_dictionary_fetcher.cc',
+ 'base/sdch_dictionary_fetcher.h',
+ 'base/sdch_manager.cc',
+ 'base/sdch_manager.h',
+ 'base/static_cookie_policy.cc',
+ 'base/static_cookie_policy.h',
+ 'base/test_data_stream.cc',
+ 'base/test_data_stream.h',
+ 'base/upload_bytes_element_reader.cc',
+ 'base/upload_bytes_element_reader.h',
+ 'base/upload_data_stream.cc',
+ 'base/upload_data_stream.h',
+ 'base/upload_element.cc',
+ 'base/upload_element.h',
+ 'base/upload_element_reader.cc',
+ 'base/upload_element_reader.h',
+ 'base/upload_file_element_reader.cc',
+ 'base/upload_file_element_reader.h',
+ 'base/upload_progress.h',
+ 'base/url_util.cc',
+ 'base/url_util.h',
+ 'base/winsock_init.cc',
+ 'base/winsock_init.h',
+ 'base/winsock_util.cc',
+ 'base/winsock_util.h',
+ 'cert/cert_database_android.cc',
+ 'cert/cert_database_ios.cc',
+ 'cert/cert_database_mac.cc',
+ 'cert/cert_database_nss.cc',
+ 'cert/cert_database_win.cc',
+ 'cert/cert_trust_anchor_provider.h',
+ 'cert/cert_verify_proc.cc',
+ 'cert/cert_verify_proc.h',
+ 'cert/cert_verify_proc_android.cc',
+ 'cert/cert_verify_proc_android.h',
+ 'cert/cert_verify_proc_mac.cc',
+ 'cert/cert_verify_proc_mac.h',
+ 'cert/cert_verify_proc_nss.cc',
+ 'cert/cert_verify_proc_nss.h',
+ 'cert/cert_verify_proc_openssl.cc',
+ 'cert/cert_verify_proc_openssl.h',
+ 'cert/cert_verify_proc_win.cc',
+ 'cert/cert_verify_proc_win.h',
+ 'cert/crl_set.cc',
+ 'cert/crl_set.h',
+ 'cert/ct_known_logs.cc',
+ 'cert/ct_known_logs.h',
+ 'cert/ct_log_response_parser.cc',
+ 'cert/ct_log_response_parser.h',
+ 'cert/ct_log_verifier.cc',
+ 'cert/ct_log_verifier.h',
+ 'cert/ct_log_verifier_nss.cc',
+ 'cert/ct_log_verifier_openssl.cc',
+ 'cert/ct_objects_extractor.h',
+ 'cert/ct_objects_extractor_nss.cc',
+ 'cert/ct_objects_extractor_openssl.cc',
+ 'cert/ct_serialization.cc',
+ 'cert/ct_serialization.h',
+ 'cert/ct_signed_certificate_timestamp_log_param.cc',
+ 'cert/ct_signed_certificate_timestamp_log_param.h',
+ 'cert/ct_verifier.h',
+ 'cert/ct_verify_result.cc',
+ 'cert/ct_verify_result.h',
+ 'cert/ev_root_ca_metadata.cc',
+ 'cert/ev_root_ca_metadata.h',
+ 'cert/jwk_serializer.h',
+ 'cert/jwk_serializer_nss.cc',
+ 'cert/jwk_serializer_openssl.cc',
+ 'cert/multi_log_ct_verifier.cc',
+ 'cert/multi_log_ct_verifier.h',
+ 'cert/multi_threaded_cert_verifier.cc',
+ 'cert/multi_threaded_cert_verifier.h',
+ 'cert/nss_cert_database.cc',
+ 'cert/nss_cert_database.h',
+ 'cert/nss_cert_database_chromeos.cc',
+ 'cert/nss_cert_database_chromeos.h',
+ 'cert/nss_profile_filter_chromeos.cc',
+ 'cert/nss_profile_filter_chromeos.h',
+ 'cert/scoped_nss_types.h',
+ 'cert/sct_status_flags.h',
+ 'cert/test_root_certs.cc',
+ 'cert/test_root_certs.h',
+ 'cert/test_root_certs_android.cc',
+ 'cert/test_root_certs_mac.cc',
+ 'cert/test_root_certs_nss.cc',
+ 'cert/test_root_certs_openssl.cc',
+ 'cert/test_root_certs_win.cc',
+ 'cert/x509_cert_types_mac.cc',
+ 'cert/x509_cert_types_win.cc',
+ 'cert/x509_certificate_ios.cc',
+ 'cert/x509_certificate_mac.cc',
+ 'cert/x509_certificate_nss.cc',
+ 'cert/x509_certificate_win.cc',
+ 'cert/x509_util_android.cc',
+ 'cert/x509_util_android.h',
+ 'cert/x509_util_ios.cc',
+ 'cert/x509_util_ios.h',
+ 'cert/x509_util_mac.cc',
+ 'cert/x509_util_mac.h',
+ 'cert/x509_util_nss.cc',
+ 'cert/x509_util_nss.h',
+ 'cookies/canonical_cookie.cc',
+ 'cookies/canonical_cookie.h',
+ 'cookies/cookie_constants.cc',
+ 'cookies/cookie_constants.h',
+ 'cookies/cookie_monster.cc',
+ 'cookies/cookie_monster.h',
+ 'cookies/cookie_options.h',
+ 'cookies/cookie_store.cc',
+ 'cookies/cookie_store.h',
+ 'cookies/cookie_util.cc',
+ 'cookies/cookie_util.h',
+ 'cookies/parsed_cookie.cc',
+ 'cookies/parsed_cookie.h',
+ 'disk_cache/blockfile/addr.cc',
+ 'disk_cache/blockfile/addr.h',
+ 'disk_cache/blockfile/backend_impl.cc',
+ 'disk_cache/blockfile/backend_impl.h',
+ 'disk_cache/blockfile/backend_impl_v3.cc',
+ 'disk_cache/blockfile/backend_impl_v3.h',
+ 'disk_cache/blockfile/backend_worker_v3.cc',
+ 'disk_cache/blockfile/backend_worker_v3.h',
+ 'disk_cache/blockfile/bitmap.cc',
+ 'disk_cache/blockfile/bitmap.h',
+ 'disk_cache/blockfile/block_bitmaps_v3.cc',
+ 'disk_cache/blockfile/block_bitmaps_v3.h',
+ 'disk_cache/blockfile/block_files.cc',
+ 'disk_cache/blockfile/block_files.h',
+ 'disk_cache/blockfile/disk_format.cc',
+ 'disk_cache/blockfile/disk_format.h',
+ 'disk_cache/blockfile/disk_format_base.h',
+ 'disk_cache/blockfile/disk_format_v3.h',
+ 'disk_cache/blockfile/entry_impl.cc',
+ 'disk_cache/blockfile/entry_impl.h',
+ 'disk_cache/blockfile/entry_impl_v3.cc',
+ 'disk_cache/blockfile/entry_impl_v3.h',
+ 'disk_cache/blockfile/errors.h',
+ 'disk_cache/blockfile/eviction.cc',
+ 'disk_cache/blockfile/eviction.h',
+ 'disk_cache/blockfile/eviction_v3.cc',
+ 'disk_cache/blockfile/eviction_v3.h',
+ 'disk_cache/blockfile/experiments.h',
+ 'disk_cache/blockfile/file.cc',
+ 'disk_cache/blockfile/file.h',
+ 'disk_cache/blockfile/file_block.h',
+ 'disk_cache/blockfile/file_ios.cc',
+ 'disk_cache/blockfile/file_lock.cc',
+ 'disk_cache/blockfile/file_lock.h',
+ 'disk_cache/blockfile/file_posix.cc',
+ 'disk_cache/blockfile/file_win.cc',
+ 'disk_cache/blockfile/histogram_macros.h',
+ 'disk_cache/blockfile/histogram_macros_v3.h',
+ 'disk_cache/blockfile/in_flight_backend_io.cc',
+ 'disk_cache/blockfile/in_flight_backend_io.h',
+ 'disk_cache/blockfile/in_flight_io.cc',
+ 'disk_cache/blockfile/in_flight_io.h',
+ 'disk_cache/blockfile/index_table_v3.cc',
+ 'disk_cache/blockfile/index_table_v3.h',
+ 'disk_cache/blockfile/mapped_file.cc',
+ 'disk_cache/blockfile/mapped_file.h',
+ 'disk_cache/blockfile/mapped_file_avoid_mmap_posix.cc',
+ 'disk_cache/blockfile/mapped_file_posix.cc',
+ 'disk_cache/blockfile/mapped_file_win.cc',
+ 'disk_cache/blockfile/rankings.cc',
+ 'disk_cache/blockfile/rankings.h',
+ 'disk_cache/blockfile/sparse_control.cc',
+ 'disk_cache/blockfile/sparse_control.h',
+ 'disk_cache/blockfile/stats.cc',
+ 'disk_cache/blockfile/stats.h',
+ 'disk_cache/blockfile/storage_block-inl.h',
+ 'disk_cache/blockfile/storage_block.h',
+ 'disk_cache/blockfile/stress_support.h',
+ 'disk_cache/blockfile/trace.cc',
+ 'disk_cache/blockfile/trace.h',
+ 'disk_cache/blockfile/webfonts_histogram.cc',
+ 'disk_cache/blockfile/webfonts_histogram.h',
+ 'disk_cache/cache_creator.cc',
+ 'disk_cache/cache_util.cc',
+ 'disk_cache/cache_util.h',
+ 'disk_cache/cache_util_posix.cc',
+ 'disk_cache/cache_util_win.cc',
+ 'disk_cache/disk_cache.h',
+ 'disk_cache/memory/mem_backend_impl.cc',
+ 'disk_cache/memory/mem_backend_impl.h',
+ 'disk_cache/memory/mem_entry_impl.cc',
+ 'disk_cache/memory/mem_entry_impl.h',
+ 'disk_cache/memory/mem_rankings.cc',
+ 'disk_cache/memory/mem_rankings.h',
+ 'disk_cache/net_log_parameters.cc',
+ 'disk_cache/net_log_parameters.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',
+ 'disk_cache/simple/simple_entry_operation.h',
+ 'disk_cache/simple/simple_histogram_macros.h' ,
+ 'disk_cache/simple/simple_index.cc',
+ 'disk_cache/simple/simple_index.h',
+ 'disk_cache/simple/simple_index_delegate.h',
+ 'disk_cache/simple/simple_index_file.cc',
+ 'disk_cache/simple/simple_index_file.h',
+ 'disk_cache/simple/simple_index_file_posix.cc',
+ 'disk_cache/simple/simple_index_file_win.cc',
+ 'disk_cache/simple/simple_net_log_parameters.cc',
+ 'disk_cache/simple/simple_net_log_parameters.h',
+ 'disk_cache/simple/simple_synchronous_entry.cc',
+ '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/tracing/tracing_cache_backend.cc',
+ 'disk_cache/tracing/tracing_cache_backend.h',
+ 'dns/address_sorter.h',
+ 'dns/address_sorter_posix.cc',
+ 'dns/address_sorter_posix.h',
+ 'dns/address_sorter_win.cc',
+ 'dns/dns_client.cc',
+ 'dns/dns_client.h',
+ 'dns/dns_config_service.cc',
+ 'dns/dns_config_service.h',
+ 'dns/dns_config_service_posix.cc',
+ 'dns/dns_config_service_posix.h',
+ 'dns/dns_config_service_win.cc',
+ 'dns/dns_config_service_win.h',
+ 'dns/dns_config_watcher_mac.cc',
+ 'dns/dns_config_watcher_mac.h',
+ 'dns/dns_hosts.cc',
+ 'dns/dns_hosts.h',
+ 'dns/dns_protocol.h',
+ 'dns/dns_query.cc',
+ 'dns/dns_query.h',
+ 'dns/dns_response.cc',
+ 'dns/dns_response.h',
+ 'dns/dns_session.cc',
+ 'dns/dns_session.h',
+ 'dns/dns_socket_pool.cc',
+ 'dns/dns_socket_pool.h',
+ 'dns/dns_transaction.cc',
+ 'dns/dns_transaction.h',
+ 'dns/host_cache.cc',
+ 'dns/host_cache.h',
+ 'dns/host_resolver.cc',
+ 'dns/host_resolver.h',
+ 'dns/host_resolver_impl.cc',
+ 'dns/host_resolver_impl.h',
+ 'dns/host_resolver_proc.cc',
+ 'dns/host_resolver_proc.h',
+ 'dns/mapped_host_resolver.cc',
+ 'dns/mapped_host_resolver.h',
+ 'dns/mdns_cache.cc',
+ 'dns/mdns_cache.h',
+ 'dns/mdns_client.cc',
+ 'dns/mdns_client.h',
+ 'dns/mdns_client_impl.cc',
+ 'dns/mdns_client_impl.h',
+ 'dns/notify_watcher_mac.cc',
+ 'dns/notify_watcher_mac.h',
+ 'dns/record_parsed.cc',
+ 'dns/record_parsed.h',
+ 'dns/record_rdata.cc',
+ 'dns/record_rdata.h',
+ 'dns/serial_worker.cc',
+ 'dns/serial_worker.h',
+ 'dns/single_request_host_resolver.cc',
+ 'dns/single_request_host_resolver.h',
+ 'filter/filter.cc',
+ 'filter/filter.h',
+ 'filter/gzip_filter.cc',
+ 'filter/gzip_filter.h',
+ 'filter/gzip_header.cc',
+ 'filter/gzip_header.h',
+ 'filter/sdch_filter.cc',
+ 'filter/sdch_filter.h',
+ 'ftp/ftp_auth_cache.cc',
+ 'ftp/ftp_auth_cache.h',
+ 'ftp/ftp_ctrl_response_buffer.cc',
+ 'ftp/ftp_ctrl_response_buffer.h',
+ 'ftp/ftp_directory_listing_parser.cc',
+ 'ftp/ftp_directory_listing_parser.h',
+ 'ftp/ftp_directory_listing_parser_ls.cc',
+ 'ftp/ftp_directory_listing_parser_ls.h',
+ 'ftp/ftp_directory_listing_parser_netware.cc',
+ 'ftp/ftp_directory_listing_parser_netware.h',
+ 'ftp/ftp_directory_listing_parser_os2.cc',
+ 'ftp/ftp_directory_listing_parser_os2.h',
+ 'ftp/ftp_directory_listing_parser_vms.cc',
+ 'ftp/ftp_directory_listing_parser_vms.h',
+ 'ftp/ftp_directory_listing_parser_windows.cc',
+ 'ftp/ftp_directory_listing_parser_windows.h',
+ 'ftp/ftp_network_layer.cc',
+ 'ftp/ftp_network_layer.h',
+ 'ftp/ftp_network_session.cc',
+ 'ftp/ftp_network_session.h',
+ 'ftp/ftp_network_transaction.cc',
+ 'ftp/ftp_network_transaction.h',
+ 'ftp/ftp_request_info.h',
+ 'ftp/ftp_response_info.cc',
+ 'ftp/ftp_response_info.h',
+ 'ftp/ftp_server_type_histograms.cc',
+ 'ftp/ftp_server_type_histograms.h',
+ 'ftp/ftp_transaction.h',
+ 'ftp/ftp_transaction_factory.h',
+ 'ftp/ftp_util.cc',
+ 'ftp/ftp_util.h',
+ 'http/des.cc',
+ 'http/des.h',
+ 'http/disk_cache_based_quic_server_info.cc',
+ 'http/disk_cache_based_quic_server_info.h',
+ 'http/failing_http_transaction_factory.cc',
+ 'http/failing_http_transaction_factory.h',
+ 'http/http_atom_list.h',
+ 'http/http_auth.cc',
+ 'http/http_auth.h',
+ 'http/http_auth_cache.cc',
+ 'http/http_auth_cache.h',
+ 'http/http_auth_controller.cc',
+ 'http/http_auth_controller.h',
+ 'http/http_auth_filter.cc',
+ 'http/http_auth_filter.h',
+ 'http/http_auth_filter_win.h',
+ 'http/http_auth_gssapi_posix.cc',
+ 'http/http_auth_gssapi_posix.h',
+ 'http/http_auth_handler.cc',
+ 'http/http_auth_handler.h',
+ 'http/http_auth_handler_basic.cc',
+ 'http/http_auth_handler_basic.h',
+ 'http/http_auth_handler_digest.cc',
+ 'http/http_auth_handler_digest.h',
+ 'http/http_auth_handler_factory.cc',
+ 'http/http_auth_handler_factory.h',
+ 'http/http_auth_handler_negotiate.cc',
+ 'http/http_auth_handler_negotiate.h',
+ 'http/http_auth_handler_ntlm.cc',
+ 'http/http_auth_handler_ntlm.h',
+ 'http/http_auth_handler_ntlm_portable.cc',
+ 'http/http_auth_handler_ntlm_win.cc',
+ 'http/http_auth_sspi_win.cc',
+ 'http/http_auth_sspi_win.h',
+ 'http/http_basic_state.cc',
+ 'http/http_basic_state.h',
+ 'http/http_basic_stream.cc',
+ 'http/http_basic_stream.h',
+ 'http/http_cache.cc',
+ 'http/http_cache.h',
+ 'http/http_cache_transaction.cc',
+ 'http/http_cache_transaction.h',
+ 'http/http_chunked_decoder.cc',
+ 'http/http_chunked_decoder.h',
+ 'http/http_content_disposition.cc',
+ 'http/http_content_disposition.h',
+ 'http/http_network_layer.cc',
+ 'http/http_network_layer.h',
+ 'http/http_network_session.cc',
+ 'http/http_network_session.h',
+ 'http/http_network_session_peer.cc',
+ 'http/http_network_session_peer.h',
+ 'http/http_network_transaction.cc',
+ 'http/http_network_transaction.h',
+ 'http/http_proxy_client_socket.cc',
+ 'http/http_proxy_client_socket.h',
+ 'http/http_proxy_client_socket_pool.cc',
+ 'http/http_proxy_client_socket_pool.h',
+ 'http/http_request_info.cc',
+ 'http/http_request_info.h',
+ 'http/http_response_body_drainer.cc',
+ 'http/http_response_body_drainer.h',
+ 'http/http_server_properties.cc',
+ 'http/http_server_properties.h',
+ 'http/http_server_properties_impl.cc',
+ 'http/http_server_properties_impl.h',
+ 'http/http_status_code.cc',
+ 'http/http_status_code.h',
+ 'http/http_stream.h',
+ 'http/http_stream_base.h',
+ 'http/http_stream_factory.cc',
+ 'http/http_stream_factory.h',
+ 'http/http_stream_factory_impl.cc',
+ 'http/http_stream_factory_impl.h',
+ 'http/http_stream_factory_impl_job.cc',
+ 'http/http_stream_factory_impl_job.h',
+ 'http/http_stream_factory_impl_request.cc',
+ 'http/http_stream_factory_impl_request.h',
+ 'http/http_stream_parser.cc',
+ 'http/http_stream_parser.h',
+ 'http/http_transaction.h',
+ 'http/http_transaction_factory.h',
+ 'http/http_version.h',
+ 'http/md4.cc',
+ 'http/md4.h',
+ 'http/partial_data.cc',
+ 'http/partial_data.h',
+ 'http/proxy_client_socket.cc',
+ 'http/proxy_client_socket.h',
+ 'http/proxy_connect_redirect_http_stream.cc',
+ 'http/proxy_connect_redirect_http_stream.h',
+ 'http/transport_security_persister.cc',
+ 'http/transport_security_persister.h',
+ 'http/transport_security_state_static.h',
+ 'http/url_security_manager.cc',
+ 'http/url_security_manager.h',
+ 'http/url_security_manager_posix.cc',
+ 'http/url_security_manager_win.cc',
+ 'ocsp/nss_ocsp.cc',
+ 'ocsp/nss_ocsp.h',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win.cc',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win.h',
+ 'proxy/dhcp_proxy_script_fetcher.cc',
+ 'proxy/dhcp_proxy_script_fetcher.h',
+ 'proxy/dhcp_proxy_script_fetcher_factory.cc',
+ 'proxy/dhcp_proxy_script_fetcher_factory.h',
+ 'proxy/dhcp_proxy_script_fetcher_win.cc',
+ 'proxy/dhcp_proxy_script_fetcher_win.h',
+ 'proxy/dhcpcsvc_init_win.cc',
+ 'proxy/dhcpcsvc_init_win.h',
+ 'proxy/multi_threaded_proxy_resolver.cc',
+ 'proxy/multi_threaded_proxy_resolver.h',
+ 'proxy/network_delegate_error_observer.cc',
+ 'proxy/network_delegate_error_observer.h',
+ 'proxy/polling_proxy_config_service.cc',
+ 'proxy/polling_proxy_config_service.h',
+ 'proxy/proxy_bypass_rules.cc',
+ 'proxy/proxy_bypass_rules.h',
+ 'proxy/proxy_config.cc',
+ 'proxy/proxy_config.h',
+ 'proxy/proxy_config_service.h',
+ 'proxy/proxy_config_service_android.cc',
+ 'proxy/proxy_config_service_android.h',
+ 'proxy/proxy_config_service_fixed.cc',
+ 'proxy/proxy_config_service_fixed.h',
+ 'proxy/proxy_config_service_ios.cc',
+ 'proxy/proxy_config_service_ios.h',
+ 'proxy/proxy_config_service_linux.cc',
+ 'proxy/proxy_config_service_linux.h',
+ 'proxy/proxy_config_service_mac.cc',
+ 'proxy/proxy_config_service_mac.h',
+ 'proxy/proxy_config_service_win.cc',
+ 'proxy/proxy_config_service_win.h',
+ 'proxy/proxy_config_source.cc',
+ 'proxy/proxy_config_source.h',
+ 'proxy/proxy_info.cc',
+ 'proxy/proxy_info.h',
+ 'proxy/proxy_list.cc',
+ 'proxy/proxy_list.h',
+ 'proxy/proxy_resolver.h',
+ 'proxy/proxy_resolver_error_observer.h',
+ 'proxy/proxy_resolver_mac.cc',
+ 'proxy/proxy_resolver_mac.h',
+ 'proxy/proxy_resolver_script.h',
+ 'proxy/proxy_resolver_script_data.cc',
+ 'proxy/proxy_resolver_script_data.h',
+ 'proxy/proxy_resolver_winhttp.cc',
+ 'proxy/proxy_resolver_winhttp.h',
+ 'proxy/proxy_retry_info.h',
+ 'proxy/proxy_script_decider.cc',
+ 'proxy/proxy_script_decider.h',
+ 'proxy/proxy_script_fetcher.h',
+ 'proxy/proxy_script_fetcher_impl.cc',
+ 'proxy/proxy_script_fetcher_impl.h',
+ 'proxy/proxy_server.cc',
+ 'proxy/proxy_server.h',
+ 'proxy/proxy_server_mac.cc',
+ 'proxy/proxy_service.cc',
+ 'proxy/proxy_service.h',
+ 'quic/congestion_control/cube_root.cc',
+ 'quic/congestion_control/cube_root.h',
+ 'quic/congestion_control/cubic.cc',
+ 'quic/congestion_control/cubic.h',
+ 'quic/congestion_control/fix_rate_receiver.cc',
+ 'quic/congestion_control/fix_rate_receiver.h',
+ 'quic/congestion_control/fix_rate_sender.cc',
+ 'quic/congestion_control/fix_rate_sender.h',
+ 'quic/congestion_control/hybrid_slow_start.cc',
+ 'quic/congestion_control/hybrid_slow_start.h',
+ 'quic/congestion_control/leaky_bucket.cc',
+ 'quic/congestion_control/leaky_bucket.h',
+ 'quic/congestion_control/loss_detection_interface.cc',
+ 'quic/congestion_control/loss_detection_interface.h',
+ 'quic/congestion_control/pacing_sender.cc',
+ 'quic/congestion_control/pacing_sender.h',
+ 'quic/congestion_control/receive_algorithm_interface.cc',
+ 'quic/congestion_control/receive_algorithm_interface.h',
+ 'quic/congestion_control/rtt_stats.cc',
+ 'quic/congestion_control/rtt_stats.h',
+ 'quic/congestion_control/send_algorithm_interface.cc',
+ 'quic/congestion_control/send_algorithm_interface.h',
+ 'quic/congestion_control/tcp_cubic_sender.cc',
+ 'quic/congestion_control/tcp_cubic_sender.h',
+ 'quic/congestion_control/tcp_loss_algorithm.cc',
+ 'quic/congestion_control/tcp_loss_algorithm.h',
+ 'quic/congestion_control/tcp_receiver.cc',
+ 'quic/congestion_control/tcp_receiver.h',
+ 'quic/congestion_control/time_loss_algorithm.cc',
+ 'quic/congestion_control/time_loss_algorithm.h',
+ 'quic/crypto/aead_base_decrypter.h',
+ 'quic/crypto/aead_base_decrypter_nss.cc',
+ 'quic/crypto/aead_base_decrypter_openssl.cc',
+ 'quic/crypto/aead_base_encrypter.h',
+ 'quic/crypto/aead_base_encrypter_nss.cc',
+ 'quic/crypto/aead_base_encrypter_openssl.cc',
+ 'quic/crypto/aes_128_gcm_12_decrypter.h',
+ 'quic/crypto/aes_128_gcm_12_decrypter_nss.cc',
+ 'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
+ 'quic/crypto/aes_128_gcm_12_encrypter.h',
+ 'quic/crypto/aes_128_gcm_12_encrypter_nss.cc',
+ 'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc',
+ 'quic/crypto/cert_compressor.cc',
+ 'quic/crypto/cert_compressor.h',
+ 'quic/crypto/chacha20_poly1305_decrypter.h',
+ 'quic/crypto/chacha20_poly1305_decrypter_nss.cc',
+ 'quic/crypto/chacha20_poly1305_decrypter_openssl.cc',
+ 'quic/crypto/chacha20_poly1305_encrypter.h',
+ 'quic/crypto/chacha20_poly1305_encrypter_nss.cc',
+ 'quic/crypto/chacha20_poly1305_encrypter_openssl.cc',
+ 'quic/crypto/channel_id.cc',
+ 'quic/crypto/channel_id.h',
+ 'quic/crypto/channel_id_nss.cc',
+ 'quic/crypto/channel_id_openssl.cc',
+ 'quic/crypto/common_cert_set.cc',
+ 'quic/crypto/common_cert_set.h',
+ 'quic/crypto/crypto_framer.cc',
+ 'quic/crypto/crypto_framer.h',
+ 'quic/crypto/crypto_handshake.cc',
+ 'quic/crypto/crypto_handshake.h',
+ 'quic/crypto/crypto_handshake_message.cc',
+ 'quic/crypto/crypto_handshake_message.h',
+ 'quic/crypto/crypto_protocol.h',
+ 'quic/crypto/crypto_secret_boxer.cc',
+ 'quic/crypto/crypto_secret_boxer.h',
+ 'quic/crypto/crypto_server_config_protobuf.cc',
+ 'quic/crypto/crypto_server_config_protobuf.h',
+ 'quic/crypto/crypto_utils.cc',
+ 'quic/crypto/crypto_utils.h',
+ 'quic/crypto/curve25519_key_exchange.cc',
+ 'quic/crypto/curve25519_key_exchange.h',
+ 'quic/crypto/ephemeral_key_source.h',
+ 'quic/crypto/key_exchange.h',
+ 'quic/crypto/local_strike_register_client.cc',
+ 'quic/crypto/local_strike_register_client.h',
+ 'quic/crypto/null_decrypter.cc',
+ 'quic/crypto/null_decrypter.h',
+ 'quic/crypto/null_encrypter.cc',
+ 'quic/crypto/null_encrypter.h',
+ 'quic/crypto/p256_key_exchange.h',
+ 'quic/crypto/p256_key_exchange_nss.cc',
+ 'quic/crypto/p256_key_exchange_openssl.cc',
+ 'quic/crypto/proof_source.h',
+ 'quic/crypto/proof_source_chromium.cc',
+ 'quic/crypto/proof_source_chromium.h',
+ 'quic/crypto/proof_verifier.h',
+ 'quic/crypto/proof_verifier_chromium.cc',
+ 'quic/crypto/proof_verifier_chromium.h',
+ 'quic/crypto/quic_crypto_client_config.cc',
+ 'quic/crypto/quic_crypto_client_config.h',
+ 'quic/crypto/quic_crypto_server_config.cc',
+ 'quic/crypto/quic_crypto_server_config.h',
+ 'quic/crypto/quic_decrypter.cc',
+ 'quic/crypto/quic_decrypter.h',
+ 'quic/crypto/quic_encrypter.cc',
+ 'quic/crypto/quic_encrypter.h',
+ 'quic/crypto/quic_random.cc',
+ 'quic/crypto/quic_random.h',
+ 'quic/crypto/quic_server_info.cc',
+ 'quic/crypto/quic_server_info.h',
+ 'quic/crypto/scoped_evp_aead_ctx.cc',
+ 'quic/crypto/scoped_evp_aead_ctx.h',
+ 'quic/crypto/source_address_token.cc',
+ 'quic/crypto/source_address_token.h',
+ 'quic/crypto/strike_register.cc',
+ 'quic/crypto/strike_register.h',
+ 'quic/crypto/strike_register_client.h',
+ 'quic/iovector.cc',
+ 'quic/iovector.h',
+ 'quic/port_suggester.cc',
+ 'quic/port_suggester.h',
+ 'quic/quic_ack_notifier.cc',
+ 'quic/quic_ack_notifier.h',
+ 'quic/quic_ack_notifier_manager.cc',
+ 'quic/quic_ack_notifier_manager.h',
+ 'quic/quic_address_mismatch.cc',
+ 'quic/quic_address_mismatch.h',
+ 'quic/quic_alarm.cc',
+ 'quic/quic_alarm.h',
+ 'quic/quic_bandwidth.cc',
+ 'quic/quic_bandwidth.h',
+ 'quic/quic_blocked_writer_interface.h',
+ 'quic/quic_client_session.cc',
+ 'quic/quic_client_session.h',
+ 'quic/quic_client_session_base.cc',
+ 'quic/quic_client_session_base.h',
+ 'quic/quic_clock.cc',
+ 'quic/quic_clock.h',
+ 'quic/quic_config.cc',
+ 'quic/quic_config.h',
+ 'quic/quic_connection.cc',
+ 'quic/quic_connection.h',
+ 'quic/quic_connection_helper.cc',
+ 'quic/quic_connection_helper.h',
+ 'quic/quic_connection_logger.cc',
+ 'quic/quic_connection_logger.h',
+ 'quic/quic_connection_stats.cc',
+ 'quic/quic_connection_stats.h',
+ 'quic/quic_crypto_client_stream.cc',
+ 'quic/quic_crypto_client_stream.h',
+ 'quic/quic_crypto_client_stream_factory.h',
+ 'quic/quic_crypto_server_stream.cc',
+ 'quic/quic_crypto_server_stream.h',
+ 'quic/quic_crypto_stream.cc',
+ 'quic/quic_crypto_stream.h',
+ 'quic/quic_data_reader.cc',
+ 'quic/quic_data_reader.h',
+ 'quic/quic_data_stream.cc',
+ 'quic/quic_data_stream.h',
+ 'quic/quic_data_writer.cc',
+ 'quic/quic_data_writer.h',
+ 'quic/quic_default_packet_writer.cc',
+ 'quic/quic_default_packet_writer.h',
+ 'quic/quic_fec_group.cc',
+ 'quic/quic_fec_group.h',
+ 'quic/quic_flags.cc',
+ 'quic/quic_flags.h',
+ 'quic/quic_flow_controller.cc',
+ 'quic/quic_flow_controller.h',
+ 'quic/quic_framer.cc',
+ 'quic/quic_framer.h',
+ 'quic/quic_headers_stream.cc',
+ 'quic/quic_headers_stream.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',
+ 'quic/quic_packet_generator.h',
+ 'quic/quic_packet_writer.h',
+ 'quic/quic_protocol.cc',
+ 'quic/quic_protocol.h',
+ 'quic/quic_received_packet_manager.cc',
+ 'quic/quic_received_packet_manager.h',
+ 'quic/quic_reliable_client_stream.cc',
+ '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_server_id.cc',
+ 'quic/quic_server_id.h',
+ 'quic/quic_session.cc',
+ 'quic/quic_session.h',
+ 'quic/quic_socket_address_coder.cc',
+ 'quic/quic_socket_address_coder.h',
+ 'quic/quic_stream_factory.cc',
+ 'quic/quic_stream_factory.h',
+ 'quic/quic_stream_sequencer.cc',
+ 'quic/quic_stream_sequencer.h',
+ 'quic/quic_time.cc',
+ 'quic/quic_time.h',
+ 'quic/quic_types.cc',
+ 'quic/quic_types.h',
+ 'quic/quic_unacked_packet_map.cc',
+ 'quic/quic_unacked_packet_map.h',
+ 'quic/quic_utils.cc',
+ 'quic/quic_utils.h',
+ 'quic/quic_utils_chromium.h',
+ 'quic/quic_write_blocked_list.cc',
+ 'quic/quic_write_blocked_list.h',
+ 'quic/reliable_quic_stream.cc',
+ 'quic/reliable_quic_stream.h',
+ 'quic/spdy_utils.cc',
+ 'quic/spdy_utils.h',
+ 'socket/client_socket_factory.cc',
+ 'socket/client_socket_factory.h',
+ 'socket/client_socket_pool.cc',
+ 'socket/client_socket_pool.h',
+ 'socket/client_socket_pool_base.cc',
+ 'socket/client_socket_pool_base.h',
+ 'socket/client_socket_pool_manager.cc',
+ 'socket/client_socket_pool_manager.h',
+ 'socket/client_socket_pool_manager_impl.cc',
+ 'socket/client_socket_pool_manager_impl.h',
+ 'socket/nss_ssl_util.cc',
+ 'socket/nss_ssl_util.h',
+ 'socket/server_socket.h',
+ 'socket/socket_descriptor.cc',
+ 'socket/socket_descriptor.h',
+ 'socket/socket_net_log_params.cc',
+ 'socket/socket_net_log_params.h',
+ 'socket/socks5_client_socket.cc',
+ 'socket/socks5_client_socket.h',
+ 'socket/socks_client_socket.cc',
+ 'socket/socks_client_socket.h',
+ 'socket/socks_client_socket_pool.cc',
+ 'socket/socks_client_socket_pool.h',
+ 'socket/ssl_client_socket_nss.cc',
+ 'socket/ssl_client_socket_nss.h',
+ 'socket/ssl_server_socket.h',
+ 'socket/ssl_server_socket_nss.cc',
+ 'socket/ssl_server_socket_nss.h',
+ 'socket/ssl_server_socket_openssl.cc',
+ 'socket/ssl_server_socket_openssl.h',
+ 'socket/stream_listen_socket.cc',
+ 'socket/stream_listen_socket.h',
+ 'socket/stream_socket.cc',
+ 'socket/stream_socket.h',
+ 'socket/tcp_client_socket.cc',
+ 'socket/tcp_client_socket.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',
+ 'socket/tcp_socket_win.cc',
+ 'socket/tcp_socket_win.h',
+ 'socket/transport_client_socket_pool.cc',
+ 'socket/transport_client_socket_pool.h',
+ 'socket/unix_domain_socket_posix.cc',
+ 'socket/unix_domain_socket_posix.h',
+ 'socket_stream/socket_stream.cc',
+ 'socket_stream/socket_stream.h',
+ 'socket_stream/socket_stream_job.cc',
+ 'socket_stream/socket_stream_job.h',
+ 'socket_stream/socket_stream_job_manager.cc',
+ 'socket_stream/socket_stream_job_manager.h',
+ 'socket_stream/socket_stream_metrics.cc',
+ 'socket_stream/socket_stream_metrics.h',
+ 'spdy/buffered_spdy_framer.cc',
+ 'spdy/buffered_spdy_framer.h',
+ 'spdy/fuzzing/hpack_fuzz_util.cc',
+ 'spdy/fuzzing/hpack_fuzz_util.h',
+ 'spdy/hpack_constants.cc',
+ 'spdy/hpack_constants.h',
+ 'spdy/hpack_decoder.cc',
+ 'spdy/hpack_decoder.h',
+ 'spdy/hpack_encoder.cc',
+ 'spdy/hpack_encoder.h',
+ 'spdy/hpack_entry.cc',
+ 'spdy/hpack_entry.h',
+ 'spdy/hpack_header_table.cc',
+ 'spdy/hpack_header_table.h',
+ 'spdy/hpack_huffman_aggregator.cc',
+ 'spdy/hpack_huffman_aggregator.h',
+ 'spdy/hpack_huffman_table.cc',
+ 'spdy/hpack_huffman_table.h',
+ 'spdy/hpack_input_stream.cc',
+ 'spdy/hpack_input_stream.h',
+ 'spdy/hpack_output_stream.cc',
+ 'spdy/hpack_output_stream.h',
+ 'spdy/hpack_string_util.cc',
+ 'spdy/hpack_string_util.h',
+ 'spdy/spdy_bitmasks.h',
+ 'spdy/spdy_buffer.cc',
+ 'spdy/spdy_buffer.h',
+ 'spdy/spdy_buffer_producer.cc',
+ 'spdy/spdy_buffer_producer.h',
+ 'spdy/spdy_frame_builder.cc',
+ 'spdy/spdy_frame_builder.h',
+ 'spdy/spdy_frame_reader.cc',
+ 'spdy/spdy_frame_reader.h',
+ 'spdy/spdy_framer.cc',
+ 'spdy/spdy_framer.h',
+ 'spdy/spdy_header_block.cc',
+ 'spdy/spdy_header_block.h',
+ 'spdy/spdy_headers_block_parser.cc',
+ 'spdy/spdy_headers_block_parser.h',
+ 'spdy/spdy_http_stream.cc',
+ 'spdy/spdy_http_stream.h',
+ 'spdy/spdy_http_utils.cc',
+ 'spdy/spdy_http_utils.h',
+ 'spdy/spdy_pinnable_buffer_piece.cc',
+ 'spdy/spdy_pinnable_buffer_piece.h',
+ 'spdy/spdy_prefixed_buffer_reader.cc',
+ 'spdy/spdy_prefixed_buffer_reader.h',
+ 'spdy/spdy_priority_forest.h',
+ 'spdy/spdy_protocol.cc',
+ 'spdy/spdy_protocol.h',
+ 'spdy/spdy_proxy_client_socket.cc',
+ 'spdy/spdy_proxy_client_socket.h',
+ 'spdy/spdy_read_queue.cc',
+ 'spdy/spdy_read_queue.h',
+ 'spdy/spdy_session.cc',
+ 'spdy/spdy_session.h',
+ 'spdy/spdy_session_key.cc',
+ 'spdy/spdy_session_key.h',
+ 'spdy/spdy_session_pool.cc',
+ 'spdy/spdy_session_pool.h',
+ 'spdy/spdy_stream.cc',
+ 'spdy/spdy_stream.h',
+ 'spdy/spdy_websocket_stream.cc',
+ 'spdy/spdy_websocket_stream.h',
+ 'spdy/spdy_write_queue.cc',
+ 'spdy/spdy_write_queue.h',
+ 'spdy/write_blocked_list.h',
+ 'ssl/client_cert_store.h',
+ 'ssl/client_cert_store_chromeos.cc',
+ 'ssl/client_cert_store_chromeos.h',
+ 'ssl/client_cert_store_mac.cc',
+ 'ssl/client_cert_store_mac.h',
+ 'ssl/client_cert_store_nss.cc',
+ 'ssl/client_cert_store_nss.h',
+ 'ssl/client_cert_store_win.cc',
+ 'ssl/client_cert_store_win.h',
+ 'ssl/ssl_cipher_suite_names.cc',
+ 'ssl/ssl_cipher_suite_names.h',
+ 'ssl/ssl_config_service.cc',
+ 'ssl/ssl_config_service.h',
+ 'ssl/ssl_config_service_defaults.cc',
+ 'ssl/ssl_config_service_defaults.h',
+ 'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
+ 'third_party/mozilla_security_manager/nsKeygenHandler.h',
+ 'third_party/mozilla_security_manager/nsNSSCertificateDB.cpp',
+ 'third_party/mozilla_security_manager/nsNSSCertificateDB.h',
+ 'third_party/mozilla_security_manager/nsPKCS12Blob.cpp',
+ 'third_party/mozilla_security_manager/nsPKCS12Blob.h',
+ 'udp/datagram_client_socket.h',
+ 'udp/datagram_server_socket.h',
+ 'udp/datagram_socket.h',
+ 'udp/udp_client_socket.cc',
+ 'udp/udp_client_socket.h',
+ 'udp/udp_net_log_parameters.cc',
+ 'udp/udp_net_log_parameters.h',
+ 'udp/udp_server_socket.cc',
+ 'udp/udp_server_socket.h',
+ 'udp/udp_socket.h',
+ 'udp/udp_socket_libevent.cc',
+ 'udp/udp_socket_libevent.h',
+ 'udp/udp_socket_win.cc',
+ 'udp/udp_socket_win.h',
+ 'url_request/data_protocol_handler.cc',
+ 'url_request/data_protocol_handler.h',
+ 'url_request/file_protocol_handler.cc',
+ 'url_request/file_protocol_handler.h',
+ 'url_request/fraudulent_certificate_reporter.h',
+ 'url_request/ftp_protocol_handler.cc',
+ 'url_request/ftp_protocol_handler.h',
+ 'url_request/http_user_agent_settings.h',
+ 'url_request/static_http_user_agent_settings.cc',
+ 'url_request/static_http_user_agent_settings.h',
+ 'url_request/url_fetcher.cc',
+ 'url_request/url_fetcher.h',
+ 'url_request/url_fetcher_core.cc',
+ 'url_request/url_fetcher_core.h',
+ 'url_request/url_fetcher_delegate.cc',
+ 'url_request/url_fetcher_delegate.h',
+ 'url_request/url_fetcher_factory.h',
+ 'url_request/url_fetcher_impl.cc',
+ 'url_request/url_fetcher_impl.h',
+ 'url_request/url_fetcher_response_writer.cc',
+ 'url_request/url_fetcher_response_writer.h',
+ 'url_request/url_range_request_job.cc',
+ 'url_request/url_range_request_job.h',
+ 'url_request/url_request.cc',
+ 'url_request/url_request.h',
+ 'url_request/url_request_about_job.cc',
+ 'url_request/url_request_about_job.h',
+ 'url_request/url_request_context.cc',
+ 'url_request/url_request_context.h',
+ 'url_request/url_request_context_builder.cc',
+ 'url_request/url_request_context_builder.h',
+ 'url_request/url_request_context_getter.cc',
+ 'url_request/url_request_context_getter.h',
+ 'url_request/url_request_context_storage.cc',
+ 'url_request/url_request_context_storage.h',
+ 'url_request/url_request_data_job.cc',
+ 'url_request/url_request_data_job.h',
+ 'url_request/url_request_error_job.cc',
+ 'url_request/url_request_error_job.h',
+ 'url_request/url_request_file_dir_job.cc',
+ 'url_request/url_request_file_dir_job.h',
+ 'url_request/url_request_file_job.cc',
+ 'url_request/url_request_file_job.h',
+ 'url_request/url_request_filter.cc',
+ 'url_request/url_request_filter.h',
+ 'url_request/url_request_ftp_job.cc',
+ 'url_request/url_request_ftp_job.h',
+ 'url_request/url_request_http_job.cc',
+ 'url_request/url_request_http_job.h',
+ 'url_request/url_request_intercepting_job_factory.cc',
+ 'url_request/url_request_intercepting_job_factory.h',
+ 'url_request/url_request_interceptor.cc',
+ 'url_request/url_request_interceptor.h',
+ 'url_request/url_request_job.cc',
+ 'url_request/url_request_job.h',
+ 'url_request/url_request_job_factory.cc',
+ 'url_request/url_request_job_factory.h',
+ 'url_request/url_request_job_factory_impl.cc',
+ 'url_request/url_request_job_factory_impl.h',
+ 'url_request/url_request_job_manager.cc',
+ 'url_request/url_request_job_manager.h',
+ 'url_request/url_request_netlog_params.cc',
+ 'url_request/url_request_netlog_params.h',
+ 'url_request/url_request_redirect_job.cc',
+ 'url_request/url_request_redirect_job.h',
+ 'url_request/url_request_simple_job.cc',
+ 'url_request/url_request_simple_job.h',
+ 'url_request/url_request_status.h',
+ 'url_request/url_request_test_job.cc',
+ 'url_request/url_request_test_job.h',
+ 'url_request/url_request_throttler_entry.cc',
+ 'url_request/url_request_throttler_entry.h',
+ 'url_request/url_request_throttler_entry_interface.h',
+ 'url_request/url_request_throttler_header_adapter.cc',
+ 'url_request/url_request_throttler_header_adapter.h',
+ 'url_request/url_request_throttler_header_interface.h',
+ 'url_request/url_request_throttler_manager.cc',
+ 'url_request/url_request_throttler_manager.h',
+ 'url_request/view_cache_helper.cc',
+ 'url_request/view_cache_helper.h',
+ 'url_request/websocket_handshake_userdata_key.cc',
+ 'url_request/websocket_handshake_userdata_key.h',
+ 'websockets/websocket_basic_handshake_stream.cc',
+ 'websockets/websocket_basic_handshake_stream.h',
+ 'websockets/websocket_basic_stream.cc',
+ 'websockets/websocket_basic_stream.h',
+ 'websockets/websocket_channel.cc',
+ 'websockets/websocket_channel.h',
+ 'websockets/websocket_deflate_predictor.h',
+ 'websockets/websocket_deflate_predictor_impl.cc',
+ 'websockets/websocket_deflate_predictor_impl.h',
+ 'websockets/websocket_deflate_stream.cc',
+ 'websockets/websocket_deflate_stream.h',
+ 'websockets/websocket_deflater.cc',
+ 'websockets/websocket_deflater.h',
+ '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',
+ 'websockets/websocket_frame_parser.h',
+ 'websockets/websocket_handshake_constants.cc',
+ 'websockets/websocket_handshake_constants.h',
+ 'websockets/websocket_handshake_handler.cc',
+ 'websockets/websocket_handshake_handler.h',
+ 'websockets/websocket_handshake_request_info.cc',
+ 'websockets/websocket_handshake_request_info.h',
+ 'websockets/websocket_handshake_response_info.cc',
+ 'websockets/websocket_handshake_response_info.h',
+ 'websockets/websocket_handshake_stream_base.h',
+ 'websockets/websocket_handshake_stream_create_helper.cc',
+ 'websockets/websocket_handshake_stream_create_helper.h',
+ 'websockets/websocket_inflater.cc',
+ 'websockets/websocket_inflater.h',
+ 'websockets/websocket_job.cc',
+ 'websockets/websocket_job.h',
+ 'websockets/websocket_mux.h',
+ 'websockets/websocket_net_log_params.cc',
+ 'websockets/websocket_net_log_params.h',
+ 'websockets/websocket_stream.cc',
+ 'websockets/websocket_stream.h',
+ 'websockets/websocket_throttle.cc',
+ 'websockets/websocket_throttle.h',
+ ],
+ 'net_test_sources': [
+ 'android/keystore_unittest.cc',
+ 'android/network_change_notifier_android_unittest.cc',
+ 'base/address_list_unittest.cc',
+ 'base/address_tracker_linux_unittest.cc',
+ 'base/backoff_entry_unittest.cc',
+ 'base/data_url_unittest.cc',
+ 'base/directory_lister_unittest.cc',
+ 'base/dns_util_unittest.cc',
+ 'base/escape_unittest.cc',
+ 'base/expiring_cache_unittest.cc',
+ 'base/file_stream_unittest.cc',
+ 'base/filename_util_unittest.cc',
+ 'base/host_mapping_rules_unittest.cc',
+ 'base/host_port_pair_unittest.cc',
+ 'base/int128_unittest.cc',
+ 'base/ip_endpoint_unittest.cc',
+ 'base/ip_pattern_unittest.cc',
+ 'base/keygen_handler_unittest.cc',
+ 'base/mime_sniffer_unittest.cc',
+ 'base/mime_util_unittest.cc',
+ 'base/net_log_logger_unittest.cc',
+ 'base/net_log_unittest.cc',
+ 'base/net_log_unittest.h',
+ 'base/net_util_unittest.cc',
+ 'base/net_util_icu_unittest.cc',
+ 'base/network_change_notifier_win_unittest.cc',
+ 'base/prioritized_dispatcher_unittest.cc',
+ 'base/priority_queue_unittest.cc',
+ 'base/registry_controlled_domains/registry_controlled_domain_unittest.cc',
+ 'base/sdch_manager_unittest.cc',
+ 'base/static_cookie_policy_unittest.cc',
+ 'base/test_completion_callback_unittest.cc',
+ 'base/upload_bytes_element_reader_unittest.cc',
+ 'base/upload_data_stream_unittest.cc',
+ 'base/upload_file_element_reader_unittest.cc',
+ 'base/url_util_unittest.cc',
+ 'cert/cert_verify_proc_unittest.cc',
+ 'cert/crl_set_unittest.cc',
+ 'cert/ct_log_response_parser_unittest.cc',
+ 'cert/ct_log_verifier_unittest.cc',
+ 'cert/ct_objects_extractor_unittest.cc',
+ 'cert/ct_serialization_unittest.cc',
+ 'cert/ev_root_ca_metadata_unittest.cc',
+ 'cert/jwk_serializer_unittest.cc',
+ 'cert/multi_log_ct_verifier_unittest.cc',
+ 'cert/multi_threaded_cert_verifier_unittest.cc',
+ 'cert/nss_cert_database_chromeos_unittest.cc',
+ 'cert/nss_cert_database_unittest.cc',
+ 'cert/nss_profile_filter_chromeos_unittest.cc',
+ 'cert/pem_tokenizer_unittest.cc',
+ 'cert/signed_certificate_timestamp_unittest.cc',
+ 'cert/test_root_certs_unittest.cc',
+ 'cert/x509_cert_types_unittest.cc',
+ 'cert/x509_certificate_unittest.cc',
+ 'cert/x509_util_nss_unittest.cc',
+ 'cert/x509_util_openssl_unittest.cc',
+ 'cert/x509_util_unittest.cc',
+ 'cookies/canonical_cookie_unittest.cc',
+ 'cookies/cookie_constants_unittest.cc',
+ 'cookies/cookie_monster_unittest.cc',
+ 'cookies/cookie_store_unittest.h',
+ 'cookies/cookie_util_unittest.cc',
+ 'cookies/parsed_cookie_unittest.cc',
+ 'disk_cache/backend_unittest.cc',
+ 'disk_cache/blockfile/addr_unittest.cc',
+ 'disk_cache/blockfile/bitmap_unittest.cc',
+ 'disk_cache/blockfile/block_bitmaps_v3_unittest.cc',
+ 'disk_cache/blockfile/block_files_unittest.cc',
+ 'disk_cache/blockfile/index_table_v3_unittest.cc',
+ 'disk_cache/blockfile/mapped_file_unittest.cc',
+ 'disk_cache/blockfile/storage_block_unittest.cc',
+ 'disk_cache/cache_util_unittest.cc',
+ 'disk_cache/entry_unittest.cc',
+ 'disk_cache/simple/simple_index_file_unittest.cc',
+ 'disk_cache/simple/simple_index_unittest.cc',
+ 'disk_cache/simple/simple_test_util.cc',
+ 'disk_cache/simple/simple_test_util.h',
+ 'disk_cache/simple/simple_util_unittest.cc',
+ 'disk_cache/simple/simple_version_upgrade_unittest.cc',
+ 'dns/address_sorter_posix_unittest.cc',
+ 'dns/address_sorter_unittest.cc',
+ 'dns/dns_config_service_posix_unittest.cc',
+ 'dns/dns_config_service_unittest.cc',
+ 'dns/dns_config_service_win_unittest.cc',
+ 'dns/dns_hosts_unittest.cc',
+ 'dns/dns_query_unittest.cc',
+ 'dns/dns_response_unittest.cc',
+ 'dns/dns_session_unittest.cc',
+ 'dns/dns_transaction_unittest.cc',
+ 'dns/host_cache_unittest.cc',
+ 'dns/host_resolver_impl_unittest.cc',
+ 'dns/mapped_host_resolver_unittest.cc',
+ 'dns/mdns_cache_unittest.cc',
+ 'dns/mdns_client_unittest.cc',
+ 'dns/record_parsed_unittest.cc',
+ 'dns/record_rdata_unittest.cc',
+ 'dns/serial_worker_unittest.cc',
+ 'dns/single_request_host_resolver_unittest.cc',
+ 'filter/filter_unittest.cc',
+ 'filter/gzip_filter_unittest.cc',
+ 'filter/mock_filter_context.cc',
+ 'filter/mock_filter_context.h',
+ 'filter/sdch_filter_unittest.cc',
+ 'ftp/ftp_auth_cache_unittest.cc',
+ 'ftp/ftp_ctrl_response_buffer_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_ls_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_netware_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_os2_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_unittest.h',
+ 'ftp/ftp_directory_listing_parser_vms_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_windows_unittest.cc',
+ 'ftp/ftp_network_transaction_unittest.cc',
+ 'ftp/ftp_util_unittest.cc',
+ 'http/des_unittest.cc',
+ 'http/disk_cache_based_quic_server_info_unittest.cc',
+ 'http/http_auth_cache_unittest.cc',
+ 'http/http_auth_challenge_tokenizer_unittest.cc',
+ 'http/http_auth_controller_unittest.cc',
+ 'http/http_auth_filter_unittest.cc',
+ 'http/http_auth_gssapi_posix_unittest.cc',
+ 'http/http_auth_handler_basic_unittest.cc',
+ 'http/http_auth_handler_digest_unittest.cc',
+ 'http/http_auth_handler_factory_unittest.cc',
+ 'http/http_auth_handler_mock.cc',
+ 'http/http_auth_handler_mock.h',
+ 'http/http_auth_handler_negotiate_unittest.cc',
+ 'http/http_auth_handler_unittest.cc',
+ 'http/http_auth_sspi_win_unittest.cc',
+ 'http/http_auth_unittest.cc',
+ 'http/http_basic_state_unittest.cc',
+ 'http/http_byte_range_unittest.cc',
+ 'http/http_cache_unittest.cc',
+ 'http/http_chunked_decoder_unittest.cc',
+ 'http/http_content_disposition_unittest.cc',
+ 'http/http_log_util_unittest.cc',
+ 'http/http_network_layer_unittest.cc',
+ 'http/http_network_transaction_ssl_unittest.cc',
+ 'http/http_network_transaction_unittest.cc',
+ 'http/http_proxy_client_socket_pool_unittest.cc',
+ 'http/http_request_headers_unittest.cc',
+ 'http/http_response_body_drainer_unittest.cc',
+ 'http/http_response_headers_unittest.cc',
+ 'http/http_security_headers_unittest.cc',
+ 'http/http_server_properties_impl_unittest.cc',
+ 'http/http_status_code_unittest.cc',
+ 'http/http_stream_factory_impl_request_unittest.cc',
+ 'http/http_stream_factory_impl_unittest.cc',
+ 'http/http_stream_parser_unittest.cc',
+ 'http/http_util_unittest.cc',
+ 'http/http_vary_data_unittest.cc',
+ 'http/mock_allow_url_security_manager.cc',
+ 'http/mock_allow_url_security_manager.h',
+ 'http/mock_gssapi_library_posix.cc',
+ 'http/mock_gssapi_library_posix.h',
+ 'http/mock_http_cache.cc',
+ 'http/mock_http_cache.h',
+ 'http/mock_sspi_library_win.cc',
+ 'http/mock_sspi_library_win.h',
+ 'http/transport_security_persister_unittest.cc',
+ 'http/transport_security_state_unittest.cc',
+ 'http/url_security_manager_unittest.cc',
+ 'ocsp/nss_ocsp_unittest.cc',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc',
+ 'proxy/dhcp_proxy_script_fetcher_factory_unittest.cc',
+ 'proxy/dhcp_proxy_script_fetcher_win_unittest.cc',
+ 'proxy/multi_threaded_proxy_resolver_unittest.cc',
+ 'proxy/network_delegate_error_observer_unittest.cc',
+ 'proxy/proxy_bypass_rules_unittest.cc',
+ 'proxy/proxy_config_service_android_unittest.cc',
+ 'proxy/proxy_config_service_linux_unittest.cc',
+ 'proxy/proxy_config_service_win_unittest.cc',
+ 'proxy/proxy_config_unittest.cc',
+ 'proxy/proxy_info_unittest.cc',
+ 'proxy/proxy_list_unittest.cc',
+ 'proxy/proxy_resolver_v8_tracing_unittest.cc',
+ 'proxy/proxy_resolver_v8_unittest.cc',
+ 'proxy/proxy_script_decider_unittest.cc',
+ 'proxy/proxy_script_fetcher_impl_unittest.cc',
+ 'proxy/proxy_server_unittest.cc',
+ 'proxy/proxy_service_unittest.cc',
+ 'quic/congestion_control/cube_root_test.cc',
+ 'quic/congestion_control/cubic_test.cc',
+ 'quic/congestion_control/fix_rate_test.cc',
+ 'quic/congestion_control/hybrid_slow_start_test.cc',
+ 'quic/congestion_control/leaky_bucket_test.cc',
+ 'quic/congestion_control/pacing_sender_test.cc',
+ 'quic/congestion_control/rtt_stats_test.cc',
+ 'quic/congestion_control/send_algorithm_simulator.cc',
+ 'quic/congestion_control/send_algorithm_simulator.h',
+ 'quic/congestion_control/tcp_cubic_sender_test.cc',
+ 'quic/congestion_control/tcp_loss_algorithm_test.cc',
+ 'quic/congestion_control/tcp_receiver_test.cc',
+ 'quic/congestion_control/time_loss_algorithm_test.cc',
+ 'quic/crypto/aes_128_gcm_12_decrypter_test.cc',
+ 'quic/crypto/aes_128_gcm_12_encrypter_test.cc',
+ 'quic/crypto/cert_compressor_test.cc',
+ 'quic/crypto/chacha20_poly1305_decrypter_test.cc',
+ 'quic/crypto/chacha20_poly1305_encrypter_test.cc',
+ 'quic/crypto/channel_id_test.cc',
+ 'quic/crypto/common_cert_set_test.cc',
+ 'quic/crypto/crypto_framer_test.cc',
+ 'quic/crypto/crypto_secret_boxer_test.cc',
+ 'quic/crypto/crypto_server_test.cc',
+ 'quic/crypto/crypto_utils_test.cc',
+ 'quic/crypto/curve25519_key_exchange_test.cc',
+ 'quic/crypto/local_strike_register_client_test.cc',
+ 'quic/crypto/null_decrypter_test.cc',
+ 'quic/crypto/null_encrypter_test.cc',
+ 'quic/crypto/p256_key_exchange_test.cc',
+ 'quic/crypto/proof_test.cc',
+ 'quic/crypto/quic_crypto_client_config_test.cc',
+ 'quic/crypto/quic_crypto_server_config_test.cc',
+ 'quic/crypto/quic_random_test.cc',
+ 'quic/crypto/strike_register_test.cc',
+ 'quic/iovector_test.cc',
+ 'quic/port_suggester_unittest.cc',
+ 'quic/quic_dispatcher.cc',
+ 'quic/quic_dispatcher.h',
+ 'quic/quic_in_memory_cache.cc',
+ 'quic/quic_in_memory_cache.h',
+ 'quic/quic_server_packet_writer.cc',
+ 'quic/quic_server_packet_writer.h',
+ 'quic/quic_server_session.cc',
+ 'quic/quic_server_session.h',
+ 'quic/quic_spdy_server_stream.cc',
+ 'quic/quic_spdy_server_stream.h',
+ 'quic/quic_time_wait_list_manager.cc',
+ 'quic/quic_time_wait_list_manager.h',
+ 'quic/test_tools/crypto_test_utils.cc',
+ 'quic/test_tools/crypto_test_utils.h',
+ 'quic/test_tools/crypto_test_utils_chromium.cc',
+ 'quic/test_tools/crypto_test_utils_nss.cc',
+ 'quic/test_tools/crypto_test_utils_openssl.cc',
+ 'quic/test_tools/delayed_verify_strike_register_client.cc',
+ 'quic/test_tools/delayed_verify_strike_register_client.h',
+ 'quic/test_tools/mock_clock.cc',
+ 'quic/test_tools/mock_clock.h',
+ 'quic/test_tools/mock_crypto_client_stream.cc',
+ 'quic/test_tools/mock_crypto_client_stream.h',
+ 'quic/test_tools/mock_crypto_client_stream_factory.cc',
+ 'quic/test_tools/mock_crypto_client_stream_factory.h',
+ 'quic/test_tools/mock_quic_dispatcher.cc',
+ 'quic/test_tools/mock_quic_dispatcher.h',
+ 'quic/test_tools/mock_random.cc',
+ 'quic/test_tools/mock_random.h',
+ 'quic/test_tools/quic_client_session_peer.cc',
+ 'quic/test_tools/quic_client_session_peer.h',
+ 'quic/test_tools/quic_config_peer.cc',
+ 'quic/test_tools/quic_config_peer.h',
+ 'quic/test_tools/quic_connection_peer.cc',
+ 'quic/test_tools/quic_connection_peer.h',
+ 'quic/test_tools/quic_data_stream_peer.cc',
+ 'quic/test_tools/quic_data_stream_peer.h',
+ 'quic/test_tools/quic_flow_controller_peer.cc',
+ 'quic/test_tools/quic_flow_controller_peer.h',
+ 'quic/test_tools/quic_framer_peer.cc',
+ 'quic/test_tools/quic_framer_peer.h',
+ 'quic/test_tools/quic_packet_creator_peer.cc',
+ 'quic/test_tools/quic_packet_creator_peer.h',
+ 'quic/test_tools/quic_packet_generator_peer.cc',
+ 'quic/test_tools/quic_packet_generator_peer.h',
+ 'quic/test_tools/quic_received_packet_manager_peer.cc',
+ 'quic/test_tools/quic_received_packet_manager_peer.h',
+ 'quic/test_tools/quic_sent_packet_manager_peer.cc',
+ 'quic/test_tools/quic_sent_packet_manager_peer.h',
+ 'quic/test_tools/quic_session_peer.cc',
+ 'quic/test_tools/quic_session_peer.h',
+ 'quic/test_tools/quic_stream_sequencer_peer.cc',
+ 'quic/test_tools/quic_stream_sequencer_peer.h',
+ 'quic/test_tools/quic_test_packet_maker.cc',
+ 'quic/test_tools/quic_test_packet_maker.h',
+ 'quic/test_tools/quic_test_utils.cc',
+ 'quic/test_tools/quic_test_utils.h',
+ 'quic/test_tools/reliable_quic_stream_peer.cc',
+ 'quic/test_tools/reliable_quic_stream_peer.h',
+ 'quic/test_tools/simple_quic_framer.cc',
+ 'quic/test_tools/simple_quic_framer.h',
+ 'quic/test_tools/test_task_runner.cc',
+ 'quic/test_tools/test_task_runner.h',
+ 'quic/quic_ack_notifier_test.cc',
+ 'quic/quic_address_mismatch_test.cc',
+ 'quic/quic_alarm_test.cc',
+ 'quic/quic_bandwidth_test.cc',
+ 'quic/quic_client_session_test.cc',
+ 'quic/quic_clock_test.cc',
+ 'quic/quic_config_test.cc',
+ 'quic/quic_connection_helper_test.cc',
+ 'quic/quic_connection_test.cc',
+ 'quic/quic_crypto_client_stream_test.cc',
+ 'quic/quic_crypto_server_stream_test.cc',
+ 'quic/quic_crypto_stream_test.cc',
+ 'quic/quic_data_stream_test.cc',
+ 'quic/quic_data_writer_test.cc',
+ 'quic/quic_fec_group_test.cc',
+ 'quic/quic_flow_controller_test.cc',
+ 'quic/quic_framer_test.cc',
+ 'quic/quic_headers_stream_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',
+ 'quic/quic_protocol_test.cc',
+ '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_server_id_test.cc',
+ 'quic/quic_session_test.cc',
+ 'quic/quic_socket_address_coder_test.cc',
+ 'quic/quic_stream_factory_test.cc',
+ 'quic/quic_stream_sequencer_test.cc',
+ 'quic/quic_time_test.cc',
+ 'quic/quic_unacked_packet_map_test.cc',
+ 'quic/quic_utils_chromium_test.cc',
+ 'quic/quic_utils_test.cc',
+ 'quic/quic_write_blocked_list_test.cc',
+ 'quic/reliable_quic_stream_test.cc',
+ 'server/http_server_response_info_unittest.cc',
+ 'server/http_server_unittest.cc',
+ 'socket/client_socket_pool_base_unittest.cc',
+ 'socket/deterministic_socket_data_unittest.cc',
+ 'socket/mock_client_socket_pool_manager.cc',
+ 'socket/mock_client_socket_pool_manager.h',
+ 'socket/socks5_client_socket_unittest.cc',
+ 'socket/socks_client_socket_pool_unittest.cc',
+ 'socket/socks_client_socket_unittest.cc',
+ 'socket/ssl_client_socket_openssl_unittest.cc',
+ 'socket/ssl_client_socket_pool_unittest.cc',
+ 'socket/ssl_client_socket_unittest.cc',
+ 'socket/ssl_server_socket_unittest.cc',
+ 'socket/ssl_session_cache_openssl_unittest.cc',
+ 'socket/tcp_client_socket_unittest.cc',
+ 'socket/tcp_listen_socket_unittest.cc',
+ 'socket/tcp_listen_socket_unittest.h',
+ 'socket/tcp_server_socket_unittest.cc',
+ 'socket/tcp_socket_unittest.cc',
+ 'socket/transport_client_socket_pool_unittest.cc',
+ 'socket/transport_client_socket_unittest.cc',
+ 'socket/unix_domain_socket_posix_unittest.cc',
+ 'socket_stream/socket_stream_metrics_unittest.cc',
+ 'socket_stream/socket_stream_unittest.cc',
+ 'spdy/buffered_spdy_framer_unittest.cc',
+ 'spdy/fuzzing/hpack_fuzz_util_test.cc',
+ 'spdy/hpack_decoder_test.cc',
+ 'spdy/hpack_encoder_test.cc',
+ 'spdy/hpack_entry_test.cc',
+ 'spdy/hpack_header_table_test.cc',
+ 'spdy/hpack_huffman_aggregator_test.cc',
+ 'spdy/hpack_huffman_table_test.cc',
+ 'spdy/hpack_input_stream_test.cc',
+ 'spdy/hpack_output_stream_test.cc',
+ 'spdy/hpack_round_trip_test.cc',
+ 'spdy/hpack_string_util_test.cc',
+ 'spdy/mock_spdy_framer_visitor.cc',
+ 'spdy/mock_spdy_framer_visitor.h',
+ 'spdy/spdy_buffer_unittest.cc',
+ 'spdy/spdy_frame_builder_test.cc',
+ 'spdy/spdy_frame_reader_test.cc',
+ 'spdy/spdy_framer_test.cc',
+ 'spdy/spdy_header_block_unittest.cc',
+ 'spdy/spdy_headers_block_parser_test.cc',
+ 'spdy/spdy_http_stream_unittest.cc',
+ 'spdy/spdy_http_utils_unittest.cc',
+ 'spdy/spdy_network_transaction_unittest.cc',
+ 'spdy/spdy_pinnable_buffer_piece_test.cc',
+ 'spdy/spdy_prefixed_buffer_reader_test.cc',
+ 'spdy/spdy_priority_forest_test.cc',
+ 'spdy/spdy_protocol_test.cc',
+ 'spdy/spdy_proxy_client_socket_unittest.cc',
+ 'spdy/spdy_read_queue_unittest.cc',
+ 'spdy/spdy_session_pool_unittest.cc',
+ 'spdy/spdy_session_test_util.cc',
+ 'spdy/spdy_session_test_util.h',
+ 'spdy/spdy_session_unittest.cc',
+ 'spdy/spdy_stream_test_util.cc',
+ 'spdy/spdy_stream_test_util.h',
+ 'spdy/spdy_stream_unittest.cc',
+ 'spdy/spdy_test_util_common.cc',
+ 'spdy/spdy_test_util_common.h',
+ 'spdy/spdy_test_utils.cc',
+ 'spdy/spdy_test_utils.h',
+ 'spdy/spdy_websocket_stream_unittest.cc',
+ 'spdy/spdy_websocket_test_util.cc',
+ 'spdy/spdy_websocket_test_util.h',
+ 'spdy/spdy_write_queue_unittest.cc',
+ 'spdy/write_blocked_list_test.cc',
+ 'ssl/client_cert_store_chromeos_unittest.cc',
+ 'ssl/client_cert_store_mac_unittest.cc',
+ 'ssl/client_cert_store_nss_unittest.cc',
+ 'ssl/client_cert_store_unittest-inl.h',
+ 'ssl/client_cert_store_win_unittest.cc',
+ 'ssl/default_server_bound_cert_store_unittest.cc',
+ 'ssl/openssl_client_key_store_unittest.cc',
+ 'ssl/server_bound_cert_service_unittest.cc',
+ 'ssl/ssl_cipher_suite_names_unittest.cc',
+ 'ssl/ssl_client_auth_cache_unittest.cc',
+ 'ssl/ssl_config_service_unittest.cc',
+ 'ssl/ssl_connection_status_flags_unittest.cc',
+ 'test/gtest_util.h',
+ 'test/embedded_test_server/embedded_test_server_unittest.cc',
+ 'test/embedded_test_server/http_request_unittest.cc',
+ 'test/embedded_test_server/http_response_unittest.cc',
+ 'test/python_utils_unittest.cc',
+ 'test/run_all_unittests.cc',
+ 'test/scoped_disable_exit_on_dfatal.cc',
+ 'test/scoped_disable_exit_on_dfatal.h',
+ 'test/scoped_mock_log.cc',
+ 'test/scoped_mock_log.h',
+ 'test/test_certificate_data.h',
+ 'tools/balsa/balsa_frame_test.cc',
+ 'tools/balsa/balsa_headers_test.cc',
+ 'tools/dump_cache/url_to_filename_encoder.cc',
+ 'tools/dump_cache/url_to_filename_encoder.h',
+ 'tools/dump_cache/url_to_filename_encoder_unittest.cc',
+ 'tools/dump_cache/url_utilities.h',
+ 'tools/dump_cache/url_utilities.cc',
+ 'tools/dump_cache/url_utilities_unittest.cc',
+ 'tools/tld_cleanup/tld_cleanup_util_unittest.cc',
+ 'udp/udp_socket_unittest.cc',
+ 'url_request/url_fetcher_impl_unittest.cc',
+ 'url_request/url_fetcher_response_writer_unittest.cc',
+ 'url_request/url_request_context_builder_unittest.cc',
+ 'url_request/url_request_file_job_unittest.cc',
+ 'url_request/url_request_filter_unittest.cc',
+ 'url_request/url_request_ftp_job_unittest.cc',
+ 'url_request/url_request_http_job_unittest.cc',
+ 'url_request/url_request_job_factory_impl_unittest.cc',
+ 'url_request/url_request_job_unittest.cc',
+ 'url_request/url_request_simple_job_unittest.cc',
+ 'url_request/url_request_throttler_simulation_unittest.cc',
+ 'url_request/url_request_throttler_test_support.cc',
+ 'url_request/url_request_throttler_test_support.h',
+ 'url_request/url_request_throttler_unittest.cc',
+ 'url_request/url_request_unittest.cc',
+ 'url_request/view_cache_helper_unittest.cc',
+ 'websockets/websocket_basic_stream_test.cc',
+ 'websockets/websocket_channel_test.cc',
+ 'websockets/websocket_deflate_predictor_impl_test.cc',
+ 'websockets/websocket_deflate_stream_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_spdy_test.cc',
+ 'websockets/websocket_handshake_handler_test.cc',
+ 'websockets/websocket_handshake_stream_create_helper_test.cc',
+ 'websockets/websocket_inflater_test.cc',
+ 'websockets/websocket_job_test.cc',
+ 'websockets/websocket_net_log_params_test.cc',
+ 'websockets/websocket_stream_test.cc',
+ 'websockets/websocket_test_util.cc',
+ 'websockets/websocket_test_util.h',
+ 'websockets/websocket_throttle_test.cc',
+ ],
+ 'net_linux_test_sources': [
+ 'quic/quic_end_to_end_unittest.cc',
+ 'tools/quic/end_to_end_test.cc',
+ 'tools/quic/quic_client_session_test.cc',
+ 'tools/quic/quic_dispatcher_test.cc',
+ 'tools/quic/quic_epoll_clock_test.cc',
+ 'tools/quic/quic_epoll_connection_helper_test.cc',
+ 'tools/quic/quic_in_memory_cache_test.cc',
+ 'tools/quic/quic_server_session_test.cc',
+ 'tools/quic/quic_server_test.cc',
+ 'tools/quic/quic_spdy_client_stream_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.cc',
+ 'tools/quic/test_tools/http_message.h',
+ 'tools/quic/test_tools/mock_epoll_server.cc',
+ 'tools/quic/test_tools/mock_epoll_server.h',
+ 'tools/quic/test_tools/mock_quic_dispatcher.cc',
+ 'tools/quic/test_tools/mock_quic_dispatcher.h',
+ 'tools/quic/test_tools/packet_dropping_test_writer.cc',
+ 'tools/quic/test_tools/packet_dropping_test_writer.h',
+ 'tools/quic/test_tools/quic_client_peer.cc',
+ 'tools/quic/test_tools/quic_client_peer.h',
+ 'tools/quic/test_tools/quic_dispatcher_peer.cc',
+ 'tools/quic/test_tools/quic_dispatcher_peer.h',
+ 'tools/quic/test_tools/quic_in_memory_cache_peer.h',
+ 'tools/quic/test_tools/quic_in_memory_cache_peer.cc',
+ 'tools/quic/test_tools/quic_server_peer.cc',
+ 'tools/quic/test_tools/quic_server_peer.h',
+ 'tools/quic/test_tools/quic_test_client.cc',
+ 'tools/quic/test_tools/quic_test_client.h',
+ 'tools/quic/test_tools/quic_test_utils.cc',
+ 'tools/quic/test_tools/quic_test_utils.h',
+ 'tools/quic/test_tools/server_thread.h',
+ 'tools/quic/test_tools/server_thread.cc',
+ 'tools/quic/test_tools/simple_client.h',
+ 'tools/quic/test_tools/simple_client.cc',
+ ],
+ }
+}
diff --git a/chromium/net/net.isolate b/chromium/net/net.isolate
new file mode 100644
index 00000000000..f5ca9b501c3
--- /dev/null
+++ b/chromium/net/net.isolate
@@ -0,0 +1,10 @@
+# Copyright 2014 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.
+{
+ 'conditions': [
+ ],
+ 'includes': [
+ '../base/base.isolate',
+ ],
+}
diff --git a/chromium/net/net_nacl.gyp b/chromium/net/net_nacl.gyp
new file mode 100644
index 00000000000..33a63d0391f
--- /dev/null
+++ b/chromium/net/net_nacl.gyp
@@ -0,0 +1,44 @@
+# Copyright 2014 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../native_client/build/untrusted.gypi',
+ 'net.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'net_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libnet_nacl.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_pnacl_newlib': 1,
+ },
+ 'dependencies': [
+ '../crypto/crypto_nacl.gyp:crypto_nacl',
+ '../native_client/tools.gyp:prep_toolchain',
+ '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted',
+ '../third_party/openssl/openssl_nacl.gyp:openssl_nacl',
+ '../url/url_nacl.gyp:url_nacl',
+ 'net.gyp:net_derived_sources',
+ 'net.gyp:net_resources',
+ ],
+ 'defines': [
+ 'NET_IMPLEMENTATION',
+ ],
+ 'pnacl_compile_flags': [
+ '-Wno-bind-to-temporary-copy',
+ ],
+ 'sources': [
+ '<@(net_nacl_common_sources)',
+ ],
+ },
+ ],
+}
diff --git a/chromium/net/net_unittests.isolate b/chromium/net/net_unittests.isolate
index 2e04ea34544..662eeb0bb5c 100644
--- a/chromium/net/net_unittests.isolate
+++ b/chromium/net/net_unittests.isolate
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Copyright 2014 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.
{
@@ -32,18 +32,14 @@
'../third_party/pyftpdlib/',
'../third_party/pywebsocket/',
'../third_party/tlslite/',
- '../tools/swarming_client/',
'<(PRODUCT_DIR)/pyproto/',
'tools/testserver/',
],
+ 'read_only': 1,
},
}],
- ['OS=="win"', {
- 'variables': {
- 'isolate_dependency_tracked': [
- '<(PRODUCT_DIR)/icudt.dll',
- ],
- },
- }],
+ ],
+ 'includes': [
+ '../base/base.isolate',
],
}
diff --git a/chromium/net/ocsp/nss_ocsp_unittest.cc b/chromium/net/ocsp/nss_ocsp_unittest.cc
index 0530282f4a6..f67e3ffc7c9 100644
--- a/chromium/net/ocsp/nss_ocsp_unittest.cc
+++ b/chromium/net/ocsp/nss_ocsp_unittest.cc
@@ -23,6 +23,7 @@
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -39,14 +40,14 @@ const char kAiaHeaders[] = "HTTP/1.1 200 OK\0"
"Content-type: application/pkix-cert\0"
"\0";
-class AiaResponseHandler : public net::URLRequestJobFactory::ProtocolHandler {
+class AiaResponseHandler : public net::URLRequestInterceptor {
public:
AiaResponseHandler(const std::string& headers, const std::string& cert_data)
: headers_(headers), cert_data_(cert_data), request_count_(0) {}
virtual ~AiaResponseHandler() {}
- // net::URLRequestJobFactory::ProtocolHandler implementation:
- virtual net::URLRequestJob* MaybeCreateJob(
+ // net::URLRequestInterceptor implementation:
+ virtual net::URLRequestJob* MaybeInterceptRequest(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const OVERRIDE {
++const_cast<AiaResponseHandler*>(this)->request_count_;
@@ -89,10 +90,10 @@ class NssHttpTest : public ::testing::Test {
new AiaResponseHandler(kAiaHeaders, file_contents));
handler_ = handler.get();
- URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
+ URLRequestFilter::GetInstance()->AddHostnameInterceptor(
"http",
kAiaHost,
- handler.PassAs<URLRequestJobFactory::ProtocolHandler>());
+ handler.PassAs<URLRequestInterceptor>());
SetURLRequestContextForNSSHttpIO(&context_);
EnsureNSSHttpIOInit();
diff --git a/chromium/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc b/chromium/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
index 569e8120782..199014f8bec 100644
--- a/chromium/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
+++ b/chromium/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
@@ -233,11 +233,11 @@ std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
// The maximum message size is typically 4096 bytes on Windows per
// http://support.microsoft.com/kb/321592
DWORD result_buffer_size = 4096;
- scoped_ptr_malloc<BYTE> result_buffer;
+ scoped_ptr<BYTE, base::FreeDeleter> result_buffer;
int retry_count = 0;
DWORD res = NO_ERROR;
do {
- result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
+ result_buffer.reset(static_cast<BYTE*>(malloc(result_buffer_size)));
// Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
// there might be an asynchronous mode, there seems to be (at least in
@@ -261,7 +261,7 @@ std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
} while (res == ERROR_MORE_DATA && retry_count <= 3);
if (res != NO_ERROR) {
- LOG(INFO) << "Error fetching PAC URL from DHCP: " << res;
+ VLOG(1) << "Error fetching PAC URL from DHCP: " << res;
UMA_HISTOGRAM_COUNTS("Net.DhcpWpadUnhandledDhcpError", 1);
} else if (wpad_params.nBytesData) {
return SanitizeDhcpApiString(
@@ -288,7 +288,7 @@ std::string DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
// the server is giving us back a buffer with embedded NULLs,
// something is broken anyway. Finally, trim trailing whitespace.
std::string result(std::string(data, count_bytes).c_str());
- TrimWhitespaceASCII(result, TRIM_TRAILING, &result);
+ base::TrimWhitespaceASCII(result, base::TRIM_TRAILING, &result);
return result;
}
diff --git a/chromium/net/proxy/dhcp_proxy_script_fetcher_win.cc b/chromium/net/proxy/dhcp_proxy_script_fetcher_win.cc
index 037268f03a0..54e2bdfcb71 100644
--- a/chromium/net/proxy/dhcp_proxy_script_fetcher_win.cc
+++ b/chromium/net/proxy/dhcp_proxy_script_fetcher_win.cc
@@ -320,14 +320,13 @@ bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
// The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
// avoid reallocation.
ULONG adapters_size = 15000;
- scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
+ scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters;
ULONG error = ERROR_SUCCESS;
int num_tries = 0;
base::ElapsedTimer time_api_access;
do {
- adapters.reset(
- reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
+ adapters.reset(static_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
// Return only unicast addresses, and skip information we do not need.
error = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST |
diff --git a/chromium/net/proxy/mock_proxy_script_fetcher.cc b/chromium/net/proxy/mock_proxy_script_fetcher.cc
index 5d66e6c6147..7604b5b34b1 100644
--- a/chromium/net/proxy/mock_proxy_script_fetcher.cc
+++ b/chromium/net/proxy/mock_proxy_script_fetcher.cc
@@ -38,7 +38,7 @@ int MockProxyScriptFetcher::Fetch(const GURL& url, base::string16* text,
void MockProxyScriptFetcher::NotifyFetchCompletion(
int result, const std::string& ascii_text) {
DCHECK(has_pending_request());
- *pending_request_text_ = ASCIIToUTF16(ascii_text);
+ *pending_request_text_ = base::ASCIIToUTF16(ascii_text);
CompletionCallback callback = pending_request_callback_;
pending_request_callback_.Reset();
callback.Run(result);
diff --git a/chromium/net/proxy/multi_threaded_proxy_resolver.cc b/chromium/net/proxy/multi_threaded_proxy_resolver.cc
index b54cf1fbac7..cab22ab2759 100644
--- a/chromium/net/proxy/multi_threaded_proxy_resolver.cc
+++ b/chromium/net/proxy/multi_threaded_proxy_resolver.cc
@@ -47,8 +47,6 @@ class MultiThreadedProxyResolver::Executor
// and resolver.
void Destroy();
- void PurgeMemory();
-
// Returns the outstanding job, or NULL.
Job* outstanding_job() const { return outstanding_job_.get(); }
@@ -316,11 +314,8 @@ MultiThreadedProxyResolver::Executor::Executor(
DCHECK(coordinator);
DCHECK(resolver);
// Start up the thread.
- // Note that it is safe to pass a temporary C-String to Thread(), as it will
- // make a copy.
- std::string thread_name =
- base::StringPrintf("PAC thread #%d", thread_number);
- thread_.reset(new base::Thread(thread_name.c_str()));
+ thread_.reset(new base::Thread(base::StringPrintf("PAC thread #%d",
+ thread_number)));
CHECK(thread_->Start());
}
@@ -370,13 +365,6 @@ void MultiThreadedProxyResolver::Executor::Destroy() {
outstanding_job_ = NULL;
}
-void MultiThreadedProxyResolver::Executor::PurgeMemory() {
- thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&ProxyResolver::PurgeMemory,
- base::Unretained(resolver_.get())));
-}
-
MultiThreadedProxyResolver::Executor::~Executor() {
// The important cleanup happens as part of Destroy(), which should always be
// called first.
@@ -483,15 +471,6 @@ void MultiThreadedProxyResolver::CancelSetPacScript() {
ReleaseAllExecutors();
}
-void MultiThreadedProxyResolver::PurgeMemory() {
- DCHECK(CalledOnValidThread());
- for (ExecutorList::iterator it = executors_.begin();
- it != executors_.end(); ++it) {
- Executor* executor = it->get();
- executor->PurgeMemory();
- }
-}
-
int MultiThreadedProxyResolver::SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback&callback) {
diff --git a/chromium/net/proxy/multi_threaded_proxy_resolver.h b/chromium/net/proxy/multi_threaded_proxy_resolver.h
index 3076c36a55f..5c470f4c54c 100644
--- a/chromium/net/proxy/multi_threaded_proxy_resolver.h
+++ b/chromium/net/proxy/multi_threaded_proxy_resolver.h
@@ -99,7 +99,6 @@ class NET_EXPORT_PRIVATE MultiThreadedProxyResolver
virtual void CancelRequest(RequestHandle request) OVERRIDE;
virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE;
virtual void CancelSetPacScript() OVERRIDE;
- virtual void PurgeMemory() OVERRIDE;
virtual int SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& callback) OVERRIDE;
diff --git a/chromium/net/proxy/multi_threaded_proxy_resolver_unittest.cc b/chromium/net/proxy/multi_threaded_proxy_resolver_unittest.cc
index 79c0acc1ded..e1c9e90b761 100644
--- a/chromium/net/proxy/multi_threaded_proxy_resolver_unittest.cc
+++ b/chromium/net/proxy/multi_threaded_proxy_resolver_unittest.cc
@@ -19,6 +19,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
+
namespace net {
namespace {
@@ -31,8 +33,7 @@ class MockProxyResolver : public ProxyResolver {
MockProxyResolver()
: ProxyResolver(true /*expects_pac_bytes*/),
wrong_loop_(base::MessageLoop::current()),
- request_count_(0),
- purge_count_(0) {}
+ request_count_(0) {}
// ProxyResolver implementation.
virtual int GetProxyForURL(const GURL& query_url,
@@ -78,12 +79,6 @@ class MockProxyResolver : public ProxyResolver {
return OK;
}
- virtual void PurgeMemory() OVERRIDE {
- CheckIsOnWorkerThread();
- ++purge_count_;
- }
-
- int purge_count() const { return purge_count_; }
int request_count() const { return request_count_; }
const ProxyResolverScriptData* last_script_data() const {
@@ -105,7 +100,6 @@ class MockProxyResolver : public ProxyResolver {
base::MessageLoop* wrong_loop_;
int request_count_;
- int purge_count_;
scoped_refptr<ProxyResolverScriptData> last_script_data_;
base::TimeDelta resolve_latency_;
};
@@ -194,10 +188,6 @@ class ForwardingProxyResolver : public ProxyResolver {
return impl_->SetPacScript(script_data, callback);
}
- virtual void PurgeMemory() OVERRIDE {
- impl_->PurgeMemory();
- }
-
private:
ProxyResolver* impl_;
};
@@ -319,19 +309,6 @@ TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
rv = callback3.WaitForResult();
EXPECT_EQ(3, rv);
EXPECT_EQ("PROXY request3:80", results3.ToPacString());
-
- // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
- // right thread.
- EXPECT_EQ(0, mock->purge_count());
- resolver.PurgeMemory();
- // There is no way to get a callback directly when PurgeMemory() completes, so
- // we queue up a dummy request after the PurgeMemory() call and wait until it
- // finishes to ensure PurgeMemory() has had a chance to run.
- TestCompletionCallback dummy_callback;
- rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
- dummy_callback.callback());
- EXPECT_EQ(OK, dummy_callback.WaitForResult());
- EXPECT_EQ(1, mock->purge_count());
}
// Tests that the NetLog is updated to include the time the request was waiting
diff --git a/chromium/net/proxy/network_delegate_error_observer_unittest.cc b/chromium/net/proxy/network_delegate_error_observer_unittest.cc
index cb32760cafd..1f6330f0dd3 100644
--- a/chromium/net/proxy/network_delegate_error_observer_unittest.cc
+++ b/chromium/net/proxy/network_delegate_error_observer_unittest.cc
@@ -41,7 +41,8 @@ class TestNetworkDelegate : public net::NetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE {
return net::OK;
}
virtual void OnBeforeRedirect(URLRequest* request,
@@ -84,9 +85,6 @@ class TestNetworkDelegate : public net::NetworkDelegate {
const CompletionCallback& callback) OVERRIDE {
return OK;
}
- virtual void OnRequestWaitStateChange(const net::URLRequest& request,
- RequestWaitState state) OVERRIDE {
- }
bool got_pac_error_;
};
diff --git a/chromium/net/proxy/proxy_bypass_rules.cc b/chromium/net/proxy/proxy_bypass_rules.cc
index a60adc352ff..e402db835c4 100644
--- a/chromium/net/proxy/proxy_bypass_rules.cc
+++ b/chromium/net/proxy/proxy_bypass_rules.cc
@@ -132,11 +132,11 @@ class BypassIPBlockRule : public ProxyBypassRules::Rule {
// Returns true if the given string represents an IP address.
bool IsIPAddress(const std::string& domain) {
// From GURL::HostIsIPAddress()
- url_canon::RawCanonOutputT<char, 128> ignored_output;
- url_canon::CanonHostInfo host_info;
- url_parse::Component domain_comp(0, domain.size());
- url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
- &ignored_output, &host_info);
+ url::RawCanonOutputT<char, 128> ignored_output;
+ url::CanonHostInfo host_info;
+ url::Component domain_comp(0, domain.size());
+ url::CanonicalizeIPAddress(domain.c_str(), domain_comp, &ignored_output,
+ &host_info);
return host_info.IsIPAddress();
}
@@ -262,7 +262,7 @@ bool ProxyBypassRules::AddRuleFromStringInternal(
const std::string& raw_untrimmed,
bool use_hostname_suffix_matching) {
std::string raw;
- TrimWhitespaceASCII(raw_untrimmed, TRIM_ALL, &raw);
+ base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL, &raw);
// This is the special syntax used by WinInet's bypass list -- we allow it
// on all platforms and interpret it the same way.
diff --git a/chromium/net/proxy/proxy_config.cc b/chromium/net/proxy/proxy_config.cc
index c1fe0f5ceb4..86c5284d837 100644
--- a/chromium/net/proxy/proxy_config.cc
+++ b/chromium/net/proxy/proxy_config.cc
@@ -112,7 +112,7 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
}
// Trim whitespace off the url scheme.
- TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme);
+ base::TrimWhitespaceASCII(url_scheme, base::TRIM_ALL, &url_scheme);
// Add it to the per-scheme mappings (if supported scheme).
type = TYPE_PROXY_PER_SCHEME;
diff --git a/chromium/net/proxy/proxy_config_service_android.cc b/chromium/net/proxy/proxy_config_service_android.cc
index 088621da83d..4129bf24c2f 100644
--- a/chromium/net/proxy/proxy_config_service_android.cc
+++ b/chromium/net/proxy/proxy_config_service_android.cc
@@ -39,10 +39,9 @@ typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
// Returns whether the provided string was successfully converted to a port.
bool ConvertStringToPort(const std::string& port, int* output) {
- url_parse::Component component(0, port.size());
- int result = url_parse::ParsePort(port.c_str(), component);
- if (result == url_parse::PORT_INVALID ||
- result == url_parse::PORT_UNSPECIFIED)
+ url::Component component(0, port.size());
+ int result = url::ParsePort(port.c_str(), component);
+ if (result == url::PORT_INVALID || result == url::PORT_UNSPECIFIED)
return false;
*output = result;
return true;
@@ -106,7 +105,7 @@ void AddBypassRules(const std::string& scheme,
while (tokenizer.GetNext()) {
std::string token = tokenizer.token();
std::string pattern;
- TrimWhitespaceASCII(token, TRIM_ALL, &pattern);
+ base::TrimWhitespaceASCII(token, base::TRIM_ALL, &pattern);
if (pattern.empty())
continue;
// '?' is not one of the specified pattern characters above.
diff --git a/chromium/net/proxy/proxy_config_service_linux.cc b/chromium/net/proxy/proxy_config_service_linux.cc
index d01e5e6b5be..158fd5c1ff7 100644
--- a/chromium/net/proxy/proxy_config_service_linux.cc
+++ b/chromium/net/proxy/proxy_config_service_linux.cc
@@ -4,6 +4,15 @@
#include "net/proxy/proxy_config_service_linux.h"
+// glib >=2.40 deprecate g_settings_list_schemas in favor of
+// g_settings_schema_source_list_schemas. This function is not available on
+// earlier versions that we still need to support (specifically, 2.32), so
+// disable the warning.
+// TODO(mgiuca): Remove this suppression when we drop support for Ubuntu 13.10
+// (saucy) and earlier. Update the code to use
+// g_settings_schema_source_list_schemas instead.
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
#include <errno.h>
#include <fcntl.h>
#if defined(USE_GCONF)
@@ -19,9 +28,11 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/debug/leak_annotations.h"
#include "base/environment.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/nix/xdg_util.h"
@@ -509,6 +520,8 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
#endif // defined(USE_GCONF)
#if defined(USE_GIO)
+const char kProxyGConfSchema[] = "org.gnome.system.proxy";
+
// This setting getter uses gsettings, as used in most GNOME 3 desktops.
class SettingGetterImplGSettings
: public ProxyConfigServiceLinux::SettingGetter {
@@ -564,8 +577,8 @@ class SettingGetterImplGSettings
DCHECK(!client_);
DCHECK(!task_runner_.get());
- if (!SchemaExists("org.gnome.system.proxy") ||
- !(client_ = libgio_loader_.g_settings_new("org.gnome.system.proxy"))) {
+ if (!SchemaExists(kProxyGConfSchema) ||
+ !(client_ = libgio_loader_.g_settings_new(kProxyGConfSchema))) {
// It's not clear whether/when this can return NULL.
LOG(ERROR) << "Unable to create a gsettings client";
return false;
@@ -805,9 +818,12 @@ bool SettingGetterImplGSettings::LoadAndCheckVersion(
}
}
- GSettings* client;
- if (!SchemaExists("org.gnome.system.proxy") ||
- !(client = libgio_loader_.g_settings_new("org.gnome.system.proxy"))) {
+ GSettings* client = NULL;
+ if (SchemaExists(kProxyGConfSchema)) {
+ ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/380782
+ client = libgio_loader_.g_settings_new(kProxyGConfSchema);
+ }
+ if (!client) {
VLOG(1) << "Cannot create gsettings client. Will fall back to gconf.";
return false;
}
@@ -884,8 +900,8 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
base::FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
bool use_kde4 = false;
if (base::DirectoryExists(kde4_path)) {
- base::PlatformFileInfo kde3_info;
- base::PlatformFileInfo kde4_info;
+ base::File::Info kde3_info;
+ base::File::Info kde4_info;
if (base::GetFileInfo(kde4_config, &kde4_info)) {
if (base::GetFileInfo(kde3_config, &kde3_info)) {
use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
@@ -1171,7 +1187,7 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
// each relevant name-value pair to the appropriate value table.
void UpdateCachedSettings() {
base::FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
- file_util::ScopedFILE input(base::OpenFile(kioslaverc, "r"));
+ base::ScopedFILE input(base::OpenFile(kioslaverc, "r"));
if (!input.get())
return;
ResetCachedSettings();
@@ -1215,8 +1231,8 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
*(split++) = 0;
std::string key = line;
std::string value = split;
- TrimWhitespaceASCII(key, TRIM_ALL, &key);
- TrimWhitespaceASCII(value, TRIM_ALL, &value);
+ base::TrimWhitespaceASCII(key, base::TRIM_ALL, &key);
+ base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
// Skip this line if the key name is empty.
if (key.empty())
continue;
@@ -1230,7 +1246,7 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
// Trim the localization indicator off.
key.resize(length);
// Remove any resulting trailing whitespace.
- TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
+ base::TrimWhitespaceASCII(key, base::TRIM_TRAILING, &key);
// Skip this line if the key name is now empty.
if (key.empty())
continue;
diff --git a/chromium/net/proxy/proxy_config_service_linux_unittest.cc b/chromium/net/proxy/proxy_config_service_linux_unittest.cc
index 99a3b570b25..5635a87d053 100644
--- a/chromium/net/proxy/proxy_config_service_linux_unittest.cc
+++ b/chromium/net/proxy/proxy_config_service_linux_unittest.cc
@@ -1502,8 +1502,8 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEConfigParser) {
new ProxyConfigServiceLinux(env));
ProxyConfig config;
// Overwrite the kioslaverc file.
- file_util::WriteFile(kioslaverc_, tests[i].kioslaverc.c_str(),
- tests[i].kioslaverc.length());
+ base::WriteFile(kioslaverc_, tests[i].kioslaverc.c_str(),
+ tests[i].kioslaverc.length());
sync_config_getter.SetupAndInitialFetch();
ProxyConfigService::ConfigAvailability availability =
sync_config_getter.SyncGetLatestProxyConfig(&config);
@@ -1526,7 +1526,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) {
GURL slaverc4_pac_url("http://wpad/wpad.dat");
// Overwrite the .kde kioslaverc file.
- file_util::WriteFile(kioslaverc_, slaverc3.c_str(), slaverc3.length());
+ base::WriteFile(kioslaverc_, slaverc3.c_str(), slaverc3.length());
// If .kde4 exists it will mess up the first test. It should not, as
// we created the directory for $HOME in the test setup.
@@ -1549,7 +1549,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) {
// Now create .kde4 and put a kioslaverc in the config directory.
// Note that its timestamp will be at least as new as the .kde one.
base::CreateDirectory(kde4_config_);
- file_util::WriteFile(kioslaverc4_, slaverc4.c_str(), slaverc4.length());
+ base::WriteFile(kioslaverc4_, slaverc4.c_str(), slaverc4.length());
CHECK(base::PathExists(kioslaverc4_));
{ SCOPED_TRACE("KDE4, .kde4 directory present, use it");
diff --git a/chromium/net/proxy/proxy_config_service_win.cc b/chromium/net/proxy/proxy_config_service_win.cc
index a1295f33de8..828b5cca524 100644
--- a/chromium/net/proxy/proxy_config_service_win.cc
+++ b/chromium/net/proxy/proxy_config_service_win.cc
@@ -12,6 +12,7 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/string_tokenizer.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/registry.h"
#include "net/base/net_errors.h"
@@ -175,10 +176,11 @@ void ProxyConfigServiceWin::SetFromIEConfig(
if (ie_config.lpszProxy) {
// lpszProxy may be a single proxy, or a proxy per scheme. The format
// is compatible with ProxyConfig::ProxyRules's string format.
- config->proxy_rules().ParseFromString(WideToASCII(ie_config.lpszProxy));
+ config->proxy_rules().ParseFromString(
+ base::UTF16ToASCII(ie_config.lpszProxy));
}
if (ie_config.lpszProxyBypass) {
- std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass);
+ std::string proxy_bypass = base::UTF16ToASCII(ie_config.lpszProxyBypass);
base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
while (proxy_server_bypass_list.GetNext()) {
diff --git a/chromium/net/proxy/proxy_info.h b/chromium/net/proxy/proxy_info.h
index 243cbba80fd..336cb3a5888 100644
--- a/chromium/net/proxy/proxy_info.h
+++ b/chromium/net/proxy/proxy_info.h
@@ -75,6 +75,13 @@ class NET_EXPORT ProxyInfo {
return proxy_server().is_http();
}
+ // Returns true if the first valid proxy server is a quic proxy.
+ bool is_quic() const {
+ if (is_empty())
+ return false;
+ return proxy_server().is_quic();
+ }
+
// Returns true if the first valid proxy server is a socks server.
bool is_socks() const {
if (is_empty())
diff --git a/chromium/net/proxy/proxy_list.cc b/chromium/net/proxy/proxy_list.cc
index 53284026128..4bbb6bc6a96 100644
--- a/chromium/net/proxy/proxy_list.cc
+++ b/chromium/net/proxy/proxy_list.cc
@@ -51,7 +51,7 @@ void ProxyList::DeprioritizeBadProxies(
// (1) the known bad proxies
// (2) everything else
std::vector<ProxyServer> good_proxies;
- std::vector<ProxyServer> bad_proxies;
+ std::vector<ProxyServer> bad_proxies_to_try;
std::vector<ProxyServer>::const_iterator iter = proxies_.begin();
for (; iter != proxies_.end(); ++iter) {
@@ -61,7 +61,8 @@ void ProxyList::DeprioritizeBadProxies(
// This proxy is bad. Check if it's time to retry.
if (bad_proxy->second.bad_until >= TimeTicks::Now()) {
// still invalid.
- bad_proxies.push_back(*iter);
+ if (bad_proxy->second.try_while_bad)
+ bad_proxies_to_try.push_back(*iter);
continue;
}
}
@@ -70,27 +71,8 @@ void ProxyList::DeprioritizeBadProxies(
// "proxies_ = good_proxies + bad_proxies"
proxies_.swap(good_proxies);
- proxies_.insert(proxies_.end(), bad_proxies.begin(), bad_proxies.end());
-}
-
-bool ProxyList::HasUntriedProxies(
- const ProxyRetryInfoMap& proxy_retry_info) const {
- std::vector<ProxyServer>::const_iterator iter = proxies_.begin();
- for (; iter != proxies_.end(); ++iter) {
- ProxyRetryInfoMap::const_iterator bad_proxy =
- proxy_retry_info.find(iter->ToURI());
- if (bad_proxy != proxy_retry_info.end()) {
- // This proxy is bad. Check if it's time to retry.
- if (bad_proxy->second.bad_until >= TimeTicks::Now()) {
- continue;
- }
- }
- // Either we've found the entry in the retry map and it's expired or we
- // didn't find a corresponding entry in the retry map. In either case, we
- // have a proxy to try.
- return true;
- }
- return false;
+ proxies_.insert(proxies_.end(), bad_proxies_to_try.begin(),
+ bad_proxies_to_try.end());
}
void ProxyList::RemoveProxiesWithoutScheme(int scheme_bit_field) {
@@ -185,8 +167,8 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info,
NOTREACHED();
return false;
}
- UpdateRetryInfoOnFallback(proxy_retry_info, base::TimeDelta(), ProxyServer(),
- net_log);
+ UpdateRetryInfoOnFallback(proxy_retry_info, base::TimeDelta(), true,
+ ProxyServer(), net_log);
// Remove this proxy from our list.
proxies_.erase(proxies_.begin());
@@ -195,9 +177,11 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info,
void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
- const std::string& proxy_key,
+ bool try_while_bad,
+ const ProxyServer& proxy_to_retry,
const BoundNetLog& net_log) const {
// Mark this proxy as bad.
+ std::string proxy_key = proxy_to_retry.ToURI();
ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(proxy_key);
if (iter != proxy_retry_info->end()) {
// TODO(nsylvain): This is not the first time we get this. We should
@@ -207,6 +191,7 @@ void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
ProxyRetryInfo retry_info;
retry_info.current_delay = retry_delay;
retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay;
+ retry_info.try_while_bad = try_while_bad;
(*proxy_retry_info)[proxy_key] = retry_info;
}
net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK,
@@ -216,6 +201,7 @@ void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
void ProxyList::UpdateRetryInfoOnFallback(
ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
+ bool reconsider,
const ProxyServer& another_proxy_to_bypass,
const BoundNetLog& net_log) const {
// Time to wait before retrying a bad proxy server.
@@ -236,21 +222,14 @@ void ProxyList::UpdateRetryInfoOnFallback(
}
if (!proxies_[0].is_direct()) {
- std::string key = proxies_[0].ToURI();
- AddProxyToRetryList(proxy_retry_info, retry_delay, key, net_log);
+ AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, proxies_[0],
+ net_log);
- // If additional proxies to bypass are specified, add these to the retry
- // map as well.
+ // If an additional proxy to bypass is specified, add it to the retry map
+ // as well.
if (another_proxy_to_bypass.is_valid()) {
- // Start at index 1 because index 0 is already handled above.
- for (size_t j = 1; j < proxies_.size(); ++j) {
- if (proxies_[j].is_direct())
- break;
- if (another_proxy_to_bypass == proxies_[j]) {
- key = proxies_[j].ToURI();
- AddProxyToRetryList(proxy_retry_info, retry_delay, key, net_log);
- }
- }
+ AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider,
+ another_proxy_to_bypass, net_log);
}
}
}
diff --git a/chromium/net/proxy/proxy_list.h b/chromium/net/proxy/proxy_list.h
index e8df0ba83fb..ac9635a977a 100644
--- a/chromium/net/proxy/proxy_list.h
+++ b/chromium/net/proxy/proxy_list.h
@@ -39,14 +39,10 @@ class NET_EXPORT_PRIVATE ProxyList {
// Append a single proxy server to the end of the proxy list.
void AddProxyServer(const ProxyServer& proxy_server);
- // De-prioritizes the proxies that we have cached as not working, by moving
- // them to the end of the fallback list.
+ // De-prioritizes the proxies that are cached as not working but are allowed
+ // to be reconsidered, by moving them to the end of the fallback list.
void DeprioritizeBadProxies(const ProxyRetryInfoMap& proxy_retry_info);
- // Returns true if this proxy list contains at least one proxy that is
- // not currently present in |proxy_retry_info|.
- bool HasUntriedProxies(const ProxyRetryInfoMap& proxy_retry_info) const;
-
// Delete any entry which doesn't have one of the specified proxy schemes.
// |scheme_bit_field| is a bunch of ProxyServer::Scheme bitwise ORed together.
void RemoveProxiesWithoutScheme(int scheme_bit_field);
@@ -92,24 +88,24 @@ class NET_EXPORT_PRIVATE ProxyList {
// is bad. This is distinct from Fallback(), above, to allow updating proxy
// retry information without modifying a given transction's proxy list. Will
// retry after |retry_delay| if positive, and will use the default proxy retry
- // duration otherwise. Additionally updates |proxy_retry_info| with
+ // duration otherwise. It may reconsider the proxy beforehand if |reconsider|
+ // is true. Additionally updates |proxy_retry_info| with
// |another_proxy_to_bypass| if non-empty.
void UpdateRetryInfoOnFallback(
ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
+ bool reconsider,
const ProxyServer& another_proxy_to_bypass,
const BoundNetLog& net_log) const;
private:
- // Updates |proxy_retry_info| to indicate that the proxy in |proxies_| with
- // the URI of |proxy_key| is bad. The |proxy_key| must start with the scheme
- // (only if https) followed by the host and then an explicit port. For
- // example, if the proxy origin is https://proxy.chromium.org:443/ the key is
- // https://proxy.chrome.org:443 whereas if the origin is
- // http://proxy.chrome.org/, the key is proxy.chrome.org:80.
+ // Updates |proxy_retry_info| to indicate that the |proxy_to_retry| in
+ // |proxies_| is bad for |retry_delay|, but may be reconsidered earlier if
+ // |try_while_bad| is true.
void AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
- const std::string& proxy_key,
+ bool try_while_bad,
+ const ProxyServer& proxy_to_retry,
const BoundNetLog& net_log) const;
// List of proxies.
diff --git a/chromium/net/proxy/proxy_list_unittest.cc b/chromium/net/proxy/proxy_list_unittest.cc
index 470a9000882..9ccf9c4e4f7 100644
--- a/chromium/net/proxy/proxy_list_unittest.cc
+++ b/chromium/net/proxy/proxy_list_unittest.cc
@@ -4,6 +4,8 @@
#include "net/proxy/proxy_list.h"
+#include "net/base/net_log.h"
+#include "net/proxy/proxy_retry_info.h"
#include "net/proxy/proxy_server.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -89,42 +91,6 @@ TEST(ProxyListTest, RemoveProxiesWithoutScheme) {
}
}
-TEST(ProxyListTest, HasUntriedProxies) {
- // As in DeprioritizeBadProxies, we use a lengthy timeout to avoid depending
- // on the current time.
- ProxyRetryInfo proxy_retry_info;
- proxy_retry_info.bad_until =
- base::TimeTicks::Now() + base::TimeDelta::FromDays(1);
-
- // An empty list has nothing to try.
- {
- ProxyList list;
- ProxyRetryInfoMap proxy_retry_info;
- EXPECT_FALSE(list.HasUntriedProxies(proxy_retry_info));
- }
-
- // A list with one bad proxy has something to try. With two bad proxies,
- // there's nothing to try.
- {
- ProxyList list;
- list.SetFromPacString("PROXY bad1:80; PROXY bad2:80");
- ProxyRetryInfoMap retry_info_map;
- retry_info_map["bad1:80"] = proxy_retry_info;
- EXPECT_TRUE(list.HasUntriedProxies(retry_info_map));
- retry_info_map["bad2:80"] = proxy_retry_info;
- EXPECT_FALSE(list.HasUntriedProxies(retry_info_map));
- }
-
- // A list with one bad proxy and a DIRECT entry has something to try.
- {
- ProxyList list;
- list.SetFromPacString("PROXY bad1:80; DIRECT");
- ProxyRetryInfoMap retry_info_map;
- retry_info_map["bad1:80"] = proxy_retry_info;
- EXPECT_TRUE(list.HasUntriedProxies(retry_info_map));
- }
-}
-
TEST(ProxyListTest, DeprioritizeBadProxies) {
// Retry info that marks a proxy as being bad for a *very* long time (to avoid
// the test depending on the current time.)
@@ -176,6 +142,81 @@ TEST(ProxyListTest, DeprioritizeBadProxies) {
EXPECT_EQ("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80",
list.ToPacString());
}
+
+ // Call DeprioritizeBadProxies with 2 of the three proxies marked as bad. Of
+ // the 2 bad proxies, one is to be reconsidered and should be retried last.
+ // The other is not to be reconsidered and should be removed from the list.
+ {
+ ProxyList list;
+ list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
+
+ ProxyRetryInfoMap retry_info_map;
+ // |proxy_retry_info.reconsider defaults to true.
+ retry_info_map["foopy1:80"] = proxy_retry_info;
+ proxy_retry_info.try_while_bad = false;
+ retry_info_map["foopy3:80"] = proxy_retry_info;
+ proxy_retry_info.try_while_bad = true;
+ retry_info_map["socks5://localhost:1080"] = proxy_retry_info;
+
+ list.DeprioritizeBadProxies(retry_info_map);
+
+ EXPECT_EQ("PROXY foopy2:80;PROXY foopy1:80",
+ list.ToPacString());
+ }
+}
+
+TEST(ProxyListTest, UpdateRetryInfoOnFallback) {
+ ProxyRetryInfo proxy_retry_info;
+ // Retrying should put the first proxy on the retry list.
+ {
+ ProxyList list;
+ ProxyRetryInfoMap retry_info_map;
+ BoundNetLog net_log;
+ list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
+ list.UpdateRetryInfoOnFallback(&retry_info_map,
+ base::TimeDelta::FromSeconds(60),
+ true,
+ ProxyServer(),
+ net_log);
+ EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
+ }
+ // Including another bad proxy should put both the first and the specified
+ // proxy on the retry list.
+ {
+ ProxyList list;
+ ProxyRetryInfoMap retry_info_map;
+ BoundNetLog net_log;
+ ProxyServer proxy_server = ProxyServer::FromURI("foopy3:80",
+ ProxyServer::SCHEME_HTTP);
+ list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
+ list.UpdateRetryInfoOnFallback(&retry_info_map,
+ base::TimeDelta::FromSeconds(60),
+ true,
+ proxy_server,
+ net_log);
+ EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
+ EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy3:80"));
+ }
+ // If the first proxy is DIRECT, nothing is added to the retry list, even
+ // if another bad proxy is specified.
+ {
+ ProxyList list;
+ ProxyRetryInfoMap retry_info_map;
+ BoundNetLog net_log;
+ ProxyServer proxy_server = ProxyServer::FromURI("foopy2:80",
+ ProxyServer::SCHEME_HTTP);
+ list.SetFromPacString("DIRECT;PROXY foopy2:80;PROXY foopy3:80");
+ list.UpdateRetryInfoOnFallback(&retry_info_map,
+ base::TimeDelta::FromSeconds(60),
+ true,
+ proxy_server,
+ net_log);
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
+ }
}
} // namesapce
diff --git a/chromium/net/proxy/proxy_resolver.h b/chromium/net/proxy/proxy_resolver.h
index 47ec31af5c6..721d90f1f1c 100644
--- a/chromium/net/proxy/proxy_resolver.h
+++ b/chromium/net/proxy/proxy_resolver.h
@@ -60,11 +60,6 @@ class NET_EXPORT_PRIVATE ProxyResolver {
virtual void CancelSetPacScript() = 0;
- // Frees any unneeded memory held by the resolver, e.g. garbage in the JS
- // engine. Most subclasses don't need to do anything, so we provide a default
- // no-op implementation.
- virtual void PurgeMemory() {}
-
// Called to set the PAC script backend to use.
// Returns ERR_IO_PENDING in the case of asynchronous completion, and notifies
// the result through |callback|.
diff --git a/chromium/net/proxy/proxy_resolver_perftest.cc b/chromium/net/proxy/proxy_resolver_perftest.cc
index 12ffd1bd91a..6b0b68fb71b 100644
--- a/chromium/net/proxy/proxy_resolver_perftest.cc
+++ b/chromium/net/proxy/proxy_resolver_perftest.cc
@@ -218,8 +218,7 @@ class MockJSBindings : public net::ProxyResolverV8::JSBindings {
};
TEST(ProxyResolverPerfTest, ProxyResolverV8) {
- // This has to be done on the main thread.
- net::ProxyResolverV8::RememberDefaultIsolate();
+ net::ProxyResolverV8::EnsureIsolateCreated();
MockJSBindings js_bindings;
net::ProxyResolverV8 resolver;
diff --git a/chromium/net/proxy/proxy_resolver_script_data.cc b/chromium/net/proxy/proxy_resolver_script_data.cc
index 7ee07a5d794..8c4fd3187ae 100644
--- a/chromium/net/proxy/proxy_resolver_script_data.cc
+++ b/chromium/net/proxy/proxy_resolver_script_data.cc
@@ -14,7 +14,7 @@ scoped_refptr<ProxyResolverScriptData> ProxyResolverScriptData::FromUTF8(
const std::string& utf8) {
return new ProxyResolverScriptData(TYPE_SCRIPT_CONTENTS,
GURL(),
- UTF8ToUTF16(utf8));
+ base::UTF8ToUTF16(utf8));
}
// static
diff --git a/chromium/net/proxy/proxy_resolver_v8.cc b/chromium/net/proxy/proxy_resolver_v8.cc
index 3de1c7a9f1d..0c08027bcbf 100644
--- a/chromium/net/proxy/proxy_resolver_v8.cc
+++ b/chromium/net/proxy/proxy_resolver_v8.cc
@@ -9,11 +9,13 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/debug/leak_annotations.h"
#include "base/logging.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
+#include "gin/public/isolate_holder.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
@@ -110,7 +112,7 @@ class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
// throughout this object's lifetime.
V8ExternalASCIILiteral(const char* ascii, size_t length)
: ascii_(ascii), length_(length) {
- DCHECK(IsStringASCII(ascii));
+ DCHECK(base::IsStringASCII(ascii));
}
virtual const char* data() const OVERRIDE {
@@ -157,7 +159,7 @@ base::string16 V8StringToUTF16(v8::Handle<v8::String> s) {
// Converts an ASCII std::string to a V8 string.
v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
const std::string& s) {
- DCHECK(IsStringASCII(s));
+ DCHECK(base::IsStringASCII(s));
return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
s.size());
}
@@ -180,7 +182,7 @@ v8::Local<v8::String> ScriptDataToV8String(
// Converts an ASCII string literal to a V8 string.
v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
const char* ascii) {
- DCHECK(IsStringASCII(ascii));
+ DCHECK(base::IsStringASCII(ascii));
size_t length = strlen(ascii);
if (length <= kMaxStringBytesForCopy)
return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
@@ -216,28 +218,27 @@ bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
const base::string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
// If the hostname is already in ASCII, simply return it as is.
- if (IsStringASCII(hostname_utf16)) {
- *hostname = UTF16ToASCII(hostname_utf16);
+ if (base::IsStringASCII(hostname_utf16)) {
+ *hostname = base::UTF16ToASCII(hostname_utf16);
return true;
}
// Otherwise try to convert it from IDN to punycode.
const int kInitialBufferSize = 256;
- url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode_output;
- if (!url_canon::IDNToASCII(hostname_utf16.data(),
- hostname_utf16.length(),
- &punycode_output)) {
+ url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
+ if (!url::IDNToASCII(hostname_utf16.data(), hostname_utf16.length(),
+ &punycode_output)) {
return false;
}
// |punycode_output| should now be ASCII; convert it to a std::string.
// (We could use UTF16ToASCII() instead, but that requires an extra string
// copy. Since ASCII is a subset of UTF8 the following is equivalent).
- bool success = UTF16ToUTF8(punycode_output.data(),
+ bool success = base::UTF16ToUTF8(punycode_output.data(),
punycode_output.length(),
hostname);
DCHECK(success);
- DCHECK(IsStringASCII(*hostname));
+ DCHECK(base::IsStringASCII(*hostname));
return success;
}
@@ -372,7 +373,7 @@ class ProxyResolverV8::Context {
v8::Local<v8::Value> function;
if (!GetFindProxyForURL(&function)) {
js_bindings()->OnError(
- -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
+ -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
return ERR_PAC_SCRIPT_FAILED;
}
@@ -392,25 +393,25 @@ class ProxyResolverV8::Context {
if (!ret->IsString()) {
js_bindings()->OnError(
- -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
+ -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
return ERR_PAC_SCRIPT_FAILED;
}
base::string16 ret_str = V8StringToUTF16(ret->ToString());
- if (!IsStringASCII(ret_str)) {
+ if (!base::IsStringASCII(ret_str)) {
// TODO(eroman): Rather than failing when a wide string is returned, we
// could extend the parsing to handle IDNA hostnames by
// converting them to ASCII punycode.
// crbug.com/47234
base::string16 error_message =
- ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
- "(crbug.com/47234): ") + ret_str;
+ base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
+ "(crbug.com/47234): ") + ret_str;
js_bindings()->OnError(-1, error_message);
return ERR_PAC_SCRIPT_FAILED;
}
- results->UsePacString(UTF16ToASCII(ret_str));
+ results->UsePacString(base::UTF16ToASCII(ret_str));
return OK;
}
@@ -422,7 +423,8 @@ class ProxyResolverV8::Context {
v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
v8::Local<v8::External> v8_this =
v8::Local<v8::External>::New(isolate_, v8_this_);
- v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ v8::Local<v8::ObjectTemplate> global_template =
+ v8::ObjectTemplate::New(isolate_);
// Attach the javascript bindings.
v8::Local<v8::FunctionTemplate> alert_template =
@@ -496,19 +498,13 @@ class ProxyResolverV8::Context {
v8::Local<v8::Value> function;
if (!GetFindProxyForURL(&function)) {
js_bindings()->OnError(
- -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
+ -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
return ERR_PAC_SCRIPT_FAILED;
}
return OK;
}
- void PurgeMemory() {
- v8::Locker locked(isolate_);
- v8::Isolate::Scope isolate_scope(isolate_);
- v8::V8::LowMemoryNotification();
- }
-
private:
bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
v8::Local<v8::Context> context =
@@ -564,7 +560,7 @@ class ProxyResolverV8::Context {
// disregard any arguments beyond the first.
base::string16 message;
if (args.Length() == 0) {
- message = ASCIIToUTF16("undefined");
+ message = base::ASCIIToUTF16("undefined");
} else {
if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
return; // toString() threw an exception.
@@ -665,7 +661,7 @@ class ProxyResolverV8::Context {
}
std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
- if (!IsStringASCII(ip_address_list)) {
+ if (!base::IsStringASCII(ip_address_list)) {
args.GetReturnValue().SetNull();
return;
}
@@ -690,12 +686,12 @@ class ProxyResolverV8::Context {
}
std::string ip_address = V8StringToUTF8(args[0]->ToString());
- if (!IsStringASCII(ip_address)) {
+ if (!base::IsStringASCII(ip_address)) {
args.GetReturnValue().Set(false);
return;
}
std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
- if (!IsStringASCII(ip_prefix)) {
+ if (!base::IsStringASCII(ip_prefix)) {
args.GetReturnValue().Set(false);
return;
}
@@ -750,11 +746,6 @@ void ProxyResolverV8::CancelSetPacScript() {
NOTREACHED();
}
-void ProxyResolverV8::PurgeMemory() {
- if (context_)
- context_->PurgeMemory();
-}
-
int ProxyResolverV8::SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& /*callback*/) {
@@ -774,59 +765,44 @@ int ProxyResolverV8::SetPacScript(
}
// static
-void ProxyResolverV8::RememberDefaultIsolate() {
- v8::Isolate* isolate = v8::Isolate::GetCurrent();
- DCHECK(isolate)
- << "ProxyResolverV8::RememberDefaultIsolate called on wrong thread";
- DCHECK(g_default_isolate_ == NULL || g_default_isolate_ == isolate)
- << "Default Isolate can not be changed";
- g_default_isolate_ = isolate;
-}
-
-#if defined(OS_WIN)
-// static
-void ProxyResolverV8::CreateIsolate() {
- v8::Isolate* isolate = v8::Isolate::New();
- DCHECK(isolate);
- DCHECK(g_default_isolate_ == NULL) << "Default Isolate can not be set twice";
-
- isolate->Enter();
- v8::V8::Initialize();
-
- g_default_isolate_ = isolate;
+void ProxyResolverV8::EnsureIsolateCreated() {
+ if (g_proxy_resolver_isolate_)
+ return;
+ g_proxy_resolver_isolate_ =
+ new gin::IsolateHolder(gin::IsolateHolder::kNonStrictMode);
+ ANNOTATE_LEAKING_OBJECT_PTR(g_proxy_resolver_isolate_);
}
-#endif // defined(OS_WIN)
// static
v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
- DCHECK(g_default_isolate_)
- << "Must call ProxyResolverV8::RememberDefaultIsolate() first";
- return g_default_isolate_;
+ DCHECK(g_proxy_resolver_isolate_)
+ << "Must call ProxyResolverV8::EnsureIsolateCreated() first";
+ return g_proxy_resolver_isolate_->isolate();
}
-v8::Isolate* ProxyResolverV8::g_default_isolate_ = NULL;
+gin::IsolateHolder* ProxyResolverV8::g_proxy_resolver_isolate_ = NULL;
// static
size_t ProxyResolverV8::GetTotalHeapSize() {
- if (!g_default_isolate_)
+ if (!g_proxy_resolver_isolate_)
return 0;
- v8::Locker locked(g_default_isolate_);
- v8::Isolate::Scope isolate_scope(g_default_isolate_);
+ v8::Locker locked(g_proxy_resolver_isolate_->isolate());
+ v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
v8::HeapStatistics heap_statistics;
- g_default_isolate_->GetHeapStatistics(&heap_statistics);
+ g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
return heap_statistics.total_heap_size();
}
// static
size_t ProxyResolverV8::GetUsedHeapSize() {
- if (!g_default_isolate_)
+ if (!g_proxy_resolver_isolate_)
return 0;
- v8::Locker locked(g_default_isolate_);
- v8::Isolate::Scope isolate_scope(g_default_isolate_);
+ v8::Locker locked(g_proxy_resolver_isolate_->isolate());
+ v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
v8::HeapStatistics heap_statistics;
- g_default_isolate_->GetHeapStatistics(&heap_statistics);
+ g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
return heap_statistics.used_heap_size();
}
diff --git a/chromium/net/proxy/proxy_resolver_v8.h b/chromium/net/proxy/proxy_resolver_v8.h
index 76ec7252700..c754e311247 100644
--- a/chromium/net/proxy/proxy_resolver_v8.h
+++ b/chromium/net/proxy/proxy_resolver_v8.h
@@ -10,6 +10,10 @@
#include "net/base/net_export.h"
#include "net/proxy/proxy_resolver.h"
+namespace gin {
+class IsolateHolder;
+} // namespace gin
+
namespace v8 {
class HeapStatistics;
class Isolate;
@@ -86,23 +90,14 @@ class NET_EXPORT_PRIVATE ProxyResolverV8 : public ProxyResolver {
virtual void CancelRequest(RequestHandle request) OVERRIDE;
virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE;
virtual void CancelSetPacScript() OVERRIDE;
- virtual void PurgeMemory() OVERRIDE;
virtual int SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const net::CompletionCallback& /*callback*/) OVERRIDE;
- // Remember the default Isolate, must be called from the main thread. This
- // hack can be removed when the "default Isolate" concept is gone.
- static void RememberDefaultIsolate();
-
-#if defined(OS_WIN)
- // Create an isolate to use for the proxy resolver. Until the "default
- // Isolate" concept is gone, it is preferable to invoke
- // RememberDefaultIsolate() as creating a new Isolate in additional to the
- // default Isolate will waste a few MB of memory and the runtime it took to
- // create the default Isolate.
- static void CreateIsolate();
-#endif
+ // Create an isolate to use for the proxy resolver. If the embedder invokes
+ // this method multiple times, it must be invoked in a thread safe manner,
+ // e.g. always from the same thread.
+ static void EnsureIsolateCreated();
static v8::Isolate* GetDefaultIsolate();
@@ -112,7 +107,7 @@ class NET_EXPORT_PRIVATE ProxyResolverV8 : public ProxyResolver {
static size_t GetUsedHeapSize();
private:
- static v8::Isolate* g_default_isolate_;
+ static gin::IsolateHolder* g_proxy_resolver_isolate_;
// Context holds the Javascript state for the most recently loaded PAC
// script. It corresponds with the data from the last call to
diff --git a/chromium/net/proxy/proxy_resolver_v8_tracing.cc b/chromium/net/proxy/proxy_resolver_v8_tracing.cc
index dfea44a3044..9be256a4fab 100644
--- a/chromium/net/proxy/proxy_resolver_v8_tracing.cc
+++ b/chromium/net/proxy/proxy_resolver_v8_tracing.cc
@@ -973,7 +973,12 @@ HostResolver::RequestInfo ProxyResolverV8Tracing::Job::MakeDnsRequestInfo(
}
HostResolver::RequestInfo info(host_port);
-
+ // Flag myIpAddress requests.
+ if (op == MY_IP_ADDRESS || op == MY_IP_ADDRESS_EX) {
+ // TODO: Provide a RequestInfo construction mechanism that does not
+ // require a hostname and sets is_my_ip_address to true instead of this.
+ info.set_is_my_ip_address(true);
+ }
// The non-ex flavors are limited to IPv4 results.
if (op == MY_IP_ADDRESS || op == DNS_RESOLVE) {
info.set_address_family(ADDRESS_FAMILY_IPV4);
@@ -1152,15 +1157,6 @@ void ProxyResolverV8Tracing::CancelSetPacScript() {
set_pac_script_job_ = NULL;
}
-void ProxyResolverV8Tracing::PurgeMemory() {
- thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&ProxyResolverV8::PurgeMemory,
- // The use of unretained is safe, since the worker thread
- // cannot outlive |this|.
- base::Unretained(v8_resolver_.get())));
-}
-
int ProxyResolverV8Tracing::SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& callback) {
diff --git a/chromium/net/proxy/proxy_resolver_v8_tracing.h b/chromium/net/proxy/proxy_resolver_v8_tracing.h
index 5877aa2088d..d738a092740 100644
--- a/chromium/net/proxy/proxy_resolver_v8_tracing.h
+++ b/chromium/net/proxy/proxy_resolver_v8_tracing.h
@@ -53,7 +53,6 @@ class NET_EXPORT_PRIVATE ProxyResolverV8Tracing
virtual void CancelRequest(RequestHandle request) OVERRIDE;
virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE;
virtual void CancelSetPacScript() OVERRIDE;
- virtual void PurgeMemory() OVERRIDE;
virtual int SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& callback) OVERRIDE;
diff --git a/chromium/net/proxy/proxy_resolver_v8_tracing_unittest.cc b/chromium/net/proxy/proxy_resolver_v8_tracing_unittest.cc
index 805a8734365..5cad3a21b10 100644
--- a/chromium/net/proxy/proxy_resolver_v8_tracing_unittest.cc
+++ b/chromium/net/proxy/proxy_resolver_v8_tracing_unittest.cc
@@ -75,7 +75,7 @@ class MockErrorObserver : public ProxyResolverErrorObserver {
{
base::AutoLock l(lock_);
output += base::StringPrintf("Error: line %d: %s\n", line_number,
- UTF16ToASCII(error).c_str());
+ base::UTF16ToASCII(error).c_str());
}
event_.Signal();
}
@@ -148,7 +148,7 @@ TEST_F(ProxyResolverV8TracingTest, JavascriptError) {
EXPECT_EQ(0u, host_resolver.num_resolve());
- EXPECT_EQ("Error: line 5: Uncaught TypeError: Cannot call method 'split' "
+ EXPECT_EQ("Error: line 5: Uncaught TypeError: Cannot read property 'split' "
"of null\n", error_observer->GetOutput());
// Check the NetLogs -- there was 1 alert and 1 javascript error, and they
@@ -169,7 +169,7 @@ TEST_F(ProxyResolverV8TracingTest, JavascriptError) {
EXPECT_EQ("{\"message\":\"Prepare to DIE!\"}", entries[0].GetParamsJson());
EXPECT_EQ("{\"line_number\":5,\"message\":\"Uncaught TypeError: Cannot "
- "call method 'split' of null\"}", entries[1].GetParamsJson());
+ "read property 'split' of null\"}", entries[1].GetParamsJson());
}
}
diff --git a/chromium/net/proxy/proxy_resolver_v8_unittest.cc b/chromium/net/proxy/proxy_resolver_v8_unittest.cc
index 7ff97702e52..8540eab120c 100644
--- a/chromium/net/proxy/proxy_resolver_v8_unittest.cc
+++ b/chromium/net/proxy/proxy_resolver_v8_unittest.cc
@@ -28,7 +28,7 @@ class MockJSBindings : public ProxyResolverV8::JSBindings {
virtual void Alert(const base::string16& message) OVERRIDE {
VLOG(1) << "PAC-alert: " << message; // Helpful when debugging.
- alerts.push_back(UTF16ToUTF8(message));
+ alerts.push_back(base::UTF16ToUTF8(message));
}
virtual bool ResolveDns(const std::string& host,
@@ -70,7 +70,7 @@ class MockJSBindings : public ProxyResolverV8::JSBindings {
// Helpful when debugging.
VLOG(1) << "PAC-error: [" << line_number << "] " << message;
- errors.push_back(UTF16ToUTF8(message));
+ errors.push_back(base::UTF16ToUTF8(message));
errors_line_number.push_back(line_number);
}
@@ -103,7 +103,6 @@ class ProxyResolverV8WithMockBindings : public ProxyResolverV8 {
}
virtual ~ProxyResolverV8WithMockBindings() {
- PurgeMemory();
}
MockJSBindings* mock_js_bindings() {
diff --git a/chromium/net/proxy/proxy_resolver_winhttp.cc b/chromium/net/proxy/proxy_resolver_winhttp.cc
index 32737875e3e..2d90dab6e05 100644
--- a/chromium/net/proxy/proxy_resolver_winhttp.cc
+++ b/chromium/net/proxy/proxy_resolver_winhttp.cc
@@ -54,7 +54,7 @@ int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url,
WINHTTP_AUTOPROXY_OPTIONS options = {0};
options.fAutoLogonIfChallenged = FALSE;
options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
- std::wstring pac_url_wide = ASCIIToWide(pac_url_.spec());
+ std::wstring pac_url_wide = base::ASCIIToWide(pac_url_.spec());
options.lpszAutoConfigUrl = pac_url_wide.c_str();
WINHTTP_PROXY_INFO info = {0};
@@ -65,13 +65,14 @@ int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url,
// Otherwise, we fail over to trying it with a value of true. This way we
// get good performance in the case where WinHTTP uses an out-of-process
// resolver. This is important for Vista and Win2k3.
- BOOL ok = WinHttpGetProxyForUrl(
- session_handle_, ASCIIToWide(query_url.spec()).c_str(), &options, &info);
+ BOOL ok = WinHttpGetProxyForUrl(session_handle_,
+ base::ASCIIToWide(query_url.spec()).c_str(),
+ &options, &info);
if (!ok) {
if (ERROR_WINHTTP_LOGIN_FAILURE == GetLastError()) {
options.fAutoLogonIfChallenged = TRUE;
ok = WinHttpGetProxyForUrl(
- session_handle_, ASCIIToWide(query_url.spec()).c_str(),
+ session_handle_, base::ASCIIToWide(query_url.spec()).c_str(),
&options, &info);
}
if (!ok) {
@@ -107,7 +108,7 @@ int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url,
// things like "foopy1:80;foopy2:80". It strips out the non-HTTP
// proxy types, and stops the list when PAC encounters a "DIRECT".
// So UseNamedProxy() should work OK.
- results->UseNamedProxy(WideToASCII(info.lpszProxy));
+ results->UseNamedProxy(base::UTF16ToASCII(info.lpszProxy));
break;
default:
NOTREACHED();
diff --git a/chromium/net/proxy/proxy_retry_info.h b/chromium/net/proxy/proxy_retry_info.h
index 8825289a352..0d073b9c2cd 100644
--- a/chromium/net/proxy/proxy_retry_info.h
+++ b/chromium/net/proxy/proxy_retry_info.h
@@ -13,12 +13,17 @@ namespace net {
// Contains the information about when to retry a proxy server.
struct ProxyRetryInfo {
+ ProxyRetryInfo() : try_while_bad(true) {}
+
// We should not retry until this time.
base::TimeTicks bad_until;
// This is the current delay. If the proxy is still bad, we need to increase
// this delay.
base::TimeDelta current_delay;
+
+ // True if this proxy should be considered even if still bad.
+ bool try_while_bad;
};
// Map of proxy servers with the associated RetryInfo structures.
diff --git a/chromium/net/proxy/proxy_script_decider.cc b/chromium/net/proxy/proxy_script_decider.cc
index eab59ac373c..939f7af1422 100644
--- a/chromium/net/proxy/proxy_script_decider.cc
+++ b/chromium/net/proxy/proxy_script_decider.cc
@@ -30,7 +30,8 @@ bool LooksLikePacScript(const base::string16& script) {
// file not containing the string is not likely to be a PAC script.
//
// An exact test would have to load the script in a javascript evaluator.
- return script.find(ASCIIToUTF16("FindProxyForURL")) != base::string16::npos;
+ return script.find(base::ASCIIToUTF16("FindProxyForURL")) !=
+ base::string16::npos;
}
}
@@ -86,7 +87,8 @@ ProxyScriptDecider::ProxyScriptDecider(
next_state_(STATE_NONE),
net_log_(BoundNetLog::Make(
net_log, NetLog::SOURCE_PROXY_SCRIPT_DECIDER)),
- fetch_pac_bytes_(false) {
+ fetch_pac_bytes_(false),
+ quick_check_enabled_(true) {
if (proxy_script_fetcher &&
proxy_script_fetcher->GetRequestContext() &&
proxy_script_fetcher->GetRequestContext()->host_resolver()) {
@@ -240,7 +242,7 @@ int ProxyScriptDecider::DoWaitComplete(int result) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT,
result);
}
- if (current_pac_source().type == PacSource::WPAD_DNS)
+ if (quick_check_enabled_ && current_pac_source().type == PacSource::WPAD_DNS)
next_state_ = STATE_QUICK_CHECK;
else
next_state_ = GetStartState();
@@ -248,6 +250,7 @@ int ProxyScriptDecider::DoWaitComplete(int result) {
}
int ProxyScriptDecider::DoQuickCheck() {
+ DCHECK(quick_check_enabled_);
if (host_resolver_.get() == NULL) {
// If we have no resolver, skip QuickCheck altogether.
next_state_ = GetStartState();
@@ -274,6 +277,7 @@ int ProxyScriptDecider::DoQuickCheck() {
}
int ProxyScriptDecider::DoQuickCheckComplete(int result) {
+ DCHECK(quick_check_enabled_);
base::TimeDelta delta = base::Time::Now() - quick_check_start_time_;
if (result == OK)
UMA_HISTOGRAM_TIMES("Net.WpadQuickCheckSuccess", delta);
@@ -409,7 +413,7 @@ int ProxyScriptDecider::TryToFallbackPacSource(int error) {
net_log_.AddEvent(
NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE);
- if (current_pac_source().type == PacSource::WPAD_DNS)
+ if (quick_check_enabled_ && current_pac_source().type == PacSource::WPAD_DNS)
next_state_ = STATE_QUICK_CHECK;
else
next_state_ = GetStartState();
diff --git a/chromium/net/proxy/proxy_script_decider.h b/chromium/net/proxy/proxy_script_decider.h
index 23fa7af85c2..d5790806a1e 100644
--- a/chromium/net/proxy/proxy_script_decider.h
+++ b/chromium/net/proxy/proxy_script_decider.h
@@ -79,6 +79,12 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
// TODO(eroman): Return a const-pointer.
ProxyResolverScriptData* script_data() const;
+ void set_quick_check_enabled(bool enabled) {
+ quick_check_enabled_ = enabled;
+ }
+
+ bool quick_check_enabled() const { return quick_check_enabled_; }
+
private:
// Represents the sources from which we can get PAC files; two types of
// auto-detect or a custom URL.
@@ -182,6 +188,9 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
base::TimeDelta wait_delay_;
base::OneShotTimer<ProxyScriptDecider> wait_timer_;
+ // Whether to do DNS quick check
+ bool quick_check_enabled_;
+
// Results.
ProxyConfig effective_config_;
scoped_refptr<ProxyResolverScriptData> script_data_;
diff --git a/chromium/net/proxy/proxy_script_decider_unittest.cc b/chromium/net/proxy/proxy_script_decider_unittest.cc
index 5e2b489d619..c50b31e4a91 100644
--- a/chromium/net/proxy/proxy_script_decider_unittest.cc
+++ b/chromium/net/proxy/proxy_script_decider_unittest.cc
@@ -17,6 +17,7 @@
#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/mock_proxy_script_fetcher.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_decider.h"
@@ -43,9 +44,9 @@ class Rules {
base::string16 text() const {
if (is_valid_script)
- return UTF8ToUTF16(url.spec() + "!FindProxyForURL");
+ return base::UTF8ToUTF16(url.spec() + "!FindProxyForURL");
if (fetch_error == OK)
- return UTF8ToUTF16(url.spec() + "!invalid-script");
+ return base::UTF8ToUTF16(url.spec() + "!invalid-script");
return base::string16();
}
@@ -329,11 +330,10 @@ class ProxyScriptDeciderQuickCheckTest : public ::testing::Test {
TestCompletionCallback callback_;
RuleBasedProxyScriptFetcher fetcher_;
ProxyConfig config_;
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher_;
private:
URLRequestContext request_context_;
-
- DoNothingDhcpProxyScriptFetcher dhcp_fetcher_;
};
// Fails if a synchronous DNS lookup success for wpad causes QuickCheck to fail.
@@ -401,6 +401,21 @@ TEST_F(ProxyScriptDeciderQuickCheckTest, QuickCheckInhibitsDhcp) {
EXPECT_EQ(decider_->effective_config().pac_url(), url);
}
+// Fails if QuickCheck still happens when disabled. To ensure QuickCheck is not
+// happening, we add a synchronous failing resolver, which would ordinarily
+// mean a QuickCheck failure, then ensure that our ProxyScriptFetcher is still
+// asked to fetch.
+TEST_F(ProxyScriptDeciderQuickCheckTest, QuickCheckDisabled) {
+ const char *kPac = "function FindProxyForURL(u,h) { return \"DIRECT\"; }";
+ resolver_.set_synchronous_mode(true);
+ resolver_.rules()->AddSimulatedFailure("wpad");
+ MockProxyScriptFetcher fetcher;
+ decider_.reset(new ProxyScriptDecider(&fetcher, &dhcp_fetcher_, NULL));
+ EXPECT_EQ(ERR_IO_PENDING, StartDecider());
+ EXPECT_TRUE(fetcher.has_pending_request());
+ fetcher.NotifyFetchCompletion(OK, kPac);
+}
+
TEST_F(ProxyScriptDeciderQuickCheckTest, ExplicitPacUrl) {
const char *kCustomUrl = "http://custom/proxy.pac";
config_.set_pac_url(GURL(kCustomUrl));
@@ -660,7 +675,7 @@ TEST(ProxyScriptDeciderTest, AutodetectDhcpSuccess) {
Rules rules;
RuleBasedProxyScriptFetcher fetcher(&rules);
SynchronousSuccessDhcpFetcher dhcp_fetcher(
- WideToUTF16(L"http://bingo/!FindProxyForURL"));
+ base::WideToUTF16(L"http://bingo/!FindProxyForURL"));
ProxyConfig config;
config.set_auto_detect(true);
@@ -683,7 +698,7 @@ TEST(ProxyScriptDeciderTest, AutodetectDhcpFailParse) {
Rules rules;
RuleBasedProxyScriptFetcher fetcher(&rules);
SynchronousSuccessDhcpFetcher dhcp_fetcher(
- WideToUTF16(L"http://bingo/!invalid-script"));
+ base::WideToUTF16(L"http://bingo/!invalid-script"));
ProxyConfig config;
config.set_auto_detect(true);
diff --git a/chromium/net/proxy/proxy_script_fetcher_impl.cc b/chromium/net/proxy/proxy_script_fetcher_impl.cc
index 705bbbf26b0..737c6f8450f 100644
--- a/chromium/net/proxy/proxy_script_fetcher_impl.cc
+++ b/chromium/net/proxy/proxy_script_fetcher_impl.cc
@@ -5,7 +5,6 @@
#include "net/proxy/proxy_script_fetcher_impl.h"
#include "base/compiler_specific.h"
-#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
@@ -13,6 +12,7 @@
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
+#include "net/base/net_string_util.h"
#include "net/base/request_priority.h"
#include "net/cert/cert_status_flags.h"
#include "net/http/http_response_headers.h"
@@ -56,18 +56,15 @@ void ConvertResponseToUTF16(const std::string& charset,
if (charset.empty()) {
// Assume ISO-8859-1 if no charset was specified.
- codepage = base::kCodepageLatin1;
+ codepage = kCharsetLatin1;
} else {
// Otherwise trust the charset that was provided.
codepage = charset.c_str();
}
- // We will be generous in the conversion -- if any characters lie
- // outside of |charset| (i.e. invalid), then substitute them with
- // U+FFFD rather than failing.
- base::CodepageToUTF16(bytes, codepage,
- base::OnStringConversionError::SUBSTITUTE,
- utf16);
+ // Be generous in the conversion -- if any characters lie outside of |charset|
+ // (i.e. invalid), then substitute them with U+FFFD rather than failing.
+ ConvertToUTF16WithSubstitutions(bytes, codepage, utf16);
}
} // namespace
@@ -135,7 +132,7 @@ int ProxyScriptFetcherImpl::Fetch(
}
cur_request_ =
- url_request_context_->CreateRequest(url, DEFAULT_PRIORITY, this);
+ url_request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, NULL);
cur_request_->set_method("GET");
// Make sure that the PAC script is downloaded using a direct connection,
diff --git a/chromium/net/proxy/proxy_script_fetcher_impl_unittest.cc b/chromium/net/proxy/proxy_script_fetcher_impl_unittest.cc
index 9c1ca98a764..2536b029b4b 100644
--- a/chromium/net/proxy/proxy_script_fetcher_impl_unittest.cc
+++ b/chromium/net/proxy/proxy_script_fetcher_impl_unittest.cc
@@ -10,8 +10,8 @@
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
+#include "net/base/filename_util.h"
#include "net/base/load_flags.h"
-#include "net/base/net_util.h"
#include "net/base/test_completion_callback.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/disk_cache/disk_cache.h"
@@ -22,7 +22,6 @@
#include "net/http/transport_security_state.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
-#include "net/url_request/file_protocol_handler.h"
#include "net/url_request/url_request_context_storage.h"
#include "net/url_request/url_request_file_job.h"
#include "net/url_request/url_request_job_factory_impl.h"
@@ -30,6 +29,12 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+#if !defined(DISABLE_FILE_SUPPORT)
+#include "net/url_request/file_protocol_handler.h"
+#endif
+
+using base::ASCIIToUTF16;
+
namespace net {
// TODO(eroman):
@@ -46,7 +51,8 @@ struct FetchResult {
base::string16 text;
};
-// A non-mock URL request which can access http:// and file:// urls.
+// A non-mock URL request which can access http:// and file:// urls, in the case
+// the tests were built with file support.
class RequestContext : public URLRequestContext {
public:
RequestContext() : storage_(this) {
@@ -71,8 +77,10 @@ class RequestContext : public URLRequestContext {
storage_.set_http_transaction_factory(new HttpCache(
network_session.get(), HttpCache::DefaultBackend::InMemory(0)));
URLRequestJobFactoryImpl* job_factory = new URLRequestJobFactoryImpl();
+#if !defined(DISABLE_FILE_SUPPORT)
job_factory->SetProtocolHandler(
"file", new FileProtocolHandler(base::MessageLoopProxy::current()));
+#endif
storage_.set_job_factory(job_factory);
}
@@ -83,6 +91,7 @@ class RequestContext : public URLRequestContext {
URLRequestContextStorage storage_;
};
+#if !defined(DISABLE_FILE_SUPPORT)
// Get a file:// url relative to net/data/proxy/proxy_script_fetcher_unittest.
GURL GetTestFileUrl(const std::string& relpath) {
base::FilePath path;
@@ -93,6 +102,7 @@ GURL GetTestFileUrl(const std::string& relpath) {
GURL base_url = FilePathToFileURL(path);
return GURL(base_url.spec() + "/" + relpath);
}
+#endif // !defined(DISABLE_FILE_SUPPORT)
// Really simple NetworkDelegate so we can allow local file access on ChromeOS
// without introducing layering violations. Also causes a test failure if a
@@ -124,8 +134,8 @@ class BasicNetworkDelegate : public NetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers)
- OVERRIDE {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE {
return OK;
}
@@ -177,10 +187,6 @@ class BasicNetworkDelegate : public NetworkDelegate {
return OK;
}
- virtual void OnRequestWaitStateChange(const net::URLRequest& request,
- RequestWaitState state) OVERRIDE {
- }
-
DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate);
};
@@ -201,6 +207,7 @@ class ProxyScriptFetcherImplTest : public PlatformTest {
RequestContext context_;
};
+#if !defined(DISABLE_FILE_SUPPORT)
TEST_F(ProxyScriptFetcherImplTest, FileUrl) {
ProxyScriptFetcherImpl pac_fetcher(&context_);
@@ -223,6 +230,7 @@ TEST_F(ProxyScriptFetcherImplTest, FileUrl) {
EXPECT_EQ(ASCIIToUTF16("-pac.txt-\n"), text);
}
}
+#endif // !defined(DISABLE_FILE_SUPPORT)
// Note that all mime types are allowed for PAC file, to be consistent
// with other browsers.
@@ -346,7 +354,9 @@ TEST_F(ProxyScriptFetcherImplTest, TooLarge) {
// These two URLs are the same file, but are http:// vs file://
GURL urls[] = {
test_server_.GetURL("files/large-pac.nsproxy"),
+#if !defined(DISABLE_FILE_SUPPORT)
GetTestFileUrl("large-pac.nsproxy")
+#endif
};
// Try fetching URLs that are 101 bytes large. We should abort the request
diff --git a/chromium/net/proxy/proxy_server.cc b/chromium/net/proxy/proxy_server.cc
index a6ea5deafde..b0997e0a0fa 100644
--- a/chromium/net/proxy/proxy_server.cc
+++ b/chromium/net/proxy/proxy_server.cc
@@ -36,6 +36,8 @@ ProxyServer::Scheme GetSchemeFromPacTypeInternal(
return ProxyServer::SCHEME_DIRECT;
if (LowerCaseEqualsASCII(begin, end, "https"))
return ProxyServer::SCHEME_HTTPS;
+ if (LowerCaseEqualsASCII(begin, end, "quic"))
+ return ProxyServer::SCHEME_QUIC;
return ProxyServer::SCHEME_INVALID;
}
@@ -57,6 +59,8 @@ ProxyServer::Scheme GetSchemeFromURIInternal(std::string::const_iterator begin,
return ProxyServer::SCHEME_DIRECT;
if (LowerCaseEqualsASCII(begin, end, "https"))
return ProxyServer::SCHEME_HTTPS;
+ if (LowerCaseEqualsASCII(begin, end, "quic"))
+ return ProxyServer::SCHEME_QUIC;
return ProxyServer::SCHEME_INVALID;
}
@@ -131,6 +135,8 @@ std::string ProxyServer::ToURI() const {
return std::string("socks5://") + host_port_pair().ToString();
case SCHEME_HTTPS:
return std::string("https://") + host_port_pair().ToString();
+ case SCHEME_QUIC:
+ return std::string("quic://") + host_port_pair().ToString();
default:
// Got called with an invalid scheme.
NOTREACHED();
@@ -181,6 +187,8 @@ std::string ProxyServer::ToPacString() const {
return std::string("SOCKS5 ") + host_port_pair().ToString();
case SCHEME_HTTPS:
return std::string("HTTPS ") + host_port_pair().ToString();
+ case SCHEME_QUIC:
+ return std::string("QUIC ") + host_port_pair().ToString();
default:
// Got called with an invalid scheme.
NOTREACHED();
@@ -197,10 +205,13 @@ int ProxyServer::GetDefaultPortForScheme(Scheme scheme) {
case SCHEME_SOCKS5:
return 1080;
case SCHEME_HTTPS:
+ case SCHEME_QUIC:
return 443;
- default:
- return -1;
+ case SCHEME_INVALID:
+ case SCHEME_DIRECT:
+ break;
}
+ return -1;
}
// static
@@ -211,7 +222,12 @@ ProxyServer::Scheme ProxyServer::GetSchemeFromURI(const std::string& scheme) {
// TODO(bengr): Use |scheme_| to indicate that this is the data reduction proxy.
#if defined(SPDY_PROXY_AUTH_ORIGIN)
bool ProxyServer::isDataReductionProxy() const {
- return host_port_pair_.Equals(
+ bool dev_host = false;
+#if defined (DATA_REDUCTION_DEV_HOST)
+ dev_host = host_port_pair_.Equals(
+ HostPortPair::FromURL(GURL(DATA_REDUCTION_DEV_HOST)));
+#endif
+ return dev_host || host_port_pair_.Equals(
HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)));
}
diff --git a/chromium/net/proxy/proxy_server.h b/chromium/net/proxy/proxy_server.h
index 08a20c0347c..27b8b049e50 100644
--- a/chromium/net/proxy/proxy_server.h
+++ b/chromium/net/proxy/proxy_server.h
@@ -31,6 +31,9 @@ class NET_EXPORT ProxyServer {
SCHEME_SOCKS4 = 1 << 3,
SCHEME_SOCKS5 = 1 << 4,
SCHEME_HTTPS = 1 << 5,
+ // A QUIC proxy is an HTTP proxy in which QUIC is used as the transport,
+ // instead of TCP.
+ SCHEME_QUIC = 1 << 6,
};
// Default copy-constructor and assignment operator are OK!
@@ -59,6 +62,9 @@ class NET_EXPORT ProxyServer {
return scheme_ == SCHEME_SOCKS4 || scheme_ == SCHEME_SOCKS5;
}
+ // Returns true if this ProxyServer is a QUIC proxy.
+ bool is_quic() const { return scheme_ == SCHEME_QUIC; }
+
const HostPortPair& host_port_pair() const;
// Parses from an input with format:
@@ -77,6 +83,7 @@ class NET_EXPORT ProxyServer {
// "socks5://foopy" {scheme=SOCKS5, host="foopy", port=1080}
// "http://foopy:17" {scheme=HTTP, host="foopy", port=17}
// "https://foopy:17" {scheme=HTTPS, host="foopy", port=17}
+ // "quic://foopy:17" {scheme=QUIC, host="foopy", port=17}
// "direct://" {scheme=DIRECT}
// "foopy:X" INVALID -- bad port.
static ProxyServer FromURI(const std::string& uri, Scheme default_scheme);
@@ -99,6 +106,7 @@ class NET_EXPORT ProxyServer {
// "DIRECT" {scheme=DIRECT}
// "SOCKS5 foopy" {scheme=SOCKS5, host="foopy", port=1080}
// "HTTPS foopy:123" {scheme=HTTPS, host="foopy", port=123}
+ // "QUIC foopy:123" {scheme=QUIC, host="foopy", port=123}
// "BLAH xxx:xx" INVALID
static ProxyServer FromPacString(const std::string& pac_string);
static ProxyServer FromPacString(std::string::const_iterator pac_string_begin,
diff --git a/chromium/net/proxy/proxy_service.cc b/chromium/net/proxy/proxy_service.cc
index 9d4125b6a34..c5cd6ac3354 100644
--- a/chromium/net/proxy/proxy_service.cc
+++ b/chromium/net/proxy/proxy_service.cc
@@ -13,6 +13,8 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/strings/string_util.h"
#include "base/thread_task_runner_handle.h"
#include "base/values.h"
@@ -45,10 +47,6 @@
#include "net/proxy/proxy_config_service_android.h"
#endif
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-#include "base/metrics/histogram.h"
-#endif
-
using base::TimeDelta;
using base::TimeTicks;
@@ -343,7 +341,8 @@ class ProxyService::InitProxyResolver {
public:
InitProxyResolver()
: proxy_resolver_(NULL),
- next_state_(STATE_NONE) {
+ next_state_(STATE_NONE),
+ quick_check_enabled_(true) {
}
~InitProxyResolver() {
@@ -367,6 +366,7 @@ class ProxyService::InitProxyResolver {
decider_.reset(new ProxyScriptDecider(
proxy_script_fetcher, dhcp_proxy_script_fetcher, net_log));
+ decider_->set_quick_check_enabled(quick_check_enabled_);
config_ = config;
wait_delay_ = wait_delay;
callback_ = callback;
@@ -420,6 +420,9 @@ class ProxyService::InitProxyResolver {
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
}
+ void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
+ bool quick_check_enabled() const { return quick_check_enabled_; }
+
private:
enum State {
STATE_NONE,
@@ -511,6 +514,7 @@ class ProxyService::InitProxyResolver {
ProxyResolver* proxy_resolver_;
CompletionCallback callback_;
State next_state_;
+ bool quick_check_enabled_;
DISALLOW_COPY_AND_ASSIGN(InitProxyResolver);
};
@@ -581,6 +585,9 @@ class ProxyService::ProxyScriptDeciderPoller {
return prev;
}
+ void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
+ bool quick_check_enabled() const { return quick_check_enabled_; }
+
private:
// Returns the effective poll policy (the one injected by unit-tests, or the
// default).
@@ -624,6 +631,7 @@ class ProxyService::ProxyScriptDeciderPoller {
// TODO(eroman): Pass a proper NetLog rather than NULL.
decider_.reset(new ProxyScriptDecider(
proxy_script_fetcher_, dhcp_proxy_script_fetcher_, NULL));
+ decider_->set_quick_check_enabled(quick_check_enabled_);
int result = decider_->Start(
config_, TimeDelta(), proxy_resolver_expects_pac_bytes_,
base::Bind(&ProxyScriptDeciderPoller::OnProxyScriptDeciderCompleted,
@@ -710,6 +718,8 @@ class ProxyService::ProxyScriptDeciderPoller {
const DefaultPollPolicy default_poll_policy_;
+ bool quick_check_enabled_;
+
DISALLOW_COPY_AND_ASSIGN(ProxyScriptDeciderPoller);
};
@@ -873,7 +883,8 @@ ProxyService::ProxyService(ProxyConfigService* config_service,
current_state_(STATE_NONE) ,
net_log_(net_log),
stall_proxy_auto_config_delay_(TimeDelta::FromMilliseconds(
- kDelayAfterNetworkChangesMs)) {
+ kDelayAfterNetworkChangesMs)),
+ quick_check_enabled_(true) {
NetworkChangeNotifier::AddIPAddressObserver(this);
NetworkChangeNotifier::AddDNSObserver(this);
ResetConfigService(config_service);
@@ -1124,6 +1135,7 @@ void ProxyService::OnInitProxyResolverComplete(int result) {
result,
init_proxy_resolver_->script_data(),
NULL));
+ script_poller_->set_quick_check_enabled(quick_check_enabled_);
init_proxy_resolver_.reset();
@@ -1154,6 +1166,7 @@ void ProxyService::OnInitProxyResolverComplete(int result) {
}
int ProxyService::ReconsiderProxyAfterError(const GURL& url,
+ int net_error,
ProxyInfo* result,
const CompletionCallback& callback,
PacRequest** pac_request,
@@ -1177,9 +1190,13 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url,
if (result->proxy_server().isDataReductionProxy()) {
RecordDataReductionProxyBypassInfo(
true, result->proxy_server(), ERROR_BYPASS);
+ RecordDataReductionProxyBypassOnNetworkError(
+ true, result->proxy_server(), net_error);
} else if (result->proxy_server().isDataReductionProxyFallback()) {
RecordDataReductionProxyBypassInfo(
false, result->proxy_server(), ERROR_BYPASS);
+ RecordDataReductionProxyBypassOnNetworkError(
+ false, result->proxy_server(), net_error);
}
#endif
@@ -1192,15 +1209,19 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url,
return did_fallback ? OK : ERR_FAILED;
}
-bool ProxyService::MarkProxiesAsBad(
+bool ProxyService::MarkProxiesAsBadUntil(
const ProxyInfo& result,
base::TimeDelta retry_delay,
const ProxyServer& another_bad_proxy,
const BoundNetLog& net_log) {
result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay,
+ false,
another_bad_proxy,
net_log);
- return result.proxy_list_.HasUntriedProxies(proxy_retry_info_);
+ if (another_bad_proxy.is_valid())
+ return result.proxy_list_.size() > 2;
+ else
+ return result.proxy_list_.size() > 1;
}
void ProxyService::ReportSuccess(const ProxyInfo& result) {
@@ -1258,7 +1279,7 @@ int ProxyService::DidFinishResolvingProxy(ProxyInfo* result,
// Log the result of the proxy resolution.
if (result_code == OK) {
// When logging all events is enabled, dump the proxy list.
- if (net_log.IsLoggingAllEvents()) {
+ if (net_log.IsLogging()) {
net_log.AddEvent(
NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
base::Bind(&NetLogFinishedResolvingProxyCallback, result));
@@ -1337,12 +1358,6 @@ void ProxyService::ResetConfigService(
ApplyProxyConfigIfAvailable();
}
-void ProxyService::PurgeMemory() {
- DCHECK(CalledOnValidThread());
- if (resolver_.get())
- resolver_->PurgeMemory();
-}
-
void ProxyService::ForceReloadProxyConfig() {
DCHECK(CalledOnValidThread());
ResetProxyConfig(false);
@@ -1410,7 +1425,6 @@ scoped_ptr<ProxyService::PacPollPolicy>
return scoped_ptr<PacPollPolicy>(new DefaultPollPolicy());
}
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
void ProxyService::RecordDataReductionProxyBypassInfo(
bool is_primary,
const ProxyServer& proxy_server,
@@ -1427,7 +1441,25 @@ void ProxyService::RecordDataReductionProxyBypassInfo(
bypass_type, BYPASS_EVENT_TYPE_MAX);
}
}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
+
+void ProxyService::RecordDataReductionProxyBypassOnNetworkError(
+ bool is_primary,
+ const ProxyServer& proxy_server,
+ int net_error) {
+ // Only record UMA if the proxy isn't already on the retry list.
+ if (proxy_retry_info_.find(proxy_server.ToURI()) != proxy_retry_info_.end())
+ return;
+
+ if (is_primary) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "DataReductionProxy.BypassOnNetworkErrorPrimary",
+ std::abs(net_error));
+ return;
+ }
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "DataReductionProxy.BypassOnNetworkErrorFallback",
+ std::abs(net_error));
+}
void ProxyService::OnProxyConfigChanged(
const ProxyConfig& config,
@@ -1486,6 +1518,7 @@ void ProxyService::InitializeUsingLastFetchedConfig() {
stall_proxy_autoconfig_until_ - TimeTicks::Now();
init_proxy_resolver_.reset(new InitProxyResolver());
+ init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_);
int rv = init_proxy_resolver_->Start(
resolver_.get(),
proxy_script_fetcher_.get(),
@@ -1568,13 +1601,14 @@ int SyncProxyServiceHelper::ResolveProxy(const GURL& url,
}
int SyncProxyServiceHelper::ReconsiderProxyAfterError(
- const GURL& url, ProxyInfo* proxy_info, const BoundNetLog& net_log) {
+ const GURL& url, int net_error, ProxyInfo* proxy_info,
+ const BoundNetLog& net_log) {
DCHECK(io_message_loop_ != base::MessageLoop::current());
io_message_loop_->PostTask(
FROM_HERE,
base::Bind(&SyncProxyServiceHelper::StartAsyncReconsider, this, url,
- net_log));
+ net_error, net_log));
event_.Wait();
@@ -1596,9 +1630,10 @@ void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url,
}
void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url,
+ int net_error,
const BoundNetLog& net_log) {
result_ = proxy_service_->ReconsiderProxyAfterError(
- url, &proxy_info_, callback_, NULL, net_log);
+ url, net_error, &proxy_info_, callback_, NULL, net_log);
if (result_ != net::ERR_IO_PENDING) {
OnCompletion(result_);
}
diff --git a/chromium/net/proxy/proxy_service.h b/chromium/net/proxy/proxy_service.h
index 136ec6763f2..b6ef447d43b 100644
--- a/chromium/net/proxy/proxy_service.h
+++ b/chromium/net/proxy/proxy_service.h
@@ -132,8 +132,10 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// This method is called after a failure to connect or resolve a host name.
// It gives the proxy service an opportunity to reconsider the proxy to use.
// The |results| parameter contains the results returned by an earlier call
- // to ResolveProxy. The semantics of this call are otherwise similar to
- // ResolveProxy.
+ // to ResolveProxy. The |net_error| parameter contains the network error
+ // code associated with the failure. See "net/base/net_error_list.h" for a
+ // list of possible values. The semantics of this call are otherwise
+ // similar to ResolveProxy.
//
// NULL can be passed for |pac_request| if the caller will not need to
// cancel the request.
@@ -142,6 +144,7 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
//
// Profiling information for the request is saved to |net_log| if non-NULL.
int ReconsiderProxyAfterError(const GURL& url,
+ int net_error,
ProxyInfo* results,
const CompletionCallback& callback,
PacRequest** pac_request,
@@ -150,13 +153,14 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// Explicitly trigger proxy fallback for the given |results| by updating our
// list of bad proxies to include the first entry of |results|, and,
// optionally, another bad proxy. Will retry after |retry_delay| if positive,
- // and will use the default proxy retry duration otherwise. Returns true if
+ // and will use the default proxy retry duration otherwise. Proxies marked as
+ // bad will not be retried until |retry_delay| has passed. Returns true if
// there will be at least one proxy remaining in the list after fallback and
// false otherwise.
- bool MarkProxiesAsBad(const ProxyInfo& results,
- base::TimeDelta retry_delay,
- const ProxyServer& another_bad_proxy,
- const BoundNetLog& net_log);
+ bool MarkProxiesAsBadUntil(const ProxyInfo& results,
+ base::TimeDelta retry_delay,
+ const ProxyServer& another_bad_proxy,
+ const BoundNetLog& net_log);
// Called to report that the last proxy connection succeeded. If |proxy_info|
// has a non empty proxy_retry_info map, the proxies that have been tried (and
@@ -184,10 +188,6 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// |new_proxy_config_service|.
void ResetConfigService(ProxyConfigService* new_proxy_config_service);
- // Tells the resolver to purge any memory it does not need.
- void PurgeMemory();
-
-
// Returns the last configuration fetched from ProxyConfigService.
const ProxyConfig& fetched_config() {
return fetched_config_;
@@ -263,7 +263,12 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// of the default internal PacPollPolicy used by ProxyService.
static scoped_ptr<PacPollPolicy> CreateDefaultPacPollPolicy();
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
+ void set_quick_check_enabled(bool value) {
+ quick_check_enabled_ = value;
+ }
+
+ bool quick_check_enabled() const { return quick_check_enabled_; }
+
// Values of the UMA DataReductionProxy.BypassInfo{Primary|Fallback}
// histograms. This enum must remain synchronized with the enum of the same
// name in metrics/histograms/histograms.xml.
@@ -283,6 +288,12 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// Bypass the proxy because responses appear not to be coming via it.
MISSING_VIA_HEADER,
+ // Bypass the proxy because the proxy, not the origin, sent a 4xx response.
+ PROXY_4XX_BYPASS,
+
+ // Bypass the proxy because we got a 407 from the proxy without a challenge.
+ MALFORMED_407_BYPASS,
+
// This must always be last.
BYPASS_EVENT_TYPE_MAX
};
@@ -293,7 +304,13 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
bool is_primary,
const ProxyServer& proxy_server,
DataReductionProxyBypassEventType bypass_type) const;
-#endif
+
+ // Records a net error code that resulted in bypassing the data reduction
+ // proxy (|is_primary| is true) or the data reduction proxy fallback.
+ void RecordDataReductionProxyBypassOnNetworkError(
+ bool is_primary,
+ const ProxyServer& proxy_server,
+ int net_error);
private:
FRIEND_TEST_ALL_PREFIXES(ProxyServiceTest, UpdateConfigAfterFailedAutodetect);
@@ -438,6 +455,9 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// The amount of time to stall requests following IP address changes.
base::TimeDelta stall_proxy_auto_config_delay_;
+ // Whether child ProxyScriptDeciders should use QuickCheck
+ bool quick_check_enabled_;
+
DISALLOW_COPY_AND_ASSIGN(ProxyService);
};
@@ -452,6 +472,7 @@ class NET_EXPORT SyncProxyServiceHelper
ProxyInfo* proxy_info,
const BoundNetLog& net_log);
int ReconsiderProxyAfterError(const GURL& url,
+ int net_error,
ProxyInfo* proxy_info,
const BoundNetLog& net_log);
@@ -461,7 +482,9 @@ class NET_EXPORT SyncProxyServiceHelper
virtual ~SyncProxyServiceHelper();
void StartAsyncResolve(const GURL& url, const BoundNetLog& net_log);
- void StartAsyncReconsider(const GURL& url, const BoundNetLog& net_log);
+ void StartAsyncReconsider(const GURL& url,
+ int net_error,
+ const BoundNetLog& net_log);
void OnCompletion(int result);
diff --git a/chromium/net/proxy/proxy_service_unittest.cc b/chromium/net/proxy/proxy_service_unittest.cc
index c8551b05e84..cc245b69a3c 100644
--- a/chromium/net/proxy/proxy_service_unittest.cc
+++ b/chromium/net/proxy/proxy_service_unittest.cc
@@ -23,6 +23,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
+
// TODO(eroman): Write a test which exercises
// ProxyService::SuspendAllPendingRequests().
namespace net {
@@ -313,7 +315,8 @@ TEST_F(ProxyServiceTest, PAC_FailoverWithoutDirect) {
// DIRECT.
TestCompletionCallback callback2;
rv = service.ReconsiderProxyAfterError(
- url, &info, callback2.callback(), NULL, BoundNetLog());
+ url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL, BoundNetLog());
// ReconsiderProxyAfterError returns error indicating nothing left.
EXPECT_EQ(ERR_FAILED, rv);
EXPECT_TRUE(info.is_empty());
@@ -408,7 +411,8 @@ TEST_F(ProxyServiceTest, PAC_FailoverAfterDirect) {
// Fallback 1.
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_FALSE(info.is_direct());
@@ -416,14 +420,16 @@ TEST_F(ProxyServiceTest, PAC_FailoverAfterDirect) {
// Fallback 2.
TestCompletionCallback callback3;
- rv = service.ReconsiderProxyAfterError(url, &info, callback3.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback3.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_TRUE(info.is_direct());
// Fallback 3.
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info, callback4.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback4.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_FALSE(info.is_direct());
@@ -431,7 +437,8 @@ TEST_F(ProxyServiceTest, PAC_FailoverAfterDirect) {
// Fallback 4 -- Nothing to fall back to!
TestCompletionCallback callback5;
- rv = service.ReconsiderProxyAfterError(url, &info, callback5.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback5.callback(), NULL,
BoundNetLog());
EXPECT_EQ(ERR_FAILED, rv);
EXPECT_TRUE(info.is_empty());
@@ -726,7 +733,8 @@ TEST_F(ProxyServiceTest, ProxyFallback) {
// Fake an error on the proxy.
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
@@ -768,7 +776,8 @@ TEST_F(ProxyServiceTest, ProxyFallback) {
// We fake another error. It should now try the third one.
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info, callback4.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback4.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
@@ -777,7 +786,8 @@ TEST_F(ProxyServiceTest, ProxyFallback) {
// proxy servers we thought were valid; next we try the proxy server
// that was in our bad proxies map (foopy1:8080).
TestCompletionCallback callback5;
- rv = service.ReconsiderProxyAfterError(url, &info, callback5.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback5.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
@@ -785,7 +795,8 @@ TEST_F(ProxyServiceTest, ProxyFallback) {
// Fake another error, the last proxy is gone, the list should now be empty,
// so there is nothing left to try.
TestCompletionCallback callback6;
- rv = service.ReconsiderProxyAfterError(url, &info, callback6.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback6.callback(), NULL,
BoundNetLog());
EXPECT_EQ(ERR_FAILED, rv);
EXPECT_FALSE(info.is_direct());
@@ -859,7 +870,8 @@ TEST_F(ProxyServiceTest, ProxyFallbackToDirect) {
// Fake an error on the proxy.
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
@@ -868,7 +880,8 @@ TEST_F(ProxyServiceTest, ProxyFallbackToDirect) {
// Fake an error on this proxy as well.
TestCompletionCallback callback3;
- rv = service.ReconsiderProxyAfterError(url, &info, callback3.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback3.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
@@ -881,7 +894,8 @@ TEST_F(ProxyServiceTest, ProxyFallbackToDirect) {
// Now we tell the proxy service that even DIRECT failed.
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info, callback4.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback4.callback(), NULL,
BoundNetLog());
// There was nothing left to try after DIRECT, so we are out of
// choices.
@@ -929,7 +943,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_NewSettings) {
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy-new/proxy.pac")));
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -950,7 +965,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_NewSettings) {
// We fake another error. It should now ignore the first one.
TestCompletionCallback callback3;
- rv = service.ReconsiderProxyAfterError(url, &info, callback3.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback3.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
@@ -962,7 +978,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_NewSettings) {
// We fake another error. It should go back to the first proxy.
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info, callback4.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback4.callback(), NULL,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1021,7 +1038,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_BadConfig) {
// Fake a proxy error.
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
@@ -1053,7 +1071,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_BadConfig) {
// "just work" the next time we call it.
ProxyInfo info3;
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info3, callback4.callback(),
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info3, callback4.callback(),
NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1114,7 +1133,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_BadConfigMandatory) {
// Fake a proxy error.
TestCompletionCallback callback2;
- rv = service.ReconsiderProxyAfterError(url, &info, callback2.callback(), NULL,
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info, callback2.callback(), NULL,
BoundNetLog());
EXPECT_EQ(OK, rv);
@@ -1147,7 +1167,8 @@ TEST_F(ProxyServiceTest, ProxyFallback_BadConfigMandatory) {
// "just work" the next time we call it.
ProxyInfo info3;
TestCompletionCallback callback4;
- rv = service.ReconsiderProxyAfterError(url, &info3, callback4.callback(),
+ rv = service.ReconsiderProxyAfterError(url, net::ERR_PROXY_CONNECTION_FAILED,
+ &info3, callback4.callback(),
NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.cc b/chromium/net/quic/congestion_control/available_channel_estimator.cc
deleted file mode 100644
index ef091abce43..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/available_channel_estimator.h"
-
-static const int kNumberOfSamples = 9;
-
-namespace net {
-
-AvailableChannelEstimator::AvailableChannelEstimator(
- QuicPacketSequenceNumber sequence_number,
- QuicTime first_send_time,
- QuicTime first_receive_time)
- : first_sequence_number_(sequence_number),
- first_send_time_(first_send_time),
- first_receive_time_(first_receive_time),
- last_incorporated_sequence_number_(sequence_number),
- last_time_sent_(QuicTime::Zero()),
- last_receive_time_(QuicTime::Zero()),
- number_of_sequence_numbers_(0),
- received_bytes_(0) {
-}
-
-void AvailableChannelEstimator::OnIncomingFeedback(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime sent_time,
- QuicTime receive_time) {
- if (sequence_number <= first_sequence_number_) {
- // Ignore pre-probe feedback.
- return;
- }
- if (sequence_number <= last_incorporated_sequence_number_) {
- // Ignore old feedback; will remove duplicates.
- return;
- }
- // Remember the highest received sequence number.
- last_incorporated_sequence_number_ = sequence_number;
- if (number_of_sequence_numbers_ < kNumberOfSamples) {
- // We don't care how many sequence numbers we have after we pass
- // kNumberOfSamples.
- number_of_sequence_numbers_++;
- }
- last_receive_time_ = receive_time;
- last_time_sent_ = sent_time;
- received_bytes_ += packet_size;
- // TODO(pwestin): the variance here should give us information about accuracy.
-}
-
-AvailableChannelEstimateState
- AvailableChannelEstimator::GetAvailableChannelEstimate(
- QuicBandwidth* bandwidth) const {
- if (number_of_sequence_numbers_ < 2) {
- return kAvailableChannelEstimateUnknown;
- }
- QuicTime::Delta send_delta = last_time_sent_.Subtract(first_send_time_);
- QuicTime::Delta receive_delta =
- last_receive_time_.Subtract(first_receive_time_);
-
- // TODO(pwestin): room for improvement here. Keeping it simple for now.
- *bandwidth = QuicBandwidth::FromBytesAndTimeDelta(received_bytes_,
- receive_delta);
-
- QuicTime::Delta diff = receive_delta.Subtract(send_delta);
- QuicTime::Delta ten_percent_of_send_time =
- QuicTime::Delta::FromMicroseconds(send_delta.ToMicroseconds() / 10);
-
- if (diff < ten_percent_of_send_time) {
- return kAvailableChannelEstimateSenderLimited;
- }
- if (number_of_sequence_numbers_ < kNumberOfSamples) {
- return kAvailableChannelEstimateUncertain;
- }
- return kAvailableChannelEstimateGood;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.h b/chromium/net/quic/congestion_control/available_channel_estimator.h
deleted file mode 100644
index e2ad19a8b66..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.
-
-// Based on the inter arrival time of the received packets relative to the time
-// those packets where sent we can estimate the available capacity of the
-// channel.
-// We can only use packet trains that are sent out faster than the acctual
-// available channel capacity.
-// Note 1: this is intended to be a temporary class created when you send out a
-// channel probing burst. Once the last packet have arrived you ask it for an
-// estimate.
-// Note 2: During this phase we should not update the overuse detector.
-#ifndef NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum NET_EXPORT_PRIVATE AvailableChannelEstimateState {
- kAvailableChannelEstimateUnknown = 0,
- kAvailableChannelEstimateUncertain = 1,
- kAvailableChannelEstimateGood = 2,
- kAvailableChannelEstimateSenderLimited = 3,
-};
-
-class NET_EXPORT_PRIVATE AvailableChannelEstimator {
- public:
- explicit AvailableChannelEstimator(
- QuicPacketSequenceNumber first_sequence_number,
- QuicTime first_send_time,
- QuicTime first_receive_time);
-
- // Update the statistics with each receive time, for every packet we get a
- // feedback message for.
- void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime sent_time,
- QuicTime receive_time);
-
- // Get the current estimated available channel capacity.
- // bandwidth_estimate is invalid if kAvailableChannelEstimateUnknown
- // is returned.
- AvailableChannelEstimateState GetAvailableChannelEstimate(
- QuicBandwidth* bandwidth_estimate) const;
-
- private:
- const QuicPacketSequenceNumber first_sequence_number_;
- const QuicTime first_send_time_;
- const QuicTime first_receive_time_;
- QuicPacketSequenceNumber last_incorporated_sequence_number_;
- QuicTime last_time_sent_;
- QuicTime last_receive_time_;
- int number_of_sequence_numbers_;
- QuicByteCount received_bytes_;
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc b/chromium/net/quic/congestion_control/available_channel_estimator_test.cc
deleted file mode 100644
index b4a4b9c341c..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/available_channel_estimator.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class AvailableChannelEstimatorTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- srand(1234);
- packet_size_ = 1200;
- sequence_number_ = 1;
- QuicTime receive_time = receive_clock_.Now();
- QuicTime sent_time = send_clock_.Now();
- estimator_.reset(new AvailableChannelEstimator(sequence_number_,
- sent_time,
- receive_time));
- }
-
- MockClock send_clock_;
- MockClock receive_clock_;
- QuicPacketSequenceNumber sequence_number_;
- QuicByteCount packet_size_;
- scoped_ptr<AvailableChannelEstimator> estimator_;
-};
-
-TEST_F(AvailableChannelEstimatorTest, SimpleBasic) {
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
- receive_clock_.AdvanceTime(received_delta);
- send_clock_.AdvanceTime(send_delta);
- QuicTime receive_time = receive_clock_.Now();
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- EXPECT_EQ(kAvailableChannelEstimateUnknown,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
-
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- sent_time = send_clock_.Now();
-
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- EXPECT_EQ(kAvailableChannelEstimateUncertain,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-// TODO(pwestin): simulate cross traffic.
-TEST_F(AvailableChannelEstimatorTest, SimpleUncertainEstimate) {
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
-
- for (int i = 0; i < 8; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- }
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- EXPECT_EQ(kAvailableChannelEstimateUncertain,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-TEST_F(AvailableChannelEstimatorTest, SimpleGoodEstimate) {
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- }
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- EXPECT_EQ(kAvailableChannelEstimateGood,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/channel_estimator.cc b/chromium/net/quic/congestion_control/channel_estimator.cc
deleted file mode 100644
index a012b666a39..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/channel_estimator.h"
-
-// To get information about bandwidth, our send rate for a pair of packets must
-// be much faster (ideally back to back) than the receive rate. In that
-// scenario, the arriving packet pair will tend to arrive at the max bandwidth
-// of the channel. Said another way, when our inter-departure time is a small
-// fraction of the inter-arrival time for the same pair of packets, then we can
-// get an estimate of bandwidth from that interarrival time. The following
-// constant is the threshold ratio for deriving bandwidth information.
-static const int kInterarrivalRatioThresholdForBandwidthEstimation = 5;
-static const size_t kMinNumberOfSamples = 10;
-static const size_t kMaxNumberOfSamples = 100;
-
-namespace net {
-
-ChannelEstimator::ChannelEstimator()
- : last_sequence_number_(0),
- last_send_time_(QuicTime::Zero()),
- last_receive_time_(QuicTime::Zero()),
- sorted_bitrate_estimates_(kMaxNumberOfSamples) {
-}
-
-ChannelEstimator::~ChannelEstimator() {
-}
-
-void ChannelEstimator::OnAcknowledgedPacket(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime send_time,
- QuicTime receive_time) {
- if (last_sequence_number_ > sequence_number) {
- // Old packet. The sequence_number use the full 64 bits even though it's
- // less on the wire.
- return;
- }
- if (last_sequence_number_ != sequence_number - 1) {
- DVLOG(1) << "Skip channel estimator due to lost packet(s)";
- } else if (last_send_time_.IsInitialized()) {
- QuicTime::Delta sent_delta = send_time.Subtract(last_send_time_);
- QuicTime::Delta received_delta = receive_time.Subtract(last_receive_time_);
- if (received_delta.ToMicroseconds() >
- kInterarrivalRatioThresholdForBandwidthEstimation *
- sent_delta.ToMicroseconds()) {
- UpdateFilter(received_delta, packet_size, sequence_number);
- }
- }
- last_sequence_number_ = sequence_number;
- last_send_time_ = send_time;
- last_receive_time_ = receive_time;
-}
-
-ChannelEstimateState ChannelEstimator::GetChannelEstimate(
- QuicBandwidth* estimate) const {
- if (sorted_bitrate_estimates_.Size() < kMinNumberOfSamples) {
- // Not enough data to make an estimate.
- return kChannelEstimateUnknown;
- }
- // Our data is stored in a sorted map, we need to iterate through our map to
- // find the estimated bitrates at our targeted percentiles.
-
- // Calculate 25th percentile.
- size_t beginning_window = sorted_bitrate_estimates_.Size() / 4;
- // Calculate 50th percentile.
- size_t median = sorted_bitrate_estimates_.Size() / 2;
- // Calculate 75th percentile.
- size_t end_window = sorted_bitrate_estimates_.Size() - beginning_window;
-
- QuicBandwidth bitrate_25th_percentile = QuicBandwidth::Zero();
- QuicBandwidth median_bitrate = QuicBandwidth::Zero();
- QuicBandwidth bitrate_75th_percentile = QuicBandwidth::Zero();
- QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>::ConstIterator it =
- sorted_bitrate_estimates_.Begin();
-
- for (size_t i = 0; i <= end_window; ++i, ++it) {
- DCHECK(it != sorted_bitrate_estimates_.End());
- if (i == beginning_window) {
- bitrate_25th_percentile = it->first;
- }
- if (i == median) {
- median_bitrate = it->first;
- }
- if (i == end_window) {
- bitrate_75th_percentile = it->first;
- }
- }
- *estimate = median_bitrate;
- DVLOG(1) << "Channel estimate is:"
- << median_bitrate.ToKBitsPerSecond() << " Kbit/s";
- // If the bitrates in our 25th to 75th percentile window varies more than
- // 25% of the median bitrate we consider the estimate to be uncertain.
- if (bitrate_75th_percentile.Subtract(bitrate_25th_percentile) >
- median_bitrate.Scale(0.25f)) {
- return kChannelEstimateUncertain;
- }
- return kChannelEstimateGood;
-}
-
-void ChannelEstimator::UpdateFilter(QuicTime::Delta received_delta,
- QuicByteCount size_delta,
- QuicPacketSequenceNumber sequence_number) {
- QuicBandwidth estimate =
- QuicBandwidth::FromBytesAndTimeDelta(size_delta, received_delta);
- sorted_bitrate_estimates_.Insert(estimate, sequence_number);
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/channel_estimator.h b/chromium/net/quic/congestion_control/channel_estimator.h
deleted file mode 100644
index 9be8031145b..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-// Based on the inter arrival time of the received packets relative to the time
-// those packets where sent we can estimate the max capacity of the channel.
-// We can only use packet pair that are sent out faster than the acctual
-// channel capacity.
-#ifndef NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
-
-#include <list>
-#include <map>
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/quic_max_sized_map.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum ChannelEstimateState {
- kChannelEstimateUnknown = 0,
- kChannelEstimateUncertain = 1,
- kChannelEstimateGood = 2
-};
-
-class NET_EXPORT_PRIVATE ChannelEstimator {
- public:
- ChannelEstimator();
- ~ChannelEstimator();
-
- // This method should be called each time we acquire a receive time for a
- // packet we previously sent. It calculates deltas between consecutive
- // receive times, and may use that to update the channel bandwidth estimate.
- void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime send_time,
- QuicTime receive_time);
-
- // Get the current estimated state and channel capacity.
- // Note: estimate will not be valid when kChannelEstimateUnknown is returned.
- ChannelEstimateState GetChannelEstimate(QuicBandwidth* estimate) const;
-
- private:
- void UpdateFilter(QuicTime::Delta received_delta, QuicByteCount size_delta,
- QuicPacketSequenceNumber sequence_number);
-
- QuicPacketSequenceNumber last_sequence_number_;
- QuicTime last_send_time_;
- QuicTime last_receive_time_;
- QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>
- sorted_bitrate_estimates_;
-
- DISALLOW_COPY_AND_ASSIGN(ChannelEstimator);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
diff --git a/chromium/net/quic/congestion_control/channel_estimator_test.cc b/chromium/net/quic/congestion_control/channel_estimator_test.cc
deleted file mode 100644
index 460e436589e..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator_test.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/channel_estimator.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class ChannelEstimatorTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- srand(1234);
- packet_size_ = 1200;
- sequence_number_ = 1;
- }
-
- QuicPacketSequenceNumber sequence_number_;
- QuicByteCount packet_size_;
- MockClock send_clock_;
- MockClock receive_clock_;
- ChannelEstimator channel_estimator_;
-};
-
-TEST_F(ChannelEstimatorTest, SimpleNonDetect) {
- // In this test, the send times differ by the same delta as the receive times,
- // so we haven't sent packets closely enough to detect "spreading," or
- // effective bandwidth.
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUnknown,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_TRUE(estimate.IsZero());
-}
-
-TEST_F(ChannelEstimatorTest, SimplePacketPairDetect) {
- // In this test, we start by sending packet pairs back-to-back and
- // add a receive side spreading that indicate an effective bandwidth.
- // We do 2 testes with different effective bandwidth to make sure that we
- // detect the new effective bandwidth.
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
-
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- estimate);
- received_delta = QuicTime::Delta::FromMilliseconds(1);
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- }
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleFlatSlope) {
- // In this test, we send packet pairs back-to-back and add a slowly increasing
- // receive side spreading. We expect the estimate to be good and that our
- // mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(10));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleMediumSlope) {
- // In this test, we send packet pairs back-to-back and add an increasing
- // receive side spreading. We expect the estimate to be uncertaint and that
- // our mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(50));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUncertain,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleSteepSlope) {
- // In this test, we send packet pairs back-to-back and add a rapidly
- // increasing receive side spreading. We expect the estimate to be uncertain
- // and that our mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(100));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUncertain,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/cube_root.h b/chromium/net/quic/congestion_control/cube_root.h
index 3b3736c1c59..293a7199f86 100644
--- a/chromium/net/quic/congestion_control/cube_root.h
+++ b/chromium/net/quic/congestion_control/cube_root.h
@@ -15,6 +15,9 @@ class NET_EXPORT_PRIVATE CubeRoot {
// Calculates the cube root using a table lookup followed by one Newton-
// Raphson iteration.
static uint32 Root(uint64 a);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CubeRoot);
};
} // namespace net
diff --git a/chromium/net/quic/congestion_control/cubic.cc b/chromium/net/quic/congestion_control/cubic.cc
index 3d03b9f60d1..f64e58b05b9 100644
--- a/chromium/net/quic/congestion_control/cubic.cc
+++ b/chromium/net/quic/congestion_control/cubic.cc
@@ -9,9 +9,15 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/time/time.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "net/quic/quic_protocol.h"
+
+using std::max;
namespace net {
+namespace {
+
// Constants based on TCP defaults.
// The following constants are in 2^10 fractions of a second instead of ms to
// allow a 10 shift right to divide.
@@ -21,93 +27,37 @@ const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
const int kCubeCongestionWindowScale = 410;
const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) /
kCubeCongestionWindowScale;
-const uint32 kBetaSPDY = 939; // Back off factor after loss for SPDY, reduces
- // the CWND by 1/12th.
-const uint32 kBetaLastMax = 871; // Additional back off factor after loss for
- // the stored max value.
-namespace {
-// Find last bit in a 64-bit word.
-int FindMostSignificantBit(uint64 x) {
- if (!x) {
- return 0;
- }
- int r = 0;
- if (x & 0xffffffff00000000ull) {
- x >>= 32;
- r += 32;
- }
- if (x & 0xffff0000u) {
- x >>= 16;
- r += 16;
- }
- if (x & 0xff00u) {
- x >>= 8;
- r += 8;
- }
- if (x & 0xf0u) {
- x >>= 4;
- r += 4;
- }
- if (x & 0xcu) {
- x >>= 2;
- r += 2;
- }
- if (x & 0x02u) {
- x >>= 1;
- r++;
- }
- if (x & 0x01u) {
- r++;
- }
- return r;
-}
+const uint32 kNumConnections = 2;
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+// Additional backoff factor when loss occurs in the concave part of the Cubic
+// curve. This additional backoff factor is expected to give up bandwidth to
+// new concurrent flows and speed up convergence.
+const float kBetaLastMax = 0.85f;
+
+// kNConnectionBeta is the backoff factor after loss for our N-connection
+// emulation, which emulates the effective backoff of an ensemble of N TCP-Reno
+// connections on a single loss event. The effective multiplier is computed as:
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+
+// TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
+// kBeta here is a cwnd multiplier, and is equal to 1-beta from the CUBIC paper.
+// We derive the equivalent kNConnectionAlpha for an N-connection emulation as:
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
+// TODO(jri): Compute kNConnectionBeta and kNConnectionAlpha from
+// number of active streams.
-// 6 bits table [0..63]
-const uint32 cube_root_table[] = {
- 0, 54, 54, 54, 118, 118, 118, 118, 123, 129, 134, 138, 143, 147, 151,
- 156, 157, 161, 164, 168, 170, 173, 176, 179, 181, 185, 187, 190, 192, 194,
- 197, 199, 200, 202, 204, 206, 209, 211, 213, 215, 217, 219, 221, 222, 224,
- 225, 227, 229, 231, 232, 234, 236, 237, 239, 240, 242, 244, 245, 246, 248,
- 250, 251, 252, 254
-};
} // namespace
-Cubic::Cubic(const QuicClock* clock)
+Cubic::Cubic(const QuicClock* clock, QuicConnectionStats* stats)
: clock_(clock),
epoch_(QuicTime::Zero()),
- last_update_time_(QuicTime::Zero()) {
+ last_update_time_(QuicTime::Zero()),
+ stats_(stats) {
Reset();
}
-// Calculate the cube root using a table lookup followed by one Newton-Raphson
-// iteration.
-uint32 Cubic::CubeRoot(uint64 a) {
- uint32 msb = FindMostSignificantBit(a);
- DCHECK_LE(msb, 64u);
-
- if (msb < 7) {
- // MSB in our table.
- return ((cube_root_table[static_cast<uint32>(a)]) + 31) >> 6;
- }
- // MSB 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ...
- // cubic_shift 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ...
- uint32 cubic_shift = (msb - 4);
- cubic_shift = ((cubic_shift * 342) >> 10); // Div by 3, biased high.
-
- // 4 to 6 bits accuracy depending on MSB.
- uint32 down_shifted_to_6bit = (a >> (cubic_shift * 3));
- uint64 root = ((cube_root_table[down_shifted_to_6bit] + 10) << cubic_shift)
- >> 6;
-
- // Make one Newton-Raphson iteration.
- // Since x has an error (inaccuracy due to the use of fix point) we get a
- // more accurate result by doing x * (x - 1) instead of x * x.
- root = 2 * root + (a / (root * (root - 1)));
- root = ((root * 341) >> 10); // Div by 3, biased low.
- return static_cast<uint32>(root);
-}
-
void Cubic::Reset() {
epoch_ = QuicTime::Zero(); // Reset time.
last_update_time_ = QuicTime::Zero(); // Reset time.
@@ -120,18 +70,36 @@ void Cubic::Reset() {
last_target_congestion_window_ = 0;
}
+void Cubic::UpdateCongestionControlStats(
+ QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd) {
+
+ QuicTcpCongestionWindow highest_new_cwnd = std::max(new_cubic_mode_cwnd,
+ new_reno_mode_cwnd);
+ if (last_congestion_window_ < highest_new_cwnd) {
+ // cwnd will increase to highest_new_cwnd.
+ stats_->cwnd_increase_congestion_avoidance +=
+ highest_new_cwnd - last_congestion_window_;
+ if (new_cubic_mode_cwnd > new_reno_mode_cwnd) {
+ // This cwnd increase is due to cubic mode.
+ stats_->cwnd_increase_cubic_mode +=
+ new_cubic_mode_cwnd - last_congestion_window_;
+ }
+ }
+}
+
QuicTcpCongestionWindow Cubic::CongestionWindowAfterPacketLoss(
QuicTcpCongestionWindow current_congestion_window) {
if (current_congestion_window < last_max_congestion_window_) {
// We never reached the old max, so assume we are competing with another
// flow. Use our extra back off factor to allow the other flow to go up.
last_max_congestion_window_ =
- (kBetaLastMax * current_congestion_window) >> 10;
+ static_cast<int>(kBetaLastMax * current_congestion_window);
} else {
last_max_congestion_window_ = current_congestion_window;
}
epoch_ = QuicTime::Zero(); // Reset time.
- return (current_congestion_window * kBetaSPDY) >> 10;
+ return static_cast<int>(current_congestion_window * kNConnectionBeta);
}
QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
@@ -143,8 +111,8 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
// Cubic is "independent" of RTT, the update is limited by the time elapsed.
if (last_congestion_window_ == current_congestion_window &&
(current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval())) {
- return std::max(last_target_congestion_window_,
- estimated_tcp_congestion_window_);
+ return max(last_target_congestion_window_,
+ estimated_tcp_congestion_window_);
}
last_congestion_window_ = current_congestion_window;
last_update_time_ = current_time;
@@ -160,7 +128,7 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
time_to_origin_point_ = 0;
origin_point_congestion_window_ = current_congestion_window;
} else {
- time_to_origin_point_ = CubeRoot(kCubeFactor *
+ time_to_origin_point_ = CubeRoot::Root(kCubeFactor *
(last_max_congestion_window_ - current_congestion_window));
origin_point_congestion_window_ =
last_max_congestion_window_;
@@ -180,22 +148,35 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
QuicTcpCongestionWindow target_congestion_window =
origin_point_congestion_window_ - delta_congestion_window;
+ DCHECK_LT(0u, estimated_tcp_congestion_window_);
+ // With dynamic beta/alpha based on number of active streams, it is possible
+ // for the required_ack_count to become much lower than acked_packets_count_
+ // suddenly, leading to more than one iteration through the following loop.
+ while (true) {
+ // Update estimated TCP congestion_window.
+ uint32 required_ack_count =
+ estimated_tcp_congestion_window_ / kNConnectionAlpha;
+ if (acked_packets_count_ < required_ack_count) {
+ break;
+ }
+ acked_packets_count_ -= required_ack_count;
+ estimated_tcp_congestion_window_++;
+ }
+
+ // Update cubic mode and reno mode stats in QuicConnectionStats.
+ UpdateCongestionControlStats(target_congestion_window,
+ estimated_tcp_congestion_window_);
+
// We have a new cubic congestion window.
last_target_congestion_window_ = target_congestion_window;
- // Update estimated TCP congestion_window.
- // Note: we do a normal Reno congestion avoidance calculation not the
- // calculation described in section 3.3 TCP-friendly region of the document.
- while (acked_packets_count_ >= estimated_tcp_congestion_window_) {
- acked_packets_count_ -= estimated_tcp_congestion_window_;
- estimated_tcp_congestion_window_++;
- }
// Compute target congestion_window based on cubic target and estimated TCP
// congestion_window, use highest (fastest).
if (target_congestion_window < estimated_tcp_congestion_window_) {
target_congestion_window = estimated_tcp_congestion_window_;
}
- DVLOG(1) << "Target congestion_window:" << target_congestion_window;
+
+ DVLOG(1) << "Target congestion_window: " << target_congestion_window;
return target_congestion_window;
}
diff --git a/chromium/net/quic/congestion_control/cubic.h b/chromium/net/quic/congestion_control/cubic.h
index e365bbe0ed7..da41ddf8ab2 100644
--- a/chromium/net/quic/congestion_control/cubic.h
+++ b/chromium/net/quic/congestion_control/cubic.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_time.h"
namespace net {
@@ -20,7 +21,7 @@ typedef uint32 QuicTcpCongestionWindow;
class NET_EXPORT_PRIVATE Cubic {
public:
- explicit Cubic(const QuicClock* clock);
+ Cubic(const QuicClock* clock, QuicConnectionStats* stats);
// Call after a timeout to reset the cubic state.
void Reset();
@@ -39,16 +40,14 @@ class NET_EXPORT_PRIVATE Cubic {
QuicTcpCongestionWindow current,
QuicTime::Delta delay_min);
- protected:
- // Calculates the cubic root using a table lookup followed by one Newton-
- // Raphson iteration.
- uint32 CubeRoot(uint64 a);
-
private:
static const QuicTime::Delta MaxCubicTimeInterval() {
return QuicTime::Delta::FromMilliseconds(30);
}
+ // Update congestion control variables in QuicConnectionStats.
+ void UpdateCongestionControlStats(QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd);
const QuicClock* clock_;
// Time when this cycle started, after last loss event.
@@ -80,8 +79,12 @@ class NET_EXPORT_PRIVATE Cubic {
// Last congestion window in packets computed by cubic function.
QuicTcpCongestionWindow last_target_congestion_window_;
+ // QuicConnectionStats includes congestion control related stats.
+ QuicConnectionStats* stats_;
+
DISALLOW_COPY_AND_ASSIGN(Cubic);
};
} // namespace net
+
#endif // NET_QUIC_CONGESTION_CONTROL_CUBIC_H_
diff --git a/chromium/net/quic/congestion_control/cubic_test.cc b/chromium/net/quic/congestion_control/cubic_test.cc
index c01bb050643..915274a13d3 100644
--- a/chromium/net/quic/congestion_control/cubic_test.cc
+++ b/chromium/net/quic/congestion_control/cubic_test.cc
@@ -4,66 +4,35 @@
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/cubic.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/test_tools/mock_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
-class CubicPeer : public Cubic {
- public:
- explicit CubicPeer(QuicClock* clock)
- : Cubic(clock) {
- }
- using Cubic::CubeRoot;
-};
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+const uint32 kNumConnections = 2;
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
class CubicTest : public ::testing::Test {
protected:
CubicTest()
: one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- hundred_ms_(QuicTime::Delta::FromMilliseconds(100)) {
- }
- virtual void SetUp() {
- cubic_.reset(new CubicPeer(&clock_));
+ hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
+ cubic_(&clock_, &stats_) {
}
const QuicTime::Delta one_ms_;
const QuicTime::Delta hundred_ms_;
MockClock clock_;
- scoped_ptr<CubicPeer> cubic_;
+ QuicConnectionStats stats_;
+ Cubic cubic_;
};
-TEST_F(CubicTest, CubeRootLow) {
- for (uint32 i = 1; i < 256; ++i) {
- uint64 cube = i * i * i;
- uint8 cube_root = cubic_->CubeRoot(cube);
- EXPECT_EQ(i, cube_root);
- }
-}
-
-TEST_F(CubicTest, CubeRootHigh) {
- // Test the range we will opperate in, 1300 to 130 000.
- // We expect some loss in accuracy, accepting +-0.2%.
- for (uint64 i = 1300; i < 20000; i += 100) {
- uint64 cube = i * i * i;
- uint32 cube_root = cubic_->CubeRoot(cube);
- uint32 margin = cube_root >> 9; // Calculate 0.2% roughly by
- // dividing by 512.
- EXPECT_LE(i - margin, cube_root);
- EXPECT_GE(i + margin, cube_root);
- }
- for (uint64 i = 20000; i < 130000; i *= 2) {
- uint64 cube = i * i * i;
- uint32 cube_root = cubic_->CubeRoot(cube);
- uint32 margin = cube_root >> 9;
- EXPECT_LE(i - margin, cube_root);
- EXPECT_GE(i + margin, cube_root);
- }
-}
-
-TEST_F(CubicTest, AboveOrgin) {
+TEST_F(CubicTest, AboveOrigin) {
// Convex growth.
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 10;
@@ -71,36 +40,84 @@ TEST_F(CubicTest, AboveOrgin) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
current_cwnd = expected_cwnd;
// Normal TCP phase.
for (int i = 0; i < 48; ++i) {
- for (uint32 n = 1; n < current_cwnd; ++n) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
// Call once per ACK.
- EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ EXPECT_NEAR(current_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min), 1);
}
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
- EXPECT_EQ(expected_cwnd, current_cwnd);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd, current_cwnd, 1);
expected_cwnd++;
}
// Cubic phase.
- for (int j = 48; j < 100; ++j) {
+ for (int i = 0; i < 52; ++i) {
for (uint32 n = 1; n < current_cwnd; ++n) {
// Call once per ACK.
EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
}
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
}
- float elapsed_time_s = 10.0f + 0.1f; // We need to add the RTT here.
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
/ 1024;
EXPECT_EQ(expected_cwnd, current_cwnd);
}
+TEST_F(CubicTest, CwndIncreaseStatsDuringConvexRegion) {
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 10;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize controller state.
+ clock_.AdvanceTime(one_ms_);
+ expected_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = expected_cwnd;
+ // Testing Reno mode increase.
+ for (int i = 0; i < 48; ++i) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
+ // Call once per ACK, causing cwnd growth in Reno mode.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Advance current time so that cwnd update is allowed to happen by Cubic.
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd - 10, stats_.cwnd_increase_congestion_avoidance,
+ 1);
+ EXPECT_NEAR(1u, stats_.cwnd_increase_cubic_mode, 1);
+ expected_cwnd++;
+ }
+ uint32 old_cwnd = current_cwnd;
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+
+ // Testing Cubic mode increase.
+ for (int i = 0; i < 52; ++i) {
+ for (uint32 n = 1; n < current_cwnd; ++n) {
+ // Call once per ACK.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
+ expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
+ / 1024;
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
+}
+
+
TEST_F(CubicTest, LossEvents) {
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 422;
@@ -108,16 +125,16 @@ TEST_F(CubicTest, LossEvents) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
}
-TEST_F(CubicTest, BelowOrgin) {
+TEST_F(CubicTest, BelowOrigin) {
// Concave growth.
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 422;
@@ -125,26 +142,27 @@ TEST_F(CubicTest, BelowOrgin) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
current_cwnd = expected_cwnd;
- // First update after epoch.
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ // First update after loss to initialize the epoch.
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ uint32 old_cwnd = current_cwnd;
// Cubic phase.
- for (int i = 0; i < 54; ++i) {
- for (uint32 n = 1; n < current_cwnd; ++n) {
- // Call once per ACK.
- EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- }
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+ for (int i = 0; i < 40 ; ++i) {
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
}
- expected_cwnd = 440;
+ expected_cwnd = 422;
EXPECT_EQ(expected_cwnd, current_cwnd);
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
}
-} // namespace testing
+} // namespace test
} // namespace net
diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.cc b/chromium/net/quic/congestion_control/fix_rate_receiver.cc
index 950b49c0d6b..a13362ff226 100644
--- a/chromium/net/quic/congestion_control/fix_rate_receiver.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_receiver.cc
@@ -27,8 +27,7 @@ bool FixRateReceiver::GenerateCongestionFeedback(
void FixRateReceiver::RecordIncomingPacket(
QuicByteCount /*bytes*/,
QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime /*timestamp*/,
- bool /*recovered*/) {
+ QuicTime /*timestamp*/) {
// Nothing to do for this simple implementation.
}
diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.h b/chromium/net/quic/congestion_control/fix_rate_receiver.h
index 690f13880f1..4ed24b993d4 100644
--- a/chromium/net/quic/congestion_control/fix_rate_receiver.h
+++ b/chromium/net/quic/congestion_control/fix_rate_receiver.h
@@ -30,8 +30,7 @@ class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface {
// Implements ReceiveAlgorithmInterface.
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool recovered) OVERRIDE;
+ QuicTime timestamp) OVERRIDE;
private:
friend class test::FixRateReceiverPeer;
diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.cc b/chromium/net/quic/congestion_control/fix_rate_sender.cc
index 8280f1eeb31..c346a63d6ff 100644
--- a/chromium/net/quic/congestion_control/fix_rate_sender.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_sender.cc
@@ -9,8 +9,11 @@
#include <algorithm>
#include "base/logging.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/quic_protocol.h"
+using std::max;
+
namespace {
const int kInitialBitrate = 100000; // In bytes per second.
const uint64 kWindowSizeUs = 10000; // 10 ms.
@@ -18,12 +21,11 @@ namespace {
namespace net {
-FixRateSender::FixRateSender(const QuicClock* clock)
- : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)),
+FixRateSender::FixRateSender(const RttStats* rtt_stats)
+ : rtt_stats_(rtt_stats),
+ bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)),
max_segment_size_(kDefaultMaxPacketSize),
fix_rate_leaky_bucket_(bitrate_),
- paced_sender_(bitrate_, max_segment_size_),
- data_in_flight_(0),
latest_rtt_(QuicTime::Delta::Zero()) {
DVLOG(1) << "FixRateSender";
}
@@ -34,101 +36,55 @@ FixRateSender::~FixRateSender() {
void FixRateSender::SetFromConfig(const QuicConfig& config, bool is_server) {
}
-void FixRateSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- paced_sender_.set_max_segment_size(max_segment_size_);
-}
-
void FixRateSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& /*sent_packets*/) {
- if (feedback.type != kFixRate) {
- LOG(DFATAL) << "Invalid incoming CongestionFeedbackType:" << feedback.type;
- }
+ QuicTime feedback_receive_time) {
+ LOG_IF(DFATAL, feedback.type != kFixRate) <<
+ "Invalid incoming CongestionFeedbackType:" << feedback.type;
if (feedback.type == kFixRate) {
bitrate_ = feedback.fix_rate.bitrate;
fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_);
- paced_sender_.UpdateBandwidthEstimate(feedback_receive_time, bitrate_);
}
// Silently ignore invalid messages in release mode.
}
-void FixRateSender::OnPacketAcked(
- QuicPacketSequenceNumber /*acked_sequence_number*/,
- QuicByteCount bytes_acked,
- QuicTime::Delta rtt) {
- // RTT can't be negative.
- DCHECK_LE(0, rtt.ToMicroseconds());
-
- data_in_flight_ -= bytes_acked;
- if (rtt.IsInfinite()) {
- return;
- }
- latest_rtt_ = rtt;
-}
-
-void FixRateSender::OnPacketLost(QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime /*ack_receive_time*/) {
- // Ignore losses for fix rate sender.
+void FixRateSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
}
bool FixRateSender::OnPacketSent(
QuicTime sent_time,
+ QuicByteCount /*bytes_in_flight*/,
QuicPacketSequenceNumber /*sequence_number*/,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData /*has_retransmittable_data*/) {
fix_rate_leaky_bucket_.Add(sent_time, bytes);
- paced_sender_.OnPacketSent(sent_time, bytes);
- if (transmission_type == NOT_RETRANSMISSION) {
- data_in_flight_ += bytes;
- }
+
return true;
}
-void FixRateSender::OnRetransmissionTimeout() { }
-
-void FixRateSender::OnPacketAbandoned(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicByteCount /*abandoned_bytes*/) {
-}
+void FixRateSender::OnRetransmissionTimeout(bool packets_retransmitted) { }
QuicTime::Delta FixRateSender::TimeUntilSend(
QuicTime now,
- TransmissionType /* transmission_type */,
- HasRetransmittableData /*has_retransmittable_data*/,
- IsHandshake /*handshake*/) {
- if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) {
- if (CongestionWindow() <= data_in_flight_) {
- // We need an ack before we send more.
- return QuicTime::Delta::Infinite();
- }
- return paced_sender_.TimeUntilSend(now, QuicTime::Delta::Zero());
- }
- QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(now);
- if (time_remaining.IsZero()) {
- // We need an ack before we send more.
- return QuicTime::Delta::Infinite();
- }
- return paced_sender_.TimeUntilSend(now, time_remaining);
+ QuicByteCount /*bytes_in_flight*/,
+ HasRetransmittableData /*has_retransmittable_data*/) const {
+ return fix_rate_leaky_bucket_.TimeRemaining(now);
}
-QuicByteCount FixRateSender::CongestionWindow() {
+QuicByteCount FixRateSender::CongestionWindow() const {
QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod(
QuicTime::Delta::FromMicroseconds(kWindowSizeUs));
// Make sure window size is not less than a packet.
- return std::max(kDefaultMaxPacketSize, window_size_bytes);
+ return max(kDefaultMaxPacketSize, window_size_bytes);
}
QuicBandwidth FixRateSender::BandwidthEstimate() const {
return bitrate_;
}
-QuicTime::Delta FixRateSender::SmoothedRtt() const {
- // TODO(satyamshekhar): Calculate and return smoothed rtt.
- return latest_rtt_;
-}
QuicTime::Delta FixRateSender::RetransmissionDelay() const {
// TODO(pwestin): Calculate and return retransmission delay.
diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.h b/chromium/net/quic/congestion_control/fix_rate_sender.h
index d3d48c7e3da..3cb07ff4ddf 100644
--- a/chromium/net/quic/congestion_control/fix_rate_sender.h
+++ b/chromium/net/quic/congestion_control/fix_rate_sender.h
@@ -11,58 +11,52 @@
#include "base/compiler_specific.h"
#include "net/base/net_export.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_time.h"
#include "net/quic/congestion_control/leaky_bucket.h"
-#include "net/quic/congestion_control/paced_sender.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
namespace net {
+class RttStats;
+
class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface {
public:
- explicit FixRateSender(const QuicClock* clock);
+ explicit FixRateSender(const RttStats* rtt_stats);
virtual ~FixRateSender();
// Start implementation of SendAlgorithmInterface.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(
QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
// End implementation of SendAlgorithmInterface.
private:
- QuicByteCount CongestionWindow();
+ QuicByteCount CongestionWindow() const;
+ const RttStats* rtt_stats_;
QuicBandwidth bitrate_;
QuicByteCount max_segment_size_;
LeakyBucket fix_rate_leaky_bucket_;
- PacedSender paced_sender_;
- QuicByteCount data_in_flight_;
QuicTime::Delta latest_rtt_;
DISALLOW_COPY_AND_ASSIGN(FixRateSender);
diff --git a/chromium/net/quic/congestion_control/fix_rate_test.cc b/chromium/net/quic/congestion_control/fix_rate_test.cc
index 752d37fe641..c82bb60d823 100644
--- a/chromium/net/quic/congestion_control/fix_rate_test.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_test.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/fix_rate_receiver.h"
#include "net/quic/congestion_control/fix_rate_sender.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/test_tools/mock_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -16,6 +17,9 @@
namespace net {
namespace test {
+// bytes_in_flight is unused by FixRateSender's OnPacketSent.
+QuicByteCount kUnused = 0;
+
class FixRateReceiverPeer : public FixRateReceiver {
public:
FixRateReceiverPeer()
@@ -29,16 +33,14 @@ class FixRateReceiverPeer : public FixRateReceiver {
class FixRateTest : public ::testing::Test {
protected:
FixRateTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(30)),
- sender_(new FixRateSender(&clock_)),
+ : sender_(new FixRateSender(&rtt_stats_)),
receiver_(new FixRateReceiverPeer()),
start_(clock_.Now()) {
// Make sure clock does not start at 0.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
}
- const QuicTime::Delta rtt_;
+ RttStats rtt_stats_;
MockClock clock_;
- SendAlgorithmInterface::SentPacketsMap unused_packet_map_;
scoped_ptr<FixRateSender> sender_;
scoped_ptr<FixRateReceiverPeer> receiver_;
const QuicTime start_;
@@ -48,7 +50,7 @@ TEST_F(FixRateTest, ReceiverAPI) {
QuicCongestionFeedbackFrame feedback;
QuicTime timestamp(QuicTime::Zero());
receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(300));
- receiver_->RecordIncomingPacket(1, 1, timestamp, false);
+ receiver_->RecordIncomingPacket(1, 1, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kFixRate, feedback.type);
EXPECT_EQ(300000u, feedback.fix_rate.bitrate.ToBytesPerSecond());
@@ -58,65 +60,41 @@ TEST_F(FixRateTest, SenderAPI) {
QuicCongestionFeedbackFrame feedback;
feedback.type = kFixRate;
feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300);
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- unused_packet_map_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond());
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), 1, kDefaultMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ sender_->OnPacketSent(clock_.Now(), kUnused, 1, kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ clock_.AdvanceTime(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA));
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), 2, kDefaultMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- sender_->OnPacketSent(clock_.Now(), 3, 600, NOT_RETRANSMISSION,
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_->OnPacketSent(clock_.Now(), kUnused, 2, kDefaultMaxPacketSize,
HAS_RETRANSMITTABLE_DATA);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
- EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
- sender_->OnPacketAcked(1, kDefaultMaxPacketSize, rtt_);
- sender_->OnPacketAcked(2, kDefaultMaxPacketSize, rtt_);
- sender_->OnPacketAcked(3, 600, rtt_);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Advance the time twice as much and expect only one packet to be sent.
+ clock_.AdvanceTime(sender_->TimeUntilSend(
+ clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).Multiply(2));
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-}
-
-TEST_F(FixRateTest, FixRatePacing) {
- const QuicByteCount packet_size = 1200;
- const QuicBandwidth bitrate = QuicBandwidth::FromKBytesPerSecond(240);
- const int64 num_packets = 200;
- QuicCongestionFeedbackFrame feedback;
- receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240));
- ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- unused_packet_map_);
- QuicTime acc_advance_time(QuicTime::Zero());
- QuicPacketSequenceNumber sequence_number = 0;
- for (int i = 0; i < num_packets; i += 2) {
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- clock_.AdvanceTime(advance_time);
- sender_->OnPacketAcked(sequence_number - 1, packet_size, rtt_);
- sender_->OnPacketAcked(sequence_number - 2, packet_size, rtt_);
- acc_advance_time = acc_advance_time.Add(advance_time);
- }
- EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(),
- static_cast<uint64>(acc_advance_time.Subtract(start_)
- .ToMicroseconds()));
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_->OnPacketSent(clock_.Now(), kUnused, 3, kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.cc b/chromium/net/quic/congestion_control/hybrid_slow_start.cc
index 0edb10e86ea..2ae3b503265 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start.cc
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start.cc
@@ -6,104 +6,133 @@
#include <algorithm>
+using std::max;
+using std::min;
+
namespace net {
// Note(pwestin): the magic clamping numbers come from the original code in
// tcp_cubic.c.
+const int64 kHybridStartLowWindow = 16;
// Number of delay samples for detecting the increase of delay.
-const int kHybridStartMinSamples = 8;
+const uint32 kHybridStartMinSamples = 8;
const int kHybridStartDelayFactorExp = 4; // 2^4 = 16
-const int kHybridStartDelayMinThresholdUs = 2000;
+// The original paper specifies 2 and 8ms, but those have changed over time.
+const int kHybridStartDelayMinThresholdUs = 4000;
const int kHybridStartDelayMaxThresholdUs = 16000;
HybridSlowStart::HybridSlowStart(const QuicClock* clock)
: clock_(clock),
started_(false),
- found_ack_train_(false),
- found_delay_(false),
+ hystart_found_(NOT_FOUND),
+ last_sent_sequence_number_(0),
round_start_(QuicTime::Zero()),
end_sequence_number_(0),
- last_time_(QuicTime::Zero()),
- sample_count_(0),
- current_rtt_(QuicTime::Delta::Zero()) {
+ last_close_ack_pair_time_(QuicTime::Zero()),
+ rtt_sample_count_(0),
+ current_min_rtt_(QuicTime::Delta::Zero()) {
+}
+
+void HybridSlowStart::OnPacketAcked(
+ QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) {
+ // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
+ // the round when the final packet of the burst is received and start it on
+ // the next incoming ack.
+ if (in_slow_start && IsEndOfRound(acked_sequence_number)) {
+ started_ = false;
+ }
+}
+
+void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number) {
+ last_sent_sequence_number_ = sequence_number;
}
void HybridSlowStart::Restart() {
- found_ack_train_ = false;
- found_delay_ = false;
+ started_ = false;
+ hystart_found_ = NOT_FOUND;
}
-void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) {
- DVLOG(1) << "Reset hybrid slow start @" << end_sequence_number;
- round_start_ = last_time_ = clock_->ApproximateNow();
- end_sequence_number_ = end_sequence_number;
- current_rtt_ = QuicTime::Delta::Zero();
- sample_count_ = 0;
+void HybridSlowStart::StartReceiveRound(QuicPacketSequenceNumber last_sent) {
+ DVLOG(1) << "Reset hybrid slow start @" << last_sent;
+ round_start_ = last_close_ack_pair_time_ = clock_->ApproximateNow();
+ end_sequence_number_ = last_sent;
+ current_min_rtt_ = QuicTime::Delta::Zero();
+ rtt_sample_count_ = 0;
started_ = true;
}
-bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) {
+bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const {
return end_sequence_number_ <= ack;
}
-void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) {
- // The original code doesn't invoke this until we hit 16 packet per burst.
- // Since the code handles lower than 16 grecefully and I removed that
- // limit.
- if (found_ack_train_ || found_delay_) {
- return;
+bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window) {
+ if (!started_) {
+ // Time to start the hybrid slow start.
+ StartReceiveRound(last_sent_sequence_number_);
+ }
+ if (hystart_found_ != NOT_FOUND) {
+ return true;
}
QuicTime current_time = clock_->ApproximateNow();
// First detection parameter - ack-train detection.
// Since slow start burst out packets we can indirectly estimate the inter-
// arrival time by looking at the arrival time of the ACKs if the ACKs are
- // spread out more then half the minimum RTT packets are beeing spread out
+ // spread out more then half the minimum RTT packets are being spread out
// more than the capacity.
- // This first trigger will not come into play until we hit roughly 4.8 Mbit/s.
+ // This first trigger will not come into play until we hit roughly 9.6 Mbps
+ // with delayed acks (or 4.8Mbps without delayed acks)
+ // TODO(ianswett): QUIC always uses delayed acks, even at the beginning, so
+ // this should likely be at least 4ms.
// TODO(pwestin): we need to make sure our pacing don't trigger this detector.
- if (current_time.Subtract(last_time_).ToMicroseconds() <=
- kHybridStartDelayMinThresholdUs) {
- last_time_ = current_time;
+ // TODO(ianswett): Pacing or other cases could be handled by checking the send
+ // time of the first acked packet in a receive round.
+ if (current_time.Subtract(last_close_ack_pair_time_).ToMicroseconds() <=
+ kHybridStartDelayMinThresholdUs) {
+ last_close_ack_pair_time_ = current_time;
if (current_time.Subtract(round_start_).ToMicroseconds() >=
- (delay_min.ToMicroseconds() >> 1)) {
- found_ack_train_ = true;
+ min_rtt.ToMicroseconds() >> 1) {
+ hystart_found_ = ACK_TRAIN;
}
+ } else if (last_close_ack_pair_time_ == round_start_) {
+ // If the previous ack wasn't close, then move forward the round start time
+ // to the incoming ack.
+ last_close_ack_pair_time_ = round_start_ = current_time;
}
// Second detection parameter - delay increase detection.
- // Compare the minimum delay (current_rtt_) of the current
+ // Compare the minimum delay (current_min_rtt_) of the current
// burst of packets relative to the minimum delay during the session.
// Note: we only look at the first few(8) packets in each burst, since we
// only want to compare the lowest RTT of the burst relative to previous
// bursts.
- sample_count_++;
- if (sample_count_ <= kHybridStartMinSamples) {
- if (current_rtt_.IsZero() || current_rtt_ > rtt) {
- current_rtt_ = rtt;
+ rtt_sample_count_++;
+ if (rtt_sample_count_ <= kHybridStartMinSamples) {
+ if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) {
+ current_min_rtt_ = latest_rtt;
}
}
- // We only need to check this once.
- if (sample_count_ == kHybridStartMinSamples) {
- int accepted_variance_us = delay_min.ToMicroseconds() >>
+ // We only need to check this once per round.
+ if (rtt_sample_count_ == kHybridStartMinSamples) {
+ // Divide min_rtt by 16 to get a rtt increase threshold for exiting.
+ int min_rtt_increase_threshold_us = min_rtt.ToMicroseconds() >>
kHybridStartDelayFactorExp;
- accepted_variance_us = std::min(accepted_variance_us,
- kHybridStartDelayMaxThresholdUs);
- QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds(
- std::max(accepted_variance_us, kHybridStartDelayMinThresholdUs));
+ // Ensure the rtt threshold is never less than 2ms or more than 16ms.
+ min_rtt_increase_threshold_us = min(min_rtt_increase_threshold_us,
+ kHybridStartDelayMaxThresholdUs);
+ QuicTime::Delta min_rtt_increase_threshold =
+ QuicTime::Delta::FromMicroseconds(max(min_rtt_increase_threshold_us,
+ kHybridStartDelayMinThresholdUs));
- if (current_rtt_ > delay_min.Add(accepted_variance)) {
- found_delay_ = true;
+ if (current_min_rtt_ > min_rtt.Add(min_rtt_increase_threshold)) {
+ hystart_found_= DELAY;
}
}
-}
-
-bool HybridSlowStart::Exit() {
- // If either one of the two conditions are met we exit from slow start
- // immediately.
- if (found_ack_train_ || found_delay_) {
- return true;
- }
- return false;
+ // Exit from slow start if the cwnd is greater than 16 and an ack train or
+ // increasing delay are found.
+ return congestion_window >= kHybridStartLowWindow &&
+ hystart_found_ != NOT_FOUND;
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.h b/chromium/net/quic/congestion_control/hybrid_slow_start.h
index cee9c731257..5d36c53a334 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start.h
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start.h
@@ -26,36 +26,61 @@ class NET_EXPORT_PRIVATE HybridSlowStart {
public:
explicit HybridSlowStart(const QuicClock* clock);
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ bool in_slow_start);
+
+ void OnPacketSent(QuicPacketSequenceNumber sequence_number);
+
+ // ShouldExitSlowStart should be called on every new ack frame, since a new
+ // RTT measurement can be made then.
+ // rtt: the RTT for this ack packet.
+ // min_rtt: is the lowest delay (RTT) we have seen during the session.
+ // congestion_window: the congestion window in packets.
+ bool ShouldExitSlowStart(QuicTime::Delta rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window);
+
// Start a new slow start phase.
void Restart();
+ // TODO(ianswett): The following methods should be private, but that requires
+ // a follow up CL to update the unit test.
// Returns true if this ack the last sequence number of our current slow start
// round.
// Call Reset if this returns true.
- bool EndOfRound(QuicPacketSequenceNumber ack);
-
- // Call for each round (burst) in the slow start phase.
- void Reset(QuicPacketSequenceNumber end_sequence_number);
+ bool IsEndOfRound(QuicPacketSequenceNumber ack) const;
- // rtt: it the RTT for this ack packet.
- // delay_min: is the lowest delay (RTT) we have seen during the session.
- void Update(QuicTime::Delta rtt, QuicTime::Delta delay_min);
+ // Call for the start of each receive round (burst) in the slow start phase.
+ void StartReceiveRound(QuicPacketSequenceNumber last_sent);
- // Returns true when we should exit slow start.
- bool Exit();
-
- bool started() { return started_; }
+ // Whether slow start has started.
+ bool started() const {
+ return started_;
+ }
private:
+ // Whether a condition for exiting slow start has been found.
+ enum HystartState {
+ NOT_FOUND,
+ ACK_TRAIN, // A closely spaced ack train is too long.
+ DELAY, // Too much increase in the round's min_rtt was observed.
+ };
+
const QuicClock* clock_;
+ // Whether the hybrid slow start has been started.
bool started_;
- bool found_ack_train_;
- bool found_delay_;
- QuicTime round_start_; // Beginning of each slow start round.
- QuicPacketSequenceNumber end_sequence_number_; // End of slow start round.
- QuicTime last_time_; // Last time when the ACK spacing was close.
- uint8 sample_count_; // Number of samples to decide current RTT.
- QuicTime::Delta current_rtt_; // The minimum rtt of current round.
+ HystartState hystart_found_;
+ // Last sequence number sent which was CWND limited.
+ QuicPacketSequenceNumber last_sent_sequence_number_;
+
+ // Variables for tracking acks received during a slow start round.
+ QuicTime round_start_; // Beginning of each slow start receive round.
+ QuicPacketSequenceNumber end_sequence_number_; // End of the receive round.
+ // Last time when the spacing between ack arrivals was less than 2 ms.
+ // Defaults to the beginning of the round.
+ QuicTime last_close_ack_pair_time_;
+ uint32 rtt_sample_count_; // Number of rtt samples in the current round.
+ QuicTime::Delta current_min_rtt_; // The minimum rtt of current round.
DISALLOW_COPY_AND_ASSIGN(HybridSlowStart);
};
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
index 0a9f91fd3f0..91c5d9f2d65 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
@@ -18,38 +18,40 @@ class HybridSlowStartTest : public ::testing::Test {
rtt_(QuicTime::Delta::FromMilliseconds(60)) {
}
virtual void SetUp() {
- slowStart_.reset(new HybridSlowStart(&clock_));
+ slow_start_.reset(new HybridSlowStart(&clock_));
}
const QuicTime::Delta one_ms_;
const QuicTime::Delta rtt_;
MockClock clock_;
- scoped_ptr<HybridSlowStart> slowStart_;
+ scoped_ptr<HybridSlowStart> slow_start_;
};
TEST_F(HybridSlowStartTest, Simple) {
QuicPacketSequenceNumber sequence_number = 1;
QuicPacketSequenceNumber end_sequence_number = 3;
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
// Test duplicates.
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number));
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
// Test without a new registered end_sequence_number;
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
end_sequence_number = 20;
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
while (sequence_number < end_sequence_number) {
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
}
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
}
+// TODO(ianswett): Add tests which more realistically invoke the methods,
+// simulating how actual acks arrive and packets are sent.
TEST_F(HybridSlowStartTest, AckTrain) {
// At a typical RTT 60 ms, assuming that the inter arrival is 1 ms,
// we expect to be able to send a burst of 30 packet before we trigger the
@@ -58,24 +60,22 @@ TEST_F(HybridSlowStartTest, AckTrain) {
QuicPacketSequenceNumber sequence_number = 2;
QuicPacketSequenceNumber end_sequence_number = 2;
for (int burst = 0; burst < kMaxLoopCount; ++burst) {
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
do {
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_FALSE(slowStart_->Exit());
- } while (!slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
+ } while (!slow_start_->IsEndOfRound(sequence_number++));
end_sequence_number *= 2; // Exponential growth.
}
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
- for (int n = 0; n < 29 && !slowStart_->EndOfRound(sequence_number++); ++n) {
+ for (int n = 0;
+ n < 29 && !slow_start_->IsEndOfRound(sequence_number++); ++n) {
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
}
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_TRUE(slowStart_->Exit());
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
}
TEST_F(HybridSlowStartTest, Delay) {
@@ -84,24 +84,23 @@ TEST_F(HybridSlowStartTest, Delay) {
const int kHybridStartMinSamples = 8; // Number of acks required to trigger.
QuicPacketSequenceNumber end_sequence_number = 1;
- slowStart_->Reset(end_sequence_number++);
+ slow_start_->StartReceiveRound(end_sequence_number++);
// Will not trigger since our lowest RTT in our burst is the same as the long
// term RTT provided.
for (int n = 0; n < kHybridStartMinSamples; ++n) {
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_, 100));
}
- slowStart_->Reset(end_sequence_number++);
+ slow_start_->StartReceiveRound(end_sequence_number++);
for (int n = 1; n < kHybridStartMinSamples; ++n) {
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 4)),
- rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 5)), rtt_, 100));
}
// Expect to trigger since all packets in this burst was above the long term
// RTT provided.
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(4)), rtt_);
- EXPECT_TRUE(slowStart_->Exit());
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(5)), rtt_, 100));
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
deleted file mode 100644
index 2438493cc0b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-
-#include <algorithm>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "net/quic/congestion_control/cube_root.h"
-#include "net/quic/quic_protocol.h"
-
-namespace {
-// The following constants are in 2^10 fractions of a second instead of ms to
-// allow a 10 shift right to divide.
-const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
- // where 0.100 is 100 ms which is the scaling
- // round trip time.
-// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT.
-const int kPacedCubeScale = 6000;
-const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale;
-} // namespace
-
-namespace net {
-
-InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock)
- : clock_(clock),
- current_rate_(QuicBandwidth::Zero()),
- channel_estimate_(QuicBandwidth::Zero()),
- available_channel_estimate_(QuicBandwidth::Zero()),
- halfway_point_(QuicBandwidth::Zero()),
- epoch_(QuicTime::Zero()),
- last_update_time_(QuicTime::Zero()) {
-}
-
-void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate,
- QuicBandwidth available_channel_estimate,
- QuicBandwidth channel_estimate) {
- epoch_ = clock_->ApproximateNow();
- last_update_time_ = epoch_;
- available_channel_estimate_ = std::max(new_rate, available_channel_estimate);
- channel_estimate_ = std::max(channel_estimate, available_channel_estimate_);
-
- halfway_point_ = available_channel_estimate_.Add(
- (channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f));
-
- if (new_rate < available_channel_estimate_) {
- time_to_origin_point_ = CalcuateTimeToOriginPoint(
- available_channel_estimate_.Subtract(new_rate));
- } else if (new_rate >= channel_estimate_) {
- time_to_origin_point_ = 0;
- } else if (new_rate >= halfway_point_) {
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate));
- } else {
- time_to_origin_point_ = CalcuateTimeToOriginPoint(
- new_rate.Subtract(available_channel_estimate_));
- }
- current_rate_ = new_rate;
- DVLOG(1) << "Reset; time to origin point:" << time_to_origin_point_;
-}
-
-void InterArrivalBitrateRampUp::UpdateChannelEstimate(
- QuicBandwidth channel_estimate) {
- if (available_channel_estimate_ > channel_estimate ||
- current_rate_ > channel_estimate ||
- channel_estimate_ == channel_estimate) {
- // Ignore, because one of the following reasons:
- // 1) channel estimate is bellow our current available estimate which we
- // value higher that this estimate.
- // 2) channel estimate is bellow our current send rate.
- // 3) channel estimate has not changed.
- return;
- }
- if (available_channel_estimate_ == halfway_point_ &&
- channel_estimate_ == halfway_point_) {
- // First time we get a usable channel estimate.
- channel_estimate_ = channel_estimate;
- halfway_point_ = available_channel_estimate_.Add(
- (channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f)));
- DVLOG(1) << "UpdateChannelEstimate; first usable value:"
- << channel_estimate.ToKBitsPerSecond() << " Kbits/s";
- return;
- }
- if (current_rate_ < halfway_point_) {
- // Update channel estimate without recalculating if we are bellow the
- // halfway point.
- channel_estimate_ = channel_estimate;
- return;
- }
- // We are between halfway point and our channel_estimate.
- epoch_ = clock_->ApproximateNow();
- last_update_time_ = epoch_;
- channel_estimate_ = channel_estimate;
-
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_));
-
- DVLOG(1) << "UpdateChannelEstimate; time to origin point:"
- << time_to_origin_point_;
-}
-
-QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate(
- QuicBandwidth sent_bitrate) {
- DCHECK(epoch_.IsInitialized());
- QuicTime current_time = clock_->ApproximateNow();
- // Cubic is "independent" of RTT, the update is limited by the time elapsed.
- if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) {
- return current_rate_;
- }
- QuicTime::Delta time_from_last_update =
- current_time.Subtract(last_update_time_);
-
- last_update_time_ = current_time;
-
- if (!sent_bitrate.IsZero() &&
- sent_bitrate.Add(sent_bitrate) < current_rate_) {
- // Don't go up in bitrate when we are not sending.
- // We need to update the epoch to reflect this state.
- epoch_ = epoch_.Add(time_from_last_update);
- DVLOG(1) << "Don't increase; our sent bitrate is:"
- << sent_bitrate.ToKBitsPerSecond() << " Kbits/s"
- << " current target rate is:"
- << current_rate_.ToKBitsPerSecond() << " Kbits/s";
- return current_rate_;
- }
- QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_);
-
- // Change the time unit from microseconds to 2^10 fractions per second. This
- // is done to allow us to use shift as a divide operator.
- int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) /
- kNumMicrosPerSecond;
-
- int64 offset = time_to_origin_point_ - elapsed_time;
- // Note: using int64 since QuicBandwidth can't be negative
- int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >>
- kCubeScale;
-
- bool start_bellow_halfway_point = false;
- if (current_rate_ < halfway_point_) {
- start_bellow_halfway_point = true;
-
- // available_channel_estimate_ is the orgin of the cubic function.
- QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond(
- available_channel_estimate_.ToBytesPerSecond() -
- (delta_pace_kbps << 10));
-
- if (start_bellow_halfway_point && current_rate >= halfway_point_) {
- // We passed the halfway point, recalculate with new orgin.
- epoch_ = clock_->ApproximateNow();
- // channel_estimate_ is the new orgin of the cubic function.
- if (current_rate >= channel_estimate_) {
- time_to_origin_point_ = 0;
- } else {
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate));
- }
- DVLOG(1) << "Passed the halfway point; time to origin point:"
- << time_to_origin_point_;
- }
- current_rate_ = current_rate;
- } else {
- // channel_estimate_ is the orgin of the cubic function.
- current_rate_ = QuicBandwidth::FromBytesPerSecond(
- channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10));
- }
- return current_rate_;
-}
-
-uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint(
- QuicBandwidth rate_difference) const {
- return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond());
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
deleted file mode 100644
index 931992391a5..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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.
-
-// Ramp up bitrate from a start point normally our "current_rate" as long as we
-// have no packet loss or delay events.
-// The first half of the ramp up curve follows a cubic function with its orgin
-// at the estimated available bandwidth, onece the bitrate pass the halfway
-// point between the estimated available bandwidth and the estimated max
-// bandwidth it will follw a new cubic function with its orgin at the estimated
-// max bandwidth.
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalBitrateRampUp {
- public:
- explicit InterArrivalBitrateRampUp(const QuicClock* clock);
-
- // Call after a decision to lower the bitrate and after a probe.
- void Reset(QuicBandwidth current_rate,
- QuicBandwidth available_channel_estimate,
- QuicBandwidth channel_estimate);
-
- // Call everytime we get a new channel estimate.
- void UpdateChannelEstimate(QuicBandwidth channel_estimate);
-
- // Compute a new send pace to use.
- QuicBandwidth GetNewBitrate(QuicBandwidth sent_bitrate);
-
- private:
- uint32 CalcuateTimeToOriginPoint(QuicBandwidth rate_difference) const;
-
- static const QuicTime::Delta MaxCubicTimeInterval() {
- return QuicTime::Delta::FromMilliseconds(30);
- }
-
- const QuicClock* clock_;
-
- QuicBandwidth current_rate_;
- QuicBandwidth channel_estimate_;
- QuicBandwidth available_channel_estimate_;
- QuicBandwidth halfway_point_;
-
- // Time when this cycle started, after a Reset.
- QuicTime epoch_;
-
- // Time when we updated current_rate_.
- QuicTime last_update_time_;
-
- // Time to origin point of cubic function in 2^10 fractions of a second.
- uint32 time_to_origin_point_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalBitrateRampUp);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
deleted file mode 100644
index acae78d2056..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalBitrateRampUpTest : public ::testing::Test {
- protected:
- InterArrivalBitrateRampUpTest()
- : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
- bitrate_ramp_up_(&clock_) {
- }
- virtual void SetUp() {
- clock_.AdvanceTime(one_ms_);
- }
- const QuicTime::Delta one_ms_;
- const QuicTime::Delta hundred_ms_;
- MockClock clock_;
- InterArrivalBitrateRampUp bitrate_ramp_up_;
-};
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 24; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesLimitedSendRate) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth max_sent_rate =
- QuicBandwidth::FromKBytesPerSecond(125);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- // Should pass without being affected by the max_sent_rate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- // We expect 2 * sent_bitrate to cap the rate.
- EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
- // Remove our sent cap.
- // Expect bitrate to continue to ramp from its previous rate.
- for (int j = 0; j < 5; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesCloseToChannelEstimate) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 15; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 14; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 24; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 780000,
- sent_bitrate.ToBytesPerSecond(), 20000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UncertainEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(400 * 0.7f);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 20; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 12; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 30; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
- sent_bitrate.ToBytesPerSecond(), 20000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UnknownEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- available_channel_estimate);
-
- // First convex growth, from start_rate.
- for (int j = 0; j < 20; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(available_channel_estimate.ToBytesPerSecond(),
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 31; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond(),
- sent_bitrate.ToBytesPerSecond(), 10000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateHigher) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth available_channel_estimate = start_rate;
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // Convex growth, from available_channel_estimate.
- for (int j = 0; j < 16; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Increse channel estimate.
- channel_estimate = QuicBandwidth::FromKBytesPerSecond(300);
- bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
-
- // Concave growth, towards channel_estimate.
- for (int i = 0; i < 22; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateLower) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth available_channel_estimate = start_rate;
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // Convex growth, from available_channel_estimate.
- for (int j = 0; j < 16; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Decrese channel estimate.
- channel_estimate = QuicBandwidth::FromKBytesPerSecond(240);
- bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
-
- // Concave growth, towards channel_estimate.
- for (int i = 0; i < 11; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
deleted file mode 100644
index 5e500b371bc..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include <algorithm>
-
-// Initial noise variance, equal to a standard deviation of 1 millisecond.
-static const float kInitialVarianceNoise = 1000000.0;
-
-// Minimum variance of the time delta.
-static const int kMinVarianceDelta = 10000;
-
-// Threshold for accumulated delta.
-static const int kThresholdAccumulatedDeltasUs = 1000;
-
-// Same as above, described as numerator and denominator.
-static const int kBetaNumerator = 49;
-static const int kBetaDenominator = 50;
-
-// Trigger a signal when the accumulated time drift is larger than
-// 5 x standard deviation.
-// A lower value triggers earlier with more false detect as a side effect.
-static const int kDetectDriftStandardDeviation = 5;
-
-// Trigger an overuse when the time difference between send time and receive
-// is larger than 7 x standard deviation.
-// A lower value triggers earlier with more false detect as a side effect.
-static const float kDetectTimeDiffStandardDeviation = 7;
-
-// Trigger an overuse when the mean of the time difference diverges too far
-// from 0.
-// A higher value trigger earlier with more false detect as a side effect.
-static const int kDetectSlopeFactor = 14;
-
-// We need to get some initial statistics before the detection can start.
-static const int kMinSamplesBeforeDetect = 10;
-
-namespace net {
-
-InterArrivalOveruseDetector::InterArrivalOveruseDetector()
- : last_sequence_number_(0),
- num_of_deltas_(0),
- accumulated_deltas_(QuicTime::Delta::Zero()),
- delta_mean_(0.0),
- delta_variance_(kInitialVarianceNoise),
- delta_overuse_counter_(0),
- delta_estimate_(kBandwidthSteady),
- slope_overuse_counter_(0),
- slope_estimate_(kBandwidthSteady),
- send_receive_offset_(QuicTime::Delta::Infinite()),
- estimated_congestion_delay_(QuicTime::Delta::Zero()) {
-}
-
-void InterArrivalOveruseDetector::OnAcknowledgedPacket(
- QuicPacketSequenceNumber sequence_number,
- QuicTime send_time,
- bool last_of_send_time,
- QuicTime receive_time) {
- if (last_sequence_number_ >= sequence_number) {
- // This is an old packet and should be ignored. Note that we are called
- // with a full 64 bit sequence number, even if the wire format may only
- // convey some low-order bits of that number.
- DVLOG(1) << "Skip old packet";
- return;
- }
-
- last_sequence_number_ = sequence_number;
-
- if (current_packet_group_.send_time != send_time) {
- // First value in this group. If the last packet of a group is lost we
- // overwrite the old value and start over with a new measurement.
- current_packet_group_.send_time = send_time;
- // The receive_time value might not be first in a packet burst if that
- // packet was lost, however we will still use it in this calculation.
- UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time));
- }
- if (!last_of_send_time) {
- // We expect more packet with this send time.
- return;
- }
- // First packet of a later group, the previous group sample is ready.
- if (previous_packet_group_.send_time.IsInitialized()) {
- QuicTime::Delta sent_delta = send_time.Subtract(
- previous_packet_group_.send_time);
- QuicTime::Delta receive_delta = receive_time.Subtract(
- previous_packet_group_.last_receive_time);
- // We assume that groups of packets are sent together as bursts (tagged
- // with identical send times) because it is too computationally expensive
- // to pace them out individually. The received_delta is then the total
- // delta between the receipt of the last packet of the previous group, and
- // the last packet of the current group. Assuming we are transmitting
- // these bursts on average at the available bandwidth rate, there should be
- // no change in overall spread (both deltas should be the same).
- UpdateFilter(receive_delta, sent_delta);
- }
- // Save current as previous.
- previous_packet_group_ = current_packet_group_;
- previous_packet_group_.last_receive_time = receive_time;
-}
-
-void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset(
- QuicTime::Delta offset) {
- // Note the send and receive time can have a randomly large offset, however
- // they are stable in relation to each other, hence no or extremely low clock
- // drift relative to the duration of our stream.
- if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) {
- send_receive_offset_ = offset;
- }
- estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
-}
-
-BandwidthUsage InterArrivalOveruseDetector::GetState(
- QuicTime::Delta* estimated_congestion_delay) {
- *estimated_congestion_delay = estimated_congestion_delay_;
- int64 sigma_delta = sqrt(static_cast<double>(delta_variance_));
- DetectSlope(sigma_delta);
- DetectDrift(sigma_delta);
- return std::max(slope_estimate_, delta_estimate_);
-}
-
-void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
- QuicTime::Delta sent_delta) {
- ++num_of_deltas_;
- QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
- UpdateDeltaEstimate(time_diff);
- accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
-}
-
-void InterArrivalOveruseDetector::UpdateDeltaEstimate(
- QuicTime::Delta residual) {
- DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
- int64 residual_us = residual.ToMicroseconds();
- delta_mean_ =
- (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
- delta_variance_ =
- (kBetaNumerator * delta_variance_ +
- (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
- kBetaDenominator;
-
- if (delta_variance_ < kMinVarianceDelta) {
- delta_variance_ = kMinVarianceDelta;
- }
-}
-
-void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
- // We have 2 drift detectors. The accumulate of deltas and the absolute time
- // differences.
- if (num_of_deltas_ < kMinSamplesBeforeDetect) {
- return;
- }
- if (delta_overuse_counter_ > 0 &&
- accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
- if (delta_estimate_ != kBandwidthDraining) {
- DVLOG(1) << "Bandwidth estimate drift: Draining buffer(s) "
- << accumulated_deltas_.ToMilliseconds() << " ms";
- delta_estimate_ = kBandwidthDraining;
- }
- return;
- }
- if ((sigma_delta * kDetectTimeDiffStandardDeviation >
- estimated_congestion_delay_.ToMicroseconds()) &&
- (sigma_delta * kDetectDriftStandardDeviation >
- abs(accumulated_deltas_.ToMicroseconds()))) {
- if (delta_estimate_ != kBandwidthSteady) {
- DVLOG(1) << "Bandwidth estimate drift: Steady"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthSteady;
- // Reset drift counter.
- accumulated_deltas_ = QuicTime::Delta::Zero();
- delta_overuse_counter_ = 0;
- }
- return;
- }
- if (accumulated_deltas_.ToMicroseconds() > 0) {
- if (delta_estimate_ != kBandwidthOverUsing) {
- ++delta_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate drift: Over using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthOverUsing;
- }
- } else {
- if (delta_estimate_ != kBandwidthUnderUsing) {
- --delta_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate drift: Under using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthUnderUsing;
- }
- // Adding decay of negative accumulated_deltas_ since it could be caused by
- // a starting with full buffers. This way we will always converge to 0.
- accumulated_deltas_ = accumulated_deltas_.Add(
- QuicTime::Delta::FromMicroseconds(sigma_delta >> 3));
- }
-}
-
-void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) {
- // We use the mean change since it has a constant expected mean 0
- // regardless of number of packets and spread. It is also safe to use during
- // packet loss, since a lost packet only results in a missed filter update
- // not a drift.
- if (num_of_deltas_ < kMinSamplesBeforeDetect) {
- return;
- }
- if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
- if (slope_estimate_ != kBandwidthDraining) {
- DVLOG(1) << "Bandwidth estimate slope: Draining buffer(s)";
- }
- slope_estimate_ = kBandwidthDraining;
- return;
- }
- if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
- if (slope_estimate_ != kBandwidthSteady) {
- DVLOG(1) << "Bandwidth estimate slope: Steady"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_overuse_counter_ = 0;
- slope_estimate_ = kBandwidthSteady;
- }
- return;
- }
- if (delta_mean_ > 0) {
- if (slope_estimate_ != kBandwidthOverUsing) {
- ++slope_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate slope: Over using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_estimate_ = kBandwidthOverUsing;
- }
- } else {
- if (slope_estimate_ != kBandwidthUnderUsing) {
- --slope_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate slope: Under using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_estimate_ = kBandwidthUnderUsing;
- }
- }
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h
deleted file mode 100644
index 185236279bb..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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.
-
-// This class is a helper class to the inter arrival congestion control. It
-// provide a signal to the inter arrival congestion control of the estimated
-// state of our transport channel. The estimate is based on the inter arrival
-// time of the received packets relative to the time those packets were sent;
-// we can estimate the build up of buffers on the network before packets are
-// lost.
-//
-// Note: this class is not thread-safe.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum NET_EXPORT_PRIVATE RateControlRegion {
- kRateControlRegionUnknown = 0,
- kRateControlRegionUnderMax = 1,
- kRateControlRegionNearMax = 2
-};
-
-// Note: Order is important.
-enum NET_EXPORT_PRIVATE BandwidthUsage {
- kBandwidthSteady = 0,
- kBandwidthUnderUsing = 1,
- kBandwidthDraining = 2,
- kBandwidthOverUsing = 3,
-};
-
-// Normal state transition diagram
-//
-// kBandwidthUnderUsing
-// |
-// |
-// kBandwidthSteady
-// | ^
-// | |
-// kBandwidthOverUsing |
-// | |
-// | |
-// kBandwidthDraining
-//
-// The above transitions is in normal operation, with extreme values we don't
-// enforce the state transitions, hence you could in extreme scenarios go
-// between any states.
-//
-// kBandwidthSteady When the packets arrive in the same pace as we sent
-// them. In this state we can increase our send pace.
-//
-// kBandwidthOverUsing When the packets arrive slower than the pace we sent
-// them. In this state we should decrease our send pace.
-// When we enter into this state we will also get an
-// estimate on how much delay we have built up. The
-// reduction in send pace should be chosen to drain the
-// built up delay within reasonable time.
-//
-// kBandwidthUnderUsing When the packets arrive faster than the pace we sent
-// them. In this state another stream disappeared from
-// a shared link leaving us more available bandwidth.
-// In this state we should hold our pace to make sure we
-// fully drain the buffers before we start increasing
-// our send rate. We do this to avoid operating with
-// semi-full buffers.
-//
-// kBandwidthDraining We can only be in this state after we have been in a
-// overuse state. In this state we should hold our pace
-// to make sure we fully drain the buffers before we
-// start increasing our send rate. We do this to avoid
-// operating with semi-full buffers.
-
-class NET_EXPORT_PRIVATE InterArrivalOveruseDetector {
- public:
- InterArrivalOveruseDetector();
-
- // Update the statistics with the received delta times, call for every
- // received delta time. This function assumes that there is no re-orderings.
- // If multiple packets are sent at the same time (identical send_time)
- // last_of_send_time should be set to false for all but the last calls to
- // this function. If there is only one packet sent at a given time
- // last_of_send_time must be true.
- // received_delta is the time difference between receiving this packet and the
- // previously received packet.
- void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
- QuicTime send_time,
- bool last_of_send_time,
- QuicTime receive_time);
-
- // Get the current estimated state and update the estimated congestion delay.
- // |estimated_congestion_delay| will be updated with the estimated built up
- // buffer delay; it must not be NULL as it will be updated with the estimate.
- // Note 1: estimated_buffer_delay will only be valid when kBandwidthOverUsing
- // is returned.
- // Note 2: it's assumed that the pacer lower its send pace to drain the
- // built up buffer within reasonable time. The pacer should use the
- // estimated_buffer_delay as a guidance on how much to back off.
- // Note 3: The absolute value of estimated_congestion_delay is less reliable
- // than the state itself. It is also biased to low since we can't know
- // how full the buffers are when the flow starts.
- BandwidthUsage GetState(QuicTime::Delta* estimated_congestion_delay);
-
- private:
- struct PacketGroup {
- PacketGroup()
- : send_time(QuicTime::Zero()),
- last_receive_time(QuicTime::Zero()) {
- }
- QuicTime send_time;
- QuicTime last_receive_time;
- };
-
- // Update the statistics with the absolute receive time relative to the
- // absolute send time.
- void UpdateSendReceiveTimeOffset(QuicTime::Delta offset);
-
- // Update the filter with this new data point.
- void UpdateFilter(QuicTime::Delta received_delta,
- QuicTime::Delta sent_delta);
-
- // Update the estimate with this residual.
- void UpdateDeltaEstimate(QuicTime::Delta residual);
-
- // Estimate the state based on the slope of the changes.
- void DetectSlope(int64 sigma_delta);
-
- // Estimate the state based on the accumulated drift of the changes.
- void DetectDrift(int64 sigma_delta);
-
- // Current grouping of packets that were sent at the same time.
- PacketGroup current_packet_group_;
- // Grouping of packets that were sent at the same time, just before the
- // current_packet_group_ above.
- PacketGroup previous_packet_group_;
- // Sequence number of the last acknowledged packet.
- QuicPacketSequenceNumber last_sequence_number_;
- // Number of received delta times with unique send time.
- int num_of_deltas_;
- // Estimated accumulation of received delta times.
- // Note: Can be negative and can drift over time which is why we bias it
- // towards 0 and reset it given some triggers.
- QuicTime::Delta accumulated_deltas_;
- // Current running mean of our received delta times.
- int delta_mean_;
- // Current running variance of our received delta times.
- int64 delta_variance_;
- // Number of overuse signals currently triggered in this state.
- // Note: negative represent underuse.
- int delta_overuse_counter_;
- // State estimated by the delta times.
- BandwidthUsage delta_estimate_;
- // Number of overuse signals currently triggered in this state.
- // Note: negative represent underuse.
- int slope_overuse_counter_;
- // State estimated by the slope of the delta times.
- BandwidthUsage slope_estimate_;
- // Lowest offset between send and receive time ever received in this session.
- QuicTime::Delta send_receive_offset_;
- // Last received time difference between our normalized send and receive time.
- QuicTime::Delta estimated_congestion_delay_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalOveruseDetector);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
deleted file mode 100644
index 8d37749a577..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
+++ /dev/null
@@ -1,1114 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdlib.h>
-
-#include <cmath>
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-static const double kPi = 3.14159265;
-
-namespace net {
-namespace test {
-
-class InterArrivalOveruseDetectorTest : public ::testing::Test {
- protected:
- InterArrivalOveruseDetectorTest();
-
- QuicTime::Delta GaussianRandom(QuicTime::Delta mean,
- QuicTime::Delta standard_deviation);
-
- int Run100000Samples(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation);
-
- int RunUntilOveruse(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst,
- QuicTime::Delta *estimated_buffer_delay);
-
- int RunUntilSteady(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- int RunUntilNotDraining(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- int RunUntilUnderusing(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- void RunXBursts(int bursts,
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- QuicPacketSequenceNumber sequence_number_;
- MockClock send_clock_;
- MockClock receive_clock_;
- QuicTime::Delta drift_from_mean_;
- InterArrivalOveruseDetector overuse_detector_;
- unsigned int seed_;
-};
-
-InterArrivalOveruseDetectorTest::InterArrivalOveruseDetectorTest()
- : sequence_number_(1),
- drift_from_mean_(QuicTime::Delta::Zero()),
- seed_(1234) {
-}
-
-QuicTime::Delta InterArrivalOveruseDetectorTest::GaussianRandom(
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation) {
- // Creating a Normal distribution variable from two independent uniform
- // variables based on the Box-Muller transform.
- double uniform1 = base::RandDouble();
- double uniform2 = base::RandDouble();
-
- QuicTime::Delta random = QuicTime::Delta::FromMicroseconds(
- static_cast<int>(standard_deviation.ToMicroseconds() *
- sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2))).
- Add(mean).Subtract(drift_from_mean_);
- if (random < QuicTime::Delta::Zero()) {
- // Don't do negative deltas.
- drift_from_mean_ = drift_from_mean_.Subtract(mean);
- return QuicTime::Delta::Zero();
- }
- drift_from_mean_ = drift_from_mean_.Add(random).Subtract(mean);
- return random;
-}
-
-int InterArrivalOveruseDetectorTest::Run100000Samples(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation) {
- int unique_overuse = 0;
- int last_overuse = -1;
- for (int i = 0; i < 100000; ++i) {
- // Assume that we send out the packets with perfect pacing.
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean, standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- // We expect to randomly hit a few false detects, count the unique
- // overuse events, hence not multiple signals in a row.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthOverUsing == overuse_detector_.GetState(
- &estimated_buffer_delay)) {
- if (last_overuse + 1 != i) {
- unique_overuse++;
- }
- last_overuse = i;
- }
- }
- return unique_overuse;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilOveruse(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst,
- QuicTime::Delta *estimated_buffer_delay) {
- // Simulate a higher send pace, that is too high.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- if (kBandwidthOverUsing == overuse_detector_.GetState(
- estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilSteady(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthSteady ==
- overuse_detector_.GetState(&estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilNotDraining(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthDraining >
- overuse_detector_.GetState(&estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilUnderusing(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthUnderUsing == overuse_detector_.GetState(
- &estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-void InterArrivalOveruseDetectorTest::RunXBursts(
- int bursts,
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- for (int i = 0; i < bursts; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- }
-}
-
-// TODO(pwestin): test packet loss impact on accuracy.
-// TODO(pwestin): test colored noise by dropping late frames.
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_TestNoise) {
- int count[100];
- memset(count, 0, sizeof(count));
- for (int i = 0; i < 10000; ++i) {
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(30);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(10);
- count[GaussianRandom(mean, standard_deviation).ToMilliseconds()]++;
- }
- for (int j = 0; j < 100; ++j) {
- DLOG(INFO) << j << ":" << count[j];
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruse) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_SimpleNonOveruseSendClockAhead) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
- send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_SimpleNonOveruseSendClockBehind) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruseWithVariance) {
- QuicPacketSequenceNumber sequence_number = 1;
- for (int i = 0; i < 1000; ++i) {
- if (i % 2) {
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- } else {
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(15));
- }
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleOveruse) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
-
- for (int i = 0; i < 100000; ++i) {
- send_clock_.AdvanceTime(send_delta);
- receive_clock_.AdvanceTime(received_delta);
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- // Sending 2 packets the same time as that is what we expect to do.
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- false,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
-
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
- // Simulate a higher send pace, that is too high by receiving 1 millisecond
- // late per packet.
- received_delta = QuicTime::Delta::FromMilliseconds(6);
- send_clock_.AdvanceTime(send_delta);
- receive_clock_.AdvanceTime(received_delta);
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- false,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- EXPECT_EQ(kBandwidthOverUsing,
- overuse_detector_.GetState(&estimated_buffer_delay));
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance10Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 5 milliseconds before
- // detection.
- // Resulting in a minimal buffer build up of 30 milliseconds.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance10Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 50 milliseconds before
- // detection.
- // Resulting in a minimal buffer build up of 300 milliseconds.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(400, estimated_buffer_delay.ToMilliseconds(), 150);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 5 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 30 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(1,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 50 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 200 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(300, estimated_buffer_delay.ToMilliseconds(), 150);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(1,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 5 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 20 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(30, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(14, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 1 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 6 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(8, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(16, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20Mbit) {
- int packets_per_burst = 2;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 2 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_NEAR(100, bursts_until_not_draining, 10);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Mbit) {
- int packets_per_burst = 10;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 2 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_NEAR(100, bursts_until_not_draining, 10);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_VeryHighVariance100Mbit) {
- int packets_per_burst = 10;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- // We get false overuse in this scenario due to that the standard deviation is
- // higher than our mean and the fact that a delta time can't be negative. This
- // results in an under estimated standard deviation in the estimator causing
- // false detects.
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(2000, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 25 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 12.5 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(17, bursts_until_overuse);
- EXPECT_NEAR(22, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(117, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-//
-// Tests simulating big drop in bitrate.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTo100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(100);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 1 update with 100 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 100 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(1, bursts_until_overuse);
- EXPECT_NEAR(104, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Back off 20% lower than estimate to drain.
- mean = QuicTime::Delta::FromMilliseconds(100);
- drift_per_burst = QuicTime::Delta::FromMilliseconds(20);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(3, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20MbitTo1Mbit) {
- int packets_per_burst = 2;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(10);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 1 update with 10 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 10 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(1, bursts_until_overuse);
- EXPECT_NEAR(12, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Back off 20% lower than estimate to drain.
- mean = QuicTime::Delta::FromMilliseconds(10);
- drift_per_burst = QuicTime::Delta::FromMilliseconds(2);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(3, bursts_until_underusing);
-}
-
-//
-// Tests that we can detect slow drifts.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitSmallSteps) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(100);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 41 updates with 100 microseconds before
- // detection.
- // Resulting in a minimal buffer build up of 4.1 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(41, bursts_until_overuse);
- EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(29, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(71, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTinySteps) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(10);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 345 updates with 10 microseconds before
- // detection.
- // Resulting in a minimal buffer build up of 3.45 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(345, bursts_until_overuse);
- EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(18, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(683, bursts_until_underusing);
-}
-
-//
-// Tests simulating starting with full buffers.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersHighVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a lower send pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_underusing);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_steady);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersHighVariance1MbitSlowDrift) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a faster receive pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(21, bursts_until_underusing);
-
- // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
- // detection.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_steady);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersLowVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a lower send pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-
- // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
- // detection.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(41, bursts_until_steady);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.cc b/chromium/net/quic/congestion_control/inter_arrival_probe.cc
deleted file mode 100644
index 6d3af3208fe..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-namespace {
-const int kProbeSizePackets = 10;
-const net::QuicByteCount kMinPacketSize = 500;
-const int64 kDefaultBytesPerSecond = 40000;
-const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor.
-}
-
-namespace net {
-
-InterArrivalProbe::InterArrivalProbe(QuicByteCount max_segment_size)
- : max_segment_size_(max_segment_size),
- estimate_available_(false),
- available_channel_estimate_(QuicBandwidth::Zero()),
- unacked_data_(0) {
-}
-
-InterArrivalProbe::~InterArrivalProbe() {
-}
-
-void InterArrivalProbe::set_max_segment_size(QuicByteCount max_segment_size) {
- max_segment_size_ = max_segment_size;
-}
-
-bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) {
- if (!estimate_available_) {
- return false;
- }
- *available_channel_estimate = available_channel_estimate_;
- return true;
-}
-
-void InterArrivalProbe::OnPacketSent(QuicByteCount bytes) {
- if (!estimate_available_) {
- unacked_data_ += bytes;
- }
-}
-
-void InterArrivalProbe::OnAcknowledgedPacket(QuicByteCount bytes) {
- if (!estimate_available_) {
- DCHECK_LE(bytes, unacked_data_);
- unacked_data_ -= bytes;
- }
-}
-
-QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() {
- if (estimate_available_) {
- return 0;
- }
- return (kProbeSizePackets * max_segment_size_) - unacked_data_;
-}
-
-void InterArrivalProbe::OnIncomingFeedback(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes_sent,
- QuicTime time_sent,
- QuicTime time_received) {
- if (estimate_available_) {
- return;
- }
-
- if (available_channel_estimator_.get() == NULL) {
- if (bytes_sent < kMinPacketSize) {
- // Packet too small to start the probe phase.
- return;
- }
- first_sequence_number_ = sequence_number;
- available_channel_estimator_.reset(new AvailableChannelEstimator(
- sequence_number, time_sent, time_received));
- return;
- }
-
- available_channel_estimator_->OnIncomingFeedback(sequence_number,
- bytes_sent,
- time_sent,
- time_received);
- if (sequence_number < kProbeSizePackets - 1 + first_sequence_number_) {
- // We need more feedback before we have a probe estimate.
- return;
- }
- // Get the current estimated available channel capacity.
- // available_channel_estimate is invalid if kAvailableChannelEstimateUnknown
- // is returned.
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
- AvailableChannelEstimateState available_channel_estimate_state =
- available_channel_estimator_->GetAvailableChannelEstimate(
- &available_channel_estimate);
- switch (available_channel_estimate_state) {
- case kAvailableChannelEstimateUnknown:
- // Backup when we miss our probe.
- available_channel_estimate_ =
- QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond);
- break;
- case kAvailableChannelEstimateUncertain:
- available_channel_estimate_ =
- available_channel_estimate.Scale(kUncertainScaleFactor);
- break;
- case kAvailableChannelEstimateGood:
- available_channel_estimate_ = available_channel_estimate;
- break;
- case kAvailableChannelEstimateSenderLimited:
- available_channel_estimate_ =
- std::max(available_channel_estimate,
- QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond));
- break;
- }
- estimate_available_ = true;
- available_channel_estimator_.reset(NULL);
- DVLOG(1) << "Probe estimate:"
- << available_channel_estimate_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.h b/chromium/net/quic/congestion_control/inter_arrival_probe.h
deleted file mode 100644
index 0cba7eec8db..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-// Class that handle the initial probing phase of inter arrival congestion
-// control.
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/available_channel_estimator.h"
-#include "net/quic/quic_bandwidth.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalProbe {
- public:
- explicit InterArrivalProbe(QuicByteCount max_segment_size);
- ~InterArrivalProbe();
-
- void set_max_segment_size(QuicByteCount max_segment_size);
-
- // Call every time a packet is sent to the network.
- void OnPacketSent(QuicByteCount bytes);
-
- // Call once for each sent packet that we receive an acknowledgement from
- // the peer for.
- void OnAcknowledgedPacket(QuicByteCount bytes);
-
- // Call to get the number of bytes that can be sent as part of this probe.
- QuicByteCount GetAvailableCongestionWindow();
-
- // Call once for each sent packet we receive a congestion feedback from the
- // peer for.
- // If a peer sends both and ack and feedback for a sent packet, both
- // OnAcknowledgedPacket and OnIncomingFeedback should be called.
- void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes_sent,
- QuicTime time_sent,
- QuicTime time_received);
-
- // Returns false as long as we are probing, available_channel_estimate is
- // invalid during that time. When the probe is completed this function return
- // true and available_channel_estimate contains the estimate.
- bool GetEstimate(QuicBandwidth* available_channel_estimate);
-
- private:
- QuicByteCount max_segment_size_;
- scoped_ptr<AvailableChannelEstimator> available_channel_estimator_;
- QuicPacketSequenceNumber first_sequence_number_;
- bool estimate_available_;
- QuicBandwidth available_channel_estimate_;
- QuicByteCount unacked_data_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalProbe);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc b/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc
deleted file mode 100644
index d242a6caf4e..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalProbeTest : public ::testing::Test {
- protected:
- InterArrivalProbeTest()
- : probe_(kDefaultMaxPacketSize),
- start_(QuicTime::Zero()) {
- }
-
- InterArrivalProbe probe_;
- QuicTime start_;
-};
-
-TEST_F(InterArrivalProbeTest, CongestionWindow) {
- for (size_t i = 0; i < 10; i++) {
- probe_.OnPacketSent(kDefaultMaxPacketSize);
- EXPECT_EQ((9 - i) * kDefaultMaxPacketSize,
- probe_.GetAvailableCongestionWindow());
- }
- probe_.OnAcknowledgedPacket(kDefaultMaxPacketSize);
- EXPECT_EQ(kDefaultMaxPacketSize, probe_.GetAvailableCongestionWindow());
-
- probe_.OnPacketSent(kDefaultMaxPacketSize);
- EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow());
-}
-
-TEST_F(InterArrivalProbeTest, Estimate) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicByteCount bytes_sent = kDefaultMaxPacketSize;
- QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
- QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
-
- for (size_t i = 0; i < 10; ++i) {
- EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
-
- probe_.OnIncomingFeedback(sequence_number++,
- bytes_sent,
- time_sent,
- time_received);
- time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
- time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
- }
- EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
- EXPECT_EQ(kDefaultMaxPacketSize * 100,
- static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
-}
-
-TEST_F(InterArrivalProbeTest, EstimateWithLoss) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicByteCount bytes_sent = kDefaultMaxPacketSize;
- QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
- QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
-
- for (size_t i = 0; i < 6; ++i) {
- EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
-
- probe_.OnIncomingFeedback(sequence_number,
- bytes_sent,
- time_sent,
- time_received);
- sequence_number += 2;
- time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
- time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
- }
- EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
- EXPECT_EQ(kDefaultMaxPacketSize * 50,
- static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver.cc
deleted file mode 100644
index 770b287db0b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_receiver.h"
-
-#include "base/basictypes.h"
-
-namespace net {
-
-InterArrivalReceiver::InterArrivalReceiver()
- : accumulated_number_of_recoverd_lost_packets_(0) {
-}
-
-InterArrivalReceiver::~InterArrivalReceiver() {
-}
-
-bool InterArrivalReceiver::GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* feedback) {
- if (received_packet_times_.size() <= 1) {
- // Don't waste resources by sending a feedback frame for only one packet.
- return false;
- }
- feedback->type = kInterArrival;
- feedback->inter_arrival.accumulated_number_of_lost_packets =
- accumulated_number_of_recoverd_lost_packets_;
-
- // Copy our current receive set to our feedback message, we will not resend
- // this data if it is lost.
- feedback->inter_arrival.received_packet_times = received_packet_times_;
-
- // Prepare for the next set of arriving packets by clearing our current set.
- received_packet_times_.clear();
- return true;
-}
-
-void InterArrivalReceiver::RecordIncomingPacket(
- QuicByteCount /*bytes*/,
- QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) {
- if (revived) {
- ++accumulated_number_of_recoverd_lost_packets_;
- }
- received_packet_times_.insert(std::make_pair(sequence_number, timestamp));
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.h b/chromium/net/quic/congestion_control/inter_arrival_receiver.h
deleted file mode 100644
index a9de62cb1ea..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/receive_algorithm_interface.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_protocol.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalReceiver
- : public ReceiveAlgorithmInterface {
- public:
- InterArrivalReceiver();
- virtual ~InterArrivalReceiver();
-
- // Start implementation of ReceiveAlgorithmInterface.
- virtual bool GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* feedback) OVERRIDE;
-
- virtual void RecordIncomingPacket(QuicByteCount bytes,
- QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) OVERRIDE;
- // End implementation of ReceiveAlgorithmInterface.
-
- private:
- // We need to keep track of FEC recovered packets.
- int accumulated_number_of_recoverd_lost_packets_;
-
- // The set of received packets since the last feedback was sent, along with
- // their arrival times.
- TimeMap received_packet_times_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalReceiver);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc
deleted file mode 100644
index 927fb6d92f7..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/inter_arrival_receiver.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalReceiverTest : public ::testing::Test {
- protected:
- InterArrivalReceiver receiver_;
- MockClock clock_;
-};
-
-TEST_F(InterArrivalReceiverTest, SimpleReceiver) {
- QuicTime start = clock_.ApproximateNow();
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- clock_.AdvanceTime(received_delta);
- QuicTime receive_timestamp = clock_.ApproximateNow();
- receiver_.RecordIncomingPacket(1, 1, receive_timestamp, false);
-
- QuicCongestionFeedbackFrame feedback;
- ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback));
-
- clock_.AdvanceTime(received_delta);
- receive_timestamp = clock_.ApproximateNow();
- // Packet not received; but rather revived by FEC.
- receiver_.RecordIncomingPacket(1, 2, receive_timestamp, true);
- clock_.AdvanceTime(received_delta);
- receive_timestamp = clock_.ApproximateNow();
- receiver_.RecordIncomingPacket(1, 3, receive_timestamp, false);
-
- ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback));
-
- EXPECT_EQ(kInterArrival, feedback.type);
- EXPECT_EQ(1, feedback.inter_arrival.accumulated_number_of_lost_packets);
- EXPECT_EQ(3u, feedback.inter_arrival.received_packet_times.size());
- TimeMap::iterator it = feedback.inter_arrival.received_packet_times.begin();
- EXPECT_EQ(1u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start));
- it = feedback.inter_arrival.received_packet_times.begin();
- it++;
- EXPECT_EQ(2u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start));
- it++;
- EXPECT_EQ(3u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start));
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.cc b/chromium/net/quic/congestion_control/inter_arrival_sender.cc
deleted file mode 100644
index 632a41612be..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender.cc
+++ /dev/null
@@ -1,529 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_sender.h"
-
-namespace net {
-
-namespace {
-const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s
-const float kPacketLossBitrateReduction = 0.7f;
-const float kUncertainSafetyMargin = 0.7f;
-const float kMaxBitrateReduction = 0.9f;
-const float kMinBitrateReduction = 0.05f;
-const uint64 kMinBitrateKbit = 10;
-const int kInitialRttMs = 60; // At a typical RTT 60 ms.
-const float kAlpha = 0.125f;
-const float kOneMinusAlpha = 1 - kAlpha;
-
-static const int kBitrateSmoothingPeriodMs = 1000;
-static const int kMinBitrateSmoothingPeriodMs = 500;
-
-} // namespace
-
-InterArrivalSender::InterArrivalSender(const QuicClock* clock)
- : probing_(true),
- max_segment_size_(kDefaultMaxPacketSize),
- current_bandwidth_(QuicBandwidth::Zero()),
- smoothed_rtt_(QuicTime::Delta::Zero()),
- channel_estimator_(new ChannelEstimator()),
- bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)),
- overuse_detector_(new InterArrivalOveruseDetector()),
- probe_(new InterArrivalProbe(max_segment_size_)),
- state_machine_(new InterArrivalStateMachine(clock)),
- paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond(
- kProbeBitrateKBytesPerSecond), max_segment_size_)),
- accumulated_number_of_lost_packets_(0),
- bandwidth_usage_state_(kBandwidthSteady),
- back_down_time_(QuicTime::Zero()),
- back_down_bandwidth_(QuicBandwidth::Zero()),
- back_down_congestion_delay_(QuicTime::Delta::Zero()) {
-}
-
-InterArrivalSender::~InterArrivalSender() {
-}
-
-void InterArrivalSender::SetFromConfig(const QuicConfig& config,
- bool is_server) {
-}
-
-void InterArrivalSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- paced_sender_->set_max_segment_size(max_segment_size_);
- probe_->set_max_segment_size(max_segment_size_);
-}
-
-// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest).
-// static
-QuicBandwidth InterArrivalSender::CalculateSentBandwidth(
- const SendAlgorithmInterface::SentPacketsMap& sent_packets_map,
- QuicTime feedback_receive_time) {
- const QuicTime::Delta kBitrateSmoothingPeriod =
- QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs);
- const QuicTime::Delta kMinBitrateSmoothingPeriod =
- QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs);
-
- QuicByteCount sum_bytes_sent = 0;
-
- // Sum packet from new until they are kBitrateSmoothingPeriod old.
- SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit =
- sent_packets_map.rbegin();
-
- QuicTime::Delta max_diff = QuicTime::Delta::Zero();
- for (; history_rit != sent_packets_map.rend(); ++history_rit) {
- QuicTime::Delta diff =
- feedback_receive_time.Subtract(history_rit->second->send_timestamp());
- if (diff > kBitrateSmoothingPeriod) {
- break;
- }
- sum_bytes_sent += history_rit->second->bytes_sent();
- max_diff = diff;
- }
- if (max_diff < kMinBitrateSmoothingPeriod) {
- // No estimate.
- return QuicBandwidth::Zero();
- }
- return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff);
-}
-
-void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) {
- DCHECK(feedback.type == kInterArrival);
-
- if (feedback.type != kInterArrival) {
- return;
- }
-
- QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets,
- feedback_receive_time);
-
- TimeMap::const_iterator received_it;
- for (received_it = feedback.inter_arrival.received_packet_times.begin();
- received_it != feedback.inter_arrival.received_packet_times.end();
- ++received_it) {
- QuicPacketSequenceNumber sequence_number = received_it->first;
-
- SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number);
- if (sent_it == sent_packets.end()) {
- // Too old data; ignore and move forward.
- DVLOG(1) << "Too old feedback move forward, sequence_number:"
- << sequence_number;
- continue;
- }
- QuicTime time_received = received_it->second;
- QuicTime time_sent = sent_it->second->send_timestamp();
- QuicByteCount bytes_sent = sent_it->second->bytes_sent();
-
- channel_estimator_->OnAcknowledgedPacket(
- sequence_number, bytes_sent, time_sent, time_received);
- if (probing_) {
- probe_->OnIncomingFeedback(
- sequence_number, bytes_sent, time_sent, time_received);
- } else {
- bool last_of_send_time = false;
- SentPacketsMap::const_iterator next_sent_it = ++sent_it;
- if (next_sent_it == sent_packets.end()) {
- // No more sent packets; hence this must be the last.
- last_of_send_time = true;
- } else {
- if (time_sent != next_sent_it->second->send_timestamp()) {
- // Next sent packet have a different send time.
- last_of_send_time = true;
- }
- }
- overuse_detector_->OnAcknowledgedPacket(
- sequence_number, time_sent, last_of_send_time, time_received);
- }
- }
- if (probing_) {
- probing_ = ProbingPhase(feedback_receive_time);
- return;
- }
-
- bool packet_loss_event = false;
- if (accumulated_number_of_lost_packets_ !=
- feedback.inter_arrival.accumulated_number_of_lost_packets) {
- accumulated_number_of_lost_packets_ =
- feedback.inter_arrival.accumulated_number_of_lost_packets;
- packet_loss_event = true;
- }
- InterArrivalState state = state_machine_->GetInterArrivalState();
-
- if (state == kInterArrivalStatePacketLoss ||
- state == kInterArrivalStateCompetingTcpFLow) {
- if (packet_loss_event) {
- if (!state_machine_->PacketLossEvent()) {
- // Less than one RTT since last PacketLossEvent.
- return;
- }
- EstimateBandwidthAfterLossEvent(feedback_receive_time);
- } else {
- EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
- }
- return;
- }
- EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth);
-}
-
-bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) {
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
- if (!probe_->GetEstimate(&available_channel_estimate)) {
- // Continue probing phase.
- return true;
- }
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- QuicBandwidth new_rate =
- available_channel_estimate.Scale(kUncertainSafetyMargin);
-
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = available_channel_estimate;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing.
- break;
- }
- new_rate = std::max(new_rate,
- QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
-
- bitrate_ramp_up_->Reset(new_rate, available_channel_estimate,
- channel_estimate);
-
- current_bandwidth_ = new_rate;
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate);
- DVLOG(1) << "Probe result; new rate:"
- << new_rate.ToKBitsPerSecond() << " Kbits/s "
- << " available estimate:"
- << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s "
- << " channel estimate:"
- << channel_estimate.ToKBitsPerSecond() << " Kbits/s ";
- return false;
-}
-
-void InterArrivalSender::OnPacketAcked(
- QuicPacketSequenceNumber /*acked_sequence_number*/,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- // RTT can't be negative.
- DCHECK_LE(0, rtt.ToMicroseconds());
-
- if (probing_) {
- probe_->OnAcknowledgedPacket(acked_bytes);
- }
-
- if (rtt.IsInfinite()) {
- return;
- }
-
- if (smoothed_rtt_.IsZero()) {
- smoothed_rtt_ = rtt;
- } else {
- smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
- kAlpha * rtt.ToMicroseconds());
- }
- state_machine_->set_rtt(smoothed_rtt_);
-}
-
-void InterArrivalSender::OnPacketLost(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime ack_receive_time) {
- // Packet loss was reported.
- if (!probing_) {
- if (!state_machine_->PacketLossEvent()) {
- // Less than one RTT since last PacketLossEvent.
- return;
- }
- // Calculate new pace rate.
- EstimateBandwidthAfterLossEvent(ack_receive_time);
- }
-}
-
-bool InterArrivalSender::OnPacketSent(
- QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- TransmissionType /*transmission_type*/,
- HasRetransmittableData /*has_retransmittable_data*/) {
- if (probing_) {
- probe_->OnPacketSent(bytes);
- }
- paced_sender_->OnPacketSent(sent_time, bytes);
- return true;
-}
-
-void InterArrivalSender::OnRetransmissionTimeout() {
- // TODO(ianswett): Decrease the available bandwidth.
-}
-
-void InterArrivalSender::OnPacketAbandoned(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicByteCount abandoned_bytes) {
- // TODO(pwestin): use for out outer_congestion_window_ logic.
- if (probing_) {
- probe_->OnAcknowledgedPacket(abandoned_bytes);
- }
-}
-
-QuicTime::Delta InterArrivalSender::TimeUntilSend(
- QuicTime now,
- TransmissionType /*transmission_type*/,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake /*handshake*/) {
- // TODO(pwestin): implement outer_congestion_window_ logic.
- QuicTime::Delta outer_window = QuicTime::Delta::Zero();
-
- if (probing_) {
- if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA &&
- probe_->GetAvailableCongestionWindow() == 0) {
- outer_window = QuicTime::Delta::Infinite();
- }
- }
- return paced_sender_->TimeUntilSend(now, outer_window);
-}
-
-void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth) {
- QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero();
- BandwidthUsage new_bandwidth_usage_state =
- overuse_detector_->GetState(&estimated_congestion_delay);
-
- switch (new_bandwidth_usage_state) {
- case kBandwidthDraining:
- case kBandwidthUnderUsing:
- // Hold our current bitrate.
- break;
- case kBandwidthOverUsing:
- if (!state_machine_->IncreasingDelayEvent()) {
- // Less than one RTT since last IncreasingDelayEvent.
- return;
- }
- EstimateBandwidthAfterDelayEvent(feedback_receive_time,
- estimated_congestion_delay);
- break;
- case kBandwidthSteady:
- // Calculate new pace rate.
- if (bandwidth_usage_state_ == kBandwidthDraining ||
- bandwidth_usage_state_ == kBandwidthOverUsing) {
- EstimateNewBandwidthAfterDraining(feedback_receive_time,
- estimated_congestion_delay);
- } else {
- EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
- }
- break;
- }
- bandwidth_usage_state_ = new_bandwidth_usage_state;
-}
-
-QuicBandwidth InterArrivalSender::BandwidthEstimate() const {
- return current_bandwidth_;
-}
-
-QuicTime::Delta InterArrivalSender::SmoothedRtt() const {
- if (smoothed_rtt_.IsZero()) {
- return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
- }
- return smoothed_rtt_;
-}
-
-QuicTime::Delta InterArrivalSender::RetransmissionDelay() const {
- // TODO(pwestin): Calculate and return retransmission delay.
- // Use 2 * the smoothed RTT for now.
- return smoothed_rtt_.Add(smoothed_rtt_);
-}
-
-QuicByteCount InterArrivalSender::GetCongestionWindow() const {
- return 0;
-}
-
-void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth) {
- QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth);
- if (current_bandwidth_ == new_bandwidth) {
- return;
- }
- current_bandwidth_ = new_bandwidth;
- state_machine_->IncreaseBitrateDecision();
-
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- if (channel_estimator_state == kChannelEstimateGood) {
- bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate);
- }
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
- current_bandwidth_);
- DVLOG(1) << "New bandwidth estimate in steady state:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-// Did we drain the network buffers in our expected pace?
-void InterArrivalSender::EstimateNewBandwidthAfterDraining(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay) {
- if (current_bandwidth_ > back_down_bandwidth_) {
- // Do nothing, our current bandwidth is higher than our bandwidth at the
- // previous back down.
- DVLOG(1) << "Current bandwidth estimate is higher than before draining";
- return;
- }
- if (estimated_congestion_delay >= back_down_congestion_delay_) {
- // Do nothing, our estimated delay have increased.
- DVLOG(1) << "Current delay estimate is higher than before draining";
- return;
- }
- DCHECK(back_down_time_.IsInitialized());
- QuicTime::Delta buffer_reduction =
- back_down_congestion_delay_.Subtract(estimated_congestion_delay);
- QuicTime::Delta elapsed_time =
- feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt());
-
- QuicBandwidth new_estimate = QuicBandwidth::Zero();
- if (buffer_reduction >= elapsed_time) {
- // We have drained more than the elapsed time... go back to our old rate.
- new_estimate = back_down_bandwidth_;
- } else {
- float fraction_of_rate =
- static_cast<float>(buffer_reduction.ToMicroseconds()) /
- elapsed_time.ToMicroseconds(); // < 1.0
-
- QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate);
- QuicBandwidth max_estimated_draining_rate =
- back_down_bandwidth_.Subtract(current_bandwidth_);
- if (draining_rate > max_estimated_draining_rate) {
- // We drained faster than our old send rate, go back to our old rate.
- new_estimate = back_down_bandwidth_;
- } else {
- // Use our drain rate and our kMinBitrateReduction to go to our
- // new estimate.
- new_estimate = std::max(current_bandwidth_,
- current_bandwidth_.Add(draining_rate).Scale(
- 1.0f - kMinBitrateReduction));
- DVLOG(1) << "Draining calculation; current rate:"
- << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s "
- << "draining rate:"
- << draining_rate.ToKBitsPerSecond() << " Kbits/s "
- << "new estimate:"
- << new_estimate.ToKBitsPerSecond() << " Kbits/s "
- << " buffer reduction:"
- << buffer_reduction.ToMicroseconds() << " us "
- << " elapsed time:"
- << elapsed_time.ToMicroseconds() << " us ";
- }
- }
- if (new_estimate == current_bandwidth_) {
- return;
- }
-
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- // TODO(pwestin): we need to analyze channel_estimate too.
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = current_bandwidth_;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing, estimate is accurate.
- break;
- }
- bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate);
- state_machine_->IncreaseBitrateDecision();
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate);
- current_bandwidth_ = new_estimate;
- DVLOG(1) << "New bandwidth estimate after draining:"
- << new_estimate.ToKBitsPerSecond() << " Kbits/s";
-}
-
-void InterArrivalSender::EstimateBandwidthAfterDelayEvent(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay) {
- QuicByteCount estimated_byte_buildup =
- current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay);
-
- // To drain all build up buffer within one RTT we need to reduce the
- // bitrate with the following.
- // TODO(pwestin): this is a crude first implementation.
- int64 draining_rate_per_rtt = (estimated_byte_buildup *
- kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds();
-
- float decrease_factor =
- draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond();
-
- decrease_factor = std::max(decrease_factor, kMinBitrateReduction);
- decrease_factor = std::min(decrease_factor, kMaxBitrateReduction);
- back_down_congestion_delay_ = estimated_congestion_delay;
- QuicBandwidth new_target_bitrate =
- current_bandwidth_.Scale(1.0f - decrease_factor);
-
- // While in delay sensing mode send at least one packet per RTT.
- QuicBandwidth min_delay_bitrate =
- QuicBandwidth::FromBytesAndTimeDelta(max_segment_size_, SmoothedRtt());
- new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate);
-
- ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate);
-
- DVLOG(1) << "New bandwidth estimate after delay event:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s min delay bitrate:"
- << min_delay_bitrate.ToKBitsPerSecond()
- << " Kbits/s RTT:"
- << SmoothedRtt().ToMicroseconds()
- << " us";
-}
-
-void InterArrivalSender::EstimateBandwidthAfterLossEvent(
- QuicTime feedback_receive_time) {
- ResetCurrentBandwidth(feedback_receive_time,
- current_bandwidth_.Scale(kPacketLossBitrateReduction));
- DVLOG(1) << "New bandwidth estimate after loss event:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth new_rate) {
- new_rate = std::max(new_rate,
- QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = current_bandwidth_;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing.
- break;
- }
- back_down_time_ = feedback_receive_time;
- back_down_bandwidth_ = current_bandwidth_;
- bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate);
- if (new_rate != current_bandwidth_) {
- current_bandwidth_ = new_rate;
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
- current_bandwidth_);
- state_machine_->DecreaseBitrateDecision();
- }
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.h b/chromium/net/quic/congestion_control/inter_arrival_sender.h
deleted file mode 100644
index fabc7b65e34..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/channel_estimator.h"
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-#include "net/quic/congestion_control/paced_sender.h"
-#include "net/quic/congestion_control/send_algorithm_interface.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface {
- public:
- explicit InterArrivalSender(const QuicClock* clock);
- virtual ~InterArrivalSender();
-
- static QuicBandwidth CalculateSentBandwidth(
- const SendAlgorithmInterface::SentPacketsMap& sent_packets_map,
- QuicTime feedback_receive_time);
-
- // Start implementation of SendAlgorithmInterface.
- virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
- virtual void OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
- virtual bool OnPacketSent(
- QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
- virtual QuicTime::Delta TimeUntilSend(
- QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
- virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
- virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
- virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
- // End implementation of SendAlgorithmInterface.
-
- private:
- void EstimateDelayBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth);
- void EstimateNewBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth);
- void EstimateNewBandwidthAfterDraining(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay);
- void EstimateBandwidthAfterLossEvent(QuicTime feedback_receive_time);
- void EstimateBandwidthAfterDelayEvent(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay);
- void ResetCurrentBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth new_rate);
- bool ProbingPhase(QuicTime feedback_receive_time);
-
- bool probing_; // Are we currently in the probing phase?
- QuicByteCount max_segment_size_;
- QuicBandwidth current_bandwidth_;
- QuicTime::Delta smoothed_rtt_;
- scoped_ptr<ChannelEstimator> channel_estimator_;
- scoped_ptr<InterArrivalBitrateRampUp> bitrate_ramp_up_;
- scoped_ptr<InterArrivalOveruseDetector> overuse_detector_;
- scoped_ptr<InterArrivalProbe> probe_;
- scoped_ptr<InterArrivalStateMachine> state_machine_;
- scoped_ptr<PacedSender> paced_sender_;
- int accumulated_number_of_lost_packets_;
- BandwidthUsage bandwidth_usage_state_;
- QuicTime back_down_time_; // Time when we decided to back down.
- QuicBandwidth back_down_bandwidth_; // Bandwidth before backing down.
- QuicTime::Delta back_down_congestion_delay_; // Delay when backing down.
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalSender);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc b/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc
deleted file mode 100644
index fe8dd3cf29f..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc
+++ /dev/null
@@ -1,569 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_sender.h"
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/stl_util.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::pair;
-
-namespace net {
-namespace test {
-
-class InterArrivalSenderTest : public ::testing::Test {
- protected:
- InterArrivalSenderTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(60)),
- one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- one_s_(QuicTime::Delta::FromMilliseconds(1000)),
- nine_ms_(QuicTime::Delta::FromMilliseconds(9)),
- send_start_time_(send_clock_.Now()),
- sender_(&send_clock_),
- sequence_number_(1),
- acked_sequence_number_(1),
- feedback_sequence_number_(1) {
- send_clock_.AdvanceTime(one_ms_);
- receive_clock_.AdvanceTime(one_ms_);
- }
-
- virtual ~InterArrivalSenderTest() {
- STLDeleteValues(&sent_packets_);
- }
-
- void SendAvailableCongestionWindow() {
- while (sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) {
- QuicByteCount bytes_in_packet = kDefaultMaxPacketSize;
- sent_packets_[sequence_number_] =
- new class SendAlgorithmInterface::SentPacket(
- bytes_in_packet, send_clock_.Now(), HAS_RETRANSMITTABLE_DATA);
-
- sender_.OnPacketSent(send_clock_.Now(), sequence_number_, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- sequence_number_++;
- }
- EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
-
- void AckNPackets(int n) {
- for (int i = 0; i < n; ++i) {
- sender_.OnPacketAcked(
- acked_sequence_number_++, kDefaultMaxPacketSize, rtt_);
- }
- }
-
- void SendDelaySpikeFeedbackMessage(QuicTime::Delta spike_time) {
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kInterArrival;
- feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
- receive_clock_.AdvanceTime(spike_time);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
-
- // We need to send feedback for 2 packets since they where sent at the
- // same time.
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
-
- sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(),
- sent_packets_);
- }
-
- void SendFeedbackMessageNPackets(int n,
- QuicTime::Delta delta_odd,
- QuicTime::Delta delta_even) {
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kInterArrival;
- feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
- for (int i = 0; i < n; ++i) {
- if (feedback_sequence_number_ % 2) {
- receive_clock_.AdvanceTime(delta_even);
- } else {
- receive_clock_.AdvanceTime(delta_odd);
- }
- QuicTime receive_time = receive_clock_.ApproximateNow();
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
- }
- sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(),
- sent_packets_);
- }
-
- QuicTime::Delta SenderDeltaSinceStart() {
- return send_clock_.ApproximateNow().Subtract(send_start_time_);
- }
-
- const QuicTime::Delta rtt_;
- const QuicTime::Delta one_ms_;
- const QuicTime::Delta one_s_;
- const QuicTime::Delta nine_ms_;
- MockClock send_clock_;
- MockClock receive_clock_;
- const QuicTime send_start_time_;
- InterArrivalSender sender_;
- QuicPacketSequenceNumber sequence_number_;
- QuicPacketSequenceNumber acked_sequence_number_;
- QuicPacketSequenceNumber feedback_sequence_number_;
- SendAlgorithmInterface::SentPacketsMap sent_packets_;
-};
-
-TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- DLOG(INFO) << "After probe";
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- // Send 50 bursts, make sure that we slow down towards the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.95f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1100, 10);
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.99f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1560, 10);
- DLOG(INFO) << "Near available channel estimate";
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.00f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2000, 100);
- DLOG(INFO) << "At available channel estimate";
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.01f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2500, 100);
-
- // Send 50 bursts, make sure that we accelerate after the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.01f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2900, 100);
-
- // Send 50 bursts, make sure that we accelerate after the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.03f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100);
-
- int64 max_rate = kDefaultMaxPacketSize * kNumMicrosPerSecond /
- one_ms_.ToMicroseconds();
-
- int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2;
-
- // Send until we reach halfway point.
- for (int i = 0; i < 570; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(halfway_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 6600, 100);
- DLOG(INFO) << "Near halfway point";
-
- // Send until we reach max channel capacity.
- for (int i = 0; i < 1500; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(max_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 10000, 200);
-}
-
-TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
-
- int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
- QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
- SendDelaySpikeFeedbackMessage(spike_time);
-
- // Backing as much as we can, currently 90%.
- EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
-
- // Run until we are catched up after our introduced delay spike.
- while (send_clock_.Now() < receive_clock_.Now()) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
- }
- // Expect that we go back to 67% of the rate before the spike.
- EXPECT_NEAR(0.67f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 820, 10);
-
- // Send 100 bursts, make sure that we slow down towards the rate we had
- // before the spike.
- for (int i = 0; i < 100; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.97f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2100, 10);
-}
-
-TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
-
- int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
- QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
- SendDelaySpikeFeedbackMessage(spike_time);
-
- // Backing as much as we can, currently 90%.
- EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
-
- // Move send time forward.
- send_clock_.AdvanceTime(spike_time);
- // Make sure our clocks are aligned again.
- receive_clock_.AdvanceTime(send_clock_.Now().Subtract(receive_clock_.Now()));
-
- SendAvailableCongestionWindow();
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
- // We should now be back where we introduced the delay spike.
- EXPECT_NEAR(rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 710, 10);
-}
-
-TEST_F(InterArrivalSenderTest, MinBitrateDueToDelay) {
- QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- AckNPackets(10);
-
- // One second spread per packet is expected to result in an estimate at
- // our minimum bitrate.
- SendFeedbackMessageNPackets(10, one_s_, one_s_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
-}
-
-TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) {
- QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, nine_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- for (int i = 0; i < 15; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now());
- sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_);
- acked_sequence_number_ += 2; // Create a loss by not acking both packets.
- SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
- }
- // Test that our exponentail back off stop at expected_min_bitrate.
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
-
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now());
- sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_);
- acked_sequence_number_ += 2; // Create a loss by not acking both packets.
- SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
-
- // Make sure our bitrate is fixed at the expected_min_bitrate.
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
- }
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc
deleted file mode 100644
index 7095e60306b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-
-#include "base/logging.h"
-
-namespace {
-const int kIncreaseEventsBeforeDowngradingState = 5;
-const int kDecreaseEventsBeforeUpgradingState = 2;
-// Note: Can not be higher than kDecreaseEventsBeforeUpgradingState;
-const int kLossEventsBeforeUpgradingState = 2;
-// Timeout old loss and delay events after this time.
-const int kEventTimeoutMs = 10000;
-// A reasonable arbitrary chosen value for initial round trip time.
-const int kInitialRttMs = 80;
-}
-
-namespace net {
-
-InterArrivalStateMachine::InterArrivalStateMachine(const QuicClock* clock)
- : clock_(clock),
- current_state_(kInterArrivalStateStable),
- smoothed_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)),
- decrease_event_count_(0),
- last_decrease_event_(QuicTime::Zero()),
- increase_event_count_(0),
- last_increase_event_(QuicTime::Zero()),
- loss_event_count_(0),
- last_loss_event_(QuicTime::Zero()),
- delay_event_count_(0),
- last_delay_event_(QuicTime::Zero()) {
-}
-
-InterArrivalState InterArrivalStateMachine::GetInterArrivalState() {
- return current_state_;
-}
-
-void InterArrivalStateMachine::IncreaseBitrateDecision() {
- // Multiple increase event without packet loss or delay events will drive
- // state back to stable.
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_increase_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return;
- }
- last_increase_event_ = current_time;
- increase_event_count_++;
- decrease_event_count_ = 0; // Reset previous decrease events.
-
- if (increase_event_count_ < kIncreaseEventsBeforeDowngradingState) {
- // Not enough increase events to change state.
- return;
- }
- increase_event_count_ = 0; // Reset increase events.
-
- switch (current_state_) {
- case kInterArrivalStateStable:
- // Keep this state.
- break;
- case kInterArrivalStatePacketLoss:
- current_state_ = kInterArrivalStateStable;
- break;
- case kInterArrivalStateDelay:
- current_state_ = kInterArrivalStateStable;
- break;
- case kInterArrivalStateCompetingFlow:
- current_state_ = kInterArrivalStateDelay;
- break;
- case kInterArrivalStateCompetingTcpFLow:
- current_state_ = kInterArrivalStateDelay;
- break;
- }
-}
-
-void InterArrivalStateMachine::DecreaseBitrateDecision() {
- DCHECK(kDecreaseEventsBeforeUpgradingState >=
- kLossEventsBeforeUpgradingState);
-
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_decrease_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return;
- }
- last_decrease_event_ = current_time;
- decrease_event_count_++;
- increase_event_count_ = 0; // Reset previous increase events.
- if (decrease_event_count_ < kDecreaseEventsBeforeUpgradingState) {
- // Not enough decrease events to change state.
- return;
- }
- decrease_event_count_ = 0; // Reset decrease events.
-
- switch (current_state_) {
- case kInterArrivalStateStable:
- if (delay_event_count_ == 0 && loss_event_count_ > 0) {
- // No recent delay events; only packet loss events.
- current_state_ = kInterArrivalStatePacketLoss;
- } else {
- current_state_ = kInterArrivalStateDelay;
- }
- break;
- case kInterArrivalStatePacketLoss:
- // Keep this state.
- break;
- case kInterArrivalStateDelay:
- if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
- // We have packet loss events. Assume fighting with TCP.
- current_state_ = kInterArrivalStateCompetingTcpFLow;
- } else {
- current_state_ = kInterArrivalStateCompetingFlow;
- }
- break;
- case kInterArrivalStateCompetingFlow:
- if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
- // We have packet loss events. Assume fighting with TCP.
- current_state_ = kInterArrivalStateCompetingTcpFLow;
- }
- break;
- case kInterArrivalStateCompetingTcpFLow:
- // Keep this state.
- break;
- }
-}
-
-void InterArrivalStateMachine::set_rtt(QuicTime::Delta rtt) {
- smoothed_rtt_ = rtt;
-}
-
-bool InterArrivalStateMachine::PacketLossEvent() {
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_loss_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return false;
- }
- last_loss_event_ = current_time;
- loss_event_count_++;
- if (current_time.Subtract(last_delay_event_) >
- QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
- // Delay event have timed out.
- delay_event_count_ = 0;
- }
- return true;
-}
-
-bool InterArrivalStateMachine::IncreasingDelayEvent() {
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_delay_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return false;
- }
- last_delay_event_ = current_time;
- delay_event_count_++;
- if (current_time.Subtract(last_loss_event_) >
- QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
- // Loss event have timed out.
- loss_event_count_ = 0;
- }
- return true;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h b/chromium/net/quic/congestion_control/inter_arrival_state_machine.h
deleted file mode 100644
index 829aa29938a..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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.
-
-// State machine for congestion control. The state is updated by calls from
-// other modules as they detect events, or decide on taking specific actions.
-// Events include things like packet loss, or growing delay, while decisions
-// include decisions to increase or decrease bitrates.
-// This class should be called for every event and decision made by the
-// congestion control, this class will coalesce all calls relative to the
-// smoothed RTT.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
-
-#include "net/base/net_export.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-// State transition diagram.
-//
-// kInterArrivalStatePacketLoss
-// |
-// kInterArrivalStateStable
-// |
-// kInterArrivalStateDelay
-// | |
-// kInterArrivalStateCompetingFlow -> kInterArrivalStateCompetingTcpFLow
-
-enum NET_EXPORT_PRIVATE InterArrivalState {
- // We are on a network with a delay that is too small to be reliably detected,
- // such as a local ethernet.
- kInterArrivalStatePacketLoss = 1,
- // We are on an underutilized network operating with low delay and low loss.
- kInterArrivalStateStable = 2,
- // We are on a network where we can detect delay changes and suffer only
- // low loss. Nothing indicates that we are competing with another flow.
- kInterArrivalStateDelay = 3,
- // We are on a network where we can detect delay changes and suffer only
- // low loss. We have indications that we are compete with another flow.
- kInterArrivalStateCompetingFlow = 4,
- // We are on a network where we can detect delay changes, however we suffer
- // packet loss due to sharing the bottleneck link with another flow, which is
- // most likely a TCP flow.
- kInterArrivalStateCompetingTcpFLow = 5,
-};
-
-class NET_EXPORT_PRIVATE InterArrivalStateMachine {
- public:
- explicit InterArrivalStateMachine(const QuicClock* clock);
-
- InterArrivalState GetInterArrivalState();
-
- // Inter arrival congestion control decided to increase bitrate.
- void IncreaseBitrateDecision();
-
- // Inter arrival congestion control decided to decrease bitrate.
- void DecreaseBitrateDecision();
-
- // Estimated smoothed round trip time.
- // This should be called whenever the smoothed RTT estimate is updated.
- void set_rtt(QuicTime::Delta rtt);
-
- // This method is called when a packet loss was reported.
- bool PacketLossEvent();
-
- // This method is called when we believe that packet transit delay is
- // increasing, presumably due to a growing queue along the path.
- bool IncreasingDelayEvent();
-
- private:
- const QuicClock* clock_;
- InterArrivalState current_state_;
- QuicTime::Delta smoothed_rtt_;
-
- int decrease_event_count_;
- QuicTime last_decrease_event_;
-
- int increase_event_count_;
- QuicTime last_increase_event_;
-
- int loss_event_count_;
- QuicTime last_loss_event_;
-
- int delay_event_count_;
- QuicTime last_delay_event_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalStateMachine);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc
deleted file mode 100644
index 1a4bc8523ea..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalStateMachineTest : public ::testing::Test {
- protected:
- InterArrivalStateMachineTest() {
- }
-
- virtual void SetUp() {
- state_machine_.reset(new InterArrivalStateMachine(&clock_));
- }
-
- MockClock clock_;
- scoped_ptr<InterArrivalStateMachine> state_machine_;
-};
-
-TEST_F(InterArrivalStateMachineTest, SimplePacketLoss) {
- QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
- state_machine_->set_rtt(rtt);
- state_machine_->IncreaseBitrateDecision();
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateStable,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state packet loss.
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state packet loss.
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-}
-
-TEST_F(InterArrivalStateMachineTest, SimpleDelay) {
- QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
- state_machine_->set_rtt(rtt);
- state_machine_->IncreaseBitrateDecision();
-
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateStable,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state delay.
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateDelay,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateDelay,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state competing flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state competing flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state competing TCP flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state competing TCP flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
- state_machine_->GetInterArrivalState());
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/leaky_bucket.cc b/chromium/net/quic/congestion_control/leaky_bucket.cc
index 06c1f87f3ae..f3972f62eac 100644
--- a/chromium/net/quic/congestion_control/leaky_bucket.cc
+++ b/chromium/net/quic/congestion_control/leaky_bucket.cc
@@ -24,11 +24,15 @@ void LeakyBucket::Add(QuicTime now, QuicByteCount bytes) {
bytes_ += bytes;
}
-QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) {
- Update(now);
- return QuicTime::Delta::FromMicroseconds(
+QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) const {
+ QuicTime::Delta time_since_last_update = now.Subtract(time_last_updated_);
+ QuicTime::Delta send_delay = QuicTime::Delta::FromMicroseconds(
(bytes_ * base::Time::kMicrosecondsPerSecond) /
draining_rate_.ToBytesPerSecond());
+ if (send_delay < time_since_last_update) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_delay.Subtract(time_since_last_update);
}
QuicByteCount LeakyBucket::BytesPending(QuicTime now) {
diff --git a/chromium/net/quic/congestion_control/leaky_bucket.h b/chromium/net/quic/congestion_control/leaky_bucket.h
index 63ef8102a39..eb4cdb0c636 100644
--- a/chromium/net/quic/congestion_control/leaky_bucket.h
+++ b/chromium/net/quic/congestion_control/leaky_bucket.h
@@ -13,7 +13,6 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
@@ -30,7 +29,7 @@ class NET_EXPORT_PRIVATE LeakyBucket {
void Add(QuicTime now, QuicByteCount bytes);
// Time until the buffer is empty.
- QuicTime::Delta TimeRemaining(QuicTime now);
+ QuicTime::Delta TimeRemaining(QuicTime now) const;
// Number of bytes in the buffer.
QuicByteCount BytesPending(QuicTime now);
diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.cc b/chromium/net/quic/congestion_control/loss_detection_interface.cc
new file mode 100644
index 00000000000..035848c9c01
--- /dev/null
+++ b/chromium/net/quic/congestion_control/loss_detection_interface.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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/congestion_control/loss_detection_interface.h"
+
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+
+namespace net {
+
+// Factory for loss detection algorithm.
+LossDetectionInterface* LossDetectionInterface::Create(
+ LossDetectionType loss_type) {
+ switch (loss_type) {
+ case kNack:
+ return new TCPLossAlgorithm();
+ case kTime:
+ return new TimeLossAlgorithm();
+ }
+ LOG(DFATAL) << "Unknown loss detection algorithm:" << loss_type;
+ return NULL;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.h b/chromium/net/quic/congestion_control/loss_detection_interface.h
new file mode 100644
index 00000000000..5aaa51d46f6
--- /dev/null
+++ b/chromium/net/quic/congestion_control/loss_detection_interface.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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.
+//
+// The pure virtual class for send side loss detection algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class QuicUnackedPacketMap;
+class RttStats;
+
+class NET_EXPORT_PRIVATE LossDetectionInterface {
+ public:
+ // Creates a TCP loss detector.
+ static LossDetectionInterface* Create(LossDetectionType loss_type);
+
+ virtual ~LossDetectionInterface() {}
+
+ virtual LossDetectionType GetLossDetectionType() const = 0;
+
+ // Called when a new ack arrives or the loss alarm fires.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) = 0;
+
+ // Get the time the LossDetectionAlgorithm wants to re-evaluate losses.
+ // Returns QuicTime::Zero if no alarm needs to be set.
+ virtual QuicTime GetLossTimeout() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
diff --git a/chromium/net/quic/congestion_control/paced_sender.cc b/chromium/net/quic/congestion_control/paced_sender.cc
deleted file mode 100644
index 9a5cc448962..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender.cc
+++ /dev/null
@@ -1,57 +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/quic/congestion_control/paced_sender.h"
-
-#include <algorithm>
-
-#include "net/quic/quic_protocol.h"
-
-namespace net {
-
-// To prevent too aggressive pacing we allow the following packet burst size.
-const int64 kMinPacketBurstSize = 2;
-// Max estimated time between calls to TimeUntilSend and
-// AvailableCongestionWindow.
-const int64 kMaxSchedulingDelayUs = 2000;
-
-PacedSender::PacedSender(QuicBandwidth estimate, QuicByteCount max_segment_size)
- : leaky_bucket_(estimate),
- pace_(estimate),
- max_segment_size_(kDefaultMaxPacketSize) {
-}
-
-void PacedSender::set_max_segment_size(QuicByteCount max_segment_size) {
- max_segment_size_ = max_segment_size;
-}
-
-void PacedSender::UpdateBandwidthEstimate(QuicTime now,
- QuicBandwidth estimate) {
- leaky_bucket_.SetDrainingRate(now, estimate);
- pace_ = estimate;
-}
-
-void PacedSender::OnPacketSent(QuicTime now, QuicByteCount bytes) {
- leaky_bucket_.Add(now, bytes);
-}
-
-QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now,
- QuicTime::Delta time_until_send) {
- if (time_until_send.ToMicroseconds() >= kMaxSchedulingDelayUs) {
- return time_until_send;
- }
- // Pace the data.
- QuicByteCount pacing_window = pace_.ToBytesPerPeriod(
- QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs));
- QuicByteCount min_window_size = kMinPacketBurstSize * max_segment_size_;
- pacing_window = std::max(pacing_window, min_window_size);
-
- if (pacing_window > leaky_bucket_.BytesPending(now)) {
- // We have not filled our pacing window yet.
- return time_until_send;
- }
- return leaky_bucket_.TimeRemaining(now);
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/paced_sender.h b/chromium/net/quic/congestion_control/paced_sender.h
deleted file mode 100644
index 6d7c42919d0..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender.h
+++ /dev/null
@@ -1,44 +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.
-//
-// Helper class that limits the congestion window to pace the packets.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
-#define NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/leaky_bucket.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE PacedSender {
- public:
- PacedSender(QuicBandwidth bandwidth_estimate, QuicByteCount max_segment_size);
-
- void set_max_segment_size(QuicByteCount max_segment_size);
-
- // The estimated bandidth from the congestion algorithm changed.
- void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate);
-
- // A packet of size bytes was sent.
- void OnPacketSent(QuicTime now, QuicByteCount bytes);
-
- // Return time until we can send based on the pacing.
- QuicTime::Delta TimeUntilSend(QuicTime now, QuicTime::Delta time_until_send);
-
- private:
- // Helper object to track the rate data can leave the buffer for pacing.
- LeakyBucket leaky_bucket_;
- QuicBandwidth pace_;
- QuicByteCount max_segment_size_;
-
- DISALLOW_COPY_AND_ASSIGN(PacedSender);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
diff --git a/chromium/net/quic/congestion_control/paced_sender_test.cc b/chromium/net/quic/congestion_control/paced_sender_test.cc
deleted file mode 100644
index fa42c2ce579..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender_test.cc
+++ /dev/null
@@ -1,78 +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 "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/paced_sender.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-const int kHundredKBytesPerS = 100;
-
-class PacedSenderTest : public ::testing::Test {
- protected:
- PacedSenderTest()
- : zero_time_(QuicTime::Delta::Zero()),
- paced_sender_(new PacedSender(
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS),
- kDefaultMaxPacketSize)) {
- }
-
- const QuicTime::Delta zero_time_;
- MockClock clock_;
- scoped_ptr<PacedSender> paced_sender_;
-};
-
-TEST_F(PacedSenderTest, Basic) {
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 2),
- paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-TEST_F(PacedSenderTest, LowRate) {
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 20),
- paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-TEST_F(PacedSenderTest, HighRate) {
- QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond(
- kHundredKBytesPerS * 100);
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- for (int i = 0; i < 16; ++i) {
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).IsZero());
- }
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(2040, paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/pacing_sender.cc b/chromium/net/quic/congestion_control/pacing_sender.cc
index a20e7522750..97138f767d0 100644
--- a/chromium/net/quic/congestion_control/pacing_sender.cc
+++ b/chromium/net/quic/congestion_control/pacing_sender.cc
@@ -10,50 +10,44 @@ PacingSender::PacingSender(SendAlgorithmInterface* sender,
QuicTime::Delta alarm_granularity)
: sender_(sender),
alarm_granularity_(alarm_granularity),
+ last_delayed_packet_sent_time_(QuicTime::Zero()),
next_packet_send_time_(QuicTime::Zero()),
was_last_send_delayed_(false),
- max_segment_size_(kDefaultMaxPacketSize) {
+ has_valid_rtt_(false) {
}
PacingSender::~PacingSender() {}
-void PacingSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- sender_->SetMaxPacketSize(max_packet_size);
-}
-
void PacingSender::SetFromConfig(const QuicConfig& config, bool is_server) {
sender_->SetFromConfig(config, is_server);
}
void PacingSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SendAlgorithmInterface::SentPacketsMap& sent_packets) {
+ QuicTime feedback_receive_time) {
sender_->OnIncomingQuicCongestionFeedbackFrame(
- feedback, feedback_receive_time, sent_packets);
-}
-
-void PacingSender::OnPacketAcked(
- QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- sender_->OnPacketAcked(acked_sequence_number, acked_bytes, rtt);
+ feedback, feedback_receive_time);
}
-void PacingSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) {
- sender_->OnPacketLost(sequence_number, ack_receive_time);
+void PacingSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
+ if (rtt_updated) {
+ has_valid_rtt_ = true;
+ }
+ sender_->OnCongestionEvent(
+ rtt_updated, bytes_in_flight, acked_packets, lost_packets);
}
bool PacingSender::OnPacketSent(
QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) {
- // Only pace data packets.
- if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) {
+ // Only pace data packets once we have an updated RTT.
+ if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA && has_valid_rtt_) {
// The next packet should be sent as soon as the current packets has
// been transferred. We pace at twice the rate of the underlying
// sender's bandwidth estimate to help ensure that pacing doesn't become
@@ -61,29 +55,50 @@ bool PacingSender::OnPacketSent(
const float kPacingAggression = 2;
QuicTime::Delta delay =
BandwidthEstimate().Scale(kPacingAggression).TransferTime(bytes);
- next_packet_send_time_ = next_packet_send_time_.Add(delay);
+ // If the last send was delayed, and the alarm took a long time to get
+ // invoked, allow the connection to make up for lost time.
+ if (was_last_send_delayed_) {
+ next_packet_send_time_ = next_packet_send_time_.Add(delay);
+ // The send was application limited if it takes longer than the
+ // pacing delay between sent packets.
+ const bool application_limited =
+ last_delayed_packet_sent_time_.IsInitialized() &&
+ sent_time > last_delayed_packet_sent_time_.Add(delay);
+ const bool making_up_for_lost_time = next_packet_send_time_ <= sent_time;
+ // As long as we're making up time and not application limited,
+ // continue to consider the packets delayed, allowing the packets to be
+ // sent immediately.
+ if (making_up_for_lost_time && !application_limited) {
+ last_delayed_packet_sent_time_ = sent_time;
+ } else {
+ was_last_send_delayed_ = false;
+ last_delayed_packet_sent_time_ = QuicTime::Zero();
+ }
+ } else {
+ next_packet_send_time_ =
+ QuicTime::Max(next_packet_send_time_.Add(delay),
+ sent_time.Add(delay).Subtract(alarm_granularity_));
+ }
}
- return sender_->OnPacketSent(sent_time, sequence_number, bytes,
- transmission_type, has_retransmittable_data);
-}
-
-void PacingSender::OnRetransmissionTimeout() {
- sender_->OnRetransmissionTimeout();
+ return sender_->OnPacketSent(sent_time, bytes_in_flight, sequence_number,
+ bytes, has_retransmittable_data);
}
-void PacingSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) {
- sender_->OnPacketAbandoned(sequence_number, abandoned_bytes);
+void PacingSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ sender_->OnRetransmissionTimeout(packets_retransmitted);
}
QuicTime::Delta PacingSender::TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) {
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
QuicTime::Delta time_until_send =
- sender_->TimeUntilSend(now, transmission_type,
- has_retransmittable_data, handshake);
+ sender_->TimeUntilSend(now, bytes_in_flight, has_retransmittable_data);
+ if (!has_valid_rtt_) {
+ // Don't pace if we don't have an updated RTT estimate.
+ return time_until_send;
+ }
+
if (!time_until_send.IsZero()) {
DCHECK(time_until_send.IsInfinite());
// The underlying sender prevents sending.
@@ -96,25 +111,14 @@ QuicTime::Delta PacingSender::TimeUntilSend(
return QuicTime::Delta::Zero();
}
- if (!was_last_send_delayed_ &&
- (!next_packet_send_time_.IsInitialized() ||
- now > next_packet_send_time_.Add(alarm_granularity_))) {
- // An alarm did not go off late, instead the application is "slow"
- // delivering data. In this case, we restrict the amount of lost time
- // that we can make up for.
- next_packet_send_time_ = now.Subtract(alarm_granularity_);
- }
-
- // If the end of the epoch is far enough in the future, delay the send.
+ // If the next send time is within the alarm granularity, send immediately.
if (next_packet_send_time_ > now.Add(alarm_granularity_)) {
- was_last_send_delayed_ = true;
DVLOG(1) << "Delaying packet: "
<< next_packet_send_time_.Subtract(now).ToMicroseconds();
+ was_last_send_delayed_ = true;
return next_packet_send_time_.Subtract(now);
}
- // Sent it immediately. The epoch end will be adjusted in OnPacketSent.
- was_last_send_delayed_ = false;
DVLOG(1) << "Sending packet now";
return QuicTime::Delta::Zero();
}
@@ -123,10 +127,6 @@ QuicBandwidth PacingSender::BandwidthEstimate() const {
return sender_->BandwidthEstimate();
}
-QuicTime::Delta PacingSender::SmoothedRtt() const {
- return sender_->SmoothedRtt();
-}
-
QuicTime::Delta PacingSender::RetransmissionDelay() const {
return sender_->RetransmissionDelay();
}
diff --git a/chromium/net/quic/congestion_control/pacing_sender.h b/chromium/net/quic/congestion_control/pacing_sender.h
index 92970b93ce9..c74177125d2 100644
--- a/chromium/net/quic/congestion_control/pacing_sender.h
+++ b/chromium/net/quic/congestion_control/pacing_sender.h
@@ -17,7 +17,6 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
@@ -32,42 +31,35 @@ class NET_EXPORT_PRIVATE PacingSender : public SendAlgorithmInterface {
// SendAlgorithmInterface methods.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SendAlgorithmInterface::SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
private:
- QuicTime::Delta GetTransferTime(QuicByteCount bytes);
-
scoped_ptr<SendAlgorithmInterface> sender_; // Underlying sender.
QuicTime::Delta alarm_granularity_;
+ // Send time of the last packet considered delayed.
+ QuicTime last_delayed_packet_sent_time_;
QuicTime next_packet_send_time_; // When can the next packet be sent.
- bool was_last_send_delayed_; // True when the last send was delayed.
- QuicByteCount max_segment_size_;
+ mutable bool was_last_send_delayed_; // True when the last send was delayed.
+ bool has_valid_rtt_; // True if we have at least one RTT update.
DISALLOW_COPY_AND_ASSIGN(PacingSender);
};
diff --git a/chromium/net/quic/congestion_control/pacing_sender_test.cc b/chromium/net/quic/congestion_control/pacing_sender_test.cc
index dfc78b84ece..2dc696098ff 100644
--- a/chromium/net/quic/congestion_control/pacing_sender_test.cc
+++ b/chromium/net/quic/congestion_control/pacing_sender_test.cc
@@ -13,10 +13,13 @@
using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
+const QuicByteCount kBytesInFlight = 1024;
+
class PacingSenderTest : public ::testing::Test {
protected:
PacingSenderTest()
@@ -35,23 +38,24 @@ class PacingSenderTest : public ::testing::Test {
void CheckPacketIsSentImmediately() {
// In order for the packet to be sendable, the underlying sender must
// permit it to be sent immediately.
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- // Verify that the packet can be sent immediately.
- EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet can be sent immediately.
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
// Actually send the packet.
EXPECT_CALL(*mock_sender_,
- OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
- pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++,
- kMaxPacketSize, NOT_RETRANSMISSION,
+ OnPacketSent(clock_.Now(), kBytesInFlight, sequence_number_,
+ kMaxPacketSize, HAS_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), kBytesInFlight,
+ sequence_number_++, kMaxPacketSize,
HAS_RETRANSMITTABLE_DATA);
}
@@ -59,38 +63,38 @@ class PacingSenderTest : public ::testing::Test {
// In order for the ack to be sendable, the underlying sender must
// permit it to be sent immediately.
EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- NO_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
+ 0,
+ NO_RETRANSMITTABLE_DATA))
.WillOnce(Return(zero_time_));
// Verify that the ACK can be sent immediately.
EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- NO_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ NO_RETRANSMITTABLE_DATA));
// Actually send the packet.
EXPECT_CALL(*mock_sender_,
- OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize,
- NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
- pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++,
- kMaxPacketSize, NOT_RETRANSMISSION,
+ OnPacketSent(clock_.Now(), 0, sequence_number_,
+ kMaxPacketSize, NO_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), 0,
+ sequence_number_++, kMaxPacketSize,
NO_RETRANSMITTABLE_DATA);
}
void CheckPacketIsDelayed(QuicTime::Delta delay) {
// In order for the packet to be sendable, the underlying sender must
// permit it to be sent immediately.
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- // Verify that the packet is delayed.
- EXPECT_EQ(delay.ToMicroseconds(),
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).ToMicroseconds());
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet is delayed.
+ EXPECT_EQ(delay.ToMicroseconds(),
+ pacing_sender_->TimeUntilSend(
+ clock_.Now(), kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA).ToMicroseconds());
+ }
}
const QuicTime::Delta zero_time_;
@@ -102,27 +106,29 @@ class PacingSenderTest : public ::testing::Test {
};
TEST_F(PacingSenderTest, NoSend) {
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(infinite_time_));
- EXPECT_EQ(infinite_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(infinite_time_));
+ EXPECT_EQ(infinite_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
}
TEST_F(PacingSenderTest, SendNow) {
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
}
TEST_F(PacingSenderTest, VariousSending) {
@@ -132,6 +138,16 @@ TEST_F(PacingSenderTest, VariousSending) {
.WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta(
kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2))));
+ // Send a whole pile of packets, and verify that they are not paced.
+ for (int i = 0 ; i < 1000; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // Now update the RTT and verify that packets are actually paced.
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _));
+ SendAlgorithmInterface::CongestionMap empty_map;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map);
+
CheckPacketIsSentImmediately();
CheckPacketIsSentImmediately();
CheckPacketIsSentImmediately();
@@ -155,6 +171,29 @@ TEST_F(PacingSenderTest, VariousSending) {
CheckPacketIsSentImmediately();
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+ // Wake up really late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late again, but application pause partway through.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
// Wake up too early.
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map.h b/chromium/net/quic/congestion_control/quic_max_sized_map.h
deleted file mode 100644
index a4ed7769d61..00000000000
--- a/chromium/net/quic/congestion_control/quic_max_sized_map.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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.
-
-// Simple max sized map. Automatically deletes the oldest element when the
-// max limit is reached.
-// Note: the ConstIterator will NOT be valid after an Insert or RemoveAll.
-#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
-#define NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
-
-#include <stdlib.h>
-
-#include <list>
-#include <map>
-
-#include "base/basictypes.h"
-
-namespace net {
-
-template <class Key, class Value>
-class QuicMaxSizedMap {
- public:
- typedef typename std::multimap<Key, Value>::const_iterator ConstIterator;
-
- explicit QuicMaxSizedMap(size_t max_numer_of_items)
- : max_numer_of_items_(max_numer_of_items) {
- }
-
- size_t MaxSize() const {
- return max_numer_of_items_;
- }
-
- size_t Size() const {
- return table_.size();
- }
-
- void Insert(const Key& k, const Value& value) {
- if (Size() == MaxSize()) {
- ListIterator list_it = insert_order_.begin();
- table_.erase(*list_it);
- insert_order_.pop_front();
- }
- TableIterator it = table_.insert(std::pair<Key, Value>(k, value));
- insert_order_.push_back(it);
- }
-
- void RemoveAll() {
- table_.clear();
- insert_order_.clear();
- }
-
- // STL style const_iterator support.
- ConstIterator Find(const Key& k) const {
- return table_.find(k);
- }
-
- ConstIterator Begin() const {
- return ConstIterator(table_.begin());
- }
-
- ConstIterator End() const {
- return ConstIterator(table_.end());
- }
-
- private:
- typedef typename std::multimap<Key, Value>::iterator TableIterator;
- typedef typename std::list<TableIterator>::iterator ListIterator;
-
- const size_t max_numer_of_items_;
- std::multimap<Key, Value> table_;
- std::list<TableIterator> insert_order_;
-
- DISALLOW_COPY_AND_ASSIGN(QuicMaxSizedMap);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc b/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc
deleted file mode 100644
index 89c05cccbaf..00000000000
--- a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/quic_max_sized_map.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class QuicMaxSizedMapTest : public ::testing::Test {
-};
-
-TEST_F(QuicMaxSizedMapTest, Basic) {
- QuicMaxSizedMap<int, int> test_map(100);
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(0u, test_map.Size());
- test_map.Insert(1, 2);
- test_map.Insert(1, 3);
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(2u, test_map.Size());
- test_map.RemoveAll();
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(0u, test_map.Size());
-}
-
-TEST_F(QuicMaxSizedMapTest, Find) {
- QuicMaxSizedMap<int, int> test_map(100);
- test_map.Insert(1, 2);
- test_map.Insert(1, 3);
- test_map.Insert(2, 4);
- test_map.Insert(3, 5);
- QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Find(2);
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(4, it->second);
- it = test_map.Find(1);
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(2, it->second);
- ++it;
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(3, it->second);
-}
-
-TEST_F(QuicMaxSizedMapTest, Sort) {
- QuicMaxSizedMap<int, int> test_map(100);
- test_map.Insert(9, 9);
- test_map.Insert(8, 8);
- test_map.Insert(7, 7);
- test_map.Insert(6, 6);
- test_map.Insert(2, 2);
- test_map.Insert(4, 4);
- test_map.Insert(5, 5);
- test_map.Insert(3, 3);
- test_map.Insert(0, 0);
- test_map.Insert(1, 1);
- QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Begin();
- for (int i = 0; i < 10; ++i, ++it) {
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(i, it->first);
- EXPECT_EQ(i, it->second);
- }
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
index a49f64f160a..5384bdcddf7 100644
--- a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
+++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
@@ -15,8 +15,12 @@ ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create(
switch (type) {
case kTCP:
return new TcpReceiver();
+ case kTCPBBR:
+ LOG(DFATAL) << "TCPBBR is not yet supported.";
+ return NULL;
case kInterArrival:
- break; // TODO(pwestin) Implement.
+ LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported.";
+ return NULL;
case kFixRate:
return new FixRateReceiver();
}
diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.h b/chromium/net/quic/congestion_control/receive_algorithm_interface.h
index 17e7793749a..cd96b022db2 100644
--- a/chromium/net/quic/congestion_control/receive_algorithm_interface.h
+++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.h
@@ -30,12 +30,9 @@ class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface {
// bytes: is the packet size in bytes including IP headers.
// sequence_number: is the unique sequence number from the QUIC packet header.
// timestamp: is the sent timestamp from the QUIC packet header.
- // revived: is set if the packet is lost and then recovered with help of FEC
- // (Forward Error Correction) packet(s).
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) = 0;
+ QuicTime timestamp) = 0;
};
} // namespace net
diff --git a/chromium/net/quic/congestion_control/rtt_stats.cc b/chromium/net/quic/congestion_control/rtt_stats.cc
new file mode 100644
index 00000000000..745deb58cb3
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 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/congestion_control/rtt_stats.h"
+
+namespace net {
+
+namespace {
+
+// Default initial rtt used before any samples are received.
+const int kInitialRttMs = 100;
+const float kAlpha = 0.125f;
+const float kOneMinusAlpha = (1 - kAlpha);
+const float kBeta = 0.25f;
+const float kOneMinusBeta = (1 - kBeta);
+const float kHalfWindow = 0.5f;
+const float kQuarterWindow = 0.25f;
+
+} // namespace
+
+RttStats::RttStats()
+ : latest_rtt_(QuicTime::Delta::Zero()),
+ min_rtt_(QuicTime::Delta::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ mean_deviation_(QuicTime::Delta::Zero()),
+ initial_rtt_us_(kInitialRttMs * base::Time::kMicrosecondsPerMillisecond),
+ num_min_rtt_samples_remaining_(0),
+ recent_min_rtt_window_(QuicTime::Delta::Infinite()) {}
+
+bool RttStats::HasUpdates() const {
+ return !smoothed_rtt_.IsZero();
+}
+
+void RttStats::SampleNewRecentMinRtt(uint32 num_samples) {
+ num_min_rtt_samples_remaining_ = num_samples;
+ new_min_rtt_ = RttSample();
+}
+
+// Updates the RTT based on a new sample.
+void RttStats::UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now) {
+ QuicTime::Delta rtt_sample(QuicTime::Delta::Zero());
+ if (send_delta > ack_delay) {
+ rtt_sample = send_delta.Subtract(ack_delay);
+ } else if (!HasUpdates()) {
+ // Even though we received information from the peer suggesting
+ // an invalid (negative) RTT, we can use the send delta as an
+ // approximation until we get a better estimate.
+ rtt_sample = send_delta;
+ }
+
+ if (rtt_sample.IsInfinite() || rtt_sample.IsZero()) {
+ DVLOG(1) << "Ignoring rtt, because it's "
+ << (rtt_sample.IsZero() ? "Zero" : "Infinite");
+ return;
+ }
+ // RTT can't be negative.
+ DCHECK_LT(0, rtt_sample.ToMicroseconds());
+
+ latest_rtt_ = rtt_sample;
+ // First time call or link delay decreases.
+ if (min_rtt_.IsZero() || min_rtt_ > rtt_sample) {
+ min_rtt_ = rtt_sample;
+ }
+ UpdateRecentMinRtt(rtt_sample, now);
+ // First time call.
+ if (!HasUpdates()) {
+ smoothed_rtt_ = rtt_sample;
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ rtt_sample.ToMicroseconds() / 2);
+ } else {
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ kOneMinusBeta * mean_deviation_.ToMicroseconds() +
+ kBeta * std::abs(smoothed_rtt_.Subtract(rtt_sample).ToMicroseconds()));
+ smoothed_rtt_ = smoothed_rtt_.Multiply(kOneMinusAlpha).Add(
+ rtt_sample.Multiply(kAlpha));
+ DVLOG(1) << "Cubic; smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds()
+ << " mean_deviation(us):" << mean_deviation_.ToMicroseconds();
+ }
+}
+
+void RttStats::UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now) {
+ // Recent min_rtt update.
+ if (num_min_rtt_samples_remaining_ > 0) {
+ --num_min_rtt_samples_remaining_;
+ if (new_min_rtt_.rtt.IsZero() || rtt_sample <= new_min_rtt_.rtt) {
+ new_min_rtt_ = RttSample(rtt_sample, now);
+ }
+ if (num_min_rtt_samples_remaining_ == 0) {
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_ = new_min_rtt_;
+ }
+ }
+
+ // Update the three recent rtt samples.
+ if (recent_min_rtt_.rtt.IsZero() || rtt_sample <= recent_min_rtt_.rtt) {
+ recent_min_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_;
+ } else if (rtt_sample <= half_window_rtt_.rtt) {
+ half_window_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_;
+ } else if (rtt_sample <= quarter_window_rtt_.rtt) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+
+ // Expire old min rtt samples.
+ if (recent_min_rtt_.time < now.Subtract(recent_min_rtt_window_)) {
+ recent_min_rtt_ = half_window_rtt_;
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (half_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kHalfWindow))) {
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (quarter_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kQuarterWindow))) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+}
+
+QuicTime::Delta RttStats::SmoothedRtt() const {
+ if (!HasUpdates()) {
+ return QuicTime::Delta::FromMicroseconds(initial_rtt_us_);
+ }
+ return smoothed_rtt_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/rtt_stats.h b/chromium/net/quic/congestion_control/rtt_stats.h
new file mode 100644
index 00000000000..ee8d1809086
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats.h
@@ -0,0 +1,112 @@
+// Copyright 2014 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.
+//
+// A convenience class to store rtt samples and calculate smoothed rtt.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+#define NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+namespace test {
+class RttStatsPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE RttStats {
+ public:
+ RttStats();
+
+ // Returns true if any RTT measurements have been made.
+ bool HasUpdates() const;
+
+ // Updates the RTT from an incoming ack which is received |send_delta| after
+ // the packet is sent and the peer reports the ack being delayed |ack_delay|.
+ void UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now);
+
+ // Forces RttStats to sample a new recent min rtt within the next
+ // |num_samples| UpdateRtt calls.
+ void SampleNewRecentMinRtt(uint32 num_samples);
+
+ QuicTime::Delta SmoothedRtt() const;
+
+ int64 initial_rtt_us() const {
+ return initial_rtt_us_;
+ }
+
+ // Sets an initial RTT to be used for SmoothedRtt before any RTT updates.
+ void set_initial_rtt_us(int64 initial_rtt_us) {
+ initial_rtt_us_ = initial_rtt_us;
+ }
+
+ QuicTime::Delta latest_rtt() const {
+ return latest_rtt_;
+ }
+
+ // Returns the min_rtt for the entire connection.
+ QuicTime::Delta min_rtt() const {
+ return min_rtt_;
+ }
+
+ // Returns the min_rtt since SampleNewRecentMinRtt has been called, or the
+ // min_rtt for the entire connection if SampleNewMinRtt was never called.
+ QuicTime::Delta recent_min_rtt() const {
+ return recent_min_rtt_.rtt;
+ }
+
+ QuicTime::Delta mean_deviation() const {
+ return mean_deviation_;
+ }
+
+ // Sets how old a recent min rtt sample can be.
+ void set_recent_min_rtt_window(QuicTime::Delta recent_min_rtt_window) {
+ recent_min_rtt_window_ = recent_min_rtt_window;
+ }
+
+ private:
+ friend class test::RttStatsPeer;
+
+ // Used to track a sampled RTT window.
+ struct RttSample {
+ RttSample() : rtt(QuicTime::Delta::Zero()), time(QuicTime::Zero()) { }
+ RttSample(QuicTime::Delta rtt, QuicTime time) : rtt(rtt), time(time) { }
+
+ QuicTime::Delta rtt;
+ QuicTime time; // Time the rtt sample was recorded.
+ };
+
+ // Implements the resampling algorithm and the windowed min rtt algorithm.
+ void UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now);
+
+ QuicTime::Delta latest_rtt_;
+ QuicTime::Delta min_rtt_;
+ QuicTime::Delta smoothed_rtt_;
+ // Mean RTT deviation during this session.
+ // Approximation of standard deviation, the error is roughly 1.25 times
+ // larger than the standard deviation, for a normally distributed signal.
+ QuicTime::Delta mean_deviation_;
+ int64 initial_rtt_us_;
+
+ RttSample new_min_rtt_;
+ uint32 num_min_rtt_samples_remaining_;
+
+ // State variables for Kathleen Nichols MinRTT algorithm.
+ QuicTime::Delta recent_min_rtt_window_;
+ RttSample recent_min_rtt_; // a in the windowed algorithm.
+ RttSample half_window_rtt_; // b in the sampled algorithm.
+ RttSample quarter_window_rtt_; // c in the sampled algorithm.
+
+ DISALLOW_COPY_AND_ASSIGN(RttStats);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
diff --git a/chromium/net/quic/congestion_control/rtt_stats_test.cc b/chromium/net/quic/congestion_control/rtt_stats_test.cc
new file mode 100644
index 00000000000..f1c0fc1ce98
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats_test.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 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/congestion_control/rtt_stats.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RttStatsPeer {
+ public:
+ static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->half_window_rtt_.rtt;
+ }
+
+ static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->quarter_window_rtt_.rtt;
+ }
+};
+
+class RttStatsTest : public ::testing::Test {
+ protected:
+ RttStats rtt_stats_;
+};
+
+TEST_F(RttStatsTest, MinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(20)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(30)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(40)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, RecentMinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ rtt_stats_.SampleNewRecentMinRtt(4);
+ for (int i = 0; i < 3; ++i) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ }
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, WindowedRecentMinRtt) {
+ // Set the window to 99ms, so 25ms is more than a quarter rtt.
+ rtt_stats_.set_recent_min_rtt_window(QuicTime::Delta::FromMilliseconds(99));
+
+ QuicTime now = QuicTime::Zero();
+ QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ // Gradually increase the rtt samples and ensure the recent_min_rtt starts
+ // rising.
+ for (int i = 0; i < 8; ++i) {
+ now = now.Add(QuicTime::Delta::FromMilliseconds(25));
+ rtt_sample = rtt_sample.Add(QuicTime::Delta::FromMilliseconds(10));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(10)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ if (i < 3) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 5) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 7) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50),
+ rtt_stats_.recent_min_rtt());
+ } else {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+ }
+ }
+
+ // A new quarter rtt low sets that, but nothing else.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new half rtt low sets that and the quarter rtt low.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(15));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new full window loss sets the recent_min_rtt, but not min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(65);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+
+ // A new all time low sets both the min_rtt and the recent_min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(5);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+
+ EXPECT_EQ(rtt_sample, rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+}
+
+
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.cc b/chromium/net/quic/congestion_control/send_algorithm_interface.cc
index 493d84e88ef..58b92bce081 100644
--- a/chromium/net/quic/congestion_control/send_algorithm_interface.cc
+++ b/chromium/net/quic/congestion_control/send_algorithm_interface.cc
@@ -12,17 +12,26 @@ namespace net {
const bool kUseReno = false;
+class RttStats;
+
// Factory for send side congestion control algorithm.
SendAlgorithmInterface* SendAlgorithmInterface::Create(
const QuicClock* clock,
- CongestionFeedbackType type) {
+ const RttStats* rtt_stats,
+ CongestionFeedbackType type,
+ QuicConnectionStats* stats) {
switch (type) {
case kTCP:
- return new TcpCubicSender(clock, kUseReno, kMaxTcpCongestionWindow);
+ return new TcpCubicSender(clock, rtt_stats, kUseReno,
+ kMaxTcpCongestionWindow, stats);
case kInterArrival:
- break; // TODO(pwestin) Implement.
+ LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported.";
+ return NULL;
case kFixRate:
- return new FixRateSender(clock);
+ return new FixRateSender(rtt_stats);
+ case kTCPBBR:
+ LOG(DFATAL) << "BbrTcpSender is not supported.";
+ return NULL;
}
return NULL;
}
diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.h b/chromium/net/quic/congestion_control/send_algorithm_interface.h
index 87a5f9c03fc..6b1c75597f7 100644
--- a/chromium/net/quic/congestion_control/send_algorithm_interface.h
+++ b/chromium/net/quic/congestion_control/send_algorithm_interface.h
@@ -15,101 +15,68 @@
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_clock.h"
#include "net/quic/quic_config.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
namespace net {
+class RttStats;
+
class NET_EXPORT_PRIVATE SendAlgorithmInterface {
public:
- class SentPacket {
- public:
- SentPacket(QuicByteCount bytes,
- QuicTime timestamp,
- HasRetransmittableData has_retransmittable_data)
- : bytes_sent_(bytes),
- send_timestamp_(timestamp),
- has_retransmittable_data_(has_retransmittable_data),
- nack_count_(0) {
- }
- QuicByteCount bytes_sent() const { return bytes_sent_; }
- const QuicTime& send_timestamp() const { return send_timestamp_; }
- HasRetransmittableData has_retransmittable_data() const {
- return has_retransmittable_data_;
- }
- size_t nack_count() const { return nack_count_; }
-
- void Nack(size_t min_nacks) {
- nack_count_ = std::max(min_nacks, nack_count_ + 1);
- }
-
- private:
- QuicByteCount bytes_sent_;
- QuicTime send_timestamp_;
- HasRetransmittableData has_retransmittable_data_;
- size_t nack_count_;
- };
-
- typedef std::map<QuicPacketSequenceNumber, SentPacket*> SentPacketsMap;
+ typedef std::map<QuicPacketSequenceNumber, TransmissionInfo> CongestionMap;
static SendAlgorithmInterface* Create(const QuicClock* clock,
- CongestionFeedbackType type);
+ const RttStats* rtt_stats,
+ CongestionFeedbackType type,
+ QuicConnectionStats* stats);
virtual ~SendAlgorithmInterface() {}
virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0;
- // Sets the maximum size of packets that will be sent.
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) = 0;
-
// Called when we receive congestion feedback from remote peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) = 0;
-
- // Called for each received ACK, with sequence number from remote peer.
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) = 0;
-
- // Indicates a loss event of one packet. |sequence_number| is the
- // sequence number of the lost packet.
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- 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.
+ QuicTime feedback_receive_time) = 0;
+
+ // Indicates an update to the congestion state, caused either by an incoming
+ // ack or loss event timeout. |rtt_updated| indicates whether a new
+ // latest_rtt sample has been taken, |byte_in_flight| the bytes in flight
+ // prior to the congestion event. |acked_packets| and |lost_packets| are
+ // any packets considered acked or lost as a result of the congestion event.
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) = 0;
+
+ // Inform that we sent |bytes| to the wire, and if the packet is
+ // retransmittable. Returns true if the packet should be tracked by the
+ // congestion manager and included in bytes_in_flight, false otherwise.
+ // |bytes_in_flight| is the number of bytes in flight before the packet was
+ // sent.
// Note: this function must be called for every packet sent to the wire.
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) = 0;
- // Called when the retransmission timeout fires.
- virtual void OnRetransmissionTimeout() = 0;
-
- // Called when a packet is timed out.
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) = 0;
+ // Called when the retransmission timeout fires. Neither OnPacketAbandoned
+ // nor OnPacketLost will be called for these packets.
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0;
// Calculate the time until we can send the next packet.
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) = 0;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const = 0;
// What's the current estimated bandwidth in bytes per second.
// Returns 0 when it does not have an estimate.
virtual QuicBandwidth BandwidthEstimate() const = 0;
- // TODO(satyamshekhar): Monitor MinRtt.
- virtual QuicTime::Delta SmoothedRtt() const = 0;
-
// Get the send algorithm specific retransmission delay, called RTO in TCP,
// Note 1: the caller is responsible for sanity checking this value.
// Note 2: this will return zero if we don't have enough data for an estimate.
diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.cc b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc
new file mode 100644
index 00000000000..d539013c10e
--- /dev/null
+++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 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/congestion_control/send_algorithm_simulator.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/quic/crypto/quic_random.h"
+
+using std::list;
+using std::max;
+using std::min;
+
+namespace net {
+
+namespace {
+
+const QuicByteCount kPacketSize = 1200;
+
+} // namespace
+
+SendAlgorithmSimulator::SendAlgorithmSimulator(
+ SendAlgorithmInterface* send_algorithm,
+ MockClock* clock,
+ RttStats* rtt_stats,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt)
+ : send_algorithm_(send_algorithm),
+ clock_(clock),
+ rtt_stats_(rtt_stats),
+ next_sent_(1),
+ last_acked_(0),
+ next_acked_(1),
+ lose_next_ack_(false),
+ bytes_in_flight_(0),
+ forward_loss_rate_(0),
+ reverse_loss_rate_(0),
+ loss_correlation_(0),
+ bandwidth_(bandwidth),
+ rtt_(rtt),
+ buffer_size_(1000000),
+ max_cwnd_(0),
+ min_cwnd_(100000),
+ max_cwnd_drop_(0),
+ last_cwnd_(0) {
+ uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
+ DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed;
+ simple_random_.set_seed(seed);
+}
+
+SendAlgorithmSimulator::~SendAlgorithmSimulator() {}
+
+// Sends the specified number of bytes as quickly as possible and returns the
+// average bandwidth in bytes per second. The time elapsed is based on
+// waiting for all acks to arrive.
+QuicBandwidth SendAlgorithmSimulator::SendBytes(size_t num_bytes) {
+ const QuicTime start_time = clock_->Now();
+ size_t bytes_acked = 0;
+ while (bytes_acked < num_bytes) {
+ DVLOG(1) << "bytes_acked:" << bytes_acked << " bytes_in_flight_:"
+ << bytes_in_flight_ << " CWND(bytes):"
+ << send_algorithm_->GetCongestionWindow();
+ // Determine the times of next send and of the next ack arrival.
+ QuicTime::Delta send_delta = send_algorithm_->TimeUntilSend(
+ clock_->Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA);
+ // If we've already sent enough bytes, wait for them to be acked.
+ if (bytes_acked + bytes_in_flight_ >= num_bytes) {
+ send_delta = QuicTime::Delta::Infinite();
+ }
+ QuicTime::Delta ack_delta = NextAckDelta();
+ // If both times are infinite, fire a TLP.
+ if (ack_delta.IsInfinite() && send_delta.IsInfinite()) {
+ DVLOG(1) << "Both times are infinite, simulating a TLP.";
+ // TODO(ianswett): Use a more sophisticated TLP timer.
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ SendDataNow();
+ } else if (ack_delta < send_delta) {
+ DVLOG(1) << "Handling ack, advancing time:"
+ << ack_delta.ToMicroseconds() << "us";
+ // Ack data all the data up to ack time and lose any missing sequence
+ // numbers.
+ clock_->AdvanceTime(ack_delta);
+ bytes_acked += HandlePendingAck();
+ } else {
+ DVLOG(1) << "Sending, advancing time:"
+ << send_delta.ToMicroseconds() << "us";
+ clock_->AdvanceTime(send_delta);
+ SendDataNow();
+ }
+ RecordStats();
+ }
+ return QuicBandwidth::FromBytesAndTimeDelta(
+ num_bytes, clock_->Now().Subtract(start_time));
+}
+
+// NextAck takes into account packet loss in both forward and reverse
+// direction, as well as correlated losses. And it assumes the receiver acks
+// every other packet when there is no loss.
+QuicTime::Delta SendAlgorithmSimulator::NextAckDelta() {
+ if (sent_packets_.empty() || AllPacketsLost()) {
+ DVLOG(1) << "No outstanding packets to cause acks. sent_packets_.size():"
+ << sent_packets_.size();
+ return QuicTime::Delta::Infinite();
+ }
+
+ // If necessary, determine next_acked_.
+ // This is only done once to ensure multiple calls return the same time.
+ FindNextAcked();
+
+ // If only one packet is acked, simulate a delayed ack.
+ if (next_acked_ - last_acked_ == 1) {
+ return sent_packets_.front().ack_time.Add(
+ QuicTime::Delta::FromMilliseconds(100)).Subtract(clock_->Now());
+ }
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ if (next_acked_ == it->sequence_number) {
+ return it->ack_time.Subtract(clock_->Now());
+ }
+ }
+ LOG(DFATAL) << "Error, next_acked_: " << next_acked_
+ << " should have been found in sent_packets_";
+ return QuicTime::Delta::Infinite();
+}
+
+bool SendAlgorithmSimulator::AllPacketsLost() {
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ if (it->ack_time.IsInitialized()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SendAlgorithmSimulator::FindNextAcked() {
+ // TODO(ianswett): Add a simpler mode which acks every packet.
+ bool packets_lost = false;
+ if (next_acked_ == last_acked_) {
+ // Determine if the next ack is lost only once, to ensure determinism.
+ lose_next_ack_ =
+ reverse_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ }
+ bool two_acks_remaining = lose_next_ack_;
+ next_acked_ = last_acked_;
+ // Remove any packets that are simulated as lost.
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ // Lost packets don't trigger an ack.
+ if (it->ack_time == QuicTime::Zero()) {
+ packets_lost = true;
+ continue;
+ }
+ // Buffer dropped packets are skipped automatically, but still end up
+ // being lost and cause acks to be sent immediately.
+ if (next_acked_ < it->sequence_number - 1) {
+ packets_lost = true;
+ }
+ next_acked_ = it->sequence_number;
+ if (packets_lost || (next_acked_ - last_acked_) % 2 == 0) {
+ if (two_acks_remaining) {
+ two_acks_remaining = false;
+ } else {
+ break;
+ }
+ }
+ }
+ DVLOG(1) << "FindNextAcked found next_acked_:" << next_acked_
+ << " last_acked:" << last_acked_;
+}
+
+int SendAlgorithmSimulator::HandlePendingAck() {
+ DCHECK_LT(last_acked_, next_acked_);
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ // Some entries may be missing from the sent_packets_ array, if they were
+ // dropped due to buffer overruns.
+ SentPacket largest_observed = sent_packets_.front();
+ while (last_acked_ < next_acked_) {
+ ++last_acked_;
+ TransmissionInfo info = TransmissionInfo();
+ info.bytes_sent = kPacketSize;
+ info.in_flight = true;
+ // If it's missing from the array, it's a loss.
+ if (sent_packets_.front().sequence_number > last_acked_) {
+ DVLOG(1) << "Lost packet:" << last_acked_
+ << " dropped by buffer overflow.";
+ lost_packets[last_acked_] = info;
+ continue;
+ }
+ if (sent_packets_.front().ack_time.IsInitialized()) {
+ acked_packets[last_acked_] = info;
+ } else {
+ lost_packets[last_acked_] = info;
+ }
+ // Remove all packets from the front to next_acked_.
+ largest_observed = sent_packets_.front();
+ sent_packets_.pop_front();
+ }
+
+ DCHECK(largest_observed.ack_time.IsInitialized());
+ rtt_stats_->UpdateRtt(
+ largest_observed.ack_time.Subtract(largest_observed.send_time),
+ QuicTime::Delta::Zero(),
+ clock_->Now());
+ send_algorithm_->OnCongestionEvent(
+ true, bytes_in_flight_, acked_packets, lost_packets);
+ DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()),
+ bytes_in_flight_);
+ bytes_in_flight_ -=
+ kPacketSize * (acked_packets.size() + lost_packets.size());
+ return acked_packets.size() * kPacketSize;
+}
+
+void SendAlgorithmSimulator::SendDataNow() {
+ DVLOG(1) << "Sending packet:" << next_sent_ << " bytes_in_flight:"
+ << bytes_in_flight_;
+ send_algorithm_->OnPacketSent(
+ clock_->Now(), bytes_in_flight_,
+ next_sent_, kPacketSize, HAS_RETRANSMITTABLE_DATA);
+ // Lose the packet immediately if the buffer is full.
+ if (sent_packets_.size() * kPacketSize < buffer_size_) {
+ // TODO(ianswett): This buffer simulation is an approximation.
+ // An ack time of zero means loss.
+ bool packet_lost =
+ forward_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ // Handle correlated loss.
+ if (!sent_packets_.empty() &&
+ !sent_packets_.back().ack_time.IsInitialized() &&
+ loss_correlation_ * kuint64max > simple_random_.RandUint64()) {
+ packet_lost = true;
+ }
+
+ QuicTime ack_time = clock_->Now().Add(rtt_);
+ // If the number of bytes in flight are less than the bdp, there's
+ // no buffering delay. Bytes lost from the buffer are not counted.
+ QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_);
+ if (sent_packets_.size() * kPacketSize > bdp) {
+ QuicByteCount qsize = sent_packets_.size() * kPacketSize - bdp;
+ ack_time = ack_time.Add(bandwidth_.TransferTime(qsize));
+ }
+ // If the packet is lost, give it an ack time of Zero.
+ sent_packets_.push_back(SentPacket(
+ next_sent_, clock_->Now(), packet_lost ? QuicTime::Zero() : ack_time));
+ }
+ ++next_sent_;
+ bytes_in_flight_ += kPacketSize;
+}
+
+void SendAlgorithmSimulator::RecordStats() {
+ QuicByteCount cwnd = send_algorithm_->GetCongestionWindow();
+ max_cwnd_ = max(max_cwnd_, cwnd);
+ min_cwnd_ = min(min_cwnd_, cwnd);
+ if (last_cwnd_ > cwnd) {
+ max_cwnd_drop_ = max(max_cwnd_drop_, last_cwnd_ - cwnd);
+ }
+ last_cwnd_ = cwnd;
+}
+
+// Advance the time by |delta| without sending anything.
+void SendAlgorithmSimulator::AdvanceTime(QuicTime::Delta delta) {
+ clock_->AdvanceTime(delta);
+}
+
+// Elapsed time from the start of the connection.
+QuicTime SendAlgorithmSimulator::ElapsedTime() {
+ return clock_->Now();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.h b/chromium/net/quic/congestion_control/send_algorithm_simulator.h
new file mode 100644
index 00000000000..691b0c82dbd
--- /dev/null
+++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.h
@@ -0,0 +1,133 @@
+// Copyright 2014 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.
+
+// A test only class to enable simulations of send algorithms.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+
+class SendAlgorithmSimulator {
+ public:
+ struct SentPacket {
+ SentPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ QuicTime ack_time)
+ : sequence_number(sequence_number),
+ send_time(send_time),
+ ack_time(ack_time) {}
+ QuicPacketSequenceNumber sequence_number;
+ QuicTime send_time;
+ QuicTime ack_time;
+ };
+
+ // |rtt_stats| should be the same RttStats used by the |send_algorithm|.
+ SendAlgorithmSimulator(SendAlgorithmInterface* send_algorithm,
+ MockClock* clock_,
+ RttStats* rtt_stats,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt);
+ ~SendAlgorithmSimulator();
+
+ void set_forward_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ forward_loss_rate_ = loss_rate;
+ }
+
+ void set_reverse_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ reverse_loss_rate_ = loss_rate;
+ }
+
+ void set_loss_correlation(float loss_correlation) {
+ DCHECK_LT(loss_correlation, 1.0f);
+ loss_correlation_ = loss_correlation;
+ }
+
+ void set_buffer_size(size_t buffer_size_bytes) {
+ buffer_size_ = buffer_size_bytes;
+ }
+
+ // Sends the specified number of bytes as quickly as possible and returns the
+ // average bandwidth in bytes per second. The time elapsed is based on
+ // waiting for all acks to arrive.
+ QuicBandwidth SendBytes(size_t num_bytes);
+
+ const RttStats* rtt_stats() const { return rtt_stats_; }
+
+ QuicByteCount max_cwnd() const { return max_cwnd_; }
+ QuicByteCount min_cwnd() const { return min_cwnd_; }
+ QuicByteCount max_cwnd_drop() const { return max_cwnd_drop_; }
+ QuicByteCount last_cwnd() const { return last_cwnd_; }
+
+ private:
+ // NextAckTime takes into account packet loss in both forward and reverse
+ // direction, as well as delayed ack behavior.
+ QuicTime::Delta NextAckDelta();
+
+ // Whether all packets in sent_packets_ are lost.
+ bool AllPacketsLost();
+
+ // Sets the next acked.
+ void FindNextAcked();
+
+ // Process all the acks that should have arrived by the current time, and
+ // lose any packets that are missing. Returns the number of bytes acked.
+ int HandlePendingAck();
+
+ void SendDataNow();
+ void RecordStats();
+
+ // Advance the time by |delta| without sending anything.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ // Elapsed time from the start of the connection.
+ QuicTime ElapsedTime();
+
+ SendAlgorithmInterface* send_algorithm_;
+ MockClock* clock_;
+ RttStats* rtt_stats_;
+ // Next packet sequence number to send.
+ QuicPacketSequenceNumber next_sent_;
+ // Last packet sequence number acked.
+ QuicPacketSequenceNumber last_acked_;
+ // Packet sequence number to ack up to.
+ QuicPacketSequenceNumber next_acked_;
+ // Whether the next ack should be lost.
+ bool lose_next_ack_;
+ QuicByteCount bytes_in_flight_;
+ // The times acks are expected, assuming acks are not lost and every packet
+ // is acked.
+ std::list<SentPacket> sent_packets_;
+
+ test::SimpleRandom simple_random_;
+ float forward_loss_rate_; // Loss rate on the forward path.
+ float reverse_loss_rate_; // Loss rate on the reverse path.
+ float loss_correlation_; // Likelihood the subsequent packet is lost.
+ QuicBandwidth bandwidth_;
+ QuicTime::Delta rtt_;
+ size_t buffer_size_; // In bytes.
+
+ // Stats collected for understanding the congestion control.
+ QuicByteCount max_cwnd_;
+ QuicByteCount min_cwnd_;
+ QuicByteCount max_cwnd_drop_;
+ QuicByteCount last_cwnd_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
index 50fc3937b99..19f07d40e55 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -7,8 +7,10 @@
#include <algorithm>
#include "base/metrics/histogram.h"
+#include "net/quic/congestion_control/rtt_stats.h"
using std::max;
+using std::min;
namespace net {
@@ -17,311 +19,306 @@ namespace {
// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
// fast retransmission. The cwnd after a timeout is still 1.
const QuicTcpCongestionWindow kMinimumCongestionWindow = 2;
-const int64 kHybridStartLowWindow = 16;
const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
const QuicByteCount kDefaultReceiveWindow = 64000;
const int64 kInitialCongestionWindow = 10;
const int kMaxBurstLength = 3;
-// Constants used for RTT calculation.
-const int kInitialRttMs = 60; // At a typical RTT 60 ms.
-const float kAlpha = 0.125f;
-const float kOneMinusAlpha = (1 - kAlpha);
-const float kBeta = 0.25f;
-const float kOneMinusBeta = (1 - kBeta);
}; // namespace
TcpCubicSender::TcpCubicSender(
const QuicClock* clock,
+ const RttStats* rtt_stats,
bool reno,
- QuicTcpCongestionWindow max_tcp_congestion_window)
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats)
: hybrid_slow_start_(clock),
- cubic_(clock),
+ cubic_(clock, stats),
+ rtt_stats_(rtt_stats),
+ stats_(stats),
reno_(reno),
congestion_window_count_(0),
receive_window_(kDefaultReceiveWindow),
- last_received_accumulated_number_of_lost_packets_(0),
- bytes_in_flight_(0),
- update_end_sequence_number_(true),
- end_sequence_number_(0),
+ prr_out_(0),
+ prr_delivered_(0),
+ ack_count_since_loss_(0),
+ bytes_in_flight_before_loss_(0),
largest_sent_sequence_number_(0),
largest_acked_sequence_number_(0),
largest_sent_at_last_cutback_(0),
congestion_window_(kInitialCongestionWindow),
slowstart_threshold_(max_tcp_congestion_window),
- max_tcp_congestion_window_(max_tcp_congestion_window),
- delay_min_(QuicTime::Delta::Zero()),
- smoothed_rtt_(QuicTime::Delta::Zero()),
- mean_deviation_(QuicTime::Delta::Zero()) {
+ last_cutback_exited_slowstart_(false),
+ max_tcp_congestion_window_(max_tcp_congestion_window) {
}
TcpCubicSender::~TcpCubicSender() {
UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_);
}
-void TcpCubicSender::SetMaxPacketSize(QuicByteCount /*max_packet_size*/) {
+bool TcpCubicSender::InSlowStart() const {
+ return congestion_window_ < slowstart_threshold_;
}
void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) {
- if (is_server) {
+ if (is_server && config.HasReceivedInitialCongestionWindow()) {
// Set the initial window size.
- congestion_window_ = config.server_initial_congestion_window();
+ congestion_window_ = min(kMaxInitialWindow,
+ config.ReceivedInitialCongestionWindow());
}
}
void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& /*sent_packets*/) {
- if (last_received_accumulated_number_of_lost_packets_ !=
- feedback.tcp.accumulated_number_of_lost_packets) {
- int recovered_lost_packets =
- last_received_accumulated_number_of_lost_packets_ -
- feedback.tcp.accumulated_number_of_lost_packets;
- last_received_accumulated_number_of_lost_packets_ =
- feedback.tcp.accumulated_number_of_lost_packets;
- if (recovered_lost_packets > 0) {
- // Assume the loss could be as late as the last acked packet.
- OnPacketLost(largest_acked_sequence_number_, feedback_receive_time);
- }
- }
+ QuicTime feedback_receive_time) {
receive_window_ = feedback.tcp.receive_window;
}
+void TcpCubicSender::OnCongestionEvent(
+ bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
+ if (rtt_updated && InSlowStart() &&
+ hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(),
+ rtt_stats_->min_rtt(),
+ congestion_window_)) {
+ slowstart_threshold_ = congestion_window_;
+ }
+ for (CongestionMap::const_iterator it = lost_packets.begin();
+ it != lost_packets.end(); ++it) {
+ OnPacketLost(it->first, bytes_in_flight);
+ }
+ for (CongestionMap::const_iterator it = acked_packets.begin();
+ it != acked_packets.end(); ++it) {
+ OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight);
+ }
+}
+
void TcpCubicSender::OnPacketAcked(
QuicPacketSequenceNumber acked_sequence_number,
QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- DCHECK_GE(bytes_in_flight_, acked_bytes);
- bytes_in_flight_ -= acked_bytes;
+ QuicByteCount bytes_in_flight) {
largest_acked_sequence_number_ = max(acked_sequence_number,
largest_acked_sequence_number_);
- CongestionAvoidance(acked_sequence_number);
- AckAccounting(rtt);
- if (end_sequence_number_ == acked_sequence_number) {
- DVLOG(1) << "Start update end sequence number @" << acked_sequence_number;
- update_end_sequence_number_ = true;
+ if (InRecovery()) {
+ PrrOnPacketAcked(acked_bytes);
+ return;
}
+ MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight);
+ // TODO(ianswett): Should this even be called when not in slow start?
+ hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart());
}
void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime /*ack_receive_time*/) {
+ QuicByteCount bytes_in_flight) {
// TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
// already sent should be treated as a single loss event, since it's expected.
if (sequence_number <= largest_sent_at_last_cutback_) {
+ if (last_cutback_exited_slowstart_) {
+ ++stats_->slowstart_packets_lost;
+ }
DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number
- << " because it was sent prior to the last CWND cutback.";
+ << " because it was sent prior to the last CWND cutback.";
return;
}
+ ++stats_->tcp_loss_events;
+ last_cutback_exited_slowstart_ = InSlowStart();
+ if (InSlowStart()) {
+ ++stats_->slowstart_packets_lost;
+ }
+ PrrOnPacketLost(bytes_in_flight);
- // In a normal TCP we would need to know the lowest missing packet to detect
- // if we receive 3 missing packets. Here we get a missing packet for which we
- // enter TCP Fast Retransmit immediately.
if (reno_) {
congestion_window_ = congestion_window_ >> 1;
- slowstart_threshold_ = congestion_window_;
} else {
congestion_window_ =
cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
- slowstart_threshold_ = congestion_window_;
}
- // Enforce TCP's minimimum congestion window of 2*MSS.
+ slowstart_threshold_ = congestion_window_;
+ // Enforce TCP's minimum congestion window of 2*MSS.
if (congestion_window_ < kMinimumCongestionWindow) {
congestion_window_ = kMinimumCongestionWindow;
}
largest_sent_at_last_cutback_ = largest_sent_sequence_number_;
- DVLOG(1) << "Incoming loss; congestion window:" << congestion_window_;
+ // reset packet count from congestion avoidance mode. We start
+ // counting again when we're out of recovery.
+ congestion_window_count_ = 0;
+ DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
}
bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/,
+ QuicByteCount /*bytes_in_flight*/,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) {
// Only update bytes_in_flight_ for data packets.
if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
return false;
}
- bytes_in_flight_ += bytes;
+ prr_out_ += bytes;
if (largest_sent_sequence_number_ < sequence_number) {
// TODO(rch): Ensure that packets are really sent in order.
// DCHECK_LT(largest_sent_sequence_number_, sequence_number);
largest_sent_sequence_number_ = sequence_number;
}
- if (transmission_type == NOT_RETRANSMISSION && update_end_sequence_number_) {
- end_sequence_number_ = sequence_number;
- if (AvailableSendWindow() == 0) {
- update_end_sequence_number_ = false;
- DVLOG(1) << "Stop update end sequence number @" << sequence_number;
- }
- }
+ hybrid_slow_start_.OnPacketSent(sequence_number);
return true;
}
-void TcpCubicSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) {
- DCHECK_GE(bytes_in_flight_, abandoned_bytes);
- bytes_in_flight_ -= abandoned_bytes;
-}
-
QuicTime::Delta TcpCubicSender::TimeUntilSend(
QuicTime /* now */,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) {
- if (transmission_type == NACK_RETRANSMISSION ||
- has_retransmittable_data == NO_RETRANSMITTABLE_DATA ||
- handshake == IS_HANDSHAKE) {
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
+ if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) {
// For TCP we can always send an ACK immediately.
- // We also immediately send any handshake packet (CHLO, etc.). We provide
- // this special dispensation for handshake messages in QUIC, although the
- // concept is not present in TCP.
return QuicTime::Delta::Zero();
}
- if (AvailableSendWindow() == 0) {
- return QuicTime::Delta::Infinite();
+ if (InRecovery()) {
+ return PrrTimeUntilSend(bytes_in_flight);
}
- return QuicTime::Delta::Zero();
-}
-
-QuicByteCount TcpCubicSender::AvailableSendWindow() {
- if (bytes_in_flight_ > SendWindow()) {
- return 0;
+ if (SendWindow() > bytes_in_flight) {
+ return QuicTime::Delta::Zero();
}
- return SendWindow() - bytes_in_flight_;
+ return QuicTime::Delta::Infinite();
}
-QuicByteCount TcpCubicSender::SendWindow() {
+QuicByteCount TcpCubicSender::SendWindow() const {
// What's the current send window in bytes.
- return std::min(receive_window_, GetCongestionWindow());
+ return min(receive_window_, GetCongestionWindow());
}
QuicBandwidth TcpCubicSender::BandwidthEstimate() const {
return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(),
- SmoothedRtt());
-}
-
-QuicTime::Delta TcpCubicSender::SmoothedRtt() const {
- if (smoothed_rtt_.IsZero()) {
- return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
- }
- return smoothed_rtt_;
+ rtt_stats_->SmoothedRtt());
}
QuicTime::Delta TcpCubicSender::RetransmissionDelay() const {
+ if (!rtt_stats_->HasUpdates()) {
+ return QuicTime::Delta::Zero();
+ }
return QuicTime::Delta::FromMicroseconds(
- smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds());
+ rtt_stats_->SmoothedRtt().ToMicroseconds() +
+ 4 * rtt_stats_->mean_deviation().ToMicroseconds());
}
QuicByteCount TcpCubicSender::GetCongestionWindow() const {
return congestion_window_ * kMaxSegmentSize;
}
-void TcpCubicSender::Reset() {
- delay_min_ = QuicTime::Delta::Zero();
- hybrid_slow_start_.Restart();
-}
-
-bool TcpCubicSender::IsCwndLimited() const {
+bool TcpCubicSender::IsCwndLimited(QuicByteCount bytes_in_flight) const {
const QuicByteCount congestion_window_bytes = congestion_window_ *
kMaxSegmentSize;
- if (bytes_in_flight_ >= congestion_window_bytes) {
+ if (bytes_in_flight >= congestion_window_bytes) {
return true;
}
- const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize;
- const QuicByteCount left = congestion_window_bytes - bytes_in_flight_;
- return left <= tcp_max_burst;
+ const QuicByteCount max_burst = kMaxBurstLength * kMaxSegmentSize;
+ const QuicByteCount available_bytes =
+ congestion_window_bytes - bytes_in_flight;
+ const bool slow_start_limited = InSlowStart() &&
+ bytes_in_flight > congestion_window_bytes / 2;
+ return slow_start_limited || available_bytes <= max_burst;
+}
+
+bool TcpCubicSender::InRecovery() const {
+ return largest_acked_sequence_number_ <= largest_sent_at_last_cutback_ &&
+ largest_acked_sequence_number_ != 0;
}
// Called when we receive an ack. Normal TCP tracks how many packets one ack
// represents, but quic has a separate ack for each packet.
-void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) {
- if (!IsCwndLimited()) {
+void TcpCubicSender::MaybeIncreaseCwnd(
+ QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight) {
+ LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery.";
+ if (!IsCwndLimited(bytes_in_flight)) {
// We don't update the congestion window unless we are close to using the
// window we have available.
return;
}
- if (congestion_window_ < slowstart_threshold_) {
- // Slow start.
- if (hybrid_slow_start_.EndOfRound(ack)) {
- hybrid_slow_start_.Reset(end_sequence_number_);
- }
+ if (InSlowStart()) {
// congestion_window_cnt is the number of acks since last change of snd_cwnd
if (congestion_window_ < max_tcp_congestion_window_) {
// TCP slow start, exponential growth, increase by one for each ACK.
- congestion_window_++;
+ ++congestion_window_;
}
- DVLOG(1) << "Slow start; congestion window:" << congestion_window_;
- } else {
- if (congestion_window_ < max_tcp_congestion_window_) {
- if (reno_) {
- // Classic Reno congestion avoidance provided for testing.
- if (congestion_window_count_ >= congestion_window_) {
- congestion_window_++;
- congestion_window_count_ = 0;
- } else {
- congestion_window_count_++;
- }
- DVLOG(1) << "Reno; congestion window:" << congestion_window_;
- } else {
- congestion_window_ = std::min(
- max_tcp_congestion_window_,
- cubic_.CongestionWindowAfterAck(congestion_window_, delay_min_));
- DVLOG(1) << "Cubic; congestion window:" << congestion_window_;
- }
+ DVLOG(1) << "Slow start; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ return;
+ }
+ if (congestion_window_ >= max_tcp_congestion_window_) {
+ return;
+ }
+ // Congestion avoidance
+ if (reno_) {
+ // Classic Reno congestion avoidance provided for testing.
+
+ ++congestion_window_count_;
+ if (congestion_window_count_ >= congestion_window_) {
+ ++congestion_window_;
+ congestion_window_count_ = 0;
}
+
+ DVLOG(1) << "Reno; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_
+ << " congestion window count: " << congestion_window_count_;
+ } else {
+ congestion_window_ = min(max_tcp_congestion_window_,
+ cubic_.CongestionWindowAfterAck(
+ congestion_window_, rtt_stats_->min_rtt()));
+ DVLOG(1) << "Cubic; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
}
}
-void TcpCubicSender::OnRetransmissionTimeout() {
- cubic_.Reset();
- congestion_window_ = kMinimumCongestionWindow;
+void TcpCubicSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ largest_sent_at_last_cutback_ = 0;
+ if (packets_retransmitted) {
+ cubic_.Reset();
+ hybrid_slow_start_.Restart();
+ congestion_window_ = kMinimumCongestionWindow;
+ }
}
-void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) {
- if (rtt.IsInfinite() || rtt.IsZero()) {
- DVLOG(1) << "Ignoring rtt, because it's "
- << (rtt.IsZero() ? "Zero" : "Infinite");
- return;
- }
- // RTT can't be negative.
- DCHECK_LT(0, rtt.ToMicroseconds());
+void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) {
+ prr_out_ = 0;
+ bytes_in_flight_before_loss_ = bytes_in_flight;
+ prr_delivered_ = 0;
+ ack_count_since_loss_ = 0;
+}
- // TODO(pwestin): Discard delay samples right after fast recovery,
- // during 1 second?.
+void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) {
+ prr_delivered_ += acked_bytes;
+ ++ack_count_since_loss_;
+}
- // First time call or link delay decreases.
- if (delay_min_.IsZero() || delay_min_ > rtt) {
- delay_min_ = rtt;
- }
- // First time call.
- if (smoothed_rtt_.IsZero()) {
- smoothed_rtt_ = rtt;
- mean_deviation_ = QuicTime::Delta::FromMicroseconds(
- rtt.ToMicroseconds() / 2);
- } else {
- mean_deviation_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusBeta * mean_deviation_.ToMicroseconds() +
- kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds()));
- smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
- kAlpha * rtt.ToMicroseconds());
- DVLOG(1) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds()
- << " mean_deviation_:" << mean_deviation_.ToMicroseconds();
+QuicTime::Delta TcpCubicSender::PrrTimeUntilSend(
+ QuicByteCount bytes_in_flight) const {
+ DCHECK(InRecovery());
+ // Return QuicTime::Zero In order to ensure limited transmit always works.
+ if (prr_out_ == 0) {
+ return QuicTime::Delta::Zero();
}
-
- // Hybrid start triggers when cwnd is larger than some threshold.
- if (congestion_window_ <= slowstart_threshold_ &&
- congestion_window_ >= kHybridStartLowWindow) {
- if (!hybrid_slow_start_.started()) {
- // Time to start the hybrid slow start.
- hybrid_slow_start_.Reset(end_sequence_number_);
- }
- hybrid_slow_start_.Update(rtt, delay_min_);
- if (hybrid_slow_start_.Exit()) {
- slowstart_threshold_ = congestion_window_;
+ if (SendWindow() > bytes_in_flight) {
+ // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead
+ // of sending the entire available window. This prevents burst retransmits
+ // when more packets are lost than the CWND reduction.
+ // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS
+ if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) {
+ return QuicTime::Delta::Infinite();
}
+ return QuicTime::Delta::Zero();
+ }
+ // Implement Proportional Rate Reduction (RFC6937)
+ // Checks a simplified version of the PRR formula that doesn't use division:
+ // AvailableSendWindow =
+ // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent
+ if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize >
+ prr_out_ * bytes_in_flight_before_loss_) {
+ return QuicTime::Delta::Zero();
}
+ return QuicTime::Delta::Infinite();
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.h b/chromium/net/quic/congestion_control/tcp_cubic_sender.h
index f026fd56b3f..f9234c92dcc 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender.h
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.h
@@ -15,13 +15,13 @@
#include "net/quic/congestion_control/hybrid_slow_start.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
namespace net {
-// Default maximum packet size used in Linux TCP implementations.
-const QuicByteCount kDefaultTCPMSS = 1460;
+class RttStats;
namespace test {
class TcpCubicSenderPeer;
@@ -31,37 +31,34 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
public:
// Reno option and max_tcp_congestion_window are provided for testing.
TcpCubicSender(const QuicClock* clock,
+ const RttStats* rtt_stats,
bool reno,
- QuicTcpCongestionWindow max_tcp_congestion_window);
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats);
virtual ~TcpCubicSender();
+ bool InSlowStart() const;
+
// Start implementation of SendAlgorithmInterface.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber largest_loss,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
// End implementation of SendAlgorithmInterface.
@@ -69,16 +66,28 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
private:
friend class test::TcpCubicSenderPeer;
- QuicByteCount AvailableSendWindow();
- QuicByteCount SendWindow();
- void Reset();
- void AckAccounting(QuicTime::Delta rtt);
- void CongestionAvoidance(QuicPacketSequenceNumber ack);
- bool IsCwndLimited() const;
- void OnTimeOut();
+ // TODO(ianswett): Remove these and migrate to OnCongestionEvent.
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount bytes_in_flight);
+ void OnPacketLost(QuicPacketSequenceNumber largest_loss,
+ QuicByteCount bytes_in_flight);
+
+ QuicByteCount SendWindow() const;
+ void MaybeIncreaseCwnd(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight);
+ bool IsCwndLimited(QuicByteCount bytes_in_flight) const;
+ bool InRecovery() const;
+ // Methods for isolating PRR from the rest of TCP Cubic.
+ void PrrOnPacketLost(QuicByteCount bytes_in_flight);
+ void PrrOnPacketAcked(QuicByteCount acked_bytes);
+ QuicTime::Delta PrrTimeUntilSend(QuicByteCount bytes_in_flight) const;
+
HybridSlowStart hybrid_slow_start_;
Cubic cubic_;
+ const RttStats* rtt_stats_;
+ QuicConnectionStats* stats_;
// Reno provided for testing.
const bool reno_;
@@ -89,15 +98,13 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
// Receiver side advertised window.
QuicByteCount receive_window_;
- // Receiver side advertised packet loss.
- int last_received_accumulated_number_of_lost_packets_;
-
- // Bytes in flight, aka bytes on the wire.
- QuicByteCount bytes_in_flight_;
+ // Bytes sent and acked since the last loss event. Used for PRR.
+ QuicByteCount prr_out_;
+ QuicByteCount prr_delivered_;
+ size_t ack_count_since_loss_;
- // We need to keep track of the end sequence number of each RTT "burst".
- bool update_end_sequence_number_;
- QuicPacketSequenceNumber end_sequence_number_;
+ // The congestion window before the last loss event.
+ QuicByteCount bytes_in_flight_before_loss_;
// Track the largest packet that has been sent.
QuicPacketSequenceNumber largest_sent_sequence_number_;
@@ -111,23 +118,16 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
// Congestion window in packets.
QuicTcpCongestionWindow congestion_window_;
- // Slow start congestion window in packets.
+ // Slow start congestion window in packets, aka ssthresh.
QuicTcpCongestionWindow slowstart_threshold_;
+ // Whether the last loss event caused us to exit slowstart.
+ // Used for stats collection of slowstart_packets_lost
+ bool last_cutback_exited_slowstart_;
+
// Maximum number of outstanding packets for tcp.
QuicTcpCongestionWindow max_tcp_congestion_window_;
- // Min RTT during this session.
- QuicTime::Delta delay_min_;
-
- // Smoothed RTT during this session.
- QuicTime::Delta smoothed_rtt_;
-
- // Mean RTT deviation during this session.
- // Approximation of standard deviation, the error is roughly 1.25 times
- // larger than the standard deviation, for a normally distributed signal.
- QuicTime::Delta mean_deviation_;
-
DISALLOW_COPY_AND_ASSIGN(TcpCubicSender);
};
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
index 20dd6a6d316..c5d991a2a30 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -2,14 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/quic/congestion_control/tcp_cubic_sender.h"
+#include <algorithm>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "testing/gtest/include/gtest/gtest.h"
+using std::min;
+
namespace net {
namespace test {
@@ -23,107 +29,176 @@ class TcpCubicSenderPeer : public TcpCubicSender {
TcpCubicSenderPeer(const QuicClock* clock,
bool reno,
QuicTcpCongestionWindow max_tcp_congestion_window)
- : TcpCubicSender(clock, reno, max_tcp_congestion_window) {
+ : TcpCubicSender(
+ clock, &rtt_stats_, reno, max_tcp_congestion_window, &stats_) {
}
QuicTcpCongestionWindow congestion_window() {
return congestion_window_;
}
- using TcpCubicSender::AvailableSendWindow;
+ const HybridSlowStart& hybrid_slow_start() const {
+ return hybrid_slow_start_;
+ }
+
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+
using TcpCubicSender::SendWindow;
- using TcpCubicSender::AckAccounting;
};
class TcpCubicSenderTest : public ::testing::Test {
protected:
TcpCubicSenderTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(60)),
- one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
sender_(new TcpCubicSenderPeer(&clock_, true,
kDefaultMaxCongestionWindowTCP)),
receiver_(new TcpReceiver()),
sequence_number_(1),
- acked_sequence_number_(0) {
+ acked_sequence_number_(0),
+ bytes_in_flight_(0) {
+ standard_packet_.bytes_sent = kDefaultTCPMSS;
}
- void SendAvailableSendWindow() {
- QuicByteCount bytes_to_send = sender_->AvailableSendWindow();
- while (bytes_to_send > 0) {
- QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, bytes_to_send);
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- bytes_to_send -= bytes_in_packet;
- if (bytes_to_send > 0) {
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
+ int SendAvailableSendWindow() {
+ // Send as long as TimeUntilSend returns Zero.
+ int packets_sent = 0;
+ bool can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
+ while (can_send) {
+ sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, sequence_number_++,
+ kDefaultTCPMSS, HAS_RETRANSMITTABLE_DATA);
+ ++packets_sent;
+ bytes_in_flight_ += kDefaultTCPMSS;
+ can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
}
+ return packets_sent;
}
+
// Normal is that TCP acks every other segment.
void AckNPackets(int n) {
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_sequence_number_;
+ acked_packets[acked_sequence_number_] = standard_packet_;
+ }
+ sender_->OnCongestionEvent(
+ true, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ clock_.AdvanceTime(one_ms_);
+ }
+
+ void LoseNPackets(int n) {
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
for (int i = 0; i < n; ++i) {
- acked_sequence_number_++;
- sender_->OnPacketAcked(acked_sequence_number_, kDefaultTCPMSS, rtt_);
+ ++acked_sequence_number_;
+ lost_packets[acked_sequence_number_] = standard_packet_;
}
- clock_.AdvanceTime(one_ms_); // 1 millisecond.
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ }
+
+ // Does not increment acked_sequence_number_.
+ void LosePacket(QuicPacketSequenceNumber sequence_number) {
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ lost_packets[sequence_number] = standard_packet_;
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= kDefaultTCPMSS;
}
- const QuicTime::Delta rtt_;
const QuicTime::Delta one_ms_;
MockClock clock_;
- SendAlgorithmInterface::SentPacketsMap not_used_;
scoped_ptr<TcpCubicSenderPeer> sender_;
scoped_ptr<TcpReceiver> receiver_;
QuicPacketSequenceNumber sequence_number_;
QuicPacketSequenceNumber acked_sequence_number_;
+ QuicByteCount bytes_in_flight_;
+ TransmissionInfo standard_packet_;
};
TEST_F(TcpCubicSenderTest, SimpleSender) {
QuicCongestionFeedbackFrame feedback;
// At startup make sure we are at the default.
- EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow());
EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
// At startup make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
// Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// And that window is un-affected.
- EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow());
EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
- // A retransmit should always return 0.
+ // Fill the send window with data, then verify that we can't send.
+ SendAvailableSendWindow();
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ sender_->GetCongestionWindow(),
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+}
+
+TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) {
+ // Send exactly 10 packets and ensure the CWND ends at 14 packets.
+ const int kNumberOfAcks = 5;
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+ // Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NACK_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ SendAvailableSendWindow();
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->SendWindow();
+ // It's expected 2 acks will arrive when the bytes_in_flight are greater than
+ // half the CWND.
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2,
+ bytes_to_send);
}
TEST_F(TcpCubicSenderTest, ExponentialSlowStart) {
- const int kNumberOfAck = 20;
+ const int kNumberOfAcks = 20;
QuicCongestionFeedbackFrame feedback;
// At startup make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
// Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
- for (int n = 0; n < kNumberOfAck; ++n) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount bytes_to_send = sender_->SendWindow();
- EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAck,
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks,
bytes_to_send);
}
@@ -133,123 +208,243 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) {
// Ack2Packets in one round.
// Since we start at 10 packet first round will be 5 second round 10 etc
// Hence we should pass 30 at 65 = 5 + 10 + 20 + 30
- const int kNumberOfAck = 65;
+ const int kNumberOfAcks = 65;
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int n = 0; n < kNumberOfAck; ++n) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount expected_send_window =
- kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAck);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
- // We should now have fallen out of slow start.
- SendAvailableSendWindow();
- AckNPackets(2);
- expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ // We should now have fallen out of slow start.
// Testing Reno phase.
- // We should need 141(65*2+1+10) ACK:ed packets before increasing window by
+ // We should need 140(65*2+10) ACK:ed packets before increasing window by
// one.
- for (int m = 0; m < 70; ++m) {
+ for (int i = 0; i < 69; ++i) {
SendAvailableSendWindow();
AckNPackets(2);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
SendAvailableSendWindow();
AckNPackets(2);
expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
}
TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
// Make sure that we fall out of slow start when we encounter a packet loss.
- const int kNumberOfAck = 10;
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int i = 0; i < kNumberOfAck; ++i) {
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
SendAvailableSendWindow();
QuicByteCount expected_send_window = kDefaultWindowTCP +
- (kDefaultTCPMSS * 2 * kNumberOfAck);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
-
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
- // Make sure that we should not send right now.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
+ // Lose a packet to exit slow start.
+ LoseNPackets(1);
// We should now have fallen out of slow start.
- // We expect window to be cut in half.
+ // We expect window to be cut in half by Reno.
expected_send_window /= 2;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
// Testing Reno phase.
// We need to ack half of the pending packet before we can send again.
- int number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
AckNPackets(number_of_packets_in_window);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
- EXPECT_EQ(0u, sender_->AvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We need to ack every packet in the window before we exit recovery.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window - 2; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
AckNPackets(1);
expected_send_window += kDefaultTCPMSS;
- number_of_packets_in_window++;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartPacketLossPRR) {
+ // Test based on the first example in RFC6937.
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- // We should need number_of_packets_in_window ACK:ed packets before
- // increasing window by one.
- for (int k = 0; k < number_of_packets_in_window; ++k) {
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
SendAvailableSendWindow();
- AckNPackets(1);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ AckNPackets(2);
}
SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Testing TCP proportional rate reduction.
+ // We should send one packet for every two received acks over the remaining
+ // 18 outstanding packets.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ // The number of packets before we exit recovery is the original CWND minus
+ // the packet that has been lost and the one which triggered the loss.
+ size_t remaining_packets_in_recovery = number_of_packets_in_window * 2 - 1;
+ for (size_t i = 0; i < remaining_packets_in_recovery - 1; i += 2) {
+ AckNPackets(2);
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
AckNPackets(1);
expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) {
+ // Test based on the second example in RFC6937, though we also implement
+ // forward acknowledgements, so the first two incoming acks will trigger
+ // PRR immediately.
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Ack a packet with a 15 packet gap, losing 13 of them due to FACK.
+ LoseNPackets(13);
+ // Immediately after the loss, ensure at least one packet can be sent.
+ // Losses without subsequent acks can occur with timer based loss detection.
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Only 2 packets should be allowed to be sent, per PRR-SSRB
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // The window should not have changed.
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Exit recovery and return to sending at the new rate.
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ }
}
TEST_F(TcpCubicSenderTest, RTOCongestionWindow) {
EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
// Expect the window to decrease to the minimum once the RTO fires.
- sender_->OnRetransmissionTimeout();
+ sender_->OnRetransmissionTimeout(true);
EXPECT_EQ(2 * kDefaultTCPMSS, sender_->SendWindow());
}
+TEST_F(TcpCubicSenderTest, RTOCongestionWindowNoRetransmission) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+
+ // Expect the window to remain unchanged if the RTO fires but no
+ // packets are retransmitted.
+ sender_->OnRetransmissionTimeout(false);
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+}
+
TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
const int64 kRttMs = 10;
const int64 kDeviationMs = 3;
EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay());
- sender_->AckAccounting(QuicTime::Delta::FromMilliseconds(kRttMs));
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs),
+ QuicTime::Delta::Zero(), clock_.Now());
// Initial value is to set the median deviation to half of the initial
// rtt, the median in then multiplied by a factor of 4 and finally the
@@ -260,75 +455,63 @@ TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
for (int i = 0; i < 100; ++i) {
// Run to make sure that we converge.
- sender_->AckAccounting(
- QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs));
- sender_->AckAccounting(
- QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs));
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
}
expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4);
- EXPECT_NEAR(kRttMs, sender_->SmoothedRtt().ToMilliseconds(), 1);
+ EXPECT_NEAR(kRttMs, sender_->rtt_stats_.SmoothedRtt().ToMilliseconds(), 1);
EXPECT_NEAR(expected_delay.ToMilliseconds(),
sender_->RetransmissionDelay().ToMilliseconds(),
1);
EXPECT_EQ(static_cast<int64>(
sender_->GetCongestionWindow() * kNumMicrosPerSecond /
- sender_->SmoothedRtt().ToMicroseconds()),
+ sender_->rtt_stats_.SmoothedRtt().ToMicroseconds()),
sender_->BandwidthEstimate().ToBytesPerSecond());
}
TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 100;
+ const int kNumberOfAcks = 100;
sender_.reset(
new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 1000;
+ const int kNumberOfAcks = 1000;
sender_.reset(
new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
SendAvailableSendWindow();
AckNPackets(2);
// Make sure we fall out of slow start.
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LoseNPackets(1);
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
@@ -336,33 +519,28 @@ TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) {
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 1000;
+ // Set to 10000 to compensate for small cubic alpha.
+ const int kNumberOfAcks = 10000;
+
sender_.reset(
new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
SendAvailableSendWindow();
AckNPackets(2);
// Make sure we fall out of slow start.
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LoseNPackets(1);
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
@@ -370,52 +548,93 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, MultipleLossesInOneWindow) {
SendAvailableSendWindow();
const QuicByteCount initial_window = sender_->GetCongestionWindow();
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LosePacket(acked_sequence_number_ + 1);
const QuicByteCount post_loss_window = sender_->GetCongestionWindow();
EXPECT_GT(initial_window, post_loss_window);
- sender_->OnPacketLost(acked_sequence_number_ + 3, clock_.Now());
+ LosePacket(acked_sequence_number_ + 3);
EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
- sender_->OnPacketLost(sequence_number_ - 1, clock_.Now());
+ LosePacket(sequence_number_ - 1);
EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
// Lose a later packet and ensure the window decreases.
- sender_->OnPacketLost(sequence_number_, clock_.Now());
+ LosePacket(sequence_number_);
EXPECT_GT(post_loss_window, sender_->GetCongestionWindow());
}
-TEST_F(TcpCubicSenderTest, SendWindowNotAffectedByAcks) {
- QuicByteCount send_window = sender_->AvailableSendWindow();
-
- // Send a packet with no retransmittable data, and ensure that the congestion
- // window doesn't change.
- QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, send_window);
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
- EXPECT_EQ(send_window, sender_->AvailableSendWindow());
-
- // Send a data packet with retransmittable data, and ensure that the
- // congestion window has shrunk.
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- EXPECT_GT(send_window, sender_->AvailableSendWindow());
+TEST_F(TcpCubicSenderTest, DontTrackAckPackets) {
+ // Send a packet with no retransmittable data, and ensure it's not tracked.
+ EXPECT_FALSE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ NO_RETRANSMITTABLE_DATA));
+
+ // Send a data packet with retransmittable data, and ensure it is tracked.
+ EXPECT_TRUE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ HAS_RETRANSMITTABLE_DATA));
}
TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) {
QuicTcpCongestionWindow congestion_window = sender_->congestion_window();
QuicConfig config;
- config.set_server_initial_congestion_window(2 * congestion_window,
- 2 * congestion_window);
- EXPECT_EQ(2 * congestion_window, config.server_initial_congestion_window());
+ QuicConfigPeer::SetReceivedInitialWindow(&config, 2 * congestion_window);
sender_->SetFromConfig(config, true);
EXPECT_EQ(2 * congestion_window, sender_->congestion_window());
}
+TEST_F(TcpCubicSenderTest, CongestionAvoidanceAtEndOfRecovery) {
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start, and window should be cut in
+ // half by Reno. New cwnd should be 10.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e.,
+ // until the currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Out of recovery now. Congestion window should not grow during RTT.
+ for (int i = 0; i < 4; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc
new file mode 100644
index 00000000000..8dad3f18429
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 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/congestion_control/tcp_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace {
+
+// TCP retransmits after 3 nacks.
+static const size_t kNumberOfNacksBeforeRetransmission = 3;
+
+// How many RTTs the algorithm waits before determining a packet is lost due
+// to early retransmission.
+static const double kEarlyRetransmitLossDelayMultiplier = 1.25;
+
+}
+
+TCPLossAlgorithm::TCPLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TCPLossAlgorithm::GetLossDetectionType() const {
+ return kNack;
+}
+
+// Uses nack counts to decide when packets are lost.
+SequenceNumberSet TCPLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay =
+ rtt_stats.SmoothedRtt().Multiply(kEarlyRetransmitLossDelayMultiplier);
+
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && it->first <= largest_observed; ++it) {
+ if (!it->second.in_flight) {
+ continue;
+ }
+
+ LOG_IF(DFATAL, it->second.nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+ if (it->second.nack_count >= kNumberOfNacksBeforeRetransmission) {
+ lost_packets.insert(it->first);
+ continue;
+ }
+
+ // Only early retransmit(RFC5827) when the last packet gets acked and
+ // there are retransmittable packets in flight.
+ // This also implements a timer-protected variant of FACK.
+ if (it->second.retransmittable_frames &&
+ unacked_packets.largest_sent_packet() == largest_observed) {
+ // Early retransmit marks the packet as lost once 1.25RTTs have passed
+ // since the packet was sent and otherwise sets an alarm.
+ if (time >= it->second.sent_time.Add(loss_delay)) {
+ lost_packets.insert(it->first);
+ } else {
+ // Set the timeout for the earliest retransmittable packet where early
+ // retransmit applies.
+ loss_detection_timeout_ = it->second.sent_time.Add(loss_delay);
+ break;
+ }
+ }
+ }
+
+ return lost_packets;
+}
+
+QuicTime TCPLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.h b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h
new file mode 100644
index 00000000000..5f0bcfb523f
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// Class which implement's TCP's approach of detecting loss when 3 nacks have
+// been received for a packet. Also implements TCP's early retransmit(RFC5827).
+class NET_EXPORT_PRIVATE TCPLossAlgorithm : public LossDetectionInterface {
+ public:
+ TCPLossAlgorithm();
+ virtual ~TCPLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Uses nack counts to decide when packets are lost.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns a non-zero value when the early retransmit timer is active.
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TCPLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc
new file mode 100644
index 00000000000..eccda4a81c5
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 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 <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TcpLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TcpLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NULL, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TCPLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1Packet) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // No loss on one ack.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ // No loss on two acks.
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.NackPacket(1, 2);
+ VerifyLosses(3, NULL, 0);
+ // Loss on three acks.
+ unacked_packets_.RemoveFromInFlight(4);
+ unacked_packets_.NackPacket(1, 3);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// A stretch ack is an ack that covers more than 1 packet of previously
+// unacknowledged data.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in a single StretchAck.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Ack a packet 3 packets ahead, causing a retransmit.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketSingleAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in an AckFrame with three missing packets.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.NackPacket(2, 2);
+ unacked_packets_.NackPacket(3, 1);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmit1Packet) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(1.25)),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.latest_rtt().Multiply(1.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmitAllPackets) {
+ const size_t kNumSentPackets = 5;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ // Advance the time 1/4 RTT between 3 and 4.
+ if (i == 3) {
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ }
+ }
+
+ // Early retransmit when the final packet gets acked and 1.25 RTTs have
+ // elapsed since the packets were sent.
+ unacked_packets_.RemoveFromInFlight(kNumSentPackets);
+ // This simulates a single ack following multiple missing packets with FACK.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, kNumSentPackets - i);
+ }
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ VerifyLosses(kNumSentPackets, lost, arraysize(lost));
+ // The time has already advanced 1/4 an RTT, so ensure the timeout is set
+ // 1.25 RTTs after the earliest pending packet(3), not the last(4).
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt()),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ QuicPacketSequenceNumber lost2[] = { 1, 2, 3 };
+ VerifyLosses(kNumSentPackets, lost2, arraysize(lost2));
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(0.25)),
+ loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost3[] = { 1, 2, 3, 4 };
+ VerifyLosses(kNumSentPackets, lost3, arraysize(lost3));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Neuter packet 1.
+ unacked_packets_.RemoveRetransmittability(1);
+
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.IncreaseLargestObserved(2);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_receiver.cc b/chromium/net/quic/congestion_control/tcp_receiver.cc
index ecff130b003..a8c5489b604 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver.cc
+++ b/chromium/net/quic/congestion_control/tcp_receiver.cc
@@ -7,31 +7,24 @@
namespace net {
+// Originally 64K bytes, but increased it to 256K to support higher bitrates.
// static
-// Originally 64K bytes for TCP, setting it to 256K to support higher bitrates.
const QuicByteCount TcpReceiver::kReceiveWindowTCP = 256000;
TcpReceiver::TcpReceiver()
- : accumulated_number_of_recoverd_lost_packets_(0),
- receive_window_(kReceiveWindowTCP) {
+ : receive_window_(kReceiveWindowTCP) {
}
bool TcpReceiver::GenerateCongestionFeedback(
QuicCongestionFeedbackFrame* feedback) {
feedback->type = kTCP;
- feedback->tcp.accumulated_number_of_lost_packets =
- accumulated_number_of_recoverd_lost_packets_;
feedback->tcp.receive_window = receive_window_;
return true;
}
void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) {
- if (revived) {
- ++accumulated_number_of_recoverd_lost_packets_;
- }
+ QuicTime timestamp) {
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_receiver.h b/chromium/net/quic/congestion_control/tcp_receiver.h
index 99cf93c4933..a5e5b1b7c7d 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver.h
+++ b/chromium/net/quic/congestion_control/tcp_receiver.h
@@ -29,12 +29,9 @@ class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface {
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) OVERRIDE;
+ QuicTime timestamp) OVERRIDE;
private:
- // We need to keep track of FEC recovered packets.
- int accumulated_number_of_recoverd_lost_packets_;
QuicByteCount receive_window_;
DISALLOW_COPY_AND_ASSIGN(TcpReceiver);
diff --git a/chromium/net/quic/congestion_control/tcp_receiver_test.cc b/chromium/net/quic/congestion_control/tcp_receiver_test.cc
index 305ff75eb8b..e074fdfe6c7 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver_test.cc
+++ b/chromium/net/quic/congestion_control/tcp_receiver_test.cc
@@ -22,16 +22,14 @@ class QuicTcpReceiverTest : public ::testing::Test {
TEST_F(QuicTcpReceiverTest, SimpleReceiver) {
QuicCongestionFeedbackFrame feedback;
QuicTime timestamp(QuicTime::Zero());
- receiver_->RecordIncomingPacket(1, 1, timestamp, false);
+ receiver_->RecordIncomingPacket(1, 1, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kTCP, feedback.type);
EXPECT_EQ(256000u, feedback.tcp.receive_window);
- EXPECT_EQ(0, feedback.tcp.accumulated_number_of_lost_packets);
- receiver_->RecordIncomingPacket(1, 2, timestamp, true);
+ receiver_->RecordIncomingPacket(1, 2, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kTCP, feedback.type);
EXPECT_EQ(256000u, feedback.tcp.receive_window);
- EXPECT_EQ(1, feedback.tcp.accumulated_number_of_lost_packets);
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.cc b/chromium/net/quic/congestion_control/time_loss_algorithm.cc
new file mode 100644
index 00000000000..d23deadf118
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 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/congestion_control/time_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace {
+
+// The minimum delay before a packet will be considered lost,
+// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only
+// triggers when a nack has been receieved for the packet.
+static const size_t kMinLossDelayMs = 5;
+
+// How many RTTs the algorithm waits before determining a packet is lost.
+static const double kLossDelayMultiplier = 1.25;
+
+} // namespace
+
+TimeLossAlgorithm::TimeLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TimeLossAlgorithm::GetLossDetectionType() const {
+ return kTime;
+}
+
+SequenceNumberSet TimeLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay = QuicTime::Delta::Max(
+ QuicTime::Delta::FromMilliseconds(kMinLossDelayMs),
+ QuicTime::Delta::Max(rtt_stats.SmoothedRtt(), rtt_stats.latest_rtt())
+ .Multiply(kLossDelayMultiplier));
+
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && it->first <= largest_observed; ++it) {
+ if (!it->second.in_flight) {
+ continue;
+ }
+ LOG_IF(DFATAL, it->second.nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+
+ // Packets are sent in order, so break when we haven't waited long enough
+ // to lose any more packets and leave the loss_time_ set for the timeout.
+ QuicTime when_lost = it->second.sent_time.Add(loss_delay);
+ if (time < when_lost) {
+ loss_detection_timeout_ = when_lost;
+ break;
+ }
+ lost_packets.insert(it->first);
+ }
+
+ return lost_packets;
+}
+
+// loss_time_ is updated in DetectLostPackets, which must be called every time
+// an ack is received or the timeout expires.
+QuicTime TimeLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.h b/chromium/net/quic/congestion_control/time_loss_algorithm.h
new file mode 100644
index 00000000000..ae37e1e3258
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm.h
@@ -0,0 +1,52 @@
+// Copyright 2014 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_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// A loss detection algorithm which avoids spurious losses and retransmissions
+// by waiting 1.25 RTTs after a packet was sent instead of nack count.
+class NET_EXPORT_PRIVATE TimeLossAlgorithm : public LossDetectionInterface {
+ public:
+ TimeLossAlgorithm();
+ virtual ~TimeLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Declares pending packets less than the largest observed lost when it has
+ // been 1.25 RTT since they were sent. Packets larger than the largest
+ // observed are retransmitted via TLP.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns the time the next packet will be lost, or zero if there
+ // are no nacked pending packets outstanding.
+ // TODO(ianswett): Ideally the RTT variance and the RTT would be used to
+ // determine the time a packet is considered lost.
+ // TODO(ianswett): Consider using Max(1.25 * srtt, 1.125 * last_rtt).
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc
new file mode 100644
index 00000000000..bd03e608bcc
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 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 <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TimeLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TimeLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NULL, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TimeLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TimeLossAlgorithmTest, NoLossFor500Nacks) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ unacked_packets_.RemoveFromInFlight(2);
+ for (size_t i = 1; i < 500; ++i) {
+ unacked_packets_.NackPacket(1, i);
+ VerifyLosses(2, NULL, 0);
+ }
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(1.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossUntilTimeout) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ unacked_packets_.NackPacket(1, 1);
+ unacked_packets_.RemoveFromInFlight(2);
+ VerifyLosses(2, NULL, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ unacked_packets_.NackPacket(1, 5);
+ VerifyLosses(2, NULL, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossWithoutNack) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost without a nack.
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyLosses(1, NULL, 0);
+ // The timer should still not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ VerifyLosses(1, NULL, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ VerifyLosses(1, NULL, 0);
+
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, MultipleLossesAtOnce) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at once and then go forward an RTT.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, 1);
+ }
+ unacked_packets_.RemoveFromInFlight(10);
+ VerifyLosses(10, NULL, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ VerifyLosses(10, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_decrypter.h b/chromium/net/quic/crypto/aead_base_decrypter.h
new file mode 100644
index 00000000000..6257409f9fb
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_decrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_DecryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* enc, unsigned encLen);
+#endif
+
+namespace net {
+
+// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseDecrypter();
+
+ // QuicDecrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Decrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE;
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext) OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif // !defined(USE_OPENSSL)
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_DecryptFunction pk11_decrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseDecrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/chromium/net/quic/crypto/aead_base_decrypter_nss.cc b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc
new file mode 100644
index 00000000000..bbf30244f87
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseDecrypter::AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_decrypt_(pk11_decrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+ // NSS 3.14.x incorrectly requires an output buffer at least as long as
+ // the ciphertext (NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
+ // QuicDecrypter::Decrypt() specifies that |output| must be as long as
+ // |ciphertext| on entry.
+ size_t plaintext_size = ciphertext.length() - auth_tag_size_;
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL));
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_decrypt_(aead_key.get(), aead_mechanism_, &param,
+ output, &output_len, ciphertext.length(),
+ reinterpret_cast<const unsigned char*>(ciphertext.data()),
+ ciphertext.length()) != SECSuccess) {
+ return false;
+ }
+
+ if (output_len != plaintext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+ *output_length = output_len;
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return NULL;
+ }
+ size_t plaintext_size;
+ scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc
new file mode 100644
index 00000000000..2190bf6966e
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// Clear OpenSSL error stack.
+void ClearOpenSslErrors() {
+ while (ERR_get_error()) {}
+}
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ ClearOpenSslErrors();
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, NULL)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ ssize_t len = EVP_AEAD_CTX_open(
+ ctx_.get(), output, ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()), ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size());
+
+ if (len < 0) {
+ // Because QuicFramer does trial decryption, decryption errors are expected
+ // when encryption level changes. So we don't log decryption errors.
+ ClearOpenSslErrors();
+ return false;
+ }
+
+ *output_length = len;
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return NULL;
+ }
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_encrypter.h b/chromium/net/quic/crypto/aead_base_encrypter.h
new file mode 100644
index 00000000000..713813150ec
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_EncryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* data, unsigned int dataLen);
+#endif
+
+namespace net {
+
+// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseEncrypter();
+
+ // QuicEncrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Encrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext,
+ unsigned char* output) OVERRIDE;
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext) OVERRIDE;
+ virtual size_t GetKeySize() const OVERRIDE;
+ virtual size_t GetNoncePrefixSize() const OVERRIDE;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_EncryptFunction pk11_encrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseEncrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/chromium/net/quic/crypto/aead_base_encrypter_nss.cc b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc
new file mode 100644
index 00000000000..fd4d888554c
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseEncrypter::AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_encrypt_(pk11_encrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL));
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_encrypt_(aead_key.get(), aead_mechanism_, &param,
+ output, &output_len, ciphertext_size,
+ reinterpret_cast<const unsigned char*>(plaintext.data()),
+ plaintext.size()) != SECSuccess) {
+ DVLOG(1) << "pk11_encrypt_ failed";
+ return false;
+ }
+
+ if (output_len != ciphertext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc
new file mode 100644
index 00000000000..9f053abf932
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <string.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ while (ERR_get_error()) {}
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, NULL)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ ssize_t len = EVP_AEAD_CTX_seal(
+ ctx_.get(), output, plaintext.size() + auth_tag_size_,
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size());
+
+ if (len < 0) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
index aba610f25df..0511c8b7268 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
@@ -5,28 +5,17 @@
#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "net/quic/crypto/quic_decrypter.h"
-
-#if defined(USE_OPENSSL)
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-#endif
+#include "net/quic/crypto/aead_base_decrypter.h"
namespace net {
-namespace test {
-class Aes128Gcm12DecrypterPeer;
-} // namespace test
-
// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
// calling QuicDecrypter::Create(kAESG).
//
// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
// of the nonce is four bytes.
-class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
+class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter {
public:
enum {
// Authentication tags are truncated to 96 bits.
@@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
Aes128Gcm12Decrypter();
virtual ~Aes128Gcm12Decrypter();
- // Returns true if the underlying crypto library supports AES GCM.
- static bool IsSupported();
-
- // QuicDecrypter implementation
- virtual bool SetKey(base::StringPiece key) OVERRIDE;
- virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
- virtual bool Decrypt(base::StringPiece nonce,
- base::StringPiece associated_data,
- base::StringPiece ciphertext,
- unsigned char* output,
- size_t* output_length) OVERRIDE;
- virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
- base::StringPiece associated_data,
- base::StringPiece ciphertext) OVERRIDE;
- virtual base::StringPiece GetKey() const OVERRIDE;
- virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
private:
- // The 128-bit AES key.
- unsigned char key_[16];
- // The nonce prefix.
- unsigned char nonce_prefix_[4];
-
-#if defined(USE_OPENSSL)
- // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge
- // internal CL 53267501.
- ScopedEVPCipherCtx ctx_;
-#endif
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Decrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
index e0b42378021..0ba82698a86 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
@@ -4,12 +4,10 @@
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
-#include <nss.h>
#include <pk11pub.h>
#include <secerr.h>
#include "base/lazy_instance.h"
-#include "base/memory/scoped_ptr.h"
#include "crypto/ghash.h"
#include "crypto/scoped_nss_types.h"
@@ -23,31 +21,8 @@ namespace net {
namespace {
-// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR
-// and GCM types, so define them here.
-#if !defined(CKM_AES_CTR)
-#define CKM_AES_CTR 0x00001086
-#define CKM_AES_GCM 0x00001087
-
-struct CK_AES_CTR_PARAMS {
- CK_ULONG ulCounterBits;
- CK_BYTE cb[16];
-};
-
-struct CK_GCM_PARAMS {
- CK_BYTE_PTR pIv;
- CK_ULONG ulIvLen;
- CK_BYTE_PTR pAAD;
- CK_ULONG ulAADLen;
- CK_ULONG ulTagBits;
-};
-#endif // CKM_AES_CTR
-
-typedef SECStatus
-(*PK11_DecryptFunction)(
- PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
- unsigned char* out, unsigned int* outLen, unsigned int maxLen,
- const unsigned char* enc, unsigned encLen);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
// On Linux, dynamically link against the system version of libnss3.so. In
// order to continue working on systems without up-to-date versions of NSS,
@@ -61,10 +36,6 @@ class GcmSupportChecker {
return pk11_decrypt_func_;
}
- static CK_MECHANISM_TYPE aes_key_mechanism() {
- return aes_key_mechanism_;
- }
-
private:
friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
@@ -80,33 +51,19 @@ class GcmSupportChecker {
// AES-GCM directly. This was introduced in NSS 3.15.
pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT,
"PK11_Decrypt");
- if (pk11_decrypt_func_ == NULL) {
- aes_key_mechanism_ = CKM_AES_ECB;
- }
#endif
}
// |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt.
static PK11_DecryptFunction pk11_decrypt_func_;
-
- // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of
- // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in
- // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15.
- static CK_MECHANISM_TYPE aes_key_mechanism_;
};
// static
PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = NULL;
-// static
-CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM;
-
base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
LAZY_INSTANCE_INITIALIZER;
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
// Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using
// CKM_AES_CTR and the GaloisHash class.
SECStatus My_Decrypt(PK11SymKey* key,
@@ -250,134 +207,30 @@ SECStatus My_Decrypt(PK11SymKey* key,
} // namespace
-Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(CKM_AES_GCM, My_Decrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
ignore_result(g_gcm_support_checker.Get());
}
Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
-// static
-bool Aes128Gcm12Decrypter::IsSupported() {
- // NSS 3.15 supports CKM_AES_GCM directly.
- // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM.
- // Versions earlier than NSS 3.14 are not supported.
- return NSS_VersionCheck("3.14") != PR_FALSE;
-}
-
-bool Aes128Gcm12Decrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
- return true;
-}
-
-bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
-
-bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece ciphertext,
- uint8* output,
- size_t* output_length) {
- if (ciphertext.length() < kAuthTagSize ||
- nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
- // NSS 3.14.x incorrectly requires an output buffer at least as long as
- // the ciphertext (NSS bug
- // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
- // QuicDecrypter::Decrypt() specifies that |output| must be as long as
- // |ciphertext| on entry.
- size_t plaintext_size = ciphertext.length() - kAuthTagSize;
-
- // Import key_ into NSS.
- SECItem key_item;
- key_item.type = siBuffer;
- key_item.data = key_;
- key_item.len = sizeof(key_);
- PK11SlotInfo* slot = PK11_GetInternalSlot();
- // The exact value of the |origin| argument doesn't matter to NSS as long as
- // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
- // placeholder.
- crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey(
- slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap,
- CKA_DECRYPT, &key_item, NULL));
- PK11_FreeSlot(slot);
- slot = NULL;
- if (!aes_key) {
- DVLOG(1) << "PK11_ImportSymKey failed";
- return false;
- }
-
- CK_GCM_PARAMS gcm_params = {0};
- gcm_params.pIv =
+void Aes128Gcm12Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
- gcm_params.ulIvLen = nonce.size();
- gcm_params.pAAD =
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
- gcm_params.ulAADLen = associated_data.size();
- gcm_params.ulTagBits = kAuthTagSize * 8;
-
- SECItem param;
- param.type = siBuffer;
- param.data = reinterpret_cast<unsigned char*>(&gcm_params);
- param.len = sizeof(gcm_params);
-
- unsigned int output_len;
- if (My_Decrypt(aes_key.get(), CKM_AES_GCM, &param,
- output, &output_len, ciphertext.length(),
- reinterpret_cast<const unsigned char*>(ciphertext.data()),
- ciphertext.length()) != SECSuccess) {
- return false;
- }
-
- if (output_len != plaintext_size) {
- DVLOG(1) << "Wrong output length";
- return false;
- }
- *output_length = output_len;
- return true;
-}
-
-QuicData* Aes128Gcm12Decrypter::DecryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece ciphertext) {
- if (ciphertext.length() < kAuthTagSize) {
- return NULL;
- }
- size_t plaintext_size;
- scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
-
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, ciphertext,
- reinterpret_cast<uint8*>(plaintext.get()),
- &plaintext_size)) {
- return NULL;
- }
- return new QuicData(plaintext.release(), plaintext_size, true);
-}
-
-StringPiece Aes128Gcm12Decrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
index 56317fdd3c4..109d2dae85d 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
@@ -6,142 +6,23 @@
#include <openssl/evp.h>
-#include "base/memory/scoped_ptr.h"
-
-using base::StringPiece;
-
namespace net {
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
-Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {}
-
-Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
-
-// static
-bool Aes128Gcm12Decrypter::IsSupported() { return true; }
-
-bool Aes128Gcm12Decrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
-
- // Set the cipher type and the key.
- if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_,
- NULL) == 0) {
- return false;
- }
-
- // Set the IV (nonce) length.
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize,
- NULL) == 0) {
- return false;
- }
-
- return true;
-}
-
-bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
+namespace {
-bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece ciphertext,
- uint8* output,
- size_t* output_length) {
- if (ciphertext.length() < kAuthTagSize ||
- nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
- const size_t plaintext_size = ciphertext.length() - kAuthTagSize;
-
- // Set the IV (nonce).
- if (EVP_DecryptInit_ex(
- ctx_.get(), NULL, NULL, NULL,
- reinterpret_cast<const uint8*>(nonce.data())) == 0) {
- return false;
- }
-
- // Set the authentication tag.
- if (EVP_CIPHER_CTX_ctrl(
- ctx_.get(), EVP_CTRL_GCM_SET_TAG, kAuthTagSize,
- const_cast<char*>(ciphertext.data()) + plaintext_size) == 0) {
- return false;
- }
-
- // If we pass a NULL, zero-length associated data to OpenSSL then it breaks.
- // Thus we only set non-empty associated data.
- if (!associated_data.empty()) {
- // Set the associated data. The second argument (output buffer) must be
- // NULL.
- int unused_len;
- if (EVP_DecryptUpdate(
- ctx_.get(), NULL, &unused_len,
- reinterpret_cast<const uint8*>(associated_data.data()),
- associated_data.size()) == 0) {
- return false;
- }
- }
-
- int len;
- if (EVP_DecryptUpdate(
- ctx_.get(), output, &len,
- reinterpret_cast<const uint8*>(ciphertext.data()),
- plaintext_size) == 0) {
- return false;
- }
- output += len;
-
- if (EVP_DecryptFinal_ex(ctx_.get(), output, &len) == 0) {
- return false;
- }
- output += len;
-
- *output_length = plaintext_size;
-
- return true;
-}
-
-QuicData* Aes128Gcm12Decrypter::DecryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece ciphertext) {
- if (ciphertext.length() < kAuthTagSize) {
- return NULL;
- }
- size_t plaintext_size;
- scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, ciphertext,
- reinterpret_cast<uint8*>(plaintext.get()),
- &plaintext_size)) {
- return NULL;
- }
- return new QuicData(plaintext.release(), plaintext_size, true);
-}
+} // namespace
-StringPiece Aes128Gcm12Decrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
}
-StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
-}
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
index dd37085894c..5a719a05a41 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -268,69 +268,65 @@ QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter,
}
TEST(Aes128Gcm12DecrypterTest, Decrypt) {
- if (!Aes128Gcm12Decrypter::IsSupported()) {
- LOG(INFO) << "AES GCM not supported. Test skipped.";
- return;
- }
-
- string key;
- string iv;
- string ct;
- string aad;
- string tag;
- string pt;
-
for (size_t i = 0; i < arraysize(test_group_array); i++) {
SCOPED_TRACE(i);
- const TestVector* test_vector = test_group_array[i];
+ const TestVector* test_vectors = test_group_array[i];
const TestGroupInfo& test_info = test_group_info[i];
- for (size_t j = 0; test_vector[j].key != NULL; j++) {
+ for (size_t j = 0; test_vectors[j].key != NULL; j++) {
// If not present then decryption is expected to fail.
- bool has_pt = test_vector[j].pt;
+ bool has_pt = test_vectors[j].pt;
// Decode the test vector.
- ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key));
- ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv));
- ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct));
- ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad));
- ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag));
- if (has_pt)
- ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt));
+ string key;
+ string iv;
+ string ct;
+ string aad;
+ string tag;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ }
// The test vector's lengths should look sane. Note that the lengths
// in |test_info| are in bits.
- EXPECT_EQ(test_info.key_len, key.size() * 8);
- EXPECT_EQ(test_info.iv_len, iv.size() * 8);
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
- EXPECT_EQ(test_info.aad_len, aad.size() * 8);
- EXPECT_EQ(test_info.tag_len, tag.size() * 8);
- if (has_pt)
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
// The test vectors have 16 byte authenticators but this code only uses
// the first 12.
ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize),
- tag.size());
- string ciphertext =
- ct + tag.substr(0, Aes128Gcm12Decrypter::kAuthTagSize);
+ tag.length());
+ tag.resize(Aes128Gcm12Decrypter::kAuthTagSize);
+ string ciphertext = ct + tag;
Aes128Gcm12Decrypter decrypter;
ASSERT_TRUE(decrypter.SetKey(key));
scoped_ptr<QuicData> decrypted(DecryptWithNonce(
&decrypter, iv,
- // OpenSSL fails if NULL is set as the AAD, as opposed to a
- // zero-length, non-NULL pointer.
- aad.size() ? aad : StringPiece(), ciphertext));
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ aad.length() ? aad : StringPiece(), ciphertext));
if (!decrypted.get()) {
EXPECT_FALSE(has_pt);
continue;
}
EXPECT_TRUE(has_pt);
- ASSERT_EQ(pt.size(), decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
- pt.size(), pt.data(), pt.size());
+ pt.length(), pt.data(), pt.length());
}
}
}
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
index a2d1dc9a088..1d8f32149bf 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
@@ -5,28 +5,17 @@
#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
#define NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "net/quic/crypto/quic_encrypter.h"
-
-#if defined(USE_OPENSSL)
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-#endif
+#include "net/quic/crypto/aead_base_encrypter.h"
namespace net {
-namespace test {
-class Aes128Gcm12EncrypterPeer;
-} // namespace test
-
// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the
// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
// calling QuicEncrypter::Create(kAESG).
//
// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
// of the nonce is four bytes.
-class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter {
+class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter {
public:
enum {
// Authentication tags are truncated to 96 bits.
@@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter {
Aes128Gcm12Encrypter();
virtual ~Aes128Gcm12Encrypter();
- // QuicEncrypter implementation
- virtual bool SetKey(base::StringPiece key) OVERRIDE;
- virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
- virtual bool Encrypt(base::StringPiece nonce,
- base::StringPiece associated_data,
- base::StringPiece plaintext,
- unsigned char* output) OVERRIDE;
- virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
- base::StringPiece associated_data,
- base::StringPiece plaintext) OVERRIDE;
- virtual size_t GetKeySize() const OVERRIDE;
- virtual size_t GetNoncePrefixSize() const OVERRIDE;
- virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
- virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
- virtual base::StringPiece GetKey() const OVERRIDE;
- virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
private:
- // The 128-bit AES key.
- unsigned char key_[16];
- // The nonce prefix.
- unsigned char nonce_prefix_[4];
-
-#if defined(USE_OPENSSL)
- // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge
- // internal CL 53267501.
- ScopedEVPCipherCtx ctx_;
-#endif
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Encrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
index 2cd072af461..09901a47672 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
@@ -4,12 +4,10 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include <nss.h>
#include <pk11pub.h>
#include <secerr.h>
#include "base/lazy_instance.h"
-#include "base/memory/scoped_ptr.h"
#include "crypto/ghash.h"
#include "crypto/scoped_nss_types.h"
@@ -23,31 +21,8 @@ namespace net {
namespace {
-// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR
-// and GCM types, so define them here.
-#if !defined(CKM_AES_CTR)
-#define CKM_AES_CTR 0x00001086
-#define CKM_AES_GCM 0x00001087
-
-struct CK_AES_CTR_PARAMS {
- CK_ULONG ulCounterBits;
- CK_BYTE cb[16];
-};
-
-struct CK_GCM_PARAMS {
- CK_BYTE_PTR pIv;
- CK_ULONG ulIvLen;
- CK_BYTE_PTR pAAD;
- CK_ULONG ulAADLen;
- CK_ULONG ulTagBits;
-};
-#endif // CKM_AES_CTR
-
-typedef SECStatus
-(*PK11_EncryptFunction)(
- PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
- unsigned char* out, unsigned int* outLen, unsigned int maxLen,
- const unsigned char* data, unsigned int dataLen);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
// On Linux, dynamically link against the system version of libnss3.so. In
// order to continue working on systems without up-to-date versions of NSS,
@@ -61,10 +36,6 @@ class GcmSupportChecker {
return pk11_encrypt_func_;
}
- static CK_MECHANISM_TYPE aes_key_mechanism() {
- return aes_key_mechanism_;
- }
-
private:
friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
@@ -80,34 +51,19 @@ class GcmSupportChecker {
// AES-GCM directly. This was introduced in NSS 3.15.
pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT,
"PK11_Encrypt");
- if (pk11_encrypt_func_ == NULL) {
- aes_key_mechanism_ = CKM_AES_ECB;
- }
#endif
}
// |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt.
static PK11_EncryptFunction pk11_encrypt_func_;
-
- // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of
- // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in
- // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15.
- static CK_MECHANISM_TYPE aes_key_mechanism_;
};
// static
PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = NULL;
-// static
-CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM;
-
base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
LAZY_INSTANCE_INITIALIZER;
-const size_t kKeySize = 16;
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
// Calls PK11_Encrypt if it's available. Otherwise, emulates CKM_AES_GCM using
// CKM_AES_CTR and the GaloisHash class.
SECStatus My_Encrypt(PK11SymKey* key,
@@ -250,136 +206,30 @@ SECStatus My_Encrypt(PK11SymKey* key,
} // namespace
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(CKM_AES_GCM, My_Encrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
ignore_result(g_gcm_support_checker.Get());
}
Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
-bool Aes128Gcm12Encrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
- return true;
-}
-
-bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
-
-bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece plaintext,
- unsigned char* output) {
- if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
-
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
-
- // Import key_ into NSS.
- SECItem key_item;
- key_item.type = siBuffer;
- key_item.data = key_;
- key_item.len = sizeof(key_);
- PK11SlotInfo* slot = PK11_GetInternalSlot();
- // The exact value of the |origin| argument doesn't matter to NSS as long as
- // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
- // placeholder.
- crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey(
- slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap,
- CKA_ENCRYPT, &key_item, NULL));
- PK11_FreeSlot(slot);
- slot = NULL;
- if (!aes_key) {
- DVLOG(1) << "PK11_ImportSymKey failed";
- return false;
- }
-
- CK_GCM_PARAMS gcm_params = {0};
- gcm_params.pIv =
+void Aes128Gcm12Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
- gcm_params.ulIvLen = nonce.size();
- gcm_params.pAAD =
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
- gcm_params.ulAADLen = associated_data.size();
- gcm_params.ulTagBits = kAuthTagSize * 8;
-
- SECItem param;
- param.type = siBuffer;
- param.data = reinterpret_cast<unsigned char*>(&gcm_params);
- param.len = sizeof(gcm_params);
-
- unsigned int output_len;
- if (My_Encrypt(aes_key.get(), CKM_AES_GCM, &param,
- output, &output_len, ciphertext_size,
- reinterpret_cast<const unsigned char*>(plaintext.data()),
- plaintext.size()) != SECSuccess) {
- DVLOG(1) << "My_Encrypt failed";
- return false;
- }
-
- if (output_len != ciphertext_size) {
- DVLOG(1) << "Wrong output length";
- return false;
- }
-
- return true;
-}
-
-QuicData* Aes128Gcm12Encrypter::EncryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece plaintext) {
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
- scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
-
- // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
- // same sequence number twice.
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, plaintext,
- reinterpret_cast<unsigned char*>(ciphertext.get()))) {
- return NULL;
- }
-
- return new QuicData(ciphertext.release(), ciphertext_size, true);
-}
-
-size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; }
-
-size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const {
- return kNoncePrefixSize;
-}
-
-size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - kAuthTagSize;
-}
-
-// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its
-// corresponding plaintext.
-size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const {
- return plaintext_size + kAuthTagSize;
-}
-
-StringPiece Aes128Gcm12Encrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
index 394971a7382..6489528fd37 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
@@ -4,13 +4,7 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include <openssl/err.h>
#include <openssl/evp.h>
-#include <string.h>
-
-#include "base/memory/scoped_ptr.h"
-
-using base::StringPiece;
namespace net {
@@ -18,161 +12,17 @@ namespace {
const size_t kKeySize = 16;
const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
-void ClearOpenSslErrors() {
-#ifdef NDEBUG
- while (ERR_get_error()) {}
-#else
- while (long error = ERR_get_error()) {
- char buf[120];
- ERR_error_string_n(error, buf, arraysize(buf));
- DLOG(ERROR) << "OpenSSL error: " << buf;
- }
-#endif
-}
} // namespace
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {}
-
-Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
-
-bool Aes128Gcm12Encrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
-
- // Set the cipher type and the key.
- if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_,
- NULL) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- // Set the IV (nonce) length.
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize,
- NULL) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- return true;
-}
-
-bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
}
-bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece plaintext,
- unsigned char* output) {
- if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
-
- // Set the IV (nonce).
- if (EVP_EncryptInit_ex(
- ctx_.get(), NULL, NULL, NULL,
- reinterpret_cast<const unsigned char*>(nonce.data())) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- // If we pass a NULL, zero-length associated data to OpenSSL then it breaks.
- // Thus we only set non-empty associated data.
- if (!associated_data.empty()) {
- // Set the associated data. The second argument (output buffer) must be
- // NULL.
- int unused_len;
- if (EVP_EncryptUpdate(
- ctx_.get(), NULL, &unused_len,
- reinterpret_cast<const unsigned char*>(associated_data.data()),
- associated_data.size()) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- }
-
- int len;
- if (EVP_EncryptUpdate(
- ctx_.get(), output, &len,
- reinterpret_cast<const unsigned char*>(plaintext.data()),
- plaintext.size()) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- output += len;
-
- if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- output += len;
-
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize,
- output) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- return true;
-}
-
-QuicData* Aes128Gcm12Encrypter::EncryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece plaintext) {
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
- scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
-
- // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
- // same sequence number twice.
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, plaintext,
- reinterpret_cast<unsigned char*>(ciphertext.get()))) {
- return NULL;
- }
-
- return new QuicData(ciphertext.release(), ciphertext_size, true);
-}
-
-size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; }
-
-size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const {
- return kNoncePrefixSize;
-}
-
-size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - kAuthTagSize;
-}
-
-// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its
-// corresponding plaintext.
-size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const {
- return plaintext_size + kAuthTagSize;
-}
-
-StringPiece Aes128Gcm12Encrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
-}
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
index bfbf93cd716..afe431002e8 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -224,57 +224,55 @@ QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter,
}
TEST(Aes128Gcm12EncrypterTest, Encrypt) {
- string key;
- string iv;
- string pt;
- string aad;
- string ct;
- string tag;
-
for (size_t i = 0; i < arraysize(test_group_array); i++) {
SCOPED_TRACE(i);
- const TestVector* test_vector = test_group_array[i];
+ const TestVector* test_vectors = test_group_array[i];
const TestGroupInfo& test_info = test_group_info[i];
- for (size_t j = 0; test_vector[j].key != NULL; j++) {
+ for (size_t j = 0; test_vectors[j].key != NULL; j++) {
// Decode the test vector.
- ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key));
- ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv));
- ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt));
- ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad));
- ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct));
- ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag));
+ string key;
+ string iv;
+ string pt;
+ string aad;
+ string ct;
+ string tag;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
// The test vector's lengths should look sane. Note that the lengths
// in |test_info| are in bits.
- EXPECT_EQ(test_info.key_len, key.size() * 8);
- EXPECT_EQ(test_info.iv_len, iv.size() * 8);
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
- EXPECT_EQ(test_info.aad_len, aad.size() * 8);
- EXPECT_EQ(test_info.pt_len, ct.size() * 8);
- EXPECT_EQ(test_info.tag_len, tag.size() * 8);
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
Aes128Gcm12Encrypter encrypter;
ASSERT_TRUE(encrypter.SetKey(key));
scoped_ptr<QuicData> encrypted(EncryptWithNonce(
&encrypter, iv,
- // OpenSSL fails if NULL is set as the AAD, as opposed to a
- // zero-length, non-NULL pointer. This deliberately tests that we
- // handle this case.
- aad.size() ? aad : StringPiece(), pt));
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ aad.length() ? aad : StringPiece(), pt));
ASSERT_TRUE(encrypted.get());
// The test vectors have 16 byte authenticators but this code only uses
// the first 12.
ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize),
- tag.size());
- size_t tag_len = Aes128Gcm12Encrypter::kAuthTagSize;
+ tag.length());
+ tag.resize(Aes128Gcm12Encrypter::kAuthTagSize);
- ASSERT_EQ(ct.size() + tag_len, encrypted->length());
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
- ct.size(), ct.data(), ct.size());
+ ct.length(), ct.data(), ct.length());
test::CompareCharArraysWithHexError(
- "authentication tag", encrypted->data() + ct.size(), tag_len,
- tag.data(), tag_len);
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
}
}
}
diff --git a/chromium/net/quic/crypto/cert_compressor.h b/chromium/net/quic/crypto/cert_compressor.h
index 7b7e2f0eb05..d95c5bce540 100644
--- a/chromium/net/quic/crypto/cert_compressor.h
+++ b/chromium/net/quic/crypto/cert_compressor.h
@@ -48,6 +48,9 @@ class NET_EXPORT_PRIVATE CertCompressor {
const std::vector<std::string>& cached_certs,
const CommonCertSets* common_sets,
std::vector<std::string>* out_certs);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertCompressor);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h
new file mode 100644
index 00000000000..9d24ba2d13e
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicDecrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Decrypter();
+ virtual ~ChaCha20Poly1305Decrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Decrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
new file mode 100644
index 00000000000..e8100af49f6
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_INVALID_MECHANISM, NULL, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Decrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
new file mode 100644
index 00000000000..7f0e24da704
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_decrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
new file mode 100644
index 00000000000..1825187bb33
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,132 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_decrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. NULL means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ "86d09974840bded2a5ca"
+ },
+ // Modify the ciphertext (ChaCha20 encryption output).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "f3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the ciphertext (Poly1305 authenticator).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28476", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the associated data.
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "dd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ if (!decrypter->Decrypt(nonce, associated_data, ciphertext,
+ reinterpret_cast<unsigned char*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+TEST(ChaCha20Poly1305DecrypterTest, Decrypt) {
+ if (!ChaCha20Poly1305Decrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != NULL; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ string key;
+ string iv;
+ string aad;
+ string ct;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ }
+
+ ChaCha20Poly1305Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ scoped_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ StringPiece(aad.length() ? aad.data() : NULL, aad.length()), ct));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h
new file mode 100644
index 00000000000..4a68caa531b
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicEncrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Encrypter();
+ virtual ~ChaCha20Poly1305Encrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Encrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
new file mode 100644
index 00000000000..fd6f76ca52f
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_INVALID_MECHANISM, NULL, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Encrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
new file mode 100644
index 00000000000..e256c2a5807
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_encrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
new file mode 100644
index 00000000000..0273b27933c
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 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/crypto/chacha20_poly1305_encrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "86d09974840bded2a5ca",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475" // "3896e1d6" truncated.
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, Encrypt) {
+ if (!ChaCha20Poly1305Encrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != NULL; i++) {
+ // Decode the test vector.
+ string key;
+ string pt;
+ string iv;
+ string aad;
+ string ct;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+
+ ChaCha20Poly1305Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ scoped_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ StringPiece(aad.length() ? aad.data() : NULL, aad.length()), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/channel_id.h b/chromium/net/quic/crypto/channel_id.h
index 2d0c29de25a..7d29932406b 100644
--- a/chromium/net/quic/crypto/channel_id.h
+++ b/chromium/net/quic/crypto/channel_id.h
@@ -7,28 +7,59 @@
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
namespace net {
-// ChannelIDSigner is an abstract interface that implements signing by
-// ChannelID keys.
-class NET_EXPORT_PRIVATE ChannelIDSigner {
+// ChannelIDKey is an interface that supports signing with and serializing a
+// ChannelID key.
+class NET_EXPORT_PRIVATE ChannelIDKey {
public:
- virtual ~ChannelIDSigner() { }
-
- // Sign signs |signed_data| using the ChannelID key for |hostname| and puts
- // the serialized public key into |out_key| and the signature into
- // |out_signature|. It returns true on success.
- virtual bool Sign(const std::string& hostname,
- base::StringPiece signed_data,
- std::string* out_key,
- std::string* out_signature) = 0;
-
- // GetKeyForHostname returns the ChannelID key that |ChannelIDSigner| will use
- // for the given hostname.
- virtual std::string GetKeyForHostname(const std::string& hostname) = 0;
+ virtual ~ChannelIDKey() {}
+
+ // Sign signs |signed_data| using the ChannelID private key and puts the
+ // signature into |out_signature|. It returns true on success.
+ virtual bool Sign(base::StringPiece signed_data,
+ std::string* out_signature) const = 0;
+
+ // SerializeKey returns the serialized ChannelID public key.
+ virtual std::string SerializeKey() const = 0;
+};
+
+// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource
+// to call back after an asynchronous GetChannelIDKey operation.
+class ChannelIDSourceCallback {
+ public:
+ virtual ~ChannelIDSourceCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not NULL
+ // then the channel ID lookup is successful. |Run| may take ownership of
+ // |*channel_id_key| by calling |release| on it.
+ virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) = 0;
+};
+
+// ChannelIDSource is an abstract interface by which a QUIC client can obtain
+// a ChannelIDKey for a given hostname.
+class NET_EXPORT_PRIVATE ChannelIDSource {
+ public:
+ virtual ~ChannelIDSource() {}
+
+ // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it
+ // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|,
+ // which the caller takes ownership of. On failure, it returns QUIC_FAILURE.
+ //
+ // This function may also return QUIC_PENDING, in which case the
+ // ChannelIDSource will call back, on the original thread, via |callback|
+ // when complete. In this case, the ChannelIDSource will take ownership of
+ // |callback|.
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) = 0;
};
// ChannelIDVerifier verifies ChannelID signatures.
@@ -57,6 +88,9 @@ class NET_EXPORT_PRIVATE ChannelIDVerifier {
base::StringPiece signed_data,
base::StringPiece signature,
bool is_channel_id_signature);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChannelIDVerifier);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/channel_id_test.cc b/chromium/net/quic/crypto/channel_id_test.cc
index 48319ec334b..711060c9d7f 100644
--- a/chromium/net/quic/crypto/channel_id_test.cc
+++ b/chromium/net/quic/crypto/channel_id_test.cc
@@ -220,16 +220,20 @@ TEST(ChannelIDTest, VerifyKnownAnswerTest) {
}
TEST(ChannelIDTest, SignAndVerify) {
- scoped_ptr<ChannelIDSigner> signer(
- CryptoTestUtils::ChannelIDSignerForTesting());
+ scoped_ptr<ChannelIDSource> source(
+ CryptoTestUtils::ChannelIDSourceForTesting());
const string signed_data = "signed data";
const string hostname = "foo.example.com";
- string key, signature;
- ASSERT_TRUE(signer->Sign(hostname, signed_data, &key, &signature));
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ source->GetChannelIDKey(hostname, &channel_id_key, NULL);
+ ASSERT_EQ(QUIC_SUCCESS, status);
- EXPECT_EQ(key, signer->GetKeyForHostname(hostname));
+ string signature;
+ ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature));
+ string key = channel_id_key->SerializeKey();
EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature));
EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature));
diff --git a/chromium/net/quic/crypto/crypto_framer.cc b/chromium/net/quic/crypto/crypto_framer.cc
index dd5d24f9ae2..5cb16743e69 100644
--- a/chromium/net/quic/crypto/crypto_framer.cc
+++ b/chromium/net/quic/crypto/crypto_framer.cc
@@ -4,7 +4,7 @@
#include "net/quic/crypto/crypto_framer.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_data_reader.h"
#include "net/quic/quic_data_writer.h"
@@ -24,22 +24,21 @@ const size_t kNumEntriesSize = sizeof(uint16);
// OneShotVisitor is a framer visitor that records a single handshake message.
class OneShotVisitor : public CryptoFramerVisitorInterface {
public:
- explicit OneShotVisitor(CryptoHandshakeMessage* out)
- : out_(out),
- error_(false) {
- }
+ OneShotVisitor() : error_(false) {}
virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE {
- *out_ = message;
+ out_.reset(new CryptoHandshakeMessage(message));
}
bool error() const { return error_; }
+ CryptoHandshakeMessage* release() { return out_.release(); }
+
private:
- CryptoHandshakeMessage* const out_;
+ scoped_ptr<CryptoHandshakeMessage> out_;
bool error_;
};
@@ -56,8 +55,7 @@ CryptoFramer::~CryptoFramer() {}
// static
CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
- scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage);
- OneShotVisitor visitor(msg.get());
+ OneShotVisitor visitor;
CryptoFramer framer;
framer.set_visitor(&visitor);
@@ -66,7 +64,7 @@ CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
return NULL;
}
- return msg.release();
+ return visitor.release();
}
bool CryptoFramer::ProcessInput(StringPiece input) {
diff --git a/chromium/net/quic/crypto/crypto_framer.h b/chromium/net/quic/crypto/crypto_framer.h
index ea69f3a5724..7ce714a666b 100644
--- a/chromium/net/quic/crypto/crypto_framer.h
+++ b/chromium/net/quic/crypto/crypto_framer.h
@@ -10,11 +10,9 @@
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_handshake.h"
-#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/crypto_handshake.cc b/chromium/net/quic/crypto/crypto_handshake.cc
index 056272e9c94..408b76bdecf 100644
--- a/chromium/net/quic/crypto/crypto_handshake.cc
+++ b/chromium/net/quic/crypto/crypto_handshake.cc
@@ -4,323 +4,13 @@
#include "net/quic/crypto/crypto_handshake.h"
-#include <ctype.h>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "crypto/secure_hash.h"
-#include "net/base/net_util.h"
#include "net/quic/crypto/common_cert_set.h"
-#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/key_exchange.h"
#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_protocol.h"
-#include "net/quic/quic_utils.h"
-
-using base::StringPiece;
-using base::StringPrintf;
-using std::string;
-using std::vector;
namespace net {
-CryptoHandshakeMessage::CryptoHandshakeMessage()
- : tag_(0),
- minimum_size_(0) {}
-
-CryptoHandshakeMessage::CryptoHandshakeMessage(
- const CryptoHandshakeMessage& other)
- : tag_(other.tag_),
- tag_value_map_(other.tag_value_map_),
- minimum_size_(other.minimum_size_) {
- // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
- // The new object can lazily reconstruct serialized_.
-}
-
-CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
-
-CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
- const CryptoHandshakeMessage& other) {
- tag_ = other.tag_;
- tag_value_map_ = other.tag_value_map_;
- // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
- // However, invalidate serialized_.
- serialized_.reset();
- minimum_size_ = other.minimum_size_;
- return *this;
-}
-
-void CryptoHandshakeMessage::Clear() {
- tag_ = 0;
- tag_value_map_.clear();
- minimum_size_ = 0;
- serialized_.reset();
-}
-
-const QuicData& CryptoHandshakeMessage::GetSerialized() const {
- if (!serialized_.get()) {
- serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
- }
- return *serialized_.get();
-}
-
-void CryptoHandshakeMessage::MarkDirty() {
- serialized_.reset();
-}
-
-void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
- // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
- // because the terminating 0 will only be promoted to int.
- COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
- crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
-
- vector<QuicTag> tags;
- va_list ap;
-
- va_start(ap, tag);
- for (;;) {
- QuicTag list_item = va_arg(ap, QuicTag);
- if (list_item == 0) {
- break;
- }
- tags.push_back(list_item);
- }
-
- // Because of the way that we keep tags in memory, we can copy the contents
- // of the vector and get the correct bytes in wire format. See
- // crypto_protocol.h. This assumes that the system is little-endian.
- SetVector(tag, tags);
-
- va_end(ap);
-}
-
-void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
- tag_value_map_[tag] = value.as_string();
-}
-
-void CryptoHandshakeMessage::Erase(QuicTag tag) {
- tag_value_map_.erase(tag);
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
- const QuicTag** out_tags,
- size_t* out_len) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- QuicErrorCode ret = QUIC_NO_ERROR;
-
- if (it == tag_value_map_.end()) {
- ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- } else if (it->second.size() % sizeof(QuicTag) != 0) {
- ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (ret != QUIC_NO_ERROR) {
- *out_tags = NULL;
- *out_len = 0;
- return ret;
- }
-
- *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
- *out_len = it->second.size() / sizeof(QuicTag);
- return ret;
-}
-
-bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
- StringPiece* out) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- if (it == tag_value_map_.end()) {
- return false;
- }
- *out = it->second;
- return true;
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
- unsigned index,
- StringPiece* out) const {
- StringPiece value;
- if (!GetStringPiece(tag, &value)) {
- return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- }
-
- for (unsigned i = 0;; i++) {
- if (value.empty()) {
- return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
- }
- if (value.size() < 3) {
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- const unsigned char* data =
- reinterpret_cast<const unsigned char*>(value.data());
- size_t size = static_cast<size_t>(data[0]) |
- (static_cast<size_t>(data[1]) << 8) |
- (static_cast<size_t>(data[2]) << 16);
- value.remove_prefix(3);
-
- if (value.size() < size) {
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (i == index) {
- *out = StringPiece(value.data(), size);
- return QUIC_NO_ERROR;
- }
-
- value.remove_prefix(size);
- }
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
- uint16* out) const {
- return GetPOD(tag, out, sizeof(uint16));
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
- uint32* out) const {
- return GetPOD(tag, out, sizeof(uint32));
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
- uint64* out) const {
- return GetPOD(tag, out, sizeof(uint64));
-}
-
-size_t CryptoHandshakeMessage::size() const {
- size_t ret = sizeof(QuicTag) +
- sizeof(uint16) /* number of entries */ +
- sizeof(uint16) /* padding */;
- ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
- tag_value_map_.size();
- for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
- i != tag_value_map_.end(); ++i) {
- ret += i->second.size();
- }
-
- return ret;
-}
-
-void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
- if (min_bytes == minimum_size_) {
- return;
- }
- serialized_.reset();
- minimum_size_ = min_bytes;
-}
-
-size_t CryptoHandshakeMessage::minimum_size() const {
- return minimum_size_;
-}
-
-string CryptoHandshakeMessage::DebugString() const {
- return DebugStringInternal(0);
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetPOD(
- QuicTag tag, void* out, size_t len) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- QuicErrorCode ret = QUIC_NO_ERROR;
-
- if (it == tag_value_map_.end()) {
- ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- } else if (it->second.size() != len) {
- ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (ret != QUIC_NO_ERROR) {
- memset(out, 0, len);
- return ret;
- }
-
- memcpy(out, it->second.data(), len);
- return ret;
-}
-
-string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
- string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
- ++indent;
- for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
- it != tag_value_map_.end(); ++it) {
- ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
-
- bool done = false;
- switch (it->first) {
- case kICSL:
- case kIRTT:
- case kKATO:
- case kMSPC:
- case kSWND:
- // uint32 value
- if (it->second.size() == 4) {
- uint32 value;
- memcpy(&value, it->second.data(), sizeof(value));
- ret += base::UintToString(value);
- done = true;
- }
- break;
- case kVERS:
- // uint16 value
- if (it->second.size() == 2) {
- uint16 value;
- memcpy(&value, it->second.data(), sizeof(value));
- ret += base::UintToString(value);
- done = true;
- }
- break;
- case kKEXS:
- case kAEAD:
- case kCGST:
- case kPDMD:
- case kVER:
- // tag lists
- if (it->second.size() % sizeof(QuicTag) == 0) {
- for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
- QuicTag tag;
- memcpy(&tag, it->second.data() + j, sizeof(tag));
- if (j > 0) {
- ret += ",";
- }
- ret += "'" + QuicUtils::TagToString(tag) + "'";
- }
- done = true;
- }
- break;
- case kSCFG:
- // nested messages.
- if (!it->second.empty()) {
- scoped_ptr<CryptoHandshakeMessage> msg(
- CryptoFramer::ParseMessage(it->second));
- if (msg.get()) {
- ret += "\n";
- ret += msg->DebugStringInternal(indent + 1);
-
- done = true;
- }
- }
- break;
- case kPAD:
- ret += StringPrintf("(%d bytes of padding)",
- static_cast<int>(it->second.size()));
- done = true;
- break;
- }
-
- if (!done) {
- // If there's no specific format for this tag, or the value is invalid,
- // then just use hex.
- ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
- }
- ret += "\n";
- }
- --indent;
- ret += string(2 * indent, ' ') + ">";
- return ret;
-}
-
QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
: key_exchange(0),
aead(0) {
diff --git a/chromium/net/quic/crypto/crypto_handshake.h b/chromium/net/quic/crypto/crypto_handshake.h
index 962e13336ef..fec53935326 100644
--- a/chromium/net/quic/crypto/crypto_handshake.h
+++ b/chromium/net/quic/crypto/crypto_handshake.h
@@ -5,143 +5,19 @@
#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
-#include <map>
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/x509_certificate.h"
-#include "net/quic/crypto/crypto_protocol.h"
-#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/quic_protocol.h"
namespace net {
-class ChannelIDSigner;
class CommonCertSets;
class KeyExchange;
-class ProofVerifier;
class QuicDecrypter;
class QuicEncrypter;
-class QuicRandom;
-
-// An intermediate format of a handshake message that's convenient for a
-// CryptoFramer to serialize from or parse into.
-class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
- public:
- CryptoHandshakeMessage();
- CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
- ~CryptoHandshakeMessage();
-
- CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
-
- // Clears state.
- void Clear();
-
- // GetSerialized returns the serialized form of this message and caches the
- // result. Subsequently altering the message does not invalidate the cache.
- const QuicData& GetSerialized() const;
-
- // MarkDirty invalidates the cache created by |GetSerialized|.
- void MarkDirty();
-
- // SetValue sets an element with the given tag to the raw, memory contents of
- // |v|.
- template<class T> void SetValue(QuicTag tag, const T& v) {
- tag_value_map_[tag] =
- std::string(reinterpret_cast<const char*>(&v), sizeof(v));
- }
-
- // SetVector sets an element with the given tag to the raw contents of an
- // array of elements in |v|.
- template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) {
- if (v.empty()) {
- tag_value_map_[tag] = std::string();
- } else {
- tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
- v.size() * sizeof(T));
- }
- }
-
- // Returns the message tag.
- QuicTag tag() const { return tag_; }
- // Sets the message tag.
- void set_tag(QuicTag tag) { tag_ = tag; }
-
- const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
-
- // SetTaglist sets an element with the given tag to contain a list of tags,
- // passed as varargs. The argument list must be terminated with a 0 element.
- void SetTaglist(QuicTag tag, ...);
-
- void SetStringPiece(QuicTag tag, base::StringPiece value);
-
- // Erase removes a tag/value, if present, from the message.
- void Erase(QuicTag tag);
-
- // GetTaglist finds an element with the given tag containing zero or more
- // tags. If such a tag doesn't exist, it returns false. Otherwise it sets
- // |out_tags| and |out_len| to point to the array of tags and returns true.
- // The array points into the CryptoHandshakeMessage and is valid only for as
- // long as the CryptoHandshakeMessage exists and is not modified.
- QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags,
- size_t* out_len) const;
-
- bool GetStringPiece(QuicTag tag, base::StringPiece* out) const;
-
- // GetNthValue24 interprets the value with the given tag to be a series of
- // 24-bit, length prefixed values and it returns the subvalue with the given
- // index.
- QuicErrorCode GetNthValue24(QuicTag tag,
- unsigned index,
- base::StringPiece* out) const;
- QuicErrorCode GetUint16(QuicTag tag, uint16* out) const;
- QuicErrorCode GetUint32(QuicTag tag, uint32* out) const;
- QuicErrorCode GetUint64(QuicTag tag, uint64* out) const;
-
- // size returns 4 (message tag) + 2 (uint16, number of entries) +
- // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
- size_t size() const;
-
- // set_minimum_size sets the minimum number of bytes that the message should
- // consume. The CryptoFramer will add a PAD tag as needed when serializing in
- // order to ensure this. Setting a value of 0 disables padding.
- //
- // Padding is useful in order to ensure that messages are a minimum size. A
- // QUIC server can require a minimum size in order to reduce the
- // amplification factor of any mirror DoS attack.
- void set_minimum_size(size_t min_bytes);
-
- size_t minimum_size() const;
-
- // DebugString returns a multi-line, string representation of the message
- // suitable for including in debug output.
- std::string DebugString() const;
-
- private:
- // GetPOD is a utility function for extracting a plain-old-data value. If
- // |tag| exists in the message, and has a value of exactly |len| bytes then
- // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
- // are zeroed out.
- //
- // If used to copy integers then this assumes that the machine is
- // little-endian.
- QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
-
- std::string DebugStringInternal(size_t indent) const;
-
- QuicTag tag_;
- QuicTagValueMap tag_value_map_;
-
- size_t minimum_size_;
-
- // The serialized form of the handshake message. This member is constructed
- // lasily.
- mutable scoped_ptr<QuicData> serialized_;
-};
// A CrypterPair contains the encrypter and decrypter for an encryption level.
struct NET_EXPORT_PRIVATE CrypterPair {
@@ -167,10 +43,10 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
std::string sni;
std::string client_nonce;
std::string server_nonce;
- // hkdf_input_suffix contains the HKDF input following the label: the GUID,
- // client hello and server config. This is only populated in the client
- // because only the client needs to derive the forward secure keys at a later
- // time from the initial keys.
+ // hkdf_input_suffix contains the HKDF input following the label: the
+ // ConnectionId, client hello and server config. This is only populated in the
+ // client because only the client needs to derive the forward secure keys at a
+ // later time from the initial keys.
std::string hkdf_input_suffix;
// cached_certs contains the cached certificates that a client used when
// sending a client hello.
diff --git a/chromium/net/quic/crypto/crypto_handshake_message.cc b/chromium/net/quic/crypto/crypto_handshake_message.cc
new file mode 100644
index 00000000000..77f7c525cf3
--- /dev/null
+++ b/chromium/net/quic/crypto/crypto_handshake_message.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_handshake_message.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace net {
+
+CryptoHandshakeMessage::CryptoHandshakeMessage()
+ : tag_(0),
+ minimum_size_(0) {}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(
+ const CryptoHandshakeMessage& other)
+ : tag_(other.tag_),
+ tag_value_map_(other.tag_value_map_),
+ minimum_size_(other.minimum_size_) {
+ // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
+ // The new object can lazily reconstruct serialized_.
+}
+
+CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ const CryptoHandshakeMessage& other) {
+ tag_ = other.tag_;
+ tag_value_map_ = other.tag_value_map_;
+ // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
+ // However, invalidate serialized_.
+ serialized_.reset();
+ minimum_size_ = other.minimum_size_;
+ return *this;
+}
+
+void CryptoHandshakeMessage::Clear() {
+ tag_ = 0;
+ tag_value_map_.clear();
+ minimum_size_ = 0;
+ serialized_.reset();
+}
+
+const QuicData& CryptoHandshakeMessage::GetSerialized() const {
+ if (!serialized_.get()) {
+ serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
+ }
+ return *serialized_.get();
+}
+
+void CryptoHandshakeMessage::MarkDirty() {
+ serialized_.reset();
+}
+
+void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
+ // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
+ // because the terminating 0 will only be promoted to int.
+ COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
+ crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
+
+ vector<QuicTag> tags;
+ va_list ap;
+
+ va_start(ap, tag);
+ for (;;) {
+ QuicTag list_item = va_arg(ap, QuicTag);
+ if (list_item == 0) {
+ break;
+ }
+ tags.push_back(list_item);
+ }
+
+ // Because of the way that we keep tags in memory, we can copy the contents
+ // of the vector and get the correct bytes in wire format. See
+ // crypto_protocol.h. This assumes that the system is little-endian.
+ SetVector(tag, tags);
+
+ va_end(ap);
+}
+
+void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
+ tag_value_map_[tag] = value.as_string();
+}
+
+void CryptoHandshakeMessage::Erase(QuicTag tag) {
+ tag_value_map_.erase(tag);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
+ const QuicTag** out_tags,
+ size_t* out_len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() % sizeof(QuicTag) != 0) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ *out_tags = NULL;
+ *out_len = 0;
+ return ret;
+ }
+
+ *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
+ *out_len = it->second.size() / sizeof(QuicTag);
+ return ret;
+}
+
+bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
+ StringPiece* out) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ if (it == tag_value_map_.end()) {
+ return false;
+ }
+ *out = it->second;
+ return true;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
+ unsigned index,
+ StringPiece* out) const {
+ StringPiece value;
+ if (!GetStringPiece(tag, &value)) {
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ for (unsigned i = 0;; i++) {
+ if (value.empty()) {
+ return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
+ }
+ if (value.size() < 3) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(value.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8) |
+ (static_cast<size_t>(data[2]) << 16);
+ value.remove_prefix(3);
+
+ if (value.size() < size) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (i == index) {
+ *out = StringPiece(value.data(), size);
+ return QUIC_NO_ERROR;
+ }
+
+ value.remove_prefix(size);
+ }
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
+ uint16* out) const {
+ return GetPOD(tag, out, sizeof(uint16));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
+ uint32* out) const {
+ return GetPOD(tag, out, sizeof(uint32));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
+ uint64* out) const {
+ return GetPOD(tag, out, sizeof(uint64));
+}
+
+size_t CryptoHandshakeMessage::size() const {
+ size_t ret = sizeof(QuicTag) +
+ sizeof(uint16) /* number of entries */ +
+ sizeof(uint16) /* padding */;
+ ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
+ tag_value_map_.size();
+ for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
+ i != tag_value_map_.end(); ++i) {
+ ret += i->second.size();
+ }
+
+ return ret;
+}
+
+void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
+ serialized_.reset();
+ minimum_size_ = min_bytes;
+}
+
+size_t CryptoHandshakeMessage::minimum_size() const {
+ return minimum_size_;
+}
+
+string CryptoHandshakeMessage::DebugString() const {
+ return DebugStringInternal(0);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetPOD(
+ QuicTag tag, void* out, size_t len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() != len) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ memset(out, 0, len);
+ return ret;
+ }
+
+ memcpy(out, it->second.data(), len);
+ return ret;
+}
+
+string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
+ string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
+ ++indent;
+ for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
+ it != tag_value_map_.end(); ++it) {
+ ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
+
+ bool done = false;
+ switch (it->first) {
+ case kICSL:
+ case kIFCW:
+ case kCFCW:
+ case kSFCW:
+ case kIRTT:
+ case kKATO:
+ case kMSPC:
+ case kSWND:
+ // uint32 value
+ if (it->second.size() == 4) {
+ uint32 value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ ret += base::UintToString(value);
+ done = true;
+ }
+ break;
+ case kKEXS:
+ case kAEAD:
+ case kCGST:
+ case kCOPT:
+ case kLOSS:
+ case kPDMD:
+ case kVER:
+ // tag lists
+ if (it->second.size() % sizeof(QuicTag) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + j, sizeof(tag));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += "'" + QuicUtils::TagToString(tag) + "'";
+ }
+ done = true;
+ }
+ break;
+ case kCADR:
+ // IP address and port
+ if (!it->second.empty()) {
+ QuicSocketAddressCoder decoder;
+ if (decoder.Decode(it->second.data(), it->second.size())) {
+ ret += IPAddressToStringWithPort(decoder.ip(), decoder.port());
+ done = true;
+ }
+ }
+ break;
+ case kSCFG:
+ // nested messages.
+ if (!it->second.empty()) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(it->second));
+ if (msg.get()) {
+ ret += "\n";
+ ret += msg->DebugStringInternal(indent + 1);
+
+ done = true;
+ }
+ }
+ break;
+ case kPAD:
+ ret += StringPrintf("(%d bytes of padding)",
+ static_cast<int>(it->second.size()));
+ done = true;
+ break;
+ case kUAID:
+ ret += it->second;
+ done = true;
+ break;
+ }
+
+ if (!done) {
+ // If there's no specific format for this tag, or the value is invalid,
+ // then just use hex.
+ ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
+ }
+ ret += "\n";
+ }
+ --indent;
+ ret += string(2 * indent, ' ') + ">";
+ return ret;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_handshake_message.h b/chromium/net/quic/crypto/crypto_handshake_message.h
new file mode 100644
index 00000000000..fcb39307b1c
--- /dev/null
+++ b/chromium/net/quic/crypto/crypto_handshake_message.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// An intermediate format of a handshake message that's convenient for a
+// CryptoFramer to serialize from or parse into.
+class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
+ public:
+ CryptoHandshakeMessage();
+ CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
+ ~CryptoHandshakeMessage();
+
+ CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
+
+ // Clears state.
+ void Clear();
+
+ // GetSerialized returns the serialized form of this message and caches the
+ // result. Subsequently altering the message does not invalidate the cache.
+ const QuicData& GetSerialized() const;
+
+ // MarkDirty invalidates the cache created by |GetSerialized|.
+ void MarkDirty();
+
+ // SetValue sets an element with the given tag to the raw, memory contents of
+ // |v|.
+ template<class T> void SetValue(QuicTag tag, const T& v) {
+ tag_value_map_[tag] =
+ std::string(reinterpret_cast<const char*>(&v), sizeof(v));
+ }
+
+ // SetVector sets an element with the given tag to the raw contents of an
+ // array of elements in |v|.
+ template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) {
+ if (v.empty()) {
+ tag_value_map_[tag] = std::string();
+ } else {
+ tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
+ v.size() * sizeof(T));
+ }
+ }
+
+ // Returns the message tag.
+ QuicTag tag() const { return tag_; }
+ // Sets the message tag.
+ void set_tag(QuicTag tag) { tag_ = tag; }
+
+ const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
+
+ // SetTaglist sets an element with the given tag to contain a list of tags,
+ // passed as varargs. The argument list must be terminated with a 0 element.
+ void SetTaglist(QuicTag tag, ...);
+
+ void SetStringPiece(QuicTag tag, base::StringPiece value);
+
+ // Erase removes a tag/value, if present, from the message.
+ void Erase(QuicTag tag);
+
+ // GetTaglist finds an element with the given tag containing zero or more
+ // tags. If such a tag doesn't exist, it returns false. Otherwise it sets
+ // |out_tags| and |out_len| to point to the array of tags and returns true.
+ // The array points into the CryptoHandshakeMessage and is valid only for as
+ // long as the CryptoHandshakeMessage exists and is not modified.
+ QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags,
+ size_t* out_len) const;
+
+ bool GetStringPiece(QuicTag tag, base::StringPiece* out) const;
+
+ // GetNthValue24 interprets the value with the given tag to be a series of
+ // 24-bit, length prefixed values and it returns the subvalue with the given
+ // index.
+ QuicErrorCode GetNthValue24(QuicTag tag,
+ unsigned index,
+ base::StringPiece* out) const;
+ QuicErrorCode GetUint16(QuicTag tag, uint16* out) const;
+ QuicErrorCode GetUint32(QuicTag tag, uint32* out) const;
+ QuicErrorCode GetUint64(QuicTag tag, uint64* out) const;
+
+ // size returns 4 (message tag) + 2 (uint16, number of entries) +
+ // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
+ size_t size() const;
+
+ // set_minimum_size sets the minimum number of bytes that the message should
+ // consume. The CryptoFramer will add a PAD tag as needed when serializing in
+ // order to ensure this. Setting a value of 0 disables padding.
+ //
+ // Padding is useful in order to ensure that messages are a minimum size. A
+ // QUIC server can require a minimum size in order to reduce the
+ // amplification factor of any mirror DoS attack.
+ void set_minimum_size(size_t min_bytes);
+
+ size_t minimum_size() const;
+
+ // DebugString returns a multi-line, string representation of the message
+ // suitable for including in debug output.
+ std::string DebugString() const;
+
+ private:
+ // GetPOD is a utility function for extracting a plain-old-data value. If
+ // |tag| exists in the message, and has a value of exactly |len| bytes then
+ // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
+ // are zeroed out.
+ //
+ // If used to copy integers then this assumes that the machine is
+ // little-endian.
+ QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
+
+ std::string DebugStringInternal(size_t indent) const;
+
+ QuicTag tag_;
+ QuicTagValueMap tag_value_map_;
+
+ size_t minimum_size_;
+
+ // The serialized form of the handshake message. This member is constructed
+ // lasily.
+ mutable scoped_ptr<QuicData> serialized_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/chromium/net/quic/crypto/crypto_protocol.h b/chromium/net/quic/crypto/crypto_protocol.h
index 1971b58c543..f2e7a16399f 100644
--- a/chromium/net/quic/crypto/crypto_protocol.h
+++ b/chromium/net/quic/crypto/crypto_protocol.h
@@ -5,9 +5,7 @@
#ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
#define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
-#include <map>
#include <string>
-#include <vector>
#include "net/base/net_export.h"
#include "net/quic/quic_protocol.h"
@@ -26,71 +24,98 @@
namespace net {
typedef std::string ServerConfigID;
-typedef std::map<QuicTag, std::string> QuicTagValueMap;
-const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
-const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
-const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
-const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
-const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
- // pairs
+const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
+const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
+const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
+const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
+const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
+ // pairs
+const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset
// Key exchange methods
-const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
-const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
+const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
+const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
// AEAD algorithms
-const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm
-const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm
+const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kCC12 = TAG('C', 'C', '1', '2'); // ChaCha20 + Poly1305
// Congestion control feedback types
-const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
-const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic
-const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival
+const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
+const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic
+const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival
+
+// Congestion control options
+const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP
+
+// Loss detection algorithm types
+const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting
+const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based
// Proof types (i.e. certificate types)
// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
// is allowed and is equivalent to specifying only kX509.
-const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
- // types
-const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
- // only
-const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
+const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
+ // types
+const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
+ // only
+const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
// Client hello tags
-// TODO(rch): Remove once we remove QUIC_VERSION_12.
-const QuicTag kVERS = TAG('V', 'E', 'R', 'S'); // Version (obsolete)
-const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new)
-const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
-const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
-const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
- // encryption algorithms
-const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
- // feedback types
-const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
- // lifetime
-const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
-const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
-const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
-const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion
- // window.
-const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
- // indication
-const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
-const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
-const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
-const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
-const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
-const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
-const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
-const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new)
+const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
+const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
+const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
+ // encryption algorithms
+const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
+ // feedback types
+const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Congestion control options
+// kLOSS was 'L', 'O', 'S', 'S', but was changed from a tag vector to a tag.
+const QuicTag kLOSS = TAG('L', 'O', 'S', 'A'); // Loss detection algorithms
+const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
+ // lifetime
+const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
+const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
+const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
+const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion
+ // window.
+const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
+ // indication
+const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
+const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
+const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
+const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
+const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
+const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
+const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
+const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+// TODO(rjshade): Remove kIFCW when removing QUIC_VERSION_19.
+const QuicTag kIFCW = TAG('I', 'F', 'C', 'W'); // Initial flow control receive
+ // window.
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control
+ // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection
+ // flow control receive window.
+const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
+
+// Server hello tags
+const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port
// CETV tags
-const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
-const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
+const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+
+// Public reset tags
+const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof
+const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected sequence number
// Universal tags
-const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+
+// Reasons for server sending rejection message tag.
+const QuicTag kRREJ = TAG('R', 'R', 'E', 'J');
// These tags have a special form so that they appear either at the beginning
// or the end of a handshake message. Since handshake messages are sorted by
@@ -135,12 +160,6 @@ const char kProofSignatureLabel[] = "QUIC server config signature";
// rejection message.
const size_t kClientHelloMinimumSize = 1024;
-// kClientHelloMinimumSizeOld is the previous value of kClientHelloMinimumSize.
-// To support old clients, the server only enforces this size.
-// TODO(wtc): Replace it with kClientHelloMinimumSize when we drop support for
-// QUIC_VERSION_12 clients.
-const size_t kClientHelloMinimumSizeOld = 512;
-
} // namespace net
#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.cc b/chromium/net/quic/crypto/crypto_secret_boxer.cc
index 73562c638bc..5d8a11b27a3 100644
--- a/chromium/net/quic/crypto/crypto_secret_boxer.cc
+++ b/chromium/net/quic/crypto/crypto_secret_boxer.cc
@@ -6,8 +6,9 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "crypto/secure_hash.h"
-#include "crypto/sha2.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
using base::StringPiece;
@@ -19,21 +20,36 @@ namespace net {
static const size_t kKeySize = 16;
// kBoxNonceSize contains the number of bytes of nonce that we use in each box.
-static const size_t kBoxNonceSize = 16;
+// TODO(rtenneti): Add support for kBoxNonceSize to be 16 bytes.
+//
+// From agl@:
+// 96-bit nonces are on the edge. An attacker who can collect 2^41
+// source-address tokens has a 1% chance of finding a duplicate.
+//
+// The "average" DDoS is now 32.4M PPS. That's 2^25 source-address tokens
+// per second. So one day of that DDoS botnot would reach the 1% mark.
+//
+// It's not terrible, but it's not a "forget about it" margin.
+static const size_t kBoxNonceSize = 12;
// static
size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; }
void CryptoSecretBoxer::SetKey(StringPiece key) {
- DCHECK_EQ(static_cast<size_t>(kKeySize), key.size());
+ DCHECK_EQ(kKeySize, key.size());
key_ = key.as_string();
}
-// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Encrypter to Box the
-// plaintext. This is temporary solution for tests to pass.
string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
+ scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG));
+ if (!encrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed.";
+ return string();
+ }
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+
string ret;
- const size_t len = kBoxNonceSize + plaintext.size() + crypto::kSHA256Length;
+ const size_t len = kBoxNonceSize + ciphertext_size;
ret.resize(len);
char* data = &ret[0];
@@ -41,49 +57,43 @@ string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
rand->RandBytes(data, kBoxNonceSize);
memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size());
- // Compute sha256 for nonce + plaintext.
- scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create(
- crypto::SecureHash::SHA256));
- sha256->Update(data, kBoxNonceSize + plaintext.size());
- sha256->Finish(data + kBoxNonceSize + plaintext.size(),
- crypto::kSHA256Length);
+ if (!encrypter->Encrypt(StringPiece(data, kBoxNonceSize), StringPiece(),
+ plaintext, reinterpret_cast<unsigned char*>(
+ data + kBoxNonceSize))) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's Encrypt failed.";
+ return string();
+ }
return ret;
}
-// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Decrypter to Unbox
-// the plaintext. This is temporary solution for tests to pass.
bool CryptoSecretBoxer::Unbox(StringPiece ciphertext,
string* out_storage,
StringPiece* out) const {
- if (ciphertext.size() < kBoxNonceSize + crypto::kSHA256Length) {
+ if (ciphertext.size() < kBoxNonceSize) {
return false;
}
- const size_t plaintext_len =
- ciphertext.size() - kBoxNonceSize - crypto::kSHA256Length;
- out_storage->resize(plaintext_len);
+ char nonce[kBoxNonceSize];
+ memcpy(nonce, ciphertext.data(), kBoxNonceSize);
+ ciphertext.remove_prefix(kBoxNonceSize);
+
+ size_t len = ciphertext.size();
+ out_storage->resize(len);
char* data = const_cast<char*>(out_storage->data());
- // Copy plaintext from ciphertext.
- if (plaintext_len != 0) {
- memcpy(data, ciphertext.data() + kBoxNonceSize, plaintext_len);
+ scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG));
+ if (!decrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed.";
+ return false;
}
-
- // Compute sha256 for nonce + plaintext.
- scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create(
- crypto::SecureHash::SHA256));
- sha256->Update(ciphertext.data(), ciphertext.size() - crypto::kSHA256Length);
- char sha256_bytes[crypto::kSHA256Length];
- sha256->Finish(sha256_bytes, sizeof(sha256_bytes));
-
- // Verify sha256.
- if (0 != memcmp(ciphertext.data() + ciphertext.size() - crypto::kSHA256Length,
- sha256_bytes, crypto::kSHA256Length)) {
+ if (!decrypter->Decrypt(StringPiece(nonce, kBoxNonceSize), StringPiece(),
+ ciphertext, reinterpret_cast<unsigned char*>(data),
+ &len)) {
return false;
}
- out->set(data, plaintext_len);
+ out->set(data, len);
return true;
}
diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.h b/chromium/net/quic/crypto/crypto_secret_boxer.h
index ba9baf2bb2a..38b8fb339ec 100644
--- a/chromium/net/quic/crypto/crypto_secret_boxer.h
+++ b/chromium/net/quic/crypto/crypto_secret_boxer.h
@@ -19,6 +19,8 @@ class QuicRandom;
// thread-safe.
class NET_EXPORT_PRIVATE CryptoSecretBoxer {
public:
+ CryptoSecretBoxer() {}
+
// GetKeySize returns the number of bytes in a key.
static size_t GetKeySize();
@@ -42,6 +44,8 @@ class NET_EXPORT_PRIVATE CryptoSecretBoxer {
private:
std::string key_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
index a3418e4228a..d292f9e9db9 100644
--- a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
+++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
@@ -10,7 +10,8 @@
namespace net {
QuicServerConfigProtobuf::QuicServerConfigProtobuf()
- : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()) {
+ : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()),
+ priority_(0) {
}
QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.h b/chromium/net/quic/crypto/crypto_server_config_protobuf.h
index 6340ae023c4..57ebfb04847 100644
--- a/chromium/net/quic/crypto/crypto_server_config_protobuf.h
+++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.h
@@ -61,7 +61,7 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
}
void set_config(base::StringPiece config) {
- config_ = config.as_string();
+ config.CopyToString(&config_);
}
QuicServerConfigProtobuf::PrivateKey* add_key() {
@@ -85,6 +85,32 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
primary_time_ = primary_time;
}
+ bool has_priority() const {
+ return priority_ > 0;
+ }
+
+ uint64 priority() const {
+ return priority_;
+ }
+
+ void set_priority(int64 priority) {
+ priority_ = priority;
+ }
+
+ bool has_source_address_token_secret_override() const {
+ return !source_address_token_secret_override_.empty();
+ }
+
+ std::string source_address_token_secret_override() const {
+ return source_address_token_secret_override_;
+ }
+
+ void set_source_address_token_secret_override(
+ base::StringPiece source_address_token_secret_override) {
+ source_address_token_secret_override.CopyToString(
+ &source_address_token_secret_override_);
+ }
+
private:
std::vector<PrivateKey*> keys_;
@@ -94,6 +120,18 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
// primary_time_ contains a UNIX epoch seconds value that indicates when this
// config should become primary.
int64 primary_time_;
+
+ // Relative priority of this config vs other configs with the same
+ // primary time. For use as a secondary sort key when selecting the
+ // primary config.
+ uint64 priority_;
+
+ // Optional override to the secret used to box/unbox source address
+ // tokens when talking to clients that select this server config.
+ // It can be of any length as it is fed into a KDF before use.
+ std::string source_address_token_secret_override_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerConfigProtobuf);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_server_test.cc b/chromium/net/quic/crypto/crypto_server_test.cc
index 4eec2a86a28..7cb7e990fa2 100644
--- a/chromium/net/quic/crypto/crypto_server_test.cc
+++ b/chromium/net/quic/crypto/crypto_server_test.cc
@@ -2,33 +2,94 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <ostream>
+#include <vector>
+
+#include "base/basictypes.h"
#include "base/strings/string_number_conversions.h"
#include "crypto/secure_hash.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
+using std::ostream;
using std::string;
+using std::vector;
namespace net {
namespace test {
-class CryptoServerTest : public ::testing::Test {
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
+ }
+
+ private:
+ QuicCryptoServerConfig* server_config_;
+};
+
+// Run tests with combinations of
+// {FLAGS_use_early_return_when_verifying_chlo,
+// FLAGS_send_quic_crypto_reject_reason}.
+struct TestParams {
+ TestParams(bool use_early_return_when_verifying_chlo,
+ bool send_quic_crypto_reject_reason)
+ : use_early_return_when_verifying_chlo(
+ use_early_return_when_verifying_chlo),
+ send_quic_crypto_reject_reason(send_quic_crypto_reject_reason) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ use_early_return_when_verifying_chlo: "
+ << p.use_early_return_when_verifying_chlo
+ << " send_quic_crypto_reject_reason: "
+ << p.send_quic_crypto_reject_reason << " }";
+ return os;
+ }
+
+ bool use_early_return_when_verifying_chlo;
+ bool send_quic_crypto_reject_reason;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ params.push_back(TestParams(false, false));
+ params.push_back(TestParams(false, true));
+ params.push_back(TestParams(true, false));
+ params.push_back(TestParams(true, true));
+ return params;
+}
+
+class CryptoServerTest : public ::testing::TestWithParam<TestParams> {
public:
CryptoServerTest()
: rand_(QuicRandom::GetInstance()),
- config_(QuicCryptoServerConfig::TESTING, rand_),
- addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ?
- ip_ : IPAddressNumber(), 1) {
+ client_address_(Loopback4(), 1234),
+ config_(QuicCryptoServerConfig::TESTING, rand_) {
config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting());
supported_versions_ = QuicSupportedVersions();
+ client_version_ = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
+ FLAGS_use_early_return_when_verifying_chlo =
+ GetParam().use_early_return_when_verifying_chlo;
+ FLAGS_send_quic_crypto_reject_reason =
+ GetParam().send_quic_crypto_reject_reason;
}
virtual void SetUp() {
@@ -54,12 +115,17 @@ class CryptoServerTest : public ::testing::Test {
"KEXS", "C255",
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
ShouldSucceed(client_hello);
// The message should be rejected because the source-address token is
// missing.
ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
StringPiece srct;
ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
@@ -91,6 +157,15 @@ class CryptoServerTest : public ::testing::Test {
virtual void RunImpl(const CryptoHandshakeMessage& client_hello,
const Result& result) OVERRIDE {
+ {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(&test_->config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current
+ // thread. Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+ }
ASSERT_FALSE(*called_);
test_->ProcessValidationResult(
client_hello, result, should_succeed_, error_substr_);
@@ -104,17 +179,33 @@ class CryptoServerTest : public ::testing::Test {
bool* called_;
};
+ void CheckServerHello(const CryptoHandshakeMessage& server_hello) {
+ const QuicTag* versions;
+ size_t num_versions;
+ server_hello.GetTaglist(kVER, &versions, &num_versions);
+ ASSERT_EQ(QuicSupportedVersions().size(), num_versions);
+ for (size_t i = 0; i < num_versions; ++i) {
+ EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]);
+ }
+
+ StringPiece address;
+ ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address));
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(address.data(), address.size()));
+ EXPECT_EQ(client_address_.address(), decoder.ip());
+ EXPECT_EQ(client_address_.port(), decoder.port());
+ }
+
void ShouldSucceed(const CryptoHandshakeMessage& message) {
bool called = false;
- ShouldSucceed(message, &called);
+ RunValidate(message, new ValidateCallback(this, true, "", &called));
EXPECT_TRUE(called);
}
- void ShouldSucceed(const CryptoHandshakeMessage& message,
- bool* called) {
- config_.ValidateClientHello(
- message, addr_, &clock_,
- new ValidateCallback(this, true, "", called));
+ void RunValidate(
+ const CryptoHandshakeMessage& message,
+ ValidateClientHelloResultCallback* cb) {
+ config_.ValidateClientHello(message, client_address_, &clock_, cb);
}
void ShouldFailMentioning(const char* error_substr,
@@ -128,7 +219,7 @@ class CryptoServerTest : public ::testing::Test {
const CryptoHandshakeMessage& message,
bool* called) {
config_.ValidateClientHello(
- message, addr_, &clock_,
+ message, client_address_, &clock_,
new ValidateCallback(this, false, error_substr, called));
}
@@ -138,7 +229,7 @@ class CryptoServerTest : public ::testing::Test {
const char* error_substr) {
string error_details;
QuicErrorCode error = config_.ProcessClientHello(
- result, 1 /* GUID */, addr_,
+ result, 1 /* ConnectionId */, client_address_,
supported_versions_.front(), supported_versions_, &clock_, rand_,
&params_, &out_, &error_details);
@@ -176,16 +267,39 @@ class CryptoServerTest : public ::testing::Test {
return nonce;
}
+ void CheckRejectReasons(
+ const HandshakeFailureReason* expected_handshake_failures,
+ size_t expected_count) {
+ const QuicTag* reject_reason_tags;
+ size_t num_reject_reasons;
+ QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reason_tags,
+ &num_reject_reasons);
+ if (!FLAGS_send_quic_crypto_reject_reason) {
+ ASSERT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error_code);
+ return;
+ }
+ ASSERT_EQ(QUIC_NO_ERROR, error_code);
+
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ EXPECT_EQ(1u, num_reject_reasons);
+ } else {
+ EXPECT_EQ(expected_count, num_reject_reasons);
+ }
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ EXPECT_EQ(expected_handshake_failures[i], reject_reason_tags[i]);
+ }
+ }
+
protected:
QuicRandom* const rand_;
MockClock clock_;
+ const IPEndPoint client_address_;
QuicVersionVector supported_versions_;
+ string client_version_;
QuicCryptoServerConfig config_;
QuicCryptoServerConfig::ConfigOptions config_options_;
QuicCryptoNegotiatedParameters params_;
CryptoHandshakeMessage out_;
- IPAddressNumber ip_;
- IPEndPoint addr_;
uint8 orbit_[kOrbitSize];
// These strings contain hex escaped values from the server suitable for
@@ -194,7 +308,14 @@ class CryptoServerTest : public ::testing::Test {
scoped_ptr<CryptoHandshakeMessage> server_config_;
};
-TEST_F(CryptoServerTest, BadSNI) {
+// Run all CryptoServerTest with all combinations of
+// FLAGS_use_early_return_when_verifying_chlo and
+// FLAGS_send_quic_crypto_reject_reason.
+INSTANTIATE_TEST_CASE_P(CryptoServerTests,
+ CryptoServerTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(CryptoServerTest, BadSNI) {
static const char* kBadSNIs[] = {
"",
"foo",
@@ -204,11 +325,19 @@ TEST_F(CryptoServerTest, BadSNI) {
"ffee::1",
};
+ string client_version = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
for (size_t i = 0; i < arraysize(kBadSNIs); i++) {
ShouldFailMentioning("SNI", InchoateClientHello(
"CHLO",
"SNI", kBadSNIs[i],
+ "VER\0", client_version.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
@@ -226,6 +355,7 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) {
"NONC", nonce_hex_.c_str(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
"PDMD", "X509",
+ "VER\0", client_version_.data(),
NULL));
StringPiece cert, proof;
@@ -233,15 +363,24 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) {
EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
EXPECT_NE(0u, cert.size());
EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, TooSmall) {
+TEST_P(CryptoServerTest, TooSmall) {
ShouldFailMentioning("too small", CryptoTestUtils::Message(
"CHLO",
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, BadSourceAddressToken) {
+TEST_P(CryptoServerTest, BadSourceAddressToken) {
// Invalid source-address tokens should be ignored.
static const char* kBadSourceAddressTokens[] = {
"",
@@ -254,11 +393,16 @@ TEST_F(CryptoServerTest, BadSourceAddressToken) {
ShouldSucceed(InchoateClientHello(
"CHLO",
"STK", kBadSourceAddressTokens[i],
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
-TEST_F(CryptoServerTest, BadClientNonce) {
+TEST_P(CryptoServerTest, BadClientNonce) {
// Invalid nonces should be ignored.
static const char* kBadNonces[] = {
"",
@@ -270,27 +414,124 @@ TEST_F(CryptoServerTest, BadClientNonce) {
ShouldSucceed(InchoateClientHello(
"CHLO",
"NONC", kBadNonces[i],
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
-TEST_F(CryptoServerTest, DowngradeAttack) {
+TEST_P(CryptoServerTest, DowngradeAttack) {
if (supported_versions_.size() == 1) {
// No downgrade attack is possible if the server only supports one version.
return;
}
// Set the client's preferred version to a supported version that
// is not the "current" version (supported_versions_.front()).
- string client_version = QuicUtils::TagToString(
+ string bad_version = QuicUtils::TagToString(
QuicVersionToQuicTag(supported_versions_.back()));
ShouldFailMentioning("Downgrade", InchoateClientHello(
"CHLO",
- "VER\0", client_version.data(),
+ "VER\0", bad_version.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptServerConfig) {
+ // This tests corrupted server config.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", (string(1, 'X') + scid_hex_).c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, ReplayProtection) {
+TEST_P(CryptoServerTest, CorruptSourceAddressToken) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) {
+ // This test corrupts client nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptMultipleTags) {
+ // This test corrupts client nonce, server nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "SNO\0", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE,
+ SERVER_NONCE_DECRYPTION_FAILURE,
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, ReplayProtection) {
// This tests that disabling replay protection works.
CryptoHandshakeMessage msg = CryptoTestUtils::Message(
"CHLO",
@@ -300,6 +541,7 @@ TEST_F(CryptoServerTest, ReplayProtection) {
"#004b5453", srct_hex_.c_str(),
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
ShouldSucceed(msg);
@@ -307,22 +549,22 @@ TEST_F(CryptoServerTest, ReplayProtection) {
// quiescent.
ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+
config_.set_replay_protection(false);
ShouldSucceed(msg);
// The message should be accepted now.
ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
ShouldSucceed(msg);
// The message should accepted twice when replay protection is off.
ASSERT_EQ(kSHLO, out_.tag());
- const QuicTag* versions;
- size_t num_versions;
- out_.GetTaglist(kVER, &versions, &num_versions);
- ASSERT_EQ(QuicSupportedVersions().size(), num_versions);
- for (size_t i = 0; i < num_versions; ++i) {
- EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]);
- }
+ CheckServerHello(out_);
}
TEST(CryptoServerConfigGenerationTest, Determinism) {
@@ -392,7 +634,7 @@ TEST(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) {
hash->Finish(digest, sizeof(digest));
ASSERT_EQ(scid.size(), sizeof(digest));
- EXPECT_TRUE(0 == memcmp(digest, scid_str.data(), sizeof(digest)));
+ EXPECT_EQ(0, memcmp(digest, scid_str.data(), sizeof(digest)));
}
class CryptoServerTestNoConfig : public CryptoServerTest {
@@ -402,10 +644,16 @@ class CryptoServerTestNoConfig : public CryptoServerTest {
}
};
-TEST_F(CryptoServerTestNoConfig, DontCrash) {
- ShouldFailMentioning("No config", InchoateClientHello(
- "CHLO",
- NULL));
+TEST_P(CryptoServerTestNoConfig, DontCrash) {
+ ShouldFailMentioning("No config", InchoateClientHello(
+ "CHLO",
+ "VER\0", client_version_.data(),
+ NULL));
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
class AsyncStrikeServerVerificationTest : public CryptoServerTest {
@@ -430,7 +678,7 @@ class AsyncStrikeServerVerificationTest : public CryptoServerTest {
DelayedVerifyStrikeRegisterClient* strike_register_client_;
};
-TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
+TEST_P(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
// This tests async validation with a strike register works.
CryptoHandshakeMessage msg = CryptoTestUtils::Message(
"CHLO",
@@ -440,6 +688,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
"#004b5453", srct_hex_.c_str(),
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
@@ -447,7 +696,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
out_.set_tag(0);
bool called = false;
- ShouldSucceed(msg, &called);
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
// The verification request was queued.
ASSERT_FALSE(called);
EXPECT_EQ(0u, out_.tag());
@@ -461,7 +710,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
EXPECT_EQ(kSHLO, out_.tag());
// Rejected if replayed.
- ShouldSucceed(msg, &called);
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
// The verification request was queued.
ASSERT_FALSE(called);
EXPECT_EQ(1, strike_register_client_->PendingVerifications());
diff --git a/chromium/net/quic/crypto/crypto_utils.cc b/chromium/net/quic/crypto/crypto_utils.cc
index eec8a9d05a2..29433960489 100644
--- a/chromium/net/quic/crypto/crypto_utils.cc
+++ b/chromium/net/quic/crypto/crypto_utils.cc
@@ -51,7 +51,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) {
// based on the above spec, we may be losing some hostnames that windows
// would consider valid. By far the most common hostname character NOT
// accepted by the above spec is '_'.
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info));
return !host_info.IsIPAddress() &&
IsCanonicalizedHostCompliant(canonicalized_host, std::string()) &&
@@ -60,7 +60,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) {
// static
string CryptoUtils::NormalizeHostname(const char* hostname) {
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
string host(CanonicalizeHost(hostname, &host_info));
// Walk backwards over the string, stopping at the first trailing dot.
diff --git a/chromium/net/quic/crypto/crypto_utils.h b/chromium/net/quic/crypto/crypto_utils.h
index 147e41436f8..ec6384c131e 100644
--- a/chromium/net/quic/crypto/crypto_utils.h
+++ b/chromium/net/quic/crypto/crypto_utils.h
@@ -62,6 +62,9 @@ class NET_EXPORT_PRIVATE CryptoUtils {
const std::string& hkdf_input,
Perspective perspective,
CrypterPair* out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptoUtils);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/curve25519_key_exchange.cc b/chromium/net/quic/crypto/curve25519_key_exchange.cc
index 3b8880453a3..8ed95aa26bc 100644
--- a/chromium/net/quic/crypto/curve25519_key_exchange.cc
+++ b/chromium/net/quic/crypto/curve25519_key_exchange.cc
@@ -4,6 +4,7 @@
#include "net/quic/crypto/curve25519_key_exchange.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "crypto/curve25519.h"
#include "net/quic/crypto/quic_random.h"
diff --git a/chromium/net/quic/crypto/local_strike_register_client.cc b/chromium/net/quic/crypto/local_strike_register_client.cc
index c3751450397..1f78d9833d5 100644
--- a/chromium/net/quic/crypto/local_strike_register_client.cc
+++ b/chromium/net/quic/crypto/local_strike_register_client.cc
@@ -21,10 +21,12 @@ LocalStrikeRegisterClient::LocalStrikeRegisterClient(
orbit, startup) {
}
-string LocalStrikeRegisterClient::orbit() {
+bool LocalStrikeRegisterClient::IsKnownOrbit(StringPiece orbit) const {
base::AutoLock lock(m_);
- return string(reinterpret_cast<const char*>(strike_register_.orbit()),
- kOrbitSize);
+ if (orbit.length() != kOrbitSize) {
+ return false;
+ }
+ return memcmp(orbit.data(), strike_register_.orbit(), kOrbitSize) == 0;
}
void LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(
diff --git a/chromium/net/quic/crypto/local_strike_register_client.h b/chromium/net/quic/crypto/local_strike_register_client.h
index 37020b7b420..fe8ae93d3f4 100644
--- a/chromium/net/quic/crypto/local_strike_register_client.h
+++ b/chromium/net/quic/crypto/local_strike_register_client.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
#define NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "net/base/net_export.h"
@@ -25,13 +26,13 @@ class NET_EXPORT_PRIVATE LocalStrikeRegisterClient
const uint8 orbit[8],
StrikeRegister::StartupType startup);
- virtual std::string orbit() OVERRIDE;
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const OVERRIDE;
virtual void VerifyNonceIsValidAndUnique(base::StringPiece nonce,
QuicWallTime now,
ResultCallback* cb) OVERRIDE;
private:
- base::Lock m_;
+ mutable base::Lock m_;
StrikeRegister strike_register_;
DISALLOW_COPY_AND_ASSIGN(LocalStrikeRegisterClient);
diff --git a/chromium/net/quic/crypto/local_strike_register_client_test.cc b/chromium/net/quic/crypto/local_strike_register_client_test.cc
index 0227e92c6dc..268e714d612 100644
--- a/chromium/net/quic/crypto/local_strike_register_client_test.cc
+++ b/chromium/net/quic/crypto/local_strike_register_client_test.cc
@@ -26,7 +26,8 @@ class RecordResultCallback : public StrikeRegisterClient::ResultCallback {
// |*saved_value| and sets |*called| to true. The callback is self
// deleting.
RecordResultCallback(bool* called, bool* saved_value)
- : called_(called), saved_value_(saved_value) {
+ : called_(called),
+ saved_value_(saved_value) {
*called_ = false;
}
@@ -63,15 +64,21 @@ class LocalStrikeRegisterClientTest : public ::testing::Test {
};
TEST_F(LocalStrikeRegisterClientTest, CheckOrbit) {
- EXPECT_EQ(StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize),
- strike_register_->orbit());
+ EXPECT_TRUE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize - 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize + 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit) + 1, kOrbitSize)));
}
TEST_F(LocalStrikeRegisterClientTest, IncorrectNonceLength) {
string valid_nonce;
uint32 norder = htonl(kCurrentTimeExternalSecs);
valid_nonce.assign(reinterpret_cast<const char*>(&norder), sizeof(norder));
- valid_nonce.append(strike_register_->orbit());
+ valid_nonce.append(string(reinterpret_cast<const char*>(kOrbit), kOrbitSize));
valid_nonce.append(string(20, '\x17')); // 20 'random' bytes.
{
diff --git a/chromium/net/quic/crypto/null_decrypter.h b/chromium/net/quic/crypto/null_decrypter.h
index e85e1247582..2bc2fe8cd8a 100644
--- a/chromium/net/quic/crypto/null_decrypter.h
+++ b/chromium/net/quic/crypto/null_decrypter.h
@@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
private:
bool ReadHash(QuicDataReader* reader, uint128* hash);
uint128 ComputeHash(const std::string& data) const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullDecrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/null_encrypter.h b/chromium/net/quic/crypto/null_encrypter.h
index 44e6f5555e6..1bcdff5a2c2 100644
--- a/chromium/net/quic/crypto/null_encrypter.h
+++ b/chromium/net/quic/crypto/null_encrypter.h
@@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
private:
size_t GetHashLength() const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullEncrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/p256_key_exchange.h b/chromium/net/quic/crypto/p256_key_exchange.h
index 8145cc05249..49a66cec3de 100644
--- a/chromium/net/quic/crypto/p256_key_exchange.h
+++ b/chromium/net/quic/crypto/p256_key_exchange.h
@@ -73,6 +73,8 @@ class NET_EXPORT_PRIVATE P256KeyExchange : public KeyExchange {
#endif
// The public key stored as an uncompressed P-256 point.
uint8 public_key_[kUncompressedP256PointBytes];
+
+ DISALLOW_COPY_AND_ASSIGN(P256KeyExchange);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_test.cc b/chromium/net/quic/crypto/proof_test.cc
index cc9e0992c1a..56e9abcfb61 100644
--- a/chromium/net/quic/crypto/proof_test.cc
+++ b/chromium/net/quic/crypto/proof_test.cc
@@ -24,74 +24,7 @@ using std::vector;
namespace net {
namespace test {
-
-TEST(ProofTest, Verify) {
- // TODO(rtenneti): Enable testing of ProofVerifier.
-#if 0
- scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
- scoped_ptr<ProofVerifier> verifier(
- CryptoTestUtils::ProofVerifierForTesting());
-
- const string server_config = "server config bytes";
- const string hostname = "test.example.com";
- const vector<string>* certs;
- const vector<string>* first_certs;
- string error_details, signature, first_signature;
- CertVerifyResult cert_verify_result;
-
- 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);
- ASSERT_EQ(signature, first_signature);
-
- int rv;
- TestCompletionCallback callback;
- 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("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(hostname, server_config.substr(1, string::npos),
- *certs, signature, &error_details,
- &cert_verify_result, callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-
- const string corrupt_signature = "1" + signature;
- rv = verifier->VerifyProof(hostname, server_config, *certs,
- corrupt_signature, &error_details,
- &cert_verify_result, callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-
- vector<string> wrong_certs;
- for (size_t i = 1; i < certs->size(); i++) {
- wrong_certs.push_back((*certs)[i]);
- }
- 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);
- ASSERT_NE("", error_details);
-#endif // 0
-}
+namespace {
// TestProofVerifierCallback is a simple callback for a ProofVerifier that
// signals a TestCompletionCallback when called and stores the results from the
@@ -100,13 +33,13 @@ class TestProofVerifierCallback : public ProofVerifierCallback {
public:
TestProofVerifierCallback(TestCompletionCallback* comp_callback,
bool* ok,
- std::string* error_details)
+ string* error_details)
: comp_callback_(comp_callback),
ok_(ok),
error_details_(error_details) {}
virtual void Run(bool ok,
- const std::string& error_details,
+ const string& error_details,
scoped_ptr<ProofVerifyDetails>* details) OVERRIDE {
*ok_ = ok;
*error_details_ = error_details;
@@ -117,48 +50,54 @@ class TestProofVerifierCallback : public ProofVerifierCallback {
private:
TestCompletionCallback* const comp_callback_;
bool* const ok_;
- std::string* const error_details_;
+ string* const error_details_;
};
// RunVerification runs |verifier->VerifyProof| and asserts that the result
// matches |expected_ok|.
-static void RunVerification(ProofVerifier* verifier,
- const std::string& hostname,
- const std::string& server_config,
- const vector<std::string>& certs,
- const std::string& proof,
- bool expected_ok) {
+void RunVerification(ProofVerifier* verifier,
+ const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& proof,
+ bool expected_ok) {
scoped_ptr<ProofVerifyDetails> details;
TestCompletionCallback comp_callback;
bool ok;
- std::string error_details;
+ string error_details;
+ scoped_ptr<ProofVerifyContext> verify_context(
+ CryptoTestUtils::ProofVerifyContextForTesting());
TestProofVerifierCallback* callback =
new TestProofVerifierCallback(&comp_callback, &ok, &error_details);
- ProofVerifier::Status status = verifier->VerifyProof(
- hostname, server_config, certs, proof, &error_details, &details,
- callback);
+ QuicAsyncStatus status = verifier->VerifyProof(
+ hostname, server_config, certs, proof, verify_context.get(),
+ &error_details, &details, callback);
switch (status) {
- case ProofVerifier::FAILURE:
+ case QUIC_FAILURE:
+ delete callback;
ASSERT_FALSE(expected_ok);
ASSERT_NE("", error_details);
return;
- case ProofVerifier::SUCCESS:
+ case QUIC_SUCCESS:
+ delete callback;
ASSERT_TRUE(expected_ok);
ASSERT_EQ("", error_details);
return;
- case ProofVerifier::PENDING:
+ case QUIC_PENDING:
comp_callback.WaitForResult();
ASSERT_EQ(expected_ok, ok);
break;
}
}
-static string PEMCertFileToDER(const string& file_name) {
+// Reads the certificate named "quic_" + |file_name| in the test data directory.
+// The certificate must be PEM encoded. Returns the DER-encoded certificate.
+string LoadTestCert(const string& file_name) {
base::FilePath certs_dir = GetTestCertsDirectory();
scoped_refptr<X509Certificate> cert =
- ImportCertFromFile(certs_dir, file_name);
+ ImportCertFromFile(certs_dir, "quic_" + file_name);
CHECK_NE(static_cast<X509Certificate*>(NULL), cert);
string der_bytes;
@@ -166,12 +105,58 @@ static string PEMCertFileToDER(const string& file_name) {
return der_bytes;
}
+} // namespace
+
+// TODO(rtenneti): Enable testing of ProofVerifier.
+TEST(ProofTest, DISABLED_Verify) {
+ scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
+ scoped_ptr<ProofVerifier> verifier(
+ CryptoTestUtils::ProofVerifierForTesting());
+
+ const string server_config = "server config bytes";
+ const string hostname = "test.example.com";
+ const vector<string>* certs;
+ const vector<string>* first_certs;
+ string error_details, signature, first_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);
+ ASSERT_EQ(signature, first_signature);
+
+ RunVerification(
+ verifier.get(), hostname, server_config, *certs, signature, true);
+
+ RunVerification(
+ verifier.get(), "foo.com", server_config, *certs, signature, false);
+
+ RunVerification(
+ verifier.get(), server_config.substr(1, string::npos), server_config,
+ *certs, signature, false);
+
+ const string corrupt_signature = "1" + signature;
+ RunVerification(
+ 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(
+ verifier.get(), "foo.com", server_config, wrong_certs, corrupt_signature,
+ false);
+}
+
// A known answer test that allows us to test ProofVerifier without a working
// ProofSource.
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[] = {
0x31, 0xd5, 0xfb, 0x40, 0x30, 0x75, 0xd2, 0x7d, 0x61, 0xf9, 0xd7, 0x54,
0x30, 0x06, 0xaf, 0x54, 0x0d, 0xb0, 0x0a, 0xda, 0x63, 0xca, 0x7e, 0x9e,
@@ -250,11 +235,10 @@ TEST(ProofTest, VerifyRSAKnownAnswerTest) {
const string server_config = "server config bytes";
const string hostname = "test.example.com";
- CertVerifyResult cert_verify_result;
vector<string> certs(2);
- certs[0] = PEMCertFileToDER("quic_test.example.com.crt");
- certs[1] = PEMCertFileToDER("quic_intermediate.crt");
+ certs[0] = LoadTestCert("test.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
// Signatures are nondeterministic, so we test multiple signatures on the
// same server_config.
@@ -333,11 +317,10 @@ TEST(ProofTest, VerifyECDSAKnownAnswerTest) {
const string server_config = "server config bytes";
const string hostname = "test.example.com";
- CertVerifyResult cert_verify_result;
vector<string> certs(2);
- certs[0] = PEMCertFileToDER("quic_test_ecc.example.com.crt");
- certs[1] = PEMCertFileToDER("quic_intermediate.crt");
+ certs[0] = LoadTestCert("test_ecc.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
// Signatures are nondeterministic, so we test multiple signatures on the
// same server_config.
diff --git a/chromium/net/quic/crypto/proof_verifier.cc b/chromium/net/quic/crypto/proof_verifier.cc
deleted file mode 100644
index 7bccba2bdb8..00000000000
--- a/chromium/net/quic/crypto/proof_verifier.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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/crypto/proof_verifier.h"
-
-namespace net {
-
-ProofVerifyDetails::~ProofVerifyDetails() {}
-
-ProofVerifierCallback::~ProofVerifierCallback() {}
-
-ProofVerifier::~ProofVerifier() {}
-
-} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier.h b/chromium/net/quic/crypto/proof_verifier.h
index f469c552959..83f7c4366fd 100644
--- a/chromium/net/quic/crypto/proof_verifier.h
+++ b/chromium/net/quic/crypto/proof_verifier.h
@@ -8,30 +8,36 @@
#include <string>
#include <vector>
-#include "net/base/completion_callback.h"
+#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
namespace net {
-class CertVerifyResult;
-
// ProofVerifyDetails is an abstract class that acts as a container for any
// implementation specific details that a ProofVerifier wishes to return. These
-// details are saved in the CachedInfo for the origin in question.
-class ProofVerifyDetails {
+// details are saved in the CachedState for the origin in question.
+class NET_EXPORT_PRIVATE ProofVerifyDetails {
+ public:
+ virtual ~ProofVerifyDetails() {}
+};
+
+// ProofVerifyContext is an abstract class that acts as a container for any
+// implementation specific context that a ProofVerifier needs.
+class NET_EXPORT_PRIVATE ProofVerifyContext {
public:
- virtual ~ProofVerifyDetails();
+ virtual ~ProofVerifyContext() {}
};
// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to
// call back after an asynchronous verification.
class NET_EXPORT_PRIVATE ProofVerifierCallback {
public:
- virtual ~ProofVerifierCallback();
+ virtual ~ProofVerifierCallback() {}
// Run is called on the original thread to mark the completion of an
// asynchonous verification. If |ok| is true then the certificate is valid
- // and |*error_details| is unused. Otherwise, |*error_details| contains a
+ // and |error_details| is unused. Otherwise, |error_details| contains a
// description of the error. |details| contains implementation-specific
// details of the verification. |Run| may take ownership of |details| by
// calling |release| on it.
@@ -44,39 +50,33 @@ class NET_EXPORT_PRIVATE ProofVerifierCallback {
// chain that backs the public key.
class NET_EXPORT_PRIVATE ProofVerifier {
public:
- // Status enumerates the possible results of verifying a proof.
- enum Status {
- SUCCESS = 0,
- FAILURE = 1,
- // PENDING results from a verification which will occur asynchonously. When
- // the verification is complete, |callback|'s |Run| method will be called.
- PENDING = 2,
- };
-
- virtual ~ProofVerifier();
+ virtual ~ProofVerifier() {}
// VerifyProof checks that |signature| is a valid signature of
// |server_config| by the public key in the leaf certificate of |certs|, and
// that |certs| is a valid chain for |hostname|. On success, it returns
- // SUCCESS. On failure, it returns ERROR and sets |*error_details| to a
- // description of the problem. In either case it may set |*details|, which the
- // caller takes ownership of.
+ // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
+ // to a description of the problem. In either case it may set |*details|,
+ // which the caller takes ownership of.
//
- // This function may also return PENDING, in which case the ProofVerifier
- // will call back, on the original thread, via |callback| when complete.
+ // |context| specifies an implementation specific struct (which may be NULL
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
//
- // This function takes ownership of |callback|. It will be deleted even if
- // the call returns immediately.
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ // In this case, the ProofVerifier will take ownership of |callback|.
//
// The signature uses SHA-256 as the hash function and PSS padding in the
// case of RSA.
- virtual Status VerifyProof(const std::string& hostname,
- const std::string& server_config,
- const std::vector<std::string>& certs,
- const std::string& signature,
- std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
- ProofVerifierCallback* callback) = 0;
+ virtual QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* details,
+ ProofVerifierCallback* callback) = 0;
};
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc
index 68ad25ca12d..4b118da198c 100644
--- a/chromium/net/quic/crypto/proof_verifier_chromium.cc
+++ b/chromium/net/quic/crypto/proof_verifier_chromium.cc
@@ -9,6 +9,7 @@
#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "crypto/signature_verifier.h"
#include "net/base/net_errors.h"
@@ -30,37 +31,91 @@ using std::vector;
namespace net {
-ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier,
- const BoundNetLog& net_log)
- : cert_verifier_(cert_verifier),
- next_state_(STATE_NONE),
- net_log_(net_log) {
-}
+// A Job handles the verification of a single proof. It is owned by the
+// ProofVerifier. If the verification can not complete synchronously, it
+// will notify the ProofVerifier upon completion.
+class ProofVerifierChromium::Job {
+ public:
+ Job(ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ const BoundNetLog& net_log);
-ProofVerifierChromium::~ProofVerifierChromium() {
- verifier_.reset();
+ // Starts the proof verification. If |QUIC_PENDING| is returned, then
+ // |callback| will be invoked asynchronously when the verification completes.
+ QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_VERIFY_CERT,
+ STATE_VERIFY_CERT_COMPLETE,
+ };
+
+ int DoLoop(int last_io_result);
+ void OnIOComplete(int result);
+ int DoVerifyCert(int result);
+ int DoVerifyCertComplete(int result);
+
+ bool VerifySignature(const std::string& signed_data,
+ const std::string& signature,
+ const std::string& cert);
+
+ // Proof verifier to notify when this jobs completes.
+ ProofVerifierChromium* proof_verifier_;
+
+ // The underlying verifier used for verifying certificates.
+ scoped_ptr<SingleRequestCertVerifier> verifier_;
+
+ // |hostname| specifies the hostname for which |certs| is a valid chain.
+ std::string hostname_;
+
+ scoped_ptr<ProofVerifierCallback> callback_;
+ scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
+ std::string error_details_;
+
+ // X509Certificate from a chain of DER encoded certificates.
+ scoped_refptr<X509Certificate> cert_;
+
+ State next_state_;
+
+ BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+ProofVerifierChromium::Job::Job(ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ const BoundNetLog& net_log)
+ : proof_verifier_(proof_verifier),
+ verifier_(new SingleRequestCertVerifier(cert_verifier)),
+ next_state_(STATE_NONE),
+ net_log_(net_log) {
}
-ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
+QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof(
const string& hostname,
const string& server_config,
const vector<string>& certs,
const string& signature,
std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
ProofVerifierCallback* callback) {
DCHECK(error_details);
- DCHECK(details);
+ DCHECK(verify_details);
DCHECK(callback);
- callback_.reset(callback);
error_details->clear();
- DCHECK_EQ(STATE_NONE, next_state_);
if (STATE_NONE != next_state_) {
*error_details = "Certificate is already set and VerifyProof has begun";
- DLOG(WARNING) << *error_details;
- return FAILURE;
+ DLOG(DFATAL) << *error_details;
+ return QUIC_FAILURE;
}
verify_details_.reset(new ProofVerifyDetailsChromium);
@@ -69,8 +124,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to create certificate chain. Certs are empty.";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
// Convert certs to X509Certificate.
@@ -83,8 +138,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to create certificate chain";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
// We call VerifySignature first to avoid copying of server_config and
@@ -93,8 +148,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to verify signature of server config";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
hostname_ = hostname;
@@ -102,18 +157,19 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
next_state_ = STATE_VERIFY_CERT;
switch (DoLoop(OK)) {
case OK:
- details->reset(verify_details_.release());
- return SUCCESS;
+ verify_details->reset(verify_details_.release());
+ return QUIC_SUCCESS;
case ERR_IO_PENDING:
- return PENDING;
+ callback_.reset(callback);
+ return QUIC_PENDING;
default:
*error_details = error_details_;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
}
-int ProofVerifierChromium::DoLoop(int last_result) {
+int ProofVerifierChromium::Job::DoLoop(int last_result) {
int rv = last_result;
do {
State state = next_state_;
@@ -136,32 +192,34 @@ int ProofVerifierChromium::DoLoop(int last_result) {
return rv;
}
-void ProofVerifierChromium::OnIOComplete(int result) {
+void ProofVerifierChromium::Job::OnIOComplete(int result) {
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
- scoped_ptr<ProofVerifyDetails> scoped_details(verify_details_.release());
- callback_->Run(rv == OK, error_details_, &scoped_details);
- callback_.reset();
+ scoped_ptr<ProofVerifierCallback> callback(callback_.release());
+ // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
+ scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.release());
+ callback->Run(rv == OK, error_details_, &verify_details);
+ // Will delete |this|.
+ proof_verifier_->OnJobComplete(this);
}
}
-int ProofVerifierChromium::DoVerifyCert(int result) {
+int ProofVerifierChromium::Job::DoVerifyCert(int result) {
next_state_ = STATE_VERIFY_CERT_COMPLETE;
int flags = 0;
- verifier_.reset(new SingleRequestCertVerifier(cert_verifier_));
return verifier_->Verify(
cert_.get(),
hostname_,
flags,
SSLConfigService::GetCRLSet().get(),
&verify_details_->cert_verify_result,
- base::Bind(&ProofVerifierChromium::OnIOComplete,
+ base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
base::Unretained(this)),
net_log_);
}
-int ProofVerifierChromium::DoVerifyCertComplete(int result) {
+int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) {
verifier_.reset();
if (result <= ERR_FAILED) {
@@ -176,7 +234,7 @@ int ProofVerifierChromium::DoVerifyCertComplete(int result) {
return result;
}
-bool ProofVerifierChromium::VerifySignature(const string& signed_data,
+bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data,
const string& signature,
const string& cert) {
StringPiece spki;
@@ -252,4 +310,41 @@ bool ProofVerifierChromium::VerifySignature(const string& signed_data,
return true;
}
+ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier)
+ : cert_verifier_(cert_verifier) {}
+
+ProofVerifierChromium::~ProofVerifierChromium() {
+ STLDeleteElements(&active_jobs_);
+}
+
+QuicAsyncStatus ProofVerifierChromium::VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) {
+ if (!verify_context) {
+ *error_details = "Missing context";
+ return QUIC_FAILURE;
+ }
+ const ProofVerifyContextChromium* chromium_context =
+ reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
+ scoped_ptr<Job> job(new Job(this, cert_verifier_, chromium_context->net_log));
+ QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs,
+ signature, error_details,
+ verify_details, callback);
+ if (status == QUIC_PENDING) {
+ active_jobs_.insert(job.release());
+ }
+ return status;
+}
+
+void ProofVerifierChromium::OnJobComplete(Job* job) {
+ active_jobs_.erase(job);
+ delete job;
+}
+
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h
index 4969cc8aa55..6f8a23131f3 100644
--- a/chromium/net/quic/crypto/proof_verifier_chromium.h
+++ b/chromium/net/quic/crypto/proof_verifier_chromium.h
@@ -5,13 +5,13 @@
#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
#define NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
+#include <set>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
-#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
#include "net/cert/cert_verify_result.h"
@@ -30,56 +30,45 @@ struct ProofVerifyDetailsChromium : public ProofVerifyDetails {
CertVerifyResult cert_verify_result;
};
-// ProofVerifierChromium implements the QUIC ProofVerifier interface.
-// TODO(rtenneti): Add support for multiple requests for one ProofVerifier.
+// ProofVerifyContextChromium is the implementation-specific information that a
+// ProofVerifierChromium needs in order to log correctly.
+struct ProofVerifyContextChromium : public ProofVerifyContext {
+ public:
+ explicit ProofVerifyContextChromium(const BoundNetLog& net_log)
+ : net_log(net_log) {}
+
+ BoundNetLog net_log;
+};
+
+// ProofVerifierChromium implements the QUIC ProofVerifier interface. It is
+// capable of handling multiple simultaneous requests.
class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier {
public:
- ProofVerifierChromium(CertVerifier* cert_verifier,
- const BoundNetLog& net_log);
+ explicit ProofVerifierChromium(CertVerifier* cert_verifier);
virtual ~ProofVerifierChromium();
// ProofVerifier interface
- virtual Status VerifyProof(const std::string& hostname,
- const std::string& server_config,
- const std::vector<std::string>& certs,
- const std::string& signature,
- std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
- ProofVerifierCallback* callback) OVERRIDE;
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) OVERRIDE;
private:
- enum State {
- STATE_NONE,
- STATE_VERIFY_CERT,
- STATE_VERIFY_CERT_COMPLETE,
- };
-
- int DoLoop(int last_io_result);
- void OnIOComplete(int result);
- int DoVerifyCert(int result);
- int DoVerifyCertComplete(int result);
-
- bool VerifySignature(const std::string& signed_data,
- const std::string& signature,
- const std::string& cert);
-
- // |cert_verifier_| and |verifier_| are used for verifying certificates.
- CertVerifier* const cert_verifier_;
- scoped_ptr<SingleRequestCertVerifier> verifier_;
-
- // |hostname| specifies the hostname for which |certs| is a valid chain.
- std::string hostname_;
+ class Job;
- scoped_ptr<ProofVerifierCallback> callback_;
- scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
- std::string error_details_;
+ void OnJobComplete(Job* job);
- // X509Certificate from a chain of DER encoded certificates.
- scoped_refptr<X509Certificate> cert_;
+ // Set owning pointers to active jobs.
+ typedef std::set<Job*> JobSet;
+ JobSet active_jobs_;
- State next_state_;
-
- BoundNetLog net_log_;
+ // Underlying verifier used to verify certificates.
+ CertVerifier* const cert_verifier_;
DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium);
};
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.cc b/chromium/net/quic/crypto/quic_crypto_client_config.cc
index a3eeeb62d22..9d5e704e1df 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config.cc
+++ b/chromium/net/quic/crypto/quic_crypto_client_config.cc
@@ -5,7 +5,9 @@
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "base/stl_util.h"
+#include "base/strings/string_util.h"
#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/common_cert_set.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -17,18 +19,17 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
using base::StringPiece;
+using std::find;
+using std::make_pair;
using std::map;
using std::string;
using std::vector;
namespace net {
-QuicCryptoClientConfig::QuicCryptoClientConfig() {}
+QuicCryptoClientConfig::QuicCryptoClientConfig()
+ : disable_ecdsa_(false) {}
QuicCryptoClientConfig::~QuicCryptoClientConfig() {
STLDeleteValues(&cached_states_);
@@ -61,6 +62,10 @@ bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
return true;
}
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+ return server_config_.empty();
+}
+
const CryptoHandshakeMessage*
QuicCryptoClientConfig::CachedState::GetServerConfig() const {
if (server_config_.empty()) {
@@ -144,6 +149,17 @@ void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs,
server_config_sig_ = signature.as_string();
}
+void QuicCryptoClientConfig::CachedState::Clear() {
+ server_config_.clear();
+ source_address_token_.clear();
+ certs_.clear();
+ server_config_sig_.clear();
+ server_config_valid_ = false;
+ proof_verify_details_.reset();
+ scfg_.reset();
+ ++generation_counter_;
+}
+
void QuicCryptoClientConfig::CachedState::ClearProof() {
SetProofInvalid();
certs_.clear();
@@ -159,6 +175,32 @@ void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
++generation_counter_;
}
+bool QuicCryptoClientConfig::CachedState::Initialize(
+ StringPiece server_config,
+ StringPiece source_address_token,
+ const vector<string>& certs,
+ StringPiece signature,
+ QuicWallTime now) {
+ DCHECK(server_config_.empty());
+
+ if (server_config.empty()) {
+ return false;
+ }
+
+ string error_details;
+ QuicErrorCode error = SetServerConfig(server_config, now,
+ &error_details);
+ if (error != QUIC_NO_ERROR) {
+ DVLOG(1) << "SetServerConfig failed with " << error_details;
+ return false;
+ }
+
+ signature.CopyToString(&server_config_sig_);
+ source_address_token.CopyToString(&source_address_token_);
+ certs_ = certs;
+ return true;
+}
+
const string& QuicCryptoClientConfig::CachedState::server_config() const {
return server_config_;
}
@@ -208,6 +250,7 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom(
certs_ = other.certs_;
server_config_sig_ = other.server_config_sig_;
server_config_valid_ = other.server_config_valid_;
+ ++generation_counter_;
}
void QuicCryptoClientConfig::SetDefaults() {
@@ -216,26 +259,38 @@ void QuicCryptoClientConfig::SetDefaults() {
kexs[0] = kC255;
kexs[1] = kP256;
- // Authenticated encryption algorithms.
- aead.resize(1);
- aead[0] = kAESG;
+ // Authenticated encryption algorithms. Prefer ChaCha20 by default.
+ aead.clear();
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ aead.push_back(kCC12);
+ }
+ aead.push_back(kAESG);
+
+ disable_ecdsa_ = false;
}
QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
- const string& server_hostname) {
- map<string, CachedState*>::const_iterator it =
- cached_states_.find(server_hostname);
+ const QuicServerId& server_id) {
+ CachedStateMap::const_iterator it = cached_states_.find(server_id);
if (it != cached_states_.end()) {
return it->second;
}
CachedState* cached = new CachedState;
- cached_states_.insert(make_pair(server_hostname, cached));
+ cached_states_.insert(make_pair(server_id, cached));
+ PopulateFromCanonicalConfig(server_id, cached);
return cached;
}
+void QuicCryptoClientConfig::ClearCachedStates() {
+ for (CachedStateMap::const_iterator it = cached_states_.begin();
+ it != cached_states_.end(); ++it) {
+ it->second->Clear();
+ }
+}
+
void QuicCryptoClientConfig::FillInchoateClientHello(
- const string& server_hostname,
+ const QuicServerId& server_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -245,26 +300,21 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
// Server name indication. We only send SNI if it's a valid domain name, as
// per the spec.
- if (CryptoUtils::IsValidSNI(server_hostname)) {
- out->SetStringPiece(kSNI, server_hostname);
+ if (CryptoUtils::IsValidSNI(server_id.host())) {
+ out->SetStringPiece(kSNI, server_id.host());
}
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- out->SetValue(kVERS, static_cast<uint16>(0));
out->SetValue(kVER, QuicVersionToQuicTag(preferred_version));
+ if (!user_agent_id_.empty()) {
+ out->SetStringPiece(kUAID, user_agent_id_);
+ }
+
if (!cached->source_address_token().empty()) {
out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
}
- if (proof_verifier_.get()) {
- // Don't request ECDSA proofs on platforms that do not support ECDSA
- // certificates.
- bool disableECDSA = false;
-#if defined(OS_WIN)
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- disableECDSA = true;
-#endif
- if (disableECDSA) {
+ if (server_id.is_https()) {
+ if (disable_ecdsa_) {
out->SetTaglist(kPDMD, kX59R, 0);
} else {
out->SetTaglist(kPDMD, kX509, 0);
@@ -293,18 +343,19 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
}
QuicErrorCode QuicCryptoClientConfig::FillClientHello(
- const string& server_hostname,
- QuicGuid guid,
+ const QuicServerId& server_id,
+ QuicConnectionId connection_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicWallTime now,
QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out,
string* error_details) const {
DCHECK(error_details != NULL);
- FillInchoateClientHello(server_hostname, preferred_version, cached,
+ FillInchoateClientHello(server_id, preferred_version, cached,
out_params, out);
const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
@@ -333,13 +384,18 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
+ // AEAD: the work loads on the client and server are symmetric. Since the
+ // client is more likely to be CPU-constrained, break the tie by favoring
+ // the client's preference.
+ // Key exchange: the client does more work than the server, so favor the
+ // client's preference.
size_t key_exchange_index;
if (!QuicUtils::FindMutualTag(
- aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY,
+ aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY,
&out_params->aead, NULL) ||
!QuicUtils::FindMutualTag(
kexs, their_key_exchanges, num_their_key_exchanges,
- QuicUtils::PEER_PRIORITY, &out_params->key_exchange,
+ QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange,
&key_exchange_index)) {
*error_details = "Unsupported AEAD or KEXS";
return QUIC_CRYPTO_NO_SUPPORT;
@@ -388,22 +444,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
}
out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
- bool do_channel_id = false;
- if (channel_id_signer_.get()) {
- const QuicTag* their_proof_demands;
- size_t num_their_proof_demands;
- if (scfg->GetTaglist(kPDMD, &their_proof_demands,
- &num_their_proof_demands) == QUIC_NO_ERROR) {
- for (size_t i = 0; i < num_their_proof_demands; i++) {
- if (their_proof_demands[i] == kCHID) {
- do_channel_id = true;
- break;
- }
- }
- }
- }
-
- if (do_channel_id) {
+ if (channel_id_key) {
// In order to calculate the encryption key for the CETV block we need to
// serialise the client hello as it currently is (i.e. without the CETV
// block). For this, the client hello is serialized without padding.
@@ -417,14 +458,15 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
const QuicData& client_hello_serialized = out->GetSerialized();
hkdf_input.append(QuicCryptoConfig::kCETVLabel,
strlen(QuicCryptoConfig::kCETVLabel) + 1);
- hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_input.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_input.append(cached->server_config());
- string key, signature;
- if (!channel_id_signer_->Sign(server_hostname, hkdf_input,
- &key, &signature)) {
+ string key = channel_id_key->SerializeKey();
+ string signature;
+ if (!channel_id_key->Sign(hkdf_input, &signature)) {
*error_details = "Channel ID signature failed";
return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
}
@@ -457,9 +499,13 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
out->set_minimum_size(orig_min_size);
}
+ // Derive the symmetric keys and set up the encrypters and decrypters.
+ // Set the following members of out_params:
+ // out_params->hkdf_input_suffix
+ // out_params->initial_crypters
out_params->hkdf_input_suffix.clear();
- out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid),
- sizeof(guid));
+ out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
const QuicData& client_hello_serialized = out->GetSerialized();
out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
client_hello_serialized.length());
@@ -541,12 +587,23 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
}
}
+ const QuicTag* reject_reasons;
+ size_t num_reject_reasons;
+ if (rej.GetTaglist(kRREJ, &reject_reasons,
+ &num_reject_reasons) == QUIC_NO_ERROR) {
+#if defined(DEBUG)
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ DVLOG(1) << "Reasons for rejection: " << reject_reasons[i];
+ }
+#endif
+ }
+
return QUIC_NO_ERROR;
}
QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
const CryptoHandshakeMessage& server_hello,
- QuicGuid guid,
+ QuicConnectionId connection_id,
const QuicVersionVector& negotiated_versions,
CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -560,23 +617,24 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
const QuicTag* supported_version_tags;
size_t num_supported_versions;
- // TODO(rch): Once QUIC_VERSION_12 is removed, then make it a failure
- // if the server does not have a version list.
+
if (server_hello.GetTaglist(kVER, &supported_version_tags,
- &num_supported_versions) == QUIC_NO_ERROR) {
- if (!negotiated_versions.empty()) {
- bool mismatch = num_supported_versions != negotiated_versions.size();
- for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
- mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
- negotiated_versions[i];
- }
- // The server sent a list of supported versions, and the connection
- // reports that there was a version negotiation during the handshake.
+ &num_supported_versions) != QUIC_NO_ERROR) {
+ *error_details = "server hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ if (!negotiated_versions.empty()) {
+ bool mismatch = num_supported_versions != negotiated_versions.size();
+ for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
+ mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
+ negotiated_versions[i];
+ }
+ // The server sent a list of supported versions, and the connection
+ // reports that there was a version negotiation during the handshake.
// Ensure that these two lists are identical.
- if (mismatch) {
- *error_details = "Downgrade attack detected";
- return QUIC_VERSION_NEGOTIATION_MISMATCH;
- }
+ if (mismatch) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
}
}
@@ -626,25 +684,81 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) {
proof_verifier_.reset(verifier);
}
-ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const {
- return channel_id_signer_.get();
+ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const {
+ return channel_id_source_.get();
}
-void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) {
- channel_id_signer_.reset(signer);
+void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) {
+ channel_id_source_.reset(source);
}
void QuicCryptoClientConfig::InitializeFrom(
- const std::string& server_hostname,
- const std::string& canonical_server_hostname,
+ const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
QuicCryptoClientConfig* canonical_crypto_config) {
CachedState* canonical_cached =
- canonical_crypto_config->LookupOrCreate(canonical_server_hostname);
+ canonical_crypto_config->LookupOrCreate(canonical_server_id);
if (!canonical_cached->proof_valid()) {
return;
}
- CachedState* cached = LookupOrCreate(server_hostname);
+ CachedState* cached = LookupOrCreate(server_id);
cached->InitializeFrom(*canonical_cached);
}
+void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) {
+ canoncial_suffixes_.push_back(suffix);
+}
+
+void QuicCryptoClientConfig::PreferAesGcm() {
+ DCHECK(!aead.empty());
+ if (aead.size() <= 1) {
+ return;
+ }
+ QuicTagVector::iterator pos = find(aead.begin(), aead.end(), kAESG);
+ if (pos != aead.end()) {
+ aead.erase(pos);
+ aead.insert(aead.begin(), kAESG);
+ }
+}
+
+void QuicCryptoClientConfig::DisableEcdsa() {
+ disable_ecdsa_ = true;
+}
+
+void QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+ const QuicServerId& server_id,
+ CachedState* server_state) {
+ DCHECK(server_state->IsEmpty());
+ size_t i = 0;
+ for (; i < canoncial_suffixes_.size(); ++i) {
+ if (EndsWith(server_id.host(), canoncial_suffixes_[i], false)) {
+ break;
+ }
+ }
+ if (i == canoncial_suffixes_.size())
+ return;
+
+ QuicServerId suffix_server_id(canoncial_suffixes_[i], server_id.port(),
+ server_id.is_https(),
+ server_id.privacy_mode());
+ if (!ContainsKey(canonical_server_map_, suffix_server_id)) {
+ // This is the first host we've seen which matches the suffix, so make it
+ // canonical.
+ canonical_server_map_[suffix_server_id] = server_id;
+ return;
+ }
+
+ const QuicServerId& canonical_server_id =
+ canonical_server_map_[suffix_server_id];
+ CachedState* canonical_state = cached_states_[canonical_server_id];
+ if (!canonical_state->proof_valid()) {
+ return;
+ }
+
+ // Update canonical version to point at the "most recent" entry.
+ canonical_server_map_[suffix_server_id] = server_id;
+
+ server_state->InitializeFrom(*canonical_state);
+}
+
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.h b/chromium/net/quic/crypto/quic_crypto_client_config.h
index 30f4cf6f09e..8aa4527bd73 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config.h
+++ b/chromium/net/quic/crypto/quic_crypto_client_config.h
@@ -14,9 +14,17 @@
#include "net/base/net_export.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
namespace net {
+class ChannelIDKey;
+class ChannelIDSource;
+class CryptoHandshakeMessage;
+class ProofVerifier;
+class ProofVerifyDetails;
+class QuicRandom;
+
// QuicCryptoClientConfig contains crypto-related configuration settings for a
// client. Note that this object isn't thread-safe. It's designed to be used on
// a single thread at a time.
@@ -35,6 +43,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// cached server config has expired.
bool IsComplete(QuicWallTime now) const;
+ // IsEmpty returns true if |server_config_| is empty.
+ bool IsEmpty() const;
+
// GetServerConfig returns the parsed contents of |server_config|, or NULL
// if |server_config| is empty. The return value is owned by this object
// and is destroyed when this object is.
@@ -54,6 +65,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
void SetProof(const std::vector<std::string>& certs,
base::StringPiece signature);
+ // Clears all the data.
+ void Clear();
+
// Clears the certificate chain and signature and invalidates the proof.
void ClearProof();
@@ -86,8 +100,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// unchanged.
void InitializeFrom(const CachedState& other);
+ // Initializes this cached state based on the arguments provided.
+ // Returns false if there is a problem parsing the server config.
+ bool Initialize(base::StringPiece server_config,
+ base::StringPiece source_address_token,
+ const std::vector<std::string>& certs,
+ base::StringPiece signature,
+ QuicWallTime now);
+
private:
- std::string server_config_id_; // An opaque id from the server.
std::string server_config_; // A serialized handshake message.
std::string source_address_token_; // An opaque proof of IP ownership.
std::vector<std::string> certs_; // A list of certificates in leaf-first
@@ -115,18 +136,21 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// Sets the members to reasonable, default values.
void SetDefaults();
- // LookupOrCreate returns a CachedState for the given hostname. If no such
+ // LookupOrCreate returns a CachedState for the given |server_id|. If no such
// CachedState currently exists, it will be created and cached.
- CachedState* LookupOrCreate(const std::string& server_hostname);
+ CachedState* LookupOrCreate(const QuicServerId& server_id);
+
+ // Delete all CachedState objects from cached_states_.
+ void ClearCachedStates();
// FillInchoateClientHello sets |out| to be a CHLO message that elicits a
// source-address token or SCFG from a server. If |cached| is non-NULL, the
// source-address token will be taken from it. |out_params| is used in order
// to store the cached certs that were sent as hints to the server in
- // |out_params->cached_certs|. |preferred_version| is the version of the QUIC
- // protocol that this client chose to use initially. This allows the server to
- // detect downgrade attacks.
- void FillInchoateClientHello(const std::string& server_hostname,
+ // |out_params->cached_certs|. |preferred_version| is the version of the
+ // QUIC protocol that this client chose to use initially. This allows the
+ // server to detect downgrade attacks.
+ void FillInchoateClientHello(const QuicServerId& server_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -134,20 +158,25 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// FillClientHello sets |out| to be a CHLO message based on the configuration
// of this object. This object must have cached enough information about
- // |server_hostname| in order to perform a handshake. This can be checked
+ // the server's hostname in order to perform a handshake. This can be checked
// with the |IsComplete| member of |CachedState|.
//
- // |clock| and |rand| are used to generate the nonce and |out_params| is
+ // |now| and |rand| are used to generate the nonce and |out_params| is
// filled with the results of the handshake that the server is expected to
// accept. |preferred_version| is the version of the QUIC protocol that this
// client chose to use initially. This allows the server to detect downgrade
// attacks.
- QuicErrorCode FillClientHello(const std::string& server_hostname,
- QuicGuid guid,
+ //
+ // If |channel_id_key| is not null, it is used to sign a secret value derived
+ // from the client and server's keys, and the Channel ID public key and the
+ // signature are placed in the CETV value of the CHLO.
+ QuicErrorCode FillClientHello(const QuicServerId& server_id,
+ QuicConnectionId connection_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicWallTime now,
QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out,
std::string* error_details) const;
@@ -173,7 +202,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// server. The contents of this list will be compared against the list of
// versions provided in the VER tag of the server hello.
QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- QuicGuid guid,
+ QuicConnectionId connection_id,
const QuicVersionVector& negotiated_versions,
CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -187,28 +216,73 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// the server.
void SetProofVerifier(ProofVerifier* verifier);
- ChannelIDSigner* channel_id_signer() const;
+ ChannelIDSource* channel_id_source() const;
- // SetChannelIDSigner sets a ChannelIDSigner that will be called when the
- // server supports channel IDs to sign a message proving possession of the
- // given ChannelID. This object takes ownership of |signer|.
- void SetChannelIDSigner(ChannelIDSigner* signer);
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID. This object takes ownership of
+ // |source|.
+ void SetChannelIDSource(ChannelIDSource* source);
// Initialize the CachedState from |canonical_crypto_config| for the
- // |canonical_server_hostname| as the initial CachedState for
- // |server_hostname|. We will copy config data only if
- // |canonical_crypto_config| has valid proof.
- void InitializeFrom(const std::string& server_hostname,
- const std::string& canonical_server_hostname,
+ // |canonical_server_id| as the initial CachedState for |server_id|. We will
+ // copy config data only if |canonical_crypto_config| has valid proof.
+ void InitializeFrom(const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
QuicCryptoClientConfig* canonical_crypto_config);
+ // Adds |suffix| as a domain suffix for which the server's crypto config
+ // is expected to be shared among servers with the domain suffix. If a server
+ // matches this suffix, then the server config from another server with the
+ // suffix will be used to initialize the cached state for this server.
+ void AddCanonicalSuffix(const std::string& suffix);
+
+ // Prefers AES-GCM (kAESG) over other AEAD algorithms. Call this method if
+ // the CPU has hardware acceleration for AES-GCM. This method can only be
+ // called after SetDefaults().
+ void PreferAesGcm();
+
+ // Disables the use of ECDSA for proof verification.
+ // Call this method on platforms that do not support ECDSA.
+ // TODO(rch): remove this method when we drop support for Windows XP.
+ void DisableEcdsa();
+
+ // Saves the |user_agent_id| that will be passed in QUIC's CHLO message.
+ void set_user_agent_id(const std::string& user_agent_id) {
+ user_agent_id_ = user_agent_id;
+ }
+
private:
- // cached_states_ maps from the server hostname to the cached information
- // about that server.
- std::map<std::string, CachedState*> cached_states_;
+ typedef std::map<QuicServerId, CachedState*> CachedStateMap;
+
+ // If the suffix of the hostname in |server_id| is in |canoncial_suffixes_|,
+ // then populate |cached| with the canonical cached state from
+ // |canonical_server_map_| for that suffix.
+ void PopulateFromCanonicalConfig(const QuicServerId& server_id,
+ CachedState* cached);
+
+ // cached_states_ maps from the server_id to the cached information about
+ // that server.
+ CachedStateMap cached_states_;
+
+ // Contains a map of servers which could share the same server config. Map
+ // from a canonical host suffix/port/scheme to a representative server with
+ // the canonical suffix, which has a plausible set of initial certificates
+ // (or at least server public key).
+ std::map<QuicServerId, QuicServerId> canonical_server_map_;
+
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com") of canoncial hostnames.
+ std::vector<std::string> canoncial_suffixes_;
scoped_ptr<ProofVerifier> proof_verifier_;
- scoped_ptr<ChannelIDSigner> channel_id_signer_;
+ scoped_ptr<ChannelIDSource> channel_id_source_;
+
+ // True if ECDSA should be disabled.
+ bool disable_ecdsa_;
+
+ // The |user_agent_id_| passed in QUIC's CHLO message.
+ std::string user_agent_id_;
DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig);
};
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
index 2893372dd1a..fdce91c1fc7 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
+++ b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
@@ -4,30 +4,131 @@
#include "net/quic/crypto/quic_crypto_client_config.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::string;
+using std::vector;
namespace net {
namespace test {
+TEST(QuicCryptoClientConfigTest, CachedState_IsEmpty) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_IsComplete) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_GenerationCounter) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_EQ(0u, state.generation_counter());
+ state.SetProofInvalid();
+ EXPECT_EQ(1u, state.generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.proof_verify_details() == NULL);
+ ProofVerifyDetails* details = new ProofVerifyDetails;
+ state.SetProofVerifyDetails(details);
+ EXPECT_EQ(details, state.proof_verify_details());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig::CachedState other;
+ state.set_source_address_token("TOKEN");
+ // TODO(rch): Populate other fields of |state|.
+ other.InitializeFrom(state);
+ EXPECT_EQ(state.server_config(), other.server_config());
+ EXPECT_EQ(state.source_address_token(), other.source_address_token());
+ EXPECT_EQ(state.certs(), other.certs());
+ EXPECT_EQ(1u, other.generation_counter());
+}
+
TEST(QuicCryptoClientConfigTest, InchoateChlo) {
QuicCryptoClientConfig::CachedState state;
QuicCryptoClientConfig config;
QuicCryptoNegotiatedParameters params;
CryptoHandshakeMessage msg;
- config.FillInchoateClientHello("www.google.com", QuicVersionMax(), &state,
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
&params, &msg);
QuicTag cver;
EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kVER, &cver));
EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
+}
+
+TEST(QuicCryptoClientConfigTest, PreferAesGcm) {
+ QuicCryptoClientConfig config;
+ config.SetDefaults();
+ if (config.aead.size() > 1)
+ EXPECT_NE(kAESG, config.aead[0]);
+ config.PreferAesGcm();
+ EXPECT_EQ(kAESG, config.aead[0]);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecure) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ &params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX509, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecureNoEcdsa) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ config.DisableEcdsa();
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ &params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX59R, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, FillClientHello) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ QuicConnectionId kConnectionId = 1234;
+ string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillClientHello(server_id,
+ kConnectionId,
+ QuicVersionMax(),
+ &state,
+ QuicWallTime::Zero(),
+ &rand,
+ NULL, // channel_id_key
+ &params,
+ &chlo,
+ &error_details);
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- uint16 vers;
- EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint16(kVERS, &vers));
- EXPECT_EQ(0u, vers);
+ // Verify that certain QuicTags have been set correctly in the CHLO.
+ QuicTag cver;
+ EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver));
+ EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
}
TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
@@ -55,5 +156,104 @@ TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
EXPECT_EQ("Downgrade attack detected", error);
}
+TEST(QuicCryptoClientConfigTest, InitializeFrom) {
+ QuicCryptoClientConfig config;
+ QuicServerId canonical_server_id("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_server_id);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicServerId other_server_id("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ config.InitializeFrom(other_server_id, canonical_server_id, &config);
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(other_server_id);
+
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, Canonical) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(canonical_id2);
+
+ EXPECT_TRUE(state->IsEmpty());
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+
+ QuicServerId different_id("mail.google.org", 80, false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+
+ // Do not set the proof as valid, and check that it is not used
+ // as a canonical entry.
+ EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, ClearCachedStates) {
+ QuicCryptoClientConfig config;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state = config.LookupOrCreate(server_id);
+ // TODO(rch): Populate other fields of |state|.
+ vector<string> certs(1);
+ certs[0] = "Hello Cert";
+ state->SetProof(certs, "signature");
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+ EXPECT_EQ(1u, state->generation_counter());
+
+ // Verify LookupOrCreate returns the same data.
+ QuicCryptoClientConfig::CachedState* other = config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, other);
+ EXPECT_EQ(1u, other->generation_counter());
+
+ // Clear the cached states.
+ config.ClearCachedStates();
+
+ // Verify LookupOrCreate doesn't have any data.
+ QuicCryptoClientConfig::CachedState* cleared_cache =
+ config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(2u, cleared_cache->generation_counter());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.cc b/chromium/net/quic/crypto/quic_crypto_server_config.cc
index 31651dbf633..8e48fbb32ca 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config.cc
+++ b/chromium/net/quic/crypto/quic_crypto_server_config.cc
@@ -15,6 +15,7 @@
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_server_config_protobuf.h"
@@ -32,17 +33,33 @@
#include "net/quic/crypto/strike_register.h"
#include "net/quic/crypto/strike_register_client.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
#include "net/quic/quic_utils.h"
using base::StringPiece;
using crypto::SecureHash;
using std::map;
+using std::sort;
using std::string;
using std::vector;
namespace net {
+namespace {
+
+string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) {
+ crypto::HKDF hkdf(source_address_token_secret,
+ StringPiece() /* no salt */,
+ "QUIC source address token key",
+ CryptoSecretBoxer::GetKeySize(),
+ 0 /* no fixed IV needed */);
+ return hkdf.server_write_key().as_string();
+}
+
+} // namespace
+
// ClientHelloInfo contains information about a client hello message that is
// only kept for as long as it's being processed.
struct ClientHelloInfo {
@@ -64,6 +81,10 @@ struct ClientHelloInfo {
StringPiece sni;
StringPiece client_nonce;
StringPiece server_nonce;
+ StringPiece user_agent_id;
+
+ // Errors from EvaluateClientHello.
+ vector<uint32> reject_reasons;
};
struct ValidateClientHelloResultCallback::Result {
@@ -89,10 +110,8 @@ class ValidateClientHelloHelper {
}
~ValidateClientHelloHelper() {
- if (done_cb_ != NULL) {
- LOG(DFATAL) <<
- "Deleting ValidateClientHelloHelper with a pending callback.";
- }
+ LOG_IF(DFATAL, done_cb_ != NULL)
+ << "Deleting ValidateClientHelloHelper with a pending callback.";
}
void ValidationComplete(QuicErrorCode error_code, const char* error_details) {
@@ -108,9 +127,7 @@ class ValidateClientHelloHelper {
private:
void DetachCallback() {
- if (done_cb_ == NULL) {
- LOG(DFATAL) << "Callback already detached.";
- }
+ LOG_IF(DFATAL, done_cb_ == NULL) << "Callback already detached.";
done_cb_ = NULL;
}
@@ -133,6 +150,12 @@ class VerifyNonceIsValidAndUniqueCallback
virtual void RunImpl(bool nonce_is_valid_and_unique) OVERRIDE {
DVLOG(1) << "Using client nonce, unique: " << nonce_is_valid_and_unique;
result_->info.unique = nonce_is_valid_and_unique;
+ // TODO(rtenneti): Implement capturing of error from strike register.
+ // Temporarily treat them as CLIENT_NONCE_UNKNOWN_FAILURE.
+ if (!nonce_is_valid_and_unique) {
+ result_->info.reject_reasons.push_back(
+ static_cast<uint32>(CLIENT_NONCE_UNKNOWN_FAILURE));
+ }
done_cb_->Run(result_);
}
@@ -146,6 +169,11 @@ class VerifyNonceIsValidAndUniqueCallback
// static
const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {
+}
+
+PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {
+}
ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {
}
@@ -179,11 +207,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig(
source_address_token_lifetime_secs_(86400),
server_nonce_strike_register_max_entries_(1 << 10),
server_nonce_strike_register_window_secs_(120) {
- crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
- "QUIC source address token key",
- CryptoSecretBoxer::GetKeySize(),
- 0 /* no fixed IV needed */);
- source_address_token_boxer_.SetKey(hkdf.server_write_key());
+ default_source_address_token_boxer_.SetKey(
+ DeriveSourceAddressTokenKey(source_address_token_secret));
// Generate a random key and orbit for server nonces.
rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_));
@@ -200,7 +225,7 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() {
}
// static
-QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
+QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig(
QuicRandom* rand,
const QuicClock* clock,
const ConfigOptions& options) {
@@ -239,9 +264,11 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
} else {
msg.SetTaglist(kKEXS, kC255, 0);
}
- msg.SetTaglist(kAEAD, kAESG, 0);
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- msg.SetValue(kVERS, static_cast<uint16>(0));
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ msg.SetTaglist(kAEAD, kAESG, kCC12, 0);
+ } else {
+ msg.SetTaglist(kAEAD, kAESG, 0);
+ }
msg.SetStringPiece(kPUBS, encoded_public_values);
if (options.expiry_time.IsZero()) {
@@ -331,6 +358,7 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
configs_[config->id] = config;
SelectNewPrimaryConfig(now);
DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
}
return msg.release();
@@ -341,14 +369,14 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
const QuicClock* clock,
const ConfigOptions& options) {
scoped_ptr<QuicServerConfigProtobuf> config(
- DefaultConfig(rand, clock, options));
+ GenerateConfig(rand, clock, options));
return AddConfig(config.get(), clock->WallNow());
}
bool QuicCryptoServerConfig::SetConfigs(
const vector<QuicServerConfigProtobuf*>& protobufs,
const QuicWallTime now) {
- vector<scoped_refptr<Config> > new_configs;
+ vector<scoped_refptr<Config> > parsed_configs;
bool ok = true;
for (vector<QuicServerConfigProtobuf*>::const_iterator i = protobufs.begin();
@@ -358,95 +386,109 @@ bool QuicCryptoServerConfig::SetConfigs(
ok = false;
break;
}
- new_configs.push_back(config);
+
+ parsed_configs.push_back(config);
+ }
+
+ if (parsed_configs.empty()) {
+ LOG(WARNING) << "New config list is empty.";
+ ok = false;
}
if (!ok) {
LOG(WARNING) << "Rejecting QUIC configs because of above errors";
} else {
- base::AutoLock locked(configs_lock_);
- typedef ConfigMap::iterator ConfigMapIterator;
- vector<ConfigMapIterator> to_delete;
-
- DCHECK_EQ(protobufs.size(), new_configs.size());
-
- // First, look for any configs that have been removed.
- for (ConfigMapIterator i = configs_.begin();
- i != configs_.end(); ++i) {
- const scoped_refptr<Config> old_config = i->second;
- bool found = false;
-
- for (vector<scoped_refptr<Config> >::const_iterator j =
- new_configs.begin();
- j != new_configs.end(); ++j) {
- if ((*j)->id == old_config->id) {
- found = true;
- break;
- }
- }
+ VLOG(1) << "Updating configs:";
- if (!found) {
- // We cannot remove the primary config. This has probably happened
- // because our source of config information failed for a time and we're
- // suddenly seeing a jump in time. No matter - we'll configure a new
- // primary config and then we'll be able to delete it next time.
- if (!old_config->is_primary) {
- to_delete.push_back(i);
- }
- }
- }
+ base::AutoLock locked(configs_lock_);
+ ConfigMap new_configs;
- for (vector<ConfigMapIterator>::const_iterator i = to_delete.begin();
- i != to_delete.end(); ++i) {
- configs_.erase(*i);
- }
+ for (vector<scoped_refptr<Config> >::const_iterator i =
+ parsed_configs.begin();
+ i != parsed_configs.end(); ++i) {
+ scoped_refptr<Config> config = *i;
- // Find any configs that need to be added.
- for (vector<scoped_refptr<Config> >::const_iterator i = new_configs.begin();
- i != new_configs.end(); ++i) {
- const scoped_refptr<Config> new_config = *i;
- if (configs_.find(new_config->id) != configs_.end()) {
- continue;
+ ConfigMap::iterator it = configs_.find(config->id);
+ if (it != configs_.end()) {
+ VLOG(1)
+ << "Keeping scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " new primary_time " << config->primary_time.ToUNIXSeconds()
+ << " old primary_time " << it->second->primary_time.ToUNIXSeconds()
+ << " new priority " << config->priority
+ << " old priority " << it->second->priority;
+ // Update primary_time and priority.
+ it->second->primary_time = config->primary_time;
+ it->second->priority = config->priority;
+ new_configs.insert(*it);
+ } else {
+ VLOG(1) << "Adding scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " primary_time " << config->primary_time.ToUNIXSeconds()
+ << " priority " << config->priority;
+ new_configs.insert(make_pair(config->id, config));
}
-
- configs_[new_config->id] = new_config;
}
+ configs_.swap(new_configs);
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
}
return ok;
}
+void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const {
+ base::AutoLock locked(configs_lock_);
+ for (ConfigMap::const_iterator it = configs_.begin();
+ it != configs_.end(); ++it) {
+ scids->push_back(it->first);
+ }
+}
+
void QuicCryptoServerConfig::ValidateClientHello(
const CryptoHandshakeMessage& client_hello,
IPEndPoint client_ip,
const QuicClock* clock,
ValidateClientHelloResultCallback* done_cb) const {
const QuicWallTime now(clock->WallNow());
+
ValidateClientHelloResultCallback::Result* result =
new ValidateClientHelloResultCallback::Result(
client_hello, client_ip, now);
+ StringPiece requested_scid;
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+
uint8 primary_orbit[kOrbitSize];
+ scoped_refptr<Config> requested_config;
{
base::AutoLock locked(configs_lock_);
- if (!primary_config_) {
+ if (!primary_config_.get()) {
result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
result->error_details = "No configurations loaded";
} else {
if (!next_config_promotion_time_.IsZero() &&
next_config_promotion_time_.IsAfter(now)) {
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK(configs_.find(primary_config_->id)->second == primary_config_);
}
memcpy(primary_orbit, primary_config_->orbit, sizeof(primary_orbit));
}
+
+ requested_config = GetConfigWithScid(requested_scid);
}
if (result->error_code == QUIC_NO_ERROR) {
- EvaluateClientHello(primary_orbit, result, done_cb);
+ EvaluateClientHello(primary_orbit, requested_config, result, done_cb);
} else {
done_cb->Run(result);
}
@@ -454,8 +496,8 @@ void QuicCryptoServerConfig::ValidateClientHello(
QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
const ValidateClientHelloResultCallback::Result& validate_chlo_result,
- QuicGuid guid,
- IPEndPoint client_ip,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
QuicVersion version,
const QuicVersionVector& supported_versions,
const QuicClock* clock,
@@ -474,18 +516,19 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
// case, we need to make sure that we actually do not support this version
// and that it wasn't a downgrade attack.
QuicTag client_version_tag;
- // TODO(rch): Make this check mandatory when we remove QUIC_VERSION_12.
- if (client_hello.GetUint32(kVER, &client_version_tag) == QUIC_NO_ERROR) {
- QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
- if (client_version != version) {
- // Just because client_version is a valid version enum doesn't mean that
- // this server actually supports that version, so we check to see if
- // it's actually in the supported versions list.
- for (size_t i = 0; i < supported_versions.size(); ++i) {
- if (client_version == supported_versions[i]) {
- *error_details = "Downgrade attack detected";
- return QUIC_VERSION_NEGOTIATION_MISMATCH;
- }
+ if (client_hello.GetUint32(kVER, &client_version_tag) != QUIC_NO_ERROR) {
+ *error_details = "client hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
+ if (client_version != version) {
+ // Just because client_version is a valid version enum doesn't mean that
+ // this server actually supports that version, so we check to see if
+ // it's actually in the supported versions list.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (client_version == supported_versions[i]) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
}
}
}
@@ -507,19 +550,16 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
if (!next_config_promotion_time_.IsZero() &&
next_config_promotion_time_.IsAfter(now)) {
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK(configs_.find(primary_config_->id)->second == primary_config_);
}
+ // We'll use the config that the client requested in order to do
+ // key-agreement. Otherwise we'll give it a copy of |primary_config_|
+ // to use.
primary_config = primary_config_;
- if (!requested_scid.empty()) {
- ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
- if (it != configs_.end()) {
- // We'll use the config that the client requested in order to do
- // key-agreement. Otherwise we'll give it a copy of |primary_config_|
- // to use.
- requested_config = it->second;
- }
- }
+ requested_config = GetConfigWithScid(requested_scid);
}
if (validate_chlo_result.error_code != QUIC_NO_ERROR) {
@@ -533,7 +573,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
!info.client_nonce_well_formed ||
!info.unique ||
!requested_config.get()) {
- BuildRejection(primary_config.get(), client_hello, info, rand, out);
+ BuildRejection(*primary_config, client_hello, info, rand, out);
return QUIC_NO_ERROR;
}
@@ -585,9 +625,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
string hkdf_suffix;
const QuicData& client_hello_serialized = client_hello.GetSerialized();
- hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() +
+ hkdf_suffix.reserve(sizeof(connection_id) + client_hello_serialized.length() +
requested_config->serialized.size());
- hkdf_suffix.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_suffix.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_suffix.append(requested_config->serialized);
@@ -603,7 +644,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
string hkdf_input;
hkdf_input.append(QuicCryptoConfig::kCETVLabel,
strlen(QuicCryptoConfig::kCETVLabel) + 1);
- hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_input.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_input.append(requested_config->serialized);
@@ -698,65 +740,92 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
}
out->SetVector(kVER, supported_version_tags);
out->SetStringPiece(kSourceAddressTokenTag,
- NewSourceAddressToken(client_ip, rand, info.now));
+ NewSourceAddressToken(
+ *requested_config,
+ client_address, rand,
+ info.now));
+ QuicSocketAddressCoder address_coder(client_address);
+ out->SetStringPiece(kCADR, address_coder.Encode());
out->SetStringPiece(kPUBS, forward_secure_public_value);
+
return QUIC_NO_ERROR;
}
+scoped_refptr<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::GetConfigWithScid(StringPiece requested_scid) const {
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertReaderHeld API call.
+ // configs_lock_.AssertReaderHeld();
+
+ if (!requested_scid.empty()) {
+ ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
+ if (it != configs_.end()) {
+ // We'll use the config that the client requested in order to do
+ // key-agreement.
+ return scoped_refptr<Config>(it->second);
+ }
+ }
+
+ return scoped_refptr<Config>();
+}
+
// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
// Config's based on their primary_time.
// static
bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
const scoped_refptr<Config>& a,
const scoped_refptr<Config>& b) {
- return a->primary_time.IsBefore(b->primary_time);
+ if (a->primary_time.IsBefore(b->primary_time) ||
+ b->primary_time.IsBefore(a->primary_time)) {
+ // Primary times differ.
+ return a->primary_time.IsBefore(b->primary_time);
+ } else if (a->priority != b->priority) {
+ // Primary times are equal, sort backwards by priority.
+ return a->priority < b->priority;
+ } else {
+ // Primary times and priorities are equal, sort by config id.
+ return a->id < b->id;
+ }
}
void QuicCryptoServerConfig::SelectNewPrimaryConfig(
const QuicWallTime now) const {
vector<scoped_refptr<Config> > configs;
configs.reserve(configs_.size());
- scoped_refptr<Config> first_config = NULL;
for (ConfigMap::const_iterator it = configs_.begin();
it != configs_.end(); ++it) {
- const scoped_refptr<Config> config(it->second);
- if (!first_config.get()) {
- first_config = config;
- }
- if (config->primary_time.IsZero()) {
- continue;
- }
+ // TODO(avd) Exclude expired configs?
configs.push_back(it->second);
}
if (configs.empty()) {
- // Tests don't set |primary_time_|. For that case we promote the first
- // Config and leave it as primary forever.
- if (!primary_config_.get() && first_config.get()) {
- primary_config_ = first_config;
- primary_config_->is_primary = true;
+ if (primary_config_.get()) {
+ LOG(DFATAL) << "No valid QUIC server config. Keeping the current config.";
+ } else {
+ LOG(DFATAL) << "No valid QUIC server config.";
}
return;
}
- std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+ sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+
+ Config* best_candidate = configs[0];
for (size_t i = 0; i < configs.size(); ++i) {
const scoped_refptr<Config> config(configs[i]);
-
if (!config->primary_time.IsAfter(now)) {
+ if (config->primary_time.IsAfter(best_candidate->primary_time)) {
+ best_candidate = config;
+ }
continue;
}
// This is the first config with a primary_time in the future. Thus the
// previous Config should be the primary and this one should determine the
// next_config_promotion_time_.
- scoped_refptr<Config> new_primary;
+ scoped_refptr<Config> new_primary(best_candidate);
if (i == 0) {
- // There was no previous Config, so this will have to be primary.
- new_primary = config;
-
// We need the primary_time of the next config.
if (configs.size() > 1) {
next_config_promotion_time_ = configs[1]->primary_time;
@@ -764,7 +833,6 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig(
next_config_promotion_time_ = QuicWallTime::Zero();
}
} else {
- new_primary = configs[i - 1];
next_config_promotion_time_ = config->primary_time;
}
@@ -773,23 +841,40 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig(
}
primary_config_ = new_primary;
new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize);
+ if (primary_config_changed_cb_.get() != NULL) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
return;
}
// All config's primary times are in the past. We should make the most recent
- // primary.
- scoped_refptr<Config> new_primary = configs[configs.size() - 1];
+ // and highest priority candidate primary.
+ scoped_refptr<Config> new_primary(best_candidate);
if (primary_config_.get()) {
primary_config_->is_primary = false;
}
primary_config_ = new_primary;
new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize)
+ << " scid: " << base::HexEncode(primary_config_->id.data(),
+ primary_config_->id.size());
next_config_promotion_time_ = QuicWallTime::Zero();
+ if (primary_config_changed_cb_.get() != NULL) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
}
void QuicCryptoServerConfig::EvaluateClientHello(
const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
ValidateClientHelloResultCallback::Result* client_hello_state,
ValidateClientHelloResultCallback* done_cb) const {
ValidateClientHelloHelper helper(client_hello_state, done_cb);
@@ -798,7 +883,7 @@ void QuicCryptoServerConfig::EvaluateClientHello(
client_hello_state->client_hello;
ClientHelloInfo* info = &(client_hello_state->info);
- if (client_hello.size() < kClientHelloMinimumSizeOld) {
+ if (client_hello.size() < kClientHelloMinimumSize) {
helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH,
"Client hello too small");
return;
@@ -811,28 +896,67 @@ void QuicCryptoServerConfig::EvaluateClientHello(
return;
}
+ client_hello.GetStringPiece(kUAID, &info->user_agent_id);
+
+ if (!requested_config.get()) {
+ StringPiece requested_scid;
+ if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE));
+ } else {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(SERVER_CONFIG_INCHOATE_HELLO_FAILURE));
+ }
+ // No server config with the requested ID.
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ HandshakeFailureReason source_address_token_error;
StringPiece srct;
- if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) &&
- ValidateSourceAddressToken(srct, info->client_ip, info->now)) {
- info->valid_source_address_token = true;
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
+ source_address_token_error =
+ ValidateSourceAddressToken(*requested_config,
+ srct,
+ info->client_ip,
+ info->now);
+ info->valid_source_address_token =
+ (source_address_token_error == HANDSHAKE_OK);
} else {
+ source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
+ }
+
+ bool found_error = false;
+ if (source_address_token_error != HANDSHAKE_OK) {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(source_address_token_error));
// No valid source address token.
- helper.ValidationComplete(QUIC_NO_ERROR, "");
- return;
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
}
if (client_hello.GetStringPiece(kNONC, &info->client_nonce) &&
info->client_nonce.size() == kNonceSize) {
info->client_nonce_well_formed = true;
} else {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(CLIENT_NONCE_INVALID_FAILURE));
// Invalid client nonce.
DVLOG(1) << "Invalid client nonce.";
- helper.ValidationComplete(QUIC_NO_ERROR, "");
- return;
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
}
if (!replay_protection_) {
- info->unique = true;
+ if (!found_error) {
+ info->unique = true;
+ }
DVLOG(1) << "No replay protection.";
helper.ValidationComplete(QUIC_NO_ERROR, "");
return;
@@ -841,27 +965,45 @@ void QuicCryptoServerConfig::EvaluateClientHello(
client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
if (!info->server_nonce.empty()) {
// If the server nonce is present, use it to establish uniqueness.
- info->unique = ValidateServerNonce(info->server_nonce, info->now);
+ HandshakeFailureReason server_nonce_error =
+ ValidateServerNonce(info->server_nonce, info->now);
+ if (server_nonce_error == HANDSHAKE_OK) {
+ info->unique = true;
+ } else {
+ info->reject_reasons.push_back(static_cast<uint32>(server_nonce_error));
+ info->unique = false;
+ }
DVLOG(1) << "Using server nonce, unique: " << info->unique;
helper.ValidationComplete(QUIC_NO_ERROR, "");
return;
}
+ // We want to contact strike register if there are no errors because it is
+ // a RPC call and is expensive.
+ if (found_error) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
// Use the client nonce to establish uniqueness.
- base::AutoLock locked(strike_register_client_lock_);
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
- if (strike_register_client_.get() == NULL) {
- strike_register_client_.reset(new LocalStrikeRegisterClient(
- strike_register_max_entries_,
- static_cast<uint32>(info->now.ToUNIXSeconds()),
- strike_register_window_secs_,
- primary_orbit,
- strike_register_no_startup_period_ ?
- StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
- StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ if (strike_register_client_.get() == NULL) {
+ strike_register_client_.reset(new LocalStrikeRegisterClient(
+ strike_register_max_entries_,
+ static_cast<uint32>(info->now.ToUNIXSeconds()),
+ strike_register_window_secs_,
+ primary_orbit,
+ strike_register_no_startup_period_ ?
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ }
+ strike_register_client = strike_register_client_.get();
}
- strike_register_client_->VerifyNonceIsValidAndUnique(
+ strike_register_client->VerifyNonceIsValidAndUnique(
info->client_nonce,
info->now,
new VerifyNonceIsValidAndUniqueCallback(client_hello_state, done_cb));
@@ -869,19 +1011,29 @@ void QuicCryptoServerConfig::EvaluateClientHello(
}
void QuicCryptoServerConfig::BuildRejection(
- const scoped_refptr<Config>& config,
+ const Config& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
CryptoHandshakeMessage* out) const {
out->set_tag(kREJ);
- out->SetStringPiece(kSCFG, config->serialized);
+ out->SetStringPiece(kSCFG, config.serialized);
out->SetStringPiece(kSourceAddressTokenTag,
- NewSourceAddressToken(info.client_ip, rand, info.now));
+ NewSourceAddressToken(
+ config,
+ info.client_ip,
+ rand,
+ info.now));
if (replay_protection_) {
out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now));
}
+ if (FLAGS_send_quic_crypto_reject_reason) {
+ // Send client the reject reason for debugging purposes.
+ DCHECK_LT(0u, info.reject_reasons.size());
+ out->SetVector(kRREJ, info.reject_reasons);
+ }
+
// The client may have requested a certificate chain.
const QuicTag* their_proof_demands;
size_t num_their_proof_demands;
@@ -912,7 +1064,7 @@ void QuicCryptoServerConfig::BuildRejection(
const vector<string>* certs;
string signature;
- if (!proof_source_->GetProof(info.sni.as_string(), config->serialized,
+ if (!proof_source_->GetProof(info.sni.as_string(), config.serialized,
x509_ecdsa_supported, &certs, &signature)) {
return;
}
@@ -924,7 +1076,7 @@ void QuicCryptoServerConfig::BuildRejection(
const string compressed = CertCompressor::CompressChain(
*certs, their_common_set_hashes, their_cached_cert_hashes,
- config->common_cert_sets);
+ config.common_cert_sets);
// kREJOverheadBytes is a very rough estimate of how much of a REJ
// message is taken up by things other than the certificates.
@@ -943,7 +1095,7 @@ void QuicCryptoServerConfig::BuildRejection(
// token.
const size_t max_unverified_size =
client_hello.size() * kMultiplier - kREJOverheadBytes;
- COMPILE_ASSERT(kClientHelloMinimumSizeOld * kMultiplier >= kREJOverheadBytes,
+ COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
overhead_calculation_may_underflow);
if (info.valid_source_address_token ||
signature.size() + compressed.size() < max_unverified_size) {
@@ -967,11 +1119,25 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
scoped_refptr<Config> config(new Config);
config->serialized = protobuf->config();
+ if (!protobuf->has_source_address_token_secret_override()) {
+ // Use the default boxer.
+ config->source_address_token_boxer = &default_source_address_token_boxer_;
+ } else {
+ // Create override boxer instance.
+ CryptoSecretBoxer* boxer = new CryptoSecretBoxer;
+ boxer->SetKey(DeriveSourceAddressTokenKey(
+ protobuf->source_address_token_secret_override()));
+ config->source_address_token_boxer_storage.reset(boxer);
+ config->source_address_token_boxer = boxer;
+ }
+
if (protobuf->has_primary_time()) {
config->primary_time =
QuicWallTime::FromUNIXSeconds(protobuf->primary_time());
}
+ config->priority = protobuf->priority();
+
StringPiece scid;
if (!msg->GetStringPiece(kSCID, &scid)) {
LOG(WARNING) << "Server config message is missing SCID";
@@ -996,7 +1162,7 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
StringPiece orbit;
if (!msg->GetStringPiece(kORBT, &orbit)) {
- LOG(WARNING) << "Server config message is missing OBIT";
+ LOG(WARNING) << "Server config message is missing ORBT";
return NULL;
}
@@ -1009,15 +1175,18 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
{
- base::AutoLock locked(strike_register_client_lock_);
- if (strike_register_client_.get()) {
- const string& orbit = strike_register_client_->orbit();
- if (0 != memcmp(orbit.data(), config->orbit, kOrbitSize)) {
- LOG(WARNING)
- << "Server config has different orbit than current config. "
- "Switching orbits at run-time is not supported.";
- return NULL;
- }
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
+ strike_register_client = strike_register_client_.get();
+ }
+
+ if (strike_register_client != NULL &&
+ !strike_register_client->IsKnownOrbit(orbit)) {
+ LOG(WARNING)
+ << "Rejecting server config with orbit that the strike register "
+ "client doesn't know about.";
+ return NULL;
}
}
@@ -1160,37 +1329,52 @@ void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs(
server_nonce_strike_register_window_secs_ = window_secs;
}
-string QuicCryptoServerConfig::NewSourceAddressToken(
- const IPEndPoint& ip,
- QuicRandom* rand,
- QuicWallTime now) const {
+void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
+ PrimaryConfigChangedCallback* cb) {
+ base::AutoLock locked(configs_lock_);
+ primary_config_changed_cb_.reset(cb);
+}
+
+string QuicCryptoServerConfig::NewSourceAddressToken(const Config& config,
+ const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicWallTime now) const {
SourceAddressToken source_address_token;
- source_address_token.set_ip(IPAddressToPackedString(ip.address()));
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ source_address_token.set_ip(IPAddressToPackedString(ip_address));
source_address_token.set_timestamp(now.ToUNIXSeconds());
- return source_address_token_boxer_.Box(
+ return config.source_address_token_boxer->Box(
rand, source_address_token.SerializeAsString());
}
-bool QuicCryptoServerConfig::ValidateSourceAddressToken(
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken(
+ const Config& config,
StringPiece token,
const IPEndPoint& ip,
QuicWallTime now) const {
string storage;
StringPiece plaintext;
- if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) {
- return false;
+ if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) {
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
}
SourceAddressToken source_address_token;
if (!source_address_token.ParseFromArray(plaintext.data(),
plaintext.size())) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
}
- if (source_address_token.ip() != IPAddressToPackedString(ip.address())) {
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ if (source_address_token.ip() != IPAddressToPackedString(ip_address)) {
// It's for a different IP address.
- return false;
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
}
const QuicWallTime timestamp(
@@ -1199,15 +1383,15 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken(
if (now.IsBefore(timestamp) &&
delta.ToSeconds() > source_address_token_future_secs_) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
}
if (now.IsAfter(timestamp) &&
delta.ToSeconds() > source_address_token_lifetime_secs_) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
}
- return true;
+ return HANDSHAKE_OK;
}
// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
@@ -1233,12 +1417,13 @@ string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce)));
}
-bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
- QuicWallTime now) const {
+HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce(
+ StringPiece token,
+ QuicWallTime now) const {
string storage;
StringPiece plaintext;
if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) {
- return false;
+ return SERVER_NONCE_DECRYPTION_FAILURE;
}
// plaintext contains:
@@ -1248,7 +1433,7 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
if (plaintext.size() != kServerNoncePlaintextSize) {
// This should never happen because the value decrypted correctly.
LOG(DFATAL) << "Seemingly valid server nonce had incorrect length.";
- return false;
+ return SERVER_NONCE_INVALID_FAILURE;
}
uint8 server_nonce[32];
@@ -1273,13 +1458,15 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
server_nonce, static_cast<uint32>(now.ToUNIXSeconds()));
}
- return is_unique;
+ return is_unique ? HANDSHAKE_OK : SERVER_NONCE_NOT_UNIQUE_FAILURE;
}
QuicCryptoServerConfig::Config::Config()
: channel_id_enabled(false),
is_primary(false),
- primary_time(QuicWallTime::Zero()) {}
+ primary_time(QuicWallTime::Zero()),
+ priority(0),
+ source_address_token_boxer(NULL) {}
QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); }
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.h b/chromium/net/quic/crypto/quic_crypto_server_config.h
index 6d59bff6a9f..3ece064376f 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config.h
+++ b/chromium/net/quic/crypto/quic_crypto_server_config.h
@@ -22,6 +22,7 @@
namespace net {
+class CryptoHandshakeMessage;
class EphemeralKeySource;
class KeyExchange;
class ProofSource;
@@ -39,6 +40,43 @@ namespace test {
class QuicCryptoServerConfigPeer;
} // namespace test
+enum HandshakeFailureReason {
+ HANDSHAKE_OK = 0,
+
+ // Failure reasons for an invalid client nonce.
+ // TODO(rtenneti): Implement capturing of error from strike register.
+ CLIENT_NONCE_UNKNOWN_FAILURE = 100,
+ CLIENT_NONCE_INVALID_FAILURE,
+
+ // Failure reasons for an invalid server nonce.
+ SERVER_NONCE_INVALID_FAILURE = 200,
+ SERVER_NONCE_DECRYPTION_FAILURE,
+ SERVER_NONCE_NOT_UNIQUE_FAILURE,
+
+ // Failure reasons for an invalid server config.
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 300,
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE,
+
+ // Failure reasons for an invalid source adddress token.
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 400,
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE,
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+};
+
+// Hook that allows application code to subscribe to primary config changes.
+class PrimaryConfigChangedCallback {
+ public:
+ PrimaryConfigChangedCallback();
+ virtual ~PrimaryConfigChangedCallback();
+ virtual void Run(const std::string& scid) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrimaryConfigChangedCallback);
+};
+
// Callback used to accept the result of the |client_hello| validation step.
class NET_EXPORT_PRIVATE ValidateClientHelloResultCallback {
public:
@@ -101,9 +139,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// TESTING is a magic parameter for passing to the constructor in tests.
static const char TESTING[];
- // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for
- // using in tests.
- static QuicServerConfigProtobuf* DefaultConfig(
+ // Generates a QuicServerConfigProtobuf protobuf suitable for
+ // AddConfig and SetConfigs.
+ static QuicServerConfigProtobuf* GenerateConfig(
QuicRandom* rand,
const QuicClock* clock,
const ConfigOptions& options);
@@ -134,6 +172,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
bool SetConfigs(const std::vector<QuicServerConfigProtobuf*>& protobufs,
QuicWallTime now);
+ // Get the server config ids for all known configs.
+ void GetConfigIds(std::vector<std::string>* scids) const;
+
// Checks |client_hello| for gross errors and determines whether it
// can be shown to be fresh (i.e. not a replay). The result of the
// validation step must be interpreted by calling
@@ -167,12 +208,15 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// validate_chlo_result: Output from the asynchronous call to
// ValidateClientHello. Contains the client hello message and
// information about it.
- // 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.
+ // connection_id: the ConnectionId for the connection, which is used in key
+ // derivation.
+ // client_address: the IP address and port of the client. The IP address is
+ // used to generate and validate source-address tokens.
// version: version of the QUIC protocol in use for this connection
// supported_versions: versions of the QUIC protocol that this server
// supports.
+ // initial_flow_control_window: size of initial flow control window this
+ // server uses for new streams.
// clock: used to validate client nonces and ephemeral keys.
// rand: an entropy source
// params: the state of the handshake. This may be updated with a server
@@ -182,8 +226,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// error_details: used to store a string describing any error.
QuicErrorCode ProcessClientHello(
const ValidateClientHelloResultCallback::Result& validate_chlo_result,
- QuicGuid guid,
- IPEndPoint client_ip,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
QuicVersion version,
const QuicVersionVector& supported_versions,
const QuicClock* clock,
@@ -254,6 +298,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// uniqueness.
void set_server_nonce_strike_register_window_secs(uint32 window_secs);
+ // Set and take ownership of the callback to invoke on primary config changes.
+ void AcquirePrimaryConfigChangedCb(PrimaryConfigChangedCallback* cb);
+
private:
friend class test::QuicCryptoServerConfigPeer;
@@ -296,8 +343,25 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// will not be promoted at a specific time.
QuicWallTime primary_time;
+ // Secondary sort key for use when selecting primary configs and
+ // there are multiple configs with the same primary time.
+ // Smaller numbers mean higher priority.
+ uint64 priority;
+
+ // source_address_token_boxer_ is used to protect the
+ // source-address tokens that are given to clients.
+ // Points to either source_address_token_boxer_storage or the
+ // default boxer provided by QuicCryptoServerConfig.
+ const CryptoSecretBoxer* source_address_token_boxer;
+
+ // Holds the override source_address_token_boxer instance if the
+ // Config is not using the default source address token boxer
+ // instance provided by QuicCryptoServerConfig.
+ scoped_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;
+
private:
friend class base::RefCounted<Config>;
+
virtual ~Config();
DISALLOW_COPY_AND_ASSIGN(Config);
@@ -305,6 +369,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
typedef std::map<ServerConfigID, scoped_refptr<Config> > ConfigMap;
+ // Get a ref to the config with a given server config id.
+ scoped_refptr<Config> GetConfigWithScid(
+ base::StringPiece requested_scid) const;
+
// ConfigPrimaryTimeLessThan returns true if a->primary_time <
// b->primary_time.
static bool ConfigPrimaryTimeLessThan(const scoped_refptr<Config>& a,
@@ -319,12 +387,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// written to |info|.
void EvaluateClientHello(
const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
ValidateClientHelloResultCallback::Result* client_hello_state,
ValidateClientHelloResultCallback* done_cb) const;
// BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
void BuildRejection(
- const scoped_refptr<Config>& config,
+ const Config& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
@@ -337,16 +406,18 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// NewSourceAddressToken returns a fresh source address token for the given
// IP address.
- std::string NewSourceAddressToken(const IPEndPoint& ip,
+ std::string NewSourceAddressToken(const Config& config,
+ const IPEndPoint& ip,
QuicRandom* rand,
QuicWallTime now) const;
- // ValidateSourceAddressToken returns true if the source address token in
- // |token| is a valid and timely token for the IP address |ip| given that the
- // current time is |now|.
- bool ValidateSourceAddressToken(base::StringPiece token,
- const IPEndPoint& ip,
- QuicWallTime now) const;
+ // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address token
+ // in |token| is a valid and timely token for the IP address |ip| given that
+ // the current time is |now|. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateSourceAddressToken(const Config& config,
+ base::StringPiece token,
+ const IPEndPoint& ip,
+ QuicWallTime now) const;
// NewServerNonce generates and encrypts a random nonce.
std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;
@@ -354,10 +425,11 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// ValidateServerNonce decrypts |token| and verifies that it hasn't been
// previously used and is recent enough that it is plausible that it was part
// of a very recently provided rejection ("recent" will be on the order of
- // 10-30 seconds). If so, it records that it has been used and returns true.
- // Otherwise it returns false.
- bool ValidateServerNonce(base::StringPiece echoed_server_nonce,
- QuicWallTime now) const;
+ // 10-30 seconds). If so, it records that it has been used and returns
+ // HANDSHAKE_OK. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateServerNonce(
+ base::StringPiece echoed_server_nonce,
+ QuicWallTime now) const;
// replay_protection_ controls whether the server enforces that handshakes
// aren't replays.
@@ -377,15 +449,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// next_config_promotion_time_ contains the nearest, future time when an
// active config will be promoted to primary.
mutable QuicWallTime next_config_promotion_time_;
+ // Callback to invoke when the primary config changes.
+ scoped_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_;
+ // Protects access to the pointer held by strike_register_client_.
mutable base::Lock strike_register_client_lock_;
// strike_register_ contains a data structure that keeps track of previously
// observed client nonces in order to prevent replay attacks.
mutable scoped_ptr<StrikeRegisterClient> strike_register_client_;
- // source_address_token_boxer_ is used to protect the source-address tokens
- // that are given to clients.
- CryptoSecretBoxer source_address_token_boxer_;
+ // Default source_address_token_boxer_ used to protect the
+ // source-address tokens that are given to clients. Individual
+ // configs may use boxers with alternate secrets.
+ CryptoSecretBoxer default_source_address_token_boxer_;
// server_nonce_boxer_ is used to encrypt and validate suggested server
// nonces.
@@ -419,6 +495,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
uint32 source_address_token_lifetime_secs_;
uint32 server_nonce_strike_register_max_entries_;
uint32 server_nonce_strike_register_window_secs_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
index fc8882cd85e..22bfd2c6cc6 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
+++ b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
@@ -8,11 +8,14 @@
#include "base/stl_util.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_secret_boxer.h"
#include "net/quic/crypto/crypto_server_config_protobuf.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/strike_register_client.h"
#include "net/quic/quic_time.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,16 +34,50 @@ class QuicCryptoServerConfigPeer {
explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
: server_config_(server_config) {}
- string NewSourceAddressToken(IPEndPoint ip,
- QuicRandom* rand,
- QuicWallTime now) {
- return server_config_->NewSourceAddressToken(ip, rand, now);
+ scoped_refptr<QuicCryptoServerConfig::Config> GetConfig(string config_id) {
+ base::AutoLock locked(server_config_->configs_lock_);
+ if (config_id == "<primary>") {
+ return scoped_refptr<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+ } else {
+ return server_config_->GetConfigWithScid(config_id);
+ }
+ }
+
+ bool ConfigHasDefaultSourceAddressTokenBoxer(string config_id) {
+ scoped_refptr<QuicCryptoServerConfig::Config> config = GetConfig(config_id);
+ return config->source_address_token_boxer ==
+ &(server_config_->default_source_address_token_boxer_);
+ }
+
+ string NewSourceAddressToken(
+ string config_id,
+ IPEndPoint ip,
+ QuicRandom* rand,
+ QuicWallTime now) {
+ return server_config_->NewSourceAddressToken(
+ *GetConfig(config_id), ip, rand, now);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressToken(string config_id,
+ StringPiece srct,
+ IPEndPoint ip,
+ QuicWallTime now) {
+ return server_config_->ValidateSourceAddressToken(
+ *GetConfig(config_id), srct, ip, now);
+ }
+
+ string NewServerNonce(QuicRandom* rand, QuicWallTime now) const {
+ return server_config_->NewServerNonce(rand, now);
+ }
+
+ HandshakeFailureReason ValidateServerNonce(StringPiece token,
+ QuicWallTime now) {
+ return server_config_->ValidateServerNonce(token, now);
}
- bool ValidateSourceAddressToken(StringPiece srct,
- IPEndPoint ip,
- QuicWallTime now) {
- return server_config_->ValidateSourceAddressToken(srct, ip, now);
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
}
// CheckConfigs compares the state of the Configs in |server_config_| to the
@@ -144,6 +181,40 @@ class QuicCryptoServerConfigPeer {
const QuicCryptoServerConfig* server_config_;
};
+class TestStrikeRegisterClient : public StrikeRegisterClient {
+ public:
+ explicit TestStrikeRegisterClient(QuicCryptoServerConfig* config)
+ : config_(config),
+ is_known_orbit_called_(false) {
+ }
+
+ virtual bool IsKnownOrbit(StringPiece orbit) const OVERRIDE {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+
+ is_known_orbit_called_ = true;
+ return true;
+ }
+
+ virtual void VerifyNonceIsValidAndUnique(
+ StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) OVERRIDE {
+ LOG(FATAL) << "Not implemented";
+ }
+
+ bool is_known_orbit_called() { return is_known_orbit_called_; }
+
+ private:
+ QuicCryptoServerConfig* config_;
+ mutable bool is_known_orbit_called_;
+};
+
TEST(QuicCryptoServerConfigTest, ServerConfig) {
QuicRandom* rand = QuicRandom::GetInstance();
QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
@@ -154,32 +225,139 @@ TEST(QuicCryptoServerConfigTest, ServerConfig) {
QuicCryptoServerConfig::ConfigOptions()));
}
-TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
+TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) {
QuicRandom* rand = QuicRandom::GetInstance();
QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
- IPAddressNumber ip;
- CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
- IPEndPoint ip4 = IPEndPoint(ip, 1);
- CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip));
- IPEndPoint ip6 = IPEndPoint(ip, 2);
+ MockClock clock;
+
+ TestStrikeRegisterClient* strike_register =
+ new TestStrikeRegisterClient(&server);
+ server.SetStrikeRegisterClient(strike_register);
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock, options));
+ EXPECT_TRUE(strike_register->is_known_orbit_called());
+}
+
+TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
+ const string kPrimary = "<primary>";
+ const string kOverride = "Config with custom source address token key";
+
MockClock clock;
clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
- QuicCryptoServerConfigPeer peer(&server);
QuicWallTime now = clock.WallNow();
const QuicWallTime original_time = now;
- const string token4 = peer.NewSourceAddressToken(ip4, rand, now);
- const string token6 = peer.NewSourceAddressToken(ip6, rand, now);
- EXPECT_TRUE(peer.ValidateSourceAddressToken(token4, ip4, now));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip6, now));
- EXPECT_TRUE(peer.ValidateSourceAddressToken(token6, ip6, now));
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock,
+ QuicCryptoServerConfig::ConfigOptions()));
+ // Add a config that overrides the default boxer.
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = kOverride;
+ scoped_ptr<QuicServerConfigProtobuf> protobuf(
+ QuicCryptoServerConfig::GenerateConfig(rand, &clock, options));
+ protobuf->set_source_address_token_secret_override("a secret key");
+ // Lower priority than the default config.
+ protobuf->set_priority(1);
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddConfig(protobuf.get(), now));
+
+ EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary));
+ EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride));
+
+ IPEndPoint ip4 = IPEndPoint(Loopback4(), 1);
+ IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1);
+ IPEndPoint ip6 = IPEndPoint(Loopback6(), 2);
+
+ // Primary config generates configs that validate successfully.
+ const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now);
+ const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now);
+ const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token6, ip6, now));
+
+ // Override config generates configs that validate successfully.
+ const string override_token4 = peer.NewSourceAddressToken(
+ kOverride, ip4, rand, now);
+ const string override_token6 = peer.NewSourceAddressToken(
+ kOverride, ip6, rand, now);
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, override_token4, ip6,
+ now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token6, ip6, now));
+
+ // Tokens generated by the primary config do not validate
+ // successfully against the override config, and vice versa.
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token6, ip6, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4,
+ now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6,
+ now));
+
+ // Validation fails after tokens expire.
now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
+}
+
+TEST(QuicCryptoServerConfigTest, ValidateServerNonce) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ StringPiece message("hello world");
+ const size_t key_size = CryptoSecretBoxer::GetKeySize();
+ scoped_ptr<uint8[]> key(new uint8[key_size]);
+ memset(key.get(), 0x11, key_size);
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size));
+ const string box = boxer.Box(rand, message);
+ MockClock clock;
+ QuicWallTime now = clock.WallNow();
+ const QuicWallTime original_time = now;
+ EXPECT_EQ(SERVER_NONCE_DECRYPTION_FAILURE,
+ peer.ValidateServerNonce(box, now));
+
+ string server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
+ EXPECT_EQ(SERVER_NONCE_NOT_UNIQUE_FAILURE,
+ peer.ValidateServerNonce(server_nonce, now));
+
+ now = original_time.Add(QuicTime::Delta::FromSeconds(1000 * 7));
+ server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
}
class CryptoServerConfigsTest : public ::testing::Test {
@@ -202,18 +380,23 @@ class CryptoServerConfigsTest : public ::testing::Test {
// SetConfigs(NULL); // calls |config_.SetConfigs| with no protobufs.
//
// // Calls |config_.SetConfigs| with two protobufs: one for a Config with
- // // a |primary_time| of 900, and another with a |primary_time| of 1000.
+ // // a |primary_time| of 900 and priority 1, and another with
+ // // a |primary_time| of 1000 and priority 2.
+
// CheckConfigs(
- // "id1", 900,
- // "id2", 1000,
+ // "id1", 900, 1,
+ // "id2", 1000, 2,
// NULL);
//
// If the server config id starts with "INVALID" then the generated protobuf
// will be invalid.
void SetConfigs(const char* server_config_id1, ...) {
+ const char kOrbit[] = "12345678";
+
va_list ap;
va_start(ap, server_config_id1);
bool has_invalid = false;
+ bool is_empty = true;
vector<QuicServerConfigProtobuf*> protobufs;
bool first = true;
@@ -230,13 +413,17 @@ class CryptoServerConfigsTest : public ::testing::Test {
break;
}
+ is_empty = false;
int primary_time = va_arg(ap, int);
+ int priority = va_arg(ap, int);
QuicCryptoServerConfig::ConfigOptions options;
options.id = server_config_id;
+ options.orbit = kOrbit;
QuicServerConfigProtobuf* protobuf(
- QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options));
+ QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options));
protobuf->set_primary_time(primary_time);
+ protobuf->set_priority(priority);
if (string(server_config_id).find("INVALID") == 0) {
protobuf->clear_key();
has_invalid = true;
@@ -244,7 +431,8 @@ class CryptoServerConfigsTest : public ::testing::Test {
protobufs.push_back(protobuf);
}
- ASSERT_EQ(!has_invalid, config_.SetConfigs(protobufs, clock_.WallNow()));
+ ASSERT_EQ(!has_invalid && !is_empty,
+ config_.SetConfigs(protobufs, clock_.WallNow()));
STLDeleteElements(&protobufs);
}
@@ -261,8 +449,8 @@ TEST_F(CryptoServerConfigsTest, NoConfigs) {
TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
// Make sure that "b" is primary even though "a" comes first.
- SetConfigs("a", 1100,
- "b", 900,
+ SetConfigs("a", 1100, 1,
+ "b", 900, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
@@ -272,8 +460,8 @@ TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
// Make sure that a remains primary after b is added.
- SetConfigs("a", 900,
- "b", 1100,
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"a", true,
@@ -283,12 +471,17 @@ TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
TEST_F(CryptoServerConfigsTest, Delete) {
// Ensure that configs get deleted when removed.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
- SetConfigs("b", 900,
- "c", 1100,
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ NULL);
+ SetConfigs("b", 900, 1,
+ "c", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"b", true,
@@ -296,26 +489,148 @@ TEST_F(CryptoServerConfigsTest, Delete) {
NULL);
}
-TEST_F(CryptoServerConfigsTest, DontDeletePrimary) {
- // Ensure that the primary config isn't deleted when removed.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+TEST_F(CryptoServerConfigsTest, DeletePrimary) {
+ // Ensure that deleting the primary config works.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
- SetConfigs("a", 800,
- "c", 1100,
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ NULL);
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "c", false,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
"b", true,
+ NULL);
+ SetConfigs(NULL);
+ // Config change is rejected, still using old configs.
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
+ // Check that updates to primary time get picked up.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ SetConfigs("a", 1200, 1,
+ "b", 800, 1,
+ "c", 400, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
+ // Check that the most recent config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
+ // Check that the first config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(100);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
"c", false,
NULL);
}
+TEST_F(CryptoServerConfigsTest, SortByPriority) {
+ // Check that priority is used to decide on a primary config when
+ // configs have the same primary time.
+ SetConfigs("a", 900, 1,
+ "b", 900, 2,
+ "c", 900, 3,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+
+ // Change priorities and expect sort order to change.
+ SetConfigs("a", 900, 2,
+ "b", 900, 1,
+ "c", 900, 0,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
// Check that a new primary config is enabled at the right time.
- SetConfigs("a", 900,
- "b", 1100,
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
NULL);
test_peer_.SelectNewPrimaryConfig(1000);
test_peer_.CheckConfigs(
@@ -331,18 +646,18 @@ TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
// Ensure that invalid configs don't change anything.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
"b", true,
"c", false,
NULL);
- SetConfigs("a", 800,
- "c", 1100,
- "INVALID1", 1000,
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ "INVALID1", 1000, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
diff --git a/chromium/net/quic/crypto/quic_decrypter.cc b/chromium/net/quic/crypto/quic_decrypter.cc
index fb19d4c0d70..df1192a85af 100644
--- a/chromium/net/quic/crypto/quic_decrypter.cc
+++ b/chromium/net/quic/crypto/quic_decrypter.cc
@@ -5,6 +5,8 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/null_decrypter.h"
namespace net {
@@ -14,6 +16,8 @@ QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) {
switch (algorithm) {
case kAESG:
return new Aes128Gcm12Decrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Decrypter();
case kNULL:
return new NullDecrypter();
default:
diff --git a/chromium/net/quic/crypto/quic_decrypter.h b/chromium/net/quic/crypto/quic_decrypter.h
index 124d98fea8d..30983493096 100644
--- a/chromium/net/quic/crypto/quic_decrypter.h
+++ b/chromium/net/quic/crypto/quic_decrypter.h
@@ -6,7 +6,6 @@
#define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/quic_encrypter.cc b/chromium/net/quic/crypto/quic_encrypter.cc
index 489da8ed2ec..a817870174b 100644
--- a/chromium/net/quic/crypto/quic_encrypter.cc
+++ b/chromium/net/quic/crypto/quic_encrypter.cc
@@ -5,6 +5,8 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/null_encrypter.h"
namespace net {
@@ -14,6 +16,8 @@ QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) {
switch (algorithm) {
case kAESG:
return new Aes128Gcm12Encrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Encrypter();
case kNULL:
return new NullEncrypter();
default:
diff --git a/chromium/net/quic/crypto/quic_encrypter.h b/chromium/net/quic/crypto/quic_encrypter.h
index edddf3628b6..37bba1212d1 100644
--- a/chromium/net/quic/crypto/quic_encrypter.h
+++ b/chromium/net/quic/crypto/quic_encrypter.h
@@ -6,7 +6,6 @@
#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/quic_random.cc b/chromium/net/quic/crypto/quic_random.cc
index 73fce2ef2cf..6f460130660 100644
--- a/chromium/net/quic/crypto/quic_random.cc
+++ b/chromium/net/quic/crypto/quic_random.cc
@@ -23,7 +23,7 @@ class DefaultRandom : public QuicRandom {
size_t entropy_len) OVERRIDE;
private:
- DefaultRandom();
+ DefaultRandom() {};
virtual ~DefaultRandom() {}
friend struct DefaultSingletonTraits<DefaultRandom>;
@@ -48,9 +48,6 @@ void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
// No such function exists in crypto/random.h.
}
-DefaultRandom::DefaultRandom() {
-}
-
} // namespace
// static
diff --git a/chromium/net/quic/crypto/quic_server_info.cc b/chromium/net/quic/crypto/quic_server_info.cc
new file mode 100644
index 00000000000..519d7b5fd60
--- /dev/null
+++ b/chromium/net/quic/crypto/quic_server_info.cc
@@ -0,0 +1,141 @@
+// Copyright 2014 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/crypto/quic_server_info.h"
+
+#include <limits>
+
+#include "base/pickle.h"
+
+using std::string;
+
+namespace {
+
+const int kQuicCryptoConfigVersion = 1;
+
+} // namespace
+
+namespace net {
+
+QuicServerInfo::State::State() {}
+
+QuicServerInfo::State::~State() {}
+
+void QuicServerInfo::State::Clear() {
+ server_config.clear();
+ source_address_token.clear();
+ server_config_sig.clear();
+ certs.clear();
+}
+
+QuicServerInfo::QuicServerInfo(const QuicServerId& server_id)
+ : server_id_(server_id) {
+}
+
+QuicServerInfo::~QuicServerInfo() {
+}
+
+const QuicServerInfo::State& QuicServerInfo::state() const {
+ return state_;
+}
+
+QuicServerInfo::State* QuicServerInfo::mutable_state() {
+ return &state_;
+}
+
+bool QuicServerInfo::Parse(const string& data) {
+ State* state = mutable_state();
+
+ state->Clear();
+
+ bool r = ParseInner(data);
+ if (!r)
+ state->Clear();
+ return r;
+}
+
+bool QuicServerInfo::ParseInner(const string& data) {
+ State* state = mutable_state();
+
+ // No data was read from the disk cache.
+ if (data.empty()) {
+ return false;
+ }
+
+ Pickle p(data.data(), data.size());
+ PickleIterator iter(p);
+
+ int version = -1;
+ if (!p.ReadInt(&iter, &version)) {
+ DVLOG(1) << "Missing version";
+ return false;
+ }
+
+ if (version != kQuicCryptoConfigVersion) {
+ DVLOG(1) << "Unsupported version";
+ return false;
+ }
+
+ if (!p.ReadString(&iter, &state->server_config)) {
+ DVLOG(1) << "Malformed server_config";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->source_address_token)) {
+ DVLOG(1) << "Malformed source_address_token";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->server_config_sig)) {
+ DVLOG(1) << "Malformed server_config_sig";
+ return false;
+ }
+
+ // Read certs.
+ uint32 num_certs;
+ if (!p.ReadUInt32(&iter, &num_certs)) {
+ DVLOG(1) << "Malformed num_certs";
+ return false;
+ }
+
+ for (uint32 i = 0; i < num_certs; i++) {
+ string cert;
+ if (!p.ReadString(&iter, &cert)) {
+ DVLOG(1) << "Malformed cert";
+ return false;
+ }
+ state->certs.push_back(cert);
+ }
+
+ return true;
+}
+
+string QuicServerInfo::Serialize() {
+ string pickled_data = SerializeInner();
+ state_.Clear();
+ return pickled_data;
+}
+
+string QuicServerInfo::SerializeInner() const {
+ Pickle p(sizeof(Pickle::Header));
+
+ if (!p.WriteInt(kQuicCryptoConfigVersion) ||
+ !p.WriteString(state_.server_config) ||
+ !p.WriteString(state_.source_address_token) ||
+ !p.WriteString(state_.server_config_sig) ||
+ state_.certs.size() > std::numeric_limits<uint32>::max() ||
+ !p.WriteUInt32(state_.certs.size())) {
+ return string();
+ }
+
+ for (size_t i = 0; i < state_.certs.size(); i++) {
+ if (!p.WriteString(state_.certs[i])) {
+ return string();
+ }
+ }
+
+ return string(reinterpret_cast<const char *>(p.data()), p.size());
+}
+
+QuicServerInfoFactory::~QuicServerInfoFactory() {}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/quic_server_info.h b/chromium/net/quic/crypto/quic_server_info.h
new file mode 100644
index 00000000000..fd54b94ad92
--- /dev/null
+++ b/chromium/net/quic/crypto/quic_server_info.h
@@ -0,0 +1,118 @@
+// Copyright 2014 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_CRYPTO_QUIC_SERVER_INFO_H_
+#define NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+class X509Certificate;
+
+// QuicServerInfo is an interface for fetching information about a QUIC server.
+// This information may be stored on disk so does not include keys or other
+// sensitive information. Primarily it's intended for caching the QUIC server's
+// crypto config.
+class NET_EXPORT_PRIVATE QuicServerInfo {
+ public:
+ QuicServerInfo(const QuicServerId& server_id);
+ virtual ~QuicServerInfo();
+
+ // Start will commence the lookup. This must be called before any other
+ // methods. By opportunistically calling this early, it may be possible to
+ // overlap this object's lookup and reduce latency.
+ virtual void Start() = 0;
+
+ // WaitForDataReady returns OK if the fetch of the requested data has
+ // completed. Otherwise it returns ERR_IO_PENDING and will call |callback| on
+ // the current thread when ready.
+ //
+ // Only a single callback can be outstanding at a given time and, in the
+ // event that WaitForDataReady returns OK, it's the caller's responsibility
+ // to delete |callback|.
+ //
+ // |callback| may be NULL, in which case ERR_IO_PENDING may still be returned
+ // but, obviously, a callback will never be made.
+ virtual int WaitForDataReady(const CompletionCallback& callback) = 0;
+
+ // Returns true if data is loaded from disk cache and ready (WaitForDataReady
+ // doesn't have a pending callback).
+ virtual bool IsDataReady() = 0;
+
+ // Returns true if the object is ready to persist data, in other words, if
+ // data is loaded from disk cache and ready and there are no pending writes.
+ virtual bool IsReadyToPersist() = 0;
+
+ // Persist allows for the server information to be updated for future users.
+ // This is a fire and forget operation: the caller may drop its reference
+ // from this object and the store operation will still complete. This can
+ // only be called once WaitForDataReady has returned OK or called its
+ // callback.
+ virtual void Persist() = 0;
+
+ struct State {
+ State();
+ ~State();
+
+ void Clear();
+
+ // This class matches QuicClientCryptoConfig::CachedState.
+ std::string server_config; // A serialized handshake message.
+ std::string source_address_token; // An opaque proof of IP ownership.
+ std::vector<std::string> certs; // A list of certificates in leaf-first
+ // order.
+ std::string server_config_sig; // A signature of |server_config_|.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ // Once the data is ready, it can be read using the following members. These
+ // members can then be updated before calling |Persist|.
+ const State& state() const;
+ State* mutable_state();
+
+ protected:
+ // Parse parses pickled data and fills out the public member fields of this
+ // object. It returns true iff the parse was successful. The public member
+ // fields will be set to something sane in any case.
+ bool Parse(const std::string& data);
+ std::string Serialize();
+ State state_;
+
+ private:
+ // ParseInner is a helper function for Parse.
+ bool ParseInner(const std::string& data);
+
+ // SerializeInner is a helper function for Serialize.
+ std::string SerializeInner() const;
+
+ // This is the QUIC server (hostname, port, is_https, privacy_mode) tuple for
+ // which we restore the crypto_config.
+ const QuicServerId server_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerInfo);
+};
+
+class QuicServerInfoFactory {
+ public:
+ virtual ~QuicServerInfoFactory();
+
+ // GetForServer returns a fresh, allocated QuicServerInfo for the given
+ // |server_id| or NULL on failure.
+ virtual QuicServerInfo* GetForServer(const QuicServerId& server_id) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc
new file mode 100644
index 00000000000..2b5feb37fca
--- /dev/null
+++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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/crypto/scoped_evp_aead_ctx.h"
+
+namespace net {
+
+ScopedEVPAEADCtx::ScopedEVPAEADCtx() {
+ ctx_.aead = NULL;
+}
+
+ScopedEVPAEADCtx::~ScopedEVPAEADCtx() {
+ if (ctx_.aead != NULL) {
+ EVP_AEAD_CTX_cleanup(&ctx_);
+ }
+}
+
+EVP_AEAD_CTX* ScopedEVPAEADCtx::get() {
+ return &ctx_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.h b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h
new file mode 100644
index 00000000000..f0f04c1caaf
--- /dev/null
+++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+#define NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+
+#include <openssl/evp.h>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+// ScopedEVPAEADCtx manages an EVP_AEAD_CTX object and calls the needed cleanup
+// functions when it goes out of scope.
+class ScopedEVPAEADCtx {
+ public:
+ ScopedEVPAEADCtx();
+ ~ScopedEVPAEADCtx();
+
+ EVP_AEAD_CTX* get();
+
+ private:
+ EVP_AEAD_CTX ctx_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEVPAEADCtx);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc
deleted file mode 100644
index b904f870fdd..00000000000
--- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-
-#include <openssl/evp.h>
-
-namespace net {
-
-ScopedEVPCipherCtx::ScopedEVPCipherCtx()
- : ctx_(EVP_CIPHER_CTX_new()) { }
-
-ScopedEVPCipherCtx::~ScopedEVPCipherCtx() {
- EVP_CIPHER_CTX_free(ctx_);
-}
-
-EVP_CIPHER_CTX* ScopedEVPCipherCtx::get() const {
- return ctx_;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h
deleted file mode 100644
index ec0fd51b92a..00000000000
--- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
-#define NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
-
-typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
-
-namespace net {
-
-// TODO(wtc): this is the same as the ScopedCipherCTX class defined in
-// crypto/encryptor_openssl.cc. Eliminate the duplicate code.
-// crypto::ScopedOpenSSL is not suitable for EVP_CIPHER_CTX because
-// there are no EVP_CIPHER_CTX_create and EVP_CIPHER_CTX_destroy
-// functions.
-class ScopedEVPCipherCtx {
- public:
- ScopedEVPCipherCtx();
- ~ScopedEVPCipherCtx();
-
- EVP_CIPHER_CTX* get() const;
-
- private:
- EVP_CIPHER_CTX* const ctx_;
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
diff --git a/chromium/net/quic/crypto/source_address_token.h b/chromium/net/quic/crypto/source_address_token.h
index 8c509651e28..fbb50b1028e 100644
--- a/chromium/net/quic/crypto/source_address_token.h
+++ b/chromium/net/quic/crypto/source_address_token.h
@@ -41,6 +41,8 @@ class SourceAddressToken {
private:
std::string ip_;
int64 timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(SourceAddressToken);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/strike_register.cc b/chromium/net/quic/crypto/strike_register.cc
index f45bfabd9f0..9aec6ffb812 100644
--- a/chromium/net/quic/crypto/strike_register.cc
+++ b/chromium/net/quic/crypto/strike_register.cc
@@ -60,6 +60,13 @@ class StrikeRegister::InternalNode {
// to consider times that are before the creation time.
static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years.
+void StrikeRegister::ValidateStrikeRegisterConfig(unsigned max_entries) {
+ // We only have 23 bits of index available.
+ CHECK_LT(max_entries, 1u << 23);
+ CHECK_GT(max_entries, 1u); // There must be at least two entries.
+ CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes.
+}
+
StrikeRegister::StrikeRegister(unsigned max_entries,
uint32 current_time,
uint32 window_secs,
@@ -78,10 +85,7 @@ StrikeRegister::StrikeRegister(unsigned max_entries,
horizon_valid_(startup == DENY_REQUESTS_AT_STARTUP) {
memcpy(orbit_, orbit, sizeof(orbit_));
- // We only have 23 bits of index available.
- CHECK_LT(max_entries, 1u << 23);
- CHECK_GT(max_entries, 1u); // There must be at least two entries.
- CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes.
+ ValidateStrikeRegisterConfig(max_entries);
internal_nodes_ = new InternalNode[max_entries];
external_nodes_.reset(new uint8[kExternalNodeSize * max_entries]);
diff --git a/chromium/net/quic/crypto/strike_register.h b/chromium/net/quic/crypto/strike_register.h
index fda62a802b2..3f49e92e3d5 100644
--- a/chromium/net/quic/crypto/strike_register.h
+++ b/chromium/net/quic/crypto/strike_register.h
@@ -81,6 +81,9 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// external node. We flag the 24th bit to mark a pointer as external.
static const uint32 kExternalFlag;
+ // Allows early validation before a strike register is created.
+ static void ValidateStrikeRegisterConfig(unsigned max_entries);
+
// Construct a new set which can hold, at most, |max_entries| (which must be
// less than 2**23). See the comments around StartupType about initial
// behaviour. Otherwise, all nonces that are outside +/- |window_secs| from
@@ -178,6 +181,8 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// this header.
InternalNode* internal_nodes_;
scoped_ptr<uint8[]> external_nodes_;
+
+ DISALLOW_COPY_AND_ASSIGN(StrikeRegister);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/strike_register_client.h b/chromium/net/quic/crypto/strike_register_client.h
index 4a27bd7b85c..e37827a899d 100644
--- a/chromium/net/quic/crypto/strike_register_client.h
+++ b/chromium/net/quic/crypto/strike_register_client.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/quic/quic_time.h"
@@ -38,8 +39,8 @@ class NET_EXPORT_PRIVATE StrikeRegisterClient {
StrikeRegisterClient() {}
virtual ~StrikeRegisterClient() {}
- // Returns the strike server orbit if known, else empty string.
- virtual std::string orbit() = 0;
+ // Returns true iff the strike register knows about the given orbit.
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const = 0;
// Validate a nonce for freshness and uniqueness.
// Will invoke cb->Run(ValidateResponse::nonce_is_valid_and_unique())
// once the asynchronous operation is complete.
diff --git a/chromium/net/quic/iovector.h b/chromium/net/quic/iovector.h
index 74fcbef3087..22d2cc9a8cd 100644
--- a/chromium/net/quic/iovector.h
+++ b/chromium/net/quic/iovector.h
@@ -57,7 +57,6 @@ inline size_t TotalIovecLength(const struct iovec* iov, size_t iovcnt) {
// to avoid accidentally change an entry that is assembled by two or more
// Append()'s by simply an index access.
//
-
class NET_EXPORT_PRIVATE IOVector {
public:
// Provide a default constructor so it'll never be inhibited by adding other
diff --git a/chromium/net/quic/quic_ack_notifier.cc b/chromium/net/quic/quic_ack_notifier.cc
index 5eaf38581dc..3bd65e50a35 100644
--- a/chromium/net/quic/quic_ack_notifier.cc
+++ b/chromium/net/quic/quic_ack_notifier.cc
@@ -9,39 +9,54 @@
#include "base/logging.h"
#include "base/stl_util.h"
+using base::hash_map;
+using std::make_pair;
+
namespace net {
+QuicAckNotifier::PacketInfo::PacketInfo() : packet_payload_size(0) {
+}
+
+QuicAckNotifier::PacketInfo::PacketInfo(int payload_size)
+ : packet_payload_size(payload_size) {
+}
+
QuicAckNotifier::DelegateInterface::DelegateInterface() {}
QuicAckNotifier::DelegateInterface::~DelegateInterface() {}
QuicAckNotifier::QuicAckNotifier(DelegateInterface* delegate)
- : delegate_(delegate) {
- DCHECK(delegate_);
+ : delegate_(delegate),
+ original_packet_count_(0),
+ original_byte_count_(0),
+ retransmitted_packet_count_(0),
+ retransmitted_byte_count_(0) {
+ DCHECK(delegate);
}
-QuicAckNotifier::~QuicAckNotifier() {}
-
-void QuicAckNotifier::AddSequenceNumber(
- const QuicPacketSequenceNumber& sequence_number) {
- sequence_numbers_.insert(sequence_number);
+QuicAckNotifier::~QuicAckNotifier() {
}
-void QuicAckNotifier::AddSequenceNumbers(
- const SequenceNumberSet& sequence_numbers) {
- for (SequenceNumberSet::const_iterator it = sequence_numbers.begin();
- it != sequence_numbers.end(); ++it) {
- AddSequenceNumber(*it);
- }
+void QuicAckNotifier::AddSequenceNumber(
+ const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size) {
+ sequence_numbers_.insert(make_pair(sequence_number,
+ PacketInfo(packet_payload_size)));
+ ++original_packet_count_;
+ original_byte_count_ += packet_payload_size;
}
-bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) {
+bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
DCHECK(ContainsKey(sequence_numbers_, sequence_number));
sequence_numbers_.erase(sequence_number);
if (IsEmpty()) {
// We have seen all the sequence numbers we were waiting for, trigger
// callback notification.
- delegate_->OnAckNotification();
+ delegate_->OnAckNotification(
+ original_packet_count_, original_byte_count_,
+ retransmitted_packet_count_, retransmitted_byte_count_,
+ delta_largest_observed);
return true;
}
return false;
@@ -50,8 +65,21 @@ bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) {
void QuicAckNotifier::UpdateSequenceNumber(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
- sequence_numbers_.erase(old_sequence_number);
- sequence_numbers_.insert(new_sequence_number);
+ DCHECK(!ContainsKey(sequence_numbers_, new_sequence_number));
+
+ PacketInfo packet_info;
+ hash_map<QuicPacketSequenceNumber, PacketInfo>::iterator it =
+ sequence_numbers_.find(old_sequence_number);
+ if (it != sequence_numbers_.end()) {
+ packet_info = it->second;
+ sequence_numbers_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Old sequence number not found.";
+ }
+
+ ++retransmitted_packet_count_;
+ retransmitted_byte_count_ += packet_info.packet_payload_size;
+ sequence_numbers_.insert(make_pair(new_sequence_number, packet_info));
}
}; // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier.h b/chromium/net/quic/quic_ack_notifier.h
index 5ec92134e78..f19d0f8c6b9 100644
--- a/chromium/net/quic/quic_ack_notifier.h
+++ b/chromium/net/quic/quic_ack_notifier.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_QUIC_ACK_NOTIFIER_H_
#define NET_QUIC_QUIC_ACK_NOTIFIER_H_
+#include "base/memory/ref_counted.h"
#include "net/quic/quic_protocol.h"
namespace net {
@@ -16,22 +17,35 @@ namespace net {
// trigger a call to a provided Closure.
class NET_EXPORT_PRIVATE QuicAckNotifier {
public:
- class NET_EXPORT_PRIVATE DelegateInterface {
+ class NET_EXPORT_PRIVATE DelegateInterface
+ : public base::RefCounted<DelegateInterface> {
public:
DelegateInterface();
+ // Args:
+ // num_original_packets - Number of packets in the original transmission.
+ // num_original_bytes - Number of packets in the original transmission.
+ // num_retransmitted_packets - Number of packets that had to be
+ // retransmitted.
+ // num_retransmitted_bytes - Number of bytes that had to be retransmitted.
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) = 0;
+ protected:
+ friend class base::RefCounted<DelegateInterface>;
+
+ // Delegates are ref counted.
virtual ~DelegateInterface();
- virtual void OnAckNotification() = 0;
};
+ // QuicAckNotifier is expected to keep its own reference to the delegate.
explicit QuicAckNotifier(DelegateInterface* delegate);
virtual ~QuicAckNotifier();
// Register a sequence number that this AckNotifier should be interested in.
- void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number);
-
- // Register a set of sequence numbers that this AckNotifier should be
- // interested in.
- void AddSequenceNumbers(const SequenceNumberSet& sequence_numbers);
+ void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size);
// Called by the QuicConnection on receipt of new ACK frame, with the sequence
// number referenced by the ACK frame.
@@ -41,7 +55,8 @@ class NET_EXPORT_PRIVATE QuicAckNotifier {
//
// Returns true if the provided sequence_number caused the delegate to be
// called, false otherwise.
- bool OnAck(QuicPacketSequenceNumber sequence_number);
+ bool OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
bool IsEmpty() { return sequence_numbers_.empty(); }
@@ -52,14 +67,33 @@ class NET_EXPORT_PRIVATE QuicAckNotifier {
QuicPacketSequenceNumber new_sequence_number);
private:
+ struct PacketInfo {
+ PacketInfo();
+ explicit PacketInfo(int payload_size);
+
+ int packet_payload_size;
+ };
+
// The delegate's OnAckNotification() method will be called once we have been
// notified of ACKs for all the sequence numbers we are tracking.
// This is not owned by OnAckNotifier and must outlive it.
- DelegateInterface* delegate_;
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Sequence numbers this notifier is waiting to hear about. The
+ // delegate will not be called until this is empty.
+ base::hash_map<QuicPacketSequenceNumber, PacketInfo> sequence_numbers_;
+
+ // Transmission and retransmission stats.
+ // Number of packets in the original transmission.
+ int original_packet_count_;
+ // Number of packets in the original transmission.
+ int original_byte_count_;
+ // Number of packets that had to be retransmitted.
+ int retransmitted_packet_count_;
+ // Number of bytes that had to be retransmitted.
+ int retransmitted_byte_count_;
- // Set of sequence numbers this notifier is waiting to hear about. The
- // delegate will not be called until this is an empty set.
- SequenceNumberSet sequence_numbers_;
+ DISALLOW_COPY_AND_ASSIGN(QuicAckNotifier);
};
}; // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier_manager.cc b/chromium/net/quic/quic_ack_notifier_manager.cc
index 8676315d22f..70cb927a86d 100644
--- a/chromium/net/quic/quic_ack_notifier_manager.cc
+++ b/chromium/net/quic/quic_ack_notifier_manager.cc
@@ -23,7 +23,8 @@ AckNotifierManager::~AckNotifierManager() {
}
void AckNotifierManager::OnPacketAcked(
- QuicPacketSequenceNumber sequence_number) {
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
// Inform all the registered AckNotifiers of the new ACK.
AckNotifierMap::iterator map_it = ack_notifier_map_.find(sequence_number);
if (map_it == ack_notifier_map_.end()) {
@@ -36,7 +37,7 @@ void AckNotifierManager::OnPacketAcked(
for (AckNotifierSet::iterator set_it = map_it->second.begin();
set_it != map_it->second.end(); ++set_it) {
QuicAckNotifier* ack_notifier = *set_it;
- ack_notifier->OnAck(sequence_number);
+ ack_notifier->OnAck(sequence_number, delta_largest_observed);
// If this has resulted in an empty AckNotifer, erase it.
if (ack_notifier->IsEmpty()) {
@@ -90,7 +91,8 @@ void AckNotifierManager::OnSerializedPacket(
// The AckNotifier needs to know it is tracking this packet's sequence
// number.
- notifier->AddSequenceNumber(serialized_packet.sequence_number);
+ notifier->AddSequenceNumber(serialized_packet.sequence_number,
+ serialized_packet.packet->length());
// Update the mapping in the other direction, from sequence
// number to AckNotifier.
diff --git a/chromium/net/quic/quic_ack_notifier_manager.h b/chromium/net/quic/quic_ack_notifier_manager.h
index 5ddf794e7bb..bf5b345d106 100644
--- a/chromium/net/quic/quic_ack_notifier_manager.h
+++ b/chromium/net/quic/quic_ack_notifier_manager.h
@@ -31,7 +31,6 @@ class QuicAckNotifier;
// a set of AckNotifiers and a map from sequence number to AckNotifier the sake
// of efficiency - we can quickly check the map to see if any AckNotifiers are
// interested in a given sequence number.
-
class NET_EXPORT_PRIVATE AckNotifierManager {
public:
AckNotifierManager();
@@ -40,7 +39,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager {
// Called when the connection receives a new AckFrame. If |sequence_number|
// exists in ack_notifier_map_ then the corresponding AckNotifiers will have
// their OnAck method called.
- void OnPacketAcked(QuicPacketSequenceNumber sequence_number);
+ void OnPacketAcked(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
// If a packet has been retransmitted with a new sequence number, then this
// will be called. It updates the mapping in ack_notifier_map_, and also
@@ -71,6 +71,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager {
// number, call OnAck for all mapped AckNotifiers.
// Does not own the AckNotifiers.
AckNotifierMap ack_notifier_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckNotifierManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier_test.cc b/chromium/net/quic/quic_ack_notifier_test.cc
index 8facee340f0..63bc25405b4 100644
--- a/chromium/net/quic/quic_ack_notifier_test.cc
+++ b/chromium/net/quic/quic_ack_notifier_test.cc
@@ -8,40 +8,44 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::_;
+
namespace net {
namespace test {
namespace {
class QuicAckNotifierTest : public ::testing::Test {
protected:
+ QuicAckNotifierTest() : zero_(QuicTime::Delta::Zero()) {}
+
virtual void SetUp() {
- notifier_.reset(new QuicAckNotifier(&delegate_));
+ delegate_ = new MockAckNotifierDelegate;
+ notifier_.reset(new QuicAckNotifier(delegate_));
- sequence_numbers_.insert(26);
- sequence_numbers_.insert(99);
- sequence_numbers_.insert(1234);
- notifier_->AddSequenceNumbers(sequence_numbers_);
+ notifier_->AddSequenceNumber(26, 100);
+ notifier_->AddSequenceNumber(99, 20);
+ notifier_->AddSequenceNumber(1234, 3);
}
- SequenceNumberSet sequence_numbers_;
- MockAckNotifierDelegate delegate_;
+ MockAckNotifierDelegate* delegate_;
scoped_ptr<QuicAckNotifier> notifier_;
+ QuicTime::Delta zero_;
};
// Should trigger callback when we receive acks for all the registered seqnums.
TEST_F(QuicAckNotifierTest, TriggerCallback) {
- EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
- EXPECT_FALSE(notifier_->OnAck(26));
- EXPECT_FALSE(notifier_->OnAck(99));
- EXPECT_TRUE(notifier_->OnAck(1234));
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 0, 0, zero_)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
+ EXPECT_TRUE(notifier_->OnAck(1234, zero_));
}
// Should not trigger callback if we never provide all the seqnums.
TEST_F(QuicAckNotifierTest, DoesNotTrigger) {
// Should not trigger callback as not all packets have been seen.
- EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
- EXPECT_FALSE(notifier_->OnAck(26));
- EXPECT_FALSE(notifier_->OnAck(99));
+ EXPECT_CALL(*delegate_, OnAckNotification(_, _, _, _, _)).Times(0);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
}
// Should trigger even after updating sequence numbers and receiving ACKs for
@@ -51,10 +55,24 @@ TEST_F(QuicAckNotifierTest, UpdateSeqNums) {
notifier_->UpdateSequenceNumber(99, 3000);
notifier_->UpdateSequenceNumber(1234, 3001);
- EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
- EXPECT_FALSE(notifier_->OnAck(26)); // original
- EXPECT_FALSE(notifier_->OnAck(3000)); // updated
- EXPECT_TRUE(notifier_->OnAck(3001)); // updated
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 2, 20 + 3, _)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_)); // original
+ EXPECT_FALSE(notifier_->OnAck(3000, zero_)); // updated
+ EXPECT_TRUE(notifier_->OnAck(3001, zero_)); // updated
+}
+
+// Make sure the delegate is called with the delta time from the last ACK.
+TEST_F(QuicAckNotifierTest, DeltaTime) {
+ const QuicTime::Delta first_delta = QuicTime::Delta::FromSeconds(5);
+ const QuicTime::Delta second_delta = QuicTime::Delta::FromSeconds(33);
+ const QuicTime::Delta third_delta = QuicTime::Delta::FromSeconds(10);
+
+ EXPECT_CALL(*delegate_,
+ OnAckNotification(3, 123, 0, 0, third_delta))
+ .Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, first_delta));
+ EXPECT_FALSE(notifier_->OnAck(99, second_delta));
+ EXPECT_TRUE(notifier_->OnAck(1234, third_delta));
}
} // namespace
diff --git a/chromium/net/quic/quic_address_mismatch.cc b/chromium/net/quic/quic_address_mismatch.cc
new file mode 100644
index 00000000000..96aaef41030
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 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_address_mismatch.h"
+
+#include "base/logging.h"
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+
+int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address) {
+ if (first_address.address().empty() || second_address.address().empty()) {
+ return -1;
+ }
+ IPAddressNumber first_ip_address = first_address.address();
+ if (IsIPv4Mapped(first_ip_address)) {
+ first_ip_address = ConvertIPv4MappedToIPv4(first_ip_address);
+ }
+ IPAddressNumber second_ip_address = second_address.address();
+ if (IsIPv4Mapped(second_ip_address)) {
+ second_ip_address = ConvertIPv4MappedToIPv4(second_ip_address);
+ }
+
+ int sample;
+ if (first_ip_address != second_ip_address) {
+ sample = QUIC_ADDRESS_MISMATCH_BASE;
+ } else if (first_address.port() != second_address.port()) {
+ sample = QUIC_PORT_MISMATCH_BASE;
+ } else {
+ sample = QUIC_ADDRESS_AND_PORT_MATCH_BASE;
+ }
+
+ // Add an offset to |sample|:
+ // V4_V4: add 0
+ // V6_V6: add 1
+ // V4_V6: add 2
+ // V6_V4: add 3
+ bool first_ipv4 = (first_ip_address.size() == kIPv4AddressSize);
+ bool second_ipv4 = (second_ip_address.size() == kIPv4AddressSize);
+ if (first_ipv4 != second_ipv4) {
+ CHECK_EQ(sample, QUIC_ADDRESS_MISMATCH_BASE);
+ sample += 2;
+ }
+ if (!first_ipv4) {
+ sample += 1;
+ }
+ return sample;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_address_mismatch.h b/chromium/net/quic/quic_address_mismatch.h
new file mode 100644
index 00000000000..3179c20a93a
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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_ADDRESS_MISMATCH_H_
+#define NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class IPEndPoint;
+
+enum QuicAddressMismatch {
+ // The addresses don't match.
+ QUIC_ADDRESS_MISMATCH_BASE = 0,
+ QUIC_ADDRESS_MISMATCH_V4_V4 = 0,
+ QUIC_ADDRESS_MISMATCH_V6_V6 = 1,
+ QUIC_ADDRESS_MISMATCH_V4_V6 = 2,
+ QUIC_ADDRESS_MISMATCH_V6_V4 = 3,
+
+ // The addresses match, but the ports don't match.
+ QUIC_PORT_MISMATCH_BASE = 4,
+ QUIC_PORT_MISMATCH_V4_V4 = 4,
+ QUIC_PORT_MISMATCH_V6_V6 = 5,
+
+ QUIC_ADDRESS_AND_PORT_MATCH_BASE = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V4_V4 = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V6_V6 = 7,
+
+ QUIC_ADDRESS_MISMATCH_MAX,
+};
+
+// Returns a value of the QuicAddressMismatch enum type that indicates how
+// |first_address| differs from |second_address|. Returns -1 if either address
+// is empty.
+//
+// Only used by the Net.QuicSession.PublicResetAddressMismatch histogram.
+NET_EXPORT_PRIVATE int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address);
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
diff --git a/chromium/net/quic/quic_address_mismatch_test.cc b/chromium/net/quic/quic_address_mismatch_test.cc
new file mode 100644
index 00000000000..b7c683b5a48
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 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_address_mismatch.h"
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Test all cases of the GetAddressMismatch function.
+TEST(QuicAddressMismatchTest, GetAddressMismatch) {
+ IPAddressNumber ip4_1;
+ IPAddressNumber ip4_2;
+ IPAddressNumber ip6_1;
+ IPAddressNumber ip6_2;
+ IPAddressNumber ip4_mapped_1;
+ IPAddressNumber ip4_mapped_2;
+ ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip4_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("5.6.7.8", &ip4_2));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::1", &ip6_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::2", &ip6_2));
+ ip4_mapped_1 = ConvertIPv4NumberToIPv6Number(ip4_1);
+ ip4_mapped_2 = ConvertIPv4NumberToIPv6Number(ip4_2);
+ ASSERT_NE(ip4_1, ip4_2);
+ ASSERT_NE(ip6_1, ip6_2);
+ ASSERT_NE(ip4_mapped_1, ip4_mapped_2);
+
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint()));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(ip4_1, 443), IPEndPoint()));
+
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_bandwidth.h b/chromium/net/quic/quic_bandwidth.h
index 6412aa5c68d..4ae953d00ec 100644
--- a/chromium/net/quic/quic_bandwidth.h
+++ b/chromium/net/quic/quic_bandwidth.h
@@ -15,7 +15,6 @@ namespace net {
typedef uint64 QuicByteCount;
class NET_EXPORT_PRIVATE QuicBandwidth {
-
public:
// Creates a new QuicBandwidth with an internal value of 0.
static QuicBandwidth Zero();
diff --git a/chromium/net/quic/quic_blocked_writer_interface.h b/chromium/net/quic/quic_blocked_writer_interface.h
index a694193fe31..a6f6faf8175 100644
--- a/chromium/net/quic/quic_blocked_writer_interface.h
+++ b/chromium/net/quic/quic_blocked_writer_interface.h
@@ -18,9 +18,8 @@ class NET_EXPORT_PRIVATE QuicBlockedWriterInterface {
virtual ~QuicBlockedWriterInterface() {}
// Called by the PacketWriter when the underlying socket becomes writable
- // so that the BlockedWriter can go ahead and try writing. This methods
- // should return false if the socket has become blocked while writing.
- virtual bool OnCanWrite() = 0;
+ // so that the BlockedWriter can go ahead and try writing.
+ virtual void OnCanWrite() = 0;
};
} // namespace net
diff --git a/chromium/net/quic/quic_client_session.cc b/chromium/net/quic/quic_client_session.cc
index ab70e7f445c..745692c2e12 100644
--- a/chromium/net/quic/quic_client_session.cc
+++ b/chromium/net/quic/quic_client_session.cc
@@ -13,10 +13,14 @@
#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_crypto_client_stream_factory.h"
#include "net/quic/quic_default_packet_writer.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/quic_stream_factory.h"
+#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "net/udp/datagram_client_socket.h"
@@ -24,6 +28,53 @@ namespace net {
namespace {
+// The length of time to wait for a 0-RTT handshake to complete
+// before allowing the requests to possibly proceed over TCP.
+const int k0RttHandshakeTimeoutMs = 300;
+
+// Histograms for tracking down the crashes from http://crbug.com/354669
+// Note: these values must be kept in sync with the corresponding values in:
+// tools/metrics/histograms/histograms.xml
+enum Location {
+ DESTRUCTOR = 0,
+ ADD_OBSERVER = 1,
+ TRY_CREATE_STREAM = 2,
+ CREATE_OUTGOING_RELIABLE_STREAM = 3,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED = 5,
+ NUM_LOCATIONS = 6,
+};
+
+void RecordUnexpectedOpenStreams(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedObservers(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedNotGoingAway(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location,
+ NUM_LOCATIONS);
+}
+
+// Histogram for recording the different reasons that a QUIC session is unable
+// to complete the handshake.
+enum HandshakeFailureReason {
+ HANDSHAKE_FAILURE_UNKNOWN = 0,
+ HANDSHAKE_FAILURE_BLACK_HOLE = 1,
+ HANDSHAKE_FAILURE_PUBLIC_RESET = 2,
+ NUM_HANDSHAKE_FAILURE_REASONS = 3,
+};
+
+void RecordHandshakeFailureReason(HandshakeFailureReason reason) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason",
+ reason, NUM_HANDSHAKE_FAILURE_REASONS);
+}
+
// Note: these values must be kept in sync with the corresponding values in:
// tools/metrics/histograms/histograms.xml
enum HandshakeState {
@@ -86,49 +137,74 @@ QuicClientSession::QuicClientSession(
scoped_ptr<QuicDefaultPacketWriter> writer,
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
- const string& server_hostname,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicServerId& server_id,
const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
+ base::TaskRunner* task_runner,
NetLog* net_log)
- : QuicSession(connection, config),
+ : QuicClientSessionBase(connection,
+ config),
require_confirmation_(false),
stream_factory_(stream_factory),
socket_(socket.Pass()),
writer_(writer.Pass()),
read_buffer_(new IOBufferWithSize(kMaxPacketSize)),
+ server_info_(server_info.Pass()),
read_pending_(false),
num_total_streams_(0),
+ task_runner_(task_runner),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
logger_(net_log_),
num_packets_read_(0),
+ going_away_(false),
weak_factory_(this) {
crypto_stream_.reset(
crypto_client_stream_factory ?
crypto_client_stream_factory->CreateQuicCryptoClientStream(
- server_hostname, this, crypto_config) :
- new QuicCryptoClientStream(server_hostname, this, crypto_config));
+ server_id, this, crypto_config) :
+ new QuicCryptoClientStream(server_id, this,
+ new ProofVerifyContextChromium(net_log_),
+ crypto_config));
connection->set_debug_visitor(&logger_);
// TODO(rch): pass in full host port proxy pair
net_log_.BeginEvent(
NetLog::TYPE_QUIC_SESSION,
- NetLog::StringCallback("host", &server_hostname));
+ NetLog::StringCallback("host", &server_id.host()));
}
QuicClientSession::~QuicClientSession() {
- // The session must be closed before it is destroyed.
- DCHECK(streams()->empty());
- CloseAllStreams(ERR_UNEXPECTED);
- DCHECK(observers_.empty());
- CloseAllObservers(ERR_UNEXPECTED);
-
- connection()->set_debug_visitor(NULL);
- net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(DESTRUCTOR);
+ if (!observers_.empty())
+ RecordUnexpectedObservers(DESTRUCTOR);
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(DESTRUCTOR);
+
+ while (!streams()->empty() ||
+ !observers_.empty() ||
+ !stream_requests_.empty()) {
+ // The session must be closed before it is destroyed.
+ DCHECK(streams()->empty());
+ CloseAllStreams(ERR_UNEXPECTED);
+ DCHECK(observers_.empty());
+ CloseAllObservers(ERR_UNEXPECTED);
+
+ connection()->set_debug_visitor(NULL);
+ net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
+
+ while (!stream_requests_.empty()) {
+ StreamRequest* request = stream_requests_.front();
+ stream_requests_.pop_front();
+ request->OnRequestCompleteFailure(ERR_ABORTED);
+ }
+ }
- while (!stream_requests_.empty()) {
- StreamRequest* request = stream_requests_.front();
- stream_requests_.pop_front();
- request->OnRequestCompleteFailure(ERR_ABORTED);
+ if (connection()->connected()) {
+ // Ensure that the connection is closed by the time the session is
+ // destroyed.
+ connection()->CloseConnection(QUIC_INTERNAL_ERROR, false);
}
if (IsEncryptionEstablished())
@@ -138,17 +214,59 @@ QuicClientSession::~QuicClientSession() {
else
RecordHandshakeState(STATE_FAILED);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
crypto_stream_->num_sent_client_hellos());
- if (IsCryptoHandshakeConfirmed()) {
- UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed",
- crypto_stream_->num_sent_client_hellos());
- }
+ if (!IsCryptoHandshakeConfirmed())
+ return;
- UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
+ // Sending one client_hello means we had zero handshake-round-trips.
+ int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1;
+
+ // Don't bother with these histogram during tests, which mock out
+ // num_sent_client_hellos().
+ if (round_trip_handshakes < 0 || !stream_factory_)
+ return;
+
+ bool port_selected = stream_factory_->enable_port_selection();
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ } else {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ }
+ const QuicConnectionStats stats = connection()->GetStats();
+ if (stats.max_sequence_reordering == 0)
+ return;
+ const uint64 kMaxReordering = 100;
+ uint64 reordering = kMaxReordering;
+ if (stats.min_rtt_us > 0 ) {
+ reordering =
+ GG_UINT64_C(100) * stats.max_time_reordering_us / stats.min_rtt_us;
+ }
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime",
+ reordering, 0, kMaxReordering, 50);
+ if (stats.min_rtt_us > 100 * 1000) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt",
+ reordering, 0, kMaxReordering, 50);
+ }
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering",
+ stats.max_sequence_reordering);
}
-bool QuicClientSession::OnStreamFrames(
+void QuicClientSession::OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) {
// Record total number of stream frames.
UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size());
@@ -169,6 +287,12 @@ bool QuicClientSession::OnStreamFrames(
}
void QuicClientSession::AddObserver(Observer* observer) {
+ if (going_away_) {
+ RecordUnexpectedObservers(ADD_OBSERVER);
+ observer->OnSessionClosed(ERR_UNEXPECTED);
+ return;
+ }
+
DCHECK(!ContainsKey(observers_, observer));
observers_.insert(observer);
}
@@ -195,6 +319,11 @@ int QuicClientSession::TryCreateStream(StreamRequest* request,
return ERR_CONNECTION_CLOSED;
}
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(TRY_CREATE_STREAM);
+ return ERR_CONNECTION_CLOSED;
+ }
+
if (GetNumOpenStreams() < get_max_open_streams()) {
*stream = CreateOutgoingReliableStreamImpl();
return OK;
@@ -221,15 +350,18 @@ QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() {
}
if (GetNumOpenStreams() >= get_max_open_streams()) {
DVLOG(1) << "Failed to create a new outgoing stream. "
- << "Already " << GetNumOpenStreams() << " open.";
+ << "Already " << GetNumOpenStreams() << " open.";
return NULL;
}
if (goaway_received()) {
DVLOG(1) << "Failed to create a new outgoing stream. "
- << "Already received goaway.";
+ << "Already received goaway.";
+ return NULL;
+ }
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM);
return NULL;
}
-
return CreateOutgoingReliableStreamImpl();
}
@@ -248,14 +380,60 @@ QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
return crypto_stream_.get();
};
-bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) {
- DCHECK(crypto_stream_.get());
- return crypto_stream_->GetSSLInfo(ssl_info);
+// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
+// we learn about SSL info (sync vs async vs cached).
+bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const {
+ ssl_info->Reset();
+ if (!cert_verify_result_) {
+ return false;
+ }
+
+ ssl_info->cert_status = cert_verify_result_->cert_status;
+ ssl_info->cert = cert_verify_result_->verified_cert;
+
+ // TODO(wtc): Define QUIC "cipher suites".
+ // Report the TLS cipher suite that most closely resembles the crypto
+ // parameters of the QUIC connection.
+ QuicTag aead = crypto_stream_->crypto_negotiated_params().aead;
+ int cipher_suite;
+ int security_bits;
+ switch (aead) {
+ case kAESG:
+ cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ security_bits = 128;
+ break;
+ case kCC12:
+ cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ security_bits = 256;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ int ssl_connection_status = 0;
+ ssl_connection_status |=
+ (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
+ SSL_CONNECTION_CIPHERSUITE_SHIFT;
+ ssl_connection_status |=
+ (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) <<
+ SSL_CONNECTION_VERSION_SHIFT;
+
+ ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
+ ssl_info->is_issued_by_known_root =
+ cert_verify_result_->is_issued_by_known_root;
+
+ ssl_info->connection_status = ssl_connection_status;
+ ssl_info->client_cert_sent = false;
+ ssl_info->channel_id_sent = false;
+ ssl_info->security_bits = security_bits;
+ ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
+ return true;
}
int QuicClientSession::CryptoConnect(bool require_confirmation,
const CompletionCallback& callback) {
require_confirmation_ = require_confirmation;
+ handshake_start_ = base::TimeTicks::Now();
RecordHandshakeState(STATE_STARTED);
if (!crypto_stream_->CryptoConnect()) {
// TODO(wtc): change crypto_stream_.CryptoConnect() to return a
@@ -263,20 +441,64 @@ int QuicClientSession::CryptoConnect(bool require_confirmation,
return ERR_CONNECTION_FAILED;
}
- bool can_notify = require_confirmation_ ?
- IsCryptoHandshakeConfirmed() : IsEncryptionEstablished();
- if (can_notify) {
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ // Unless we require handshake confirmation, activate the session if
+ // we have established initial encryption.
+ if (!require_confirmation_ && IsEncryptionEstablished()) {
+ // To mitigate the effects of hanging 0-RTT connections, set up a timer to
+ // cancel any requests, if the handshake takes too long.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuicClientSession::OnConnectTimeout,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs));
return OK;
+
}
callback_ = callback;
return ERR_IO_PENDING;
}
+int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) {
+
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ if (!connection()->connected())
+ return ERR_QUIC_HANDSHAKE_FAILED;
+
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
int QuicClientSession::GetNumSentClientHellos() const {
return crypto_stream_->num_sent_client_hellos();
}
+bool QuicClientSession::CanPool(const std::string& hostname) const {
+ // TODO(rch): When QUIC supports channel ID or client certificates, this
+ // logic will need to be revised.
+ DCHECK(connection()->connected());
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ // We can always pool with insecure QUIC sessions.
+ return true;
+ }
+
+ // Disable pooling for secure sessions.
+ // TODO(rch): re-enable this.
+ return false;
+
+#if 0
+ bool unused = false;
+ // Only pool secure QUIC sessions if the cert matches the new hostname.
+ return ssl_info.cert->VerifyNameMatch(hostname, &unused);
+#endif
+}
+
QuicDataStream* QuicClientSession::CreateIncomingDataStream(
QuicStreamId id) {
DLOG(ERROR) << "Server push not supported";
@@ -284,13 +506,20 @@ QuicDataStream* QuicClientSession::CreateIncomingDataStream(
}
void QuicClientSession::CloseStream(QuicStreamId stream_id) {
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (stream) {
+ logger_.UpdateReceivedFrameCounts(
+ stream_id, stream->num_frames_received(),
+ stream->num_duplicate_frames_received());
+ }
QuicSession::CloseStream(stream_id);
OnClosedStream();
}
void QuicClientSession::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- QuicSession::SendRstStream(id, error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QuicSession::SendRstStream(id, error, bytes_written);
OnClosedStream();
}
@@ -299,6 +528,7 @@ void QuicClientSession::OnClosedStream() {
!stream_requests_.empty() &&
crypto_stream_->encryption_established() &&
!goaway_received() &&
+ !going_away_ &&
connection()->connected()) {
StreamRequest* request = stream_requests_.front();
stream_requests_.pop_front();
@@ -320,6 +550,8 @@ void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
base::ResetAndReturn(&callback_).Run(OK);
}
if (event == HANDSHAKE_CONFIRMED) {
+ UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime",
+ base::TimeTicks::Now() - handshake_start_);
ObserverSet::iterator it = observers_.begin();
while (it != observers_.end()) {
Observer* observer = *it;
@@ -357,14 +589,31 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error,
"Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
GetNumOpenStreams());
if (!IsCryptoHandshakeConfirmed()) {
- // If there have been any streams created, they were 0-RTT speculative
- // requests that have not be serviced.
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut",
+ GetNumOpenStreams());
UMA_HISTOGRAM_COUNTS(
"Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
num_total_streams_);
}
}
+ if (!IsCryptoHandshakeConfirmed()) {
+ if (error == QUIC_PUBLIC_RESET) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET);
+ } else if (connection()->GetStats().packets_received == 0) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError",
+ error);
+ } else {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError",
+ error);
+ }
+ }
+
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
connection()->version());
NotifyFactoryOfSessionGoingAway();
@@ -385,6 +634,34 @@ void QuicClientSession::OnSuccessfulVersionNegotiation(
QuicSession::OnSuccessfulVersionNegotiation(version);
}
+void QuicClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) {
+ DCHECK(cached.proof_valid());
+
+ if (!server_info_ || !server_info_->IsReadyToPersist()) {
+ return;
+ }
+
+ QuicServerInfo::State* state = server_info_->mutable_state();
+
+ state->server_config = cached.server_config();
+ state->source_address_token = cached.source_address_token();
+ state->server_config_sig = cached.signature();
+ state->certs = cached.certs();
+
+ server_info_->Persist();
+}
+
+void QuicClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) {
+ const CertVerifyResult* cert_verify_result_other =
+ &(reinterpret_cast<const ProofVerifyDetailsChromium*>(
+ &verify_details))->cert_verify_result;
+ CertVerifyResult* result_copy = new CertVerifyResult;
+ result_copy->CopyFrom(*cert_verify_result_other);
+ cert_verify_result_.reset(result_copy);
+}
+
void QuicClientSession::StartReading() {
if (read_pending_) {
return;
@@ -402,8 +679,8 @@ void QuicClientSession::StartReading() {
if (++num_packets_read_ > 32) {
num_packets_read_ = 0;
// Data was read, process it.
- // Schedule the work through the message loop to avoid recursive
- // callbacks.
+ // Schedule the work through the message loop to 1) prevent infinite
+ // recursion and 2) avoid blocking the thread for too long.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&QuicClientSession::OnReadComplete,
@@ -430,7 +707,8 @@ void QuicClientSession::CloseSessionOnErrorInner(int net_error,
NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
NetLog::IntegerCallback("net_error", net_error));
- connection()->CloseConnection(quic_error, false);
+ if (connection()->connected())
+ connection()->CloseConnection(quic_error, false);
DCHECK(!connection()->connected());
}
@@ -451,15 +729,41 @@ void QuicClientSession::CloseAllObservers(int net_error) {
}
}
-base::Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const {
+base::Value* QuicClientSession::GetInfoAsValue(
+ const std::set<HostPortPair>& aliases) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("host_port_pair", pair.ToString());
+ // TODO(rch): remove "host_port_pair" when Chrome 34 is stable.
+ dict->SetString("host_port_pair", aliases.begin()->ToString());
dict->SetString("version", QuicVersionToString(connection()->version()));
dict->SetInteger("open_streams", GetNumOpenStreams());
+ base::ListValue* stream_list = new base::ListValue();
+ for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it
+ = streams()->begin();
+ it != streams()->end();
+ ++it) {
+ stream_list->Append(new base::StringValue(
+ base::Uint64ToString(it->second->id())));
+ }
+ dict->Set("active_streams", stream_list);
+
dict->SetInteger("total_streams", num_total_streams_);
dict->SetString("peer_address", peer_address().ToString());
- dict->SetString("guid", base::Uint64ToString(guid()));
+ dict->SetString("connection_id", base::Uint64ToString(connection_id()));
dict->SetBoolean("connected", connection()->connected());
+ const QuicConnectionStats& stats = connection()->GetStats();
+ dict->SetInteger("packets_sent", stats.packets_sent);
+ dict->SetInteger("packets_received", stats.packets_received);
+ dict->SetInteger("packets_lost", stats.packets_lost);
+ SSLInfo ssl_info;
+ dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert);
+
+ base::ListValue* alias_list = new base::ListValue();
+ for (std::set<HostPortPair>::const_iterator it = aliases.begin();
+ it != aliases.end(); it++) {
+ alias_list->Append(new base::StringValue(it->ToString()));
+ }
+ dict->Set("aliases", alias_list);
+
return dict;
}
@@ -481,9 +785,7 @@ void QuicClientSession::OnReadComplete(int result) {
return;
}
- scoped_refptr<IOBufferWithSize> buffer(read_buffer_);
- read_buffer_ = new IOBufferWithSize(kMaxPacketSize);
- QuicEncryptedPacket packet(buffer->data(), result);
+ QuicEncryptedPacket packet(read_buffer_->data(), result);
IPEndPoint local_address;
IPEndPoint peer_address;
socket_->GetLocalAddress(&local_address);
@@ -492,18 +794,26 @@ void QuicClientSession::OnReadComplete(int result) {
// use a weak pointer to be safe.
connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!connection()->connected()) {
- stream_factory_->OnSessionClosed(this);
+ NotifyFactoryOfSessionClosedLater();
return;
}
StartReading();
}
void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
+ going_away_ = true;
if (stream_factory_)
stream_factory_->OnSessionGoingAway(this);
}
void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ going_away_ = true;
DCHECK_EQ(0u, GetNumOpenStreams());
DCHECK(!connection()->connected());
base::MessageLoop::current()->PostTask(
@@ -513,10 +823,31 @@ void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
}
void QuicClientSession::NotifyFactoryOfSessionClosed() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ going_away_ = true;
DCHECK_EQ(0u, GetNumOpenStreams());
// Will delete |this|.
if (stream_factory_)
stream_factory_->OnSessionClosed(this);
}
+void QuicClientSession::OnConnectTimeout() {
+ DCHECK(callback_.is_null());
+ DCHECK(IsEncryptionEstablished());
+
+ if (IsCryptoHandshakeConfirmed())
+ return;
+
+ // TODO(rch): re-enable this code once beta is cut.
+ // if (stream_factory_)
+ // stream_factory_->OnSessionConnectTimeout(this);
+ // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED);
+ // DCHECK_EQ(0u, GetNumOpenStreams());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_client_session.h b/chromium/net/quic/quic_client_session.h
index 07da96aa96f..7fbe3db5cd1 100644
--- a/chromium/net/quic/quic_client_session.h
+++ b/chromium/net/quic/quic_client_session.h
@@ -12,20 +12,27 @@
#include <string>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
#include "net/base/completion_callback.h"
+#include "net/proxy/proxy_server.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_connection_logger.h"
#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/quic_reliable_client_stream.h"
-#include "net/quic/quic_session.h"
namespace net {
+class CertVerifyResult;
class DatagramClientSocket;
class QuicConnectionHelper;
class QuicCryptoClientStreamFactory;
class QuicDefaultPacketWriter;
+class QuicServerId;
+class QuicServerInfo;
class QuicStreamFactory;
class SSLInfo;
@@ -33,7 +40,7 @@ namespace test {
class QuicClientSessionPeer;
} // namespace test
-class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
+class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase {
public:
// An interface for observing events on a session.
class NET_EXPORT_PRIVATE Observer {
@@ -88,11 +95,12 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
scoped_ptr<QuicDefaultPacketWriter> writer,
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
- const std::string& server_hostname,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicServerId& server_id,
const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
+ base::TaskRunner* task_runner,
NetLog* net_log);
-
virtual ~QuicClientSession();
void AddObserver(Observer* observer);
@@ -112,19 +120,26 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
void CancelRequest(StreamRequest* request);
// QuicSession methods:
- virtual bool OnStreamFrames(
+ virtual void OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual QuicReliableClientStream* CreateOutgoingDataStream() OVERRIDE;
virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
virtual void CloseStream(QuicStreamId stream_id) OVERRIDE;
virtual void SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) OVERRIDE;
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) OVERRIDE;
virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE;
virtual void OnCryptoHandshakeMessageSent(
const CryptoHandshakeMessage& message) OVERRIDE;
virtual void OnCryptoHandshakeMessageReceived(
const CryptoHandshakeMessage& message) OVERRIDE;
- virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const OVERRIDE;
+
+ // QuicClientSessionBase methods:
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) OVERRIDE;
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) OVERRIDE;
// QuicConnectionVisitorInterface methods:
virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
@@ -135,6 +150,9 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
int CryptoConnect(bool require_confirmation,
const CompletionCallback& callback);
+ // Resumes a crypto handshake with the server after a timeout.
+ int ResumeCryptoConnect(const CompletionCallback& callback);
+
// Causes the QuicConnectionHelper to start reading from the socket
// and passing the data along to the QuicConnection.
void StartReading();
@@ -143,7 +161,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// that this session has been closed, which will delete the session.
void CloseSessionOnError(int error);
- base::Value* GetInfoAsValue(const HostPortPair& pair) const;
+ base::Value* GetInfoAsValue(const std::set<HostPortPair>& aliases);
const BoundNetLog& net_log() const { return net_log_; }
@@ -154,6 +172,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// than the number of round-trips needed for the handshake.
int GetNumSentClientHellos() const;
+ // Returns true if |hostname| may be pooled onto this session. If this
+ // is a secure QUIC session, then |hostname| must match the certificate
+ // presented during the handshake.
+ bool CanPool(const std::string& hostname) const;
+
protected:
// QuicSession methods:
virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
@@ -194,21 +217,30 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// delete |this|.
void NotifyFactoryOfSessionClosed();
+ void OnConnectTimeout();
+
bool require_confirmation_;
scoped_ptr<QuicCryptoClientStream> crypto_stream_;
QuicStreamFactory* stream_factory_;
scoped_ptr<DatagramClientSocket> socket_;
scoped_ptr<QuicDefaultPacketWriter> writer_;
scoped_refptr<IOBufferWithSize> read_buffer_;
+ scoped_ptr<QuicServerInfo> server_info_;
+ scoped_ptr<CertVerifyResult> cert_verify_result_;
ObserverSet observers_;
StreamRequestQueue stream_requests_;
bool read_pending_;
CompletionCallback callback_;
size_t num_total_streams_;
+ base::TaskRunner* task_runner_;
BoundNetLog net_log_;
+ base::TimeTicks handshake_start_; // Time the handshake was started.
QuicConnectionLogger logger_;
// Number of packets read in the current read loop.
size_t num_packets_read_;
+ // True when the session is going away, and streams may no longer be created
+ // on this session. Existing stream will continue to be processed.
+ bool going_away_;
base::WeakPtrFactory<QuicClientSession> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(QuicClientSession);
diff --git a/chromium/net/quic/quic_client_session_base.cc b/chromium/net/quic/quic_client_session_base.cc
new file mode 100644
index 00000000000..4343011437e
--- /dev/null
+++ b/chromium/net/quic/quic_client_session_base.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 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_client_session_base.h"
+
+namespace net {
+
+QuicClientSessionBase::QuicClientSessionBase(
+ QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicSession(connection, config) {}
+
+QuicClientSessionBase::~QuicClientSessionBase() {}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_client_session_base.h b/chromium/net/quic/quic_client_session_base.h
new file mode 100644
index 00000000000..eab5d08df98
--- /dev/null
+++ b/chromium/net/quic/quic_client_session_base.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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_CLIENT_SESSION_BASE_H_
+#define NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
+
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+// Base class for all client-specific QuicSession subclasses.
+class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession {
+ public:
+ QuicClientSessionBase(QuicConnection* connection,
+ const QuicConfig& config);
+
+ virtual ~QuicClientSessionBase();
+
+ // Called when the proof in |cached| is marked valid. If this is a secure
+ // QUIC session, then this will happen only after the proof verifier
+ // completes. If this is an insecure QUIC connection, this will happen
+ // as soon as a valid config is discovered (either from the cache or
+ // from the server).
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) = 0;
+
+ // Called when proof verification details become available, either because
+ // proof verification is complete, or when cached details are used. This
+ // will only be called for secure QUIC connections.
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
diff --git a/chromium/net/quic/quic_client_session_test.cc b/chromium/net/quic/quic_client_session_test.cc
index f0b4dd4ecab..264fbfa5270 100644
--- a/chromium/net/quic/quic_client_session_test.cc
+++ b/chromium/net/quic/quic_client_session_test.cc
@@ -13,10 +13,12 @@
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_client_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
#include "net/socket/socket_test_util.h"
#include "net/udp/datagram_client_socket.h"
@@ -27,24 +29,21 @@ namespace test {
namespace {
const char kServerHostname[] = "www.example.com";
+const uint16 kServerPort = 80;
class TestPacketWriter : public QuicDefaultPacketWriter {
public:
- TestPacketWriter() {
- }
+ TestPacketWriter(QuicVersion version) : version_(version) {}
// QuicPacketWriter
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), true);
- FramerVisitorCapturingFrames visitor;
- framer.set_visitor(&visitor);
+ const IPEndPoint& peer_address) OVERRIDE {
+ SimpleQuicFramer framer(SupportedVersions(version_));
QuicEncryptedPacket packet(buffer, buf_len);
EXPECT_TRUE(framer.ProcessPacket(packet));
- header_ = *visitor.header();
+ header_ = framer.header();
return WriteResult(WRITE_STATUS_OK, packet.length());
}
@@ -58,16 +57,22 @@ class TestPacketWriter : public QuicDefaultPacketWriter {
const QuicPacketHeader& header() { return header_; }
private:
+ QuicVersion version_;
QuicPacketHeader header_;
};
-class QuicClientSessionTest : public ::testing::Test {
+class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicClientSessionTest()
- : writer_(new TestPacketWriter()),
- connection_(new PacketSavingConnection(false)),
+ : writer_(new TestPacketWriter(GetParam())),
+ connection_(
+ new PacketSavingConnection(false, SupportedVersions(GetParam()))),
session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL,
- kServerHostname, DefaultQuicConfig(), &crypto_config_,
+ make_scoped_ptr((QuicServerInfo*)NULL),
+ QuicServerId(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(), &crypto_config_,
+ base::MessageLoop::current()->message_loop_proxy().get(),
&net_log_) {
session_.config()->SetDefaults();
crypto_config_.SetDefaults();
@@ -105,11 +110,14 @@ class QuicClientSessionTest : public ::testing::Test {
QuicCryptoClientConfig crypto_config_;
};
-TEST_F(QuicClientSessionTest, CryptoConnect) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicClientSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicClientSessionTest, CryptoConnect) {
CompleteCryptoHandshake();
}
-TEST_F(QuicClientSessionTest, MaxNumStreams) {
+TEST_P(QuicClientSessionTest, MaxNumStreams) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
@@ -125,7 +133,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreams) {
EXPECT_TRUE(session_.CreateOutgoingDataStream());
}
-TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) {
+TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
@@ -149,7 +157,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) {
EXPECT_TRUE(stream != NULL);
}
-TEST_F(QuicClientSessionTest, GoAwayReceived) {
+TEST_P(QuicClientSessionTest, GoAwayReceived) {
CompleteCryptoHandshake();
// After receiving a GoAway, I should no longer be able to create outgoing
diff --git a/chromium/net/quic/quic_clock.cc b/chromium/net/quic/quic_clock.cc
index 865562369fa..9d5ac6d17ab 100644
--- a/chromium/net/quic/quic_clock.cc
+++ b/chromium/net/quic/quic_clock.cc
@@ -14,7 +14,8 @@ QuicClock::QuicClock() {
QuicClock::~QuicClock() {}
QuicTime QuicClock::ApproximateNow() const {
- // Chrome does not have a distinct notion of ApproximateNow().
+ // At the moment, Chrome does not have a distinct notion of ApproximateNow().
+ // We should consider implementing this using MessageLoop::recent_time_.
return Now();
}
diff --git a/chromium/net/quic/quic_clock.h b/chromium/net/quic/quic_clock.h
index e6135a930d0..4e96d4c781e 100644
--- a/chromium/net/quic/quic_clock.h
+++ b/chromium/net/quic/quic_clock.h
@@ -30,6 +30,9 @@ class NET_EXPORT_PRIVATE QuicClock {
// WallNow returns the current wall-time - a time that is consistent across
// different clocks.
virtual QuicWallTime WallNow() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClock);
};
} // namespace net
diff --git a/chromium/net/quic/quic_config.cc b/chromium/net/quic/quic_config.cc
index 152da46a80f..f69aa379494 100644
--- a/chromium/net/quic/quic_config.cc
+++ b/chromium/net/quic/quic_config.cc
@@ -7,23 +7,68 @@
#include <algorithm>
#include "base/logging.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_utils.h"
+using std::min;
using std::string;
namespace net {
-QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence)
+// Reads the value corresponding to |name_| from |msg| into |out|. If the
+// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set
+// to |default_value|.
+QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
+ QuicTag tag,
+ QuicConfigPresence presence,
+ uint32 default_value,
+ uint32* out,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ QuicErrorCode error = msg.GetUint32(tag, out);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out = default_value;
+ break;
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag);
+ break;
+ }
+ return error;
+}
+
+
+QuicConfigValue::QuicConfigValue(QuicTag tag,
+ QuicConfigPresence presence)
: tag_(tag),
- presence_(presence),
+ presence_(presence) {
+}
+QuicConfigValue::~QuicConfigValue() {}
+
+QuicNegotiableValue::QuicNegotiableValue(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
negotiated_(false) {
}
+QuicNegotiableValue::~QuicNegotiableValue() {}
-QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence)
+QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag,
+ QuicConfigPresence presence)
: QuicNegotiableValue(tag, presence),
max_value_(0),
default_value_(0) {
}
+QuicNegotiableUint32::~QuicNegotiableUint32() {}
void QuicNegotiableUint32::set(uint32 max, uint32 default_value) {
DCHECK_LE(default_value, max);
@@ -47,70 +92,34 @@ void QuicNegotiableUint32::ToHandshakeMessage(
}
}
-QuicErrorCode QuicNegotiableUint32::ReadUint32(
- const CryptoHandshakeMessage& msg,
- uint32* out,
- string* error_details) const {
- DCHECK(error_details != NULL);
- QuicErrorCode error = msg.GetUint32(tag_, out);
- switch (error) {
- case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
- if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) {
- *error_details = "Missing " + QuicUtils::TagToString(tag_);
- break;
- }
- error = QUIC_NO_ERROR;
- *out = default_value_;
-
- case QUIC_NO_ERROR:
- break;
- default:
- *error_details = "Bad " + QuicUtils::TagToString(tag_);
- break;
- }
- return error;
-}
-
-QuicErrorCode QuicNegotiableUint32::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
- string* error_details) {
- DCHECK(!negotiated_);
- DCHECK(error_details != NULL);
- uint32 value;
- QuicErrorCode error = ReadUint32(client_hello, &value, error_details);
- if (error != QUIC_NO_ERROR) {
- return error;
- }
-
- negotiated_ = true;
- negotiated_value_ = std::min(value, max_value_);
-
- return QUIC_NO_ERROR;
-}
-
-QuicErrorCode QuicNegotiableUint32::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
+QuicErrorCode QuicNegotiableUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(!negotiated_);
DCHECK(error_details != NULL);
uint32 value;
- QuicErrorCode error = ReadUint32(server_hello, &value, error_details);
+ QuicErrorCode error = ReadUint32(peer_hello,
+ tag_,
+ presence_,
+ default_value_,
+ &value,
+ error_details);
if (error != QUIC_NO_ERROR) {
return error;
}
-
- if (value > max_value_) {
- *error_details = "Invalid value received for " +
- QuicUtils::TagToString(tag_);
+ if (hello_type == SERVER && value > max_value_) {
+ *error_details =
+ "Invalid value received for " + QuicUtils::TagToString(tag_);
return QUIC_INVALID_NEGOTIATED_VALUE;
}
negotiated_ = true;
- negotiated_value_ = value;
+ negotiated_value_ = min(value, max_value_);
return QUIC_NO_ERROR;
}
-QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence)
+QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, QuicConfigPresence presence)
: QuicNegotiableValue(tag, presence),
negotiated_tag_(0),
default_value_(0) {
@@ -120,8 +129,7 @@ QuicNegotiableTag::~QuicNegotiableTag() {}
void QuicNegotiableTag::set(const QuicTagVector& possible,
QuicTag default_value) {
- DCHECK(std::find(possible.begin(), possible.end(), default_value) !=
- possible.end());
+ DCHECK(ContainsQuicTag(possible, default_value));
possible_values_ = possible;
default_value_ = default_value;
}
@@ -169,85 +177,309 @@ QuicErrorCode QuicNegotiableTag::ReadVector(
return error;
}
-QuicErrorCode QuicNegotiableTag::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
+QuicErrorCode QuicNegotiableTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(!negotiated_);
DCHECK(error_details != NULL);
const QuicTag* received_tags;
size_t received_tags_length;
- QuicErrorCode error = ReadVector(client_hello, &received_tags,
+ QuicErrorCode error = ReadVector(peer_hello, &received_tags,
&received_tags_length, error_details);
if (error != QUIC_NO_ERROR) {
return error;
}
- QuicTag negotiated_tag;
- if (!QuicUtils::FindMutualTag(possible_values_,
- received_tags,
- received_tags_length,
- QuicUtils::LOCAL_PRIORITY,
- &negotiated_tag,
- NULL)) {
- *error_details = "Unsuported " + QuicUtils::TagToString(tag_);
- return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
+ if (hello_type == SERVER) {
+ if (received_tags_length != 1 ||
+ !ContainsQuicTag(possible_values_, *received_tags)) {
+ *error_details = "Invalid " + QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
+ }
+ negotiated_tag_ = *received_tags;
+ } else {
+ QuicTag negotiated_tag;
+ if (!QuicUtils::FindMutualTag(possible_values_,
+ received_tags,
+ received_tags_length,
+ QuicUtils::LOCAL_PRIORITY,
+ &negotiated_tag,
+ NULL)) {
+ *error_details = "Unsupported " + QuicUtils::TagToString(tag_);
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
+ }
+ negotiated_tag_ = negotiated_tag;
}
negotiated_ = true;
- negotiated_tag_ = negotiated_tag;
return QUIC_NO_ERROR;
}
-QuicErrorCode QuicNegotiableTag::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
+QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+QuicFixedUint32::~QuicFixedUint32() {}
+
+bool QuicFixedUint32::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedUint32::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedUint32::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedUint32::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedUint32::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedUint32::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
- DCHECK(!negotiated_);
DCHECK(error_details != NULL);
- const QuicTag* received_tags;
- size_t received_tags_length;
- QuicErrorCode error = ReadVector(server_hello, &received_tags,
- &received_tags_length, error_details);
- if (error != QUIC_NO_ERROR) {
- return error;
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
}
+ return error;
+}
- if (received_tags_length != 1 ||
- std::find(possible_values_.begin(), possible_values_.end(),
- *received_tags) == possible_values_.end()) {
- *error_details = "Invalid " + QuicUtils::TagToString(tag_);
- return QUIC_INVALID_NEGOTIATED_VALUE;
+QuicFixedTag::QuicFixedTag(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+
+QuicFixedTag::~QuicFixedTag() {}
+
+bool QuicFixedTag::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedTag::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedTag::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedTag::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedTag::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedTag::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
}
+}
- negotiated_ = true;
- negotiated_tag_ = *received_tags;
- return QUIC_NO_ERROR;
+QuicErrorCode QuicFixedTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
}
-QuicConfig::QuicConfig() :
- congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED),
- idle_connection_state_lifetime_seconds_(
- kICSL, QuicNegotiableValue::PRESENCE_REQUIRED),
- keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL),
- max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED),
- max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
- server_initial_congestion_window_(
- kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL),
- initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) {
- // All optional non-zero parameters should be initialized here.
- server_initial_congestion_window_.set(kMaxInitialWindow,
- kDefaultInitialWindow);
+QuicFixedTagVector::QuicFixedTagVector(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_values_(false),
+ has_receive_values_(false) {
+}
+
+QuicFixedTagVector::~QuicFixedTagVector() {}
+
+bool QuicFixedTagVector::HasSendValues() const {
+ return has_send_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetSendValues() const {
+ LOG_IF(DFATAL, !has_send_values_)
+ << "No send values to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_values_;
+}
+
+void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) {
+ has_send_values_ = true;
+ send_values_ = values;
+}
+
+bool QuicFixedTagVector::HasReceivedValues() const {
+ return has_receive_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetReceivedValues() const {
+ LOG_IF(DFATAL, !has_receive_values_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_values_;
+}
+
+void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) {
+ has_receive_values_ = true;
+ receive_values_ = values;
+}
+
+void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_values_) {
+ out->SetVector(tag_, send_values_);
+ }
+}
+
+QuicErrorCode QuicFixedTagVector::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error =
+ peer_hello.GetTaglist(tag_, &received_tags, &received_tags_length);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_values_ = true;
+ for (size_t i = 0; i < received_tags_length; ++i) {
+ receive_values_.push_back(received_tags[i]);
+ }
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicConfig::QuicConfig()
+ : congestion_feedback_(kCGST, PRESENCE_REQUIRED),
+ congestion_options_(kCOPT, PRESENCE_OPTIONAL),
+ loss_detection_(kLOSS, PRESENCE_OPTIONAL),
+ idle_connection_state_lifetime_seconds_(kICSL, PRESENCE_REQUIRED),
+ keepalive_timeout_seconds_(kKATO, PRESENCE_OPTIONAL),
+ max_streams_per_connection_(kMSPC, PRESENCE_REQUIRED),
+ max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ initial_congestion_window_(kSWND, PRESENCE_OPTIONAL),
+ initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_17.
+ initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) {
}
QuicConfig::~QuicConfig() {}
-void QuicConfig::set_congestion_control(
- const QuicTagVector& congestion_control,
- QuicTag default_congestion_control) {
- congestion_control_.set(congestion_control, default_congestion_control);
+void QuicConfig::set_congestion_feedback(
+ const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback) {
+ congestion_feedback_.set(congestion_feedback, default_congestion_feedback);
+}
+
+QuicTag QuicConfig::congestion_feedback() const {
+ return congestion_feedback_.GetTag();
+}
+
+void QuicConfig::SetCongestionOptionsToSend(
+ const QuicTagVector& congestion_options) {
+ congestion_options_.SetSendValues(congestion_options);
}
-QuicTag QuicConfig::congestion_control() const {
- return congestion_control_.GetTag();
+bool QuicConfig::HasReceivedCongestionOptions() const {
+ return congestion_options_.HasReceivedValues();
+}
+
+QuicTagVector QuicConfig::ReceivedCongestionOptions() const {
+ return congestion_options_.GetReceivedValues();
+}
+
+void QuicConfig::SetLossDetectionToSend(QuicTag loss_detection) {
+ loss_detection_.SetSendValue(loss_detection);
+}
+
+bool QuicConfig::HasReceivedLossDetection() const {
+ return loss_detection_.HasReceivedValue();
+}
+
+QuicTag QuicConfig::ReceivedLossDetection() const {
+ return loss_detection_.GetReceivedValue();
}
void QuicConfig::set_idle_connection_state_lifetime(
@@ -286,44 +518,113 @@ QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const {
return max_time_before_crypto_handshake_;
}
-void QuicConfig::set_server_initial_congestion_window(size_t max_initial_window,
- size_t default_initial_window) {
- server_initial_congestion_window_.set(max_initial_window,
- default_initial_window);
+void QuicConfig::SetInitialCongestionWindowToSend(size_t initial_window) {
+ initial_congestion_window_.SetSendValue(initial_window);
+}
+
+bool QuicConfig::HasReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialRoundTripTimeUsToSend(size_t rtt) {
+ initial_round_trip_time_us_.SetSendValue(rtt);
+}
+
+bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial flow control receive window (" << window_bytes
+ << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialFlowControlWindowToSend() const {
+ return initial_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial stream flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const {
+ return initial_stream_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial session flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_session_flow_control_window_bytes_.SetSendValue(window_bytes);
}
-uint32 QuicConfig::server_initial_congestion_window() const {
- return server_initial_congestion_window_.GetUint32();
+uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const {
+ return initial_session_flow_control_window_bytes_.GetSendValue();
}
-void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt,
- size_t default_rtt) {
- initial_round_trip_time_us_.set(max_rtt, default_rtt);
+bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.HasReceivedValue();
}
-uint32 QuicConfig::initial_round_trip_time_us() const {
- return initial_round_trip_time_us_.GetUint32();
+uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.GetReceivedValue();
}
bool QuicConfig::negotiated() {
// TODO(ianswett): Add the negotiated parameters once and iterate over all
// of them in negotiated, ToHandshakeMessage, ProcessClientHello, and
// ProcessServerHello.
- return congestion_control_.negotiated() &&
+ return congestion_feedback_.negotiated() &&
idle_connection_state_lifetime_seconds_.negotiated() &&
keepalive_timeout_seconds_.negotiated() &&
- max_streams_per_connection_.negotiated() &&
- server_initial_congestion_window_.negotiated() &&
- initial_round_trip_time_us_.negotiated();
+ max_streams_per_connection_.negotiated();
}
void QuicConfig::SetDefaults() {
- QuicTagVector congestion_control;
+ QuicTagVector congestion_feedback;
if (FLAGS_enable_quic_pacing) {
- congestion_control.push_back(kPACE);
+ congestion_feedback.push_back(kPACE);
}
- congestion_control.push_back(kQBIC);
- congestion_control_.set(congestion_control, kQBIC);
+ congestion_feedback.push_back(kQBIC);
+ congestion_feedback_.set(congestion_feedback, kQBIC);
idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs,
kDefaultInitialTimeoutSecs);
// kKATO is optional. Return 0 if not negotiated.
@@ -332,80 +633,85 @@ void QuicConfig::SetDefaults() {
kDefaultMaxStreamsPerConnection);
max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds(
kDefaultMaxTimeForCryptoHandshakeSecs);
- server_initial_congestion_window_.set(kDefaultInitialWindow,
- kDefaultInitialWindow);
+
+ SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+}
+
+void QuicConfig::EnablePacing(bool enable_pacing) {
+ QuicTagVector congestion_feedback;
+ if (enable_pacing) {
+ congestion_feedback.push_back(kPACE);
+ }
+ congestion_feedback.push_back(kQBIC);
+ congestion_feedback_.set(congestion_feedback, kQBIC);
}
void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
- congestion_control_.ToHandshakeMessage(out);
+ congestion_feedback_.ToHandshakeMessage(out);
idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out);
keepalive_timeout_seconds_.ToHandshakeMessage(out);
max_streams_per_connection_.ToHandshakeMessage(out);
- server_initial_congestion_window_.ToHandshakeMessage(out);
- // TODO(ianswett): Don't transmit parameters which are optional and not set.
+ initial_congestion_window_.ToHandshakeMessage(out);
initial_round_trip_time_us_.ToHandshakeMessage(out);
+ loss_detection_.ToHandshakeMessage(out);
+ initial_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_session_flow_control_window_bytes_.ToHandshakeMessage(out);
+ congestion_options_.ToHandshakeMessage(out);
}
-QuicErrorCode QuicConfig::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
+QuicErrorCode QuicConfig::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(error_details != NULL);
QuicErrorCode error = QUIC_NO_ERROR;
if (error == QUIC_NO_ERROR) {
- error = congestion_control_.ProcessClientHello(client_hello, error_details);
+ error = congestion_feedback_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = idle_connection_state_lifetime_seconds_.ProcessClientHello(
- client_hello, error_details);
+ error = idle_connection_state_lifetime_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = keepalive_timeout_seconds_.ProcessClientHello(
- client_hello, error_details);
+ error = keepalive_timeout_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = max_streams_per_connection_.ProcessClientHello(
- client_hello, error_details);
+ error = max_streams_per_connection_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = server_initial_congestion_window_.ProcessClientHello(
- client_hello, error_details);
+ error = initial_congestion_window_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = initial_round_trip_time_us_.ProcessClientHello(
- client_hello, error_details);
- }
- return error;
-}
-
-QuicErrorCode QuicConfig::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
- string* error_details) {
- DCHECK(error_details != NULL);
-
- QuicErrorCode error = QUIC_NO_ERROR;
- if (error == QUIC_NO_ERROR) {
- error = congestion_control_.ProcessServerHello(server_hello, error_details);
+ error = initial_round_trip_time_us_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = idle_connection_state_lifetime_seconds_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = keepalive_timeout_seconds_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_stream_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = max_streams_per_connection_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_session_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = server_initial_congestion_window_.ProcessServerHello(
- server_hello, error_details);
+ error = loss_detection_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = initial_round_trip_time_us_.ProcessServerHello(
- server_hello, error_details);
+ error = congestion_options_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
return error;
}
diff --git a/chromium/net/quic/quic_config.h b/chromium/net/quic/quic_config.h
index 6c0561b1ca4..8f46917450e 100644
--- a/chromium/net/quic/quic_config.h
+++ b/chromium/net/quic/quic_config.h
@@ -8,40 +8,74 @@
#include <string>
#include "base/basictypes.h"
-#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
-#include "net/quic/quic_utils.h"
namespace net {
-class NET_EXPORT_PRIVATE QuicNegotiableValue {
+namespace test {
+class QuicConfigPeer;
+} // namespace test
+
+class CryptoHandshakeMessage;
+
+// Describes whether or not a given QuicTag is required or optional in the
+// handshake message.
+enum QuicConfigPresence {
+ // This negotiable value can be absent from the handshake message. Default
+ // value is selected as the negotiated value in such a case.
+ PRESENCE_OPTIONAL,
+ // This negotiable value is required in the handshake message otherwise the
+ // Process*Hello function returns an error.
+ PRESENCE_REQUIRED,
+};
+
+// Whether the CryptoHandshakeMessage is from the client or server.
+enum HelloType {
+ CLIENT,
+ SERVER,
+};
+
+// An abstract base class that stores a value that can be sent in CHLO/SHLO
+// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|.
+class NET_EXPORT_PRIVATE QuicConfigValue {
public:
- enum Presence {
- // This negotiable value can be absent from the handshake message. Default
- // value is selected as the negotiated value in such a case.
- PRESENCE_OPTIONAL,
- // This negotiable value is required in the handshake message otherwise the
- // Process*Hello function returns an error.
- PRESENCE_REQUIRED,
- };
+ QuicConfigValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicConfigValue();
+
+ // Serialises tag name and value(s) to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0;
+
+ // Selects a mutually acceptable value from those offered in |peer_hello|
+ // and those defined in the subclass.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) = 0;
- QuicNegotiableValue(QuicTag tag, Presence presence);
+ protected:
+ const QuicTag tag_;
+ const QuicConfigPresence presence_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue {
+ public:
+ QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicNegotiableValue();
bool negotiated() const {
return negotiated_;
}
protected:
- const QuicTag tag_;
- const Presence presence_;
bool negotiated_;
};
class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
public:
// Default and max values default to 0.
- QuicNegotiableUint32(QuicTag name, Presence presence);
+ QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableUint32();
// Sets the maximum possible value that can be achieved after negotiation and
// also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg
@@ -55,30 +89,18 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
// Serialises |name_| and value to |out|. If |negotiated_| is true then
// |negotiated_value_| is serialised, otherwise |max_value_| is serialised.
- void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
// Sets |negotiated_value_| to the minimum of |max_value_| and the
- // corresponding value from |client_hello|. If the corresponding value is
+ // corresponding value from |peer_hello|. If the corresponding value is
// missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to
// |default_value_|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Sets the |negotiated_value_| to the corresponding value from
- // |server_hello|. Returns error if the value received in |server_hello| is
- // greater than |max_value_|. If the corresponding value is missing and
- // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|,
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
private:
- // Reads the value corresponding to |name_| from |msg| into |out|. If the
- // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set
- // to |max_value_|.
- QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
- uint32* out,
- std::string* error_details) const;
-
uint32 max_value_;
uint32 default_value_;
uint32 negotiated_value_;
@@ -86,8 +108,8 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
public:
- QuicNegotiableTag(QuicTag name, Presence presence);
- ~QuicNegotiableTag();
+ QuicNegotiableTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableTag();
// Sets the possible values that |negotiated_tag_| can take after negotiation
// and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO
@@ -101,19 +123,15 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
// Serialises |name_| and vector (either possible or negotiated) to |out|. If
// |negotiated_| is true then |negotiated_tag_| is serialised, otherwise
// |possible_values_| is serialised.
- void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
// Selects the tag common to both tags in |client_hello| for |name_| and
// |possible_values_| with preference to tag in |possible_values_|. The
// selected tag is set as |negotiated_tag_|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|.
- // Returns error if the value received in |server_hello| isn't present in
- // |possible_values_|.
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
private:
// Reads the vector corresponding to |name_| from |msg| into |out|. If the
@@ -129,6 +147,110 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
QuicTag default_value_;
};
+// Stores uint32 from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue {
+ public:
+ QuicFixedUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedUint32();
+
+ bool HasSendValue() const;
+
+ uint32 GetSendValue() const;
+
+ void SetSendValue(uint32 value);
+
+ bool HasReceivedValue() const;
+
+ uint32 GetReceivedValue() const;
+
+ void SetReceivedValue(uint32 value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |peer_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ uint32 send_value_;
+ bool has_send_value_;
+ uint32 receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTag : public QuicConfigValue {
+ public:
+ QuicFixedTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTag();
+
+ bool HasSendValue() const;
+
+ QuicTag GetSendValue() const;
+
+ void SetSendValue(QuicTag value);
+
+ bool HasReceivedValue() const;
+
+ QuicTag GetReceivedValue() const;
+
+ void SetReceivedValue(QuicTag value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |client_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTag send_value_;
+ bool has_send_value_;
+ QuicTag receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue {
+ public:
+ QuicFixedTagVector(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTagVector();
+
+ bool HasSendValues() const;
+
+ QuicTagVector GetSendValues() const;
+
+ void SetSendValues(const QuicTagVector& values);
+
+ bool HasReceivedValues() const;
+
+ QuicTagVector GetReceivedValues() const;
+
+ void SetReceivedValues(const QuicTagVector& values);
+
+ // If has_send_value is true, serialises |tag_vector_| and |send_value_| to
+ // |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |receive_values_| to the corresponding value from |client_hello_| if
+ // it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTagVector send_values_;
+ bool has_send_values_;
+ QuicTagVector receive_values_;
+ bool has_receive_values_;
+};
+
// QuicConfig contains non-crypto configuration options that are negotiated in
// the crypto handshake.
class NET_EXPORT_PRIVATE QuicConfig {
@@ -136,10 +258,22 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicConfig();
~QuicConfig();
- void set_congestion_control(const QuicTagVector& congestion_control,
- QuicTag default_congestion_control);
+ void set_congestion_feedback(const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback);
+
+ QuicTag congestion_feedback() const;
+
+ void SetCongestionOptionsToSend(const QuicTagVector& congestion_options);
+
+ bool HasReceivedCongestionOptions() const;
+
+ QuicTagVector ReceivedCongestionOptions() const;
+
+ void SetLossDetectionToSend(QuicTag loss_detection);
- QuicTag congestion_control() const;
+ bool HasReceivedLossDetection() const;
+
+ QuicTag ReceivedLossDetection() const;
void set_idle_connection_state_lifetime(
QuicTime::Delta max_idle_connection_state_lifetime,
@@ -159,40 +293,76 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicTime::Delta max_time_before_crypto_handshake() const;
- // Sets the server's TCP sender's max and default initial congestion window
- // in packets.
- void set_server_initial_congestion_window(size_t max_initial_window,
- size_t default_initial_window);
+ // Sets the peer's default initial congestion window in packets.
+ void SetInitialCongestionWindowToSend(size_t initial_window);
+
+ bool HasReceivedInitialCongestionWindow() const;
- uint32 server_initial_congestion_window() const;
+ uint32 ReceivedInitialCongestionWindow() const;
// Sets an estimated initial round trip time in us.
- void set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt);
+ void SetInitialRoundTripTimeUsToSend(size_t rtt_us);
+
+ bool HasReceivedInitialRoundTripTimeUs() const;
+
+ uint32 ReceivedInitialRoundTripTimeUs() const;
+
+ // TODO(rjshade): Remove all InitialFlowControlWindow methods when removing
+ // QUIC_VERSION_19.
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialFlowControlWindowBytes() const;
+
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialStreamFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialStreamFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialStreamFlowControlWindowBytes() const;
- uint32 initial_round_trip_time_us() const;
+ uint32 ReceivedInitialStreamFlowControlWindowBytes() const;
+
+ // Sets an initial session flow control window size to transmit to the peer.
+ void SetInitialSessionFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialSessionFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialSessionFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialSessionFlowControlWindowBytes() const;
bool negotiated();
// SetDefaults sets the members to sensible, default values.
void SetDefaults();
- // ToHandshakeMessage serializes the settings in this object as a series of
+ // Enabled pacing.
+ void EnablePacing(bool enable_pacing);
+
+ // ToHandshakeMessage serialises the settings in this object as a series of
// tags /value pairs and adds them to |out|.
void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
- // Calls ProcessClientHello on each negotiable parameter. On failure returns
+ // Calls ProcessPeerHello on each negotiable parameter. On failure returns
// the corresponding QuicErrorCode and sets detailed error in |error_details|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Calls ProcessServerHello on each negotiable parameter. On failure returns
- // the corresponding QuicErrorCode and sets detailed error in |error_details|.
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details);
private:
+ friend class test::QuicConfigPeer;
+
// Congestion control feedback type.
- QuicNegotiableTag congestion_control_;
+ QuicNegotiableTag congestion_feedback_;
+ // Congestion control option.
+ QuicFixedTagVector congestion_options_;
+ // Loss detection feedback type.
+ QuicFixedTag loss_detection_;
// Idle connection state lifetime
QuicNegotiableUint32 idle_connection_state_lifetime_seconds_;
// Keepalive timeout, or 0 to turn off keepalive probes
@@ -203,9 +373,18 @@ class NET_EXPORT_PRIVATE QuicConfig {
// finished. (Not negotiated).
QuicTime::Delta max_time_before_crypto_handshake_;
// Initial congestion window in packets.
- QuicNegotiableUint32 server_initial_congestion_window_;
+ QuicFixedUint32 initial_congestion_window_;
// Initial round trip time estimate in microseconds.
- QuicNegotiableUint32 initial_round_trip_time_us_;
+ QuicFixedUint32 initial_round_trip_time_us_;
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ // Initial flow control receive window in bytes.
+ QuicFixedUint32 initial_flow_control_window_bytes_;
+
+ // Initial stream flow control receive window in bytes.
+ QuicFixedUint32 initial_stream_flow_control_window_bytes_;
+ // Initial session flow control receive window in bytes.
+ QuicFixedUint32 initial_session_flow_control_window_bytes_;
};
} // namespace net
diff --git a/chromium/net/quic/quic_config_test.cc b/chromium/net/quic/quic_config_test.cc
index 2434cd772fd..3cb2dd26d3a 100644
--- a/chromium/net/quic/quic_config_test.cc
+++ b/chromium/net/quic/quic_config_test.cc
@@ -4,12 +4,15 @@
#include "net/quic/quic_config.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_sent_packet_manager.h"
#include "net/quic/quic_time.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::string;
@@ -22,15 +25,20 @@ class QuicConfigTest : public ::testing::Test {
protected:
QuicConfigTest() {
config_.SetDefaults();
- config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0);
}
QuicConfig config_;
};
TEST_F(QuicConfigTest, ToHandshakeMessage) {
- FLAGS_enable_quic_pacing = false;
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, false);
config_.SetDefaults();
+ config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5),
QuicTime::Delta::FromSeconds(2));
config_.set_max_streams_per_connection(4, 2);
@@ -46,6 +54,18 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) {
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_EQ(4u, value);
+ error = msg.GetUint32(kIFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kSFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kCFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
const QuicTag* out;
size_t out_len;
error = msg.GetTaglist(kCGST, &out, &out_len);
@@ -73,65 +93,130 @@ TEST_F(QuicConfigTest, ProcessClientHello) {
QuicTagVector cgst;
cgst.push_back(kINAR);
cgst.push_back(kQBIC);
- client_config.set_congestion_control(cgst, kQBIC);
+ client_config.set_congestion_feedback(cgst, kQBIC);
client_config.set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs));
client_config.set_max_streams_per_connection(
2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
- client_config.set_initial_round_trip_time_us(
- 10 * base::Time::kMicrosecondsPerMillisecond,
+ client_config.SetInitialRoundTripTimeUsToSend(
10 * base::Time::kMicrosecondsPerMillisecond);
-
+ client_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetCongestionOptionsToSend(copt);
CryptoHandshakeMessage msg;
client_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_TRUE(config_.negotiated());
- EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(kQBIC, config_.congestion_feedback());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs),
config_.idle_connection_state_lifetime());
EXPECT_EQ(kDefaultMaxStreamsPerConnection,
config_.max_streams_per_connection());
EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
- config_.initial_round_trip_time_us());
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_FALSE(config_.HasReceivedLossDetection());
+ EXPECT_TRUE(config_.HasReceivedCongestionOptions());
+ EXPECT_EQ(1u, config_.ReceivedCongestionOptions().size());
+ EXPECT_EQ(config_.ReceivedCongestionOptions()[0], kTBBR);
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
}
TEST_F(QuicConfigTest, ProcessServerHello) {
QuicConfig server_config;
QuicTagVector cgst;
cgst.push_back(kQBIC);
- server_config.set_congestion_control(cgst, kQBIC);
+ server_config.set_congestion_feedback(cgst, kQBIC);
server_config.set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2));
server_config.set_max_streams_per_connection(
kDefaultMaxStreamsPerConnection / 2,
kDefaultMaxStreamsPerConnection / 2);
- server_config.set_server_initial_congestion_window(kDefaultInitialWindow / 2,
- kDefaultInitialWindow / 2);
- server_config.set_initial_round_trip_time_us(
- 10 * base::Time::kMicrosecondsPerMillisecond,
+ server_config.SetInitialCongestionWindowToSend(kDefaultInitialWindow / 2);
+ server_config.SetInitialRoundTripTimeUsToSend(
10 * base::Time::kMicrosecondsPerMillisecond);
-
+ server_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ server_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_TRUE(config_.negotiated());
- EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(kQBIC, config_.congestion_feedback());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
config_.idle_connection_state_lifetime());
EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2,
config_.max_streams_per_connection());
EXPECT_EQ(kDefaultInitialWindow / 2,
- config_.server_initial_congestion_window());
+ config_.ReceivedInitialCongestionWindow());
EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
- config_.initial_round_trip_time_us());
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_FALSE(config_.HasReceivedLossDetection());
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) {
+ CryptoHandshakeMessage msg;
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
}
TEST_F(QuicConfigTest, MissingValueInCHLO) {
@@ -140,7 +225,8 @@ TEST_F(QuicConfigTest, MissingValueInCHLO) {
msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
// Missing kMSPC. KATO is optional.
string error_details;
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
}
@@ -150,7 +236,8 @@ TEST_F(QuicConfigTest, MissingValueInSHLO) {
msg.SetValue(kMSPC, 3);
// Missing CGST. KATO is optional.
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
}
@@ -163,7 +250,8 @@ TEST_F(QuicConfigTest, OutOfBoundSHLO) {
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
}
@@ -172,12 +260,13 @@ TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) {
QuicTagVector cgst;
cgst.push_back(kQBIC);
cgst.push_back(kINAR);
- server_config.set_congestion_control(cgst, kQBIC);
+ server_config.set_congestion_feedback(cgst, kQBIC);
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
}
@@ -186,16 +275,29 @@ TEST_F(QuicConfigTest, NoOverLapInCGST) {
server_config.SetDefaults();
QuicTagVector cgst;
cgst.push_back(kINAR);
- server_config.set_congestion_control(cgst, kINAR);
+ server_config.set_congestion_feedback(cgst, kINAR);
CryptoHandshakeMessage msg;
string error_details;
server_config.ToHandshakeMessage(&msg);
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
- LOG(INFO) << QuicUtils::ErrorToString(error);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ DVLOG(1) << QuicUtils::ErrorToString(error);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error);
}
+TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+ // QuicConfig should not accept an invalid flow control window to send to the
+ // peer: the receive window must be at least the default of 16 Kb.
+ QuicConfig config;
+ const uint64 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ EXPECT_DFATAL(config.SetInitialFlowControlWindowToSend(kInvalidWindow),
+ "Initial flow control receive window");
+
+ EXPECT_EQ(kDefaultFlowControlSendWindow,
+ config.GetInitialFlowControlWindowToSend());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_connection.cc b/chromium/net/quic/quic_connection.cc
index 51a83cc41a0..a977b3fdcdb 100644
--- a/chromium/net/quic/quic_connection.cc
+++ b/chromium/net/quic/quic_connection.cc
@@ -13,6 +13,7 @@
#include <set>
#include <utility>
+#include "base/debug/stack_trace.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/base/net_errors.h"
@@ -21,26 +22,20 @@
#include "net/quic/iovector.h"
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_config.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+using base::StringPiece;
using base::hash_map;
using base::hash_set;
-using base::StringPiece;
using std::list;
using std::make_pair;
-using std::min;
using std::max;
+using std::min;
using std::numeric_limits;
-using std::vector;
using std::set;
using std::string;
-
-int FLAGS_fake_packet_loss_percentage = 0;
-
-// If true, then QUIC connections will bundle acks with any outgoing packet when
-// an ack is being delayed. This is an optimization to reduce ack latency and
-// packet count of pure ack packets.
-bool FLAGS_bundle_ack_with_outgoing_packet = false;
+using std::vector;
namespace net {
@@ -66,7 +61,6 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
return delta <= kMaxPacketGap;
}
-
// An alarm that is scheduled to send an ack if a timeout occurs.
class AckAlarm : public QuicAlarm::Delegate {
public:
@@ -81,6 +75,8 @@ class AckAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckAlarm);
};
// This alarm will be scheduled any time a data-bearing packet is sent out.
@@ -99,6 +95,8 @@ class RetransmissionAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetransmissionAlarm);
};
// An alarm that is scheduled when the sent scheduler requires a
@@ -111,12 +109,14 @@ class SendAlarm : public QuicAlarm::Delegate {
virtual QuicTime OnAlarm() OVERRIDE {
connection_->WriteIfNotBlocked();
- // Never reschedule the alarm, since OnCanWrite does that.
+ // Never reschedule the alarm, since CanWrite does that.
return QuicTime::Zero();
}
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlarm);
};
class TimeoutAlarm : public QuicAlarm::Delegate {
@@ -133,90 +133,119 @@ class TimeoutAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeoutAlarm);
};
-// Indicates if any of the frames are intended to be sent with FORCE.
-// Returns FORCE 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;
+class PingAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit PingAlarm(QuicConnection* connection)
+ : connection_(connection) {
}
- for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
- if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
- return net::QuicConnection::FORCE;
- }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->SendPing();
+ return QuicTime::Zero();
}
- return net::QuicConnection::NO_FORCE;
-}
-net::IsHandshake HasCryptoHandshake(
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingAlarm);
+};
+
+QuicConnection::PacketType GetPacketType(
const RetransmittableFrames* retransmittable_frames) {
if (!retransmittable_frames) {
- return net::NOT_HANDSHAKE;
+ return QuicConnection::NORMAL;
}
for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
- if (retransmittable_frames->frames()[i].type == STREAM_FRAME &&
- retransmittable_frames->frames()[i].stream_frame->stream_id ==
- kCryptoStreamId) {
- return net::IS_HANDSHAKE;
+ if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
+ return QuicConnection::CONNECTION_CLOSE;
}
}
- return net::NOT_HANDSHAKE;
+ return QuicConnection::NORMAL;
}
} // namespace
+QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet,
+ EncryptionLevel level,
+ TransmissionType transmission_type)
+ : sequence_number(packet.sequence_number),
+ packet(packet.packet),
+ encryption_level(level),
+ transmission_type(transmission_type),
+ retransmittable((transmission_type != NOT_RETRANSMISSION ||
+ packet.retransmittable_frames != NULL) ?
+ HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA),
+ handshake(packet.retransmittable_frames == NULL ?
+ NOT_HANDSHAKE : packet.retransmittable_frames->HasCryptoHandshake()),
+ type(GetPacketType(packet.retransmittable_frames)),
+ length(packet.packet->length()) {
+}
+
#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
-QuicConnection::QuicConnection(QuicGuid guid,
+QuicConnection::QuicConnection(QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelperInterface* helper,
QuicPacketWriter* writer,
bool is_server,
const QuicVersionVector& supported_versions)
- : framer_(supported_versions,
- helper->GetClock()->ApproximateNow(),
+ : framer_(supported_versions, helper->GetClock()->ApproximateNow(),
is_server),
helper_(helper),
writer_(writer),
encryption_level_(ENCRYPTION_NONE),
clock_(helper->GetClock()),
random_generator_(helper->GetRandomGenerator()),
- guid_(guid),
+ connection_id_(connection_id),
peer_address_(address),
+ migrating_peer_port_(0),
+ last_packet_revived_(false),
+ last_size_(0),
+ last_decrypted_packet_level_(ENCRYPTION_NONE),
largest_seen_packet_with_ack_(0),
+ largest_seen_packet_with_stop_waiting_(0),
pending_version_negotiation_packet_(false),
- write_blocked_(false),
- received_packet_manager_(kTCP),
+ received_packet_manager_(kTCP, &stats_),
+ ack_queued_(false),
+ stop_waiting_count_(0),
ack_alarm_(helper->CreateAlarm(new AckAlarm(this))),
retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))),
send_alarm_(helper->CreateAlarm(new SendAlarm(this))),
resume_writes_alarm_(helper->CreateAlarm(new SendAlarm(this))),
timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))),
+ ping_alarm_(helper->CreateAlarm(new PingAlarm(this))),
debug_visitor_(NULL),
- packet_creator_(guid_, &framer_, random_generator_, is_server),
- packet_generator_(this, NULL, &packet_creator_),
+ packet_generator_(connection_id_, &framer_, random_generator_, this),
idle_network_timeout_(
QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)),
overall_connection_timeout_(QuicTime::Delta::Infinite()),
- creation_time_(clock_->ApproximateNow()),
time_of_last_received_packet_(clock_->ApproximateNow()),
- time_of_last_sent_packet_(clock_->ApproximateNow()),
- sequence_number_of_last_inorder_packet_(0),
- sent_packet_manager_(is_server, this, clock_, kTCP),
+ time_of_last_sent_new_packet_(clock_->ApproximateNow()),
+ sequence_number_of_last_sent_packet_(0),
+ sent_packet_manager_(
+ is_server, clock_, &stats_, kTCP,
+ FLAGS_quic_use_time_loss_detection ? kTime : kNack),
version_negotiation_state_(START_NEGOTIATION),
is_server_(is_server),
connected_(true),
- address_migrating_(false) {
+ peer_ip_changed_(false),
+ peer_port_changed_(false),
+ self_ip_changed_(false),
+ self_port_changed_(false) {
if (!is_server_) {
// Pacing will be enabled if the client negotiates it.
sent_packet_manager_.MaybeEnablePacing();
}
- DVLOG(1) << ENDPOINT << "Created connection with guid: " << guid;
+ DVLOG(1) << ENDPOINT << "Created connection with connection_id: "
+ << connection_id;
timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_));
framer_.set_visitor(this);
framer_.set_received_entropy_calculator(&received_packet_manager_);
+ stats_.connection_creation_time = clock_->ApproximateNow();
}
QuicConnection::~QuicConnection() {
@@ -229,7 +258,6 @@ QuicConnection::~QuicConnection() {
}
void QuicConnection::SetFromConfig(const QuicConfig& config) {
- DCHECK_LT(0u, config.server_initial_congestion_window());
SetIdleNetworkTimeout(config.idle_connection_state_lifetime());
sent_packet_manager_.SetFromConfig(config);
// TODO(satyamshekhar): Set congestion control and ICSL also.
@@ -265,9 +293,12 @@ void QuicConnection::OnError(QuicFramer* framer) {
void QuicConnection::OnPacket() {
DCHECK(last_stream_frames_.empty() &&
last_goaway_frames_.empty() &&
+ last_window_update_frames_.empty() &&
+ last_blocked_frames_.empty() &&
last_rst_frames_.empty() &&
last_ack_frames_.empty() &&
- last_congestion_frames_.empty());
+ last_congestion_frames_.empty() &&
+ last_stop_waiting_frames_.empty());
}
void QuicConnection::OnPublicResetPacket(
@@ -280,7 +311,7 @@ void QuicConnection::OnPublicResetPacket(
bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) {
DVLOG(1) << ENDPOINT << "Received packet with mismatched version "
- << received_version;
+ << received_version;
// TODO(satyamshekhar): Implement no server state in this mode.
if (!is_server_) {
LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. "
@@ -375,10 +406,19 @@ void QuicConnection::OnVersionNegotiationPacket(
void QuicConnection::OnRevivedPacket() {
}
+bool QuicConnection::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
return true;
}
+void QuicConnection::OnDecryptedPacket(EncryptionLevel level) {
+ last_decrypted_packet_level_ = level;
+}
+
bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
if (debug_visitor_) {
debug_visitor_->OnPacketHeader(header);
@@ -391,16 +431,17 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// Will be decrement below if we fall through to return true;
++stats_.packets_dropped;
- if (header.public_header.guid != guid_) {
- DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected GUID: "
- << header.public_header.guid << " instead of " << guid_;
+ if (header.public_header.connection_id != connection_id_) {
+ DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected ConnectionId: "
+ << header.public_header.connection_id << " instead of "
+ << connection_id_;
return false;
}
if (!Near(header.packet_sequence_number,
last_header_.packet_sequence_number)) {
DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
- << " out of bounds. Discarding";
+ << " out of bounds. Discarding";
SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER,
"Packet sequence number out of bounds");
return false;
@@ -410,14 +451,18 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// has told us will not be retransmitted, then stop processing the packet.
if (!received_packet_manager_.IsAwaitingPacket(
header.packet_sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " no longer being waited for. Discarding.";
+ // TODO(jri): Log reception of duplicate packets or packets the peer has
+ // told us to stop waiting for.
return false;
}
if (version_negotiation_state_ != NEGOTIATED_VERSION) {
if (is_server_) {
if (!header.public_header.version_flag) {
- DLOG(WARNING) << ENDPOINT << "Got packet without version flag before "
- << "version negotiated.";
+ DLOG(WARNING) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " without version flag before version negotiated.";
// Packets should have the version flag till version negotiation is
// done.
CloseConnection(QUIC_INVALID_VERSION, false);
@@ -432,7 +477,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
DCHECK(!header.public_header.version_flag);
// If the client gets a packet without the version flag from the server
// it should stop sending version since the version negotiation is done.
- packet_creator_.StopSendingVersion();
+ packet_generator_.StopSendingVersion();
version_negotiation_state_ = NEGOTIATED_VERSION;
visitor_->OnSuccessfulVersionNegotiation(version());
}
@@ -452,7 +497,7 @@ void QuicConnection::OnFecProtectedPayload(StringPiece payload) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
if (group != NULL) {
- group->Update(last_header_, payload);
+ group->Update(last_decrypted_packet_level_, last_header_, payload);
}
}
@@ -461,6 +506,13 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
if (debug_visitor_) {
debug_visitor_->OnStreamFrame(frame);
}
+ if (frame.stream_id != kCryptoStreamId &&
+ last_decrypted_packet_level_ == ENCRYPTION_NONE) {
+ DLOG(WARNING) << ENDPOINT
+ << "Received an unencrypted data frame: closing connection";
+ SendConnectionClose(QUIC_UNENCRYPTED_STREAM_DATA);
+ return false;
+ }
last_stream_frames_.push_back(frame);
return true;
}
@@ -488,34 +540,39 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
largest_seen_packet_with_ack_ = last_header_.packet_sequence_number;
-
- received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack);
- received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack);
- // Possibly close any FecGroups which are now irrelevant.
- CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1);
+ received_packet_manager_.UpdatePacketInformationReceivedByPeer(
+ incoming_ack.received_info);
+ if (version() <= QUIC_VERSION_15) {
+ ProcessStopWaitingFrame(incoming_ack.sent_info);
+ }
sent_entropy_manager_.ClearEntropyBefore(
received_packet_manager_.least_packet_awaited_by_peer() - 1);
- bool reset_retransmission_alarm =
- sent_packet_manager_.OnIncomingAck(incoming_ack.received_info,
- time_of_last_received_packet_);
+ sent_packet_manager_.OnIncomingAck(incoming_ack.received_info,
+ time_of_last_received_packet_);
if (sent_packet_manager_.HasPendingRetransmissions()) {
WriteIfNotBlocked();
}
- if (reset_retransmission_alarm) {
- retransmission_alarm_->Cancel();
- // Reset the RTO and FEC alarms if the are unacked packets.
- if (sent_packet_manager_.HasUnackedPackets()) {
- QuicTime::Delta retransmission_delay =
- sent_packet_manager_.GetRetransmissionDelay();
- retransmission_alarm_->Set(
- clock_->ApproximateNow().Add(retransmission_delay));
- }
+ // Always reset the retransmission alarm when an ack comes in, since we now
+ // have a better estimate of the current rtt than when it was set.
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time =
+ sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
}
}
+void QuicConnection::ProcessStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ largest_seen_packet_with_stop_waiting_ = last_header_.packet_sequence_number;
+ received_packet_manager_.UpdatePacketInformationSentByPeer(stop_waiting);
+ // Possibly close any FecGroups which are now irrelevant.
+ CloseFecGroupsBefore(stop_waiting.least_unacked + 1);
+}
+
bool QuicConnection::OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback) {
DCHECK(connected_);
@@ -526,12 +583,42 @@ bool QuicConnection::OnCongestionFeedbackFrame(
return connected_;
}
+bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ DCHECK(connected_);
+
+ if (last_header_.packet_sequence_number <=
+ largest_seen_packet_with_stop_waiting_) {
+ DVLOG(1) << ENDPOINT << "Received an old stop waiting frame: ignoring";
+ return true;
+ }
+
+ if (!ValidateStopWaitingFrame(frame)) {
+ SendConnectionClose(QUIC_INVALID_STOP_WAITING_DATA);
+ return false;
+ }
+
+ if (debug_visitor_) {
+ debug_visitor_->OnStopWaitingFrame(frame);
+ }
+
+ last_stop_waiting_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnPingFrame(frame);
+ }
+ return true;
+}
+
bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
if (incoming_ack.received_info.largest_observed >
- packet_creator_.sequence_number()) {
+ packet_generator_.sequence_number()) {
DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:"
<< incoming_ack.received_info.largest_observed << " vs "
- << packet_creator_.sequence_number();
+ << packet_generator_.sequence_number();
// We got an error for data we have not sent. Error out.
return false;
}
@@ -546,22 +633,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return false;
}
- if (incoming_ack.sent_info.least_unacked <
- received_packet_manager_.peer_least_packet_awaiting_ack()) {
- DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
- << incoming_ack.sent_info.least_unacked << " vs "
- << received_packet_manager_.peer_least_packet_awaiting_ack();
- // We never process old ack frames, so this number should only increase.
- return false;
- }
-
- if (incoming_ack.sent_info.least_unacked >
- last_header_.packet_sequence_number) {
- DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:"
- << incoming_ack.sent_info.least_unacked
- << " greater than the enclosing packet sequence number:"
- << last_header_.packet_sequence_number;
- return false;
+ if (version() <= QUIC_VERSION_15) {
+ if (!ValidateStopWaitingFrame(incoming_ack.sent_info)) {
+ return false;
+ }
}
if (!incoming_ack.received_info.missing_packets.empty() &&
@@ -592,6 +667,38 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return false;
}
+ for (SequenceNumberSet::const_iterator iter =
+ incoming_ack.received_info.revived_packets.begin();
+ iter != incoming_ack.received_info.revived_packets.end(); ++iter) {
+ if (!ContainsKey(incoming_ack.received_info.missing_packets, *iter)) {
+ DLOG(ERROR) << ENDPOINT
+ << "Peer specified revived packet which was not missing.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicConnection::ValidateStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ if (stop_waiting.least_unacked <
+ received_packet_manager_.peer_least_packet_awaiting_ack()) {
+ DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
+ << stop_waiting.least_unacked << " vs "
+ << received_packet_manager_.peer_least_packet_awaiting_ack();
+ // We never process old ack frames, so this number should only increase.
+ return false;
+ }
+
+ if (stop_waiting.least_unacked >
+ last_header_.packet_sequence_number) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:"
+ << stop_waiting.least_unacked
+ << " greater than the enclosing packet sequence number:"
+ << last_header_.packet_sequence_number;
+ return false;
+ }
+
return true;
}
@@ -600,8 +707,8 @@ void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
if (group != NULL) {
- group->UpdateFec(last_header_.packet_sequence_number,
- last_header_.entropy_flag, fec);
+ group->UpdateFec(last_decrypted_packet_level_,
+ last_header_.packet_sequence_number, fec);
}
}
@@ -611,7 +718,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
debug_visitor_->OnRstStreamFrame(frame);
}
DVLOG(1) << ENDPOINT << "Stream reset with error "
- << QuicUtils::StreamErrorToString(frame.error_code);
+ << QuicUtils::StreamErrorToString(frame.error_code);
last_rst_frames_.push_back(frame);
return connected_;
}
@@ -622,22 +729,48 @@ bool QuicConnection::OnConnectionCloseFrame(
if (debug_visitor_) {
debug_visitor_->OnConnectionCloseFrame(frame);
}
- DVLOG(1) << ENDPOINT << "Connection " << guid() << " closed with error "
- << QuicUtils::ErrorToString(frame.error_code)
- << " " << frame.error_details;
+ DVLOG(1) << ENDPOINT << "Connection " << connection_id()
+ << " closed with error "
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " " << frame.error_details;
last_close_frames_.push_back(frame);
return connected_;
}
bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnGoAwayFrame(frame);
+ }
DVLOG(1) << ENDPOINT << "Go away received with error "
- << QuicUtils::ErrorToString(frame.error_code)
- << " and reason:" << frame.reason_phrase;
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " and reason:" << frame.reason_phrase;
last_goaway_frames_.push_back(frame);
return connected_;
}
+bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnWindowUpdateFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "WindowUpdate received for stream: "
+ << frame.stream_id << " with byte offset: " << frame.byte_offset;
+ last_window_update_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnBlockedFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Blocked frame received for stream: "
+ << frame.stream_id;
+ last_blocked_frames_.push_back(frame);
+ return connected_;
+}
+
void QuicConnection::OnPacketComplete() {
// Don't do anything if this packet closed the connection.
if (!connected_) {
@@ -646,38 +779,51 @@ void QuicConnection::OnPacketComplete() {
}
DVLOG(1) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got")
- << " packet " << last_header_.packet_sequence_number
- << " with " << last_ack_frames_.size() << " acks, "
- << last_congestion_frames_.size() << " congestions, "
- << last_goaway_frames_.size() << " goaways, "
- << last_rst_frames_.size() << " rsts, "
- << last_close_frames_.size() << " closes, "
- << last_stream_frames_.size()
- << " stream frames for " << last_header_.public_header.guid;
-
- // Must called before ack processing, because processing acks removes entries
- // from unacket_packets_, increasing the least_unacked.
- const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck();
+ << " packet " << last_header_.packet_sequence_number
+ << " with " << last_ack_frames_.size() << " acks, "
+ << last_congestion_frames_.size() << " congestions, "
+ << last_stop_waiting_frames_.size() << " stop_waiting, "
+ << last_goaway_frames_.size() << " goaways, "
+ << last_window_update_frames_.size() << " window updates, "
+ << last_blocked_frames_.size() << " blocked, "
+ << last_rst_frames_.size() << " rsts, "
+ << last_close_frames_.size() << " closes, "
+ << last_stream_frames_.size()
+ << " stream frames for "
+ << last_header_.public_header.connection_id;
+
+ // Call MaybeQueueAck() before recording the received packet, since we want
+ // to trigger an ack if the newly received packet was previously missing.
+ MaybeQueueAck();
+
+ // Record received or revived packet to populate ack info correctly before
+ // processing stream frames, since the processing may result in a response
+ // packet with a bundled ack.
+ if (last_packet_revived_) {
+ received_packet_manager_.RecordPacketRevived(
+ last_header_.packet_sequence_number);
+ } else {
+ received_packet_manager_.RecordPacketReceived(
+ last_size_, last_header_, time_of_last_received_packet_);
+ }
- // If the incoming packet was missing, send an ack immediately.
- bool send_ack_immediately = received_packet_manager_.IsMissing(
- last_header_.packet_sequence_number);
+ if (!last_stream_frames_.empty()) {
+ visitor_->OnStreamFrames(last_stream_frames_);
+ }
- // Ensure the visitor can process the stream frames before recording and
- // processing the rest of the packet.
- if (last_stream_frames_.empty() ||
- visitor_->OnStreamFrames(last_stream_frames_)) {
- received_packet_manager_.RecordPacketReceived(last_size_,
- last_header_,
- time_of_last_received_packet_,
- last_packet_revived_);
- for (size_t i = 0; i < last_stream_frames_.size(); ++i) {
- stats_.stream_bytes_received +=
- last_stream_frames_[i].data.TotalBufferSize();
- }
+ for (size_t i = 0; i < last_stream_frames_.size(); ++i) {
+ stats_.stream_bytes_received +=
+ last_stream_frames_[i].data.TotalBufferSize();
}
- // Process stream resets, then acks, then congestion feedback.
+ // Process window updates, blocked, stream resets, acks, then congestion
+ // feedback.
+ if (!last_window_update_frames_.empty()) {
+ visitor_->OnWindowUpdateFrames(last_window_update_frames_);
+ }
+ if (!last_blocked_frames_.empty()) {
+ visitor_->OnBlockedFrames(last_blocked_frames_);
+ }
for (size_t i = 0; i < last_goaway_frames_.size(); ++i) {
visitor_->OnGoAway(last_goaway_frames_[i]);
}
@@ -691,6 +837,9 @@ void QuicConnection::OnPacketComplete() {
sent_packet_manager_.OnIncomingQuicCongestionFeedbackFrame(
last_congestion_frames_[i], time_of_last_received_packet_);
}
+ for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) {
+ ProcessStopWaitingFrame(last_stop_waiting_frames_[i]);
+ }
if (!last_close_frames_.empty()) {
CloseConnection(last_close_frames_[0].error_code, true);
DCHECK(!connected_);
@@ -698,20 +847,48 @@ void QuicConnection::OnPacketComplete() {
// If there are new missing packets to report, send an ack immediately.
if (received_packet_manager_.HasNewMissingPackets()) {
- send_ack_immediately = true;
+ ack_queued_ = true;
+ ack_alarm_->Cancel();
}
- MaybeSendInResponseToPacket(send_ack_immediately,
- last_packet_should_instigate_ack);
+ UpdateStopWaitingCount();
ClearLastFrames();
}
+void QuicConnection::MaybeQueueAck() {
+ // If the incoming packet was missing, send an ack immediately.
+ ack_queued_ = received_packet_manager_.IsMissing(
+ last_header_.packet_sequence_number);
+
+ if (!ack_queued_ && ShouldLastPacketInstigateAck()) {
+ if (ack_alarm_->IsSet()) {
+ ack_queued_ = true;
+ } else {
+ // Send an ack much more quickly for crypto handshake packets.
+ QuicTime::Delta delayed_ack_time = sent_packet_manager_.DelayedAckTime();
+ if (last_stream_frames_.size() == 1 &&
+ last_stream_frames_[0].stream_id == kCryptoStreamId) {
+ delayed_ack_time = QuicTime::Delta::Zero();
+ }
+ ack_alarm_->Set(clock_->ApproximateNow().Add(delayed_ack_time));
+ DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK.";
+ }
+ }
+
+ if (ack_queued_) {
+ ack_alarm_->Cancel();
+ }
+}
+
void QuicConnection::ClearLastFrames() {
last_stream_frames_.clear();
last_goaway_frames_.clear();
+ last_window_update_frames_.clear();
+ last_blocked_frames_.clear();
last_rst_frames_.clear();
last_ack_frames_.clear();
+ last_stop_waiting_frames_.clear();
last_congestion_frames_.clear();
}
@@ -719,7 +896,7 @@ QuicAckFrame* QuicConnection::CreateAckFrame() {
QuicAckFrame* outgoing_ack = new QuicAckFrame();
received_packet_manager_.UpdateReceivedPacketInfo(
&(outgoing_ack->received_info), clock_->ApproximateNow());
- UpdateSentPacketInfo(&(outgoing_ack->sent_info));
+ UpdateStopWaiting(&(outgoing_ack->sent_info));
DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack;
return outgoing_ack;
}
@@ -728,82 +905,91 @@ QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() {
return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_);
}
-bool QuicConnection::ShouldLastPacketInstigateAck() {
+QuicStopWaitingFrame* QuicConnection::CreateStopWaitingFrame() {
+ QuicStopWaitingFrame stop_waiting;
+ UpdateStopWaiting(&stop_waiting);
+ return new QuicStopWaitingFrame(stop_waiting);
+}
+
+bool QuicConnection::ShouldLastPacketInstigateAck() const {
if (!last_stream_frames_.empty() ||
!last_goaway_frames_.empty() ||
- !last_rst_frames_.empty()) {
+ !last_rst_frames_.empty() ||
+ !last_window_update_frames_.empty() ||
+ !last_blocked_frames_.empty()) {
return true;
}
- // If the peer is still waiting for a packet that we are no
- // longer planning to send, we should send an ack to raise
- // the high water mark.
if (!last_ack_frames_.empty() &&
- !last_ack_frames_.back().received_info.missing_packets.empty()) {
- return sent_packet_manager_.GetLeastUnackedSentPacket() >
- *last_ack_frames_.back().received_info.missing_packets.begin();
+ last_ack_frames_.back().received_info.is_truncated) {
+ return true;
}
return false;
}
-void QuicConnection::MaybeSendInResponseToPacket(
- bool send_ack_immediately,
- bool last_packet_should_instigate_ack) {
- // |include_ack| is false since we decide about ack bundling below.
- ScopedPacketBundler bundler(this, false);
+void QuicConnection::UpdateStopWaitingCount() {
+ if (last_ack_frames_.empty()) {
+ return;
+ }
- if (last_packet_should_instigate_ack) {
- // In general, we ack every second packet. When we don't ack the first
- // packet, we set the delayed ack alarm. Thus, if the ack alarm is set
- // then we know this is the second packet, and we should send an ack.
- if (send_ack_immediately || ack_alarm_->IsSet()) {
- SendAck();
- DCHECK(!ack_alarm_->IsSet());
- } else {
- ack_alarm_->Set(clock_->ApproximateNow().Add(
- sent_packet_manager_.DelayedAckTime()));
- DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK.";
- }
+ // If the peer is still waiting for a packet that we are no longer planning to
+ // send, send an ack to raise the high water mark.
+ if (!last_ack_frames_.back().received_info.missing_packets.empty() &&
+ GetLeastUnacked() >
+ *last_ack_frames_.back().received_info.missing_packets.begin()) {
+ ++stop_waiting_count_;
+ } else {
+ stop_waiting_count_ = 0;
}
+}
- if (!last_ack_frames_.empty()) {
- // Now the we have received an ack, we might be able to send packets which
- // are queued locally, or drain streams which are blocked.
- QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(
- time_of_last_received_packet_, NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- if (delay.IsZero()) {
- send_alarm_->Cancel();
- WriteIfNotBlocked();
- } else if (!delay.IsInfinite()) {
- send_alarm_->Cancel();
- send_alarm_->Set(time_of_last_received_packet_.Add(delay));
- }
+QuicPacketSequenceNumber QuicConnection::GetLeastUnacked() const {
+ return sent_packet_manager_.HasUnackedPackets() ?
+ sent_packet_manager_.GetLeastUnackedSentPacket() :
+ packet_generator_.sequence_number() + 1;
+}
+
+void QuicConnection::MaybeSendInResponseToPacket() {
+ if (!connected_) {
+ return;
+ }
+ ScopedPacketBundler bundler(this, ack_queued_ ? SEND_ACK : NO_ACK);
+
+ // Now that we have received an ack, we might be able to send packets which
+ // are queued locally, or drain streams which are blocked.
+ if (CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ OnCanWrite();
}
}
void QuicConnection::SendVersionNegotiationPacket() {
- scoped_ptr<QuicEncryptedPacket> version_packet(
- packet_creator_.SerializeVersionNegotiationPacket(
- framer_.supported_versions()));
- // TODO(satyamshekhar): implement zero server state negotiation.
- WriteResult result =
- writer_->WritePacket(version_packet->data(), version_packet->length(),
- self_address().address(), peer_address(), this);
- if (result.status == WRITE_STATUS_BLOCKED) {
- write_blocked_ = true;
- }
- if (result.status == WRITE_STATUS_OK ||
- (result.status == WRITE_STATUS_BLOCKED &&
- writer_->IsWriteBlockedDataBuffered())) {
- pending_version_negotiation_packet_ = false;
+ // TODO(alyssar): implement zero server state negotiation.
+ pending_version_negotiation_packet_ = true;
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
return;
}
+ scoped_ptr<QuicEncryptedPacket> version_packet(
+ packet_generator_.SerializeVersionNegotiationPacket(
+ framer_.supported_versions()));
+ WriteResult result = writer_->WritePacket(
+ version_packet->data(), version_packet->length(),
+ self_address().address(), peer_address());
+
if (result.status == WRITE_STATUS_ERROR) {
// We can't send an error as the socket is presumably borked.
CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
+ return;
}
- pending_version_negotiation_packet_ = true;
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ visitor_->OnWriteBlocked();
+ if (writer_->IsWriteBlockedDataBuffered()) {
+ pending_version_negotiation_packet_ = false;
+ }
+ return;
+ }
+
+ pending_version_negotiation_packet_ = false;
}
QuicConsumedData QuicConnection::SendStreamData(
@@ -811,6 +997,7 @@ QuicConsumedData QuicConnection::SendStreamData(
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* delegate) {
if (!fin && data.Empty()) {
LOG(DFATAL) << "Attempt to send empty stream frame";
@@ -823,11 +1010,23 @@ QuicConsumedData QuicConnection::SendStreamData(
notifier = new QuicAckNotifier(delegate);
}
- // Opportunistically bundle an ack with this outgoing packet, unless it's the
- // crypto stream.
- ScopedPacketBundler ack_bundler(this, id != kCryptoStreamId);
+ // Opportunistically bundle an ack with every outgoing packet.
+ // Particularly, we want to bundle with handshake packets since we don't know
+ // which decrypter will be used on an ack packet following a handshake
+ // packet (a handshake packet from client to server could result in a REJ or a
+ // SHLO from the server, leading to two different decrypters at the server.)
+ //
+ // TODO(jri): Note that ConsumeData may cause a response packet to be sent.
+ // We may end up sending stale ack information if there are undecryptable
+ // packets hanging around and/or there are revivable packets which may get
+ // handled after this packet is sent. Change ScopedPacketBundler to do the
+ // right thing: check ack_queued_, and then check undecryptable packets and
+ // also if there is possibility of revival. Only bundle an ack if there's no
+ // processing left that may cause received_info_ to change.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
QuicConsumedData consumed_data =
- packet_generator_.ConsumeData(id, data, offset, fin, notifier);
+ packet_generator_.ConsumeData(id, data, offset, fin, fec_protection,
+ notifier);
if (notifier &&
(consumed_data.bytes_consumed == 0 && !consumed_data.fin_consumed)) {
@@ -839,19 +1038,38 @@ QuicConsumedData QuicConnection::SendStreamData(
}
void QuicConnection::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- DVLOG(1) << "Sending RST_STREAM: " << id << " code: " << error;
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
// Opportunistically bundle an ack with this outgoing packet.
- ScopedPacketBundler ack_bundler(this, true);
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicRstStreamFrame(
+ id, AdjustErrorForVersion(error, version()), bytes_written)));
+}
+
+void QuicConnection::SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
packet_generator_.AddControlFrame(
- QuicFrame(new QuicRstStreamFrame(id, error)));
+ QuicFrame(new QuicWindowUpdateFrame(id, byte_offset)));
+}
+
+void QuicConnection::SendBlocked(QuicStreamId id) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicBlockedFrame(id)));
}
const QuicConnectionStats& QuicConnection::GetStats() {
// Update rtt and estimated bandwidth.
- stats_.rtt = sent_packet_manager_.SmoothedRtt().ToMicroseconds();
+ stats_.min_rtt_us =
+ sent_packet_manager_.GetRttStats()->min_rtt().ToMicroseconds();
+ stats_.srtt_us =
+ sent_packet_manager_.GetRttStats()->SmoothedRtt().ToMicroseconds();
stats_.estimated_bandwidth =
sent_packet_manager_.BandwidthEstimate().ToBytesPerSecond();
+ stats_.congestion_window = sent_packet_manager_.GetCongestionWindow();
+ stats_.max_packet_size = packet_generator_.max_packet_length();
return stats_;
}
@@ -867,18 +1085,7 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
last_packet_revived_ = false;
last_size_ = packet.length();
- address_migrating_ = false;
-
- if (peer_address_.address().empty()) {
- peer_address_ = peer_address;
- }
- if (self_address_.address().empty()) {
- self_address_ = self_address;
- }
-
- if (!(peer_address == peer_address_ && self_address == self_address_)) {
- address_migrating_ = true;
- }
+ CheckForAddressMigration(self_address, peer_address);
stats_.bytes_received += packet.length();
++stats_.packets_received;
@@ -895,101 +1102,128 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
<< last_header_.packet_sequence_number;
return;
}
+
+ ++stats_.packets_processed;
MaybeProcessUndecryptablePackets();
MaybeProcessRevivedPacket();
+ MaybeSendInResponseToPacket();
+ SetPingAlarm();
}
-bool QuicConnection::OnCanWrite() {
- write_blocked_ = false;
- return DoWrite();
-}
+void QuicConnection::CheckForAddressMigration(
+ const IPEndPoint& self_address, const IPEndPoint& peer_address) {
+ peer_ip_changed_ = false;
+ peer_port_changed_ = false;
+ self_ip_changed_ = false;
+ self_port_changed_ = false;
-bool QuicConnection::WriteIfNotBlocked() {
- if (write_blocked_) {
- return false;
+ if (peer_address_.address().empty()) {
+ peer_address_ = peer_address;
+ }
+ if (self_address_.address().empty()) {
+ self_address_ = self_address;
+ }
+
+ if (!peer_address.address().empty() && !peer_address_.address().empty()) {
+ peer_ip_changed_ = (peer_address.address() != peer_address_.address());
+ peer_port_changed_ = (peer_address.port() != peer_address_.port());
+
+ // Store in case we want to migrate connection in ProcessValidatedPacket.
+ migrating_peer_port_ = peer_address.port();
+ }
+
+ if (!self_address.address().empty() && !self_address_.address().empty()) {
+ self_ip_changed_ = (self_address.address() != self_address_.address());
+ self_port_changed_ = (self_address.port() != self_address_.port());
}
- return DoWrite();
}
-bool QuicConnection::DoWrite() {
- DCHECK(!write_blocked_);
- WriteQueuedPackets();
+void QuicConnection::OnCanWrite() {
+ DCHECK(!writer_->IsWriteBlocked());
+ WriteQueuedPackets();
WritePendingRetransmissions();
- 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,
- pending_handshake)) {
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return;
+ }
+
+ { // Limit the scope of the bundler.
// Set |include_ack| to false in bundler; ack inclusion happens elsewhere.
- scoped_ptr<ScopedPacketBundler> bundler(
- new ScopedPacketBundler(this, false));
- bool all_bytes_written = visitor_->OnCanWrite();
- bundler.reset();
- // After the visitor writes, it may have caused the socket to become write
- // blocked or the congestion manager to prohibit sending, so check again.
- pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE
- : NOT_HANDSHAKE;
- if (!all_bytes_written && !resume_writes_alarm_->IsSet() &&
- CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- 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.
- resume_writes_alarm_->Set(clock_->ApproximateNow());
- }
+ ScopedPacketBundler bundler(this, NO_ACK);
+ visitor_->OnCanWrite();
+ }
+
+ // After the visitor writes, it may have caused the socket to become write
+ // blocked or the congestion manager to prohibit sending, so check again.
+ if (visitor_->WillingAndAbleToWrite() &&
+ !resume_writes_alarm_->IsSet() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ // 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 connections and events have had a chance to use the thread.
+ resume_writes_alarm_->Set(clock_->ApproximateNow());
}
+}
- return !write_blocked_;
+void QuicConnection::WriteIfNotBlocked() {
+ if (!writer_->IsWriteBlocked()) {
+ OnCanWrite();
+ }
}
bool QuicConnection::ProcessValidatedPacket() {
- if (address_migrating_) {
+ if ((!FLAGS_quic_allow_port_migration && peer_port_changed_) ||
+ peer_ip_changed_ || self_ip_changed_ || self_port_changed_) {
SendConnectionCloseWithDetails(
QUIC_ERROR_MIGRATING_ADDRESS,
- "Address migration is not yet a supported feature");
+ "Neither IP address migration, nor self port migration are supported.");
return false;
}
+
+ // Port migration is supported, do it now if port has changed.
+ if (FLAGS_quic_allow_port_migration &&
+ peer_port_changed_) {
+ DVLOG(1) << ENDPOINT << "Peer's port changed from "
+ << peer_address_.port() << " to " << migrating_peer_port_
+ << ", migrating connection.";
+ peer_address_ = IPEndPoint(peer_address_.address(), migrating_peer_port_);
+ }
+
time_of_last_received_packet_ = clock_->Now();
DVLOG(1) << ENDPOINT << "time of last received packet: "
<< time_of_last_received_packet_.ToDebuggingValue();
if (is_server_ && encryption_level_ == ENCRYPTION_NONE &&
- last_size_ > options()->max_packet_length) {
- options()->max_packet_length = last_size_;
+ last_size_ > packet_generator_.max_packet_length()) {
+ packet_generator_.set_max_packet_length(last_size_);
}
return true;
}
-bool QuicConnection::WriteQueuedPackets() {
- DCHECK(!write_blocked_);
+void QuicConnection::WriteQueuedPackets() {
+ DCHECK(!writer_->IsWriteBlocked());
if (pending_version_negotiation_packet_) {
SendVersionNegotiationPacket();
}
QueuedPacketList::iterator packet_iterator = queued_packets_.begin();
- while (!write_blocked_ && packet_iterator != queued_packets_.end()) {
- if (WritePacket(packet_iterator->encryption_level,
- packet_iterator->sequence_number,
- packet_iterator->packet,
- packet_iterator->transmission_type,
- packet_iterator->retransmittable,
- packet_iterator->handshake,
- packet_iterator->forced)) {
+ while (!writer_->IsWriteBlocked() &&
+ packet_iterator != queued_packets_.end()) {
+ if (WritePacket(*packet_iterator)) {
+ delete packet_iterator->packet;
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
// Continue, because some queued packets may still be writable.
- // This can happen if a retransmit send fail.
+ // This can happen if a retransmit send fails.
++packet_iterator;
}
}
-
- return !write_blocked_;
}
void QuicConnection::WritePendingRetransmissions() {
@@ -998,9 +1232,8 @@ void QuicConnection::WritePendingRetransmissions() {
while (sent_packet_manager_.HasPendingRetransmissions()) {
const QuicSentPacketManager::PendingRetransmission pending =
sent_packet_manager_.NextPendingRetransmission();
- if (HasForcedFrames(&pending.retransmittable_frames) == NO_FORCE &&
- !CanWrite(pending.transmission_type, HAS_RETRANSMITTABLE_DATA,
- HasCryptoHandshake(&pending.retransmittable_frames))) {
+ if (GetPacketType(&pending.retransmittable_frames) == NORMAL &&
+ !CanWrite(HAS_RETRANSMITTABLE_DATA)) {
break;
}
@@ -1008,22 +1241,23 @@ void QuicConnection::WritePendingRetransmissions() {
// Retransmitted data packets do not use FEC, even when it's enabled.
// Retransmitted packets use the same sequence number length as the
// original.
- // Flush the packet creator before making a new packet.
+ // Flush the packet generator before making a new packet.
// TODO(ianswett): Implement ReserializeAllFrames as a separate path that
// does not require the creator to be flushed.
- Flush();
- SerializedPacket serialized_packet = packet_creator_.ReserializeAllFrames(
+ packet_generator_.FlushAllQueuedFrames();
+ SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames(
pending.retransmittable_frames.frames(),
pending.sequence_number_length);
DVLOG(1) << ENDPOINT << "Retransmitting " << pending.sequence_number
- << " as " << serialized_packet.sequence_number;
+ << " as " << serialized_packet.sequence_number;
if (debug_visitor_) {
debug_visitor_->OnPacketRetransmitted(
pending.sequence_number, serialized_packet.sequence_number);
}
sent_packet_manager_.OnRetransmittedPacket(
- pending.sequence_number, serialized_packet.sequence_number);
+ pending.sequence_number,
+ serialized_packet.sequence_number);
SendOrQueuePacket(pending.retransmittable_frames.encryption_level(),
serialized_packet,
@@ -1038,6 +1272,16 @@ void QuicConnection::RetransmitUnackedPackets(
WriteIfNotBlocked();
}
+void QuicConnection::NeuterUnencryptedPackets() {
+ sent_packet_manager_.NeuterUnencryptedPackets();
+ // This may have changed the retransmission timer, so re-arm it.
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
+ }
+}
+
bool QuicConnection::ShouldGeneratePacket(
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
@@ -1048,35 +1292,25 @@ bool QuicConnection::ShouldGeneratePacket(
return true;
}
- return CanWrite(transmission_type, retransmittable, handshake);
+ return CanWrite(retransmittable);
}
-bool QuicConnection::CanWrite(TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake) {
- if (write_blocked_) {
- return false;
- }
-
- // TODO(rch): consider removing this check so that if an ACK comes in
- // before the alarm goes it, we might be able send out a packet.
- // This check assumes that if the send alarm is set, it applies equally to all
- // types of transmissions.
- if (send_alarm_->IsSet()) {
- DVLOG(1) << "Send alarm set. Not sending.";
+bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
return false;
}
+ send_alarm_->Cancel();
QuicTime now = clock_->Now();
QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(
- now, transmission_type, retransmittable, handshake);
+ now, retransmittable);
if (delay.IsInfinite()) {
return false;
}
// If the scheduler requires a delay, then we can not send this packet now.
if (!delay.IsZero()) {
- send_alarm_->Cancel();
send_alarm_->Set(now.Add(delay));
DVLOG(1) << "Delaying sending.";
return false;
@@ -1084,122 +1318,103 @@ bool QuicConnection::CanWrite(TransmissionType transmission_type,
return true;
}
-void QuicConnection::SetupRetransmissionAlarm(
- QuicPacketSequenceNumber sequence_number) {
- if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
- DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number;
- return;
- }
-
- // Do not set the retransmission alarm if we're already handling one, since
- // it will be reset when OnRetransmissionTimeout completes.
- if (retransmission_alarm_->IsSet()) {
- return;
- }
-
- QuicTime::Delta retransmission_delay =
- sent_packet_manager_.GetRetransmissionDelay();
- retransmission_alarm_->Set(
- clock_->ApproximateNow().Add(retransmission_delay));
-}
-
-bool QuicConnection::WritePacket(EncryptionLevel level,
- QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force forced) {
- if (ShouldDiscardPacket(level, sequence_number, retransmittable)) {
- delete packet;
+bool QuicConnection::WritePacket(QueuedPacket packet) {
+ QuicPacketSequenceNumber sequence_number = packet.sequence_number;
+ if (ShouldDiscardPacket(packet.encryption_level,
+ sequence_number,
+ packet.retransmittable)) {
+ ++stats_.packets_discarded;
return true;
}
- // If we're write blocked, we know we can't write.
- if (write_blocked_) {
- return false;
- }
-
- // If we are not forced and we can't write, then simply return false;
- if (forced == NO_FORCE &&
- !CanWrite(transmission_type, retransmittable, handshake)) {
+ // If the packet is CONNECTION_CLOSE, we need to try to send it immediately
+ // and encrypt it to hand it off to TimeWaitListManager.
+ // If the packet is QUEUED, we don't re-consult the congestion control.
+ // This ensures packets are sent in sequence number order.
+ // TODO(ianswett): The congestion control should have been consulted before
+ // serializing the packet, so this could be turned into a LOG_IF(DFATAL).
+ if (packet.type == NORMAL && !CanWrite(packet.retransmittable)) {
return false;
}
// Some encryption algorithms require the packet sequence numbers not be
// repeated.
- DCHECK_LE(sequence_number_of_last_inorder_packet_, sequence_number);
- // Only increase this when packets have not been queued. Once they're queued
- // due to a write block, there is the chance of sending forced and other
- // higher priority packets out of order.
- if (queued_packets_.empty()) {
- sequence_number_of_last_inorder_packet_ = sequence_number;
- }
+ DCHECK_LE(sequence_number_of_last_sent_packet_, sequence_number);
+ sequence_number_of_last_sent_packet_ = sequence_number;
- scoped_ptr<QuicEncryptedPacket> encrypted(
- framer_.EncryptPacket(level, sequence_number, *packet));
- if (encrypted.get() == NULL) {
+ QuicEncryptedPacket* encrypted = framer_.EncryptPacket(
+ packet.encryption_level, sequence_number, *packet.packet);
+ if (encrypted == NULL) {
LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number "
<< sequence_number;
+ // CloseConnection does not send close packet, so no infinite loop here.
CloseConnection(QUIC_ENCRYPTION_FAILURE, false);
return false;
}
- // If it's the ConnectionClose packet, the only FORCED frame type,
- // clone a copy for resending later by the TimeWaitListManager.
- if (forced == FORCE) {
+ // Connection close packets are eventually owned by TimeWaitListManager.
+ // Others are deleted at the end of this call.
+ scoped_ptr<QuicEncryptedPacket> encrypted_deleter;
+ if (packet.type == CONNECTION_CLOSE) {
DCHECK(connection_close_packet_.get() == NULL);
- connection_close_packet_.reset(encrypted->Clone());
- }
-
- if (encrypted->length() > options()->max_packet_length) {
- LOG(DFATAL) << "Writing an encrypted packet larger than max_packet_length:"
- << options()->max_packet_length << " encrypted length: "
- << encrypted->length();
- }
- DVLOG(1) << ENDPOINT << "Sending packet number " << sequence_number
- << " : " << (packet->is_fec_packet() ? "FEC " :
- (retransmittable == HAS_RETRANSMITTABLE_DATA
- ? "data bearing " : " ack only "))
- << ", encryption level: "
- << QuicUtils::EncryptionLevelToString(level)
- << ", length:" << packet->length() << ", encrypted length:"
- << encrypted->length();
+ connection_close_packet_.reset(encrypted);
+ // This assures we won't try to write *forced* packets when blocked.
+ // Return true to stop processing.
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
+ return true;
+ }
+ } else {
+ encrypted_deleter.reset(encrypted);
+ }
+
+ LOG_IF(DFATAL, encrypted->length() >
+ packet_generator_.max_packet_length())
+ << "Writing an encrypted packet larger than max_packet_length:"
+ << packet_generator_.max_packet_length() << " encrypted length: "
+ << encrypted->length();
+ DVLOG(1) << ENDPOINT << "Sending packet " << sequence_number
+ << " : " << (packet.packet->is_fec_packet() ? "FEC " :
+ (packet.retransmittable == HAS_RETRANSMITTABLE_DATA
+ ? "data bearing " : " ack only "))
+ << ", encryption level: "
+ << QuicUtils::EncryptionLevelToString(packet.encryption_level)
+ << ", length:" << packet.packet->length() << ", encrypted length:"
+ << encrypted->length();
DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl
- << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece());
+ << QuicUtils::StringToHexASCIIDump(packet.packet->AsStringPiece());
- DCHECK(encrypted->length() <= kMaxPacketSize)
+ DCHECK(encrypted->length() <= kMaxPacketSize ||
+ FLAGS_quic_allow_oversized_packets_for_test)
<< "Packet " << sequence_number << " will not be read; too large: "
- << packet->length() << " " << encrypted->length() << " "
- << " forced: " << (forced == FORCE ? "yes" : "no");
+ << packet.packet->length() << " " << encrypted->length() << " "
+ << " close: " << (packet.type == CONNECTION_CLOSE ? "yes" : "no");
DCHECK(pending_write_.get() == NULL);
- pending_write_.reset(new PendingWrite(sequence_number, transmission_type,
- retransmittable, level,
- packet->is_fec_packet(),
- packet->length()));
-
- WriteResult result =
- writer_->WritePacket(encrypted->data(), encrypted->length(),
- self_address().address(), peer_address(), this);
+ pending_write_.reset(new QueuedPacket(packet));
+
+ WriteResult result = writer_->WritePacket(encrypted->data(),
+ encrypted->length(),
+ self_address().address(),
+ peer_address());
if (result.error_code == ERR_IO_PENDING) {
DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status);
}
if (debug_visitor_) {
// Pass the write result to the visitor.
- debug_visitor_->OnPacketSent(sequence_number, level, *encrypted, result);
+ debug_visitor_->OnPacketSent(sequence_number,
+ packet.encryption_level,
+ packet.transmission_type,
+ *encrypted,
+ result);
}
if (result.status == WRITE_STATUS_BLOCKED) {
- // TODO(satyashekhar): It might be more efficient (fewer system calls), if
- // all connections share this variable i.e this becomes a part of
- // PacketWriterInterface.
- write_blocked_ = true;
+ visitor_->OnWriteBlocked();
// 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. The helper must call OnPacketSent
// when the packet is actually sent.
if (writer_->IsWriteBlockedDataBuffered()) {
- delete packet;
return true;
}
pending_write_.reset();
@@ -1207,7 +1422,6 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
}
if (OnPacketSent(result)) {
- delete packet;
return true;
}
return false;
@@ -1219,7 +1433,15 @@ bool QuicConnection::ShouldDiscardPacket(
HasRetransmittableData retransmittable) {
if (!connected_) {
DVLOG(1) << ENDPOINT
- << "Not sending packet as connection is disconnected.";
+ << "Not sending packet as connection is disconnected.";
+ return true;
+ }
+
+ // If the packet has been discarded before sending, don't send it.
+ // This occurs if a packet gets serialized, queued, then discarded.
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Dropping packet before sending: "
+ << sequence_number << " since it has already been discarded.";
return true;
}
@@ -1227,43 +1449,20 @@ bool QuicConnection::ShouldDiscardPacket(
level == ENCRYPTION_NONE) {
// Drop packets that are NULL encrypted since the peer won't accept them
// anymore.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since the packet is NULL encrypted.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
+ DVLOG(1) << ENDPOINT << "Dropping NULL encrypted packet: "
+ << sequence_number << " since the connection is forward secure.";
+ LOG_IF(DFATAL,
+ sent_packet_manager_.HasRetransmittableFrames(sequence_number))
+ << "Once forward secure, all NULL encrypted packets should be "
+ << "neutered.";
return true;
}
- if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
- if (!sent_packet_manager_.IsUnacked(sequence_number)) {
- // This is a crazy edge case, but if we retransmit a packet,
- // (but have to queue it for some reason) then receive an ack
- // for the previous transmission (but not the retransmission)
- // then receive a truncated ACK which causes us to raise the
- // high water mark, all before we're able to send the packet
- // then we can simply drop it.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been acked.";
- return true;
- }
-
- if (sent_packet_manager_.IsPreviousTransmission(sequence_number)) {
- // If somehow we have already retransmitted this packet *before*
- // we actually send it for the first time (I think this is probably
- // impossible in the real world), then don't bother sending it.
- // We don't want to call DiscardUnackedPacket because in this case
- // the peer has not yet ACK'd the data. We need the subsequent
- // retransmission to be sent.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been retransmitted.";
- return true;
- }
-
- if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since a previous transmission has been acked.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
- return true;
- }
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA &&
+ !sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Dropping unacked packet: " << sequence_number
+ << " A previous transmission was acked while write blocked.";
+ return true;
}
return false;
@@ -1279,12 +1478,12 @@ bool QuicConnection::OnPacketSent(WriteResult result) {
QuicPacketSequenceNumber sequence_number = pending_write_->sequence_number;
TransmissionType transmission_type = pending_write_->transmission_type;
HasRetransmittableData retransmittable = pending_write_->retransmittable;
- bool is_fec_packet = pending_write_->is_fec_packet;
size_t length = pending_write_->length;
pending_write_.reset();
if (result.status == WRITE_STATUS_ERROR) {
- DVLOG(1) << "Write failed with error code: " << result.error_code;
+ DVLOG(1) << "Write failed with error: " << result.error_code << " ("
+ << ErrorToString(result.error_code) << ")";
// We can't send an error as the socket is presumably borked.
CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
return false;
@@ -1292,33 +1491,34 @@ bool QuicConnection::OnPacketSent(WriteResult result) {
QuicTime now = clock_->Now();
if (transmission_type == NOT_RETRANSMISSION) {
- time_of_last_sent_packet_ = now;
+ time_of_last_sent_new_packet_ = now;
}
+ SetPingAlarm();
DVLOG(1) << ENDPOINT << "time of last sent packet: "
<< now.ToDebuggingValue();
- // Set the retransmit alarm only when we have sent the packet to the client
- // and not when it goes to the pending queue, otherwise we will end up adding
- // an entry to retransmission_timeout_ every time we attempt a write.
- if (retransmittable == HAS_RETRANSMITTABLE_DATA || is_fec_packet) {
- SetupRetransmissionAlarm(sequence_number);
- }
-
// 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_.UpdateSequenceNumberLength(
+ packet_generator_.UpdateSequenceNumberLength(
received_packet_manager_.least_packet_awaited_by_peer(),
- sent_packet_manager_.BandwidthEstimate().ToBytesPerPeriod(
- sent_packet_manager_.SmoothedRtt()));
+ sent_packet_manager_.GetCongestionWindow());
+
+ bool reset_retransmission_alarm =
+ sent_packet_manager_.OnPacketSent(sequence_number, now, length,
+ transmission_type, retransmittable);
- sent_packet_manager_.OnPacketSent(sequence_number, now, length,
- transmission_type, retransmittable);
+ if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) {
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
+ }
+ }
stats_.bytes_sent += result.bytes_written;
++stats_.packets_sent;
- if (transmission_type == NACK_RETRANSMISSION ||
- transmission_type == RTO_RETRANSMISSION) {
+ if (transmission_type != NOT_RETRANSMISSION) {
stats_.bytes_retransmitted += result.bytes_written;
++stats_.packets_retransmitted;
}
@@ -1340,39 +1540,59 @@ bool QuicConnection::OnSerializedPacket(
NOT_RETRANSMISSION);
}
-QuicPacketSequenceNumber QuicConnection::GetNextPacketSequenceNumber() {
- return packet_creator_.sequence_number() + 1;
-}
-
bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
const SerializedPacket& packet,
TransmissionType transmission_type) {
- IsHandshake handshake = HasCryptoHandshake(packet.retransmittable_frames);
- Force forced = HasForcedFrames(packet.retransmittable_frames);
- HasRetransmittableData retransmittable =
- (transmission_type != NOT_RETRANSMISSION ||
- packet.retransmittable_frames != NULL) ?
- HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA;
+ if (packet.packet == NULL) {
+ LOG(DFATAL) << "NULL packet passed in to SendOrQueuePacket";
+ return true;
+ }
+
sent_entropy_manager_.RecordPacketEntropyHash(packet.sequence_number,
packet.entropy_hash);
- if (WritePacket(level, packet.sequence_number, packet.packet,
- transmission_type, retransmittable, handshake, forced)) {
+ QueuedPacket queued_packet(packet, level, transmission_type);
+ // If there are already queued packets, put this at the end,
+ // unless it's ConnectionClose, in which case it is written immediately.
+ if ((queued_packet.type == CONNECTION_CLOSE || queued_packets_.empty()) &&
+ WritePacket(queued_packet)) {
+ delete packet.packet;
return true;
}
- queued_packets_.push_back(QueuedPacket(packet.sequence_number, packet.packet,
- level, transmission_type,
- retransmittable, handshake, forced));
+ queued_packet.type = QUEUED;
+ queued_packets_.push_back(queued_packet);
return false;
}
-void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) {
- sent_info->least_unacked = sent_packet_manager_.GetLeastUnackedSentPacket();
- sent_info->entropy_hash = sent_entropy_manager_.EntropyHash(
- sent_info->least_unacked - 1);
+void QuicConnection::UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting) {
+ stop_waiting->least_unacked = GetLeastUnacked();
+ stop_waiting->entropy_hash = sent_entropy_manager_.EntropyHash(
+ stop_waiting->least_unacked - 1);
+}
+
+void QuicConnection::SendPing() {
+ if (retransmission_alarm_->IsSet()) {
+ return;
+ }
+ if (version() <= QUIC_VERSION_17) {
+ // TODO(rch): remove this when we remove version 17.
+ // This is a horrible hideous hack which we should not support.
+ IOVector data;
+ char c_data[] = "C";
+ data.Append(c_data, 1);
+ QuicConsumedData consumed_data =
+ packet_generator_.ConsumeData(kCryptoStreamId, data, 0, false,
+ MAY_FEC_PROTECT, NULL);
+ if (consumed_data.bytes_consumed == 0) {
+ DLOG(ERROR) << "Unable to send ping!?";
+ }
+ } else {
+ packet_generator_.AddControlFrame(QuicFrame(new QuicPingFrame));
+ }
}
void QuicConnection::SendAck() {
ack_alarm_->Cancel();
+ stop_waiting_count_ = 0;
// TODO(rch): delay this until the CreateFeedbackFrame
// method is invoked. This requires changes SetShouldSendAck
// to be a no-arg method, and re-jiggering its implementation.
@@ -1384,7 +1604,8 @@ void QuicConnection::SendAck() {
send_feedback = true;
}
- packet_generator_.SetShouldSendAck(send_feedback);
+ packet_generator_.SetShouldSendAck(send_feedback,
+ version() > QUIC_VERSION_15);
}
void QuicConnection::OnRetransmissionTimeout() {
@@ -1392,18 +1613,22 @@ void QuicConnection::OnRetransmissionTimeout() {
return;
}
- ++stats_.rto_count;
-
sent_packet_manager_.OnRetransmissionTimeout();
-
WriteIfNotBlocked();
+ // In the TLP case, the SentPacketManager gives the connection the opportunity
+ // to send new data before retransmitting.
+ if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) {
+ // Send the pending retransmission now that it's been queued.
+ WriteIfNotBlocked();
+ }
- // Ensure the retransmission alarm is always set if there are unacked packets.
- if (sent_packet_manager_.HasUnackedPackets() && !HasQueuedData() &&
- !retransmission_alarm_->IsSet()) {
- QuicTime rto_timeout = clock_->ApproximateNow().Add(
- sent_packet_manager_.GetRetransmissionDelay());
- retransmission_alarm_->Set(rto_timeout);
+ // Ensure the retransmission alarm is always set if there are unacked packets
+ // and nothing waiting to be sent.
+ if (!HasQueuedData() && !retransmission_alarm_->IsSet()) {
+ QuicTime rto_timeout = sent_packet_manager_.GetRetransmissionTime();
+ if (rto_timeout != QuicTime::Zero()) {
+ retransmission_alarm_->Set(rto_timeout);
+ }
}
}
@@ -1416,18 +1641,20 @@ const QuicEncrypter* QuicConnection::encrypter(EncryptionLevel level) const {
return framer_.encrypter(level);
}
-void QuicConnection::SetDefaultEncryptionLevel(
- EncryptionLevel level) {
+void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) {
encryption_level_ = level;
+ packet_generator_.set_encryption_level(level);
}
-void QuicConnection::SetDecrypter(QuicDecrypter* decrypter) {
- framer_.SetDecrypter(decrypter);
+void QuicConnection::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
+ framer_.SetDecrypter(decrypter, level);
}
void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used) {
- framer_.SetAlternativeDecrypter(decrypter, latch_once_used);
+ framer_.SetAlternativeDecrypter(decrypter, level, latch_once_used);
}
const QuicDecrypter* QuicConnection::decrypter() const {
@@ -1445,8 +1672,7 @@ void QuicConnection::QueueUndecryptablePacket(
}
void QuicConnection::MaybeProcessUndecryptablePackets() {
- if (undecryptable_packets_.empty() ||
- encryption_level_ == ENCRYPTION_NONE) {
+ if (undecryptable_packets_.empty() || encryption_level_ == ENCRYPTION_NONE) {
return;
}
@@ -1459,6 +1685,7 @@ void QuicConnection::MaybeProcessUndecryptablePackets() {
break;
}
DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ ++stats_.packets_processed;
delete packet;
undecryptable_packets_.pop_front();
}
@@ -1479,13 +1706,19 @@ void QuicConnection::MaybeProcessRevivedPacket() {
QuicPacketHeader revived_header;
char revived_payload[kMaxPacketSize];
size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize);
- revived_header.public_header.guid = guid_;
+ revived_header.public_header.connection_id = connection_id_;
+ revived_header.public_header.connection_id_length =
+ last_header_.public_header.connection_id_length;
revived_header.public_header.version_flag = false;
revived_header.public_header.reset_flag = false;
+ revived_header.public_header.sequence_number_length =
+ last_header_.public_header.sequence_number_length;
revived_header.fec_flag = false;
revived_header.is_in_fec_group = NOT_IN_FEC_GROUP;
revived_header.fec_group = 0;
group_map_.erase(last_header_.fec_group);
+ last_decrypted_packet_level_ = group->effective_encryption_level();
+ DCHECK_LT(last_decrypted_packet_level_, NUM_ENCRYPTION_LEVELS);
delete group;
last_packet_revived_ = true;
@@ -1526,27 +1759,36 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) {
void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
const string& details) {
- if (!write_blocked_) {
- SendConnectionClosePacket(error, details);
+ // If we're write blocked, WritePacket() will not send, but will capture the
+ // serialized packet.
+ SendConnectionClosePacket(error, details);
+ if (connected_) {
+ // It's possible that while sending the connection close packet, we get a
+ // socket error and disconnect right then and there. Avoid a double
+ // disconnect in that case.
+ CloseConnection(error, false);
}
- CloseConnection(error, false);
}
void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
const string& details) {
- DVLOG(1) << ENDPOINT << "Force closing " << guid() << " with error "
- << QuicUtils::ErrorToString(error) << " (" << error << ") "
- << details;
- ScopedPacketBundler ack_bundler(this, true);
+ DVLOG(1) << ENDPOINT << "Force closing " << connection_id()
+ << " with error " << QuicUtils::ErrorToString(error)
+ << " (" << error << ") " << details;
+ ScopedPacketBundler ack_bundler(this, SEND_ACK);
QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
frame->error_code = error;
frame->error_details = details;
packet_generator_.AddControlFrame(QuicFrame(frame));
- Flush();
+ packet_generator_.FlushAllQueuedFrames();
}
void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
- DCHECK(connected_);
+ if (!connected_) {
+ DLOG(DFATAL) << "Error: attempt to close an already closed connection"
+ << base::debug::StackTrace().ToString();
+ return;
+ }
connected_ = false;
visitor_->OnConnectionClosed(error, from_peer);
// Cancel the alarms so they don't trigger any action now that the
@@ -1562,11 +1804,11 @@ void QuicConnection::SendGoAway(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const string& reason) {
DVLOG(1) << ENDPOINT << "Going away with error "
- << QuicUtils::ErrorToString(error)
- << " (" << error << ")";
+ << QuicUtils::ErrorToString(error)
+ << " (" << error << ")";
// Opportunistically bundle an ack with this outgoing packet.
- ScopedPacketBundler ack_bundler(this, true);
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
packet_generator_.AddControlFrame(
QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason)));
}
@@ -1592,8 +1834,12 @@ void QuicConnection::CloseFecGroupsBefore(
}
}
-void QuicConnection::Flush() {
- packet_generator_.FlushAllQueuedFrames();
+size_t QuicConnection::max_packet_length() const {
+ return packet_generator_.max_packet_length();
+}
+
+void QuicConnection::set_max_packet_length(size_t length) {
+ return packet_generator_.set_max_packet_length(length);
}
bool QuicConnection::HasQueuedData() const {
@@ -1601,6 +1847,23 @@ bool QuicConnection::HasQueuedData() const {
!queued_packets_.empty() || packet_generator_.HasQueuedFrames();
}
+bool QuicConnection::CanWriteStreamData() {
+ // Don't write stream data if there are negotiation or queued data packets
+ // to send. Otherwise, continue and bundle as many frames as possible.
+ if (pending_version_negotiation_packet_ || !queued_packets_.empty()) {
+ return false;
+ }
+
+ 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.
+ return ShouldGeneratePacket(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
+ pending_handshake);
+}
+
void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) {
if (timeout < idle_network_timeout_) {
idle_network_timeout_ = timeout;
@@ -1621,8 +1884,8 @@ void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) {
bool QuicConnection::CheckForTimeout() {
QuicTime now = clock_->ApproximateNow();
- QuicTime time_of_last_packet = std::max(time_of_last_received_packet_,
- time_of_last_sent_packet_);
+ QuicTime time_of_last_packet = max(time_of_last_received_packet_,
+ time_of_last_sent_new_packet_);
// |delta| can be < 0 as |now| is approximate time but |time_of_last_packet|
// is accurate time. However, this should not change the behavior of
@@ -1643,7 +1906,8 @@ bool QuicConnection::CheckForTimeout() {
QuicTime::Delta timeout = idle_network_timeout_.Subtract(delta);
if (!overall_connection_timeout_.IsInfinite()) {
- QuicTime::Delta connected_time = now.Subtract(creation_time_);
+ QuicTime::Delta connected_time =
+ now.Subtract(stats_.connection_creation_time);
DVLOG(1) << ENDPOINT << "connection time: "
<< connected_time.ToMilliseconds() << " overall timeout: "
<< overall_connection_timeout_.ToMilliseconds();
@@ -1667,24 +1931,49 @@ bool QuicConnection::CheckForTimeout() {
return false;
}
+void QuicConnection::SetPingAlarm() {
+ if (is_server_) {
+ // Only clients send pings.
+ return;
+ }
+ ping_alarm_->Cancel();
+ if (!visitor_->HasOpenDataStreams()) {
+ // Don't send a ping unless there are open streams.
+ return;
+ }
+ QuicTime::Delta ping_timeout = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ ping_alarm_->Set(clock_->ApproximateNow().Add(ping_timeout));
+}
+
QuicConnection::ScopedPacketBundler::ScopedPacketBundler(
QuicConnection* connection,
- bool include_ack)
+ AckBundling send_ack)
: connection_(connection),
- already_in_batch_mode_(connection->packet_generator_.InBatchMode()) {
+ already_in_batch_mode_(connection != NULL &&
+ connection->packet_generator_.InBatchMode()) {
+ if (connection_ == NULL) {
+ return;
+ }
// Move generator into batch mode. If caller wants us to include an ack,
// check the delayed-ack timer to see if there's ack info to be sent.
if (!already_in_batch_mode_) {
DVLOG(1) << "Entering Batch Mode.";
connection_->packet_generator_.StartBatchOperations();
}
- if (include_ack && connection_->ack_alarm_->IsSet()) {
+ // Bundle an ack if the alarm is set or with every second packet if we need to
+ // raise the peer's least unacked.
+ bool ack_pending =
+ connection_->ack_alarm_->IsSet() || connection_->stop_waiting_count_ > 1;
+ if (send_ack == SEND_ACK || (send_ack == BUNDLE_PENDING_ACK && ack_pending)) {
DVLOG(1) << "Bundling ack with outgoing packet.";
connection_->SendAck();
}
}
QuicConnection::ScopedPacketBundler::~ScopedPacketBundler() {
+ if (connection_ == NULL) {
+ return;
+ }
// If we changed the generator's batch state, restore original batch state.
if (!already_in_batch_mode_) {
DVLOG(1) << "Leaving Batch Mode.";
diff --git a/chromium/net/quic/quic_connection.h b/chromium/net/quic/quic_connection.h
index beb8985d4d1..fec6c6882e5 100644
--- a/chromium/net/quic/quic_connection.h
+++ b/chromium/net/quic/quic_connection.h
@@ -40,9 +40,7 @@
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/quic_sent_entropy_manager.h"
#include "net/quic/quic_sent_packet_manager.h"
-
-NET_EXPORT_PRIVATE extern int FLAGS_fake_packet_loss_percentage;
-NET_EXPORT_PRIVATE extern bool FLAGS_bundle_ack_with_outgoing_packet;
+#include "net/quic/quic_types.h"
namespace net {
@@ -64,11 +62,18 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
public:
virtual ~QuicConnectionVisitorInterface() {}
- // A simple visitor interface for dealing with data frames. The session
- // 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 OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
+ // A simple visitor interface for dealing with data frames.
+ virtual void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
+
+ // The session should process all WINDOW_UPDATE frames, adjusting both stream
+ // and connection level flow control windows.
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) = 0;
+
+ // BLOCKED frames tell us that the peer believes it is flow control blocked on
+ // a specified stream. If the session at this end disagrees, something has
+ // gone wrong with our flow control accounting.
+ virtual void OnBlockedFrames(const std::vector<QuicBlockedFrame>& frames) = 0;
// Called when the stream is reset by the peer.
virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
@@ -78,85 +83,110 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// Called when the connection is closed either locally by the framer, or
// remotely by the peer.
- virtual void OnConnectionClosed(QuicErrorCode error,
- bool from_peer) = 0;
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) = 0;
+
+ // Called when the connection failed to write because the socket was blocked.
+ virtual void OnWriteBlocked() = 0;
// Called once a specific QUIC version is agreed by both endpoints.
virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
- // Indicates a new QuicConfig has been negotiated.
- virtual void OnConfigNegotiated() = 0;
+ // Called when a blocked socket becomes writable.
+ virtual void OnCanWrite() = 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 the visitor wants to schedule write resumption as it both
+ // has pending data to write, and is able to write (e.g. based on flow control
+ // limits).
+ // Writes may be pending because they were write-blocked, congestion-throttled
+ // or yielded to other connections.
+ virtual bool WillingAndAbleToWrite() const = 0;
// Called to ask if any handshake messages are pending in this visitor.
virtual bool HasPendingHandshake() const = 0;
+
+ // Called to ask if any streams are open in this visitor, excluding the
+ // reserved crypto and headers stream.
+ virtual bool HasOpenDataStreams() const = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
// points. Implementations must not mutate the state of the connection
// as a result of these callbacks.
-class NET_EXPORT_PRIVATE QuicConnectionDebugVisitorInterface
- : public QuicPacketGenerator::DebugDelegateInterface {
+class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor
+ : public QuicPacketGenerator::DebugDelegate,
+ public QuicSentPacketManager::DebugDelegate {
public:
- virtual ~QuicConnectionDebugVisitorInterface() {}
+ virtual ~QuicConnectionDebugVisitor() {}
// Called when a packet has been sent.
virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
- WriteResult result) = 0;
+ WriteResult result) {}
// Called when the contents of a packet have been retransmitted as
// a new packet.
virtual void OnPacketRetransmitted(
QuicPacketSequenceNumber old_sequence_number,
- QuicPacketSequenceNumber new_sequence_number) = 0;
+ QuicPacketSequenceNumber new_sequence_number) {}
// Called when a packet has been received, but before it is
// validated or parsed.
virtual void OnPacketReceived(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
- const QuicEncryptedPacket& packet) = 0;
+ const QuicEncryptedPacket& packet) {}
// Called when the protocol version on the received packet doensn't match
// current protocol version of the connection.
- virtual void OnProtocolVersionMismatch(QuicVersion version) = 0;
+ virtual void OnProtocolVersionMismatch(QuicVersion version) {}
// Called when the complete header of a packet has been parsed.
- virtual void OnPacketHeader(const QuicPacketHeader& header) = 0;
+ virtual void OnPacketHeader(const QuicPacketHeader& header) {}
// Called when a StreamFrame has been parsed.
- virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0;
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) {}
// Called when a AckFrame has been parsed.
- virtual void OnAckFrame(const QuicAckFrame& frame) = 0;
+ virtual void OnAckFrame(const QuicAckFrame& frame) {}
// Called when a CongestionFeedbackFrame has been parsed.
virtual void OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) = 0;
+ const QuicCongestionFeedbackFrame& frame) {}
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {}
+
+ // Called when a Ping has been parsed.
+ virtual void OnPingFrame(const QuicPingFrame& frame) {}
+
+ // Called when a GoAway has been parsed.
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {}
// Called when a RstStreamFrame has been parsed.
- virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {}
// Called when a ConnectionCloseFrame has been parsed.
virtual void OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) = 0;
+ const QuicConnectionCloseFrame& frame) {}
+
+ // Called when a WindowUpdate has been parsed.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {}
+
+ // Called when a BlockedFrame has been parsed.
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {}
// Called when a public reset packet has been received.
- virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0;
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {}
// Called when a version negotiation packet has been received.
virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) = 0;
+ const QuicVersionNegotiationPacket& packet) {}
// Called after a packet has been successfully parsed which results
// in the revival of a packet via FEC.
virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
- base::StringPiece payload) = 0;
+ base::StringPiece payload) {}
};
class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
@@ -178,17 +208,23 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
class NET_EXPORT_PRIVATE QuicConnection
: public QuicFramerVisitorInterface,
public QuicBlockedWriterInterface,
- public QuicPacketGenerator::DelegateInterface,
- public QuicSentPacketManager::HelperInterface {
+ public QuicPacketGenerator::DelegateInterface {
public:
- enum Force {
- NO_FORCE,
- FORCE
+ enum PacketType {
+ NORMAL,
+ QUEUED,
+ CONNECTION_CLOSE
};
- // Constructs a new QuicConnection for the specified |guid| and |address|.
+ enum AckBundling {
+ NO_ACK = 0,
+ SEND_ACK = 1,
+ BUNDLE_PENDING_ACK = 2,
+ };
+
+ // Constructs a new QuicConnection for |connection_id| and |address|.
// |helper| and |writer| must outlive this connection.
- QuicConnection(QuicGuid guid,
+ QuicConnection(QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelperInterface* helper,
QuicPacketWriter* writer,
@@ -203,7 +239,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// 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.
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
// If |delegate| is provided, then it will be informed once ACKs have been
// received for all the packets written in this call.
// The |delegate| is not owned by the QuicConnection and must outlive it.
@@ -211,11 +250,20 @@ class NET_EXPORT_PRIVATE QuicConnection
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* delegate);
- // Send a stream reset frame to the peer.
+ // Send a RST_STREAM frame to the peer.
virtual void SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Send a BLOCKED frame to the peer.
+ virtual void SendBlocked(QuicStreamId id);
+
+ // Send a WINDOW_UPDATE frame to the peer.
+ virtual void SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset);
// Sends the connection close packet without affecting the state of the
// connection. This should only be called if the session is actively being
@@ -246,15 +294,14 @@ class NET_EXPORT_PRIVATE QuicConnection
// QuicBlockedWriterInterface
// Called when the underlying connection becomes writable to allow queued
- // writes to happen. Returns false if the socket has become blocked.
- virtual bool OnCanWrite() OVERRIDE;
+ // writes to happen.
+ virtual void OnCanWrite() OVERRIDE;
// Called when a packet has been finally sent to the network.
bool OnPacketSent(WriteResult result);
- // If the socket is not blocked, this allows queued writes to happen. Returns
- // false if the socket has become blocked.
- bool WriteIfNotBlocked();
+ // If the socket is not blocked, writes queued packets.
+ void WriteIfNotBlocked();
// Do any work which logically would be done in OnPacket but can not be
// safely done until the packet is validated. Returns true if the packet
@@ -278,17 +325,24 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual void OnVersionNegotiationPacket(
const QuicVersionNegotiationPacket& packet) OVERRIDE;
virtual void OnRevivedPacket() OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE;
virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE;
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual bool OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
virtual void OnFecData(const QuicFecData& fec) OVERRIDE;
virtual void OnPacketComplete() OVERRIDE;
@@ -298,26 +352,25 @@ class NET_EXPORT_PRIVATE QuicConnection
IsHandshake handshake) OVERRIDE;
virtual QuicAckFrame* CreateAckFrame() OVERRIDE;
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE;
virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
- // QuicSentPacketManager::HelperInterface
- virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() OVERRIDE;
-
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
}
- void set_debug_visitor(QuicConnectionDebugVisitorInterface* debug_visitor) {
+ void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) {
debug_visitor_ = debug_visitor;
packet_generator_.set_debug_delegate(debug_visitor);
+ sent_packet_manager_.set_debug_delegate(debug_visitor);
}
const IPEndPoint& self_address() const { return self_address_; }
const IPEndPoint& peer_address() const { return peer_address_; }
- QuicGuid guid() const { return guid_; }
+ QuicConnectionId connection_id() const { return connection_id_; }
const QuicClock* clock() const { return clock_; }
QuicRandom* random_generator() const { return random_generator_; }
-
- QuicPacketCreator::Options* options() { return packet_creator_.options(); }
+ size_t max_packet_length() const;
+ void set_max_packet_length(size_t length);
bool connected() const { return connected_; }
@@ -336,9 +389,10 @@ class NET_EXPORT_PRIVATE QuicConnection
return connection_close_packet_.release();
}
- // 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 underlying UDP socket is writable, there is
+ // no queued data and the connection is not congestion-control
+ // blocked.
+ bool CanWriteStreamData();
// Returns true if the connection has queued packets or frames.
bool HasQueuedData() const;
@@ -356,6 +410,9 @@ class NET_EXPORT_PRIVATE QuicConnection
// true. Otherwise, it will return false and will reset the timeout alarm.
bool CheckForTimeout();
+ // Sends a ping, and resets the ping alarm.
+ void SendPing();
+
// Sets up a packet with an QuicAckFrame and sends it out.
void SendAck();
@@ -370,6 +427,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// initially encrypted packets when the initial encrypter changes.
void RetransmitUnackedPackets(RetransmissionType retransmission_type);
+ // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the
+ // connection becomes forward secure and hasn't received acks for all packets.
+ void NeuterUnencryptedPackets();
+
// Changes the encrypter used for level |level| to |encrypter|. The function
// takes ownership of |encrypter|.
void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
@@ -383,15 +444,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// and takes ownership. If an alternative decrypter is in place then the
// function DCHECKs. This is intended for cases where one knows that future
// packets will be using the new decrypter and the previous decrypter is now
- // obsolete.
- void SetDecrypter(QuicDecrypter* decrypter);
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
// SetAlternativeDecrypter sets a decrypter that may be used to decrypt
- // future packets and takes ownership of it. If |latch_once_used| is true,
- // then the first time that the decrypter is successful it will replace the
- // primary decrypter. Otherwise both decrypters will remain active and the
- // primary decrypter will be the one last used.
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used);
const QuicDecrypter* decrypter() const;
@@ -404,16 +467,34 @@ class NET_EXPORT_PRIVATE QuicConnection
return sent_packet_manager_;
}
- bool CanWrite(TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake);
+ bool CanWrite(HasRetransmittableData retransmittable);
+
+ // Stores current batch state for connection, puts the connection
+ // into batch mode, and destruction restores the stored batch state.
+ // While the bundler is in scope, any generated frames are bundled
+ // as densely as possible into packets. In addition, this bundler
+ // can be configured to ensure that an ACK frame is included in the
+ // first packet created, if there's new ack information to be sent.
+ class ScopedPacketBundler {
+ public:
+ // In addition to all outgoing frames being bundled when the
+ // bundler is in scope, setting |include_ack| to true ensures that
+ // an ACK frame is opportunistically bundled with the first
+ // outgoing packet.
+ ScopedPacketBundler(QuicConnection* connection, AckBundling send_ack);
+ ~ScopedPacketBundler();
+
+ private:
+ QuicConnection* connection_;
+ bool already_in_batch_mode_;
+ };
protected:
// Send a packet to the peer using encryption |level|. If |sequence_number|
// 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. Updates the
+ // Deletes |packet| if WritePacket call succeeds, or transfers ownership to
+ // QueuedPacket, ultimately deleted in WriteQueuedPackets. Updates the
// entropy map corresponding to |sequence_number| using |entropy_hash|.
// |transmission_type| and |retransmittable| are supplied to the congestion
// manager, and when |forced| is true, it bypasses the congestion manager.
@@ -422,25 +503,6 @@ class NET_EXPORT_PRIVATE QuicConnection
const SerializedPacket& packet,
TransmissionType transmission_type);
- // Writes the given packet to socket, encrypted with |level|, with the help
- // of helper. Returns true on successful write, false otherwise. However,
- // behavior is undefined if connection is not established or broken. In any
- // circumstances, a return value of true implies that |packet| has been
- // deleted and should not be accessed. If |sequence_number| is present in
- // |retransmission_map_| it also sets up retransmission of the given packet
- // in case of successful write. If |force| is FORCE, then the packet will be
- // sent immediately and the send scheduler will not be consulted.
- bool WritePacket(EncryptionLevel level,
- QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force force);
-
- // Make sure an ack we got from our peer is sane.
- bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
-
QuicConnectionHelperInterface* helper() { return helper_; }
// Selects and updates the version of the protocol being used by selecting a
@@ -448,50 +510,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// such a version exists, false otherwise.
bool SelectMutualVersion(const QuicVersionVector& available_versions);
- QuicFramer framer_;
+ QuicPacketWriter* writer() { return writer_; }
private:
- // Stores current batch state for connection, puts the connection
- // into batch mode, and destruction restores the stored batch state.
- // While the bundler is in scope, any generated frames are bundled
- // as densely as possible into packets. In addition, this bundler
- // can be configured to ensure that an ACK frame is included in the
- // first packet created, if there's new ack information to be sent.
- class ScopedPacketBundler {
- public:
- // In addition to all outgoing frames being bundled when the
- // bundler is in scope, setting |include_ack| to true ensures that
- // an ACK frame is opportunistically bundled with the first
- // outgoing packet.
- ScopedPacketBundler(QuicConnection* connection, bool include_ack);
- ~ScopedPacketBundler();
-
- private:
- QuicConnection* connection_;
- bool already_in_batch_mode_;
- };
-
- friend class ScopedPacketBundler;
friend class test::QuicConnectionPeer;
// Packets which have not been written to the wire.
// Owns the QuicPacket* packet.
struct QueuedPacket {
- QueuedPacket(QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
+ QueuedPacket(SerializedPacket packet,
EncryptionLevel level,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force forced)
- : sequence_number(sequence_number),
- packet(packet),
- encryption_level(level),
- transmission_type(transmission_type),
- retransmittable(retransmittable),
- handshake(handshake),
- forced(forced) {
- }
+ TransmissionType transmission_type);
QuicPacketSequenceNumber sequence_number;
QuicPacket* packet;
@@ -499,109 +528,37 @@ class NET_EXPORT_PRIVATE QuicConnection
TransmissionType transmission_type;
HasRetransmittableData retransmittable;
IsHandshake handshake;
- Force forced;
+ PacketType type;
+ QuicByteCount length;
};
- struct RetransmissionInfo {
- RetransmissionInfo(QuicPacketSequenceNumber sequence_number,
- QuicSequenceNumberLength sequence_number_length,
- QuicTime sent_time)
- : sequence_number(sequence_number),
- sequence_number_length(sequence_number_length),
- sent_time(sent_time),
- number_nacks(0),
- number_retransmissions(0) {
- }
-
- QuicPacketSequenceNumber sequence_number;
- QuicSequenceNumberLength sequence_number_length;
- QuicTime sent_time;
- size_t number_nacks;
- size_t number_retransmissions;
- };
-
- struct RetransmissionTime {
- RetransmissionTime(QuicPacketSequenceNumber sequence_number,
- const QuicTime& scheduled_time,
- bool for_fec)
- : sequence_number(sequence_number),
- scheduled_time(scheduled_time),
- for_fec(for_fec) { }
-
- QuicPacketSequenceNumber sequence_number;
- QuicTime scheduled_time;
- bool for_fec;
- };
+ typedef std::list<QueuedPacket> QueuedPacketList;
+ typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- struct PendingWrite {
- PendingWrite(QuicPacketSequenceNumber sequence_number,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- EncryptionLevel level,
- bool is_fec_packet,
- size_t length)
- : sequence_number(sequence_number),
- transmission_type(transmission_type),
- retransmittable(retransmittable),
- level(level),
- is_fec_packet(is_fec_packet),
- length(length) { }
+ // Writes the given packet to socket, encrypted with packet's
+ // encryption_level. Returns true on successful write. Behavior is undefined
+ // if connection is not established or broken. A return value of true means
+ // the packet was transmitted and may be destroyed. If the packet is
+ // retransmittable, WritePacket sets up retransmission for a successful write.
+ // If packet is FORCE, then it will be sent immediately and the send scheduler
+ // will not be consulted.
+ bool WritePacket(QueuedPacket packet);
- QuicPacketSequenceNumber sequence_number;
- TransmissionType transmission_type;
- HasRetransmittableData retransmittable;
- EncryptionLevel level;
- bool is_fec_packet;
- size_t length;
- };
-
- class RetransmissionTimeComparator {
- public:
- bool operator()(const RetransmissionTime& lhs,
- const RetransmissionTime& rhs) const {
- DCHECK(lhs.scheduled_time.IsInitialized() &&
- rhs.scheduled_time.IsInitialized());
- return lhs.scheduled_time > rhs.scheduled_time;
- }
- };
+ // Make sure an ack we got from our peer is sane.
+ bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
- typedef std::list<QueuedPacket> QueuedPacketList;
- typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- typedef std::priority_queue<RetransmissionTime,
- std::vector<RetransmissionTime>,
- RetransmissionTimeComparator>
- RetransmissionTimeouts;
+ // Make sure a stop waiting we got from our peer is sane.
+ bool ValidateStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
// Sends a version negotiation packet to the peer.
void SendVersionNegotiationPacket();
- void SetupRetransmissionAlarm(QuicPacketSequenceNumber sequence_number);
- bool IsRetransmission(QuicPacketSequenceNumber sequence_number);
-
- void SetupAbandonFecTimer(QuicPacketSequenceNumber sequence_number);
-
// Clears any accumulated frames from the last received packet.
void ClearLastFrames();
- // Called from OnCanWrite and WriteIfNotBlocked to write queued packets.
- // Returns false if the socket has become blocked.
- bool DoWrite();
-
- // Calculates the smallest sequence number length that can also represent four
- // times the maximum of the congestion window and the difference between the
- // least_packet_awaited_by_peer_ and |sequence_number|.
- QuicSequenceNumberLength CalculateSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number);
-
- // Drop packet corresponding to |sequence_number| by deleting entries from
- // |unacked_packets_| and |retransmission_map_|, if present. We need to drop
- // all packets with encryption level NONE after the default level has been set
- // to FORWARD_SECURE.
- void DropPacket(QuicPacketSequenceNumber sequence_number);
-
// Writes as many queued packets as possible. The connection must not be
// blocked when this is called.
- bool WriteQueuedPackets();
+ void WriteQueuedPackets();
// Writes as many pending retransmissions as possible.
void WritePendingRetransmissions();
@@ -624,16 +581,29 @@ class NET_EXPORT_PRIVATE QuicConnection
void ProcessAckFrame(const QuicAckFrame& incoming_ack);
- // Update the |sent_info| for an outgoing ack.
- void UpdateSentPacketInfo(SentPacketInfo* sent_info);
+ void ProcessStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
+
+ // Update |stop_waiting| for an outgoing ack.
+ void UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting);
+
+ // Queues an ack or sets the ack alarm when an incoming packet arrives that
+ // should be acked.
+ void MaybeQueueAck();
// Checks if the last packet should instigate an ack.
- bool ShouldLastPacketInstigateAck();
+ bool ShouldLastPacketInstigateAck() const;
+
+ // Checks if the peer is waiting for packets that have been given up on, and
+ // therefore an ack frame should be sent with a larger least_unacked.
+ void UpdateStopWaitingCount();
// Sends any packets which are a response to the last packet, including both
// acks and pending writes if an ack opened the congestion window.
- void MaybeSendInResponseToPacket(bool send_ack_immediately,
- bool last_packet_should_instigate_ack);
+ void MaybeSendInResponseToPacket();
+
+ // Gets the least unacked sequence number, which is the next sequence number
+ // to be sent if there are no outstanding packets.
+ QuicPacketSequenceNumber GetLeastUnacked() const;
// Get the FEC group associate with the last processed packet or NULL, if the
// group has already been deleted.
@@ -642,26 +612,41 @@ class NET_EXPORT_PRIVATE QuicConnection
// Closes any FEC groups protecting packets before |sequence_number|.
void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number);
+ // Sets the ping alarm to the appropriate value, if any.
+ void SetPingAlarm();
+
+ // On arrival of a new packet, checks to see if the socket addresses have
+ // changed since the last packet we saw on this connection.
+ void CheckForAddressMigration(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address);
+
+ QuicFramer framer_;
QuicConnectionHelperInterface* helper_; // Not owned.
QuicPacketWriter* writer_; // Not owned.
EncryptionLevel encryption_level_;
const QuicClock* clock_;
QuicRandom* random_generator_;
- const QuicGuid guid_;
+ const QuicConnectionId connection_id_;
// Address on the last successfully processed packet received from the
// client.
IPEndPoint self_address_;
IPEndPoint peer_address_;
+ // Used to store latest peer port to possibly migrate to later.
+ int migrating_peer_port_;
bool last_packet_revived_; // True if the last packet was revived from FEC.
size_t last_size_; // Size of the last received packet.
+ EncryptionLevel last_decrypted_packet_level_;
QuicPacketHeader last_header_;
std::vector<QuicStreamFrame> last_stream_frames_;
std::vector<QuicAckFrame> last_ack_frames_;
std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_;
+ std::vector<QuicStopWaitingFrame> last_stop_waiting_frames_;
std::vector<QuicRstStreamFrame> last_rst_frames_;
std::vector<QuicGoAwayFrame> last_goaway_frames_;
+ std::vector<QuicWindowUpdateFrame> last_window_update_frames_;
+ std::vector<QuicBlockedFrame> last_blocked_frames_;
std::vector<QuicConnectionCloseFrame> last_close_frames_;
QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
@@ -670,6 +655,9 @@ 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_;
+ // Largest sequence number sent by the peer which had a stop waiting frame.
+ QuicPacketSequenceNumber largest_seen_packet_with_stop_waiting_;
+
// 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
@@ -686,19 +674,22 @@ class NET_EXPORT_PRIVATE QuicConnection
QueuedPacketList queued_packets_;
// Contains information about the current write in progress, if any.
- scoped_ptr<PendingWrite> pending_write_;
+ scoped_ptr<QueuedPacket> pending_write_;
// Contains the connection close packet if the connection has been closed.
scoped_ptr<QuicEncryptedPacket> connection_close_packet_;
- // True when the socket becomes unwritable.
- bool write_blocked_;
-
FecGroupMap group_map_;
QuicReceivedPacketManager received_packet_manager_;
QuicSentEntropyManager sent_entropy_manager_;
+ // Indicates whether an ack should be sent the next time we try to write.
+ bool ack_queued_;
+ // Indicates how many consecutive times an ack has arrived which indicates
+ // the peer needs to stop waiting for some packets.
+ int stop_waiting_count_;
+
// An alarm that fires when an ACK should be sent to the peer.
scoped_ptr<QuicAlarm> ack_alarm_;
// An alarm that fires when a packet needs to be retransmitted.
@@ -711,18 +702,17 @@ class NET_EXPORT_PRIVATE QuicConnection
scoped_ptr<QuicAlarm> resume_writes_alarm_;
// An alarm that fires when the connection may have timed out.
scoped_ptr<QuicAlarm> timeout_alarm_;
+ // An alarm that fires when a ping should be sent.
+ scoped_ptr<QuicAlarm> ping_alarm_;
QuicConnectionVisitorInterface* visitor_;
- QuicConnectionDebugVisitorInterface* debug_visitor_;
- QuicPacketCreator packet_creator_;
+ QuicConnectionDebugVisitor* debug_visitor_;
QuicPacketGenerator packet_generator_;
// Network idle time before we kill of this connection.
QuicTime::Delta idle_network_timeout_;
// Overall connection timeout.
QuicTime::Delta overall_connection_timeout_;
- // Connection creation time.
- QuicTime creation_time_;
// Statistics for this session.
QuicConnectionStats stats_;
@@ -731,13 +721,13 @@ class NET_EXPORT_PRIVATE QuicConnection
// This is used for timeouts, and does not indicate the packet was processed.
QuicTime time_of_last_received_packet_;
- // The time that we last sent a packet for this connection.
- QuicTime time_of_last_sent_packet_;
+ // The last time a new (non-retransmitted) packet was sent for this
+ // connection.
+ QuicTime time_of_last_sent_new_packet_;
- // Sequence number of the last packet guaranteed to be sent in packet sequence
- // number order. Not set when packets are queued, since that may cause
- // re-ordering.
- QuicPacketSequenceNumber sequence_number_of_last_inorder_packet_;
+ // Sequence number of the last sent packet. Packets are guaranteed to be sent
+ // in sequence number order.
+ QuicPacketSequenceNumber sequence_number_of_last_sent_packet_;
// Sent packet manager which tracks the status of packets sent by this
// connection and contains the send and receive algorithms to determine when
@@ -754,14 +744,21 @@ class NET_EXPORT_PRIVATE QuicConnection
// close.
bool connected_;
- // Set to true if the udp packet headers have a new self or peer address.
- // This is checked later on validating a data or version negotiation packet.
- bool address_migrating_;
+ // Set to true if the UDP packet headers have a new IP address for the peer.
+ // If true, do not perform connection migration.
+ bool peer_ip_changed_;
+
+ // Set to true if the UDP packet headers have a new port for the peer.
+ // If true, and the IP has not changed, then we can migrate the connection.
+ bool peer_port_changed_;
+
+ // Set to true if the UDP packet headers are addressed to a different IP.
+ // We do not support connection migration when the self IP changed.
+ bool self_ip_changed_;
- // An AckNotifier can register to be informed when ACKs have been received for
- // all packets that a given block of data was sent in. The AckNotifierManager
- // maintains the currently active notifiers.
- AckNotifierManager ack_notifier_manager_;
+ // Set to true if the UDP packet headers are addressed to a different port.
+ // We do not support connection migration when the self port changed.
+ bool self_port_changed_;
// If non-empty this contains the set of versions received in a
// version negotiation packet.
diff --git a/chromium/net/quic/quic_connection_helper.h b/chromium/net/quic/quic_connection_helper.h
index 7098039c9eb..fa866f296af 100644
--- a/chromium/net/quic/quic_connection_helper.h
+++ b/chromium/net/quic/quic_connection_helper.h
@@ -12,6 +12,7 @@
#include <set>
+#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/quic_protocol.h"
@@ -33,7 +34,6 @@ class NET_EXPORT_PRIVATE QuicConnectionHelper
QuicConnectionHelper(base::TaskRunner* task_runner,
const QuicClock* clock,
QuicRandom* random_generator);
-
virtual ~QuicConnectionHelper();
// QuicConnectionHelperInterface
diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc
index 174d99a58c6..6ac4f5563d9 100644
--- a/chromium/net/quic/quic_connection_logger.cc
+++ b/chromium/net/quic/quic_connection_logger.cc
@@ -4,6 +4,9 @@
#include "net/quic/quic_connection_logger.h"
+#include <algorithm>
+#include <string>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/histogram.h"
@@ -11,14 +14,24 @@
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "net/base/net_log.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/base/net_util.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_address_mismatch.h"
+#include "net/quic/quic_socket_address_coder.h"
+using base::StringPiece;
using std::string;
namespace net {
namespace {
+// We have ranges-of-buckets in the cumulative histogram (covering 21 packet
+// sequences) of length 2, 3, 4, ... 22.
+// Hence the largest sample is bounded by the sum of those numbers.
+const int kBoundingSampleInCumulativeHistogram = ((2 + 22) * 21) / 2;
+
base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address,
const IPEndPoint* peer_address,
size_t packet_size,
@@ -33,11 +46,13 @@ base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address,
base::Value* NetLogQuicPacketSentCallback(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
size_t packet_size,
WriteResult result,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("encryption_level", level);
+ dict->SetInteger("transmission_type", transmission_type);
dict->SetString("packet_sequence_number",
base::Uint64ToString(sequence_number));
dict->SetInteger("size", packet_size);
@@ -51,19 +66,19 @@ base::Value* NetLogQuicPacketRetransmittedCallback(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number,
NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("old_packet_sequence_number",
- base::Uint64ToString(old_sequence_number));
- dict->SetString("new_packet_sequence_number",
- base::Uint64ToString(new_sequence_number));
- return dict;
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("old_packet_sequence_number",
+ base::Uint64ToString(old_sequence_number));
+ dict->SetString("new_packet_sequence_number",
+ base::Uint64ToString(new_sequence_number));
+ return dict;
}
base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("guid",
- base::Uint64ToString(header->public_header.guid));
+ dict->SetString("connection_id",
+ base::Uint64ToString(header->public_header.connection_id));
dict->SetInteger("reset_flag", header->public_header.reset_flag);
dict->SetInteger("version_flag", header->public_header.version_flag);
dict->SetString("packet_sequence_number",
@@ -87,15 +102,12 @@ base::Value* NetLogQuicStreamFrameCallback(const QuicStreamFrame* frame,
base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
- base::DictionaryValue* sent_info = new base::DictionaryValue();
- dict->Set("sent_info", sent_info);
- sent_info->SetString("least_unacked",
- base::Uint64ToString(frame->sent_info.least_unacked));
base::DictionaryValue* received_info = new base::DictionaryValue();
dict->Set("received_info", received_info);
received_info->SetString(
"largest_observed",
base::Uint64ToString(frame->received_info.largest_observed));
+ received_info->SetBoolean("truncated", frame->received_info.is_truncated);
base::ListValue* missing = new base::ListValue();
received_info->Set("missing_packets", missing);
const SequenceNumberSet& missing_packets =
@@ -114,14 +126,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback(
switch (frame->type) {
case kInterArrival: {
dict->SetString("type", "InterArrival");
- dict->SetInteger("accumulated_number_of_lost_packets",
- frame->inter_arrival.accumulated_number_of_lost_packets);
base::ListValue* received = new base::ListValue();
dict->Set("received_packets", received);
for (TimeMap::const_iterator it =
frame->inter_arrival.received_packet_times.begin();
it != frame->inter_arrival.received_packet_times.end(); ++it) {
- std::string value = base::Uint64ToString(it->first) + "@" +
+ string value = base::Uint64ToString(it->first) + "@" +
base::Uint64ToString(it->second.ToDebuggingValue());
received->AppendString(value);
}
@@ -134,10 +144,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback(
break;
case kTCP:
dict->SetString("type", "TCP");
- dict->SetInteger("accumulated_number_of_lost_packets",
- frame->tcp.accumulated_number_of_lost_packets);
dict->SetInteger("receive_window", frame->tcp.receive_window);
break;
+ case kTCPBBR:
+ dict->SetString("type", "TCPBBR");
+ // TODO(rtenneti): Add support for BBR.
+ break;
}
return dict;
@@ -162,6 +174,44 @@ base::Value* NetLogQuicConnectionCloseFrameCallback(
return dict;
}
+base::Value* NetLogQuicWindowUpdateFrameCallback(
+ const QuicWindowUpdateFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset));
+ return dict;
+}
+
+base::Value* NetLogQuicBlockedFrameCallback(
+ const QuicBlockedFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ return dict;
+}
+
+base::Value* NetLogQuicGoAwayFrameCallback(
+ const QuicGoAwayFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("quic_error", frame->error_code);
+ dict->SetInteger("last_good_stream_id", frame->last_good_stream_id);
+ dict->SetString("reason_phrase", frame->reason_phrase);
+ return dict;
+}
+
+base::Value* NetLogQuicStopWaitingFrameCallback(
+ const QuicStopWaitingFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::DictionaryValue* sent_info = new base::DictionaryValue();
+ dict->Set("sent_info", sent_info);
+ sent_info->SetString("least_unacked",
+ base::Uint64ToString(frame->least_unacked));
+ return dict;
+}
+
base::Value* NetLogQuicVersionNegotiationPacketCallback(
const QuicVersionNegotiationPacket* packet,
NetLog::LogLevel /* log_level */) {
@@ -198,19 +248,111 @@ void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) {
num_consecutive_missing_packets);
}
+void UpdatePublicResetAddressMismatchHistogram(
+ const IPEndPoint& server_hello_address,
+ const IPEndPoint& public_reset_address) {
+ int sample = GetAddressMismatch(server_hello_address, public_reset_address);
+ // We are seemingly talking to an older server that does not support the
+ // feature, so we can't report the results in the histogram.
+ if (sample < 0) {
+ return;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PublicResetAddressMismatch2",
+ sample, QUIC_ADDRESS_MISMATCH_MAX);
+}
+
+const char* GetConnectionDescriptionString() {
+ NetworkChangeNotifier::ConnectionType type =
+ NetworkChangeNotifier::GetConnectionType();
+ const char* description = NetworkChangeNotifier::ConnectionTypeToString(type);
+ // Most platforms don't distingish Wifi vs Etherenet, and call everything
+ // CONNECTION_UNKNOWN :-(. We'll tease out some details when we are on WiFi,
+ // and hopefully leave only ethernet (with no WiFi available) in the
+ // CONNECTION_UNKNOWN category. This *might* err if there is both ethernet,
+ // as well as WiFi, where WiFi was not being used that much.
+ // This function only seems usefully defined on Windows currently.
+ if (type == NetworkChangeNotifier::CONNECTION_UNKNOWN ||
+ type == NetworkChangeNotifier::CONNECTION_WIFI) {
+ WifiPHYLayerProtocol wifi_type = GetWifiPHYLayerProtocol();
+ switch (wifi_type) {
+ case WIFI_PHY_LAYER_PROTOCOL_NONE:
+ // No wifi support or no associated AP.
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
+ // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS.
+ description = "CONNECTION_WIFI_ANCIENT";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_A:
+ // 802.11a, OFDM-based rates.
+ description = "CONNECTION_WIFI_802.11a";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_B:
+ // 802.11b, DSSS or HR DSSS.
+ description = "CONNECTION_WIFI_802.11b";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_G:
+ // 802.11g, same rates as 802.11a but compatible with 802.11b.
+ description = "CONNECTION_WIFI_802.11g";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_N:
+ // 802.11n, HT rates.
+ description = "CONNECTION_WIFI_802.11n";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
+ // Unclassified mode or failure to identify.
+ break;
+ }
+ }
+ return description;
+}
+
+// If |address| is an IPv4-mapped IPv6 address, returns ADDRESS_FAMILY_IPV4
+// instead of ADDRESS_FAMILY_IPV6. Othewise, behaves like GetAddressFamily().
+AddressFamily GetRealAddressFamily(const IPAddressNumber& address) {
+ return IsIPv4Mapped(address) ? ADDRESS_FAMILY_IPV4 :
+ GetAddressFamily(address);
+}
+
} // namespace
QuicConnectionLogger::QuicConnectionLogger(const BoundNetLog& net_log)
: net_log_(net_log),
last_received_packet_sequence_number_(0),
+ last_received_packet_size_(0),
largest_received_packet_sequence_number_(0),
largest_received_missing_packet_sequence_number_(0),
- out_of_order_recieved_packet_count_(0) {
+ num_out_of_order_received_packets_(0),
+ num_packets_received_(0),
+ num_truncated_acks_sent_(0),
+ num_truncated_acks_received_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0),
+ connection_description_(GetConnectionDescriptionString()) {
}
QuicConnectionLogger::~QuicConnectionLogger() {
UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderPacketsReceived",
- out_of_order_recieved_packet_count_);
+ num_out_of_order_received_packets_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksSent",
+ num_truncated_acks_sent_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksReceived",
+ num_truncated_acks_received_);
+ if (num_frames_received_ > 0) {
+ int duplicate_stream_frame_per_thousand =
+ num_duplicate_frames_received_ * 1000 / num_frames_received_;
+ if (num_packets_received_ < 100) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedShortConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedLongConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+
+ }
+ }
+
+ RecordLossHistograms();
}
void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
@@ -226,6 +368,8 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_ACK_FRAME_SENT,
base::Bind(&NetLogQuicAckFrameCallback, frame.ack_frame));
+ if (frame.ack_frame->received_info.is_truncated)
+ ++num_truncated_acks_sent_;
break;
case CONGESTION_FEEDBACK_FRAME:
net_log_.AddEvent(
@@ -248,6 +392,32 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
frame.connection_close_frame));
break;
case GOAWAY_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_SENT,
+ base::Bind(&NetLogQuicGoAwayFrameCallback,
+ frame.goaway_frame));
+ break;
+ case WINDOW_UPDATE_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_SENT,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback,
+ frame.window_update_frame));
+ break;
+ case BLOCKED_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_SENT,
+ base::Bind(&NetLogQuicBlockedFrameCallback,
+ frame.blocked_frame));
+ break;
+ case STOP_WAITING_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_SENT,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback,
+ frame.stop_waiting_frame));
+ break;
+ case PING_FRAME:
+ // PingFrame has no contents to log, so just record that it was sent.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_SENT);
break;
default:
DCHECK(false) << "Illegal frame type: " << frame.type;
@@ -257,12 +427,13 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
void QuicConnectionLogger::OnPacketSent(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
WriteResult result) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_SENT,
base::Bind(&NetLogQuicPacketSentCallback, sequence_number, level,
- packet.length(), result));
+ transmission_type, packet.length(), result));
}
void QuicConnectionLogger:: OnPacketRetransmitted(
@@ -277,6 +448,14 @@ void QuicConnectionLogger:: OnPacketRetransmitted(
void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
const QuicEncryptedPacket& packet) {
+ if (local_address_from_self_.GetFamily() == ADDRESS_FAMILY_UNSPECIFIED) {
+ local_address_from_self_ = self_address;
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromSelf",
+ GetRealAddressFamily(self_address.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+
+ last_received_packet_size_ = packet.length();
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED,
base::Bind(&NetLogQuicPacketCallback, &self_address, &peer_address,
@@ -292,6 +471,7 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED,
base::Bind(&NetLogQuicPacketHeaderCallback, &header));
+ ++num_packets_received_;
if (largest_received_packet_sequence_number_ <
header.packet_sequence_number) {
QuicPacketSequenceNumber delta = header.packet_sequence_number -
@@ -304,8 +484,10 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) {
}
largest_received_packet_sequence_number_ = header.packet_sequence_number;
}
+ if (header.packet_sequence_number < received_packets_.size())
+ received_packets_[header.packet_sequence_number] = true;
if (header.packet_sequence_number < last_received_packet_sequence_number_) {
- ++out_of_order_recieved_packet_count_;
+ ++num_out_of_order_received_packets_;
UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderGapReceived",
last_received_packet_sequence_number_ -
header.packet_sequence_number);
@@ -324,6 +506,14 @@ void QuicConnectionLogger::OnAckFrame(const QuicAckFrame& frame) {
NetLog::TYPE_QUIC_SESSION_ACK_FRAME_RECEIVED,
base::Bind(&NetLogQuicAckFrameCallback, &frame));
+ const size_t kApproximateLargestSoloAckBytes = 100;
+ if (last_received_packet_sequence_number_ < received_acks_.size() &&
+ last_received_packet_size_ < kApproximateLargestSoloAckBytes)
+ received_acks_[last_received_packet_sequence_number_] = true;
+
+ if (frame.received_info.is_truncated)
+ ++num_truncated_acks_received_;
+
if (frame.received_info.missing_packets.empty())
return;
@@ -367,6 +557,13 @@ void QuicConnectionLogger::OnCongestionFeedbackFrame(
base::Bind(&NetLogQuicCongestionFeedbackFrameCallback, &frame));
}
+void QuicConnectionLogger::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback, &frame));
+}
+
void QuicConnectionLogger::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.RstStreamErrorCodeServer",
frame.error_code);
@@ -382,9 +579,35 @@ void QuicConnectionLogger::OnConnectionCloseFrame(
base::Bind(&NetLogQuicConnectionCloseFrameCallback, &frame));
}
+void QuicConnectionLogger::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicBlockedFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicGoAwayFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnPingFrame(const QuicPingFrame& frame) {
+ // PingFrame has no contents to log, so just record that it was received.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_RECEIVED);
+}
+
void QuicConnectionLogger::OnPublicResetPacket(
const QuicPublicResetPacket& packet) {
net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED);
+ UpdatePublicResetAddressMismatchHistogram(local_address_from_shlo_,
+ packet.client_address);
}
void QuicConnectionLogger::OnVersionNegotiationPacket(
@@ -407,6 +630,19 @@ void QuicConnectionLogger::OnCryptoHandshakeMessageReceived(
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_RECEIVED,
base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message));
+
+ if (message.tag() == kSHLO) {
+ StringPiece address;
+ QuicSocketAddressCoder decoder;
+ if (message.GetStringPiece(kCADR, &address) &&
+ decoder.Decode(address.data(), address.size())) {
+ local_address_from_shlo_ = IPEndPoint(decoder.ip(), decoder.port());
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromPeer",
+ GetRealAddressFamily(
+ local_address_from_shlo_.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+ }
}
void QuicConnectionLogger::OnCryptoHandshakeMessageSent(
@@ -430,4 +666,172 @@ void QuicConnectionLogger::OnSuccessfulVersionNegotiation(
NetLog::StringCallback("version", &quic_version));
}
+void QuicConnectionLogger::UpdateReceivedFrameCounts(
+ QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received) {
+ if (stream_id != kCryptoStreamId) {
+ num_frames_received_ += num_frames_received;
+ num_duplicate_frames_received_ += num_duplicate_frames_received;
+ }
+}
+
+base::HistogramBase* QuicConnectionLogger::GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const {
+ string prefix("Net.QuicSession.PacketReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + statistic_name + connection_description_,
+ 1, received_packets_.size(), received_packets_.size() + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get6PacketHistogram(
+ const char* which_6) const {
+ // This histogram takes a binary encoding of the 6 consecutive packets
+ // received. As a result, there are 64 possible sample-patterns.
+ string prefix("Net.QuicSession.6PacketsPatternsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_6 + connection_description_, 1, 64, 65,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get21CumulativeHistogram(
+ const char* which_21) const {
+ // This histogram contains, for each sequence of 21 packets, the results from
+ // 21 distinct questions about that sequence. Conceptually the histogtram is
+ // broken into 21 distinct ranges, and one sample is added into each of those
+ // ranges whenever we process a set of 21 packets.
+ // There is a little rendundancy, as each "range" must have the same number
+ // of samples, all told, but the histogram is a tad easier to read this way.
+ // The questions are:
+ // Was the first packet present (bucket 0==>no; bucket 1==>yes)
+ // Of the first two packets, how many were present? (bucket 2==> none;
+ // bucket 3==> 1 of 2; bucket 4==> 2 of 2)
+ // Of the first three packets, how many were present? (bucket 5==>none;
+ // bucket 6==> 1 of 3; bucket 7==> 2 of 3; bucket 8==> 3 of 3).
+ // etc.
+ string prefix("Net.QuicSession.21CumulativePacketsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_21 + connection_description_,
+ 1, kBoundingSampleInCumulativeHistogram,
+ kBoundingSampleInCumulativeHistogram + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+// static
+void QuicConnectionLogger::AddTo21CumulativeHistogram(
+ base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask) {
+ DCHECK_LE(valid_bits_in_mask, 21);
+ DCHECK_LT(bit_mask_of_packets, 1 << 21);
+ const int blank_bits_in_mask = 21 - valid_bits_in_mask;
+ DCHECK_EQ(bit_mask_of_packets & ((1 << blank_bits_in_mask) - 1), 0);
+ bit_mask_of_packets >>= blank_bits_in_mask;
+ int bits_so_far = 0;
+ int range_start = 0;
+ for (int i = 1; i <= valid_bits_in_mask; ++i) {
+ bits_so_far += bit_mask_of_packets & 1;
+ bit_mask_of_packets >>= 1;
+ DCHECK_LT(range_start + bits_so_far, kBoundingSampleInCumulativeHistogram);
+ histogram->Add(range_start + bits_so_far);
+ range_start += i + 1;
+ }
+}
+
+void QuicConnectionLogger::RecordAggregatePacketLossRate() const {
+ // For short connections under 22 packets in length, we'll rely on the
+ // Net.QuicSession.21CumulativePacketsReceived_* histogram to indicate packet
+ // loss rates. This way we avoid tremendously anomalous contributions to our
+ // histogram. (e.g., if we only got 5 packets, but lost 1, we'd otherwise
+ // record a 20% loss in this histogram!). We may still get some strange data
+ // (1 loss in 22 is still high :-/).
+ if (largest_received_packet_sequence_number_ <= 21)
+ return;
+
+ QuicPacketSequenceNumber divisor = largest_received_packet_sequence_number_;
+ QuicPacketSequenceNumber numerator = divisor - num_packets_received_;
+ if (divisor < 100000)
+ numerator *= 1000;
+ else
+ divisor /= 1000;
+ string prefix("Net.QuicSession.PacketLossRate_");
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ prefix + connection_description_, 1, 1000, 75,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(numerator / divisor);
+}
+
+void QuicConnectionLogger::RecordLossHistograms() const {
+ if (largest_received_packet_sequence_number_ == 0)
+ return; // Connection was never used.
+ RecordAggregatePacketLossRate();
+
+ base::HistogramBase* is_not_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsNotAck_");
+ base::HistogramBase* is_an_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsAnAck_");
+ base::HistogramBase* packet_arrived_histogram =
+ GetPacketSequenceNumberHistogram("Ack_");
+ base::HistogramBase* packet_missing_histogram =
+ GetPacketSequenceNumberHistogram("Nack_");
+ base::HistogramBase* ongoing_cumulative_packet_histogram =
+ Get21CumulativeHistogram("Some21s_");
+ base::HistogramBase* first_cumulative_packet_histogram =
+ Get21CumulativeHistogram("First21_");
+ base::HistogramBase* six_packet_histogram = Get6PacketHistogram("Some6s_");
+
+ DCHECK_EQ(received_packets_.size(), received_acks_.size());
+ const QuicPacketSequenceNumber last_index =
+ std::min<QuicPacketSequenceNumber>(received_packets_.size() - 1,
+ largest_received_packet_sequence_number_);
+ const QuicPacketSequenceNumber index_of_first_21_contribution =
+ std::min<QuicPacketSequenceNumber>(21, last_index);
+ // Bit pattern of consecutively received packets that is maintained as we scan
+ // through the received_packets_ vector. Less significant bits correspond to
+ // less recent packets, and only the low order 21 bits are ever defined.
+ // Bit is 1 iff corresponding packet was received.
+ int packet_pattern_21 = 0;
+ // Zero is an invalid packet sequence number.
+ DCHECK(!received_packets_[0]);
+ for (size_t i = 1; i <= last_index; ++i) {
+ if (received_acks_[i])
+ is_an_ack_histogram->Add(i);
+ else
+ is_not_ack_histogram->Add(i);
+
+ packet_pattern_21 >>= 1;
+ if (received_packets_[i]) {
+ packet_arrived_histogram->Add(i);
+ packet_pattern_21 |= (1 << 20); // Turn on the 21st bit.
+ } else {
+ packet_missing_histogram->Add(i);
+ }
+
+ if (i == index_of_first_21_contribution) {
+ AddTo21CumulativeHistogram(first_cumulative_packet_histogram,
+ packet_pattern_21, i);
+ }
+ // We'll just record for non-overlapping ranges, to reduce histogramming
+ // cost for now. Each call does 21 separate histogram additions.
+ if (i > 21 || i % 21 == 0) {
+ AddTo21CumulativeHistogram(ongoing_cumulative_packet_histogram,
+ packet_pattern_21, 21);
+ }
+
+ if (i < 6)
+ continue; // Not enough packets to do any pattern recording.
+ int recent_6_mask = packet_pattern_21 >> 15;
+ DCHECK_LT(recent_6_mask, 64);
+ if (i == 6) {
+ Get6PacketHistogram("First6_")->Add(recent_6_mask);
+ continue;
+ }
+ // Record some overlapping patterns, to get a better picture, since this is
+ // not very expensive.
+ if (i % 3 == 0)
+ six_packet_histogram->Add(recent_6_mask);
+ }
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h
index d162282204a..2110f9ef6d3 100644
--- a/chromium/net/quic/quic_connection_logger.h
+++ b/chromium/net/quic/quic_connection_logger.h
@@ -5,18 +5,22 @@
#ifndef NET_QUIC_QUIC_CONNECTION_LOGGER_H_
#define NET_QUIC_QUIC_CONNECTION_LOGGER_H_
+#include <bitset>
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_log.h"
+#include "net/base/network_change_notifier.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_protocol.h"
namespace net {
-class BoundNetLog;
class CryptoHandshakeMessage;
// This class is a debug visitor of a QuicConnection which logs
// events to |net_log|.
class NET_EXPORT_PRIVATE QuicConnectionLogger
- : public QuicConnectionDebugVisitorInterface {
+ : public QuicConnectionDebugVisitor {
public:
explicit QuicConnectionLogger(const BoundNetLog& net_log);
@@ -28,6 +32,7 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
// QuicConnectionDebugVisitorInterface
virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
WriteResult result) OVERRIDE;
virtual void OnPacketRetransmitted(
@@ -42,9 +47,14 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual void OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual void OnPublicResetPacket(
const QuicPublicResetPacket& packet) OVERRIDE;
virtual void OnVersionNegotiationPacket(
@@ -58,20 +68,87 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
const CryptoHandshakeMessage& message);
void OnConnectionClosed(QuicErrorCode error, bool from_peer);
void OnSuccessfulVersionNegotiation(const QuicVersion& version);
+ void UpdateReceivedFrameCounts(QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received);
private:
+ // Do a factory get for a histogram for recording data, about individual
+ // packet sequence numbers, that was gathered in the vectors
+ // received_packets_ and received_acks_. |statistic_name| identifies which
+ // element of data is recorded, and is used to form the histogram name.
+ base::HistogramBase* GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const;
+ // Do a factory get for a histogram to record a 6-packet loss-sequence as a
+ // sample. The histogram will record the 64 distinct possible combinations.
+ // |which_6| is used to adjust the name of the histogram to distinguish the
+ // first 6 packets in a connection, vs. some later 6 packets.
+ base::HistogramBase* Get6PacketHistogram(const char* which_6) const;
+ // Do a factory get for a histogram to record cumulative stats across a 21
+ // packet sequence. |which_21| is used to adjust the name of the histogram
+ // to distinguish the first 21 packets' loss data, vs. some later 21 packet
+ // sequences' loss data.
+ base::HistogramBase* Get21CumulativeHistogram(const char* which_21) const;
+ // Add samples associated with a |bit_mask_of_packets| to the given histogram
+ // that was provided by Get21CumulativeHistogram(). The LSB of that mask
+ // corresponds to the oldest packet sequence number in the series of packets,
+ // and the bit in the 2^20 position corresponds to the most recently received
+ // packet. Of the maximum of 21 bits that are valid (correspond to packets),
+ // only the most significant |valid_bits_in_mask| are processed.
+ // A bit value of 0 indicates that a packet was never received, and a 1
+ // indicates the packet was received.
+ static void AddTo21CumulativeHistogram(base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask);
+ // For connections longer than 21 received packets, this call will calculate
+ // the overall packet loss rate, and record it into a histogram.
+ void RecordAggregatePacketLossRate() const;
+ // At destruction time, this records results of |pacaket_received_| into
+ // histograms for specific connection types.
+ void RecordLossHistograms() const;
+
BoundNetLog net_log_;
// The last packet sequence number received.
QuicPacketSequenceNumber last_received_packet_sequence_number_;
- // The largest packet sequence number received. In case of that a packet is
- // received late, this value will not be updated.
+ // The size of the most recently received packet.
+ size_t last_received_packet_size_;
+ // The largest packet sequence number received. In the case where a packet is
+ // received late (out of order), this value will not be updated.
QuicPacketSequenceNumber largest_received_packet_sequence_number_;
// The largest packet sequence number which the peer has failed to
// receive, according to the missing packet set in their ack frames.
QuicPacketSequenceNumber largest_received_missing_packet_sequence_number_;
// Number of times that the current received packet sequence number is
// smaller than the last received packet sequence number.
- size_t out_of_order_recieved_packet_count_;
+ size_t num_out_of_order_received_packets_;
+ // The number of times that OnPacketHeader was called.
+ // If the network replicates packets, then this number may be slightly
+ // different from the real number of distinct packets received.
+ QuicPacketSequenceNumber num_packets_received_;
+ // Number of times a truncated ACK frame was sent.
+ size_t num_truncated_acks_sent_;
+ // Number of times a truncated ACK frame was received.
+ size_t num_truncated_acks_received_;
+ // The kCADR value provided by the server in ServerHello.
+ IPEndPoint local_address_from_shlo_;
+ // The first local address from which a packet was received.
+ IPEndPoint local_address_from_self_;
+ // Count of the number of frames received.
+ int num_frames_received_;
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+ // Vector of inital packets status' indexed by packet sequence numbers, where
+ // false means never received. Zero is not a valid packet sequence number, so
+ // that offset is never used, and we'll track 150 packets.
+ std::bitset<151> received_packets_;
+ // Vector to indicate which of the initial 150 received packets turned out to
+ // contain solo ACK frames. An element is true iff an ACK frame was in the
+ // corresponding packet, and there was very little else.
+ std::bitset<151> received_acks_;
+ // The available type of connection (WiFi, 3G, etc.) when connection was first
+ // used.
+ const char* const connection_description_;
+
DISALLOW_COPY_AND_ASSIGN(QuicConnectionLogger);
};
diff --git a/chromium/net/quic/quic_connection_stats.cc b/chromium/net/quic/quic_connection_stats.cc
index 500b5f39166..cf235cd751e 100644
--- a/chromium/net/quic/quic_connection_stats.cc
+++ b/chromium/net/quic/quic_connection_stats.cc
@@ -12,16 +12,33 @@ QuicConnectionStats::QuicConnectionStats()
: bytes_sent(0),
packets_sent(0),
stream_bytes_sent(0),
+ packets_discarded(0),
bytes_received(0),
packets_received(0),
+ packets_processed(0),
stream_bytes_received(0),
bytes_retransmitted(0),
packets_retransmitted(0),
+ bytes_spuriously_retransmitted(0),
+ packets_spuriously_retransmitted(0),
+ packets_lost(0),
+ slowstart_packets_lost(0),
packets_revived(0),
packets_dropped(0),
+ crypto_retransmit_count(0),
+ loss_timeout_count(0),
+ tlp_count(0),
rto_count(0),
- rtt(0),
- estimated_bandwidth(0) {
+ min_rtt_us(0),
+ srtt_us(0),
+ estimated_bandwidth(0),
+ packets_reordered(0),
+ max_sequence_reordering(0),
+ max_time_reordering_us(0),
+ tcp_loss_events(0),
+ cwnd_increase_congestion_avoidance(0),
+ cwnd_increase_cubic_mode(0),
+ connection_creation_time(QuicTime::Zero()) {
}
QuicConnectionStats::~QuicConnectionStats() {}
@@ -30,16 +47,36 @@ ostream& operator<<(ostream& os, const QuicConnectionStats& s) {
os << "{ bytes sent: " << s.bytes_sent
<< ", packets sent:" << s.packets_sent
<< ", stream bytes sent: " << s.stream_bytes_sent
+ << ", packets discarded: " << s.packets_discarded
<< ", bytes received: " << s.bytes_received
<< ", packets received: " << s.packets_received
+ << ", packets processed: " << s.packets_processed
<< ", stream bytes received: " << s.stream_bytes_received
<< ", bytes retransmitted: " << s.bytes_retransmitted
<< ", packets retransmitted: " << s.packets_retransmitted
+ << ", bytes spuriously retransmitted: " << s.bytes_spuriously_retransmitted
+ << ", packets spuriously retransmitted: "
+ << s.packets_spuriously_retransmitted
+ << ", packets lost: " << s.packets_lost
+ << ", slowstart packets lost: " << s.slowstart_packets_lost
<< ", packets revived: " << s.packets_revived
<< ", packets dropped:" << s.packets_dropped
+ << ", crypto retransmit count: " << s.crypto_retransmit_count
<< ", rto count: " << s.rto_count
- << ", rtt(us): " << s.rtt
- << ", estimated_bandwidth: " << s.estimated_bandwidth
+ << ", tlp count: " << s.tlp_count
+ << ", min_rtt(us): " << s.min_rtt_us
+ << ", srtt(us): " << s.srtt_us
+ << ", max packet size: " << s.max_packet_size
+ << ", estimated bandwidth: " << s.estimated_bandwidth
+ << ", congestion window: " << s.congestion_window
+ << ", tcp_loss_events: " << s.tcp_loss_events
+ << ", packets reordered: " << s.packets_reordered
+ << ", max sequence reordering: " << s.max_sequence_reordering
+ << ", max time reordering(us): " << s.max_time_reordering_us
+ << ", total amount of cwnd increase in TCPCubic, in congestion avoidance: "
+ << s.cwnd_increase_congestion_avoidance
+ << ", amount of cwnd increase in TCPCubic, in cubic mode: "
+ << s.cwnd_increase_cubic_mode
<< "}\n";
return os;
}
diff --git a/chromium/net/quic/quic_connection_stats.h b/chromium/net/quic/quic_connection_stats.h
index b0761d9ba8f..0bad8569029 100644
--- a/chromium/net/quic/quic_connection_stats.h
+++ b/chromium/net/quic/quic_connection_stats.h
@@ -9,19 +9,9 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
namespace net {
-// TODO(satyamshekhar): Add more interesting stats:
-// 1. (C/S)HLO retransmission count.
-// 2. SHLO received to first stream packet processed time.
-// 3. CHLO sent to SHLO received time.
-// 4. Number of migrations.
-// 5. Number of out of order packets.
-// 6. Number of connections that require more that 1-RTT.
-// 7. Avg number of streams / session.
-// 8. Number of duplicates received.
-// 9. Fraction of data transferred that was padding.
-
// Structure to hold stats for a QuicConnection.
struct NET_EXPORT_PRIVATE QuicConnectionStats {
QuicConnectionStats();
@@ -30,24 +20,61 @@ struct NET_EXPORT_PRIVATE QuicConnectionStats {
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const QuicConnectionStats& s);
- uint64 bytes_sent; // includes retransmissions, fec.
+ uint64 bytes_sent; // Includes retransmissions, fec.
uint32 packets_sent;
uint64 stream_bytes_sent; // non-retransmitted bytes sent in a stream frame.
+ uint32 packets_discarded; // Packets serialized and discarded before sending.
- uint64 bytes_received; // includes duplicate data for a stream, fec.
- uint32 packets_received; // includes dropped packets
- uint64 stream_bytes_received; // bytes received in a stream frame.
+ // These include version negotiation and public reset packets, which do not
+ // have sequence numbers or frame data.
+ uint64 bytes_received; // Includes duplicate data for a stream, fec.
+ uint32 packets_received; // Includes packets which were not processable.
+ uint32 packets_processed; // Excludes packets which were not processable.
+ uint64 stream_bytes_received; // Bytes received in a stream frame.
uint64 bytes_retransmitted;
uint32 packets_retransmitted;
+ uint64 bytes_spuriously_retransmitted;
+ uint32 packets_spuriously_retransmitted;
+ // Number of packets abandoned as lost by the loss detection algorithm.
+ uint32 packets_lost;
+ uint32 slowstart_packets_lost; // Number of packets lost exiting slow start.
+
uint32 packets_revived;
- uint32 packets_dropped; // duplicate or less than least unacked.
- uint32 rto_count;
+ uint32 packets_dropped; // Duplicate or less than least unacked.
+ uint32 crypto_retransmit_count;
+ // Count of times the loss detection alarm fired. At least one packet should
+ // be lost when the alarm fires.
+ uint32 loss_timeout_count;
+ uint32 tlp_count;
+ uint32 rto_count; // Count of times the rto timer fired.
+
+ uint32 min_rtt_us; // Minimum RTT in microseconds.
+ uint32 srtt_us; // Smoothed RTT in microseconds.
+ uint32 max_packet_size; // In bytes.
+ uint64 estimated_bandwidth; // In bytes per second.
+ uint32 congestion_window; // In bytes
+
+ // Reordering stats for received packets.
+ // Number of packets received out of sequence number order.
+ uint32 packets_reordered;
+ // Maximum reordering observed in sequence space.
+ uint32 max_sequence_reordering;
+ // Maximum reordering observed in microseconds
+ uint32 max_time_reordering_us;
+
+ // The following stats are used only in TcpCubicSender.
+ // The number of loss events from TCP's perspective. Each loss event includes
+ // one or more lost packets.
+ uint32 tcp_loss_events;
+ // Total amount of cwnd increase by TCPCubic in congestion avoidance.
+ uint32 cwnd_increase_congestion_avoidance;
+ // Total amount of cwnd increase by TCPCubic in cubic mode.
+ uint32 cwnd_increase_cubic_mode;
- uint32 rtt; // In microseconds
- uint64 estimated_bandwidth;
- // TODO(satyamshekhar): Add window_size, mss and mtu.
+ // Creation time, as reported by the QuicClock.
+ QuicTime connection_creation_time;
};
} // namespace net
diff --git a/chromium/net/quic/quic_connection_test.cc b/chromium/net/quic/quic_connection_test.cc
index 39655d0fe2a..38ead0ca1c0 100644
--- a/chromium/net/quic/quic_connection_test.cc
+++ b/chromium/net/quic/quic_connection_test.cc
@@ -8,29 +8,33 @@
#include "base/bind.h"
#include "base/stl_util.h"
#include "net/base/net_errors.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/receive_algorithm_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.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_flags.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_sent_packet_manager.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
using std::map;
using std::vector;
-using testing::_;
using testing::AnyNumber;
+using testing::AtLeast;
using testing::ContainerEq;
+using testing::Contains;
using testing::DoAll;
using testing::InSequence;
using testing::InvokeWithoutArgs;
@@ -38,6 +42,7 @@ using testing::Ref;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
@@ -54,11 +59,6 @@ const QuicPacketEntropyHash kTestEntropyHash = 76;
const int kDefaultRetransmissionTimeMs = 500;
const int kMinRetransmissionTimeMs = 200;
-// Used by TestConnection::SendStreamData3.
-const QuicStreamId kStreamId3 = 3;
-// Used by TestConnection::SendStreamData5.
-const QuicStreamId kStreamId5 = 5;
-
class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
public:
explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
@@ -74,8 +74,8 @@ class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
return true;
}
- MOCK_METHOD4(RecordIncomingPacket,
- void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool));
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
private:
QuicCongestionFeedbackFrame* feedback_;
@@ -142,6 +142,8 @@ class TaggingEncrypter : public QuicEncrypter {
};
const uint8 tag_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggingEncrypter);
};
// TaggingDecrypter ensures that the final kTagSize bytes of the message all
@@ -269,23 +271,24 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
class TestPacketWriter : public QuicPacketWriter {
public:
- TestPacketWriter()
- : last_packet_size_(0),
- blocked_(false),
+ explicit TestPacketWriter(QuicVersion version)
+ : version_(version),
+ framer_(SupportedVersions(version_)),
+ last_packet_size_(0),
+ write_blocked_(false),
+ block_on_next_write_(false),
is_write_blocked_data_buffered_(false),
- is_server_(true),
final_bytes_of_last_packet_(0),
final_bytes_of_previous_packet_(0),
use_tagging_decrypter_(false),
packets_write_attempts_(0) {
}
- // QuicPacketWriter
+ // QuicPacketWriter interface
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
+ const IPEndPoint& peer_address) OVERRIDE {
QuicEncryptedPacket packet(buffer, buf_len);
++packets_write_attempts_;
@@ -295,14 +298,15 @@ class TestPacketWriter : public QuicPacketWriter {
sizeof(final_bytes_of_last_packet_));
}
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), !is_server_);
if (use_tagging_decrypter_) {
- framer.SetDecrypter(new TaggingDecrypter);
+ framer_.framer()->SetDecrypter(new TaggingDecrypter, ENCRYPTION_NONE);
}
- visitor_.Reset();
- framer.set_visitor(&visitor_);
- EXPECT_TRUE(framer.ProcessPacket(packet));
- if (blocked_) {
+ EXPECT_TRUE(framer_.ProcessPacket(packet));
+ if (block_on_next_write_) {
+ write_blocked_ = true;
+ block_on_next_write_ = false;
+ }
+ if (IsWriteBlocked()) {
return WriteResult(WRITE_STATUS_BLOCKED, -1);
}
last_packet_size_ = packet.length();
@@ -313,40 +317,57 @@ class TestPacketWriter : public QuicPacketWriter {
return is_write_blocked_data_buffered_;
}
- // Resets the visitor's state by clearing out the headers and frames.
- void Reset() {
- visitor_.Reset();
- }
+ virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; }
+
+ virtual void SetWritable() OVERRIDE { write_blocked_ = false; }
+
+ void BlockOnNextWrite() { block_on_next_write_ = true; }
- QuicPacketHeader* header() { return visitor_.header(); }
+ const QuicPacketHeader& header() { return framer_.header(); }
- size_t frame_count() const { return visitor_.frame_count(); }
+ size_t frame_count() const { return framer_.num_frames(); }
- QuicAckFrame* ack() { return visitor_.ack(); }
+ const vector<QuicAckFrame>& ack_frames() const {
+ return framer_.ack_frames();
+ }
+
+ const vector<QuicCongestionFeedbackFrame>& feedback_frames() const {
+ return framer_.feedback_frames();
+ }
+
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return framer_.stop_waiting_frames();
+ }
- QuicCongestionFeedbackFrame* feedback() { return visitor_.feedback(); }
+ const vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return framer_.connection_close_frames();
+ }
- QuicConnectionCloseFrame* close() { return visitor_.close(); }
+ const vector<QuicStreamFrame>& stream_frames() const {
+ return framer_.stream_frames();
+ }
- const vector<QuicStreamFrame>* stream_frames() const {
- return visitor_.stream_frames();
+ const vector<QuicPingFrame>& ping_frames() const {
+ return framer_.ping_frames();
}
size_t last_packet_size() {
return last_packet_size_;
}
- QuicVersionNegotiationPacket* version_negotiation_packet() {
- return visitor_.version_negotiation_packet();
+ const QuicVersionNegotiationPacket* version_negotiation_packet() {
+ return framer_.version_negotiation_packet();
}
- void set_blocked(bool blocked) { blocked_ = blocked; }
-
void set_is_write_blocked_data_buffered(bool buffered) {
is_write_blocked_data_buffered_ = buffered;
}
- void set_is_server(bool is_server) { is_server_ = is_server; }
+ void set_is_server(bool is_server) {
+ // We invert is_server here, because the framer needs to parse packets
+ // we send.
+ QuicFramerPeer::SetIsServer(framer_.framer(), !is_server);
+ }
// final_bytes_of_last_packet_ returns the last four bytes of the previous
// packet as a little-endian, uint32. This is intended to be used with a
@@ -365,12 +386,19 @@ class TestPacketWriter : public QuicPacketWriter {
uint32 packets_write_attempts() { return packets_write_attempts_; }
+ void Reset() { framer_.Reset(); }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
private:
- FramerVisitorCapturingFrames visitor_;
+ QuicVersion version_;
+ SimpleQuicFramer framer_;
size_t last_packet_size_;
- bool blocked_;
+ bool write_blocked_;
+ bool block_on_next_write_;
bool is_write_blocked_data_buffered_;
- bool is_server_;
uint32 final_bytes_of_last_packet_;
uint32 final_bytes_of_previous_packet_;
bool use_tagging_decrypter_;
@@ -381,15 +409,18 @@ class TestPacketWriter : public QuicPacketWriter {
class TestConnection : public QuicConnection {
public:
- TestConnection(QuicGuid guid,
+ TestConnection(QuicConnectionId connection_id,
IPEndPoint address,
TestConnectionHelper* helper,
TestPacketWriter* writer,
- bool is_server)
- : QuicConnection(guid, address, helper, writer, is_server,
- QuicSupportedVersions()),
- helper_(helper),
+ bool is_server,
+ QuicVersion version)
+ : QuicConnection(connection_id, address, helper, writer, is_server,
+ SupportedVersions(version)),
writer_(writer) {
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(this), 0);
writer_->set_is_server(is_server);
}
@@ -405,6 +436,11 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+ void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) {
+ QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicConnectionPeer::GetSentPacketManager(this), loss_algorithm);
+ }
+
void SendPacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
@@ -424,19 +460,58 @@ class TestConnection : public QuicConnection {
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MAY_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringWithFec(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MUST_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringHelper(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* delegate) {
IOVector data_iov;
if (!data.empty()) {
data_iov.Append(const_cast<char*>(data.data()), data.size());
}
- return QuicConnection::SendStreamData(id, data_iov, offset, fin, delegate);
+ return QuicConnection::SendStreamData(id, data_iov, offset, fin,
+ fec_protection, delegate);
}
QuicConsumedData SendStreamData3() {
- return SendStreamDataWithString(kStreamId3, "food", 0, !kFin, NULL);
+ return SendStreamDataWithString(kClientDataStreamId1, "food", 0, !kFin,
+ NULL);
+ }
+
+ QuicConsumedData SendStreamData3WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId1, "food", 0,
+ !kFin, NULL);
}
QuicConsumedData SendStreamData5() {
- return SendStreamDataWithString(kStreamId5, "food2", 0, !kFin, NULL);
+ return SendStreamDataWithString(kClientDataStreamId2, "food2", 0,
+ !kFin, NULL);
+ }
+
+ QuicConsumedData SendStreamData5WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId2, "food2", 0,
+ !kFin, NULL);
+ }
+ // Ensures the connection can write stream data before writing.
+ QuicConsumedData EnsureWritableAndSendStreamData5() {
+ EXPECT_TRUE(CanWriteStreamData());
+ return SendStreamData5();
}
// The crypto stream has special semantics so that it is not blocked by a
@@ -445,11 +520,7 @@ class TestConnection : public QuicConnection {
// split needlessly across packet boundaries). As a result, we have separate
// tests for some cases for this stream.
QuicConsumedData SendCryptoStreamData() {
- this->Flush();
- QuicConsumedData consumed =
- SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
- this->Flush();
- return consumed;
+ return SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
}
bool is_server() {
@@ -457,13 +528,16 @@ class TestConnection : public QuicConnection {
}
void set_version(QuicVersion version) {
- framer_.set_version(version);
+ QuicConnectionPeer::GetFramer(this)->set_version(version);
+ }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions);
+ writer_->SetSupportedVersions(versions);
}
void set_is_server(bool is_server) {
writer_->set_is_server(is_server);
- QuicPacketCreatorPeer::SetIsServer(
- QuicConnectionPeer::GetPacketCreator(this), is_server);
QuicConnectionPeer::SetIsServer(this, is_server);
}
@@ -472,6 +546,16 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::GetAckAlarm(this));
}
+ TestConnectionHelper::TestAlarm* GetPingAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetPingAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetResumeWritesAlarm(this));
+ }
+
TestConnectionHelper::TestAlarm* GetRetransmissionAlarm() {
return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
QuicConnectionPeer::GetRetransmissionAlarm(this));
@@ -482,11 +566,6 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::GetSendAlarm(this));
}
- TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() {
- return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
- QuicConnectionPeer::GetResumeWritesAlarm(this));
- }
-
TestConnectionHelper::TestAlarm* GetTimeoutAlarm() {
return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
QuicConnectionPeer::GetTimeoutAlarm(this));
@@ -495,81 +574,95 @@ class TestConnection : public QuicConnection {
using QuicConnection::SelectMutualVersion;
private:
- TestConnectionHelper* helper_;
TestPacketWriter* writer_;
DISALLOW_COPY_AND_ASSIGN(TestConnection);
};
-class QuicConnectionTest : public ::testing::TestWithParam<bool> {
+// Used for testing packets revived from FEC packets.
+class FecQuicConnectionDebugVisitor
+ : public QuicConnectionDebugVisitor {
+ public:
+ virtual void OnRevivedPacket(const QuicPacketHeader& header,
+ StringPiece data) OVERRIDE {
+ revived_header_ = header;
+ }
+
+ // Public accessor method.
+ QuicPacketHeader revived_header() const {
+ return revived_header_;
+ }
+
+ private:
+ QuicPacketHeader revived_header_;
+};
+
+class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicConnectionTest()
- : guid_(42),
- framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- creator_(guid_, &framer_, &random_generator_, false),
+ : connection_id_(42),
+ framer_(SupportedVersions(version()), QuicTime::Zero(), false),
+ peer_creator_(connection_id_, &framer_, &random_generator_),
send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ loss_algorithm_(new MockLossAlgorithm()),
helper_(new TestConnectionHelper(&clock_, &random_generator_)),
- writer_(new TestPacketWriter()),
- connection_(guid_, IPEndPoint(), helper_.get(), writer_.get(), false),
+ writer_(new TestPacketWriter(version())),
+ connection_(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), false, version()),
frame1_(1, false, 0, MakeIOVector(data1)),
frame2_(1, false, 3, MakeIOVector(data2)),
- accept_packet_(true) {
+ sequence_number_length_(PACKET_6BYTE_SEQUENCE_NUMBER),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID) {
connection_.set_visitor(&visitor_);
connection_.SetSendAlgorithm(send_algorithm_);
+ connection_.SetLossAlgorithm(loss_algorithm_);
framer_.set_received_entropy_calculator(&entropy_calculator_);
// Simplify tests by not sending feedback unless specifically configured.
SetFeedback(NULL);
EXPECT_CALL(
- *send_algorithm_, TimeUntilSend(_, _, _, _)).WillRepeatedly(Return(
+ *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return(
QuicTime::Delta::Zero()));
EXPECT_CALL(*receive_algorithm_,
- RecordIncomingPacket(_, _, _, _)).Times(AnyNumber());
+ RecordIncomingPacket(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.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)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.WillByDefault(Return(true));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber());
EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()).WillRepeatedly(
- Return(true));
- }
-
- QuicAckFrame* outgoing_ack() {
- outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_));
- return outgoing_ack_.get();
- }
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
- QuicAckFrame* last_ack() {
- return writer_->ack();
+ EXPECT_CALL(*loss_algorithm_, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
}
- QuicCongestionFeedbackFrame* last_feedback() {
- return writer_->feedback();
+ QuicVersion version() {
+ return GetParam();
}
- QuicConnectionCloseFrame* last_close() {
- return writer_->close();
- }
-
- QuicPacketHeader* last_header() {
- return writer_->header();
- }
-
- size_t last_sent_packet_size() {
- return writer_->last_packet_size();
- }
-
- uint32 final_bytes_of_last_packet() {
- return writer_->final_bytes_of_last_packet();
+ QuicAckFrame* outgoing_ack() {
+ outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_));
+ return outgoing_ack_.get();
}
- uint32 final_bytes_of_previous_packet() {
- return writer_->final_bytes_of_previous_packet();
+ QuicPacketSequenceNumber least_unacked() {
+ if (version() <= QUIC_VERSION_15) {
+ if (writer_->ack_frames().empty()) {
+ return 0;
+ }
+ return writer_->ack_frames()[0].sent_info.least_unacked;
+ }
+ if (writer_->stop_waiting_frames().empty()) {
+ return 0;
+ }
+ return writer_->stop_waiting_frames()[0].least_unacked;
}
void use_tagging_decrypter() {
@@ -577,16 +670,17 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
}
void ProcessPacket(QuicPacketSequenceNumber number) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
ProcessDataPacket(number, 0, !kEntropyFlag);
}
QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) {
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- QuicPacketCreatorPeer::SetSendVersionInPacket(&creator_,
+ QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_,
connection_.is_server());
- SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames);
+ SerializedPacket serialized_packet =
+ peer_creator_.SerializeAllFrames(frames);
scoped_ptr<QuicPacket> packet(serialized_packet.packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE,
@@ -623,12 +717,12 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
}
size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
- bool expect_revival, bool entropy_flag) {
+ bool expect_revival, bool entropy_flag) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
}
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_))
- .RetiresOnSaturation();
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1).
+ RetiresOnSaturation();
return ProcessDataPacket(number, 1, entropy_flag);
}
@@ -640,7 +734,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
bool entropy_flag,
QuicPacket* packet) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
}
// Construct the decrypted data packet so we can compute the correct
@@ -653,12 +747,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
data_packet.reset(ConstructDataPacket(number, 1, !kEntropyFlag));
}
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
+ header_.packet_sequence_number = number;
header_.entropy_flag = entropy_flag;
header_.fec_flag = true;
- header_.packet_sequence_number = number;
header_.is_in_fec_group = IN_FEC_GROUP;
header_.fec_group = min_protected_packet;
QuicFecData fec_data;
@@ -669,7 +765,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
// with itself, depending on the number of packets.
if (((number - min_protected_packet) % 2) == 0) {
for (size_t i = GetStartOfFecProtectedData(
- header_.public_header.guid_length,
+ header_.public_header.connection_id_length,
header_.public_header.version_flag,
header_.public_header.sequence_number_length);
i < data_packet->length(); ++i) {
@@ -694,7 +790,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicPacketSequenceNumber* last_packet) {
QuicByteCount packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .WillOnce(DoAll(SaveArg<2>(&packet_size), Return(true)));
+ .WillOnce(DoAll(SaveArg<3>(&packet_size), Return(true)));
connection_.SendStreamDataWithString(id, data, offset, fin, NULL);
if (last_packet != NULL) {
*last_packet =
@@ -716,6 +812,10 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
return ProcessFramePacket(QuicFrame(frame));
}
+ QuicPacketEntropyHash ProcessStopWaitingPacket(QuicStopWaitingFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
return ProcessFramePacket(QuicFrame(frame));
}
@@ -727,9 +827,11 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
QuicFecGroupNumber fec_group,
bool entropy_flag) {
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
header_.entropy_flag = entropy_flag;
header_.fec_flag = false;
header_.packet_sequence_number = number;
@@ -740,14 +842,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicFrame frame(&frame1_);
frames.push_back(frame);
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(header_, frames).packet;
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number,
QuicFecGroupNumber fec_group) {
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.packet_sequence_number = number;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
@@ -763,7 +865,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicFrame frame(&qccf);
frames.push_back(frame);
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(header_, frames).packet;
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
@@ -781,12 +883,81 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
}
- QuicGuid guid_;
+ // Initialize a frame acknowledging all packets up to largest_observed.
+ const QuicAckFrame InitAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame frame(MakeAckFrame(largest_observed, least_unacked));
+ if (largest_observed > 0) {
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, largest_observed);
+ }
+ return frame;
+ }
+
+ const QuicStopWaitingFrame InitStopWaitingFrame(
+ QuicPacketSequenceNumber least_unacked) {
+ QuicStopWaitingFrame frame;
+ frame.least_unacked = least_unacked;
+ return frame;
+ }
+ // Explicitly nack a packet.
+ void NackPacket(QuicPacketSequenceNumber missing, QuicAckFrame* frame) {
+ frame->received_info.missing_packets.insert(missing);
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, missing);
+ if (missing > 1) {
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, missing - 1);
+ }
+ }
+
+ // Undo nacking a packet within the frame.
+ void AckPacket(QuicPacketSequenceNumber arrived, QuicAckFrame* frame) {
+ EXPECT_THAT(frame->received_info.missing_packets, Contains(arrived));
+ frame->received_info.missing_packets.erase(arrived);
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived);
+ if (arrived > 1) {
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived - 1);
+ }
+ }
+
+ void TriggerConnectionClose() {
+ // Send an erroneous packet to close the connection.
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
+ // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
+ // packet call to the visitor.
+ ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
+ }
+
+ void BlockOnNextWrite() {
+ writer_->BlockOnNextWrite();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ }
+
+ void CongestionBlockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::FromSeconds(1)));
+ }
+
+ void CongestionUnblockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ }
+
+ QuicConnectionId connection_id_;
QuicFramer framer_;
- QuicPacketCreator creator_;
+ QuicPacketCreator peer_creator_;
MockEntropyCalculator entropy_calculator_;
MockSendAlgorithm* send_algorithm_;
+ MockLossAlgorithm* loss_algorithm_;
TestReceiveAlgorithm* receive_algorithm_;
MockClock clock_;
MockRandom random_generator_;
@@ -799,13 +970,19 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicStreamFrame frame1_;
QuicStreamFrame frame2_;
scoped_ptr<QuicAckFrame> outgoing_ack_;
- bool accept_packet_;
+ QuicSequenceNumberLength sequence_number_length_;
+ QuicConnectionIdLength connection_id_length_;
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest);
};
-TEST_F(QuicConnectionTest, PacketsInOrder) {
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_CASE_P(SupportedVersion,
+ QuicConnectionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicConnectionTest, PacketsInOrder) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
@@ -821,21 +998,7 @@ TEST_F(QuicConnectionTest, PacketsInOrder) {
EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
}
-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());
-
- accept_packet_ = false;
- ProcessPacket(2);
- // We should not have an ack for two.
- EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed);
- EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
-}
-
-TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
+TEST_P(QuicConnectionTest, PacketsOutOfOrder) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -854,7 +1017,7 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
EXPECT_FALSE(IsMissing(1));
}
-TEST_F(QuicConnectionTest, DuplicatePacket) {
+TEST_P(QuicConnectionTest, DuplicatePacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -870,7 +1033,7 @@ TEST_F(QuicConnectionTest, DuplicatePacket) {
EXPECT_TRUE(IsMissing(1));
}
-TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
+TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -891,8 +1054,9 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
// packet the peer will not retransmit. It indicates this by sending 'least
// awaiting' is 4. The connection should then realize 1 will not be
// retransmitted, and will remove it from the missing list.
- creator_.set_sequence_number(5);
- QuicAckFrame frame(0, QuicTime::Zero(), 4);
+ peer_creator_.set_sequence_number(5);
+ QuicAckFrame frame = InitAckFrame(1, 4);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _));
ProcessAckPacket(&frame);
// Force an ack to be sent.
@@ -900,31 +1064,53 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
EXPECT_TRUE(IsMissing(4));
}
-TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
+TEST_P(QuicConnectionTest, RejectPacketTooFarOut) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
// Call ProcessDataPacket rather than ProcessPacket, as we should not get a
// packet call to the visitor.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
}
-TEST_F(QuicConnectionTest, TruncatedAck) {
+TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) {
+ // Process an unencrypted packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA,
+ false));
+ ProcessDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
+ const vector<QuicConnectionCloseFrame>& connection_close_frames =
+ writer_->connection_close_frames();
+ EXPECT_EQ(1u, connection_close_frames.size());
+ EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
+ connection_close_frames[0].error_code);
+}
+
+TEST_P(QuicConnectionTest, TruncatedAck) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketSequenceNumber num_packets = 256 * 2 + 1;
for (QuicPacketSequenceNumber i = 0; i < num_packets; ++i) {
- SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL);
+ SendStreamDataToPeer(3, "foo", i * 3, !kFin, NULL);
}
- QuicAckFrame frame(num_packets, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(num_packets, 1);
+ SequenceNumberSet lost_packets;
// Create an ack with 256 nacks, none adjacent to one another.
for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) {
- frame.received_info.missing_packets.insert(i * 2);
+ NackPacket(i * 2, &frame);
+ if (i < 256) { // Last packet is nacked, but not lost.
+ lost_packets.insert(i * 2);
+ }
}
- frame.received_info.entropy_hash = 0;
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
EXPECT_CALL(entropy_calculator_,
EntropyHash(511)).WillOnce(testing::Return(0));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(256);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
QuicReceivedPacketManager* received_packet_manager =
@@ -933,40 +1119,40 @@ TEST_F(QuicConnectionTest, TruncatedAck) {
EXPECT_GT(num_packets,
received_packet_manager->peer_largest_observed_packet());
- frame.received_info.missing_packets.erase(192);
- frame.received_info.entropy_hash = 2;
+ AckPacket(192, &frame);
- // Removing one missing packet allows us to ack 192 and one more range.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ // Removing one missing packet allows us to ack 192 and one more range, but
+ // 192 has already been declared lost, so it doesn't register as an ack.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
EXPECT_EQ(num_packets,
received_packet_manager->peer_largest_observed_packet());
}
-TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
// Delay sending, then queue up an ack.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
QuicConnectionPeer::SendAck(&connection_);
// Process an ack with a least unacked of the received ack.
// This causes an ack to be sent when TimeUntilSend returns 0.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
// Skip a packet and then record an ack.
- creator_.set_sequence_number(2);
- QuicAckFrame frame(0, QuicTime::Zero(), 3);
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame frame = InitAckFrame(0, 3);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
+TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -986,52 +1172,70 @@ TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
EXPECT_EQ(3u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) {
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
+
QuicPacketSequenceNumber original;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&original), SaveArg<2>(&packet_size),
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original), SaveArg<3>(&packet_size),
Return(true)));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
- QuicAckFrame frame(original, QuicTime::Zero(), 1);
- frame.received_info.missing_packets.insert(original);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, original - 1);
+ QuicAckFrame frame = InitAckFrame(original, 1);
+ NackPacket(original, &frame);
// First nack triggers early retransmit.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
QuicPacketSequenceNumber retransmission;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&retransmission), Return(true)));
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _))
+ .WillOnce(DoAll(SaveArg<2>(&retransmission), Return(true)));
ProcessAckPacket(&frame);
- QuicAckFrame frame2(retransmission, QuicTime::Zero(), 1);
- frame2.received_info.missing_packets.insert(original);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, original);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _));
-
+ QuicAckFrame frame2 = InitAckFrame(retransmission, 1);
+ NackPacket(original, &frame2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
ProcessAckPacket(&frame2);
+
// 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.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
- ProcessAckPacket(&frame2);
+ // as missing, that will bundle an ack with data after two acks in a row
+ // indicate the high water mark needs to be raised.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL);
+ // No ack sent.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+
+ // No more packet loss for the rest of the test.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
ProcessAckPacket(&frame2);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL);
+ // Ack bundled.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
// But an ack with no missing packets will not send an ack.
- frame2.received_info.missing_packets.clear();
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission);
+ AckPacket(original, &frame2);
ProcessAckPacket(&frame2);
ProcessAckPacket(&frame2);
}
-TEST_F(QuicConnectionTest, LeastUnackedLower) {
+TEST_P(QuicConnectionTest, LeastUnackedLower) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
@@ -1039,355 +1243,372 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) {
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
// Start out saying the least unacked is 2.
- creator_.set_sequence_number(5);
- QuicAckFrame frame(0, QuicTime::Zero(), 2);
- ProcessAckPacket(&frame);
+ peer_creator_.set_sequence_number(5);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(2);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame frame = InitAckFrame(0, 2);
+ ProcessAckPacket(&frame);
+ }
// Change it to 1, but lower the sequence number to fake out-of-order packets.
// This should be fine.
- creator_.set_sequence_number(1);
- QuicAckFrame frame2(0, QuicTime::Zero(), 1);
- // The scheduler will not process out of order acks.
- EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
- ProcessAckPacket(&frame2);
+ peer_creator_.set_sequence_number(1);
+ // The scheduler will not process out of order acks, but all packet processing
+ // causes the connection to try to write.
+ EXPECT_CALL(visitor_, OnCanWrite());
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame2);
+ } else {
+ QuicAckFrame frame2 = InitAckFrame(0, 1);
+ ProcessAckPacket(&frame2);
+ }
// 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_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- creator_.set_sequence_number(7);
- ProcessAckPacket(&frame2);
+ peer_creator_.set_sequence_number(7);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, false));
+ QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame2);
+ } else {
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
+ QuicAckFrame frame2 = InitAckFrame(0, 1);
+ ProcessAckPacket(&frame2);
+ }
}
-TEST_F(QuicConnectionTest, LargestObservedLower) {
+TEST_P(QuicConnectionTest, LargestObservedLower) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Start out saying the largest observed is 2.
- QuicAckFrame frame(2, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, 2);
- ProcessAckPacket(&frame);
+ QuicAckFrame frame1 = InitAckFrame(1, 0);
+ QuicAckFrame frame2 = InitAckFrame(2, 0);
+ ProcessAckPacket(&frame2);
// Now change it to 1, and it should cause a connection error.
- QuicAckFrame frame2(1, QuicTime::Zero(), 0);
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
- ProcessAckPacket(&frame2);
+ ProcessAckPacket(&frame1);
}
-TEST_F(QuicConnectionTest, AckUnsentData) {
+TEST_P(QuicConnectionTest, AckUnsentData) {
// Ack a packet which has not been sent.
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
+ QuicAckFrame frame(MakeAckFrame(1, 0));
EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckAll) {
+TEST_P(QuicConnectionTest, AckAll) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- creator_.set_sequence_number(1);
- QuicAckFrame frame1(0, QuicTime::Zero(), 1);
+ peer_creator_.set_sequence_number(1);
+ QuicAckFrame frame1 = InitAckFrame(0, 1);
ProcessAckPacket(&frame1);
}
-TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) {
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000)));
-
+TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) {
QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
EXPECT_EQ(1u, last_packet);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256));
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
EXPECT_EQ(2u, last_packet);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
// The 1 packet lag is due to the sequence number length being recalculated in
// QuicConnection after a packet is sent.
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256));
SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
EXPECT_EQ(3u, last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256));
SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
EXPECT_EQ(4u, last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256 * 256));
SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
EXPECT_EQ(5u, last_packet);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
}
-TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) {
+TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) {
QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
EXPECT_EQ(1u, last_packet);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(100);
+ creator->set_sequence_number(100);
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256);
+ creator->set_sequence_number(100 * 256);
SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256 * 256);
+ creator->set_sequence_number(100 * 256 * 256);
SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256 * 256 * 256);
+ creator->set_sequence_number(100 * 256 * 256 * 256);
SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
}
-TEST_F(QuicConnectionTest, BasicSending) {
+TEST_P(QuicConnectionTest, BasicSending) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6);
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
SendAckPacketToPeer(); // Packet 2
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
SendAckPacketToPeer(); // Packet 3
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); // Packet 4
EXPECT_EQ(4u, last_packet);
SendAckPacketToPeer(); // Packet 5
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Peer acks up to packet 3.
- QuicAckFrame frame(3, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
+ QuicAckFrame frame = InitAckFrame(3, 0);
ProcessAckPacket(&frame);
SendAckPacketToPeer(); // Packet 6
// As soon as we've acked one, we skip ack packets 2 and 3 and note lack of
// ack for 4.
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Peer acks up to packet 4, the last packet.
- QuicAckFrame frame2(6, QuicTime::Zero(), 0);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
+ QuicAckFrame frame2 = InitAckFrame(6, 0);
ProcessAckPacket(&frame2); // Acks don't instigate acks.
// Verify that we did not send an ack.
- EXPECT_EQ(6u, last_header()->packet_sequence_number);
+ EXPECT_EQ(6u, writer_->header().packet_sequence_number);
// So the last ack has not changed.
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
// If we force an ack, we shouldn't change our retransmit state.
SendAckPacketToPeer(); // Packet 7
- EXPECT_EQ(7u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(7u, least_unacked());
// But if we send more data it should.
SendStreamDataToPeer(1, "eep", 6, !kFin, &last_packet); // Packet 8
EXPECT_EQ(8u, last_packet);
SendAckPacketToPeer(); // Packet 9
- EXPECT_EQ(8u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(7u, least_unacked());
}
-TEST_F(QuicConnectionTest, FECSending) {
+TEST_P(QuicConnectionTest, FECSending) {
// All packets carry version info till version is negotiated.
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 64K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
IN_FEC_GROUP, &payload_length);
- // And send FEC every two packets.
- connection_.options()->max_packets_per_fec_group = 2;
+ creator->set_max_packet_length(length);
+
+ // Enable FEC.
+ creator->set_max_packets_per_fec_group(2);
- // Send 4 data packets and 2 FEC packets.
+ // Send 4 protected data packets, which will also trigger 2 FEC packets.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- // The first stream frame will consume 2 fewer bytes than the other three.
- const string payload(payload_length * 4 - 6, 'a');
- connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL);
+ // The first stream frame will have 2 fewer overhead bytes than the other 3.
+ const string payload(payload_length * 4 + 2, 'a');
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL);
// Expect the FEC group to be closed after SendStreamDataWithString.
- EXPECT_FALSE(creator_.ShouldSendFec(true));
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
}
-TEST_F(QuicConnectionTest, FECQueueing) {
+TEST_P(QuicConnectionTest, FECQueueing) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
- connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
- IN_FEC_GROUP, &payload_length);
- // And send FEC every two packets.
- connection_.options()->max_packets_per_fec_group = 2;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ IN_FEC_GROUP, &payload_length);
+ creator->set_max_packet_length(length);
+ // Enable FEC.
+ creator->set_max_packets_per_fec_group(1);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+ BlockOnNextWrite();
const string payload(payload_length, 'a');
- connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL);
- EXPECT_FALSE(creator_.ShouldSendFec(true));
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL);
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
// Expect the first data packet and the fec packet to be queued.
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) {
- connection_.options()->max_packets_per_fec_group = 1;
+TEST_P(QuicConnectionTest, AbandonFECFromCongestionWindow) {
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
+
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
const QuicTime::Delta retransmission_time =
QuicTime::Delta::FromMilliseconds(5000);
clock_.AdvanceTime(retransmission_time);
// Abandon FEC packet and data packet.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnRetransmissionTimeout();
}
-TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
+TEST_P(QuicConnectionTest, DontAbandonAckedFEC) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- connection_.options()->max_packets_per_fec_group = 1;
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
// Send some more data afterwards to ensure early retransmit doesn't trigger.
- connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL);
- QuicAckFrame ack_fec(2, QuicTime::Zero(), 1);
+ QuicAckFrame ack_fec = InitAckFrame(2, 1);
// Data packet missing.
// TODO(ianswett): Note that this is not a sensible ack, since if the FEC was
// received, it would cause the covered packet to be acked as well.
- ack_fec.received_info.missing_packets.insert(1);
- ack_fec.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
-
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
-
+ NackPacket(1, &ack_fec);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&ack_fec);
-
clock_.AdvanceTime(DefaultRetransmissionTime());
// Don't abandon the acked FEC packet, but it will abandon 2 the subsequent
// FEC packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(5);
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
connection_.GetRetransmissionAlarm()->Fire();
}
-TEST_F(QuicConnectionTest, DontAbandonAllFEC) {
+TEST_P(QuicConnectionTest, AbandonAllFEC) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- connection_.options()->max_packets_per_fec_group = 1;
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
// Send some more data afterwards to ensure early retransmit doesn't trigger.
- connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL);
// Advance the time so not all the FEC packets are abandoned.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
- connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL);
- QuicAckFrame ack_fec(5, QuicTime::Zero(), 1);
+ QuicAckFrame ack_fec = InitAckFrame(5, 1);
// Ack all data packets, but no fec packets.
- ack_fec.received_info.missing_packets.insert(2);
- ack_fec.received_info.missing_packets.insert(4);
- ack_fec.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ NackPacket(2, &ack_fec);
+ NackPacket(4, &ack_fec);
// Lose the first FEC packet and ack the three data packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _));
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&ack_fec);
clock_.AdvanceTime(DefaultRetransmissionTime().Subtract(
QuicTime::Delta::FromMilliseconds(1)));
- // Don't abandon the acked FEC packet, but it will abandon 1 of the subsequent
- // FEC packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(4, _));
+ // Abandon all packets
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(false));
connection_.GetRetransmissionAlarm()->Fire();
- // Ensure the connection's alarm is still set, in order to abandon the third
- // FEC packet.
- EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Ensure the alarm is not set since all packets have been abandoned.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, FramePacking) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePacking) {
+ CongestionBlockWrites();
// Send an ack and two stream frames in 1 packet by queueing them.
connection_.SendAck();
@@ -1395,30 +1616,30 @@ TEST_F(QuicConnectionTest, FramePacking) {
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ &TestConnection::SendStreamData5))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(1);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's an ack and two stream frames from
// two different streams.
- EXPECT_EQ(3u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- EXPECT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
+ CongestionBlockWrites();
// Send an ack and two stream frames (one non-crypto, then one crypto) in 2
// packets by queueing them.
@@ -1427,100 +1648,88 @@ TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendCryptoStreamData)),
- Return(true)));
+ &TestConnection::SendCryptoStreamData))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(2);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's the crypto stream frame.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(kCryptoStreamId, (*writer_->stream_frames())[0].stream_id);
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
+ CongestionBlockWrites();
- // Send an ack and two stream frames (one crypto, then one non-crypto) in 3
+ // Send an ack and two stream frames (one crypto, then one non-crypto) in 2
// packets by queueing them.
connection_.SendAck();
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendCryptoStreamData)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData3)),
- Return(true)));
+ &TestConnection::SendStreamData3))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(3);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's the stream frame from stream 3.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingFEC) {
- // Enable fec.
- connection_.options()->max_packets_per_fec_group = 6;
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingFEC) {
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(6);
- // Send an ack and two stream frames in 1 packet by queueing them.
- connection_.SendAck();
+ CongestionBlockWrites();
+
+ // Queue an ack and two stream frames. Ack gets flushed when FEC is turned on
+ // for sending protected data; two stream frames are packing in 1 packet.
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
- IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData3)),
- IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData3WithFec)),
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData5WithFec))));
+ connection_.SendAck();
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)).Times(2);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's in an fec group.
- EXPECT_EQ(1u, writer_->header()->fec_group);
+ EXPECT_EQ(2u, writer_->header().fec_group);
EXPECT_EQ(0u, writer_->frame_count());
}
-TEST_F(QuicConnectionTest, FramePackingAckResponse) {
+TEST_P(QuicConnectionTest, FramePackingAckResponse) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Process a data packet to queue up a pending ack.
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
ProcessDataPacket(1, 1, kEntropyFlag);
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ &TestConnection::SendStreamData5))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(1);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
// Process an ack to cause the visitor's OnCanWrite to be invoked.
- creator_.set_sequence_number(2);
- QuicAckFrame ack_one(0, QuicTime::Zero(), 0);
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame ack_one = InitAckFrame(0, 0);
ProcessAckPacket(&ack_one);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1528,23 +1737,28 @@ TEST_F(QuicConnectionTest, FramePackingAckResponse) {
// Parse the last packet and ensure it's an ack and two stream frames from
// two different streams.
- EXPECT_EQ(3u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- ASSERT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingSendv) {
+TEST_P(QuicConnectionTest, FramePackingSendv) {
// Send data in 1 packet by writing multiple blocks in a single iovector
// using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
char data[] = "ABCD";
IOVector data_iov;
data_iov.AppendNoCoalesce(data, 2);
data_iov.AppendNoCoalesce(data + 2, 2);
- connection_.SendStreamData(1, data_iov, 0, !kFin, NULL);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
@@ -1552,87 +1766,78 @@ TEST_F(QuicConnectionTest, FramePackingSendv) {
// Parse the last packet and ensure multiple iovector blocks have
// been packed into a single stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- QuicStreamFrame frame = (*writer_->stream_frames())[0];
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ QuicStreamFrame frame = writer_->stream_frames()[0];
EXPECT_EQ(1u, frame.stream_id);
EXPECT_EQ("ABCD", string(static_cast<char*>
(frame.data.iovec()[0].iov_base),
(frame.data.iovec()[0].iov_len)));
}
-TEST_F(QuicConnectionTest, FramePackingSendvQueued) {
+TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
// Try to send two stream frames in 1 packet by using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- writer_->set_blocked(true);
+ BlockOnNextWrite();
char data[] = "ABCD";
IOVector data_iov;
data_iov.AppendNoCoalesce(data, 2);
data_iov.AppendNoCoalesce(data + 2, 2);
- connection_.SendStreamData(1, data_iov, 0, !kFin, NULL);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL);
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.
- writer_->set_blocked(false);
- EXPECT_TRUE(connection_.OnCanWrite());
+ writer_->SetWritable();
+ connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Parse the last packet and ensure it's one stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id);
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, SendingZeroBytes) {
+TEST_P(QuicConnectionTest, SendingZeroBytes) {
// Send a zero byte write with a fin using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
IOVector empty_iov;
- connection_.SendStreamData(1, empty_iov, 0, kFin, NULL);
+ connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's one stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id);
- EXPECT_TRUE((*writer_->stream_frames())[0].fin);
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
+ EXPECT_TRUE(writer_->stream_frames()[0].fin);
}
-TEST_F(QuicConnectionTest, OnCanWrite) {
- // Visitor's OnCanWill send data, but will return false.
+TEST_P(QuicConnectionTest, OnCanWrite) {
+ // Visitor's OnCanWrite will send data, but will have more pending writes.
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(false)));
-
+ &TestConnection::SendStreamData5))));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true));
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
- // Unblock the connection.
connection_.OnCanWrite();
+
// Parse the last packet and ensure it's the two stream frames from
// two different streams.
EXPECT_EQ(2u, writer_->frame_count());
- EXPECT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ EXPECT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, RetransmitOnNack) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1);
+TEST_P(QuicConnectionTest, RetransmitOnNack) {
QuicPacketSequenceNumber last_packet;
QuicByteCount second_packet_size;
SendStreamDataToPeer(3, "foo", 0, !kFin, &last_packet); // Packet 1
@@ -1642,34 +1847,26 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
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 retransmission.
- QuicAckFrame ack_one(1, QuicTime::Zero(), 0);
- ack_one.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
+ // Don't lose a packet on an ack, and nothing is retransmitted.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_one = InitAckFrame(1, 0);
ProcessAckPacket(&ack_one);
- // Peer acks up to 3 with two explicitly missing.
- // Early retransmit causes 2 to be retransmitted on the first ack.
- QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
- nack_two.received_info.missing_packets.insert(2);
- nack_two.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- // The third nack should trigger a retransmission.
+ // Lose a packet and ensure it triggers retransmission.
+ QuicAckFrame nack_two = InitAckFrame(3, 0);
+ NackPacket(2, &nack_two);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _)).Times(1);
+ OnPacketSent(_, _, _, second_packet_size - kQuicVersionSize, _)).
+ Times(1);
ProcessAckPacket(&nack_two);
}
-TEST_F(QuicConnectionTest, DiscardRetransmit) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1);
+TEST_P(QuicConnectionTest, DiscardRetransmit) {
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
@@ -1677,34 +1874,24 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) {
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 retransmission.
- QuicAckFrame ack_one(1, QuicTime::Zero(), 0);
- ack_one.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
-
- // Peer acks up to 3 with two explicitly missing. Two nacks should cause no
- // change.
- QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
- nack_two.received_info.missing_packets.insert(2);
- nack_two.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ // Instigate a loss with an ack.
+ QuicAckFrame nack_two = InitAckFrame(3, 0);
+ NackPacket(2, &nack_two);
// The first nack should trigger a fast retransmission, but we'll be
// write blocked, so the packet will be queued.
- writer_->set_blocked(true);
-
+ BlockOnNextWrite();
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&nack_two);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Now, ack the previous transmission.
- QuicAckFrame ack_all(3, QuicTime::Zero(), 0);
- ack_all.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ QuicAckFrame ack_all = InitAckFrame(3, 0);
ProcessAckPacket(&ack_all);
// Unblock the socket and attempt to send the queued packets. However,
@@ -1713,50 +1900,50 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) {
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, _, _)).Times(0);
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
+TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
QuicPacketSequenceNumber largest_observed;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size),
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&largest_observed), SaveArg<3>(&packet_size),
Return(true)));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
- QuicAckFrame frame(1, QuicTime::Zero(), largest_observed);
- frame.received_info.missing_packets.insert(largest_observed);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, largest_observed - 1);
+
+ QuicAckFrame frame = InitAckFrame(1, largest_observed);
+ NackPacket(largest_observed, &frame);
// The first nack should retransmit the largest observed packet.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _));
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _));
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) {
+TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
for (int i = 0; i < 10; ++i) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
- connection_.SendStreamDataWithString(1, "foo", i * 3, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "foo", i * 3, !kFin, NULL);
}
// Block the congestion window and ensure they're queued.
- writer_->set_blocked(true);
+ BlockOnNextWrite();
clock_.AdvanceTime(DefaultRetransmissionTime());
// Only one packet should be retransmitted.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(10);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_TRUE(connection_.HasQueuedData());
// Unblock the congestion window.
- writer_->set_blocked(false);
+ writer_->SetWritable();
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
2 * DefaultRetransmissionTime().ToMicroseconds()));
// Retransmit already retransmitted packets event though the sequence number
@@ -1766,9 +1953,8 @@ TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) {
connection_.OnCanWrite();
}
-TEST_F(QuicConnectionTest, WriteBlockedThenSent) {
- writer_->set_blocked(true);
-
+TEST_P(QuicConnectionTest, WriteBlockedThenSent) {
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -1778,22 +1964,66 @@ TEST_F(QuicConnectionTest, WriteBlockedThenSent) {
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, ResumptionAlarmThenWriteBlocked) {
- // Set the send and resumption alarm, then block the connection.
+TEST_P(QuicConnectionTest, WriteBlockedAckedThenSent) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1, 0);
+ ProcessAckPacket(&ack);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ // Simulate the retransmission alarm firing.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(_));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1, 0);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+
+ connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0));
+ // There is now a pending packet, but with no retransmittable frames.
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.sent_packet_manager().HasRetransmittableFrames(2));
+}
+
+TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) {
+ // Block the connection.
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+
+ // Set the send and resumption alarms. Fire the alarms and ensure they don't
+ // attempt to write.
connection_.GetResumeWritesAlarm()->Set(clock_.ApproximateNow());
connection_.GetSendAlarm()->Set(clock_.ApproximateNow());
- QuicConnectionPeer::SetIsWriteBlocked(&connection_, true);
-
- // Fire the alarms and ensure the connection is still write blocked.
connection_.GetResumeWritesAlarm()->Fire();
connection_.GetSendAlarm()->Fire();
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
+TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(15, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(4);
int offset = 0;
// Send packets 1 to 15.
for (int i = 0; i < 15; ++i) {
@@ -1802,29 +2032,24 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
}
// Ack 15, nack 1-14.
- QuicAckFrame nack(15, QuicTime::Zero(), 0);
+ SequenceNumberSet lost_packets;
+ QuicAckFrame nack = InitAckFrame(15, 0);
for (int i = 1; i < 15; ++i) {
- nack.received_info.missing_packets.insert(i);
+ NackPacket(i, &nack);
+ lost_packets.insert(i);
}
- nack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 15) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 14);
-
- // 13 packets have been NACK'd 3 times, but we limit retransmissions to 2.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- ProcessAckPacket(&nack);
-
- // The next call should trigger retransmitting 2 more packets.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ // 14 packets have been NACK'd and lost. In TCP cubic, PRR limits
+ // the retransmission rate in the case of burst losses.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14);
ProcessAckPacket(&nack);
}
// Test sending multiple acks from the connection to the session.
-TEST_F(QuicConnectionTest, MultipleAcks) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6);
+TEST_P(QuicConnectionTest, MultipleAcks) {
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
@@ -1839,55 +2064,79 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
EXPECT_EQ(6u, last_packet);
// Client will ack packets 1, 2, [!3], 4, 5.
- QuicAckFrame frame1(5, QuicTime::Zero(), 0);
- frame1.received_info.missing_packets.insert(3);
- frame1.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2);
-
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame1 = InitAckFrame(5, 0);
+ NackPacket(3, &frame1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
ProcessAckPacket(&frame1);
// Now the client implicitly acks 3, and explicitly acks 6.
- QuicAckFrame frame2(6, QuicTime::Zero(), 0);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
-
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame2 = InitAckFrame(6, 0);
ProcessAckPacket(&frame2);
}
-TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
+TEST_P(QuicConnectionTest, DontLatchUnackedPacket) {
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1;
+ // From now on, we send acks, so the send algorithm won't mark them pending.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
SendAckPacketToPeer(); // Packet 2
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1, 0);
ProcessAckPacket(&frame);
- // Verify that our internal state has least-unacked as 3.
+ // Verify that our internal state has least-unacked as 2, because we're still
+ // waiting for a potential ack for 2.
+ EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked);
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(2, 0);
+ ProcessAckPacket(&frame);
EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked);
// When we send an ack, we make sure our least-unacked makes sense. In this
// case since we're not waiting on an ack for 2 and all packets are acked, we
// set it to 3.
SendAckPacketToPeer(); // Packet 3
- // Since this was an ack packet, we set least_unacked to 4.
- EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ // Least_unacked remains at 3 until another ack is received.
+ EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked);
// Check that the outgoing ack had its sequence number as least_unacked.
- EXPECT_EQ(3u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(3u, least_unacked());
+ // Ack the ack, which updates the rtt and raises the least unacked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(3, 0);
+ ProcessAckPacket(&frame);
+
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
SendStreamDataToPeer(1, "bar", 3, false, NULL); // Packet 4
EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
SendAckPacketToPeer(); // Packet 5
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
+
+ // Send two data packets at the end, and ensure if the last one is acked,
+ // the least unacked is raised above the ack packets.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ SendStreamDataToPeer(1, "bar", 6, false, NULL); // Packet 6
+ SendStreamDataToPeer(1, "bar", 9, false, NULL); // Packet 7
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(7, 0);
+ NackPacket(5, &frame);
+ NackPacket(6, &frame);
+ ProcessAckPacket(&frame);
+
+ EXPECT_EQ(6u, outgoing_ack()->sent_info.least_unacked);
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
@@ -1896,7 +2145,63 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingSeqNumLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ scoped_ptr<FecQuicConnectionDebugVisitor>
+ fec_visitor(new FecQuicConnectionDebugVisitor);
+ connection_.set_debug_visitor(fec_visitor.get());
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicSequenceNumberLength lengths[] = {PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_4BYTE_SEQUENCE_NUMBER,
+ PACKET_2BYTE_SEQUENCE_NUMBER,
+ PACKET_1BYTE_SEQUENCE_NUMBER};
+ // For each sequence number length size, revive a packet and check sequence
+ // number length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set sequence_number_length_ (for data and FEC packets).
+ sequence_number_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL);
+ // Sequence number length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(sequence_number_length_, fec_visitor->revived_header().
+ public_header.sequence_number_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingConnectionIdLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ scoped_ptr<FecQuicConnectionDebugVisitor>
+ fec_visitor(new FecQuicConnectionDebugVisitor);
+ connection_.set_debug_visitor(fec_visitor.get());
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicConnectionIdLength lengths[] = {PACKET_8BYTE_CONNECTION_ID,
+ PACKET_4BYTE_CONNECTION_ID,
+ PACKET_1BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID};
+ // For each connection id length size, revive a packet and check connection
+ // id length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set connection id length (for data and FEC packets).
+ connection_id_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL);
+ // Connection id length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(connection_id_length_,
+ fec_visitor->revived_header().public_header.connection_id_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFecProtectedPacket(1, false, kEntropyFlag);
@@ -1906,18 +2211,19 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
+TEST_P(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);
- // Entropy flag should be true, so entropy should not be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure QUIC no longer revives entropy for lost packets.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 4));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
@@ -1928,7 +2234,7 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFecProtectedPacket(1, false, !kEntropyFlag);
@@ -1937,31 +2243,52 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
ProcessFecProtectedPacket(3, false, kEntropyFlag);
ProcessFecProtectedPacket(4, false, kEntropyFlag);
ProcessFecProtectedPacket(5, true, !kEntropyFlag);
- // Entropy flag should be true, so entropy should be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure entropy is not revived for the missing packet.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 3));
}
-TEST_F(QuicConnectionTest, RTO) {
+TEST_P(QuicConnectionTest, TLP) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(&connection_), 1);
+
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+ QuicTime retransmission_time =
+ connection_.GetRetransmissionAlarm()->deadline();
+ EXPECT_NE(QuicTime::Zero(), retransmission_time);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ // Simulate the retransmission alarm firing and sending a tlp,
+ // so send algorithm's OnRetransmissionTimeout is not called.
+ clock_.AdvanceTime(retransmission_time.Subtract(clock_.Now()));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+}
+
+TEST_P(QuicConnectionTest, RTO) {
QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
DefaultRetransmissionTime());
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
- EXPECT_EQ(1u, last_header()->packet_sequence_number);
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
EXPECT_EQ(default_retransmission_time,
connection_.GetRetransmissionAlarm()->deadline());
// Simulate the retransmission alarm firing.
clock_.AdvanceTime(DefaultRetransmissionTime());
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1u, _));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2u, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
connection_.GetRetransmissionAlarm()->Fire();
- EXPECT_EQ(2u, last_header()->packet_sequence_number);
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
// We do not raise the high water mark yet.
EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
}
-TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
+TEST_P(QuicConnectionTest, RTOWithSameEncryptionLevel) {
QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
DefaultRetransmissionTime());
use_tagging_decrypter();
@@ -1969,23 +2296,21 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
// A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
// the end of the packet. We can test this to check which encrypter was used.
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
- EXPECT_EQ(0x01010101u, final_bytes_of_last_packet());
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
- EXPECT_EQ(0x02020202u, final_bytes_of_last_packet());
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
EXPECT_EQ(default_retransmission_time,
connection_.GetRetransmissionAlarm()->deadline());
{
InSequence s;
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 3, _, RTO_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 4, _, RTO_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 3, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _));
}
// Simulate the retransmission alarm firing.
@@ -1993,23 +2318,23 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
connection_.GetRetransmissionAlarm()->Fire();
// Packet should have been sent with ENCRYPTION_NONE.
- EXPECT_EQ(0x01010101u, final_bytes_of_previous_packet());
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet());
// Packet should have been sent with ENCRYPTION_INITIAL.
- EXPECT_EQ(0x02020202u, final_bytes_of_last_packet());
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
}
-TEST_F(QuicConnectionTest, SendHandshakeMessages) {
+TEST_P(QuicConnectionTest, SendHandshakeMessages) {
use_tagging_decrypter();
// A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
// the end of the packet. We can test this to check which encrypter was used.
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
- // Attempt to send a handshake message while the congestion manager
- // does not permit sending.
+ // Attempt to send a handshake message and have the socket block.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Infinite()));
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
// The packet should be serialized, but not queued.
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -2019,43 +2344,43 @@ TEST_F(QuicConnectionTest, SendHandshakeMessages) {
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
// Now become writeable and flush the packets.
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ writer_->SetWritable();
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Verify that the handshake packet went out at the null encryption.
- EXPECT_EQ(0x01010101u, final_bytes_of_last_packet());
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
}
-TEST_F(QuicConnectionTest,
+TEST_P(QuicConnectionTest,
DropRetransmitsForNullEncryptedPacketAfterForwardSecure) {
use_tagging_decrypter();
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
QuicPacketSequenceNumber sequence_number;
- SendStreamDataToPeer(1, "foo", 0, !kFin, &sequence_number);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, &sequence_number);
+ // Simulate the retransmission alarm firing and the socket blocking.
+ BlockOnNextWrite();
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Go forward secure.
connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
new TaggingEncrypter(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.NeuterUnencryptedPackets();
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(sequence_number, _)).Times(1);
-
- QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
- DefaultRetransmissionTime());
-
- EXPECT_EQ(default_retransmission_time,
+ EXPECT_EQ(QuicTime::Zero(),
connection_.GetRetransmissionAlarm()->deadline());
- // Simulate the retransmission alarm firing.
- clock_.AdvanceTime(DefaultRetransmissionTime());
- connection_.GetRetransmissionAlarm()->Fire();
+ // Unblock the socket and ensure that no packets are sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
}
-TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
+TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
use_tagging_decrypter();
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE);
@@ -2066,14 +2391,12 @@ TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
SendStreamDataToPeer(2, "bar", 0, !kFin, NULL);
-
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(1);
connection_.RetransmitUnackedPackets(INITIAL_ENCRYPTION_ONLY);
}
-TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
+TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
use_tagging_decrypter();
@@ -2082,68 +2405,66 @@ TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
// Process an encrypted packet which can not yet be decrypted
// which should result in the packet being buffered.
- ProcessDataPacketAtLevel(1, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ ProcessDataPacketAtLevel(1, 0, kEntropyFlag, ENCRYPTION_INITIAL);
// Transition to the new encryption state and process another
// encrypted packet which should result in the original packet being
// processed.
- connection_.SetDecrypter(new StrictTaggingDecrypter(tag));
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
- EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2).WillRepeatedly(
- Return(true));
- ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2);
+ ProcessDataPacketAtLevel(2, 0, kEntropyFlag, ENCRYPTION_INITIAL);
// Finally, process a third packet and note that we do not
// reprocess the buffered packet.
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
- ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(3, 0, kEntropyFlag, ENCRYPTION_INITIAL);
}
-TEST_F(QuicConnectionTest, TestRetransmitOrder) {
+TEST_P(QuicConnectionTest, TestRetransmitOrder) {
QuicByteCount first_packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
- DoAll(SaveArg<2>(&first_packet_size), Return(true)));
+ DoAll(SaveArg<3>(&first_packet_size), Return(true)));
connection_.SendStreamDataWithString(3, "first_packet", 0, !kFin, NULL);
QuicByteCount second_packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
- DoAll(SaveArg<2>(&second_packet_size), Return(true)));
+ DoAll(SaveArg<3>(&second_packet_size), Return(true)));
connection_.SendStreamDataWithString(3, "second_packet", 12, !kFin, NULL);
EXPECT_NE(first_packet_size, second_packet_size);
// Advance the clock by huge time to make sure packets will be retransmitted.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
{
InSequence s;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, first_packet_size, _, _));
+ OnPacketSent(_, _, _, first_packet_size, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size, _, _));
+ OnPacketSent(_, _, _, second_packet_size, _));
}
connection_.GetRetransmissionAlarm()->Fire();
// Advance again and expect the packets to be sent again in the same order.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
{
InSequence s;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, first_packet_size, _, _));
+ OnPacketSent(_, _, _, first_packet_size, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size, _, _));
+ OnPacketSent(_, _, _, second_packet_size, _));
}
connection_.GetRetransmissionAlarm()->Fire();
}
-TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
+TEST_P(QuicConnectionTest, RetransmissionCountCalculation) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketSequenceNumber original_sequence_number;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&original_sequence_number), Return(true)));
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original_sequence_number), Return(true)));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -2151,12 +2472,10 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
&connection_, original_sequence_number));
// Force retransmission due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- OnPacketAbandoned(original_sequence_number, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
QuicPacketSequenceNumber rto_sequence_number;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&rto_sequence_number), Return(true)));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&rto_sequence_number), Return(true)));
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -2165,28 +2484,24 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
EXPECT_TRUE(QuicConnectionPeer::IsRetransmission(
&connection_, rto_sequence_number));
// Once by explicit nack.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_,
- OnPacketAbandoned(rto_sequence_number, _)).Times(1);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(rto_sequence_number);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
QuicPacketSequenceNumber nack_sequence_number = 0;
// Ack packets might generate some other packets, which are not
// retransmissions. (More ack packets).
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NACK_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(original_sequence_number);
- ack.received_info.missing_packets.insert(rto_sequence_number);
- ack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_,
- rto_sequence_number - 1) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_,
- original_sequence_number);
- for (int i = 0; i < 3; i++) {
- ProcessAckPacket(&ack);
- }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&nack_sequence_number), Return(true)));
+ QuicAckFrame ack = InitAckFrame(rto_sequence_number, 0);
+ // Nack the retransmitted packet.
+ NackPacket(original_sequence_number, &ack);
+ NackPacket(rto_sequence_number, &ack);
+ ProcessAckPacket(&ack);
+
ASSERT_NE(0u, nack_sequence_number);
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, rto_sequence_number));
@@ -2196,48 +2511,46 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
&connection_, nack_sequence_number));
}
-TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
- writer_->set_blocked(true);
+TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
// Make sure that RTO is not started when the packet is queued.
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
// Test that RTO is started once we write to the socket.
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
+TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(2);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(2, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, NULL);
QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm();
EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(clock_.Now().Add(DefaultRetransmissionTime()),
+ retransmission_alarm->deadline());
// Advance the time right before the RTO, then receive an ack for the first
// packet to delay the RTO.
clock_.AdvanceTime(DefaultRetransmissionTime());
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- QuicAckFrame ack(1, QuicTime::Zero(), 0);
- ack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack = InitAckFrame(1, 0);
ProcessAckPacket(&ack);
EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_GT(retransmission_alarm->deadline(), clock_.Now());
// Move forward past the original RTO and ensure the RTO is still pending.
- clock_.AdvanceTime(DefaultRetransmissionTime());
+ clock_.AdvanceTime(DefaultRetransmissionTime().Multiply(2));
// Ensure the second packet gets retransmitted when it finally fires.
EXPECT_TRUE(retransmission_alarm->IsSet());
- EXPECT_GE(retransmission_alarm->deadline(), clock_.ApproximateNow());
- clock_.AdvanceTime(DefaultRetransmissionTime());
EXPECT_LT(retransmission_alarm->deadline(), clock_.ApproximateNow());
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
// Manually cancel the alarm to simulate a real test.
connection_.GetRetransmissionAlarm()->Fire();
@@ -2245,29 +2558,24 @@ TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
// than previously.
EXPECT_TRUE(retransmission_alarm->IsSet());
QuicTime next_rto_time = retransmission_alarm->deadline();
- QuicTime::Delta expected_rto =
- connection_.sent_packet_manager().GetRetransmissionDelay();
- EXPECT_EQ(next_rto_time, clock_.ApproximateNow().Add(expected_rto));
+ QuicTime expected_rto_time =
+ connection_.sent_packet_manager().GetRetransmissionTime();
+ EXPECT_EQ(next_rto_time, expected_rto_time);
}
-TEST_F(QuicConnectionTest, TestQueued) {
+TEST_P(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
- // 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.
- writer_->set_blocked(false);
- EXPECT_TRUE(connection_.OnCanWrite());
+ writer_->SetWritable();
+ connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, CloseFecGroup) {
+TEST_P(QuicConnectionTest, CloseFecGroup) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
// Don't send missing packet 2.
@@ -2276,45 +2584,52 @@ TEST_F(QuicConnectionTest, CloseFecGroup) {
ASSERT_EQ(1u, connection_.NumFecGroups());
// Now send non-fec protected ack packet and close the group.
- QuicAckFrame frame(0, QuicTime::Zero(), 5);
- creator_.set_sequence_number(4);
- ProcessAckPacket(&frame);
+ peer_creator_.set_sequence_number(4);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(5);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame frame = InitAckFrame(0, 5);
+ ProcessAckPacket(&frame);
+ }
ASSERT_EQ(0u, connection_.NumFecGroups());
}
-TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, NoQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
- EXPECT_TRUE(last_feedback() == NULL);
+ EXPECT_TRUE(writer_->feedback_frames().empty());
}
-TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
QuicCongestionFeedbackFrame info;
info.type = kFixRate;
info.fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(123);
SetFeedback(&info);
SendAckPacketToPeer();
- EXPECT_EQ(kFixRate, last_feedback()->type);
- EXPECT_EQ(info.fix_rate.bitrate, last_feedback()->fix_rate.bitrate);
+ ASSERT_FALSE(writer_->feedback_frames().empty());
+ ASSERT_EQ(kFixRate, writer_->feedback_frames()[0].type);
+ ASSERT_EQ(info.fix_rate.bitrate,
+ writer_->feedback_frames()[0].fix_rate.bitrate);
}
-TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
}
-TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
+TEST_P(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendAckPacketToPeer();
// Process an FEC packet, and revive the missing data packet
// but only contact the receive_algorithm once.
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL);
}
-TEST_F(QuicConnectionTest, InitialTimeout) {
+TEST_P(QuicConnectionTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
@@ -2331,13 +2646,59 @@ TEST_F(QuicConnectionTest, InitialTimeout) {
EXPECT_FALSE(connection_.connected());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet());
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, TimeoutAfterSend) {
+TEST_P(QuicConnectionTest, PingAfterSend) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ // Advance to 5ms, and send a packet to the peer, which will set
+ // the ping alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ SendStreamDataToPeer(1, "GET /", 0, kFin, NULL);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now recevie and ACK of the previous packet, which will move the
+ // ping alarm forward.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicAckFrame frame = InitAckFrame(1, 0);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)),
+ connection_.GetPingAlarm()->deadline());
+
+ writer_->Reset();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15));
+ connection_.GetPingAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ if (version() > QUIC_VERSION_17) {
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+ } else {
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(0u, writer_->stream_frames()[0].offset);
+ }
+ writer_->Reset();
+
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ SendAckPacketToPeer();
+
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
QuicTime default_timeout = clock_.ApproximateNow().Add(
@@ -2373,12 +2734,11 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_FALSE(connection_.connected());
}
-// TODO(ianswett): Add scheduler tests when should_retransmit is false.
-TEST_F(QuicConnectionTest, SendScheduler) {
+TEST_P(QuicConnectionTest, SendScheduler) {
// Test that if we send a packet without delay, it is not queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendPacket(
@@ -2386,47 +2746,35 @@ TEST_F(QuicConnectionTest, SendScheduler) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelay) {
+TEST_P(QuicConnectionTest, SendSchedulerDelay) {
// Test that if we send a packet with a delay, it ends up queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerForce) {
- // Test that if we force send a packet, it is not queued.
+TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ BlockOnNextWrite();
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NACK_RETRANSMISSION, _, _)).Times(0);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- connection_.SendPacket(
- ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
- // XXX: fixme. was: connection_.SendPacket(1, packet, kForce);
- EXPECT_EQ(0u, connection_.NumQueuedPackets());
-}
-
-TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) {
- QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- writer_->set_blocked(true);
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenSend) {
// Test that if we send a packet with a delay, it ends up queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2435,7 +2783,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
// 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(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
@@ -2443,43 +2791,35 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _))
- .WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _));
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
+ CongestionUnblockWrites();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the time for retransmission of lost packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501));
// Test that if we send a retransmit with a delay, it ends up queued in the
// sent packet manager, but not yet serialized.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, RTO_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ CongestionBlockWrites();
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_EQ(0u, 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(_, RTO_RETRANSMISSION, _, _)).Times(2).
- WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+ CongestionUnblockWrites();
// Ensure the scheduler is notified this is a retransmit.
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, RTO_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayAndQueue) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2492,11 +2832,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2504,9 +2844,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
- QuicAckFrame frame(0, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(0, 1);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, _, _));
@@ -2517,11 +2857,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2529,41 +2869,42 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
- QuicAckFrame frame(0, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(0, 1);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
ProcessAckPacket(&frame);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) {
+ // TODO(ianswett): This test is unrealistic, because we would not serialize
+ // new data if the send algorithm said not to.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ CongestionBlockWrites();
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
- // OnCanWrite should not send the packet (because of the delay)
- // but should still return true.
- EXPECT_TRUE(connection_.OnCanWrite());
- EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ // OnCanWrite should send the packet, because it won't consult the send
+ // algorithm for queued packets.
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
+TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
- connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
- NOT_IN_FEC_GROUP, &payload_length);
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
// Queue the first packet.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
const string payload(payload_length, 'a');
EXPECT_EQ(0u,
@@ -2572,27 +2913,66 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
+TEST_P(QuicConnectionTest, LoopThroughSendingPackets) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 16K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
// Queue the first packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(7);
- // The first stream frame will consume 2 fewer bytes than the other six.
- const string payload(payload_length * 7 - 12, 'a');
+ // The first stream frame will have 2 fewer overhead bytes than the other six.
+ const string payload(payload_length * 7 + 2, 'a');
EXPECT_EQ(payload.size(),
connection_.SendStreamDataWithString(1, payload, 0,
!kFin, NULL).bytes_consumed);
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) {
+TEST_P(QuicConnectionTest, SendDelayedAck) {
QuicTime ack_time = clock_.ApproximateNow().Add(DefaultDelayedAckTime());
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8 tag = 0x07;
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
+ framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+ // instead of ENCRYPTION_NONE.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(1, 0, !kEntropyFlag, ENCRYPTION_INITIAL);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendEarlyDelayedAckForCrypto) {
+ QuicTime ack_time = clock_.ApproximateNow();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // Process a packet from the crypto stream, which is frame1_'s default.
ProcessPacket(1);
// Check if delayed ack timer is running for the expected interval.
EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -2600,90 +2980,185 @@ TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) {
// Simulate delayed ack alarm firing.
connection_.GetAckAlarm()->Fire();
// Check that ack is sent and that delayed ack alarm is reset.
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
ProcessPacket(2);
// Check that ack is sent and that delayed ack alarm is reset.
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, NoAckOnOldNacks) {
+TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Drop one packet, triggering a sequence of acks.
ProcessPacket(2);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ size_t frames_per_ack = version() > QUIC_VERSION_15 ? 2 : 1;
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(3);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(4);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(5);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- // Now only set the timer on the 6th packet, instead of sending another ack.
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
+ // Now only set the timer on the 6th packet, instead of sending another ack.
ProcessPacket(6);
EXPECT_EQ(0u, writer_->frame_count());
EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- connection_.SendStreamDataWithString(kStreamId3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0,
+ !kFin, NULL);
// Check that ack is bundled with outgoing data and that delayed ack
// alarm is reset.
- EXPECT_EQ(2u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, DontSendDelayedAckOnOutgoingCryptoPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
connection_.SendStreamDataWithString(kCryptoStreamId, "foo", 0, !kFin, NULL);
- // Check that ack is not bundled with outgoing data.
+ // Check that ack is bundled with outgoing crypto data.
+ EXPECT_EQ(version() <= QUIC_VERSION_15 ? 2u : 3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendCryptoStreamData)));
+ // Process a packet from the crypto stream, which is frame1_'s default.
+ // Receiving the CHLO as packet 2 first will cause the connection to
+ // immediately send an ack, due to the packet gap.
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0,
+ !kFin, NULL);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 3,
+ !kFin, NULL);
+ // Ack the second packet, which will retransmit the first packet.
+ QuicAckFrame ack = InitAckFrame(2, 0);
+ NackPacket(1, &ack);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_FALSE(writer_->ack());
- EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ writer_->Reset();
+
+ // Now ack the retransmission, which will both raise the high water mark
+ // and see if there is more data to send.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // Check that no packet is sent and the ack alarm isn't set.
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ writer_->Reset();
+
+ // Send the same ack, but send both data and an ack together.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_,
+ &TestConnection::EnsureWritableAndSendStreamData5)));
+ ProcessAckPacket(&ack);
+
+ // Check that ack is bundled with outgoing data and the delayed ack
+ // alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, NoAckForClose) {
+TEST_P(QuicConnectionTest, NoAckSentForClose) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(0);
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
ProcessClosePacket(2, 0);
}
-TEST_F(QuicConnectionTest, SendWhenDisconnected) {
+TEST_P(QuicConnectionTest, SendWhenDisconnected) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, false));
connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false);
EXPECT_FALSE(connection_.connected());
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
}
-TEST_F(QuicConnectionTest, PublicReset) {
+TEST_P(QuicConnectionTest, PublicReset) {
QuicPublicResetPacket header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = true;
header.public_header.version_flag = false;
header.rejected_sequence_number = 10101;
@@ -2693,7 +3168,7 @@ TEST_F(QuicConnectionTest, PublicReset) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet);
}
-TEST_F(QuicConnectionTest, GoAway) {
+TEST_P(QuicConnectionTest, GoAway) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicGoAwayFrame goaway;
@@ -2704,27 +3179,52 @@ TEST_F(QuicConnectionTest, GoAway) {
ProcessGoAwayPacket(&goaway);
}
-TEST_F(QuicConnectionTest, InvalidPacket) {
+TEST_P(QuicConnectionTest, WindowUpdate) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+}
+
+TEST_P(QuicConnectionTest, Blocked) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+}
+
+TEST_P(QuicConnectionTest, InvalidPacket) {
EXPECT_CALL(visitor_,
OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
QuicEncryptedPacket encrypted(NULL, 0);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), encrypted);
// The connection close packet should have error details.
- ASSERT_TRUE(last_close() != NULL);
- EXPECT_EQ("Unable to read public flags.", last_close()->error_details);
+ ASSERT_FALSE(writer_->connection_close_frames().empty());
+ EXPECT_EQ("Unable to read public flags.",
+ writer_->connection_close_frames()[0].error_details);
}
-TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
- QuicAckFrame ack(0, QuicTime::Zero(), 4);
+TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
// Set the sequence number of the ack packet to be least unacked (4).
- creator_.set_sequence_number(3);
+ peer_creator_.set_sequence_number(3);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- ProcessAckPacket(&ack);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 4);
+ ProcessAckPacket(&ack);
+ }
EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty());
}
-TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculation) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(4, 1, kEntropyFlag);
@@ -2733,40 +3233,66 @@ TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculationHalfFEC) {
+ // FEC packets should not change the entropy hash calculation.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessFecPacket(4, 1, false, kEntropyFlag, NULL);
+ ProcessDataPacket(3, 3, !kEntropyFlag);
+ ProcessFecPacket(7, 3, false, kEntropyFlag, NULL);
+ EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, kEntropyFlag);
ProcessDataPacket(4, 1, !kEntropyFlag);
EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash);
// Make 4th packet my least unacked, and update entropy for 2, 3 packets.
- QuicAckFrame ack(0, QuicTime::Zero(), 4);
- QuicPacketEntropyHash kRandomEntropyHash = 129u;
- ack.sent_info.entropy_hash = kRandomEntropyHash;
- creator_.set_sequence_number(5);
+ peer_creator_.set_sequence_number(5);
QuicPacketEntropyHash six_packet_entropy_hash = 0;
- if (ProcessAckPacket(&ack)) {
- six_packet_entropy_hash = 1 << 6;
+ QuicPacketEntropyHash kRandomEntropyHash = 129u;
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ frame.entropy_hash = kRandomEntropyHash;
+ if (ProcessStopWaitingPacket(&frame)) {
+ six_packet_entropy_hash = 1 << 6;
+ }
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 4);
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ if (ProcessAckPacket(&ack)) {
+ six_packet_entropy_hash = 1 << 6;
+ }
}
EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash),
outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, !kEntropyFlag);
ProcessDataPacket(22, 1, kEntropyFlag);
EXPECT_EQ(66u, outgoing_ack()->received_info.entropy_hash);
- creator_.set_sequence_number(22);
+ peer_creator_.set_sequence_number(22);
QuicPacketEntropyHash kRandomEntropyHash = 85u;
// Current packet is the least unacked packet.
- QuicAckFrame ack(0, QuicTime::Zero(), 23);
- ack.sent_info.entropy_hash = kRandomEntropyHash;
- QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack);
+ QuicPacketEntropyHash ack_entropy_hash;
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(23);
+ frame.entropy_hash = kRandomEntropyHash;
+ ack_entropy_hash = ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 23);
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ ack_entropy_hash = ProcessAckPacket(&ack);
+ }
EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash),
outgoing_ack()->received_info.entropy_hash);
ProcessDataPacket(25, 1, kEntropyFlag);
@@ -2774,13 +3300,13 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketEntropyHash entropy[51];
entropy[0] = 0;
for (int i = 1; i < 51; ++i) {
- bool should_send = i % 10 != 0;
+ bool should_send = i % 10 != 1;
bool entropy_flag = (i & (i - 1)) != 0;
if (!should_send) {
entropy[i] = entropy[i - 1];
@@ -2793,15 +3319,14 @@ TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
}
ProcessDataPacket(i, 1, entropy_flag);
}
- // Till 50 since 50th packet is not sent.
for (int i = 1; i < 50; ++i) {
EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash(
&connection_, i));
}
}
-TEST_F(QuicConnectionTest, CheckSentEntropyHash) {
- creator_.set_sequence_number(1);
+TEST_P(QuicConnectionTest, CheckSentEntropyHash) {
+ peer_creator_.set_sequence_number(1);
SequenceNumberSet missing_packets;
QuicPacketEntropyHash entropy_hash = 0;
QuicPacketSequenceNumber max_sequence_number = 51;
@@ -2829,11 +3354,12 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) {
<< "";
}
-TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2845,17 +3371,17 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_TRUE(writer_->version_negotiation_packet() != NULL);
size_t num_versions = arraysize(kSupportedQuicVersions);
- EXPECT_EQ(num_versions,
+ ASSERT_EQ(num_versions,
writer_->version_negotiation_packet()->versions.size());
// We expect all versions in kSupportedQuicVersions to be
@@ -2866,11 +3392,12 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
}
}
-TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2882,24 +3409,23 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
- writer_->set_blocked(true);
+ BlockOnNextWrite();
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
EXPECT_TRUE(connection_.HasQueuedData());
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_TRUE(writer_->version_negotiation_packet() != NULL);
size_t num_versions = arraysize(kSupportedQuicVersions);
- EXPECT_EQ(num_versions,
+ ASSERT_EQ(num_versions,
writer_->version_negotiation_packet()->versions.size());
// We expect all versions in kSupportedQuicVersions to be
@@ -2910,12 +3436,13 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
}
}
-TEST_F(QuicConnectionTest,
+TEST_P(QuicConnectionTest,
ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2927,27 +3454,26 @@ TEST_F(QuicConnectionTest,
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
- writer_->set_blocked(true);
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
EXPECT_FALSE(connection_.HasQueuedData());
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
}
-TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
+TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) {
// Start out with some unsupported version.
QuicConnectionPeer::GetFramer(&connection_)->set_version_for_tests(
QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2973,7 +3499,7 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -2983,9 +3509,9 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
QuicConnectionPeer::GetPacketCreator(&connection_)));
}
-TEST_F(QuicConnectionTest, BadVersionNegotiation) {
+TEST_P(QuicConnectionTest, BadVersionNegotiation) {
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -3009,51 +3535,37 @@ TEST_F(QuicConnectionTest, BadVersionNegotiation) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
-TEST_F(QuicConnectionTest, CheckSendStats) {
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+TEST_P(QuicConnectionTest, CheckSendStats) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendStreamDataWithString(3, "first", 0, !kFin, NULL);
- size_t first_packet_size = last_sent_packet_size();
+ size_t first_packet_size = writer_->last_packet_size();
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendStreamDataWithString(5, "second", 0, !kFin, NULL);
- size_t second_packet_size = last_sent_packet_size();
+ size_t second_packet_size = writer_->last_packet_size();
// 2 retransmissions due to rto, 1 due to explicit nack.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)).Times(2);
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NACK_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(3);
- EXPECT_CALL(visitor_, OnCanWrite()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
// Retransmit due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
connection_.GetRetransmissionAlarm()->Fire();
// Retransmit due to explicit nacks.
- QuicAckFrame nack_three(4, QuicTime::Zero(), 0);
- nack_three.received_info.missing_packets.insert(3);
- nack_three.received_info.missing_packets.insert(1);
- nack_three.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- QuicFrame frame(&nack_three);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(visitor_, OnCanWrite()).Times(4).WillRepeatedly(Return(true));
- EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
- ProcessFramePacket(frame);
- ProcessFramePacket(frame);
- ProcessFramePacket(frame);
-
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce(
- Return(QuicTime::Delta::Zero()));
+ QuicAckFrame nack_three = InitAckFrame(4, 0);
+ NackPacket(3, &nack_three);
+ NackPacket(1, &nack_three);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ lost_packets.insert(3);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessAckPacket(&nack_three);
+
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
Return(QuicBandwidth::Zero()));
@@ -3067,7 +3579,7 @@ TEST_F(QuicConnectionTest, CheckSendStats) {
EXPECT_EQ(1u, stats.rto_count);
}
-TEST_F(QuicConnectionTest, CheckReceiveStats) {
+TEST_P(QuicConnectionTest, CheckReceiveStats) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
size_t received_bytes = 0;
@@ -3077,8 +3589,6 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) {
received_bytes += ProcessDataPacket(3, 1, !kEntropyFlag);
received_bytes += ProcessFecPacket(4, 1, true, !kEntropyFlag, NULL);
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce(
- Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
Return(QuicBandwidth::Zero()));
@@ -3090,7 +3600,7 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) {
EXPECT_EQ(1u, stats.packets_dropped);
}
-TEST_F(QuicConnectionTest, TestFecGroupLimits) {
+TEST_P(QuicConnectionTest, TestFecGroupLimits) {
// Create and return a group for 1.
ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL);
@@ -3112,9 +3622,9 @@ TEST_F(QuicConnectionTest, TestFecGroupLimits) {
ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL);
}
-TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
+TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
// Construct a packet with stream frame and connection close frame.
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.packet_sequence_number = 1;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
@@ -3131,7 +3641,7 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
frames.push_back(stream_frame);
frames.push_back(close_frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header_, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet);
EXPECT_TRUE(NULL != packet.get());
scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
ENCRYPTION_NONE, 1, *packet));
@@ -3143,7 +3653,8 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
-TEST_F(QuicConnectionTest, SelectMutualVersion) {
+TEST_P(QuicConnectionTest, SelectMutualVersion) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
// Set the connection to speak the lowest quic version.
connection_.set_version(QuicVersionMin());
EXPECT_EQ(QuicVersionMin(), connection_.version());
@@ -3172,74 +3683,61 @@ TEST_F(QuicConnectionTest, SelectMutualVersion) {
EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version));
}
-TEST_F(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) {
- writer_->set_blocked(false); // Already default.
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) {
+ EXPECT_FALSE(writer_->IsWriteBlocked());
- // Send a packet (but write will not block).
+ // Send a packet.
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
+ TriggerConnectionClose();
EXPECT_EQ(2u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
- EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) {
+ BlockOnNextWrite();
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+}
- // Send a packet to so that write will really block.
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
-
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
- EXPECT_EQ(1u, writer_->packets_write_attempts());
-}
-
-TEST_F(QuicConnectionTest, ConnectionCloseWhenNothingPending) {
- writer_->set_blocked(true);
-
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ TriggerConnectionClose();
EXPECT_EQ(1u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) {
+TEST_P(QuicConnectionTest, AckNotifierTriggerCallback) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Send some data, which will register the delegate to be notified.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Process an ACK from the server which should trigger the callback.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1, 0);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
+TEST_P(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(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(0);
// Send some data, which will register the delegate to be notified. This will
// not be ACKed and so the delegate should never be called.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Send some other data which we will ACK.
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
@@ -3247,82 +3745,183 @@ TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
// Now we receive ACK for packets 2 and 3, but importantly missing packet 1
// which we registered to be notified about.
- QuicAckFrame frame(3, QuicTime::Zero(), 0);
- frame.received_info.missing_packets.insert(1);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _));
+ QuicAckFrame frame = InitAckFrame(3, 0);
+ NackPacket(1, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
-
- // In total expect ACKs for all 4 packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(4);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Send four packets, and register to be notified on ACK of packet 2.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "bar", 0, !kFin, &delegate);
- connection_.SendStreamDataWithString(1, "baz", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "qux", 0, !kFin, NULL);
-
- // Now we receive ACK for packets 1, 3, and 4, which invokes fast retransmit.
- QuicAckFrame frame(4, QuicTime::Zero(), 0);
- frame.received_info.missing_packets.insert(2);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4, 0);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
ProcessAckPacket(&frame);
// Now we get an ACK for packet 5 (retransmitted packet 2), which should
// trigger the callback.
- QuicAckFrame second_ack_frame(5, QuicTime::Zero(), 0);
- second_ack_frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(5, 0);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that timed
+// out and was retransmitted, even though the retransmission has a
+// different sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckAfterRTO) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
+ DefaultRetransmissionTime());
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, delegate.get());
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+
+ // Ack the original packet.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_frame = InitAckFrame(1, 0);
+ ProcessAckPacket(&ack_frame);
+
+ // Delegate is not notified again when the retransmit is acked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(2, 0);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that was
+// previously nacked, even though the retransmission has a different
+// sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckOfNackedPacket) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ // Send four packets, and register to be notified on ACK of packet 2.
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4, 0);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Now we get an ACK for packet 2, which was previously nacked.
+ SequenceNumberSet no_lost_packets;
+ EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ QuicAckFrame second_ack_frame = InitAckFrame(4, 0);
ProcessAckPacket(&second_ack_frame);
+
+ // Verify that the delegate is not notified again when the
+ // retransmit is acked.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame third_ack_frame = InitAckFrame(5, 0);
+ ProcessAckPacket(&third_ack_frame);
}
-// TODO(rjshade): Add a similar test that FEC recovery on peer (and resulting
-// ACK) triggers notification on our end.
-TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+TEST_P(QuicConnectionTest, AckNotifierFECTriggerCallback) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(visitor_, OnCanWrite()).Times(1).WillOnce(Return(true));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Send some data, which will register the delegate to be notified.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL);
+
+ // Process an ACK from the server with a revived packet, which should trigger
+ // the callback.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(2, 0);
+ NackPacket(1, &frame);
+ frame.received_info.revived_packets.insert(1);
+ ProcessAckPacket(&frame);
+ // If the ack is processed again, the notifier should not be called again.
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnCanWrite());
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Expect ACKs for 1 packet.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Send one packet, and register to be notified on ACK.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Ack packet gets dropped, but we receive an FEC packet that covers it.
// Should recover the Ack packet and trigger the notification callback.
QuicFrames frames;
- QuicAckFrame ack_frame(1, QuicTime::Zero(), 0);
- ack_frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ QuicAckFrame ack_frame = InitAckFrame(1, 0);
frames.push_back(QuicFrame(&ack_frame));
// Dummy stream frame to satisfy expectations set elsewhere.
frames.push_back(QuicFrame(&frame1_));
QuicPacketHeader ack_header;
- ack_header.public_header.guid = guid_;
+ ack_header.public_header.connection_id = connection_id_;
ack_header.public_header.reset_flag = false;
ack_header.public_header.version_flag = false;
ack_header.entropy_flag = !kEntropyFlag;
@@ -3332,7 +3931,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
ack_header.fec_group = 1;
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(ack_header, frames).packet;
+ BuildUnsizedDataPacket(&framer_, ack_header, frames).packet;
// Take the packet which contains the ACK frame, and construct and deliver an
// FEC packet which allows the ACK packet to be recovered.
@@ -3340,14 +3939,15 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
}
class MockQuicConnectionDebugVisitor
- : public QuicConnectionDebugVisitorInterface {
+ : public QuicConnectionDebugVisitor {
public:
MOCK_METHOD1(OnFrameAddedToPacket,
void(const QuicFrame&));
- MOCK_METHOD4(OnPacketSent,
+ MOCK_METHOD5(OnPacketSent,
void(QuicPacketSequenceNumber,
EncryptionLevel,
+ TransmissionType,
const QuicEncryptedPacket&,
WriteResult));
@@ -3375,6 +3975,9 @@ class MockQuicConnectionDebugVisitor
MOCK_METHOD1(OnCongestionFeedbackFrame,
void(const QuicCongestionFeedbackFrame&));
+ MOCK_METHOD1(OnStopWaitingFrame,
+ void(const QuicStopWaitingFrame&));
+
MOCK_METHOD1(OnRstStreamFrame,
void(const QuicRstStreamFrame&));
@@ -3391,7 +3994,7 @@ class MockQuicConnectionDebugVisitor
void(const QuicPacketHeader&, StringPiece payload));
};
-TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
+TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
QuicPacketHeader header;
scoped_ptr<MockQuicConnectionDebugVisitor>
@@ -3401,17 +4004,40 @@ TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
connection_.OnPacketHeader(header);
}
-TEST_F(QuicConnectionTest, Pacing) {
+TEST_P(QuicConnectionTest, Pacing) {
ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, true);
- TestConnection server(guid_, IPEndPoint(), helper_.get(), writer_.get(),
- true);
- TestConnection client(guid_, IPEndPoint(), helper_.get(), writer_.get(),
- false);
+ TestConnection server(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), true, version());
+ TestConnection client(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), false, version());
EXPECT_TRUE(client.sent_packet_manager().using_pacing());
EXPECT_FALSE(server.sent_packet_manager().using_pacing());
}
+TEST_P(QuicConnectionTest, ControlFramesInstigateAcks) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Send a WINDOW_UPDATE frame.
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+
+ // Cancel alarm, and try again with BLOCKED frame.
+ ack_alarm->Cancel();
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_client_stream.cc b/chromium/net/quic/quic_crypto_client_stream.cc
index 3e5f84059fc..3993637e104 100644
--- a/chromium/net/quic/quic_crypto_client_stream.cc
+++ b/chromium/net/quic/quic_crypto_client_stream.cc
@@ -4,42 +4,23 @@
#include "net/quic/quic_crypto_client_stream.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_errors.h"
+#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/null_encrypter.h"
#include "net/quic/crypto/proof_verifier.h"
-#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_session.h"
-#include "net/ssl/ssl_connection_status_flags.h"
-#include "net/ssl/ssl_info.h"
namespace net {
-namespace {
-
-// Copies CertVerifyResult from |verify_details| to |cert_verify_result|.
-void CopyCertVerifyResult(
- const ProofVerifyDetails* verify_details,
- scoped_ptr<CertVerifyResult>* cert_verify_result) {
- const CertVerifyResult* cert_verify_result_other =
- &(reinterpret_cast<const ProofVerifyDetailsChromium*>(
- verify_details))->cert_verify_result;
- CertVerifyResult* result_copy = new CertVerifyResult;
- result_copy->CopyFrom(*cert_verify_result_other);
- cert_verify_result->reset(result_copy);
-}
-
-} // namespace
-
QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
QuicCryptoClientStream* stream)
: stream_(stream) {}
QuicCryptoClientStream::ProofVerifierCallbackImpl::
- ~ProofVerifierCallbackImpl() {}
+~ProofVerifierCallbackImpl() {}
void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
bool ok,
@@ -63,18 +44,19 @@ void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
stream_ = NULL;
}
-
QuicCryptoClientStream::QuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config)
: QuicCryptoStream(session),
next_state_(STATE_IDLE),
num_client_hellos_(0),
crypto_config_(crypto_config),
- server_hostname_(server_hostname),
+ server_id_(server_id),
generation_counter_(0),
- proof_verify_callback_(NULL) {
+ proof_verify_callback_(NULL),
+ verify_context_(verify_context) {
}
QuicCryptoClientStream::~QuicCryptoClientStream() {
@@ -91,7 +73,7 @@ void QuicCryptoClientStream::OnHandshakeMessage(
}
bool QuicCryptoClientStream::CryptoConnect() {
- next_state_ = STATE_SEND_CHLO;
+ next_state_ = STATE_INITIALIZE;
DoHandshakeLoop(NULL);
return true;
}
@@ -100,42 +82,6 @@ int QuicCryptoClientStream::num_sent_client_hellos() const {
return num_client_hellos_;
}
-// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
-// we learn about SSL info (sync vs async vs cached).
-bool QuicCryptoClientStream::GetSSLInfo(SSLInfo* ssl_info) {
- ssl_info->Reset();
- if (!cert_verify_result_) {
- return false;
- }
-
- ssl_info->cert_status = cert_verify_result_->cert_status;
- ssl_info->cert = cert_verify_result_->verified_cert;
-
- // TODO(rtenneti): Figure out what to set for the following.
- // Temporarily hard coded cipher_suite as 0xc031 to represent
- // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (from
- // net/ssl/ssl_cipher_suite_names.cc) and encryption as 256.
- int cipher_suite = 0xc02f;
- int ssl_connection_status = 0;
- ssl_connection_status |=
- (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
- SSL_CONNECTION_CIPHERSUITE_SHIFT;
- ssl_connection_status |=
- (SSL_CONNECTION_VERSION_TLS1_2 & SSL_CONNECTION_VERSION_MASK) <<
- SSL_CONNECTION_VERSION_SHIFT;
-
- ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
- ssl_info->is_issued_by_known_root =
- cert_verify_result_->is_issued_by_known_root;
-
- ssl_info->connection_status = ssl_connection_status;
- ssl_info->client_cert_sent = false;
- ssl_info->channel_id_sent = false;
- ssl_info->security_bits = 256;
- ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
- return true;
-}
-
// kMaxClientHellos is the maximum number of times that we'll send a client
// hello. The value 3 accounts for:
// * One failure due to an incorrect or missing source-address token.
@@ -149,7 +95,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
QuicErrorCode error;
string error_details;
QuicCryptoClientConfig::CachedState* cached =
- crypto_config_->LookupOrCreate(server_hostname_);
+ crypto_config_->LookupOrCreate(server_id_);
if (in != NULL) {
DVLOG(1) << "Client: Received " << in->DebugString();
@@ -159,6 +105,17 @@ void QuicCryptoClientStream::DoHandshakeLoop(
const State state = next_state_;
next_state_ = STATE_IDLE;
switch (state) {
+ case STATE_INITIALIZE: {
+ if (!cached->IsEmpty() && !cached->signature().empty() &&
+ server_id_.is_https()) {
+ DCHECK(crypto_config_->proof_verifier());
+ // If the cached state needs to be verified, do it now.
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ next_state_ = STATE_SEND_CHLO;
+ }
+ break;
+ }
case STATE_SEND_CHLO: {
// Send the client hello in plaintext.
session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE);
@@ -170,13 +127,13 @@ void QuicCryptoClientStream::DoHandshakeLoop(
if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
crypto_config_->FillInchoateClientHello(
- server_hostname_,
+ server_id_,
session()->connection()->supported_versions().front(),
cached, &crypto_negotiated_params_, &out);
// Pad the inchoate client hello to fill up a packet.
const size_t kFramingOverhead = 50; // A rough estimate.
const size_t max_packet_size =
- session()->connection()->options()->max_packet_length;
+ session()->connection()->max_packet_length();
if (max_packet_size <= kFramingOverhead) {
DLOG(DFATAL) << "max_packet_length (" << max_packet_size
<< ") has no room for framing overhead.";
@@ -195,13 +152,43 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
session()->config()->ToHandshakeMessage(&out);
+
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ bool do_channel_id = false;
+ if (crypto_config_->channel_id_source()) {
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ DCHECK(scfg);
+ const QuicTag* their_proof_demands;
+ size_t num_their_proof_demands;
+ if (scfg->GetTaglist(kPDMD, &their_proof_demands,
+ &num_their_proof_demands) == QUIC_NO_ERROR) {
+ for (size_t i = 0; i < num_their_proof_demands; i++) {
+ if (their_proof_demands[i] == kCHID) {
+ do_channel_id = true;
+ break;
+ }
+ }
+ }
+ }
+ if (do_channel_id) {
+ QuicAsyncStatus status =
+ crypto_config_->channel_id_source()->GetChannelIDKey(
+ server_id_.host(), &channel_id_key, NULL);
+ if (status != QUIC_SUCCESS) {
+ CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ return;
+ }
+ }
+
error = crypto_config_->FillClientHello(
- server_hostname_,
- session()->connection()->guid(),
+ server_id_,
+ session()->connection()->connection_id(),
session()->connection()->supported_versions().front(),
cached,
session()->connection()->clock()->WallNow(),
session()->connection()->random_generator(),
+ channel_id_key.get(),
&crypto_negotiated_params_,
&out,
&error_details);
@@ -213,10 +200,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
if (cached->proof_verify_details()) {
- CopyCertVerifyResult(cached->proof_verify_details(),
- &cert_verify_result_);
- } else {
- cert_verify_result_.reset();
+ client_session()->OnProofVerifyDetailsAvailable(
+ *cached->proof_verify_details());
}
next_state_ = STATE_RECV_SHLO;
DVLOG(1) << "Client: Sending " << out.DebugString();
@@ -224,6 +209,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
// Be prepared to decrypt with the new server write key.
session()->connection()->SetAlternativeDecrypter(
crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL,
true /* latch once used */);
// Send subsequent packets under encryption on the assumption that the
// server will accept the handshake.
@@ -260,10 +246,9 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
if (!cached->proof_valid()) {
- ProofVerifier* verifier = crypto_config_->proof_verifier();
- if (!verifier) {
- // If no verifier is set then we don't check the certificates.
- cached->SetProofValid();
+ if (!server_id_.is_https()) {
+ // We don't check the certificates for insecure QUIC connections.
+ SetCachedProofValid(cached);
} else if (!cached->signature().empty()) {
next_state_ = STATE_VERIFY_PROOF;
break;
@@ -282,23 +267,26 @@ void QuicCryptoClientStream::DoHandshakeLoop(
verify_ok_ = false;
- ProofVerifier::Status status = verifier->VerifyProof(
- server_hostname_,
+ QuicAsyncStatus status = verifier->VerifyProof(
+ server_id_.host(),
cached->server_config(),
cached->certs(),
cached->signature(),
+ verify_context_.get(),
&verify_error_details_,
&verify_details_,
proof_verify_callback);
switch (status) {
- case ProofVerifier::PENDING:
+ case QUIC_PENDING:
proof_verify_callback_ = proof_verify_callback;
DVLOG(1) << "Doing VerifyProof";
return;
- case ProofVerifier::FAILURE:
+ case QUIC_FAILURE:
+ delete proof_verify_callback;
break;
- case ProofVerifier::SUCCESS:
+ case QUIC_SUCCESS:
+ delete proof_verify_callback;
verify_ok_ = true;
break;
}
@@ -306,7 +294,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
}
case STATE_VERIFY_PROOF_COMPLETE:
if (!verify_ok_) {
- CopyCertVerifyResult(verify_details_.get(), &cert_verify_result_);
+ client_session()->OnProofVerifyDetailsAvailable(*verify_details_);
CloseConnectionWithDetails(
QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_);
return;
@@ -316,7 +304,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
if (generation_counter_ != cached->generation_counter()) {
next_state_ = STATE_VERIFY_PROOF;
} else {
- cached->SetProofValid();
+ SetCachedProofValid(cached);
cached->SetProofVerifyDetails(verify_details_.release());
next_state_ = STATE_SEND_CHLO;
}
@@ -352,7 +340,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
error = crypto_config_->ProcessServerHello(
- *in, session()->connection()->guid(),
+ *in, session()->connection()->connection_id(),
session()->connection()->server_supported_versions(),
cached, &crypto_negotiated_params_, &error_details);
@@ -361,7 +349,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
error, "Server hello invalid: " + error_details);
return;
}
- error = session()->config()->ProcessServerHello(*in, &error_details);
+ error =
+ session()->config()->ProcessPeerHello(*in, SERVER, &error_details);
if (error != QUIC_NO_ERROR) {
CloseConnectionWithDetails(
error, "Server hello invalid: " + error_details);
@@ -376,7 +365,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
// with the FORWARD_SECURE key until it receives a FORWARD_SECURE
// packet from the client.
session()->connection()->SetAlternativeDecrypter(
- crypters->decrypter.release(), false /* don't latch */);
+ crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE,
+ false /* don't latch */);
session()->connection()->SetEncrypter(
ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release());
session()->connection()->SetDefaultEncryptionLevel(
@@ -394,4 +384,14 @@ void QuicCryptoClientStream::DoHandshakeLoop(
}
}
+void QuicCryptoClientStream::SetCachedProofValid(
+ QuicCryptoClientConfig::CachedState* cached) {
+ cached->SetProofValid();
+ client_session()->OnProofValid(*cached);
+}
+
+QuicClientSessionBase* QuicCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_client_stream.h b/chromium/net/quic/quic_crypto_client_stream.h
index ca2fea74d75..5bb50ef1506 100644
--- a/chromium/net/quic/quic_crypto_client_stream.h
+++ b/chromium/net/quic/quic_crypto_client_stream.h
@@ -7,18 +7,15 @@
#include <string>
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/x509_certificate.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
namespace net {
-class ProofVerifyDetails;
-class QuicSession;
-class SSLInfo;
+class QuicClientSessionBase;
namespace test {
class CryptoTestUtils;
@@ -26,8 +23,9 @@ class CryptoTestUtils;
class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
public:
- QuicCryptoClientStream(const string& server_hostname,
- QuicSession* session,
+ QuicCryptoClientStream(const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config);
virtual ~QuicCryptoClientStream();
@@ -45,9 +43,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
// than the number of round-trips needed for the handshake.
int num_sent_client_hellos() const;
- // Gets the SSL connection information.
- bool GetSSLInfo(SSLInfo* ssl_info);
-
private:
// ProofVerifierCallbackImpl is passed as the callback method to VerifyProof.
// The ProofVerifier calls this class with the result of proof verification
@@ -75,6 +70,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
enum State {
STATE_IDLE,
+ STATE_INITIALIZE,
STATE_SEND_CHLO,
STATE_RECV_REJ,
STATE_VERIFY_PROOF,
@@ -83,9 +79,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
};
// DoHandshakeLoop performs a step of the handshake state machine. Note that
- // |in| may be NULL if the call did not result from a received message
+ // |in| may be NULL if the call did not result from a received message.
void DoHandshakeLoop(const CryptoHandshakeMessage* in);
+ // Called to set the proof of |cached| valid. Also invokes the session's
+ // OnProofValid() method.
+ void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached);
+
+ QuicClientSessionBase* client_session();
+
State next_state_;
// num_client_hellos_ contains the number of client hello messages that this
// connection has sent.
@@ -95,8 +97,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
// Client's connection nonce (4-byte timestamp + 28 random bytes)
std::string nonce_;
- // Server's hostname
- std::string server_hostname_;
+ // Server's (hostname, port, is_https, privacy_mode) tuple.
+ const QuicServerId server_id_;
// Generation counter from QuicCryptoClientConfig's CachedState.
uint64 generation_counter_;
@@ -111,9 +113,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
bool verify_ok_;
string verify_error_details_;
scoped_ptr<ProofVerifyDetails> verify_details_;
-
- // The result of certificate verification.
- scoped_ptr<CertVerifyResult> cert_verify_result_;
+ scoped_ptr<ProofVerifyContext> verify_context_;
DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream);
};
diff --git a/chromium/net/quic/quic_crypto_client_stream_factory.h b/chromium/net/quic/quic_crypto_client_stream_factory.h
index 4fa5f573ea0..293fa8217b3 100644
--- a/chromium/net/quic/quic_crypto_client_stream_factory.h
+++ b/chromium/net/quic/quic_crypto_client_stream_factory.h
@@ -11,8 +11,9 @@
namespace net {
+class QuicClientSession;
class QuicCryptoClientStream;
-class QuicSession;
+class QuicServerId;
// An interface used to instantiate QuicCryptoClientStream objects. Used to
// facilitate testing code with mock implementations.
@@ -21,8 +22,8 @@ class NET_EXPORT QuicCryptoClientStreamFactory {
virtual ~QuicCryptoClientStreamFactory() {}
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) = 0;
};
diff --git a/chromium/net/quic/quic_crypto_client_stream_test.cc b/chromium/net/quic/quic_crypto_client_stream_test.cc
index 8154d177988..3515d32aac7 100644
--- a/chromium/net/quic/quic_crypto_client_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_client_stream_test.cc
@@ -8,7 +8,9 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
@@ -20,15 +22,18 @@ namespace test {
namespace {
const char kServerHostname[] = "example.com";
+const uint16 kServerPort = 80;
class QuicCryptoClientStreamTest : public ::testing::Test {
public:
QuicCryptoClientStreamTest()
: connection_(new PacketSavingConnection(false)),
- session_(new TestSession(connection_, DefaultQuicConfig())),
- stream_(new QuicCryptoClientStream(kServerHostname, session_.get(),
- &crypto_config_)) {
+ session_(new TestClientSession(connection_, DefaultQuicConfig())),
+ server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED),
+ stream_(new QuicCryptoClientStream(
+ server_id_, session_.get(), NULL, &crypto_config_)) {
session_->SetCryptoStream(stream_.get());
+ session_->config()->SetDefaults();
crypto_config_.SetDefaults();
}
@@ -43,7 +48,8 @@ class QuicCryptoClientStreamTest : public ::testing::Test {
}
PacketSavingConnection* connection_;
- scoped_ptr<TestSession> session_;
+ scoped_ptr<TestClientSession> session_;
+ QuicServerId server_id_;
scoped_ptr<QuicCryptoClientStream> stream_;
CryptoHandshakeMessage message_;
scoped_ptr<QuicData> message_data_;
@@ -87,7 +93,7 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
const QuicConfig* config = session_->config();
EXPECT_EQ(FLAGS_enable_quic_pacing ? kPACE : kQBIC,
- config->congestion_control());
+ config->congestion_feedback());
EXPECT_EQ(kDefaultTimeoutSecs,
config->idle_connection_state_lifetime().ToSeconds());
EXPECT_EQ(kDefaultMaxStreamsPerConnection,
@@ -96,12 +102,13 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
const QuicCryptoNegotiatedParameters& crypto_params(
stream_->crypto_negotiated_params());
- EXPECT_EQ(kAESG, crypto_params.aead);
- EXPECT_EQ(kC255, crypto_params.key_exchange);
+ EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead);
+ EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange);
}
TEST_F(QuicCryptoClientStreamTest, InvalidHostname) {
- stream_.reset(new QuicCryptoClientStream("invalid", session_.get(),
+ QuicServerId server_id("invalid", 80, false, PRIVACY_MODE_DISABLED);
+ stream_.reset(new QuicCryptoClientStream(server_id, session_.get(), NULL,
&crypto_config_));
session_->SetCryptoStream(stream_.get());
@@ -115,8 +122,8 @@ TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) {
CompleteCryptoHandshake();
connection_ = new PacketSavingConnection(true);
- session_.reset(new TestSession(connection_, DefaultQuicConfig()));
- stream_.reset(new QuicCryptoClientStream(kServerHostname, session_.get(),
+ session_.reset(new TestClientSession(connection_, DefaultQuicConfig()));
+ stream_.reset(new QuicCryptoClientStream(server_id_, session_.get(), NULL,
&crypto_config_));
session_->SetCryptoStream(stream_.get());
diff --git a/chromium/net/quic/quic_crypto_server_stream.cc b/chromium/net/quic/quic_crypto_server_stream.cc
index c0ae1f19b92..7bd2c034d93 100644
--- a/chromium/net/quic/quic_crypto_server_stream.cc
+++ b/chromium/net/quic/quic_crypto_server_stream.cc
@@ -20,11 +20,16 @@ QuicCryptoServerStream::QuicCryptoServerStream(
QuicSession* session)
: QuicCryptoStream(session),
crypto_config_(crypto_config),
- validate_client_hello_cb_(NULL) {
+ validate_client_hello_cb_(NULL),
+ num_handshake_messages_(0) {
}
QuicCryptoServerStream::~QuicCryptoServerStream() {
- // Detach from the validation callback.
+ CancelOutstandingCallbacks();
+}
+
+void QuicCryptoServerStream::CancelOutstandingCallbacks() {
+ // Detach from the validation callback. Calling this multiple times is safe.
if (validate_client_hello_cb_ != NULL) {
validate_client_hello_cb_->Cancel();
}
@@ -33,6 +38,7 @@ QuicCryptoServerStream::~QuicCryptoServerStream() {
void QuicCryptoServerStream::OnHandshakeMessage(
const CryptoHandshakeMessage& message) {
QuicCryptoStream::OnHandshakeMessage(message);
+ ++num_handshake_messages_;
// Do not process handshake messages after the handshake is confirmed.
if (handshake_confirmed_) {
@@ -85,7 +91,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
// If we are returning a SHLO then we accepted the handshake.
QuicConfig* config = session()->config();
- error = config->ProcessClientHello(message, &error_details);
+ OverrideQuicConfigDefaults(config);
+ error = config->ProcessPeerHello(message, CLIENT, &error_details);
if (error != QUIC_NO_ERROR) {
CloseConnectionWithDetails(error, error_details);
return;
@@ -107,7 +114,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
// Set the decrypter immediately so that we no longer accept unencrypted
// packets.
session()->connection()->SetDecrypter(
- crypto_negotiated_params_.initial_crypters.decrypter.release());
+ crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL);
SendHandshakeMessage(reply);
session()->connection()->SetEncrypter(
@@ -117,7 +125,7 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
ENCRYPTION_FORWARD_SECURE);
session()->connection()->SetAlternativeDecrypter(
crypto_negotiated_params_.forward_secure_crypters.decrypter.release(),
- false /* don't latch */);
+ ENCRYPTION_FORWARD_SECURE, false /* don't latch */);
encryption_established_ = true;
handshake_confirmed_ = true;
@@ -161,7 +169,7 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
string* error_details) {
return crypto_config_.ProcessClientHello(
result,
- session()->connection()->guid(),
+ session()->connection()->connection_id(),
session()->connection()->peer_address(),
session()->connection()->version(),
session()->connection()->supported_versions(),
@@ -170,6 +178,9 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
&crypto_negotiated_params_, reply, error_details);
}
+void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) {
+}
+
QuicCryptoServerStream::ValidateCallback::ValidateCallback(
QuicCryptoServerStream* parent) : parent_(parent) {
}
diff --git a/chromium/net/quic/quic_crypto_server_stream.h b/chromium/net/quic/quic_crypto_server_stream.h
index 43924d54d93..ee4013120e4 100644
--- a/chromium/net/quic/quic_crypto_server_stream.h
+++ b/chromium/net/quic/quic_crypto_server_stream.h
@@ -29,6 +29,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
explicit QuicCryptoServerStream(QuicSession* session);
virtual ~QuicCryptoServerStream();
+ // Cancel any outstanding callbacks, such as asynchronous validation of client
+ // hello.
+ void CancelOutstandingCallbacks();
+
// CryptoFramerVisitorInterface implementation
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE;
@@ -38,6 +42,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
// presented a ChannelID. Otherwise it returns false.
bool GetBase64SHA256ClientChannelID(std::string* output) const;
+ uint8 num_handshake_messages() const { return num_handshake_messages_; }
+
protected:
virtual QuicErrorCode ProcessClientHello(
const CryptoHandshakeMessage& message,
@@ -45,6 +51,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
CryptoHandshakeMessage* reply,
std::string* error_details);
+ // Hook that allows the server to set QuicConfig defaults just
+ // before going through the parameter negotiation step.
+ virtual void OverrideQuicConfigDefaults(QuicConfig* config);
+
private:
friend class test::CryptoTestUtils;
@@ -79,6 +89,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
// FinishProcessingHandshakeMessage for processing. NULL if no
// handshake message is being validated.
ValidateCallback* validate_client_hello_cb_;
+
+ uint8 num_handshake_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream);
};
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_server_stream_test.cc b/chromium/net/quic/quic_crypto_server_stream_test.cc
index 9897e22f738..800c245a0c4 100644
--- a/chromium/net/quic/quic_crypto_server_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_server_stream_test.cc
@@ -49,7 +49,10 @@ class QuicCryptoServerConfigPeer {
namespace {
-class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> {
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
+class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> {
public:
QuicCryptoServerStreamTest()
: connection_(new PacketSavingConnection(true)),
@@ -102,7 +105,7 @@ class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> {
protected:
PacketSavingConnection* connection_;
- TestSession session_;
+ TestClientSession session_;
QuicConfig config_;
QuicCryptoServerConfig crypto_config_;
QuicCryptoServerStream stream_;
@@ -137,13 +140,15 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
QuicConfig client_config;
client_config.SetDefaults();
- scoped_ptr<TestSession> client_session(
- new TestSession(client_conn, client_config));
+ scoped_ptr<TestClientSession> client_session(
+ new TestClientSession(client_conn, client_config));
QuicCryptoClientConfig client_crypto_config;
client_crypto_config.SetDefaults();
+ QuicServerId server_id(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED);
scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream(
- "test.example.com", client_session.get(), &client_crypto_config));
+ server_id, client_session.get(), NULL, &client_crypto_config));
client_session->SetCryptoStream(client.get());
// Do a first handshake in order to prime the client config with the server's
@@ -173,10 +178,10 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
// This causes the client's nonce to be different and thus stops the
// strike-register from rejecting the repeated nonce.
reinterpret_cast<MockRandom*>(client_conn->random_generator())->ChangeValue();
- client_session.reset(new TestSession(client_conn, client_config));
+ client_session.reset(new TestClientSession(client_conn, client_config));
server_session.reset(new TestSession(server_conn, config_));
client.reset(new QuicCryptoClientStream(
- "test.example.com", client_session.get(), &client_crypto_config));
+ server_id, client_session.get(), NULL, &client_crypto_config));
client_session->SetCryptoStream(client.get());
server.reset(new QuicCryptoServerStream(crypto_config_,
@@ -249,7 +254,6 @@ TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) {
TEST_P(QuicCryptoServerStreamTest, ChannelID) {
client_options_.channel_id_enabled = true;
- // TODO(rtenneti): Enable testing of ProofVerifier.
// CompleteCryptoHandshake verifies
// stream_.crypto_negotiated_params().channel_id is correct.
EXPECT_EQ(2, CompleteCryptoHandshake());
diff --git a/chromium/net/quic/quic_crypto_stream.cc b/chromium/net/quic/quic_crypto_stream.cc
index d79fa735786..0e447127af5 100644
--- a/chromium/net/quic/quic_crypto_stream.cc
+++ b/chromium/net/quic/quic_crypto_stream.cc
@@ -10,6 +10,7 @@
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_session.h"
+#include "net/quic/quic_utils.h"
using std::string;
using base::StringPiece;
@@ -21,6 +22,7 @@ QuicCryptoStream::QuicCryptoStream(QuicSession* session)
encryption_established_(false),
handshake_confirmed_(false) {
crypto_framer_.set_visitor(this);
+ DisableFlowControl();
}
void QuicCryptoStream::OnError(CryptoFramer* framer) {
@@ -55,12 +57,8 @@ 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.
- WriteOrBufferData(string(data.data(), data.length()), false);
- session()->connection()->Flush();
+ WriteOrBufferData(string(data.data(), data.length()), false, NULL);
}
const QuicCryptoNegotiatedParameters&
diff --git a/chromium/net/quic/quic_crypto_stream.h b/chromium/net/quic/quic_crypto_stream.h
index a082d50dbf2..2c61a18fe78 100644
--- a/chromium/net/quic/quic_crypto_stream.h
+++ b/chromium/net/quic/quic_crypto_stream.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_QUIC_CRYPTO_STREAM_H_
#define NET_QUIC_QUIC_CRYPTO_STREAM_H_
+#include "base/basictypes.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/quic_config.h"
@@ -45,8 +46,8 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
// TODO(wtc): return a success/failure status.
void SendHandshakeMessage(const CryptoHandshakeMessage& message);
- bool encryption_established() { return encryption_established_; }
- bool handshake_confirmed() { return handshake_confirmed_; }
+ bool encryption_established() const { return encryption_established_; }
+ bool handshake_confirmed() const { return handshake_confirmed_; }
const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const;
diff --git a/chromium/net/quic/quic_crypto_stream_test.cc b/chromium/net/quic/quic_crypto_stream_test.cc
index c8dbab51453..2e4b34fe34a 100644
--- a/chromium/net/quic/quic_crypto_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_stream_test.cc
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -101,6 +102,11 @@ TEST_F(QuicCryptoStreamTest, ProcessBadData) {
EXPECT_EQ(0u, stream_.ProcessRawData(bad.data(), bad.length()));
}
+TEST_F(QuicCryptoStreamTest, NoFlowControl) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ EXPECT_FALSE(stream_.flow_controller()->IsEnabled());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_data_reader.h b/chromium/net/quic/quic_data_reader.h
index 9effa1cc896..1686de028cc 100644
--- a/chromium/net/quic/quic_data_reader.h
+++ b/chromium/net/quic/quic_data_reader.h
@@ -124,6 +124,8 @@ class NET_EXPORT_PRIVATE QuicDataReader {
// The location of the next read from our data buffer.
size_t pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataReader);
};
} // namespace net
diff --git a/chromium/net/quic/quic_data_stream.cc b/chromium/net/quic/quic_data_stream.cc
index 3c992a777b0..7963f4f41eb 100644
--- a/chromium/net/quic/quic_data_stream.cc
+++ b/chromium/net/quic/quic_data_stream.cc
@@ -6,8 +6,8 @@
#include "base/logging.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::min;
@@ -24,26 +24,6 @@ namespace {
// priority in the middle.
QuicPriority kDefaultPriority = 3;
-// Appends bytes from data into partial_data_buffer. Once partial_data_buffer
-// reaches 4 bytes, copies the data into 'result' and clears
-// partial_data_buffer.
-// Returns the number of bytes consumed.
-uint32 StripUint32(const char* data, uint32 data_len,
- string* partial_data_buffer,
- uint32* result) {
- DCHECK_GT(4u, partial_data_buffer->length());
- size_t missing_size = 4 - partial_data_buffer->length();
- if (data_len < missing_size) {
- StringPiece(data, data_len).AppendToString(partial_data_buffer);
- return data_len;
- }
- StringPiece(data, missing_size).AppendToString(partial_data_buffer);
- DCHECK_EQ(4u, partial_data_buffer->length());
- memcpy(result, partial_data_buffer->data(), 4);
- partial_data_buffer->clear();
- return missing_size;
-}
-
} // namespace
QuicDataStream::QuicDataStream(QuicStreamId id,
@@ -52,15 +32,31 @@ QuicDataStream::QuicDataStream(QuicStreamId id,
visitor_(NULL),
headers_decompressed_(false),
priority_(kDefaultPriority),
- headers_id_(0),
decompression_failed_(false),
priority_parsed_(false) {
DCHECK_NE(kCryptoStreamId, id);
+ // Don't receive any callbacks from the sequencer until headers
+ // are complete.
+ sequencer()->SetBlockedUntilFlush();
}
QuicDataStream::~QuicDataStream() {
}
+size_t QuicDataStream::WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ size_t bytes_written = session()->WriteHeaders(
+ id(), header_block, fin, ack_notifier_delegate);
+ if (fin) {
+ // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent.
+ set_fin_sent(true);
+ CloseWriteSide();
+ }
+ return bytes_written;
+}
+
size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) {
if (FinishedReadingHeaders()) {
// If the headers have been read, simply delegate to the sequencer's
@@ -81,6 +77,9 @@ size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) {
++iov_index;
}
decompressed_headers_.erase(0, bytes_consumed);
+ if (FinishedReadingHeaders()) {
+ sequencer()->FlushBufferedFrames();
+ }
return bytes_consumed;
}
@@ -118,105 +117,17 @@ QuicPriority QuicDataStream::EffectivePriority() const {
}
uint32 QuicDataStream::ProcessRawData(const char* data, uint32 data_len) {
- DCHECK_NE(0u, data_len);
-
- uint32 total_bytes_consumed = 0;
- if (headers_id_ == 0u) {
- total_bytes_consumed += StripPriorityAndHeaderId(data, data_len);
- data += total_bytes_consumed;
- data_len -= total_bytes_consumed;
- if (data_len == 0 || total_bytes_consumed == 0) {
- return total_bytes_consumed;
- }
- }
- DCHECK_NE(0u, headers_id_);
-
- // Once the headers are finished, we simply pass the data through.
- if (headers_decompressed_) {
- // Some buffered header data remains.
- if (!decompressed_headers_.empty()) {
- ProcessHeaderData();
- }
- if (decompressed_headers_.empty()) {
- DVLOG(1) << "Delegating procesing to ProcessData";
- total_bytes_consumed += ProcessData(data, data_len);
- }
- return total_bytes_consumed;
- }
-
- QuicHeaderId current_header_id =
- session()->decompressor()->current_header_id();
- // Ensure that this header id looks sane.
- if (headers_id_ < current_header_id ||
- headers_id_ > kMaxHeaderIdDelta + current_header_id) {
- DVLOG(1) << ENDPOINT
- << "Invalid headers for stream: " << id()
- << " header_id: " << headers_id_
- << " current_header_id: " << current_header_id;
- session()->connection()->SendConnectionClose(QUIC_INVALID_HEADER_ID);
- return total_bytes_consumed;
- }
-
- // If we are head-of-line blocked on decompression, then back up.
- if (current_header_id != headers_id_) {
- session()->MarkDecompressionBlocked(headers_id_, id());
- DVLOG(1) << ENDPOINT
- << "Unable to decompress header data for stream: " << id()
- << " header_id: " << headers_id_;
- return total_bytes_consumed;
- }
-
- // Decompressed data will be delivered to decompressed_headers_.
- size_t bytes_consumed = session()->decompressor()->DecompressData(
- StringPiece(data, data_len), this);
- DCHECK_NE(0u, bytes_consumed);
- if (bytes_consumed > data_len) {
- DCHECK(false) << "DecompressData returned illegal value";
- OnDecompressionError();
- return total_bytes_consumed;
- }
- total_bytes_consumed += bytes_consumed;
- data += bytes_consumed;
- data_len -= bytes_consumed;
-
- if (decompression_failed_) {
- // The session will have been closed in OnDecompressionError.
- return total_bytes_consumed;
- }
-
- // Headers are complete if the decompressor has moved on to the
- // next stream.
- headers_decompressed_ =
- session()->decompressor()->current_header_id() != headers_id_;
- if (!headers_decompressed_) {
- DCHECK_EQ(0u, data_len);
- }
-
- ProcessHeaderData();
-
- if (!headers_decompressed_ || !decompressed_headers_.empty()) {
- return total_bytes_consumed;
- }
-
- // We have processed all of the decompressed data but we might
- // have some more raw data to process.
- if (data_len > 0) {
- total_bytes_consumed += ProcessData(data, data_len);
+ if (!FinishedReadingHeaders()) {
+ LOG(DFATAL) << "ProcessRawData called before headers have been finished";
+ return 0;
}
-
- // The sequencer will push any additional buffered frames if this data
- // has been completely consumed.
- return total_bytes_consumed;
+ return ProcessData(data, data_len);
}
const IPEndPoint& QuicDataStream::GetPeerAddress() {
return session()->peer_address();
}
-QuicSpdyCompressor* QuicDataStream::compressor() {
- return session()->compressor();
-}
-
bool QuicDataStream::GetSSLInfo(SSLInfo* ssl_info) {
return session()->GetSSLInfo(ssl_info);
}
@@ -236,53 +147,27 @@ uint32 QuicDataStream::ProcessHeaderData() {
return bytes_processed;
}
-void QuicDataStream::OnDecompressorAvailable() {
- DCHECK_EQ(headers_id_,
- session()->decompressor()->current_header_id());
- DCHECK(!headers_decompressed_);
- DCHECK(!decompression_failed_);
- DCHECK_EQ(0u, decompressed_headers_.length());
-
- while (!headers_decompressed_) {
- struct iovec iovec;
- if (sequencer()->GetReadableRegions(&iovec, 1) == 0) {
- return;
- }
+void QuicDataStream::OnStreamHeaders(StringPiece headers_data) {
+ headers_data.AppendToString(&decompressed_headers_);
+ ProcessHeaderData();
+}
- size_t bytes_consumed = session()->decompressor()->DecompressData(
- StringPiece(static_cast<char*>(iovec.iov_base),
- iovec.iov_len),
- this);
- DCHECK_LE(bytes_consumed, iovec.iov_len);
- if (decompression_failed_) {
- return;
- }
- sequencer()->MarkConsumed(bytes_consumed);
+void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) {
+ DCHECK(session()->connection()->is_server());
+ set_priority(priority);
+}
- headers_decompressed_ =
- session()->decompressor()->current_header_id() != headers_id_;
+void QuicDataStream::OnStreamHeadersComplete(bool fin, size_t frame_len) {
+ headers_decompressed_ = true;
+ if (fin) {
+ sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, IOVector()));
}
-
- // Either the headers are complete, or the all data as been consumed.
- ProcessHeaderData(); // Unprocessed headers remain in decompressed_headers_.
- if (IsDoneReading()) {
- OnFinRead();
- } else if (FinishedReadingHeaders()) {
+ ProcessHeaderData();
+ if (FinishedReadingHeaders()) {
sequencer()->FlushBufferedFrames();
}
}
-bool QuicDataStream::OnDecompressedData(StringPiece data) {
- data.AppendToString(&decompressed_headers_);
- return true;
-}
-
-void QuicDataStream::OnDecompressionError() {
- DCHECK(!decompression_failed_);
- decompression_failed_ = true;
- session()->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE);
-}
-
void QuicDataStream::OnClose() {
ReliableQuicStream::OnClose();
@@ -295,37 +180,6 @@ void QuicDataStream::OnClose() {
}
}
-uint32 QuicDataStream::StripPriorityAndHeaderId(
- const char* data, uint32 data_len) {
- uint32 total_bytes_parsed = 0;
-
- if (!priority_parsed_ && session()->connection()->is_server()) {
- QuicPriority temporary_priority = priority_;
- total_bytes_parsed = StripUint32(
- data, data_len, &headers_id_and_priority_buffer_, &temporary_priority);
- if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) {
- priority_parsed_ = true;
-
- // Spdy priorities are inverted, so the highest numerical value is the
- // lowest legal priority.
- if (temporary_priority > QuicUtils::LowestPriority()) {
- session()->connection()->SendConnectionClose(QUIC_INVALID_PRIORITY);
- return 0;
- }
- priority_ = temporary_priority;
- }
- data += total_bytes_parsed;
- data_len -= total_bytes_parsed;
- }
- if (data_len > 0 && headers_id_ == 0u) {
- // The headers ID has not yet been read. Strip it from the beginning of
- // the data stream.
- total_bytes_parsed += StripUint32(
- data, data_len, &headers_id_and_priority_buffer_, &headers_id_);
- }
- return total_bytes_parsed;
-}
-
bool QuicDataStream::FinishedReadingHeaders() {
return headers_decompressed_ && decompressed_headers_.empty();
}
diff --git a/chromium/net/quic/quic_data_stream.h b/chromium/net/quic/quic_data_stream.h
index 84990439e55..1af9004ef33 100644
--- a/chromium/net/quic/quic_data_stream.h
+++ b/chromium/net/quic/quic_data_stream.h
@@ -13,14 +13,15 @@
#include <list>
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "net/base/iovec.h"
#include "net/base/net_export.h"
#include "net/quic/quic_ack_notifier.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/quic_stream_sequencer.h"
#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
namespace net {
@@ -34,8 +35,7 @@ class QuicSession;
class SSLInfo;
// All this does right now is send data to subclasses via the sequencer.
-class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
- public QuicSpdyDecompressor::Visitor {
+class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream {
public:
// Visitor receives callbacks from the stream.
class Visitor {
@@ -58,19 +58,39 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
// ReliableQuicStream implementation
virtual void OnClose() OVERRIDE;
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
// 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 OVERRIDE;
- virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
-
- // QuicSpdyDecompressor::Visitor implementation.
- virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
- virtual void OnDecompressionError() OVERRIDE;
+ // Overridden by subclasses to process data. The headers will be delivered
+ // via OnStreamHeaders, so only data will be delivered through this method.
virtual uint32 ProcessData(const char* data, uint32 data_len) = 0;
+ // Called by the session when decompressed headers data is received
+ // for this stream.
+ // May be called multiple times, with each call providing additional headers
+ // data until OnStreamHeadersComplete is called.
+ virtual void OnStreamHeaders(base::StringPiece headers_data);
+
+ // Called by the session when headers with a priority have been received
+ // for this stream. This method will only be called for server streams.
+ virtual void OnStreamHeadersPriority(QuicPriority priority);
+
+ // Called by the session when decompressed headers have been completely
+ // delilvered to this stream. If |fin| is true, then this stream
+ // should be closed; no more data will be sent by the peer.
+ virtual void OnStreamHeadersComplete(bool fin, size_t frame_len);
+
+ // Writes the headers contained in |header_block| to the dedicated
+ // headers stream.
+ virtual size_t WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
// This block of functions wraps the sequencer's functions of the same
// name. These methods return uncompressed data until that has
// been fully processed. Then they simply delegate to the sequencer.
@@ -80,18 +100,12 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
virtual bool IsDoneReading() const;
virtual bool HasBytesToRead() const;
- // Called by the session when a decompression blocked stream
- // becomes unblocked.
- virtual void OnDecompressorAvailable();
-
void set_visitor(Visitor* visitor) { visitor_ = visitor; }
bool headers_decompressed() const { return headers_decompressed_; }
const IPEndPoint& GetPeerAddress();
- QuicSpdyCompressor* compressor();
-
// Gets the SSL connection information.
bool GetSSLInfo(SSLInfo* ssl_info);
@@ -110,8 +124,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
uint32 ProcessHeaderData();
- uint32 StripPriorityAndHeaderId(const char* data, uint32 data_len);
-
bool FinishedReadingHeaders();
Visitor* visitor_;
@@ -119,11 +131,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
bool headers_decompressed_;
// The priority of the stream, once parsed.
QuicPriority priority_;
- // ID of the header block sent by the peer, once parsed.
- QuicHeaderId headers_id_;
- // Buffer into which we write bytes from priority_ and headers_id_
- // until each is fully parsed.
- string headers_id_and_priority_buffer_;
// Contains a copy of the decompressed headers until they are consumed
// via ProcessData or Readv.
string decompressed_headers_;
diff --git a/chromium/net/quic/quic_data_stream_test.cc b/chromium/net/quic/quic_data_stream_test.cc
index 551ef445715..b72f3cebf06 100644
--- a/chromium/net/quic/quic_data_stream_test.cc
+++ b/chromium/net/quic/quic_data_stream_test.cc
@@ -6,28 +6,30 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::StringPiece;
using std::min;
-using testing::_;
+using testing::AnyNumber;
using testing::InSequence;
using testing::Return;
using testing::SaveArg;
-using testing::StrEq;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-const QuicGuid kStreamId = 3;
const bool kIsServer = true;
const bool kShouldProcessData = true;
@@ -57,7 +59,7 @@ class TestStream : public QuicDataStream {
string data_;
};
-class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
+class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> {
public:
QuicDataStreamTest() {
headers_[":host"] = "www.google.com";
@@ -90,16 +92,15 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
}
void Initialize(bool stream_should_process_data) {
- connection_ = new StrictMock<MockConnection>(kIsServer);
- session_.reset(new StrictMock<MockSession>(connection_));
- stream_.reset(new TestStream(kStreamId, session_.get(),
- stream_should_process_data));
- stream2_.reset(new TestStream(kStreamId + 2, session_.get(),
+ connection_ = new testing::StrictMock<MockConnection>(
+ kIsServer, SupportedVersions(GetParam()));
+ session_.reset(new testing::StrictMock<MockSession>(connection_));
+ stream_.reset(new TestStream(kClientDataStreamId1, session_.get(),
stream_should_process_data));
- compressor_.reset(new QuicSpdyCompressor());
- decompressor_.reset(new QuicSpdyDecompressor);
+ stream2_.reset(new TestStream(kClientDataStreamId2, session_.get(),
+ stream_should_process_data));
write_blocked_list_ =
- QuicSessionPeer::GetWriteblockedStreams(session_.get());
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
}
protected:
@@ -107,172 +108,160 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
scoped_ptr<MockSession> session_;
scoped_ptr<TestStream> stream_;
scoped_ptr<TestStream> stream2_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
- scoped_ptr<QuicSpdyDecompressor> decompressor_;
SpdyHeaderBlock headers_;
- WriteBlockedList<QuicStreamId>* write_blocked_list_;
+ QuicWriteBlockedList* write_blocked_list_;
};
-TEST_F(QuicDataStreamTest, ProcessHeaders) {
- Initialize(kShouldProcessData);
-
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- stream_->OnStreamFrame(frame);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data());
- EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority());
-}
+INSTANTIATE_TEST_CASE_P(Tests, QuicDataStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
-TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidHeaderId) {
+TEST_P(QuicDataStreamTest, ProcessHeaders) {
Initialize(kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- compressed_headers[4] = '\xFF'; // Illegal header id.
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID));
- stream_->OnStreamFrame(frame);
-}
-
-TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidPriority) {
- Initialize(kShouldProcessData);
-
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- compressed_headers[0] = '\xFF'; // Illegal priority.
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_PRIORITY));
- stream_->OnStreamFrame(frame);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority());
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority());
+ EXPECT_EQ(headers, stream_->data());
+ EXPECT_FALSE(stream_->IsDoneReading());
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBody) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) {
Initialize(kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
- stream_->data());
-}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyFragments) {
- Initialize(kShouldProcessData);
+ EXPECT_EQ(headers + body, stream_->data());
+}
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::LowestPriority(), headers_);
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) {
+ for (size_t fragment_size = 1; fragment_size < body.size();
+ ++fragment_size) {
Initialize(kShouldProcessData);
- for (size_t offset = 0; offset < data.size(); offset += fragment_size) {
- size_t remaining_data = data.length() - offset;
- StringPiece fragment(data.data() + offset,
+ for (size_t offset = 0; offset < headers.size();
+ offset += fragment_size) {
+ size_t remaining_data = headers.size() - offset;
+ StringPiece fragment(headers.data() + offset,
min(fragment_size, remaining_data));
- QuicStreamFrame frame(kStreamId, false, offset, MakeIOVector(fragment));
-
+ stream_->OnStreamHeaders(fragment);
+ }
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ for (size_t offset = 0; offset < body.size(); offset += fragment_size) {
+ size_t remaining_data = body.size() - offset;
+ StringPiece fragment(body.data() + offset,
+ min(fragment_size, remaining_data));
+ QuicStreamFrame frame(kClientDataStreamId1, false, offset,
+ MakeIOVector(fragment));
stream_->OnStreamFrame(frame);
}
- ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
+ ASSERT_EQ(headers + body,
stream_->data()) << "fragment_size: " << fragment_size;
}
+}
- for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) {
Initialize(kShouldProcessData);
+ StringPiece headers1(headers.data(), split_point);
+ stream_->OnStreamHeaders(headers1);
+
+ StringPiece headers2(headers.data() + split_point,
+ headers.size() - split_point);
+ stream_->OnStreamHeaders(headers2);
+ stream_->OnStreamHeadersComplete(false, headers.size());
- StringPiece fragment1(data.data(), split_point);
- QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(fragment1));
+ StringPiece fragment1(body.data(), split_point);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0,
+ MakeIOVector(fragment1));
stream_->OnStreamFrame(frame1);
- StringPiece fragment2(data.data() + split_point, data.size() - split_point);
- QuicStreamFrame frame2(
- kStreamId, false, split_point, MakeIOVector(fragment2));
+ StringPiece fragment2(body.data() + split_point,
+ body.size() - split_point);
+ QuicStreamFrame frame2(kClientDataStreamId1, false, split_point,
+ MakeIOVector(fragment2));
stream_->OnStreamFrame(frame2);
- ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
+ ASSERT_EQ(headers + body,
stream_->data()) << "split_point: " << split_point;
}
- EXPECT_EQ(QuicUtils::LowestPriority(), stream_->EffectivePriority());
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyReadv) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyReadv) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer[2048];
- ASSERT_LT(data.length(), arraysize(buffer));
+ ASSERT_LT(headers.length() + body.length(), arraysize(buffer));
struct iovec vec;
vec.iov_base = buffer;
vec.iov_len = arraysize(buffer);
size_t bytes_read = stream_->Readv(&vec, 1);
- EXPECT_EQ(uncompressed_headers.length(), bytes_read);
- EXPECT_EQ(uncompressed_headers, string(buffer, bytes_read));
+ EXPECT_EQ(headers.length(), bytes_read);
+ EXPECT_EQ(headers, string(buffer, bytes_read));
bytes_read = stream_->Readv(&vec, 1);
EXPECT_EQ(body.length(), bytes_read);
EXPECT_EQ(body, string(buffer, bytes_read));
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
-
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer[1];
struct iovec vec;
vec.iov_base = buffer;
vec.iov_len = arraysize(buffer);
- for (size_t i = 0; i < uncompressed_data.length(); ++i) {
+
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); ++i) {
size_t bytes_read = stream_->Readv(&vec, 1);
ASSERT_EQ(1u, bytes_read);
- EXPECT_EQ(uncompressed_data.data()[i], buffer[0]);
+ EXPECT_EQ(data.data()[i], buffer[0]);
}
}
-TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
+TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
-
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer1[1];
char buffer2[1];
@@ -281,168 +270,302 @@ TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
vec[0].iov_len = arraysize(buffer1);
vec[1].iov_base = buffer2;
vec[1].iov_len = arraysize(buffer2);
- for (size_t i = 0; i < uncompressed_data.length(); i += 2) {
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); i += 2) {
size_t bytes_read = stream_->Readv(vec, 2);
ASSERT_EQ(2u, bytes_read) << i;
- ASSERT_EQ(uncompressed_data.data()[i], buffer1[0]) << i;
- ASSERT_EQ(uncompressed_data.data()[i + 1], buffer2[0]) << i;
+ ASSERT_EQ(data.data()[i], buffer1[0]) << i;
+ ASSERT_EQ(data.data()[i + 1], buffer2[0]) << i;
}
}
-TEST_F(QuicDataStreamTest, ProcessCorruptHeadersEarly) {
+TEST_P(QuicDataStreamTest, StreamFlowControlBlocked) {
+ // Tests that we send a BLOCKED frame to the peer when we attempt to write,
+ // but are flow control blocked.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
Initialize(kShouldProcessData);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- // Corrupt the compressed data.
- compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1;
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(compressed_headers2));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
- stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset(
+ stream_->flow_controller()));
+
+ // Try to send more data than the flow control limit allows.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ const uint64 kOverflow = 15;
+ GenerateBody(&body, kWindow + kOverflow);
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1));
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(kWindow, true)));
+ stream_->WriteOrBufferData(body, false, NULL);
+
+ // Should have sent as much as possible, resulting in no send window left.
+ EXPECT_EQ(0u,
+ QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller()));
+
+ // And we should have queued the overflowed data.
+ EXPECT_EQ(kOverflow,
+ ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get()));
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) {
+ // The flow control receive window decreases whenever we add new bytes to the
+ // sequencer, whether they are consumed immediately or buffered. However we
+ // only send WINDOW_UPDATE frames based on increasing number of bytes
+ // consumed.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Don't process data - it will be buffered instead.
+ Initialize(!kShouldProcessData);
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
+ // Expect no WINDOW_UPDATE frames to be sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0);
+
+ // Set a small flow control receive window.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
-
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data. Since
- // the compressed data is corrupt, the stream will shutdown the session.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ("", stream2_->data());
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This should all be buffered, decreasing the receive window but
+ // not sending WINDOW_UPDATE.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(
+ kWindow - (2 * kWindow / 3),
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
}
-TEST_F(QuicDataStreamTest, ProcessPartialHeadersEarly) {
- Initialize(kShouldProcessData);
+TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the stream updates its receive window offset
+ // appropriately, and sends WINDOW_UPDATE frames when its receive window drops
+ // too low.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- string partial_compressed_headers =
- compressed_headers2.substr(0, compressed_headers2.length() / 2);
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(partial_compressed_headers));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
- stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
+ Initialize(kShouldProcessData);
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
-
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data. Since
- // the compressed data is incomplete it will not be passed to
- // the stream.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ("", stream2_->data());
-
- // Now send remaining data and verify that we have now received the
- // compressed headers.
- string remaining_compressed_headers =
- compressed_headers2.substr(partial_compressed_headers.length());
-
- QuicStreamFrame frame3(stream2_->id(), false,
- partial_compressed_headers.length(),
- MakeIOVector(remaining_compressed_headers));
- stream2_->OnStreamFrame(frame3);
- EXPECT_EQ(decompressed_headers2, stream2_->data());
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This will trigger the stream to increase its receive window
+ // offset and send a WINDOW_UPDATE. The result will be again an available
+ // window of kWindow bytes.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(kClientDataStreamId1,
+ QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()) +
+ 2 * kWindow / 3));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
}
-TEST_F(QuicDataStreamTest, ProcessHeadersEarly) {
+TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the connection updates its receive window
+ // offset appropriately, and sends WINDOW_UPDATE frames when its receive
+ // window drops too low.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
Initialize(kShouldProcessData);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(compressed_headers2));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
+ // Set a small flow control limit for streams and connection.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(),
+ kWindow);
+
+ // Supply headers to both streams so that they are happy to receive data.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ stream2_->OnStreamHeaders(headers);
+ stream2_->OnStreamHeadersComplete(false, headers.size());
+
+ // Each stream gets a quarter window of data. This should not trigger a
+ // WINDOW_UPDATE for either stream, nor for the connection.
+ string body;
+ GenerateBody(&body, kWindow / 4);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame1);
+ QuicStreamFrame frame2(kClientDataStreamId2, false, 0, MakeIOVector(body));
stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
- stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
+ // Now receive a further single byte on one stream - again this does not
+ // trigger a stream WINDOW_UPDATE, but now the connection flow control window
+ // is over half full and thus a connection WINDOW_UPDATE is sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, _)).Times(0);
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0);
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset(
+ session_->flow_controller()) +
+ 1 + kWindow / 2));
+ QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4),
+ MakeIOVector("a"));
+ stream_->OnStreamFrame(frame3);
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), then we terminate the connection.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ(decompressed_headers2, stream2_->data());
+ // Set a small flow control limit.
+ const uint64 kWindow = 50;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Receive data to overflow the window, violating flow control.
+ string body;
+ GenerateBody(&body, kWindow + 1);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
}
-TEST_F(QuicDataStreamTest, ProcessHeadersDelay) {
+TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), at the connection level (rather than the stream level)
+ // then we terminate the connection.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers));
- string decompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
+ // Set a small flow control window on streams, and connection.
+ const uint64 kStreamWindow = 50;
+ const uint64 kConnectionWindow = 10;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kStreamWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kConnectionWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Send enough data to overflow the connection level flow control window.
+ string body;
+ GenerateBody(&body, kConnectionWindow + 1);
+ EXPECT_LT(body.size(), kStreamWindow);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
+}
- // Send the headers to the stream and verify they were decompressed.
- stream_->OnStreamFrame(frame1);
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
-
- // Verify that we are now able to handle the body data,
- // even though the stream has not processed the headers.
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID))
- .Times(0);
- QuicStreamFrame frame2(stream_->id(), false, compressed_headers.length(),
- MakeIOVector("body data"));
- stream_->OnStreamFrame(frame2);
+TEST_P(QuicDataStreamTest, StreamFlowControlFinNotBlocked) {
+ // An attempt to write a FIN with no data should not be flow control blocked,
+ // even if the send window is 0.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ Initialize(kShouldProcessData);
+
+ // Set a flow control limit of zero.
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0);
+ EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Send a frame with a FIN but no data. This should not be blocked.
+ string body = "";
+ bool fin = true;
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0);
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, fin)));
+
+ stream_->WriteOrBufferData(body, fin, NULL);
}
} // namespace
diff --git a/chromium/net/quic/quic_data_writer.cc b/chromium/net/quic/quic_data_writer.cc
index b7aff26fe01..70b0fdaed31 100644
--- a/chromium/net/quic/quic_data_writer.cc
+++ b/chromium/net/quic/quic_data_writer.cc
@@ -167,7 +167,10 @@ void QuicDataWriter::WritePadding() {
}
bool QuicDataWriter::WriteUInt8ToOffset(uint8 value, size_t offset) {
- DCHECK_LT(offset, capacity_);
+ if (offset >= capacity_) {
+ LOG(DFATAL) << "offset: " << offset << " >= capacity: " << capacity_;
+ return false;
+ }
size_t latched_length = length_;
length_ = offset;
bool success = WriteUInt8(value);
diff --git a/chromium/net/quic/quic_data_writer.h b/chromium/net/quic/quic_data_writer.h
index f8a3751bf1b..0e14e87c692 100644
--- a/chromium/net/quic/quic_data_writer.h
+++ b/chromium/net/quic/quic_data_writer.h
@@ -74,6 +74,8 @@ class NET_EXPORT_PRIVATE QuicDataWriter {
char* buffer_;
size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
size_t length_; // Current length of the buffer.
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataWriter);
};
} // namespace net
diff --git a/chromium/net/quic/quic_data_writer_test.cc b/chromium/net/quic/quic_data_writer_test.cc
index 9106947b52e..07346c78891 100644
--- a/chromium/net/quic/quic_data_writer_test.cc
+++ b/chromium/net/quic/quic_data_writer_test.cc
@@ -6,6 +6,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_data_reader.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -32,13 +33,8 @@ TEST(QuicDataWriterTest, WriteUInt8ToOffset) {
TEST(QuicDataWriterDeathTest, WriteUInt8ToOffset) {
QuicDataWriter writer(4);
-#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
-#if !defined(DCHECK_ALWAYS_ON)
- EXPECT_DEBUG_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed");
-#else
- EXPECT_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed");
-#endif
-#endif
+ EXPECT_DFATAL(EXPECT_FALSE(writer.WriteUInt8ToOffset(5, 4)),
+ "offset: 4 >= capacity: 4");
}
TEST(QuicDataWriterTest, SanityCheckUFloat16Consts) {
diff --git a/chromium/net/quic/quic_default_packet_writer.cc b/chromium/net/quic/quic_default_packet_writer.cc
index 74b86af2a98..5dd80c6fd3d 100644
--- a/chromium/net/quic/quic_default_packet_writer.cc
+++ b/chromium/net/quic/quic_default_packet_writer.cc
@@ -17,7 +17,8 @@ QuicDefaultPacketWriter::QuicDefaultPacketWriter() : weak_factory_(this) {
QuicDefaultPacketWriter::QuicDefaultPacketWriter(DatagramClientSocket* socket)
: weak_factory_(this),
- socket_(socket) {
+ socket_(socket),
+ write_blocked_(false) {
}
QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
@@ -25,10 +26,10 @@ QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
WriteResult QuicDefaultPacketWriter::WritePacket(
const char* buffer, size_t buf_len,
const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) {
+ const net::IPEndPoint& peer_address) {
scoped_refptr<StringIOBuffer> buf(
new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
int rv = socket_->Write(buf.get(),
buf_len,
base::Bind(&QuicDefaultPacketWriter::OnWriteComplete,
@@ -40,6 +41,7 @@ WriteResult QuicDefaultPacketWriter::WritePacket(
status = WRITE_STATUS_ERROR;
} else {
status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
}
}
@@ -52,8 +54,17 @@ bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const {
return true;
}
+bool QuicDefaultPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicDefaultPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
void QuicDefaultPacketWriter::OnWriteComplete(int rv) {
DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
connection_->OnPacketSent(result);
connection_->OnCanWrite();
diff --git a/chromium/net/quic/quic_default_packet_writer.h b/chromium/net/quic/quic_default_packet_writer.h
index f60c76ea9be..ed3b5975cee 100644
--- a/chromium/net/quic/quic_default_packet_writer.h
+++ b/chromium/net/quic/quic_default_packet_writer.h
@@ -15,7 +15,7 @@
namespace net {
-class QuicBlockedWriterInterface;
+struct WriteResult;
// Chrome specific packet writer which uses a DatagramClientSocket for writing
// data.
@@ -26,23 +26,31 @@ class NET_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter {
virtual ~QuicDefaultPacketWriter();
// QuicPacketWriter
- virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE;
-
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
void OnWriteComplete(int rv);
void SetConnection(QuicConnection* connection) {
connection_ = connection;
}
+ protected:
+ void set_write_blocked(bool is_blocked) {
+ write_blocked_ = is_blocked;
+ }
+
private:
base::WeakPtrFactory<QuicDefaultPacketWriter> weak_factory_;
DatagramClientSocket* socket_;
QuicConnection* connection_;
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter);
};
} // namespace net
diff --git a/chromium/net/quic/quic_dispatcher.cc b/chromium/net/quic/quic_dispatcher.cc
new file mode 100644
index 00000000000..280622da381
--- /dev/null
+++ b/chromium/net/quic/quic_dispatcher.cc
@@ -0,0 +1,426 @@
+// Copyright 2014 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_dispatcher.h"
+
+#include <errno.h>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+#include "net/quic/quic_utils.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::make_pair;
+using std::find;
+
+class DeleteSessionsAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ dispatcher_->DeleteSessions();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicFramerVisitor(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher),
+ connection_id_(0) {}
+
+ // QuicFramerVisitorInterface implementation
+ virtual void OnPacket() OVERRIDE {}
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ connection_id_ = header.connection_id;
+ return dispatcher_->OnUnauthenticatedPublicHeader(header);
+ }
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ dispatcher_->OnUnauthenticatedHeader(header);
+ return false;
+ }
+ virtual void OnError(QuicFramer* framer) OVERRIDE {
+ DVLOG(1) << QuicUtils::ErrorToString(framer->error());
+ }
+
+ virtual bool OnProtocolVersionMismatch(
+ QuicVersion /*received_version*/) OVERRIDE {
+ if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait(
+ connection_id_)) {
+ // Keep processing after protocol mismatch - this will be dealt with by
+ // the TimeWaitListManager.
+ return true;
+ } else {
+ DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_
+ << ") not in time wait list.";
+ return false;
+ }
+ }
+
+ // The following methods should never get called because we always return
+ // false from OnUnauthenticatedHeader(). As a result, we never process the
+ // payload of the packet.
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnRevivedPacket() OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/)
+ OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnPacketComplete() OVERRIDE {
+ DCHECK(false);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+
+ // Latched in OnUnauthenticatedPublicHeader for use later.
+ QuicConnectionId connection_id_;
+};
+
+QuicDispatcher::QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ QuicConnectionHelperInterface* helper)
+ : config_(config),
+ crypto_config_(crypto_config),
+ helper_(helper),
+ delete_sessions_alarm_(
+ helper_->CreateAlarm(new DeleteSessionsAlarm(this))),
+ supported_versions_(supported_versions),
+ supported_versions_no_flow_control_(supported_versions),
+ supported_versions_no_connection_flow_control_(supported_versions),
+ current_packet_(NULL),
+ framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
+ framer_visitor_(new QuicFramerVisitor(this)) {
+ framer_.set_visitor(framer_visitor_.get());
+}
+
+QuicDispatcher::~QuicDispatcher() {
+ STLDeleteValues(&session_map_);
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::Initialize(QuicPacketWriter* writer) {
+ DCHECK(writer_ == NULL);
+ writer_.reset(writer);
+ time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
+
+ // Remove all versions > QUIC_VERSION_16 from the
+ // supported_versions_no_flow_control_ vector.
+ QuicVersionVector::iterator it =
+ find(supported_versions_no_flow_control_.begin(),
+ supported_versions_no_flow_control_.end(), QUIC_VERSION_17);
+ if (it != supported_versions_no_flow_control_.end()) {
+ supported_versions_no_flow_control_.erase(
+ supported_versions_no_flow_control_.begin(), it + 1);
+ }
+ CHECK(!supported_versions_no_flow_control_.empty());
+
+ // Remove all versions > QUIC_VERSION_18 from the
+ // supported_versions_no_connection_flow_control_ vector.
+ QuicVersionVector::iterator connection_it =
+ find(supported_versions_no_connection_flow_control_.begin(),
+ supported_versions_no_connection_flow_control_.end(),
+ QUIC_VERSION_19);
+ if (connection_it != supported_versions_no_connection_flow_control_.end()) {
+ supported_versions_no_connection_flow_control_.erase(
+ supported_versions_no_connection_flow_control_.begin(),
+ connection_it + 1);
+ }
+ CHECK(!supported_versions_no_connection_flow_control_.empty());
+}
+
+void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet) {
+ current_server_address_ = server_address;
+ current_client_address_ = client_address;
+ current_packet_ = &packet;
+ // ProcessPacket will cause the packet to be dispatched in
+ // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
+ // in OnAuthenticatedHeader.
+ framer_.ProcessPacket(packet);
+ // TODO(rjshade): Return a status describing if/why a packet was dropped,
+ // and log somehow. Maybe expose as a varz.
+}
+
+bool QuicDispatcher::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ QuicSession* session = NULL;
+
+ QuicConnectionId connection_id = header.connection_id;
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ if (header.reset_flag) {
+ return false;
+ }
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ return HandlePacketForTimeWait(header);
+ }
+
+ // Ensure the packet has a version negotiation bit set before creating a new
+ // session for it. All initial packets for a new connection are required to
+ // have the flag set. Otherwise it may be a stray packet.
+ if (header.version_flag) {
+ session = CreateQuicSession(connection_id, current_server_address_,
+ current_client_address_);
+ }
+
+ if (session == NULL) {
+ DVLOG(1) << "Failed to create session for " << connection_id;
+ // Add this connection_id fo the time-wait state, to safely reject future
+ // packets.
+
+ if (header.version_flag &&
+ !framer_.IsSupportedVersion(header.versions.front())) {
+ // TODO(ianswett): Produce a no-version version negotiation packet.
+ return false;
+ }
+
+ // Use the version in the packet if possible, otherwise assume the latest.
+ QuicVersion version = header.version_flag ? header.versions.front() :
+ supported_versions_.front();
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id, version, NULL);
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ return HandlePacketForTimeWait(header);
+ }
+ DVLOG(1) << "Created new session for " << connection_id;
+ session_map_.insert(make_pair(connection_id, session));
+ } else {
+ session = it->second;
+ }
+
+ session->connection()->ProcessUdpPacket(
+ current_server_address_, current_client_address_, *current_packet_);
+
+ // Do not parse the packet further. The session will process it completely.
+ return false;
+}
+
+void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
+ header.public_header.connection_id));
+ time_wait_list_manager_->ProcessPacket(current_server_address_,
+ current_client_address_,
+ header.public_header.connection_id,
+ header.packet_sequence_number,
+ *current_packet_);
+}
+
+void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
+ QuicConnection* connection = it->second->connection();
+ QuicEncryptedPacket* connection_close_packet =
+ connection->ReleaseConnectionClosePacket();
+ write_blocked_list_.erase(connection);
+ time_wait_list_manager_->AddConnectionIdToTimeWait(it->first,
+ connection->version(),
+ connection_close_packet);
+ session_map_.erase(it);
+}
+
+void QuicDispatcher::DeleteSessions() {
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::OnCanWrite() {
+ // We got an EPOLLOUT: the socket should not be blocked.
+ writer_->SetWritable();
+
+ // Give each writer one attempt to write.
+ int num_writers = write_blocked_list_.size();
+ for (int i = 0; i < num_writers; ++i) {
+ if (write_blocked_list_.empty()) {
+ return;
+ }
+ QuicBlockedWriterInterface* blocked_writer =
+ write_blocked_list_.begin()->first;
+ write_blocked_list_.erase(write_blocked_list_.begin());
+ blocked_writer->OnCanWrite();
+ if (writer_->IsWriteBlocked()) {
+ // We were unable to write. Wait for the next EPOLLOUT. The writer is
+ // responsible for adding itself to the blocked list via OnWriteBlocked().
+ return;
+ }
+ }
+}
+
+bool QuicDispatcher::HasPendingWrites() const {
+ return !write_blocked_list_.empty();
+}
+
+void QuicDispatcher::Shutdown() {
+ while (!session_map_.empty()) {
+ QuicSession* session = session_map_.begin()->second;
+ session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
+ // Validate that the session removes itself from the session map on close.
+ DCHECK(session_map_.empty() || session_map_.begin()->second != session);
+ }
+ DeleteSessions();
+}
+
+void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) {
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ LOG(DFATAL) << "ConnectionId " << connection_id
+ << " does not exist in the session map. "
+ << "Error: " << QuicUtils::ErrorToString(error);
+ LOG(DFATAL) << base::debug::StackTrace().ToString();
+ return;
+ }
+ DVLOG_IF(1, error != QUIC_NO_ERROR) << "Closing connection ("
+ << connection_id
+ << ") due to error: "
+ << QuicUtils::ErrorToString(error);
+ if (closed_session_list_.empty()) {
+ delete_sessions_alarm_->Set(helper_->GetClock()->ApproximateNow());
+ }
+ closed_session_list_.push_back(it->second);
+ CleanUpSession(it);
+}
+
+void QuicDispatcher::OnWriteBlocked(QuicBlockedWriterInterface* writer) {
+ DCHECK(writer_->IsWriteBlocked());
+ write_blocked_list_.insert(make_pair(writer, true));
+}
+
+QuicSession* QuicDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ QuicServerSession* session = new QuicServerSession(
+ config_,
+ CreateQuicConnection(connection_id, server_address, client_address),
+ this);
+ session->InitializeSession(crypto_config_);
+ return session;
+}
+
+QuicConnection* QuicDispatcher::CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ FLAGS_enable_quic_connection_flow_control_2) {
+ DVLOG(1) << "Creating QuicDispatcher with all versions.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true, supported_versions_);
+ }
+
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ !FLAGS_enable_quic_connection_flow_control_2) {
+ DVLOG(1) << "Connection flow control disabled, creating QuicDispatcher "
+ << "WITHOUT version 19 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true,
+ supported_versions_no_connection_flow_control_);
+ }
+
+ DVLOG(1) << "Flow control disabled, creating QuicDispatcher WITHOUT "
+ << "version 17 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true,
+ supported_versions_no_flow_control_);
+}
+
+QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
+ return new QuicTimeWaitListManager(
+ writer_.get(), this, helper_, supported_versions());
+}
+
+bool QuicDispatcher::HandlePacketForTimeWait(
+ const QuicPacketPublicHeader& header) {
+ if (header.reset_flag) {
+ // Public reset packets do not have sequence numbers, so ignore the packet.
+ return false;
+ }
+
+ // Switch the framer to the correct version, so that the sequence number can
+ // be parsed correctly.
+ framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId(
+ header.connection_id));
+
+ // Continue parsing the packet to extract the sequence number. Then
+ // send it to the time wait manager in OnUnathenticatedHeader.
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_dispatcher.h b/chromium/net/quic/quic_dispatcher.h
new file mode 100644
index 00000000000..1f666742e3f
--- /dev/null
+++ b/chromium/net/quic/quic_dispatcher.h
@@ -0,0 +1,230 @@
+// Copyright 2014 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.
+//
+// A server side dispatcher which dispatches a given client's data to their
+// stream.
+
+#ifndef NET_QUIC_QUIC_DISPATCHER_H_
+#define NET_QUIC_QUIC_DISPATCHER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template <>
+struct hash<net::QuicBlockedWriterInterface*> {
+ std::size_t operator()(const net::QuicBlockedWriterInterface* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+}
+#endif
+
+namespace net {
+
+class QuicConfig;
+class QuicCryptoServerConfig;
+class QuicPacketWriterWrapper;
+class QuicSession;
+class UDPServerSocket;
+
+namespace test {
+class QuicDispatcherPeer;
+} // namespace test
+
+class DeleteSessionsAlarm;
+
+class QuicDispatcher : public QuicServerSessionVisitor {
+ public:
+ // Ideally we'd have a linked_hash_set: the boolean is unused.
+ typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList;
+
+ // Due to the way delete_sessions_closure_ is registered, the Dispatcher
+ // must live until epoll_server Shutdown. |supported_versions| specifies the
+ // list of supported QUIC versions.
+ QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~QuicDispatcher();
+
+ // Takes ownership of the packet writer
+ virtual void Initialize(QuicPacketWriter* writer);
+
+ // Process the incoming packet by creating a new session, passing it to
+ // an existing session, or passing it to the TimeWaitListManager.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet);
+
+ // Called when the socket becomes writable to allow queued writes to happen.
+ virtual void OnCanWrite();
+
+ // Returns true if there's anything in the blocked writer list.
+ virtual bool HasPendingWrites() const;
+
+ // Sends ConnectionClose frames to all connected clients.
+ void Shutdown();
+
+ // QuicServerSessionVisitor interface implementation:
+ // Ensure that the closed connection is cleaned up asynchronously.
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) OVERRIDE;
+
+ // Queues the blocked writer for later resumption.
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) OVERRIDE;
+
+ typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
+
+ // Deletes all sessions on the closed session list and clears the list.
+ void DeleteSessions();
+
+ const SessionMap& session_map() const { return session_map_; }
+
+ WriteBlockedList* write_blocked_list() { return &write_blocked_list_; }
+
+ protected:
+ virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ virtual QuicConnection* CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ // Called by |framer_visitor_| when the public header has been parsed.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header);
+
+ // Create and return the time wait list manager for this dispatcher, which
+ // will be owned by the dispatcher as time_wait_list_manager_
+ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+
+ // Replaces the packet writer with |writer|. Takes ownership of |writer|.
+ void set_writer(QuicPacketWriter* writer) {
+ writer_.reset(writer);
+ }
+
+ QuicTimeWaitListManager* time_wait_list_manager() {
+ return time_wait_list_manager_.get();
+ }
+
+ const QuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ const QuicVersionVector& supported_versions_no_flow_control() const {
+ return supported_versions_no_flow_control_;
+ }
+
+ const QuicVersionVector& supported_versions_no_connection_flow_control()
+ const {
+ return supported_versions_no_connection_flow_control_;
+ }
+
+ const IPEndPoint& current_server_address() {
+ return current_server_address_;
+ }
+ const IPEndPoint& current_client_address() {
+ return current_client_address_;
+ }
+ const QuicEncryptedPacket& current_packet() {
+ return *current_packet_;
+ }
+
+ const QuicConfig& config() const { return config_; }
+
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+
+ QuicFramer* framer() { return &framer_; }
+
+ QuicConnectionHelperInterface* helper() { return helper_; }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+
+ private:
+ class QuicFramerVisitor;
+ friend class net::test::QuicDispatcherPeer;
+
+ // Called by |framer_visitor_| when the private header has been parsed
+ // of a data packet that is destined for the time wait manager.
+ void OnUnauthenticatedHeader(const QuicPacketHeader& header);
+
+ // Removes the session from the session map and write blocked list, and
+ // adds the ConnectionId to the time-wait list.
+ void CleanUpSession(SessionMap::iterator it);
+
+ bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header);
+
+ const QuicConfig& config_;
+
+ const QuicCryptoServerConfig& crypto_config_;
+
+ // The list of connections waiting to write.
+ WriteBlockedList write_blocked_list_;
+
+ SessionMap session_map_;
+
+ // Entity that manages connection_ids in time wait state.
+ scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+
+ // The helper used for all connections. Owned by the server.
+ QuicConnectionHelperInterface* helper_;
+
+ // An alarm which deletes closed sessions.
+ scoped_ptr<QuicAlarm> delete_sessions_alarm_;
+
+ // The list of closed but not-yet-deleted sessions.
+ std::list<QuicSession*> closed_session_list_;
+
+ // The writer to write to the socket with.
+ scoped_ptr<QuicPacketWriter> writer_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ const QuicVersionVector supported_versions_;
+
+ // Versions which do not support flow control (introduced in QUIC_VERSION_17).
+ // This is used to construct new QuicConnections when flow control is disabled
+ // via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_stream_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_flow_control_;
+ // Versions which do not support *connection* flow control (introduced in
+ // QUIC_VERSION_19).
+ // This is used to construct new QuicConnections when connection flow control
+ // is disabled via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_connection_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_connection_flow_control_;
+
+ // Information about the packet currently being handled.
+ IPEndPoint current_client_address_;
+ IPEndPoint current_server_address_;
+ const QuicEncryptedPacket* current_packet_;
+
+ QuicFramer framer_;
+ scoped_ptr<QuicFramerVisitor> framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DISPATCHER_H_
diff --git a/chromium/net/quic/quic_end_to_end_unittest.cc b/chromium/net/quic/quic_end_to_end_unittest.cc
index 90c670bfbeb..89622b0483d 100644
--- a/chromium/net/quic/quic_end_to_end_unittest.cc
+++ b/chromium/net/quic/quic_end_to_end_unittest.cc
@@ -17,20 +17,21 @@
#include "net/http/http_network_session.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_server_properties_impl.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/proxy/proxy_service.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
#include "net/tools/quic/test_tools/server_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
-extern int FLAGS_fake_packet_loss_percentage;
-
using base::StringPiece;
using net::tools::QuicInMemoryCache;
+using net::tools::QuicServer;
using net::tools::test::QuicInMemoryCachePeer;
using net::tools::test::ServerThread;
@@ -48,14 +49,11 @@ class TestTransactionFactory : public HttpTransactionFactory {
: session_(new HttpNetworkSession(params)) {}
virtual ~TestTransactionFactory() {
- FLAGS_fake_packet_loss_percentage = 0;
}
// HttpTransactionFactory methods
virtual int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) OVERRIDE {
- EXPECT_TRUE(delegate == NULL);
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE {
trans->reset(new HttpNetworkTransaction(priority, session_));
return OK;
}
@@ -137,13 +135,20 @@ class QuicEndToEndTest : public PlatformTest {
CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
server_address_ = IPEndPoint(ip, 0);
server_config_.SetDefaults();
- server_thread_.reset(new ServerThread(server_address_, server_config_,
- QuicSupportedVersions(),
- strike_register_no_startup_period_));
- server_thread_->Start();
- server_thread_->WaitForServerStartup();
+ server_config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ server_config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_thread_.reset(new ServerThread(
+ new QuicServer(server_config_, QuicSupportedVersions()),
+ server_address_,
+ strike_register_no_startup_period_));
+ server_thread_->Initialize();
server_address_ = IPEndPoint(server_address_.address(),
server_thread_->GetPort());
+ server_thread_->Start();
server_started_ = true;
}
diff --git a/chromium/net/quic/quic_fec_group.cc b/chromium/net/quic/quic_fec_group.cc
index 4be48100e76..344f21679ec 100644
--- a/chromium/net/quic/quic_fec_group.cc
+++ b/chromium/net/quic/quic_fec_group.cc
@@ -6,6 +6,7 @@
#include <limits>
+#include "base/basictypes.h"
#include "base/logging.h"
using base::StringPiece;
@@ -22,12 +23,13 @@ QuicFecGroup::QuicFecGroup()
: min_protected_packet_(kNoSequenceNumber),
max_protected_packet_(kNoSequenceNumber),
payload_parity_len_(0),
- entropy_parity_(false) {
+ effective_encryption_level_(NUM_ENCRYPTION_LEVELS) {
}
QuicFecGroup::~QuicFecGroup() {}
-bool QuicFecGroup::Update(const QuicPacketHeader& header,
+bool QuicFecGroup::Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
StringPiece decrypted_payload) {
if (received_packets_.count(header.packet_sequence_number) != 0) {
return false;
@@ -40,34 +42,39 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header,
<< header.packet_sequence_number;
return false;
}
- if (!UpdateParity(decrypted_payload, header.entropy_flag)) {
+ if (!UpdateParity(decrypted_payload)) {
return false;
}
received_packets_.insert(header.packet_sequence_number);
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
return true;
}
bool QuicFecGroup::UpdateFec(
+ EncryptionLevel encryption_level,
QuicPacketSequenceNumber fec_packet_sequence_number,
- bool fec_packet_entropy,
const QuicFecData& fec) {
if (min_protected_packet_ != kNoSequenceNumber) {
return false;
}
SequenceNumberSet::const_iterator it = received_packets_.begin();
while (it != received_packets_.end()) {
- if ((*it < fec.fec_group) ||
- (*it >= fec_packet_sequence_number)) {
+ if ((*it < fec.fec_group) || (*it >= fec_packet_sequence_number)) {
DLOG(ERROR) << "FEC group does not cover received packet: " << *it;
return false;
}
++it;
}
- if (!UpdateParity(fec.redundancy, fec_packet_entropy)) {
+ if (!UpdateParity(fec.redundancy)) {
return false;
}
min_protected_packet_ = fec.fec_group;
max_protected_packet_ = fec_packet_sequence_number - 1;
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
return true;
}
@@ -109,7 +116,7 @@ size_t QuicFecGroup::Revive(QuicPacketHeader* header,
}
header->packet_sequence_number = missing;
- header->entropy_flag = entropy_parity_;
+ header->entropy_flag = false; // Unknown entropy.
received_packets_.insert(missing);
return payload_parity_len_;
@@ -119,12 +126,12 @@ bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
if (max_protected_packet_ != kNoSequenceNumber) {
return max_protected_packet_ < num;
}
- // Since we might not yet have recevied the FEC packet, we must check
+ // Since we might not yet have received the FEC packet, we must check
// the packets we have received.
return *received_packets_.begin() < num;
}
-bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
+bool QuicFecGroup::UpdateParity(StringPiece payload) {
DCHECK_LE(payload.size(), kMaxPacketSize);
if (payload.size() > kMaxPacketSize) {
DLOG(ERROR) << "Illegal payload size: " << payload.size();
@@ -143,7 +150,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
memset(payload_parity_ + payload.size(), 0,
kMaxPacketSize - payload.size());
}
- entropy_parity_ = entropy;
return true;
}
// Update the parity by XORing in the data (padding with 0s if necessary).
@@ -151,8 +157,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
uint8 byte = i < payload.size() ? payload[i] : 0x00;
payload_parity_[i] ^= byte;
}
- // xor of boolean values.
- entropy_parity_ = (entropy_parity_ != entropy);
return true;
}
diff --git a/chromium/net/quic/quic_fec_group.h b/chromium/net/quic/quic_fec_group.h
index d905d03236e..718d09f83ad 100644
--- a/chromium/net/quic/quic_fec_group.h
+++ b/chromium/net/quic/quic_fec_group.h
@@ -9,8 +9,6 @@
#ifndef NET_QUIC_QUIC_FEC_GROUP_H_
#define NET_QUIC_QUIC_FEC_GROUP_H_
-#include <set>
-
#include "base/strings/string_piece.h"
#include "net/quic/quic_protocol.h"
@@ -21,17 +19,19 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
QuicFecGroup();
~QuicFecGroup();
- // Updates the FEC group based on the delivery of a data packet.
- // Returns false if this packet has already been seen, true otherwise.
- bool Update(const QuicPacketHeader& header,
+ // Updates the FEC group based on the delivery of a data packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen,
+ // true otherwise.
+ bool Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
base::StringPiece decrypted_payload);
- // Updates the FEC group based on the delivery of an FEC packet.
- // Returns false if this packet has already been seen or if it does
- // not claim to protect all the packets previously seen in this group.
- // |fec_packet_entropy|: XOR of entropy of all packets in the fec group.
- bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number,
- bool fec_packet_entropy,
+ // Updates the FEC group based on the delivery of an FEC packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen or
+ // if it does not claim to protect all the packets previously seen in this
+ // group.
+ bool UpdateFec(EncryptionLevel encryption_level,
+ QuicPacketSequenceNumber fec_packet_sequence_number,
const QuicFecData& fec);
// Returns true if a packet can be revived from this FEC group.
@@ -57,10 +57,6 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
return base::StringPiece(payload_parity_, payload_parity_len_);
}
- bool entropy_parity() const {
- return entropy_parity_;
- }
-
QuicPacketSequenceNumber min_protected_packet() const {
return min_protected_packet_;
}
@@ -69,8 +65,13 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
return received_packets_.size();
}
+ // Returns the effective encryption level of the FEC group.
+ EncryptionLevel effective_encryption_level() const {
+ return effective_encryption_level_;
+ }
+
private:
- bool UpdateParity(base::StringPiece payload, bool entropy);
+ bool UpdateParity(base::StringPiece payload);
// Returns the number of missing packets, or size_t max if the number
// of missing packets is not known.
size_t NumMissingPackets() const;
@@ -88,7 +89,9 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
// The cumulative parity calculation of all received packets.
char payload_parity_[kMaxPacketSize];
size_t payload_parity_len_;
- bool entropy_parity_;
+ // The effective encryption level, which is the lowest encryption level of
+ // the data and FEC in the group.
+ EncryptionLevel effective_encryption_level_;
DISALLOW_COPY_AND_ASSIGN(QuicFecGroup);
};
diff --git a/chromium/net/quic/quic_fec_group_test.cc b/chromium/net/quic/quic_fec_group_test.cc
index 3de40b1bc7c..b9420dd11d5 100644
--- a/chromium/net/quic/quic_fec_group_test.cc
+++ b/chromium/net/quic/quic_fec_group_test.cc
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/quic/quic_fec_group.h"
+
#include <algorithm>
#include <vector>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "net/quic/quic_fec_group.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
@@ -35,8 +37,6 @@ const bool kEntropyFlag[] = {
true,
};
-const bool kTestFecPacketEntropy = false;
-
} // namespace
class QuicFecGroupTest : public ::testing::Test {
@@ -44,7 +44,6 @@ class QuicFecGroupTest : public ::testing::Test {
void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) {
size_t max_len = strlen(kData[0]);
scoped_ptr<char[]> redundancy(new char[max_len]);
- bool entropy_redundancy = false;
for (size_t packet = 0; packet < num_packets; ++packet) {
for (size_t i = 0; i < max_len; i++) {
if (packet == 0) {
@@ -56,7 +55,6 @@ class QuicFecGroupTest : public ::testing::Test {
uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i];
redundancy[i] = redundancy[i] ^ byte;
}
- entropy_redundancy = (entropy_redundancy != kEntropyFlag[packet]);
}
QuicFecGroup group;
@@ -71,17 +69,19 @@ class QuicFecGroupTest : public ::testing::Test {
QuicFecData fec;
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
} else {
QuicPacketHeader header;
header.packet_sequence_number = packet;
header.entropy_flag = kEntropyFlag[packet];
- ASSERT_TRUE(group.Update(header, kData[packet]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
}
ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1));
}
} else {
- // Update the FEC state for each non-lost packet.
+ // Update the FEC state for each non-lost packet.
for (size_t packet = 0; packet < num_packets; packet++) {
if (packet == lost_packet) {
continue;
@@ -90,7 +90,8 @@ class QuicFecGroupTest : public ::testing::Test {
QuicPacketHeader header;
header.packet_sequence_number = packet;
header.entropy_flag = kEntropyFlag[packet];
- ASSERT_TRUE(group.Update(header, kData[packet]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
ASSERT_FALSE(group.CanRevive());
}
@@ -100,7 +101,8 @@ class QuicFecGroupTest : public ::testing::Test {
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
}
QuicPacketHeader header;
char recovered[kMaxPacketSize];
@@ -112,7 +114,8 @@ class QuicFecGroupTest : public ::testing::Test {
EXPECT_EQ(lost_packet, header.packet_sequence_number)
<< "Failed to revive packet " << lost_packet << " out of "
<< num_packets;
- EXPECT_EQ(kEntropyFlag[lost_packet], header.entropy_flag);
+ // Revived packets have an unknown entropy.
+ EXPECT_FALSE(header.entropy_flag);
ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length";
for (size_t i = 0; i < strlen(kData[lost_packet]); i++) {
EXPECT_EQ(kData[lost_packet][i], recovered[i]);
@@ -150,14 +153,14 @@ TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) {
QuicPacketHeader header;
header.packet_sequence_number = 3;
- group.Update(header, data1);
+ group.Update(ENCRYPTION_FORWARD_SECURE, header, data1);
QuicFecData fec;
fec.fec_group = 1;
fec.redundancy = redundancy;
header.packet_sequence_number = 2;
- ASSERT_FALSE(group.UpdateFec(2, kTestFecPacketEntropy, fec));
+ ASSERT_FALSE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 2, fec));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
@@ -165,7 +168,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
header.packet_sequence_number = 3;
QuicFecGroup group;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -180,13 +183,13 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) {
header.packet_sequence_number = 3;
QuicFecGroup group;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
header.packet_sequence_number = 7;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
header.packet_sequence_number = 5;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -206,7 +209,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
fec.redundancy = kData[0];
QuicFecGroup group;
- ASSERT_TRUE(group.UpdateFec(3, kTestFecPacketEntropy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 3, fec));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -216,4 +219,24 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
EXPECT_TRUE(group.ProtectsPacketsBefore(50));
}
+TEST_F(QuicFecGroupTest, EffectiveEncryptionLevel) {
+ QuicFecGroup group;
+ EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.effective_encryption_level());
+
+ QuicPacketHeader header;
+ header.packet_sequence_number = 5;
+ ASSERT_TRUE(group.Update(ENCRYPTION_INITIAL, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ QuicFecData fec;
+ fec.fec_group = 0;
+ fec.redundancy = kData[0];
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 7, fec));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ header.packet_sequence_number = 3;
+ ASSERT_TRUE(group.Update(ENCRYPTION_NONE, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_NONE, group.effective_encryption_level());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_flags.cc b/chromium/net/quic/quic_flags.cc
new file mode 100644
index 00000000000..10eaeb36847
--- /dev/null
+++ b/chromium/net/quic/quic_flags.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 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_flags.h"
+
+// TODO(rtenneti): Remove this.
+// Do not flip this flag until the flakiness of the
+// net/tools/quic/end_to_end_test is fixed.
+// If true, then QUIC connections will track the retransmission history of a
+// packet so that an ack of a previous transmission will ack the data of all
+// other transmissions.
+bool FLAGS_track_retransmission_history = false;
+
+// Do not remove this flag until the Finch-trials described in b/11706275
+// are complete.
+// If true, QUIC connections will support the use of a pacing algorithm when
+// sending packets, in an attempt to reduce packet loss. The client must also
+// request pacing for the server to enable it.
+bool FLAGS_enable_quic_pacing = true;
+
+// Do not remove this flag until b/11792453 is marked as Fixed.
+// If true, turns on stream flow control in QUIC.
+// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_17
+// or higher will timeout. New connections will be fine.
+// If disabling this flag, also disable enable_quic_connection_flow_control_2.
+bool FLAGS_enable_quic_stream_flow_control_2 = true;
+
+// Do not remove this flag until b/11792453 is marked as Fixed.
+// If true, turns on connection level flow control in QUIC.
+// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_19
+// or higher will timeout. New connections will be fine.
+bool FLAGS_enable_quic_connection_flow_control_2 = true;
+
+bool FLAGS_quic_allow_oversized_packets_for_test = false;
+
+// When true, the use time based loss detection instead of nack.
+bool FLAGS_quic_use_time_loss_detection = false;
+
+// If true, allow peer port migration of established QUIC connections.
+bool FLAGS_quic_allow_port_migration = true;
+
+// If true, it will return as soon as an error is detected while validating
+// CHLO.
+bool FLAGS_use_early_return_when_verifying_chlo = true;
+
+// If true, QUIC crypto reject message will include the reasons for rejection.
+bool FLAGS_send_quic_crypto_reject_reason = false;
diff --git a/chromium/net/quic/quic_flags.h b/chromium/net/quic/quic_flags.h
new file mode 100644
index 00000000000..c650738cfe7
--- /dev/null
+++ b/chromium/net/quic/quic_flags.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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_FLAGS_H_
+#define NET_QUIC_QUIC_FLAGS_H_
+
+#include "net/base/net_export.h"
+
+NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stream_flow_control_2;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_connection_flow_control_2;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_port_migration;
+NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo;
+NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason;
+
+#endif // NET_QUIC_QUIC_FLAGS_H_
diff --git a/chromium/net/quic/quic_flow_controller.cc b/chromium/net/quic/quic_flow_controller.cc
new file mode 100644
index 00000000000..107b4d9510a
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller.cc
@@ -0,0 +1,203 @@
+// Copyright 2014 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_flow_controller.h"
+
+#include "base/basictypes.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicFlowController::QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window)
+ : connection_(connection),
+ id_(id),
+ is_enabled_(true),
+ is_server_(is_server),
+ bytes_consumed_(0),
+ highest_received_byte_offset_(0),
+ bytes_sent_(0),
+ send_window_offset_(send_window_offset),
+ receive_window_offset_(receive_window_offset),
+ max_receive_window_(max_receive_window),
+ last_blocked_send_window_offset_(0) {
+ DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
+ << ", setting initial receive window offset to: "
+ << receive_window_offset_
+ << ", max receive window to: "
+ << max_receive_window_
+ << ", setting send window offset to: " << send_window_offset_;
+ if (connection_->version() < QUIC_VERSION_17) {
+ DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_
+ << ", QUIC version " << connection_->version();
+ Disable();
+ }
+}
+
+void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ bytes_consumed_ += bytes_consumed;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_;
+
+ MaybeSendWindowUpdate();
+}
+
+bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if offset has increased.
+ if (new_offset <= highest_received_byte_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "Stream " << id_
+ << " highest byte offset increased from: "
+ << highest_received_byte_offset_ << " to " << new_offset;
+ highest_received_byte_offset_ = new_offset;
+ return true;
+}
+
+void QuicFlowController::AddBytesSent(uint64 bytes_sent) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (bytes_sent_ + bytes_sent > send_window_offset_) {
+ LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
+ << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
+ << ", and send_window_offset_ = " << send_window_offset_;
+ bytes_sent_ = send_window_offset_;
+
+ // This is an error on our side, close the connection as soon as possible.
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ return;
+ }
+
+ bytes_sent_ += bytes_sent;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_;
+}
+
+bool QuicFlowController::FlowControlViolation() {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ if (highest_received_byte_offset_ > receive_window_offset_) {
+ LOG(ERROR) << ENDPOINT << "Flow control violation on stream "
+ << id_ << ", receive window offset: "
+ << receive_window_offset_
+ << ", highest received byte offset: "
+ << highest_received_byte_offset_;
+ return true;
+ }
+ return false;
+}
+
+void QuicFlowController::MaybeSendWindowUpdate() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ // Send WindowUpdate to increase receive window if
+ // (receive window offset - consumed bytes) < (max window / 2).
+ // This is behaviour copied from SPDY.
+ DCHECK_LT(bytes_consumed_, receive_window_offset_);
+ size_t consumed_window = receive_window_offset_ - bytes_consumed_;
+ size_t threshold = (max_receive_window_ / 2);
+
+ if (consumed_window < threshold) {
+ // Update our receive window.
+ receive_window_offset_ += (max_receive_window_ - consumed_window);
+
+ DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
+ << ", consumed bytes: " << bytes_consumed_
+ << ", consumed window: " << consumed_window
+ << ", and threshold: " << threshold
+ << ", and max recvw: " << max_receive_window_
+ << ". New receive window offset is: " << receive_window_offset_;
+
+ // Inform the peer of our new receive window.
+ connection_->SendWindowUpdate(id_, receive_window_offset_);
+ }
+}
+
+void QuicFlowController::MaybeSendBlocked() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (SendWindowSize() == 0 &&
+ last_blocked_send_window_offset_ < send_window_offset_) {
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. "
+ << "Send window: " << SendWindowSize()
+ << ", bytes sent: " << bytes_sent_
+ << ", send limit: " << send_window_offset_;
+ // The entire send_window has been consumed, we are now flow control
+ // blocked.
+ connection_->SendBlocked(id_);
+
+ // Keep track of when we last sent a BLOCKED frame so that we only send one
+ // at a given send offset.
+ last_blocked_send_window_offset_ = send_window_offset_;
+ }
+}
+
+bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if send window has increased.
+ if (new_send_window_offset <= send_window_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
+ << " with new offset " << new_send_window_offset
+ << " , current offset: " << send_window_offset_;
+
+ send_window_offset_ = new_send_window_offset;
+ return true;
+}
+
+void QuicFlowController::Disable() {
+ is_enabled_ = false;
+}
+
+bool QuicFlowController::IsEnabled() const {
+ bool connection_flow_control_enabled =
+ (id_ == kConnectionLevelId &&
+ FLAGS_enable_quic_connection_flow_control_2);
+ bool stream_flow_control_enabled =
+ (id_ != kConnectionLevelId &&
+ FLAGS_enable_quic_stream_flow_control_2);
+ return (connection_flow_control_enabled || stream_flow_control_enabled) &&
+ is_enabled_;
+}
+
+bool QuicFlowController::IsBlocked() const {
+ return IsEnabled() && SendWindowSize() == 0;
+}
+
+uint64 QuicFlowController::SendWindowSize() const {
+ if (bytes_sent_ > send_window_offset_) {
+ return 0;
+ }
+ return send_window_offset_ - bytes_sent_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_flow_controller.h b/chromium/net/quic/quic_flow_controller.h
new file mode 100644
index 00000000000..e5f5494b7f1
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller.h
@@ -0,0 +1,130 @@
+// Copyright 2014 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_FLOW_CONTROLLER_H_
+#define NET_QUIC_QUIC_FLOW_CONTROLLER_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class QuicFlowControllerPeer;
+} // namespace test
+
+class QuicConnection;
+
+const QuicStreamId kConnectionLevelId = 0;
+
+// QuicFlowController allows a QUIC stream or connection to perform flow
+// control. The stream/connection owns a QuicFlowController which keeps track of
+// bytes sent/received, can tell the owner if it is flow control blocked, and
+// can send WINDOW_UPDATE or BLOCKED frames when needed.
+class NET_EXPORT_PRIVATE QuicFlowController {
+ public:
+ QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window);
+ ~QuicFlowController() {}
+
+ // Called when we see a new highest received byte offset from the peer, either
+ // via a data frame or a RST.
+ // Returns true if this call changes highest_received_byte_offset_, and false
+ // in the case where |new_offset| is <= highest_received_byte_offset_.
+ bool UpdateHighestReceivedOffset(uint64 new_offset);
+
+ // Called when bytes received from the peer are consumed locally. This may
+ // trigger the sending of a WINDOW_UPDATE frame using |connection|.
+ void AddBytesConsumed(uint64 bytes_consumed);
+
+ // Called when bytes are sent to the peer.
+ void AddBytesSent(uint64 bytes_sent);
+
+ // Set a new send window offset.
+ // Returns true if this changes send_window_offset_, and false in the case
+ // where |new_send_window| is <= send_window_offset_.
+ bool UpdateSendWindowOffset(uint64 new_send_window_offset);
+
+ // Returns the current available send window.
+ uint64 SendWindowSize() const;
+
+ // Send a BLOCKED frame if appropriate.
+ void MaybeSendBlocked();
+
+ // Disable flow control.
+ void Disable();
+
+ // Returns true if flow control is enabled.
+ bool IsEnabled() const;
+
+ // Returns true if flow control send limits have been reached.
+ bool IsBlocked() const;
+
+ // Returns true if flow control receive limits have been violated by the peer.
+ bool FlowControlViolation();
+
+ uint64 bytes_consumed() const { return bytes_consumed_; }
+
+ uint64 highest_received_byte_offset() const {
+ return highest_received_byte_offset_;
+ }
+
+ private:
+ friend class test::QuicFlowControllerPeer;
+
+ // Send a WINDOW_UPDATE frame if appropriate.
+ void MaybeSendWindowUpdate();
+
+ // The parent connection, used to send connection close on flow control
+ // violation, and WINDOW_UPDATE and BLOCKED frames when appropriate.
+ // Not owned.
+ QuicConnection* connection_;
+
+ // ID of stream this flow controller belongs to. This can be 0 if this is a
+ // connection level flow controller.
+ QuicStreamId id_;
+
+ // True if flow control is enabled.
+ bool is_enabled_;
+
+ // True if this is owned by a server.
+ bool is_server_;
+
+ // Track number of bytes received from the peer, which have been consumed
+ // locally.
+ uint64 bytes_consumed_;
+
+ // The highest byte offset we have seen from the peer. This could be the
+ // highest offset in a data frame, or a final value in a RST.
+ uint64 highest_received_byte_offset_;
+
+ // Tracks number of bytes sent to the peer.
+ uint64 bytes_sent_;
+
+ // The absolute offset in the outgoing byte stream. If this offset is reached
+ // then we become flow control blocked until we receive a WINDOW_UPDATE.
+ uint64 send_window_offset_;
+
+ // The absolute offset in the incoming byte stream. The peer should never send
+ // us bytes which are beyond this offset.
+ uint64 receive_window_offset_;
+
+ // Largest size the receive window can grow to.
+ uint64 max_receive_window_;
+
+ // Keep track of the last time we sent a BLOCKED frame. We should only send
+ // another when the number of bytes we have sent has changed.
+ uint64 last_blocked_send_window_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowController);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FLOW_CONTROLLER_H_
diff --git a/chromium/net/quic/quic_flow_controller_test.cc b/chromium/net/quic/quic_flow_controller_test.cc
new file mode 100644
index 00000000000..26781acb65f
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller_test.cc
@@ -0,0 +1,224 @@
+// Copyright 2014 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_flow_controller.h"
+
+#include "base/strings/stringprintf.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::StringPrintf;
+
+namespace net {
+namespace test {
+
+using ::testing::_;
+
+class QuicFlowControllerTest : public ::testing::Test {
+ public:
+ QuicFlowControllerTest()
+ : stream_id_(1234),
+ send_window_(kInitialSessionFlowControlWindowForTest),
+ receive_window_(kInitialSessionFlowControlWindowForTest),
+ max_receive_window_(kInitialSessionFlowControlWindowForTest),
+ connection_(false),
+ old_flag_(&FLAGS_enable_quic_stream_flow_control_2, true) {
+ }
+
+ void Initialize() {
+ flow_controller_.reset(new QuicFlowController(
+ &connection_, stream_id_, false, send_window_,
+ receive_window_, max_receive_window_));
+ }
+
+ protected:
+ QuicStreamId stream_id_;
+ uint64 send_window_;
+ uint64 receive_window_;
+ uint64 max_receive_window_;
+ scoped_ptr<QuicFlowController> flow_controller_;
+ MockConnection connection_;
+ ValueRestore<bool> old_flag_;
+};
+
+TEST_F(QuicFlowControllerTest, SendingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send some bytes, but not enough to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(1);
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, and verify this has unblocked.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Updating with a smaller offset doesn't change anything.
+ EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10));
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Try to send more bytes, violating flow control.
+ EXPECT_CALL(connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA));
+ EXPECT_DFATAL(
+ flow_controller_->AddBytesSent(send_window_ * 10),
+ StringPrintf("Trying to send an extra %d bytes",
+ static_cast<int>(send_window_ * 10)));
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(
+ flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, _)).Times(1);
+
+ flow_controller_->AddBytesConsumed(1 + receive_window_ / 2);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+}
+
+TEST_F(QuicFlowControllerTest,
+ DisabledWhenQuicVersionDoesNotSupportFlowControl) {
+ // Only support version 16: no flow control.
+ QuicConnectionPeer::SetSupportedVersions(&connection_,
+ SupportedVersions(QUIC_VERSION_16));
+
+ Initialize();
+
+ MockConnection connection(false);
+
+ // Should not be enabled, and should not report as blocked.
+ EXPECT_FALSE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+
+ // Any attempts to add/remove bytes should have no effect.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset(
+ flow_controller_.get()));
+ flow_controller_->AddBytesSent(123);
+ flow_controller_->AddBytesConsumed(456);
+ flow_controller_->UpdateHighestReceivedOffset(789);
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset(
+ flow_controller_.get()));
+
+ // Any attempt to change offset should have no effect.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ flow_controller_->UpdateSendWindowOffset(send_window_ + 12345);
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+
+ // The connection should never send WINDOW_UPDATE or BLOCKED frames, even if
+ // the internal state implies that it should.
+
+ // If the flow controller was enabled, then a send window size of 0 would
+ // trigger a BLOCKED frame to be sent.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_CALL(connection_, SendBlocked(_)).Times(0);
+ flow_controller_->MaybeSendBlocked();
+
+ // If the flow controller was enabled, then a WINDOW_UPDATE would be sent if
+ // (receive window) < (max receive window / 2)
+ QuicFlowControllerPeer::SetReceiveWindowOffset(flow_controller_.get(),
+ max_receive_window_ / 10);
+ EXPECT_TRUE(QuicFlowControllerPeer::ReceiveWindowSize(
+ flow_controller_.get()) < (max_receive_window_ / 2));
+ EXPECT_CALL(connection_, SendWindowUpdate(_, _)).Times(0);
+ flow_controller_->AddBytesConsumed(0);
+
+ // Should not be enabled, and should not report as blocked.
+ EXPECT_FALSE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+}
+
+TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) {
+ Initialize();
+
+ // Test that we don't send duplicate BLOCKED frames. We should only send one
+ // BLOCKED frame at a given send window offset.
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // Expect that 2 BLOCKED frames should get sent in total.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(2);
+
+ // BLOCKED frame should get sent.
+ flow_controller_->MaybeSendBlocked();
+
+ // BLOCKED frame should not get sent again until our send offset changes.
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, then send enough bytes to block again.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent as send offset has changed.
+ flow_controller_->MaybeSendBlocked();
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_framer.cc b/chromium/net/quic/quic_framer.cc
index cc02882ddc9..fd077bc21da 100644
--- a/chromium/net/quic/quic_framer.cc
+++ b/chromium/net/quic/quic_framer.cc
@@ -5,10 +5,16 @@
#include "net/quic/quic_framer.h"
#include "base/containers/hash_tables.h"
+#include "base/stl_util.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.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_data_writer.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
using base::StringPiece;
using std::make_pair;
@@ -32,8 +38,8 @@ const QuicPacketSequenceNumber k2ByteSequenceNumberMask =
const QuicPacketSequenceNumber k1ByteSequenceNumberMask =
GG_UINT64_C(0x00000000000000FF);
-const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF);
-const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF);
+const QuicConnectionId k1ByteConnectionIdMask = GG_UINT64_C(0x00000000000000FF);
+const QuicConnectionId k4ByteConnectionIdMask = GG_UINT64_C(0x00000000FFFFFFFF);
// Number of bits the sequence number length bits are shifted from the right
// edge of the public header.
@@ -49,6 +55,8 @@ const uint8 kPublicHeaderSequenceNumberShift = 4;
// ResetStream : 0b 00000001 (0x01)
// ConnectionClose : 0b 00000010 (0x02)
// GoAway : 0b 00000011 (0x03)
+// WindowUpdate : 0b 00000100 (0x04)
+// Blocked : 0b 00000101 (0x05)
//
// Special Frame Types encode both a Frame Type and corresponding flags
// all in the Frame Type byte. Currently defined Special Frame Types are:
@@ -108,8 +116,45 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target,
return (Delta(target, a) < Delta(target, b)) ? a : b;
}
+QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags) {
+ switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) {
+ case PACKET_FLAGS_6BYTE_SEQUENCE:
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_4BYTE_SEQUENCE:
+ return PACKET_4BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_2BYTE_SEQUENCE:
+ return PACKET_2BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_1BYTE_SEQUENCE:
+ return PACKET_1BYTE_SEQUENCE_NUMBER;
+ default:
+ LOG(DFATAL) << "Unreachable case statement.";
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ }
+}
+
+bool CanTruncate(
+ QuicVersion version, const QuicFrame& frame, size_t free_bytes) {
+ if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) &&
+ free_bytes >=
+ QuicFramer::GetMinAckFrameSize(version,
+ PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_6BYTE_SEQUENCE_NUMBER)) {
+ return true;
+ }
+ return false;
+}
+
} // namespace
+bool QuicFramerVisitorInterface::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool QuicFramerVisitorInterface::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
QuicFramer::QuicFramer(const QuicVersionVector& supported_versions,
QuicTime creation_time,
bool is_server)
@@ -118,10 +163,13 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions,
entropy_calculator_(NULL),
error_(QUIC_NO_ERROR),
last_sequence_number_(0),
- last_serialized_guid_(0),
+ last_serialized_connection_id_(0),
supported_versions_(supported_versions),
+ decrypter_level_(ENCRYPTION_NONE),
+ alternative_decrypter_level_(ENCRYPTION_NONE),
alternative_decrypter_latch_(false),
is_server_(is_server),
+ validate_flags_(true),
creation_time_(creation_time) {
DCHECK(!supported_versions.empty());
quic_version_ = supported_versions_[0];
@@ -136,10 +184,13 @@ QuicFramer::~QuicFramer() {}
size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version,
QuicStreamId stream_id,
QuicStreamOffset offset,
- bool last_frame_in_packet) {
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group) {
+ bool no_stream_frame_length = last_frame_in_packet &&
+ is_in_fec_group == NOT_IN_FEC_GROUP;
return kQuicFrameTypeSize + GetStreamIdSize(stream_id) +
GetStreamOffsetSize(offset) +
- (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize);
+ (no_stream_frame_length ? 0 : kQuicStreamPayloadLengthSize);
}
// static
@@ -147,14 +198,25 @@ size_t QuicFramer::GetMinAckFrameSize(
QuicVersion version,
QuicSequenceNumberLength sequence_number_length,
QuicSequenceNumberLength largest_observed_length) {
- return kQuicFrameTypeSize + kQuicEntropyHashSize +
- sequence_number_length + kQuicEntropyHashSize +
+ size_t len = kQuicFrameTypeSize + kQuicEntropyHashSize +
largest_observed_length + kQuicDeltaTimeLargestObservedSize;
+ if (version <= QUIC_VERSION_15) {
+ len += sequence_number_length + kQuicEntropyHashSize;
+ }
+ return len;
}
// static
-size_t QuicFramer::GetMinRstStreamFrameSize() {
- return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize +
+size_t QuicFramer::GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length) {
+ return kQuicFrameTypeSize + kQuicEntropyHashSize +
+ sequence_number_length;
+}
+
+// static
+size_t QuicFramer::GetMinRstStreamFrameSize(QuicVersion quic_version) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ kQuicMaxStreamOffsetSize + kQuicErrorCodeSize +
kQuicErrorDetailsLengthSize;
}
@@ -170,6 +232,16 @@ size_t QuicFramer::GetMinGoAwayFrameSize() {
}
// static
+size_t QuicFramer::GetWindowUpdateFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
+
+// static
+size_t QuicFramer::GetBlockedFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize;
+}
+
+// static
size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) {
// Sizes are 1 through 4 bytes.
for (int i = 1; i <= 4; ++i) {
@@ -202,22 +274,10 @@ size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) {
// static
size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) {
- return kPublicFlagsSize + PACKET_8BYTE_GUID +
+ return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID +
number_versions * kQuicVersionSize;
}
-// static
-bool QuicFramer::CanTruncate(
- QuicVersion version, const QuicFrame& frame, size_t free_bytes) {
- if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) &&
- free_bytes >= GetMinAckFrameSize(version,
- PACKET_6BYTE_SEQUENCE_NUMBER,
- PACKET_6BYTE_SEQUENCE_NUMBER)) {
- return true;
- }
- return false;
-}
-
bool QuicFramer::IsSupportedVersion(const QuicVersion version) const {
for (size_t i = 0; i < supported_versions_.size(); ++i) {
if (version == supported_versions_[i]) {
@@ -232,57 +292,46 @@ size_t QuicFramer::GetSerializedFrameLength(
size_t free_bytes,
bool first_frame,
bool last_frame,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length) {
if (frame.type == PADDING_FRAME) {
// PADDING implies end of packet.
return free_bytes;
}
size_t frame_len =
- ComputeFrameLength(frame, last_frame, sequence_number_length);
- if (frame_len > free_bytes) {
- // Only truncate the first frame in a packet, so if subsequent ones go
- // over, stop including more frames.
- if (!first_frame) {
- return 0;
- }
- if (CanTruncate(quic_version_, frame, free_bytes)) {
- // Truncate the frame so the packet will not exceed kMaxPacketSize.
- // Note that we may not use every byte of the writer in this case.
- DVLOG(1) << "Truncating large frame";
- return free_bytes;
- }
+ ComputeFrameLength(frame, last_frame, is_in_fec_group,
+ sequence_number_length);
+ if (frame_len <= free_bytes) {
+ // Frame fits within packet. Note that acks may be truncated.
+ return frame_len;
+ }
+ // Only truncate the first frame in a packet, so if subsequent ones go
+ // over, stop including more frames.
+ if (!first_frame) {
+ return 0;
+ }
+ if (CanTruncate(quic_version_, frame, free_bytes)) {
+ // Truncate the frame so the packet will not exceed kMaxPacketSize.
+ // Note that we may not use every byte of the writer in this case.
+ DVLOG(1) << "Truncating large frame, free bytes: " << free_bytes;
+ return free_bytes;
}
+ if (!FLAGS_quic_allow_oversized_packets_for_test) {
+ return 0;
+ }
+ LOG(DFATAL) << "Packet size too small to fit frame.";
return frame_len;
}
-QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { }
+QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {}
-QuicFramer::AckFrameInfo::~AckFrameInfo() { }
+QuicFramer::AckFrameInfo::~AckFrameInfo() {}
QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash(
const QuicPacketHeader& header) const {
return header.entropy_flag << (header.packet_sequence_number % 8);
}
-// Test only.
-SerializedPacket QuicFramer::BuildUnsizedDataPacket(
- const QuicPacketHeader& header,
- const QuicFrames& frames) {
- const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize);
- size_t packet_size = GetPacketHeaderSize(header);
- for (size_t i = 0; i < frames.size(); ++i) {
- DCHECK_LE(packet_size, max_plaintext_size);
- bool first_frame = i == 0;
- bool last_frame = i == frames.size() - 1;
- const size_t frame_size = GetSerializedFrameLength(
- frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
- header.public_header.sequence_number_length);
- DCHECK(frame_size);
- packet_size += frame_size;
- }
- return BuildDataPacket(header, frames, packet_size);
-}
-
SerializedPacket QuicFramer::BuildDataPacket(
const QuicPacketHeader& header,
const QuicFrames& frames,
@@ -291,14 +340,19 @@ SerializedPacket QuicFramer::BuildDataPacket(
const SerializedPacket kNoPacket(
0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
return kNoPacket;
}
for (size_t i = 0; i < frames.size(); ++i) {
const QuicFrame& frame = frames[i];
- const bool last_frame_in_packet = i == (frames.size() - 1);
- if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) {
+ // Determine if we should write stream frame length in header.
+ const bool no_stream_frame_length =
+ (header.is_in_fec_group == NOT_IN_FEC_GROUP) &&
+ (i == frames.size() - 1);
+ if (!AppendTypeByte(frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendTypeByte failed";
return kNoPacket;
}
@@ -307,41 +361,80 @@ SerializedPacket QuicFramer::BuildDataPacket(
writer.WritePadding();
break;
case STREAM_FRAME:
- if (!AppendStreamFramePayload(
- *frame.stream_frame, last_frame_in_packet, &writer)) {
+ if (!AppendStreamFrame(
+ *frame.stream_frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendStreamFrame failed";
return kNoPacket;
}
break;
case ACK_FRAME:
- if (!AppendAckFramePayloadAndTypeByte(
+ if (!AppendAckFrameAndTypeByte(
header, *frame.ack_frame, &writer)) {
+ LOG(DFATAL) << "AppendAckFrameAndTypeByte failed";
return kNoPacket;
}
break;
case CONGESTION_FEEDBACK_FRAME:
- if (!AppendQuicCongestionFeedbackFramePayload(
+ if (!AppendCongestionFeedbackFrame(
*frame.congestion_feedback_frame, &writer)) {
+ LOG(DFATAL) << "AppendCongestionFeedbackFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ if (quic_version_ <= QUIC_VERSION_15) {
+ LOG(DFATAL) << "Attempt to add a StopWaitingFrame in "
+ << QuicVersionToString(quic_version_);
+ return kNoPacket;
+ }
+ if (!AppendStopWaitingFrame(
+ header, *frame.stop_waiting_frame, &writer)) {
+ LOG(DFATAL) << "AppendStopWaitingFrame failed";
return kNoPacket;
}
break;
+ case PING_FRAME:
+ if (quic_version_ <= QUIC_VERSION_17) {
+ LOG(DFATAL) << "Attempt to add a PingFrame in "
+ << QuicVersionToString(quic_version_);
+ return kNoPacket;
+ }
+ // Ping has no payload.
+ break;
case RST_STREAM_FRAME:
- if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) {
+ if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) {
+ LOG(DFATAL) << "AppendRstStreamFrame failed";
return kNoPacket;
}
break;
case CONNECTION_CLOSE_FRAME:
- if (!AppendConnectionCloseFramePayload(
+ if (!AppendConnectionCloseFrame(
*frame.connection_close_frame, &writer)) {
+ LOG(DFATAL) << "AppendConnectionCloseFrame failed";
return kNoPacket;
}
break;
case GOAWAY_FRAME:
- if (!AppendGoAwayFramePayload(*frame.goaway_frame, &writer)) {
+ if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) {
+ LOG(DFATAL) << "AppendGoAwayFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case WINDOW_UPDATE_FRAME:
+ if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) {
+ LOG(DFATAL) << "AppendWindowUpdateFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) {
+ LOG(DFATAL) << "AppendBlockedFrame failed";
return kNoPacket;
}
break;
default:
RaiseError(QUIC_INVALID_FRAME_DATA);
+ LOG(DFATAL) << "QUIC_INVALID_FRAME_DATA";
return kNoPacket;
}
}
@@ -352,7 +445,7 @@ SerializedPacket QuicFramer::BuildDataPacket(
// length, even though they're typically slightly shorter.
DCHECK_LE(len, packet_size);
QuicPacket* packet = QuicPacket::NewDataPacket(
- writer.take(), len, true, header.public_header.guid_length,
+ writer.take(), len, true, header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length);
@@ -377,10 +470,12 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
const SerializedPacket kNoPacket(
0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
return kNoPacket;
}
if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) {
+ LOG(DFATAL) << "Failed to add FEC";
return kNoPacket;
}
@@ -388,7 +483,7 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
header.packet_sequence_number,
header.public_header.sequence_number_length,
QuicPacket::NewFecPacket(writer.take(), len, true,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
GetPacketEntropyHash(header), NULL);
@@ -398,27 +493,37 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket(
const QuicPublicResetPacket& packet) {
DCHECK(packet.public_header.reset_flag);
- size_t len = GetPublicResetPacketSize();
+
+ CryptoHandshakeMessage reset;
+ reset.set_tag(kPRST);
+ reset.SetValue(kRNON, packet.nonce_proof);
+ reset.SetValue(kRSEQ, packet.rejected_sequence_number);
+ if (!packet.client_address.address().empty()) {
+ // packet.client_address is non-empty.
+ QuicSocketAddressCoder address_coder(packet.client_address);
+ string serialized_address = address_coder.Encode();
+ if (serialized_address.empty()) {
+ return NULL;
+ }
+ reset.SetStringPiece(kCADR, serialized_address);
+ }
+ const QuicData& reset_serialized = reset.GetSerialized();
+
+ size_t len =
+ kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + reset_serialized.length();
QuicDataWriter writer(len);
uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST |
- PACKET_PUBLIC_FLAGS_8BYTE_GUID |
- PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE);
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
if (!writer.WriteUInt8(flags)) {
return NULL;
}
- if (!writer.WriteUInt64(packet.public_header.guid)) {
- return NULL;
- }
-
- if (!writer.WriteUInt64(packet.nonce_proof)) {
+ if (!writer.WriteUInt64(packet.public_header.connection_id)) {
return NULL;
}
- if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER,
- packet.rejected_sequence_number,
- &writer)) {
+ if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) {
return NULL;
}
@@ -433,13 +538,12 @@ QuicEncryptedPacket* QuicFramer::BuildVersionNegotiationPacket(
QuicDataWriter writer(len);
uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION |
- PACKET_PUBLIC_FLAGS_8BYTE_GUID |
- PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE);
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
if (!writer.WriteUInt8(flags)) {
return NULL;
}
- if (!writer.WriteUInt64(header.guid)) {
+ if (!writer.WriteUInt64(header.connection_id)) {
return NULL;
}
@@ -466,6 +570,12 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
return RaiseError(QUIC_INVALID_PACKET_HEADER);
}
+ if (!visitor_->OnUnauthenticatedPublicHeader(public_header)) {
+ // The visitor suppresses further processing of the packet.
+ reader_.reset(NULL);
+ return true;
+ }
+
if (is_server_ && public_header.version_flag &&
public_header.versions[0] != quic_version_) {
if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) {
@@ -548,16 +658,39 @@ bool QuicFramer::ProcessDataPacket(
bool QuicFramer::ProcessPublicResetPacket(
const QuicPacketPublicHeader& public_header) {
QuicPublicResetPacket packet(public_header);
- if (!reader_->ReadUInt64(&packet.nonce_proof)) {
+
+ scoped_ptr<CryptoHandshakeMessage> reset(
+ CryptoFramer::ParseMessage(reader_->ReadRemainingPayload()));
+ if (!reset.get()) {
+ set_detailed_error("Unable to read reset message.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ if (reset->tag() != kPRST) {
+ set_detailed_error("Incorrect message tag.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+
+ if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) {
set_detailed_error("Unable to read nonce proof.");
return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
// TODO(satyamshekhar): validate nonce to protect against DoS.
- if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) {
+ if (reset->GetUint64(kRSEQ, &packet.rejected_sequence_number) !=
+ QUIC_NO_ERROR) {
set_detailed_error("Unable to read rejected sequence number.");
return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
+
+ StringPiece address;
+ if (reset->GetStringPiece(kCADR, &address)) {
+ QuicSocketAddressCoder address_coder;
+ if (address_coder.Decode(address.data(), address.length())) {
+ packet.client_address = IPEndPoint(address_coder.ip(),
+ address_coder.port());
+ }
+ }
+
visitor_->OnPublicResetPacket(packet);
return true;
}
@@ -593,6 +726,7 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header,
bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
QuicDataWriter* writer) {
+ DVLOG(1) << "Appending header: " << header;
DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP);
uint8 public_flags = 0;
if (header.public_header.reset_flag) {
@@ -606,38 +740,44 @@ bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
GetSequenceNumberFlags(header.public_header.sequence_number_length)
<< kPublicHeaderSequenceNumberShift;
- switch (header.public_header.guid_length) {
- case PACKET_0BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_0BYTE_GUID)) {
+ switch (header.public_header.connection_id_length) {
+ case PACKET_0BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) {
return false;
}
break;
- case PACKET_1BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_1BYTE_GUID)) {
+ case PACKET_1BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) {
+ if (!writer->WriteUInt8(
+ header.public_header.connection_id & k1ByteConnectionIdMask)) {
return false;
}
break;
- case PACKET_4BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_4BYTE_GUID)) {
+ case PACKET_4BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt32(header.public_header.guid & k4ByteGuidMask)) {
+ if (!writer->WriteUInt32(
+ header.public_header.connection_id & k4ByteConnectionIdMask)) {
return false;
}
break;
- case PACKET_8BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_8BYTE_GUID)) {
+ case PACKET_8BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt64(header.public_header.guid)) {
+ if (!writer->WriteUInt64(header.public_header.connection_id)) {
return false;
}
break;
}
- last_serialized_guid_ = header.public_header.guid;
+ last_serialized_connection_id_ = header.public_header.connection_id;
if (header.public_header.version_flag) {
DCHECK(!is_server_);
@@ -717,7 +857,8 @@ bool QuicFramer::ProcessPublicHeader(
public_header->version_flag =
(public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0;
- if (!public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ if (validate_flags_ &&
+ !public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
set_detailed_error("Illegal public flags value.");
return false;
}
@@ -727,46 +868,49 @@ bool QuicFramer::ProcessPublicHeader(
return false;
}
- switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) {
- case PACKET_PUBLIC_FLAGS_8BYTE_GUID:
- if (!reader_->ReadUInt64(&public_header->guid)) {
- set_detailed_error("Unable to read GUID.");
+ switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) {
+ case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID:
+ if (!reader_->ReadUInt64(&public_header->connection_id)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- public_header->guid_length = PACKET_8BYTE_GUID;
+ public_header->connection_id_length = PACKET_8BYTE_CONNECTION_ID;
break;
- case PACKET_PUBLIC_FLAGS_4BYTE_GUID:
- // If the guid is truncated, expect to read the last serialized guid.
- if (!reader_->ReadBytes(&public_header->guid, PACKET_4BYTE_GUID)) {
- set_detailed_error("Unable to read GUID.");
+ case PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID:
+ // If the connection_id is truncated, expect to read the last serialized
+ // connection_id.
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_4BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- if ((public_header->guid & k4ByteGuidMask) !=
- (last_serialized_guid_ & k4ByteGuidMask)) {
- set_detailed_error(
- "Truncated 4 byte GUID does not match previous guid.");
+ if ((public_header->connection_id & k4ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k4ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 4 byte ConnectionId does not match "
+ "previous connection_id.");
return false;
}
- public_header->guid_length = PACKET_4BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ public_header->connection_id_length = PACKET_4BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
- case PACKET_PUBLIC_FLAGS_1BYTE_GUID:
- if (!reader_->ReadBytes(&public_header->guid, PACKET_1BYTE_GUID)) {
- set_detailed_error("Unable to read GUID.");
+ case PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID:
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_1BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- if ((public_header->guid & k1ByteGuidMask) !=
- (last_serialized_guid_ & k1ByteGuidMask)) {
- set_detailed_error(
- "Truncated 1 byte GUID does not match previous guid.");
+ if ((public_header->connection_id & k1ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k1ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 1 byte ConnectionId does not match "
+ "previous connection_id.");
return false;
}
- public_header->guid_length = PACKET_1BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ public_header->connection_id_length = PACKET_1BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
- case PACKET_PUBLIC_FLAGS_0BYTE_GUID:
- public_header->guid_length = PACKET_0BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID:
+ public_header->connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
}
@@ -797,40 +941,6 @@ bool QuicFramer::ProcessPublicHeader(
}
// static
-bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet,
- QuicGuid* guid) {
- QuicDataReader reader(packet.data(), packet.length());
- uint8 public_flags;
- if (!reader.ReadBytes(&public_flags, 1)) {
- return false;
- }
- // Ensure it's an 8 byte guid.
- if ((public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) !=
- PACKET_PUBLIC_FLAGS_8BYTE_GUID) {
- return false;
- }
-
- return reader.ReadUInt64(guid);
-}
-
-// static
-QuicSequenceNumberLength QuicFramer::ReadSequenceNumberLength(uint8 flags) {
- switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) {
- case PACKET_FLAGS_6BYTE_SEQUENCE:
- return PACKET_6BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_4BYTE_SEQUENCE:
- return PACKET_4BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_2BYTE_SEQUENCE:
- return PACKET_2BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_1BYTE_SEQUENCE:
- return PACKET_1BYTE_SEQUENCE_NUMBER;
- default:
- LOG(DFATAL) << "Unreachable case statement.";
- return PACKET_6BYTE_SEQUENCE_NUMBER;
- }
-}
-
-// static
QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength(
QuicPacketSequenceNumber sequence_number) {
if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) {
@@ -1082,6 +1192,65 @@ bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) {
continue;
}
+ case WINDOW_UPDATE_FRAME: {
+ QuicWindowUpdateFrame window_update_frame;
+ if (!ProcessWindowUpdateFrame(&window_update_frame)) {
+ return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+ if (!visitor_->OnWindowUpdateFrame(window_update_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case BLOCKED_FRAME: {
+ QuicBlockedFrame blocked_frame;
+ if (!ProcessBlockedFrame(&blocked_frame)) {
+ return RaiseError(QUIC_INVALID_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(blocked_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case STOP_WAITING_FRAME: {
+ if (quic_version_ <= QUIC_VERSION_15) {
+ LOG(DFATAL) << "Trying to read a StopWaiting in "
+ << QuicVersionToString(quic_version_);
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ QuicStopWaitingFrame stop_waiting_frame;
+ if (!ProcessStopWaitingFrame(header, &stop_waiting_frame)) {
+ return RaiseError(QUIC_INVALID_STOP_WAITING_DATA);
+ }
+ if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+ case PING_FRAME: {
+ if (quic_version_ <= QUIC_VERSION_17) {
+ LOG(DFATAL) << "Trying to read a Ping in "
+ << QuicVersionToString(quic_version_);
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ // Ping has no payload.
+ QuicPingFrame ping_frame;
+ if (!visitor_->OnPingFrame(ping_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
default:
set_detailed_error("Illegal frame type.");
DLOG(WARNING) << "Illegal frame type: "
@@ -1152,8 +1321,10 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type,
bool QuicFramer::ProcessAckFrame(const QuicPacketHeader& header,
uint8 frame_type,
QuicAckFrame* frame) {
- if (!ProcessSentInfo(header, &frame->sent_info)) {
- return false;
+ if (quic_version_ <= QUIC_VERSION_15) {
+ if (!ProcessStopWaitingFrame(header, &frame->sent_info)) {
+ return false;
+ }
}
if (!ProcessReceivedInfo(frame_type, &frame->received_info)) {
return false;
@@ -1232,12 +1403,30 @@ bool QuicFramer::ProcessReceivedInfo(uint8 frame_type,
last_sequence_number -= (range_length + 1);
}
+ // Parse the revived packets list.
+ uint8 num_revived_packets;
+ if (!reader_->ReadBytes(&num_revived_packets, 1)) {
+ set_detailed_error("Unable to read num revived packets.");
+ return false;
+ }
+
+ for (size_t i = 0; i < num_revived_packets; ++i) {
+ QuicPacketSequenceNumber revived_packet = 0;
+ if (!reader_->ReadBytes(&revived_packet,
+ largest_observed_sequence_number_length)) {
+ set_detailed_error("Unable to read revived packet.");
+ return false;
+ }
+
+ received_info->revived_packets.insert(revived_packet);
+ }
+
return true;
}
-bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header,
- SentPacketInfo* sent_info) {
- if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) {
+bool QuicFramer::ProcessStopWaitingFrame(const QuicPacketHeader& header,
+ QuicStopWaitingFrame* stop_waiting) {
+ if (!reader_->ReadBytes(&stop_waiting->entropy_hash, 1)) {
set_detailed_error("Unable to read entropy hash for sent packets.");
return false;
}
@@ -1249,7 +1438,7 @@ bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header,
return false;
}
DCHECK_GE(header.packet_sequence_number, least_unacked_delta);
- sent_info->least_unacked =
+ stop_waiting->least_unacked =
header.packet_sequence_number - least_unacked_delta;
return true;
@@ -1269,12 +1458,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame(
case kInterArrival: {
CongestionFeedbackMessageInterArrival* inter_arrival =
&frame->inter_arrival;
- if (!reader_->ReadUInt16(
- &inter_arrival->accumulated_number_of_lost_packets)) {
- set_detailed_error(
- "Unable to read accumulated number of lost packets.");
- return false;
- }
uint8 num_received_packets;
if (!reader_->ReadBytes(&num_received_packets, 1)) {
set_detailed_error("Unable to read num received packets.");
@@ -1333,11 +1516,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame(
}
case kTCP: {
CongestionFeedbackMessageTCP* tcp = &frame->tcp;
- if (!reader_->ReadUInt16(&tcp->accumulated_number_of_lost_packets)) {
- set_detailed_error(
- "Unable to read accumulated number of lost packets.");
- return false;
- }
// TODO(ianswett): Remove receive window, since it's constant.
uint16 receive_window = 0;
if (!reader_->ReadUInt16(&receive_window)) {
@@ -1364,6 +1542,11 @@ bool QuicFramer::ProcessRstStreamFrame(QuicRstStreamFrame* frame) {
return false;
}
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read rst stream sent byte offset.");
+ return false;
+ }
+
uint32 error_code;
if (!reader_->ReadUInt32(&error_code)) {
set_detailed_error("Unable to read rst stream error code.");
@@ -1444,26 +1627,54 @@ bool QuicFramer::ProcessGoAwayFrame(QuicGoAwayFrame* frame) {
return true;
}
+bool QuicFramer::ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read window byte_offset.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessBlockedFrame(QuicBlockedFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ return true;
+}
+
// static
StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket(
const QuicEncryptedPacket& encrypted,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return StringPiece(encrypted.data() + kStartOfHashData,
- GetStartOfEncryptedData(
- guid_length, includes_version, sequence_number_length)
- - kStartOfHashData);
+ return StringPiece(
+ encrypted.data() + kStartOfHashData, GetStartOfEncryptedData(
+ connection_id_length, includes_version, sequence_number_length)
+ - kStartOfHashData);
}
-void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) {
+void QuicFramer::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
DCHECK(alternative_decrypter_.get() == NULL);
+ DCHECK_GE(level, decrypter_level_);
decrypter_.reset(decrypter);
+ decrypter_level_ = level;
}
void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used) {
alternative_decrypter_.reset(decrypter);
+ alternative_decrypter_level_ = level;
alternative_decrypter_latch_ = latch_once_used;
}
@@ -1489,18 +1700,6 @@ const QuicEncrypter* QuicFramer::encrypter(EncryptionLevel level) const {
return encrypter_[level].get();
}
-void QuicFramer::SwapCryptersForTest(QuicFramer* other) {
- for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
- encrypter_[i].swap(other->encrypter_[i]);
- }
- decrypter_.swap(other->decrypter_);
- alternative_decrypter_.swap(other->alternative_decrypter_);
-
- const bool other_latch = other->alternative_decrypter_latch_;
- other->alternative_decrypter_latch_ = alternative_decrypter_latch_;
- alternative_decrypter_latch_ = other_latch;
-}
-
QuicEncryptedPacket* QuicFramer::EncryptPacket(
EncryptionLevel level,
QuicPacketSequenceNumber packet_sequence_number,
@@ -1550,32 +1749,42 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header,
header.packet_sequence_number,
GetAssociatedDataFromEncryptedPacket(
packet,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
encrypted));
- if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) {
+ if (decrypted_.get() != NULL) {
+ visitor_->OnDecryptedPacket(decrypter_level_);
+ } else if (alternative_decrypter_.get() != NULL) {
decrypted_.reset(alternative_decrypter_->DecryptPacket(
header.packet_sequence_number,
GetAssociatedDataFromEncryptedPacket(
packet,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
encrypted));
if (decrypted_.get() != NULL) {
+ visitor_->OnDecryptedPacket(alternative_decrypter_level_);
if (alternative_decrypter_latch_) {
// Switch to the alternative decrypter and latch so that we cannot
// switch back.
decrypter_.reset(alternative_decrypter_.release());
+ decrypter_level_ = alternative_decrypter_level_;
+ alternative_decrypter_level_ = ENCRYPTION_NONE;
} else {
// Switch the alternative decrypter so that we use it first next time.
decrypter_.swap(alternative_decrypter_);
+ EncryptionLevel level = alternative_decrypter_level_;
+ alternative_decrypter_level_ = decrypter_level_;
+ decrypter_level_ = level;
}
}
}
if (decrypted_.get() == NULL) {
+ DLOG(WARNING) << "DecryptPacket failed for sequence_number:"
+ << header.packet_sequence_number;
return false;
}
@@ -1592,24 +1801,31 @@ size_t QuicFramer::GetAckFrameSize(
QuicSequenceNumberLength missing_sequence_number_length =
GetMinSequenceNumberLength(ack_info.max_delta);
- return GetMinAckFrameSize(quic_version_,
- sequence_number_length,
- largest_observed_length) +
- (ack_info.nack_ranges.empty() ? 0 : kNumberOfMissingPacketsSize) +
- ack_info.nack_ranges.size() *
- (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ size_t ack_size = GetMinAckFrameSize(quic_version_,
+ sequence_number_length,
+ largest_observed_length);
+ if (!ack_info.nack_ranges.empty()) {
+ ack_size += kNumberOfNackRangesSize + kNumberOfRevivedPacketsSize;
+ ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) *
+ (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ ack_size += min(ack.received_info.revived_packets.size(),
+ kMaxRevivedPackets) * largest_observed_length;
+ }
+ return ack_size;
}
size_t QuicFramer::ComputeFrameLength(
const QuicFrame& frame,
bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length) {
switch (frame.type) {
case STREAM_FRAME:
return GetMinStreamFrameSize(quic_version_,
frame.stream_frame->stream_id,
frame.stream_frame->offset,
- last_frame_in_packet) +
+ last_frame_in_packet,
+ is_in_fec_group) +
frame.stream_frame->data.TotalBufferSize();
case ACK_FRAME: {
return GetAckFrameSize(*frame.ack_frame, sequence_number_length);
@@ -1624,7 +1840,6 @@ size_t QuicFramer::ComputeFrameLength(
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
congestion_feedback.inter_arrival;
- len += 2;
len += 1; // Number received packets.
if (inter_arrival.received_packet_times.size() > 0) {
len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received.
@@ -1636,10 +1851,10 @@ size_t QuicFramer::ComputeFrameLength(
break;
}
case kFixRate:
- len += 4;
+ len += 4; // Bitrate.
break;
case kTCP:
- len += 4;
+ len += 2; // Receive window.
break;
default:
set_detailed_error("Illegal feedback type.");
@@ -1648,14 +1863,23 @@ size_t QuicFramer::ComputeFrameLength(
}
return len;
}
+ case STOP_WAITING_FRAME:
+ return GetStopWaitingFrameSize(sequence_number_length);
+ case PING_FRAME:
+ // Ping has no payload.
+ return kQuicFrameTypeSize;
case RST_STREAM_FRAME:
- return GetMinRstStreamFrameSize() +
+ return GetMinRstStreamFrameSize(quic_version_) +
frame.rst_stream_frame->error_details.size();
case CONNECTION_CLOSE_FRAME:
return GetMinConnectionCloseFrameSize() +
frame.connection_close_frame->error_details.size();
case GOAWAY_FRAME:
return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size();
+ case WINDOW_UPDATE_FRAME:
+ return GetWindowUpdateFrameSize();
+ case BLOCKED_FRAME:
+ return GetBlockedFrameSize();
case PADDING_FRAME:
DCHECK(false);
return 0;
@@ -1670,7 +1894,7 @@ size_t QuicFramer::ComputeFrameLength(
}
bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
- bool last_frame_in_packet,
+ bool no_stream_frame_length,
QuicDataWriter* writer) {
uint8 type_byte = 0;
switch (frame.type) {
@@ -1683,7 +1907,7 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
// Data Length bit.
type_byte <<= kQuicStreamDataLengthShift;
- type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask;
+ type_byte |= no_stream_frame_length ? 0: kQuicStreamDataLengthMask;
// Offset 3 bits.
type_byte <<= kQuicStreamOffsetShift;
@@ -1741,63 +1965,44 @@ bool QuicFramer::AppendPacketSequenceNumber(
packet_sequence_number & k6ByteSequenceNumberMask);
break;
default:
- NOTREACHED() << "sequence_number_length: " << sequence_number_length;
+ DCHECK(false) << "sequence_number_length: " << sequence_number_length;
return false;
}
}
-bool QuicFramer::AppendStreamFramePayload(
+bool QuicFramer::AppendStreamFrame(
const QuicStreamFrame& frame,
- bool last_frame_in_packet,
+ bool no_stream_frame_length,
QuicDataWriter* writer) {
if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) {
+ LOG(DFATAL) << "Writing stream id size failed.";
return false;
}
if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) {
+ LOG(DFATAL) << "Writing offset size failed.";
return false;
}
- if (!last_frame_in_packet) {
+ if (!no_stream_frame_length) {
if (!writer->WriteUInt16(frame.data.TotalBufferSize())) {
+ LOG(DFATAL) << "Writing stream frame length failed";
return false;
}
}
if (!writer->WriteIOVector(frame.data)) {
+ LOG(DFATAL) << "Writing frame data failed.";
return false;
}
return true;
}
// static
-bool QuicFramer::HasVersionFlag(const QuicEncryptedPacket& packet) {
- return packet.length() > 0 &&
- (packet.data()[0] & PACKET_PUBLIC_FLAGS_VERSION) != 0;
-}
-
-// static
-QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved(
- const SequenceNumberSet& missing_packets,
- SequenceNumberSet::const_iterator largest_written) {
- SequenceNumberSet::const_iterator it = largest_written;
- QuicPacketSequenceNumber previous_missing = *it;
- ++it;
-
- // See if the next thing is a gap in the missing packets: if it's a
- // non-missing packet we can return it.
- if (it != missing_packets.end() && previous_missing + 1 != *it) {
- return *it - 1;
- }
-
- // Otherwise return the largest missing packet, as indirectly observed.
- return *largest_written;
-}
-
void QuicFramer::set_version(const QuicVersion version) {
- DCHECK(IsSupportedVersion(version));
+ DCHECK(IsSupportedVersion(version)) << QuicVersionToString(version);
quic_version_ = version;
}
-bool QuicFramer::AppendAckFramePayloadAndTypeByte(
+bool QuicFramer::AppendAckFrameAndTypeByte(
const QuicPacketHeader& header,
const QuicAckFrame& frame,
QuicDataWriter* writer) {
@@ -1810,18 +2015,17 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
GetMinSequenceNumberLength(ack_info.max_delta);
// Determine whether we need to truncate ranges.
size_t available_range_bytes = writer->capacity() - writer->length() -
+ kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize -
GetMinAckFrameSize(quic_version_,
header.public_header.sequence_number_length,
largest_observed_length);
size_t max_num_ranges = available_range_bytes /
(missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
- max_num_ranges =
- min(static_cast<size_t>(numeric_limits<uint8>::max()), max_num_ranges);
+ max_num_ranges = min(kMaxNackRanges, max_num_ranges);
bool truncated = ack_info.nack_ranges.size() > max_num_ranges;
DVLOG_IF(1, truncated) << "Truncating ack from "
<< ack_info.nack_ranges.size() << " ranges to "
<< max_num_ranges;
-
// Write out the type byte by setting the low order bits and doing shifts
// to make room for the next bit flags to be set.
// Whether there are any nacks.
@@ -1845,18 +2049,10 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
return false;
}
- // TODO(satyamshekhar): Decide how often we really should send this
- // entropy_hash update.
- if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) {
- return false;
- }
-
- DCHECK_GE(header.packet_sequence_number, frame.sent_info.least_unacked);
- const QuicPacketSequenceNumber least_unacked_delta =
- header.packet_sequence_number - frame.sent_info.least_unacked;
- if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
- least_unacked_delta, writer)) {
- return false;
+ if (quic_version_ <= QUIC_VERSION_15) {
+ if (!AppendStopWaitingFrame(header, frame.sent_info, writer)) {
+ return false;
+ }
}
const ReceivedPacketInfo& received_info = frame.received_info;
@@ -1927,12 +2123,33 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
last_sequence_written = ack_iter->first - 1;
++num_ranges_written;
}
-
DCHECK_EQ(num_missing_ranges, num_ranges_written);
+
+ // Append revived packets.
+ // If not all the revived packets fit, only mention the ones that do.
+ uint8 num_revived_packets = min(received_info.revived_packets.size(),
+ kMaxRevivedPackets);
+ num_revived_packets = min(
+ static_cast<size_t>(num_revived_packets),
+ (writer->capacity() - writer->length()) / largest_observed_length);
+ if (!writer->WriteBytes(&num_revived_packets, 1)) {
+ return false;
+ }
+
+ SequenceNumberSet::const_iterator iter =
+ received_info.revived_packets.begin();
+ for (int i = 0; i < num_revived_packets; ++i, ++iter) {
+ LOG_IF(DFATAL, !ContainsKey(received_info.missing_packets, *iter));
+ if (!AppendPacketSequenceNumber(largest_observed_length,
+ *iter, writer)) {
+ return false;
+ }
+ }
+
return true;
}
-bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
+bool QuicFramer::AppendCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
QuicDataWriter* writer) {
if (!writer->WriteBytes(&frame.type, 1)) {
@@ -1943,10 +2160,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
frame.inter_arrival;
- if (!writer->WriteUInt16(
- inter_arrival.accumulated_number_of_lost_packets)) {
- return false;
- }
DCHECK_GE(numeric_limits<uint8>::max(),
inter_arrival.received_packet_times.size());
if (inter_arrival.received_packet_times.size() >
@@ -2007,9 +2220,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
DCHECK_LE(tcp.receive_window, 1u << 20);
// Simple bit packing, don't send the 4 least significant bits.
uint16 receive_window = static_cast<uint16>(tcp.receive_window >> 4);
- if (!writer->WriteUInt16(tcp.accumulated_number_of_lost_packets)) {
- return false;
- }
if (!writer->WriteUInt16(receive_window)) {
return false;
}
@@ -2022,13 +2232,48 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
return true;
}
-bool QuicFramer::AppendRstStreamFramePayload(
+bool QuicFramer::AppendStopWaitingFrame(
+ const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(header.packet_sequence_number, frame.least_unacked);
+ const QuicPacketSequenceNumber least_unacked_delta =
+ header.packet_sequence_number - frame.least_unacked;
+ const QuicPacketSequenceNumber length_shift =
+ header.public_header.sequence_number_length * 8;
+ if (!writer->WriteUInt8(frame.entropy_hash)) {
+ LOG(DFATAL) << " hash failed";
+ return false;
+ }
+
+ if (least_unacked_delta >> length_shift > 0) {
+ LOG(DFATAL) << "sequence_number_length "
+ << header.public_header.sequence_number_length
+ << " is too small for least_unacked_delta: "
+ << least_unacked_delta;
+ return false;
+ }
+ if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
+ least_unacked_delta, writer)) {
+ LOG(DFATAL) << " seq failed: "
+ << header.public_header.sequence_number_length;
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendRstStreamFrame(
const QuicRstStreamFrame& frame,
QuicDataWriter* writer) {
if (!writer->WriteUInt32(frame.stream_id)) {
return false;
}
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+
uint32 error_code = static_cast<uint32>(frame.error_code);
if (!writer->WriteUInt32(error_code)) {
return false;
@@ -2040,7 +2285,7 @@ bool QuicFramer::AppendRstStreamFramePayload(
return true;
}
-bool QuicFramer::AppendConnectionCloseFramePayload(
+bool QuicFramer::AppendConnectionCloseFrame(
const QuicConnectionCloseFrame& frame,
QuicDataWriter* writer) {
uint32 error_code = static_cast<uint32>(frame.error_code);
@@ -2053,8 +2298,8 @@ bool QuicFramer::AppendConnectionCloseFramePayload(
return true;
}
-bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
- QuicDataWriter* writer) {
+bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer) {
uint32 error_code = static_cast<uint32>(frame.error_code);
if (!writer->WriteUInt32(error_code)) {
return false;
@@ -2069,8 +2314,29 @@ bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
return true;
}
+bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ return true;
+}
+
bool QuicFramer::RaiseError(QuicErrorCode error) {
- DVLOG(1) << detailed_error_;
+ DVLOG(1) << "Error detail: " << detailed_error_;
set_error(error);
visitor_->OnError(this);
reader_.reset(NULL);
diff --git a/chromium/net/quic/quic_framer.h b/chromium/net/quic/quic_framer.h
index 132375200be..25518109a41 100644
--- a/chromium/net/quic/quic_framer.h
+++ b/chromium/net/quic/quic_framer.h
@@ -46,7 +46,15 @@ const size_t kQuicEntropyHashSize = 1;
// sequence number in ack frames.
const size_t kQuicDeltaTimeLargestObservedSize = 2;
// Size in bytes reserved for the number of missing packets in ack frames.
-const size_t kNumberOfMissingPacketsSize = 1;
+const size_t kNumberOfNackRangesSize = 1;
+// Maximum number of missing packet ranges that can fit within an ack frame.
+const size_t kMaxNackRanges =
+ (1 << (kNumberOfNackRangesSize * 8)) - 1;
+// Size in bytes reserved for the number of revived packets in ack frames.
+const size_t kNumberOfRevivedPacketsSize = 1;
+// Maximum number of revived packets that can fit within an ack frame.
+const size_t kMaxRevivedPackets =
+ (1 << (kNumberOfRevivedPacketsSize * 8)) - 1;
// This class receives callbacks from the framer when packets
// are processed.
@@ -82,11 +90,20 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
// before it has been processed.
virtual void OnRevivedPacket() = 0;
+ // Called when the public header has been parsed, but has not been
+ // authenticated. If it returns false, framing for this packet will cease.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) = 0;
+
// Called when the unauthenticated portion of the header has been parsed.
// If OnUnauthenticatedHeader returns false, framing for this packet will
// cease.
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0;
+ // Called when a packet has been decrypted. |level| is the encryption level
+ // of the packet.
+ virtual void OnDecryptedPacket(EncryptionLevel level) = 0;
+
// Called when the complete header of a packet had been parsed.
// If OnPacketHeader returns false, framing for this packet will cease.
virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0;
@@ -106,6 +123,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) = 0;
+ // Called when a StopWaitingFrame has been parsed.
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0;
+
+ // Called when a PingFrame has been parsed.
+ virtual bool OnPingFrame(const QuicPingFrame& frame) = 0;
+
// Called when a RstStreamFrame has been parsed.
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
@@ -116,6 +139,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
// Called when a GoAwayFrame has been parsed.
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0;
+ // Called when a WindowUpdateFrame has been parsed.
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
+
+ // Called when a BlockedFrame has been parsed.
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0;
+
// Called when FEC data has been parsed.
virtual void OnFecData(const QuicFecData& fec) = 0;
@@ -167,16 +196,6 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Returns true if |version| is a supported protocol version.
bool IsSupportedVersion(const QuicVersion version) const;
- // Returns true if the version flag is set in the public flags.
- static bool HasVersionFlag(const QuicEncryptedPacket& packet);
-
- // Calculates the largest observed packet to advertise in the case an Ack
- // Frame was truncated. last_written in this case is the iterator for the
- // last missing packet which fit in the outgoing ack.
- static QuicPacketSequenceNumber CalculateLargestObserved(
- const SequenceNumberSet& missing_packets,
- SequenceNumberSet::const_iterator last_written);
-
// Set callbacks to be called from the framer. A visitor must be set, or
// else the framer will likely crash. It is acceptable for the visitor
// to do nothing. If this is called multiple times, only the last visitor
@@ -239,22 +258,27 @@ class NET_EXPORT_PRIVATE QuicFramer {
static size_t GetMinStreamFrameSize(QuicVersion version,
QuicStreamId stream_id,
QuicStreamOffset offset,
- bool last_frame_in_packet);
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group);
// Size in bytes of all ack frame fields without the missing packets.
static size_t GetMinAckFrameSize(
QuicVersion version,
QuicSequenceNumberLength sequence_number_length,
QuicSequenceNumberLength largest_observed_length);
+ // Size in bytes of a stop waiting frame.
+ static size_t GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length);
// Size in bytes of all reset stream frame without the error details.
- static size_t GetMinRstStreamFrameSize();
+ static size_t GetMinRstStreamFrameSize(QuicVersion quic_version);
// Size in bytes of all connection close frame fields without the error
// details and the missing packets from the enclosed ack frame.
static size_t GetMinConnectionCloseFrameSize();
// Size in bytes of all GoAway frame fields without the reason phrase.
static size_t GetMinGoAwayFrameSize();
- // The maximum number of nacks which can be transmitted in a single ack packet
- // without exceeding kDefaultMaxPacketSize.
- static size_t GetMaxUnackedPackets(QuicPacketHeader header);
+ // Size in bytes of all WindowUpdate frame fields.
+ static size_t GetWindowUpdateFrameSize();
+ // Size in bytes of all Blocked frame fields.
+ static size_t GetBlockedFrameSize();
// Size in bytes required to serialize the stream id.
static size_t GetStreamIdSize(QuicStreamId stream_id);
// Size in bytes required to serialize the stream offset.
@@ -262,36 +286,26 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Size in bytes required for a serialized version negotiation packet
static size_t GetVersionNegotiationPacketSize(size_t number_versions);
-
- static bool CanTruncate(
- QuicVersion version, const QuicFrame& frame, size_t free_bytes);
-
// Returns the number of bytes added to the packet for the specified frame,
// and 0 if the frame doesn't fit. Includes the header size for the first
// frame.
size_t GetSerializedFrameLength(
const QuicFrame& frame,
size_t free_bytes,
- bool first_frame,
- bool last_frame,
+ bool first_frame_in_packet,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length);
// Returns the associated data from the encrypted packet |encrypted| as a
// stringpiece.
static base::StringPiece GetAssociatedDataFromEncryptedPacket(
const QuicEncryptedPacket& encrypted,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length);
// Returns a SerializedPacket whose |packet| member is owned by the caller,
- // and is populated with the fields in |header| and |frames|, or is NULL if
- // the packet could not be created.
- // TODO(ianswett): Used for testing only.
- SerializedPacket BuildUnsizedDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames);
-
- // Returns a SerializedPacket whose |packet| member is owned by the caller,
// is created from the first |num_frames| frames, or is NULL if the packet
// could not be created. The packet must be of size |packet_size|.
SerializedPacket BuildDataPacket(const QuicPacketHeader& header,
@@ -315,16 +329,18 @@ class NET_EXPORT_PRIVATE QuicFramer {
// SetDecrypter sets the primary decrypter, replacing any that already exists,
// and takes ownership. If an alternative decrypter is in place then the
// function DCHECKs. This is intended for cases where one knows that future
- // packets will be using the new decrypter and the previous decrypter is not
- // obsolete.
- void SetDecrypter(QuicDecrypter* decrypter);
+ // packets will be using the new decrypter and the previous decrypter is now
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
// SetAlternativeDecrypter sets a decrypter that may be used to decrypt
- // future packets and takes ownership of it. If |latch_once_used| is true,
- // then the first time that the decrypter is successful it will replace the
- // primary decrypter. Otherwise both decrypters will remain active and the
- // primary decrypter will be the one last used.
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used);
const QuicDecrypter* decrypter() const;
@@ -335,10 +351,6 @@ class NET_EXPORT_PRIVATE QuicFramer {
void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
const QuicEncrypter* encrypter(EncryptionLevel level) const;
- // SwapCryptersForTest exchanges the state of the crypters with |other|. To
- // be used in tests only.
- void SwapCryptersForTest(QuicFramer* other);
-
// Returns a new encrypted packet, owned by the caller.
QuicEncryptedPacket* EncryptPacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
@@ -350,17 +362,19 @@ class NET_EXPORT_PRIVATE QuicFramer {
const std::string& detailed_error() { return detailed_error_; }
- // Read the full 8 byte guid from a packet header.
- // Return true on success, else false.
- static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet,
- QuicGuid* guid);
-
- static QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags);
-
// The minimum sequence number length required to represent |sequence_number|.
static QuicSequenceNumberLength GetMinSequenceNumberLength(
QuicPacketSequenceNumber sequence_number);
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ quic_version_ = versions[0];
+ }
+
+ void set_validate_flags(bool value) { validate_flags_ = value; }
+
+ bool is_server() const { return is_server_; }
+
private:
friend class test::QuicFramerPeer;
@@ -400,13 +414,15 @@ class NET_EXPORT_PRIVATE QuicFramer {
uint8 frame_type,
QuicAckFrame* frame);
bool ProcessReceivedInfo(uint8 frame_type, ReceivedPacketInfo* received_info);
- bool ProcessSentInfo(const QuicPacketHeader& public_header,
- SentPacketInfo* sent_info);
+ bool ProcessStopWaitingFrame(const QuicPacketHeader& public_header,
+ QuicStopWaitingFrame* stop_waiting);
bool ProcessQuicCongestionFeedbackFrame(
QuicCongestionFeedbackFrame* congestion_feedback);
bool ProcessRstStreamFrame(QuicRstStreamFrame* frame);
bool ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame);
bool ProcessGoAwayFrame(QuicGoAwayFrame* frame);
+ bool ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame);
+ bool ProcessBlockedFrame(QuicBlockedFrame* frame);
bool DecryptPayload(const QuicPacketHeader& header,
const QuicEncryptedPacket& packet);
@@ -424,6 +440,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Computes the wire size in bytes of the payload of |frame|.
size_t ComputeFrameLength(const QuicFrame& frame,
bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length);
static bool AppendPacketSequenceNumber(
@@ -436,27 +453,34 @@ class NET_EXPORT_PRIVATE QuicFramer {
static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
+ // The Append* methods attempt to write the provided header or frame using the
+ // |writer|, and return true if successful.
bool AppendPacketHeader(const QuicPacketHeader& header,
QuicDataWriter* writer);
bool AppendTypeByte(const QuicFrame& frame,
bool last_frame_in_packet,
QuicDataWriter* writer);
- bool AppendStreamFramePayload(const QuicStreamFrame& frame,
- bool last_frame_in_packet,
- QuicDataWriter* builder);
- bool AppendAckFramePayloadAndTypeByte(const QuicPacketHeader& header,
- const QuicAckFrame& frame,
- QuicDataWriter* builder);
- bool AppendQuicCongestionFeedbackFramePayload(
- const QuicCongestionFeedbackFrame& frame,
- QuicDataWriter* builder);
- bool AppendRstStreamFramePayload(const QuicRstStreamFrame& frame,
- QuicDataWriter* builder);
- bool AppendConnectionCloseFramePayload(
- const QuicConnectionCloseFrame& frame,
- QuicDataWriter* builder);
- bool AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
- QuicDataWriter* writer);
+ bool AppendStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* builder);
+ bool AppendAckFrameAndTypeByte(const QuicPacketHeader& header,
+ const QuicAckFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendCongestionFeedbackFrame(const QuicCongestionFeedbackFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendStopWaitingFrame(const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendRstStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer);
+ bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+
bool RaiseError(QuicErrorCode error);
void set_error(QuicErrorCode error) {
@@ -476,7 +500,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Updated by ProcessPacketHeader when it succeeds.
QuicPacketSequenceNumber last_sequence_number_;
// Updated by WritePacketHeader.
- QuicGuid last_serialized_guid_;
+ QuicConnectionId last_serialized_connection_id_;
// Buffer containing decrypted payload data during parsing.
scoped_ptr<QuicData> decrypted_;
// Version of the protocol being used.
@@ -490,7 +514,11 @@ class NET_EXPORT_PRIVATE QuicFramer {
scoped_ptr<QuicDecrypter> decrypter_;
// Alternative decrypter that can also be used to decrypt packets.
scoped_ptr<QuicDecrypter> alternative_decrypter_;
- // alternative_decrypter_latch_is true if, when |alternative_decrypter_|
+ // The encryption level of |decrypter_|.
+ EncryptionLevel decrypter_level_;
+ // The encryption level of |alternative_decrypter_|.
+ EncryptionLevel alternative_decrypter_level_;
+ // |alternative_decrypter_latch_| is true if, when |alternative_decrypter_|
// successfully decrypts a packet, we should install it as the only
// decrypter.
bool alternative_decrypter_latch_;
@@ -499,6 +527,8 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Tracks if the framer is being used by the entity that received the
// connection or the entity that initiated it.
bool is_server_;
+ // If false, skip validation that the public flags are set to legal values.
+ bool validate_flags_;
// The time this frames was created. Time written to the wire will be
// written as a delta from this value.
QuicTime creation_time_;
diff --git a/chromium/net/quic/quic_framer_test.cc b/chromium/net/quic/quic_framer_test.cc
index eef4a5894c1..fd206e18620 100644
--- a/chromium/net/quic/quic_framer_test.cc
+++ b/chromium/net/quic/quic_framer_test.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/quic/quic_framer.h"
+
#include <algorithm>
#include <map>
#include <string>
@@ -14,11 +16,11 @@
#include "base/stl_util.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
-#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
using base::hash_set;
using base::StringPiece;
@@ -37,46 +39,54 @@ namespace test {
const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48;
const QuicPacketSequenceNumber kMask = kEpoch - 1;
-// Index into the guid offset in the header.
-const size_t kGuidOffset = kPublicFlagsSize;
+// Index into the connection_id offset in the header.
+const size_t kConnectionIdOffset = kPublicFlagsSize;
// Index into the version string in the header. (if present).
-const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID;
+const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
+
+// Size in bytes of the stream frame fields for an arbitrary StreamID and
+// offset and the last frame in a packet.
+size_t GetMinStreamFrameSize(QuicVersion version) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
// Index into the sequence number offset in the header.
-size_t GetSequenceNumberOffset(QuicGuidLength guid_length,
+size_t GetSequenceNumberOffset(QuicConnectionIdLength connection_id_length,
bool include_version) {
- return kGuidOffset + guid_length +
+ return kConnectionIdOffset + connection_id_length +
(include_version ? kQuicVersionSize : 0);
}
size_t GetSequenceNumberOffset(bool include_version) {
- return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version);
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
}
// Index into the private flags offset in the data packet header.
-size_t GetPrivateFlagsOffset(QuicGuidLength guid_length, bool include_version) {
- return GetSequenceNumberOffset(guid_length, include_version) +
+size_t GetPrivateFlagsOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetSequenceNumberOffset(connection_id_length, include_version) +
PACKET_6BYTE_SEQUENCE_NUMBER;
}
size_t GetPrivateFlagsOffset(bool include_version) {
- return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version);
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
}
size_t GetPrivateFlagsOffset(bool include_version,
QuicSequenceNumberLength sequence_number_length) {
- return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version) +
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
sequence_number_length;
}
// Index into the fec group offset in the header.
-size_t GetFecGroupOffset(QuicGuidLength guid_length, bool include_version) {
- return GetPrivateFlagsOffset(guid_length, include_version) +
+size_t GetFecGroupOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetPrivateFlagsOffset(connection_id_length, include_version) +
kPrivateFlagsSize;
}
size_t GetFecGroupOffset(bool include_version) {
- return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version) +
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
kPrivateFlagsSize;
}
@@ -86,14 +96,10 @@ size_t GetFecGroupOffset(bool include_version,
kPrivateFlagsSize;
}
-// Index into the nonce proof of the public reset packet.
-// Public resets always have full guids.
-const size_t kPublicResetPacketNonceProofOffset =
- kGuidOffset + PACKET_8BYTE_GUID;
-
-// Index into the rejected sequence number of the public reset packet.
-const size_t kPublicResetPacketRejectedSequenceNumberOffset =
- kPublicResetPacketNonceProofOffset + kPublicResetNonceSize;
+// Index into the message tag of the public reset packet.
+// Public resets always have full connection_ids.
+const size_t kPublicResetPacketMessageTagOffset =
+ kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
class TestEncrypter : public QuicEncrypter {
public:
@@ -188,20 +194,23 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
fec_count_(0),
complete_packets_(0),
revived_packets_(0),
- accept_packet_(true) {
+ accept_packet_(true),
+ accept_public_header_(true) {
}
virtual ~TestQuicVisitor() {
STLDeleteElements(&stream_frames_);
STLDeleteElements(&ack_frames_);
STLDeleteElements(&congestion_feedback_frames_);
+ STLDeleteElements(&stop_waiting_frames_);
+ STLDeleteElements(&ping_frames_);
STLDeleteElements(&fec_data_);
}
virtual void OnError(QuicFramer* f) OVERRIDE {
- DLOG(INFO) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error())
- << " (" << f->error() << ")";
- error_count_++;
+ DVLOG(1) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error())
+ << " (" << f->error() << ")";
+ ++error_count_;
}
virtual void OnPacket() OVERRIDE {}
@@ -217,19 +226,19 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
}
virtual void OnRevivedPacket() OVERRIDE {
- revived_packets_++;
+ ++revived_packets_;
}
virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE {
- DLOG(INFO) << "QuicFramer Version Mismatch, version: " << version;
- version_mismatch_++;
+ DVLOG(1) << "QuicFramer Version Mismatch, version: " << version;
+ ++version_mismatch_;
return true;
}
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
- packet_count_++;
- header_.reset(new QuicPacketHeader(header));
- return accept_packet_;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ public_header_.reset(new QuicPacketPublicHeader(header));
+ return accept_public_header_;
}
virtual bool OnUnauthenticatedHeader(
@@ -237,8 +246,16 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
return true;
}
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ ++packet_count_;
+ header_.reset(new QuicPacketHeader(header));
+ return accept_packet_;
+ }
+
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
stream_frames_.push_back(new QuicStreamFrame(frame));
return true;
}
@@ -248,26 +265,38 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
}
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
ack_frames_.push_back(new QuicAckFrame(frame));
return true;
}
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
congestion_feedback_frames_.push_back(
new QuicCongestionFeedbackFrame(frame));
return true;
}
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ stop_waiting_frames_.push_back(new QuicStopWaitingFrame(frame));
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ ping_frames_.push_back(new QuicPingFrame(frame));
+ return true;
+ }
+
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
- fec_count_++;
+ ++fec_count_;
fec_data_.push_back(new QuicFecData(fec));
}
virtual void OnPacketComplete() OVERRIDE {
- complete_packets_++;
+ ++complete_packets_;
}
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE {
@@ -286,6 +315,17 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame)
+ OVERRIDE {
+ window_update_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frame_ = frame;
+ return true;
+ }
+
// Counters from the visitor_ callbacks.
int error_count_;
int version_mismatch_;
@@ -295,18 +335,24 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
int complete_packets_;
int revived_packets_;
bool accept_packet_;
+ bool accept_public_header_;
scoped_ptr<QuicPacketHeader> header_;
+ scoped_ptr<QuicPacketPublicHeader> public_header_;
scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
vector<QuicStreamFrame*> stream_frames_;
vector<QuicAckFrame*> ack_frames_;
vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_;
+ vector<QuicStopWaitingFrame*> stop_waiting_frames_;
+ vector<QuicPingFrame*> ping_frames_;
vector<QuicFecData*> fec_data_;
string fec_protected_payload_;
QuicRstStreamFrame rst_stream_frame_;
QuicConnectionCloseFrame connection_close_frame_;
QuicGoAwayFrame goaway_frame_;
+ QuicWindowUpdateFrame window_update_frame_;
+ QuicBlockedFrame blocked_frame_;
};
class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
@@ -318,7 +364,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
framer_(QuicSupportedVersions(), start_, true) {
version_ = GetParam();
framer_.set_version(version_);
- framer_.SetDecrypter(decrypter_);
+ framer_.SetDecrypter(decrypter_, ENCRYPTION_NONE);
framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_);
framer_.set_visitor(&visitor_);
framer_.set_received_entropy_calculator(&entropy_calculator_);
@@ -369,18 +415,18 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
return false;
}
if (QuicFramer::GetAssociatedDataFromEncryptedPacket(
- encrypted, PACKET_8BYTE_GUID,
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) !=
decrypter_->associated_data_) {
LOG(ERROR) << "Decrypted incorrect associated data. expected "
<< QuicFramer::GetAssociatedDataFromEncryptedPacket(
- encrypted, PACKET_8BYTE_GUID,
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
includes_version, PACKET_6BYTE_SEQUENCE_NUMBER)
<< " actual: " << decrypter_->associated_data_;
return false;
}
StringPiece ciphertext(encrypted.AsStringPiece().substr(
- GetStartOfEncryptedData(PACKET_8BYTE_GUID, includes_version,
+ GetStartOfEncryptedData(PACKET_8BYTE_CONNECTION_ID, includes_version,
PACKET_6BYTE_SEQUENCE_NUMBER)));
if (ciphertext != decrypter_->ciphertext_) {
LOG(ERROR) << "Decrypted incorrect ciphertext data. expected "
@@ -428,7 +474,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
PACKET_6BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_STREAM_DATA);
@@ -448,6 +494,11 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
<< " wire_sequence_number: " << wire_sequence_number;
}
+ QuicPacket* BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ return BuildUnsizedDataPacket(&framer_, header, frames).packet;
+ }
+
test::TestEncrypter* encrypter_;
test::TestDecrypter* decrypter_;
QuicVersion version_;
@@ -572,9 +623,9 @@ TEST_P(QuicFramerTest, EmptyPacket) {
TEST_P(QuicFramerTest, LargePacket) {
unsigned char packet[kMaxPacketSize + 1] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -585,10 +636,10 @@ TEST_P(QuicFramerTest, LargePacket) {
};
memset(packet + GetPacketHeaderSize(
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), 0,
kMaxPacketSize - GetPacketHeaderSize(
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) + 1);
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -597,16 +648,16 @@ TEST_P(QuicFramerTest, LargePacket) {
ASSERT_TRUE(visitor_.header_.get());
// Make sure we've parsed the packet header, so we can send an error.
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
// Make sure the correct error is propagated.
EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error());
}
TEST_P(QuicFramerTest, PacketHeader) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -621,7 +672,7 @@ TEST_P(QuicFramerTest, PacketHeader) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -634,14 +685,14 @@ TEST_P(QuicFramerTest, PacketHeader) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
} else if (i < GetFecGroupOffset(!kIncludeVersion)) {
@@ -653,14 +704,14 @@ TEST_P(QuicFramerTest, PacketHeader) {
}
}
-TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (4 byte guid)
+ // public flags (4 byte connection_id)
0x38,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
@@ -674,7 +725,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -687,19 +738,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_4BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_GUID,
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_CONNECTION_ID,
!kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_4BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_4BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -708,14 +760,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
}
}
-TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (1 byte guid)
+ // public flags (1 byte connection_id)
0x34,
- // guid
+ // connection_id
0x10,
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
@@ -729,7 +781,7 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -742,18 +794,20 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_1BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_1BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -762,14 +816,14 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
}
}
-TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (0 byte guid)
+ // public flags (0 byte connection_id)
0x30,
- // guid
+ // connection_id
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -782,7 +836,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -795,18 +849,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_0BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -819,7 +875,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
unsigned char packet[] = {
// public flags (version)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -836,7 +892,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_TRUE(visitor_.header_->public_header.version_flag);
EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
@@ -850,14 +906,14 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < kVersionOffset) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetSequenceNumberOffset(kIncludeVersion)) {
expected_error = "Unable to read protocol version.";
} else if (i < GetPrivateFlagsOffset(kIncludeVersion)) {
@@ -876,9 +932,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 4 byte sequence number)
+ // public flags (8 byte connection_id and 4 byte sequence number)
0x2C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -892,7 +948,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -905,14 +961,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -931,9 +987,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 2 byte sequence number)
+ // public flags (8 byte connection_id and 2 byte sequence number)
0x1C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -947,7 +1003,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -960,14 +1016,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -986,9 +1042,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 1 byte sequence number)
+ // public flags (8 byte connection_id and 1 byte sequence number)
0x0C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1002,7 +1058,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -1015,14 +1071,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -1038,9 +1094,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
TEST_P(QuicFramerTest, InvalidPublicFlag) {
unsigned char packet[] = {
- // public flags, unknown flag at bit 6
- 0x40,
- // guid
+ // public flags: all flags set but the public reset flag and version flag.
+ 0xFC,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1057,13 +1113,18 @@ TEST_P(QuicFramerTest, InvalidPublicFlag) {
arraysize(packet),
"Illegal public flags value.",
QUIC_INVALID_PACKET_HEADER);
+
+ // Now turn off validation.
+ framer_.set_validate_flags(false);
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
};
TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) {
unsigned char packet[] = {
- // public flags (8 byte guid and version flag and an unknown flag)
+ // public flags (8 byte connection_id and version flag and an unknown flag)
0x4D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1086,9 +1147,9 @@ TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) {
TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
unsigned char packet[] = {
- // public flags (8 byte guid, version flag and an unknown flag)
+ // public flags (8 byte connection_id, version flag and an unknown flag)
0x7D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1113,9 +1174,9 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
TEST_P(QuicFramerTest, InvalidPrivateFlag) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1136,9 +1197,9 @@ TEST_P(QuicFramerTest, InvalidPrivateFlag) {
TEST_P(QuicFramerTest, InvalidFECGroupOffset) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1158,9 +1219,9 @@ TEST_P(QuicFramerTest, InvalidFECGroupOffset) {
TEST_P(QuicFramerTest, PaddingFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1198,16 +1259,16 @@ TEST_P(QuicFramerTest, PaddingFrame) {
// A packet with no frames is not acceptable.
CheckProcessingFails(
packet,
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
"Packet has no frames.", QUIC_MISSING_PAYLOAD);
}
TEST_P(QuicFramerTest, StreamFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1253,9 +1314,9 @@ TEST_P(QuicFramerTest, StreamFrame) {
TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1302,9 +1363,9 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1351,9 +1412,9 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1400,9 +1461,9 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrameWithVersion) {
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1433,8 +1494,8 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) {
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
- EXPECT_TRUE(visitor_.header_.get()->public_header.version_flag);
- EXPECT_EQ(GetParam(), visitor_.header_.get()->public_header.versions[0]);
+ EXPECT_TRUE(visitor_.header_->public_header.version_flag);
+ EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion));
ASSERT_EQ(1u, visitor_.stream_frames_.size());
@@ -1454,9 +1515,9 @@ TEST_P(QuicFramerTest, RejectPacket) {
visitor_.accept_packet_ = false;
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1491,6 +1552,25 @@ TEST_P(QuicFramerTest, RejectPacket) {
EXPECT_EQ(0u, visitor_.ack_frames_.size());
}
+TEST_P(QuicFramerTest, RejectPublicHeader) {
+ visitor_.accept_public_header_ = false;
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_header_.get());
+ ASSERT_FALSE(visitor_.header_.get());
+}
+
TEST_P(QuicFramerTest, RevivedStreamFrame) {
unsigned char payload[] = {
// frame type (stream frame with fin)
@@ -1509,7 +1589,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
};
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = true;
@@ -1526,7 +1606,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
ASSERT_EQ(1, visitor_.revived_packets_);
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_TRUE(visitor_.header_->fec_flag);
@@ -1549,9 +1629,9 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1586,8 +1666,10 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
EXPECT_EQ(GG_UINT64_C(0x341256789ABA),
visitor_.header_->fec_group);
- const size_t fec_offset = GetStartOfFecProtectedData(
- PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER);
+ const size_t fec_offset =
+ GetStartOfFecProtectedData(PACKET_8BYTE_CONNECTION_ID,
+ !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER);
EXPECT_EQ(
string(AsChars(packet) + fec_offset, arraysize(packet) - fec_offset),
visitor_.fec_protected_payload_);
@@ -1601,11 +1683,15 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
}
-TEST_P(QuicFramerTest, AckFrame) {
+TEST_P(QuicFramerTest, AckFrame15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
+
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1635,6 +1721,8 @@ TEST_P(QuicFramerTest, AckFrame) {
0x01,
// 0 more missing packets in range.
0x00,
+ // Number of revived packets.
+ 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -1667,11 +1755,13 @@ TEST_P(QuicFramerTest, AckFrame) {
const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
kQuicDeltaTimeLargestObservedSize;
const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
- kNumberOfMissingPacketsSize;
+ kNumberOfNackRangesSize;
const size_t kMissingPacketsRange = kMissingPacketsOffset +
PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
// Now test framing boundaries
- const size_t ack_frame_size = kMissingPacketsRange +
+ const size_t ack_frame_size = kRevivedPacketsLength +
PACKET_1BYTE_SEQUENCE_NUMBER;
for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
string expected_error;
@@ -1689,22 +1779,396 @@ TEST_P(QuicFramerTest, AckFrame) {
expected_error = "Unable to read num missing packet ranges.";
} else if (i < kMissingPacketsRange) {
expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrame) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
} else {
+ expected_error = "Unable to read revived packet.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xAB, frame.sent_info.entropy_hash);
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
+
+ const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+ const size_t kReceivedEntropyOffset = kLeastUnackedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLeastUnackedOffset) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
+ } else {
+ expected_error = "Unable to read revived packet.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_ACK_DATA);
}
}
TEST_P(QuicFramerTest, AckFrameNoNacks) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (no nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x4C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
+ frame->received_info.largest_observed);
+ ASSERT_EQ(0u, frame->received_info.missing_packets.size());
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrameNoNacks15) {
+ if (framer_.version() > QUIC_VERSION_15) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1750,8 +2214,7 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) {
// Verify that the packet re-serializes identically.
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1760,10 +2223,87 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) {
}
TEST_P(QuicFramerTest, AckFrame500Nacks) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x02,
+ // missing packet delta
+ 0x01,
+ // 243 more missing packets in range.
+ // The ranges are listed in this order so the re-constructed packet matches.
+ 0xF3,
+ // No gap between ranges
+ 0x00,
+ // 255 more missing packets in range.
+ 0xFF,
+ // No revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
+ frame->received_info.largest_observed);
+ EXPECT_EQ(0u, frame->received_info.revived_packets.size());
+ ASSERT_EQ(500u, frame->received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator first_missing_iter =
+ frame->received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ frame->received_info.missing_packets.rbegin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter);
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrame500Nacks15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1798,6 +2338,8 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
0x00,
// 255 more missing packets in range.
0xFF,
+ // No revived packets.
+ 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -1814,6 +2356,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
frame->received_info.largest_observed);
+ EXPECT_EQ(0u, frame->received_info.revived_packets.size());
ASSERT_EQ(500u, frame->received_info.missing_packets.size());
SequenceNumberSet::const_iterator first_missing_iter =
frame->received_info.missing_packets.begin();
@@ -1826,8 +2369,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
// Verify that the packet re-serializes identically.
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1837,9 +2379,9 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1852,8 +2394,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
0x20,
// congestion feedback type (tcp)
0x00,
- // ack_frame.feedback.tcp.accumulated_number_of_lost_packets
- 0x01, 0x02,
// ack_frame.feedback.tcp.receive_window
0x03, 0x04,
};
@@ -1870,23 +2410,19 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
const QuicCongestionFeedbackFrame& frame =
*visitor_.congestion_feedback_frames_[0];
ASSERT_EQ(kTCP, frame.type);
- EXPECT_EQ(0x0201,
- frame.tcp.accumulated_number_of_lost_packets);
EXPECT_EQ(0x4030u, frame.tcp.receive_window);
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 6; ++i) {
+ for (size_t i = kQuicFrameTypeSize; i < 4; ++i) {
string expected_error;
if (i < 2) {
expected_error = "Unable to read congestion feedback type.";
} else if (i < 4) {
- expected_error = "Unable to read accumulated number of lost packets.";
- } else if (i < 6) {
expected_error = "Unable to read receive window.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -1894,9 +2430,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1909,8 +2445,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
0x20,
// congestion feedback type (inter arrival)
0x01,
- // accumulated_number_of_lost_packets
- 0x02, 0x03,
// num received packets
0x03,
// lowest sequence number
@@ -1941,8 +2475,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
const QuicCongestionFeedbackFrame& frame =
*visitor_.congestion_feedback_frames_[0];
ASSERT_EQ(kInterArrival, frame.type);
- EXPECT_EQ(0x0302, frame.inter_arrival.
- accumulated_number_of_lost_packets);
ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size());
TimeMap::const_iterator iter =
frame.inter_arrival.received_packet_times.begin();
@@ -1959,30 +2491,28 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
iter->second.Subtract(start_).ToMicroseconds());
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 31; ++i) {
+ for (size_t i = kQuicFrameTypeSize; i < 29; ++i) {
string expected_error;
if (i < 2) {
expected_error = "Unable to read congestion feedback type.";
- } else if (i < 4) {
- expected_error = "Unable to read accumulated number of lost packets.";
- } else if (i < 5) {
+ } else if (i < 3) {
expected_error = "Unable to read num received packets.";
- } else if (i < 11) {
+ } else if (i < 9) {
expected_error = "Unable to read smallest received.";
- } else if (i < 19) {
+ } else if (i < 17) {
expected_error = "Unable to read time received.";
- } else if (i < 21) {
+ } else if (i < 19) {
expected_error = "Unable to read sequence delta in received packets.";
- } else if (i < 25) {
+ } else if (i < 23) {
expected_error = "Unable to read time delta in received packets.";
- } else if (i < 27) {
+ } else if (i < 25) {
expected_error = "Unable to read sequence delta in received packets.";
- } else if (i < 31) {
+ } else if (i < 29) {
expected_error = "Unable to read time delta in received packets.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -1990,9 +2520,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2034,7 +2564,7 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -2042,9 +2572,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2065,11 +2595,68 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
EXPECT_EQ(QUIC_INVALID_CONGESTION_FEEDBACK_DATA, framer_.error());
}
-TEST_P(QuicFramerTest, RstStreamFrame) {
+TEST_P(QuicFramerTest, StopWaitingFrame) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size());
+ const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0];
+ EXPECT_EQ(0xAB, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.least_unacked);
+
+ const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+ const size_t frame_size = 7;
+ for (size_t i = kQuicFrameTypeSize; i < frame_size; ++i) {
+ string expected_error;
+ if (i < kLeastUnackedOffset) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else {
+ expected_error = "Unable to read least unacked delta.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_STOP_WAITING_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, RstStreamFrameQuic) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2082,6 +2669,11 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
0x01,
// stream id
0x04, 0x03, 0x02, 0x01,
+
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
+
// error code
0x01, 0x00, 0x00, 0x00,
@@ -2104,21 +2696,27 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id);
EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code);
EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
+ EXPECT_EQ(GG_UINT64_C(0x0807060504030201),
+ visitor_.rst_stream_frame_.byte_offset);
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 24; ++i) {
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetMinRstStreamFrameSize(version_); ++i) {
string expected_error;
if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
expected_error = "Unable to read stream_id.";
} else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
- kQuicErrorCodeSize) {
+ + kQuicMaxStreamOffsetSize) {
+ expected_error = "Unable to read rst stream sent byte offset.";
+ } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) {
expected_error = "Unable to read rst stream error code.";
} else {
expected_error = "Unable to read rst stream error details.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_RST_STREAM_DATA);
}
@@ -2126,9 +2724,9 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
TEST_P(QuicFramerTest, ConnectionCloseFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2176,7 +2774,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA);
}
@@ -2184,9 +2782,9 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) {
TEST_P(QuicFramerTest, GoAwayFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2237,25 +2835,164 @@ TEST_P(QuicFramerTest, GoAwayFrame) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_GOAWAY_DATA);
}
}
+TEST_P(QuicFramerTest, WindowUpdateFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(GG_UINT64_C(0x0c0b0a0908070605),
+ visitor_.window_update_frame_.byte_offset);
+
+ // Now test framing boundaries
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetWindowUpdateFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+ expected_error = "Unable to read stream_id.";
+ } else {
+ expected_error = "Unable to read window byte_offset.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, BlockedFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.blocked_frame_.stream_id);
+
+ // Now test framing boundaries
+ for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize();
+ ++i) {
+ string expected_error = "Unable to read stream_id.";
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_BLOCKED_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, PingFrame) {
+ if (version_ <= QUIC_VERSION_17) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(1u, visitor_.ping_frames_.size());
+
+ // No need to check the PING frame boundaries because it has no payload.
+}
+
TEST_P(QuicFramerTest, PublicResetPacket) {
unsigned char packet[] = {
- // public flags (public reset, 8 byte guid)
- 0x3E,
- // guid
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
// nonce proof
0x89, 0x67, 0x45, 0x23,
0x01, 0xEF, 0xCD, 0xAB,
// rejected sequence number
0xBC, 0x9A, 0x78, 0x56,
- 0x34, 0x12,
+ 0x34, 0x12, 0x00, 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -2263,32 +3000,136 @@ TEST_P(QuicFramerTest, PublicResetPacket) {
ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.public_reset_packet_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.public_reset_packet_->public_header.guid);
+ visitor_.public_reset_packet_->public_header.connection_id);
EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
visitor_.public_reset_packet_->nonce_proof);
EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_TRUE(
+ visitor_.public_reset_packet_->client_address.address().empty());
// Now test framing boundaries
- for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) {
+ for (size_t i = 0; i < arraysize(packet); ++i) {
string expected_error;
- DLOG(INFO) << "iteration: " << i;
- if (i < kGuidOffset) {
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PACKET_HEADER);
- } else if (i < kPublicResetPacketNonceProofOffset) {
- expected_error = "Unable to read GUID.";
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PACKET_HEADER);
- } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) {
- expected_error = "Unable to read nonce proof.";
+ } else {
+ expected_error = "Unable to read reset message.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // trailing junk
+ 'j', 'u', 'n', 'k',
+ };
+
+ string expected_error = "Unable to read reset message.";
+ CheckProcessingFails(packet, arraysize(packet), expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address: 4.31.198.44:443
+ 0x02, 0x00,
+ 0x04, 0x1F, 0xC6, 0x2C,
+ 0xBB, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.public_reset_packet_->public_header.connection_id);
+ EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+ EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+ visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_EQ("4.31.198.44",
+ IPAddressToString(visitor_.public_reset_packet_->
+ client_address.address()));
+ EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port());
+
+ // Now test framing boundaries
+ for (size_t i = 0; i < arraysize(packet); ++i) {
+ string expected_error;
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
} else {
- expected_error = "Unable to read rejected sequence number.";
+ expected_error = "Unable to read reset message.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PUBLIC_RST_PACKET);
}
@@ -2297,9 +3138,9 @@ TEST_P(QuicFramerTest, PublicResetPacket) {
TEST_P(QuicFramerTest, VersionNegotiationPacket) {
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2316,13 +3157,13 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) {
EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size());
EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]);
- for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) {
+ for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID; ++i) {
string expected_error;
QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < kVersionOffset) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else {
expected_error = "Unable to read supported version in negotiation.";
error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET;
@@ -2333,9 +3174,9 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) {
TEST_P(QuicFramerTest, FecPacket) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2370,7 +3211,7 @@ TEST_P(QuicFramerTest, FecPacket) {
TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2384,9 +3225,9 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2401,12 +3242,11 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2417,7 +3257,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2432,9 +3272,9 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 4 byte sequence number)
+ // public flags (8 byte connection_id and 4 byte sequence number)
0x2C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2448,12 +3288,11 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2464,7 +3303,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2479,9 +3318,9 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 2 byte sequence number)
+ // public flags (8 byte connection_id and 2 byte sequence number)
0x1C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2495,12 +3334,11 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2511,7 +3349,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2526,9 +3364,9 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 1 byte sequence number)
+ // public flags (8 byte connection_id and 1 byte sequence number)
0x0C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2542,12 +3380,11 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2558,7 +3395,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, BuildStreamFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2576,9 +3413,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
frames.push_back(QuicFrame(&stream_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2600,8 +3437,62 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
'r', 'l', 'd', '!',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
+ header.is_in_fec_group = IN_FEC_GROUP;
+ header.fec_group = GG_UINT64_C(0x77123456789ABC);
+
+ QuicStreamFrame stream_frame;
+ stream_frame.stream_id = 0x01020304;
+ stream_frame.fin = true;
+ stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
+ stream_frame.data = MakeIOVector("hello world!");
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stream_frame));
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy, is_in_fec_group)
+ 0x03,
+ // FEC group
+ 0x00,
+ // frame type (stream frame with fin and data length field)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length (since packet is in an FEC group)
+ 0x0C, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2611,7 +3502,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.fec_flag = false;
@@ -2629,9 +3520,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
frames.push_back(QuicFrame(&stream_frame));
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2656,8 +3547,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
};
QuicFramerPeer::SetIsServer(&framer_, false);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2667,14 +3557,14 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
QuicPacketPublicHeader header;
- header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.reset_flag = false;
header.version_flag = true;
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
- 0x3D,
- // guid
+ // public flags (version, 8 byte connection_id)
+ 0x0D,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2692,8 +3582,74 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
}
TEST_P(QuicFramerTest, BuildAckFramePacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ ack_frame.received_info.missing_packets.insert(
+ GG_UINT64_C(0x770123456789ABE));
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacket15) {
+ if (version_ != QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2714,9 +3670,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
frames.push_back(QuicFrame(&ack_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2746,10 +3702,128 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
0x01,
// 0 more missing packets in range.
0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+// TODO(jri): Add test for tuncated packets in which the original ack frame had
+// revived packets. (In both the large and small packet cases below).
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = 2 * 300;
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.received_info.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 510 (0x1FE), since only 255 nack ranges can fit.
+ 0xFE, 0x01,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 255 by size of this field).
+ 0xFF,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges x 42 + 3 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ // 0 revived packets.
+ 0x00,
};
scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2757,9 +3831,79 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
AsChars(packet), arraysize(packet));
}
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = 2 * 300;
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.received_info.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 12 (0x0C), since only 6 nack ranges can fit.
+ 0x0C, 0x00,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 6 by packet size of 37).
+ 0x06,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, 37u).packet);
+ ASSERT_TRUE(data != NULL);
+ // Expect 1 byte unused since at least 2 bytes are needed to fit more nacks.
+ EXPECT_EQ(36u, data->length());
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2769,16 +3913,15 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
QuicCongestionFeedbackFrame congestion_feedback_frame;
congestion_feedback_frame.type = kTCP;
- congestion_feedback_frame.tcp.accumulated_number_of_lost_packets = 0x0201;
congestion_feedback_frame.tcp.receive_window = 0x4030;
QuicFrames frames;
frames.push_back(QuicFrame(&congestion_feedback_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2791,14 +3934,11 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
0x20,
// congestion feedback type (TCP)
0x00,
- // accumulated number of lost packets
- 0x01, 0x02,
// TCP receive window
0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2808,7 +3948,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2818,7 +3958,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
QuicCongestionFeedbackFrame frame;
frame.type = kInterArrival;
- frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302;
frame.inter_arrival.received_packet_times.insert(
make_pair(GG_UINT64_C(0x0123456789ABA),
start_.Add(QuicTime::Delta::FromMicroseconds(
@@ -2835,9 +3974,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
frames.push_back(QuicFrame(&frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2850,8 +3989,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
0x20,
// congestion feedback type (inter arrival)
0x01,
- // accumulated_number_of_lost_packets
- 0x02, 0x03,
// num received packets
0x03,
// lowest sequence number
@@ -2870,8 +4007,56 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
0x02, 0x00, 0x00, 0x00,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStopWaitingPacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicStopWaitingFrame stop_waiting_frame;
+ stop_waiting_frame.entropy_hash = 0x14;
+ stop_waiting_frame.least_unacked = GG_UINT64_C(0x770123456789AA0);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stop_waiting_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (stop waiting frame)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0x14,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2881,7 +4066,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2898,9 +4083,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
frames.push_back(QuicFrame(&congestion_feedback_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2917,8 +4102,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
0x01, 0x02, 0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2928,7 +4112,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2943,14 +4127,16 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
QuicFrames frames;
frames.push_back(QuicFrame(&congestion_feedback_frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data;
+ EXPECT_DFATAL(
+ data.reset(BuildDataPacket(header, frames)),
+ "AppendCongestionFeedbackFrame failed");
ASSERT_TRUE(data == NULL);
}
-TEST_P(QuicFramerTest, BuildRstFramePacket) {
+TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2962,11 +4148,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
rst_frame.stream_id = 0x01020304;
rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
rst_frame.error_details = "because I can";
+ rst_frame.byte_offset = 0x0807060504030201;
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2979,6 +4166,9 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
0x01,
// stream id
0x04, 0x03, 0x02, 0x01,
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
// error code
0x08, 0x07, 0x06, 0x05,
// error details length
@@ -2993,8 +4183,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
QuicFrames frames;
frames.push_back(QuicFrame(&rst_frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3004,7 +4193,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
TEST_P(QuicFramerTest, BuildCloseFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3020,9 +4209,9 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
frames.push_back(QuicFrame(&close_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3044,8 +4233,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
'n',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3055,7 +4243,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
TEST_P(QuicFramerTest, BuildGoAwayPacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3072,9 +4260,9 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
frames.push_back(QuicFrame(&goaway_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3098,8 +4286,7 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
'n',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3107,25 +4294,224 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
AsChars(packet), arraysize(packet));
}
+TEST_P(QuicFramerTest, BuildWindowUpdatePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id = 0x01020304;
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&window_update_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x88, 0x77, 0x66, 0x55,
+ 0x44, 0x33, 0x22, 0x11,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildBlockedPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicBlockedFrame blocked_frame;
+ blocked_frame.stream_id = 0x01020304;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&blocked_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPingPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPingFrame ping_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ping_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ if (version_ > QUIC_VERSION_17) {
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+ } else {
+ string expected_error =
+ "Attempt to add a PingFrame in " + QuicVersionToString(version_);
+ EXPECT_DFATAL(BuildDataPacket(header, frames),
+ expected_error);
+ return;
+ }
+}
+
TEST_P(QuicFramerTest, BuildPublicResetPacket) {
QuicPublicResetPacket reset_packet;
- reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
reset_packet.public_header.reset_flag = true;
reset_packet.public_header.version_flag = false;
reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
unsigned char packet[] = {
- // public flags (public reset, 8 byte GUID)
- 0x3E,
- // guid
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
// nonce proof
0x89, 0x67, 0x45, 0x23,
0x01, 0xEF, 0xCD, 0xAB,
// rejected sequence number
0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.reset_flag = true;
+ reset_packet.public_header.version_flag = false;
+ reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
+ reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
+ reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234);
+
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address
+ 0x02, 0x00,
+ 0x7F, 0x00, 0x00, 0x01,
0x34, 0x12,
};
@@ -3140,7 +4526,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacket) {
TEST_P(QuicFramerTest, BuildFecPacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = true;
@@ -3154,9 +4540,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) {
fec_data.redundancy = "abcdefghijklmnop";
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3186,9 +4572,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) {
TEST_P(QuicFramerTest, EncryptPacket) {
QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3208,7 +4594,7 @@ TEST_P(QuicFramerTest, EncryptPacket) {
scoped_ptr<QuicPacket> raw(
QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER));
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
@@ -3220,9 +4606,9 @@ TEST_P(QuicFramerTest, EncryptPacket) {
TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -3244,7 +4630,7 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
scoped_ptr<QuicPacket> raw(
QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
- PACKET_8BYTE_GUID, kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER));
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
@@ -3253,31 +4639,98 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
EXPECT_TRUE(CheckEncryption(sequence_number, raw.get()));
}
-// TODO(rch): re-enable after https://codereview.chromium.org/11820005/
-// lands. Currently this is causing valgrind problems, but it should be
-// fixed in the followup CL.
-TEST_P(QuicFramerTest, DISABLED_CalculateLargestReceived) {
- SequenceNumberSet missing;
- missing.insert(1);
- missing.insert(5);
- missing.insert(7);
+TEST_P(QuicFramerTest, AckTruncationLargePacket) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
+ ASSERT_TRUE(raw_ack_packet != NULL);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
+ EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed);
+ ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.received_info.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.received_info.missing_packets.rbegin();
+ EXPECT_EQ(509u, *last_missing_iter);
+}
+
+TEST_P(QuicFramerTest, AckTruncationSmallPacket) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
- // These two we just walk to the next gap, and return the largest seen.
- EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(1)));
- EXPECT_EQ(6u, QuicFramer::CalculateLargestObserved(missing, missing.find(5)));
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
- missing.insert(2);
- // For 1, we can't go forward as 2 would be implicitly acked so we return the
- // largest missing packet.
- EXPECT_EQ(1u, QuicFramer::CalculateLargestObserved(missing, missing.find(1)));
- // For 2, we've seen 3 and 4, so can admit to a largest observed.
- EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(2)));
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, 500).packet);
+ ASSERT_TRUE(raw_ack_packet != NULL);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
+ EXPECT_EQ(476u, processed_ack_frame.received_info.largest_observed);
+ ASSERT_EQ(238u, processed_ack_frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.received_info.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.received_info.missing_packets.rbegin();
+ EXPECT_EQ(475u, *last_missing_iter);
}
-// TODO(rch) enable after landing the revised truncation CL.
-TEST_P(QuicFramerTest, DISABLED_Truncation) {
+TEST_P(QuicFramerTest, Truncation15) {
+ if (framer_.version() > QUIC_VERSION_15) {
+ return;
+ }
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3287,31 +4740,31 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) {
QuicAckFrame ack_frame;
ack_frame.received_info.largest_observed = 601;
- ack_frame.sent_info.least_unacked = 0;
+ ack_frame.sent_info.least_unacked = header.packet_sequence_number - 1;
for (uint64 i = 1; i < ack_frame.received_info.largest_observed; i += 2) {
ack_frame.received_info.missing_packets.insert(i);
}
- // Create a packet with just the ack
+ // Create a packet with just the ack.
QuicFrame frame;
frame.type = ACK_FRAME;
frame.ack_frame = &ack_frame;
QuicFrames frames;
frames.push_back(frame);
- scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
*raw_ack_packet));
- // Now make sure we can turn our ack packet back into an ack frame
+ // Now make sure we can turn our ack packet back into an ack frame.
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
ASSERT_EQ(1u, visitor_.ack_frames_.size());
const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
- EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked);
+ EXPECT_EQ(header.packet_sequence_number - 1,
+ processed_ack_frame.sent_info.least_unacked);
EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed);
ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size());
@@ -3325,7 +4778,7 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) {
TEST_P(QuicFramerTest, CleanTruncation) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3335,27 +4788,26 @@ TEST_P(QuicFramerTest, CleanTruncation) {
QuicAckFrame ack_frame;
ack_frame.received_info.largest_observed = 201;
- ack_frame.sent_info.least_unacked = 0;
+ ack_frame.sent_info.least_unacked = header.packet_sequence_number - 2;
for (uint64 i = 1; i < ack_frame.received_info.largest_observed; ++i) {
ack_frame.received_info.missing_packets.insert(i);
}
- // Create a packet with just the ack
+ // Create a packet with just the ack.
QuicFrame frame;
frame.type = ACK_FRAME;
frame.ack_frame = &ack_frame;
QuicFrames frames;
frames.push_back(frame);
- scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
*raw_ack_packet));
- // Now make sure we can turn our ack packet back into an ack frame
+ // Now make sure we can turn our ack packet back into an ack frame.
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
// Test for clean truncation of the ack by comparing the length of the
@@ -3366,8 +4818,7 @@ TEST_P(QuicFramerTest, CleanTruncation) {
frames.push_back(frame);
size_t original_raw_length = raw_ack_packet->length();
- raw_ack_packet.reset(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ raw_ack_packet.reset(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
EXPECT_EQ(original_raw_length, raw_ack_packet->length());
ASSERT_TRUE(raw_ack_packet != NULL);
@@ -3375,9 +4826,9 @@ TEST_P(QuicFramerTest, CleanTruncation) {
TEST_P(QuicFramerTest, EntropyFlagTest) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3410,9 +4861,9 @@ TEST_P(QuicFramerTest, EntropyFlagTest) {
TEST_P(QuicFramerTest, FecEntropyTest) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3447,9 +4898,9 @@ TEST_P(QuicFramerTest, FecEntropyTest) {
TEST_P(QuicFramerTest, StopPacketProcessing) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3498,6 +4949,7 @@ TEST_P(QuicFramerTest, StopPacketProcessing) {
EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false));
EXPECT_CALL(visitor, OnAckFrame(_)).Times(0);
EXPECT_CALL(visitor, OnPacketComplete());
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true));
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
diff --git a/chromium/net/quic/quic_headers_stream.cc b/chromium/net/quic/quic_headers_stream.cc
new file mode 100644
index 00000000000..c158e085055
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream.cc
@@ -0,0 +1,266 @@
+// 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_headers_stream.h"
+
+#include "net/quic/quic_session.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const QuicStreamId kInvalidStreamId = 0;
+
+} // namespace
+
+// A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to
+// the QuicDataStream, and closes the connection if any unexpected frames
+// are received.
+class QuicHeadersStream::SpdyFramerVisitor
+ : public SpdyFramerVisitorInterface,
+ public SpdyFramerDebugVisitorInterface {
+ public:
+ explicit SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {}
+
+ // SpdyFramerVisitorInterface implementation
+ virtual void OnSynStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ if (associated_stream_id != 0) {
+ CloseConnection("associated_stream_id != 0");
+ return;
+ }
+
+ if (unidirectional != 0) {
+ CloseConnection("unidirectional != 0");
+ return;
+ }
+
+ stream_->OnSynStream(stream_id, priority, fin);
+ }
+
+ virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ stream_->OnSynReply(stream_id, fin);
+ }
+
+ virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return false;
+ }
+ stream_->OnControlFrameHeaderData(stream_id, header_data, len);
+ return true;
+ }
+
+ virtual void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin) OVERRIDE {
+ if (fin && len == 0) {
+ // The framer invokes OnStreamFrameData with zero-length data and
+ // fin = true after processing a SYN_STREAM or SYN_REPLY frame
+ // that had the fin bit set.
+ return;
+ }
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnError(SpdyFramer* framer) OVERRIDE {
+ CloseConnection("SPDY framing error.");
+ }
+
+ virtual void OnDataFrameHeader(SpdyStreamId stream_id,
+ size_t length,
+ bool fin) OVERRIDE {
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnRstStream(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status) OVERRIDE {
+ CloseConnection("SPDY RST_STREAM frame received.");
+ }
+
+ virtual void OnSetting(SpdySettingsIds id,
+ uint8 flags,
+ uint32 value) OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsAck() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsEnd() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
+ CloseConnection("SPDY PING frame received.");
+ }
+
+ virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status) OVERRIDE {
+ CloseConnection("SPDY GOAWAY frame received.");
+ }
+
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
+ CloseConnection("SPDY HEADERS frame received.");
+ }
+
+ virtual void OnWindowUpdate(SpdyStreamId stream_id,
+ uint32 delta_window_size) OVERRIDE {
+ CloseConnection("SPDY WINDOW_UPDATE frame received.");
+ }
+
+ virtual void OnPushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE {
+ LOG(DFATAL) << "PUSH_PROMISE frame received from a SPDY/3 framer";
+ CloseConnection("SPDY PUSH_PROMISE frame received.");
+ }
+
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+ CloseConnection("SPDY CONTINUATION frame received.");
+ }
+
+ // SpdyFramerDebugVisitorInterface implementation
+ virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t payload_len,
+ size_t frame_len) OVERRIDE {}
+
+ virtual void OnReceiveCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t frame_len) OVERRIDE {
+ if (stream_->IsConnected()) {
+ stream_->OnCompressedFrameSize(frame_len);
+ }
+ }
+
+ private:
+ void CloseConnection(const string& details) {
+ if (stream_->IsConnected()) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA, details);
+ }
+ }
+
+ private:
+ QuicHeadersStream* stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor);
+};
+
+QuicHeadersStream::QuicHeadersStream(QuicSession* session)
+ : ReliableQuicStream(kHeadersStreamId, session),
+ stream_id_(kInvalidStreamId),
+ fin_(false),
+ frame_len_(0),
+ spdy_framer_(SPDY3),
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+ spdy_framer_.set_visitor(spdy_framer_visitor_.get());
+ spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+ // TODO(jri): Set headers to be always FEC protected.
+ DisableFlowControl();
+}
+
+QuicHeadersStream::~QuicHeadersStream() {}
+
+size_t QuicHeadersStream::WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (session()->is_server()) {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_reply));
+ } else {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_stream));
+ }
+ WriteOrBufferData(StringPiece(frame->data(), frame->size()), false,
+ ack_notifier_delegate);
+ return frame->size();
+}
+
+uint32 QuicHeadersStream::ProcessRawData(const char* data,
+ uint32 data_len) {
+ return spdy_framer_.ProcessInput(data, data_len);
+}
+
+QuicPriority QuicHeadersStream::EffectivePriority() const { return 0; }
+
+void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin) {
+ if (!session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_STREAM frame received at the client");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+ session()->OnStreamHeadersPriority(stream_id, priority);
+}
+
+void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) {
+ if (session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_REPLY frame received at the server");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+}
+
+void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ DCHECK_EQ(stream_id_, stream_id);
+ if (len == 0) {
+ DCHECK_NE(0u, stream_id_);
+ DCHECK_NE(0u, frame_len_);
+ session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_);
+ // Reset state for the next frame.
+ stream_id_ = kInvalidStreamId;
+ fin_ = false;
+ frame_len_ = 0;
+ } else {
+ session()->OnStreamHeaders(stream_id_, StringPiece(header_data, len));
+ }
+}
+
+void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) {
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ DCHECK_EQ(0u, frame_len_);
+ frame_len_ = frame_len;
+}
+
+bool QuicHeadersStream::IsConnected() {
+ return session()->connection()->connected();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_headers_stream.h b/chromium/net/quic/quic_headers_stream.h
new file mode 100644
index 00000000000..c3ccbdada40
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream.h
@@ -0,0 +1,82 @@
+// 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_HEADERS_STREAM_H_
+#define NET_QUIC_QUIC_HEADERS_STREAM_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+// Headers in QUIC are sent as SPDY SYN_STREAM or SYN_REPLY frames
+// over a reserved reliable stream with the id 2. Each endpoint (client
+// and server) will allocate an instance of QuicHeadersStream to send
+// and receive headers.
+class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream {
+ public:
+ explicit QuicHeadersStream(QuicSession* session);
+ virtual ~QuicHeadersStream();
+
+ // Writes |headers| for |stream_id| in a SYN_STREAM or SYN_REPLY
+ // frame to the peer. If |fin| is true, the fin flag will be set on
+ // the SPDY frame. Returns the size, in bytes, of the resulting
+ // SPDY frame.
+ size_t WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // ReliableQuicStream implementation
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ private:
+ class SpdyFramerVisitor;
+
+ // The following methods are called by the SimpleVisitor.
+
+ // Called when a SYN_STREAM frame has been received.
+ void OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin);
+
+ // Called when a SYN_REPLY frame been received.
+ void OnSynReply(SpdyStreamId stream_id, bool fin);
+
+ // Called when a chunk of header data is available. This is called
+ // after OnSynStream, or OnSynReply.
+ // |stream_id| The stream receiving the header data.
+ // |header_data| A buffer containing the header data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the header data block has been completely sent.
+ void OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len);
+
+ // Called when the size of the compressed frame payload is available.
+ void OnCompressedFrameSize(size_t frame_len);
+
+ // Returns true if the session is still connected.
+ bool IsConnected();
+
+ // Data about the stream whose headers are being processed.
+ QuicStreamId stream_id_;
+ bool fin_;
+ size_t frame_len_;
+
+ SpdyFramer spdy_framer_;
+ scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HEADERS_STREAM_H_
diff --git a/chromium/net/quic/quic_headers_stream_test.cc b/chromium/net/quic/quic_headers_stream_test.cc
new file mode 100644
index 00000000000..0aa71088102
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream_test.cc
@@ -0,0 +1,334 @@
+// 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_headers_stream.h"
+
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using testing::Invoke;
+using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockVisitor : public SpdyFramerVisitorInterface {
+ public:
+ MOCK_METHOD1(OnError, void(SpdyFramer* framer));
+ MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
+ size_t length,
+ bool fin));
+ MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin));
+ MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len));
+ MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional));
+ MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
+ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status));
+ MOCK_METHOD1(OnSettings, void(bool clear_persisted));
+ MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
+ MOCK_METHOD0(OnSettingsAck, void());
+ MOCK_METHOD0(OnSettingsEnd, void());
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+ MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status));
+ MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end));
+ MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
+ uint32 delta_window_size));
+ MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
+ size_t len));
+ MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end));
+ MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
+ MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ StringPiece protocol_id,
+ StringPiece host,
+ StringPiece origin));
+};
+
+class QuicHeadersStreamTest : public ::testing::TestWithParam<bool> {
+ public:
+ static QuicVersionVector GetVersions() {
+ QuicVersionVector versions;
+ versions.push_back(QuicVersionMax());
+ return versions;
+ }
+
+ QuicHeadersStreamTest()
+ : connection_(new StrictMock<MockConnection>(is_server(), GetVersions())),
+ session_(connection_),
+ headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)),
+ body_("hello world"),
+ framer_(SPDY3) {
+ headers_[":version"] = "HTTP/1.1";
+ headers_[":status"] = "200 Ok";
+ headers_["content-length"] = "11";
+ framer_.set_visitor(&visitor_);
+ EXPECT_EQ(QuicVersionMax(), session_.connection()->version());
+ EXPECT_TRUE(headers_stream_ != NULL);
+ }
+
+ QuicConsumedData SaveIov(const IOVector& data) {
+ const iovec* iov = data.iovec();
+ int count = data.Capacity();
+ for (int i = 0 ; i < count; ++i) {
+ saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ }
+ return QuicConsumedData(saved_data_.length(), false);
+ }
+
+ bool SaveHeaderData(const char* data, int len) {
+ saved_header_data_.append(data, len);
+ return true;
+ }
+
+ void SaveHeaderDataStringPiece(StringPiece data) {
+ saved_header_data_.append(data.data(), data.length());
+ }
+
+ void WriteHeadersAndExpectSynStream(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority) {
+ WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM);
+ }
+
+ void WriteHeadersAndExpectSynReply(QuicStreamId stream_id,
+ bool fin) {
+ WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY);
+ }
+
+ void WriteHeadersAndCheckData(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority,
+ SpdyFrameType type) {
+ // Write the headers and capture the outgoing data
+ EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, NULL))
+ .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+ headers_stream_->WriteHeaders(stream_id, headers_, fin, NULL);
+
+ // Parse the outgoing data and check that it matches was was written.
+ if (type == SYN_STREAM) {
+ EXPECT_CALL(visitor_, OnSynStream(stream_id, kNoAssociatedStream, 0,
+ // priority,
+ fin, kNotUnidirectional));
+ } else {
+ EXPECT_CALL(visitor_, OnSynReply(stream_id, fin));
+ }
+ EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _))
+ .WillRepeatedly(WithArgs<1, 2>(
+ Invoke(this, &QuicHeadersStreamTest::SaveHeaderData)));
+ if (fin) {
+ EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, NULL, 0, true));
+ }
+ framer_.ProcessInput(saved_data_.data(), saved_data_.length());
+ EXPECT_FALSE(framer_.HasError()) << framer_.error_code();
+
+ CheckHeaders();
+ saved_data_.clear();
+ }
+
+ void CheckHeaders() {
+ SpdyHeaderBlock headers;
+ EXPECT_EQ(saved_header_data_.length(),
+ framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(),
+ saved_header_data_.length(),
+ &headers));
+ EXPECT_EQ(headers_, headers);
+ saved_header_data_.clear();
+ }
+
+ bool is_server() {
+ return GetParam();
+ }
+
+ void CloseConnection() {
+ QuicConnectionPeer::CloseConnection(connection_);
+ }
+
+ static const bool kNotUnidirectional = false;
+ static const bool kNoAssociatedStream = false;
+
+ StrictMock<MockConnection>* connection_;
+ StrictMock<MockSession> session_;
+ QuicHeadersStream* headers_stream_;
+ SpdyHeaderBlock headers_;
+ string body_;
+ string saved_data_;
+ string saved_header_data_;
+ SpdyFramer framer_;
+ StrictMock<MockVisitor> visitor_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicHeadersStreamTest, testing::Bool());
+
+TEST_P(QuicHeadersStreamTest, StreamId) {
+ EXPECT_EQ(3u, headers_stream_->id());
+}
+
+TEST_P(QuicHeadersStreamTest, EffectivePriority) {
+ EXPECT_EQ(0u, headers_stream_->EffectivePriority());
+}
+
+TEST_P(QuicHeadersStreamTest, WriteHeaders) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ if (is_server()) {
+ WriteHeadersAndExpectSynReply(stream_id, fin);
+ } else {
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ WriteHeadersAndExpectSynStream(stream_id, fin, priority);
+ }
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessRawData) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (is_server()) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers_);
+ syn_stream.set_fin(fin);
+ frame.reset(framer_.SerializeSynStream(syn_stream));
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers_);
+ syn_reply.set_fin(fin);
+ frame.reset(framer_.SerializeSynReply(syn_reply));
+ }
+ EXPECT_CALL(session_, OnStreamHeaders(stream_id, _))
+ .WillRepeatedly(WithArgs<1>(
+ Invoke(this,
+ &QuicHeadersStreamTest::SaveHeaderDataStringPiece)));
+ EXPECT_CALL(session_,
+ OnStreamHeadersComplete(stream_id, fin, frame->size()));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
+ SpdyDataIR data(2, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY DATA frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
+ SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY RST_STREAM frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) {
+ SpdySettingsIR data;
+ data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SETTINGS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
+ SpdyPingIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY PING frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
+ SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY GOAWAY frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) {
+ SpdyHeadersIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY HEADERS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
+ SpdyWindowUpdateIR data(1, 1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY WINDOW_UPDATE frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, NoFlowControl) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc
index f8b2e6517dc..20b60deb064 100644
--- a/chromium/net/quic/quic_http_stream.cc
+++ b/chromium/net/quic/quic_http_stream.cc
@@ -5,6 +5,7 @@
#include "net/quic/quic_http_stream.h"
#include "base/callback_helpers.h"
+#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -37,6 +38,7 @@ QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session)
response_status_(OK),
response_headers_received_(false),
read_buf_(new GrowableIOBuffer()),
+ closed_stream_received_bytes_(0),
user_buffer_len_(0),
weak_factory_(this) {
DCHECK(session_);
@@ -60,13 +62,16 @@ int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
if (request_info->url.SchemeIsSecure()) {
SSLInfo ssl_info;
- if (!session_->GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ bool secure_session = session_->GetSSLInfo(&ssl_info) && ssl_info.cert;
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SecureResourceSecureSession",
+ secure_session);
+ if (!secure_session)
return ERR_REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC;
- }
}
stream_net_log_ = stream_net_log;
request_info_ = request_info;
+ request_time_ = base::Time::Now();
priority_ = priority;
int rv = stream_request_.StartRequest(
@@ -117,12 +122,9 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
// was being called even if we didn't yet allocate raw_request_body_buf_.
// && (request_body_stream_->size() ||
// request_body_stream_->is_chunked()))
- //
- // Use kMaxPacketSize as the buffer size, since the request
- // body data is written with this size at a time.
- // TODO(rch): use a smarter value since we can't write an entire
- // packet due to overhead.
- raw_request_body_buf_ = new IOBufferWithSize(kMaxPacketSize);
+ // Use 10 packets as the body buffer size to give enough space to
+ // help ensure we don't often send out partial packets.
+ raw_request_body_buf_ = new IOBufferWithSize(10 * kMaxPacketSize);
// The request body buffer is empty at first.
request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), 0);
}
@@ -162,10 +164,6 @@ int QuicHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
return ERR_IO_PENDING;
}
-const HttpResponseInfo* QuicHttpStream::GetResponseInfo() const {
- return response_info_;
-}
-
int QuicHttpStream::ReadResponseBody(
IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
CHECK(buf);
@@ -213,6 +211,7 @@ int QuicHttpStream::ReadResponseBody(
void QuicHttpStream::Close(bool not_reusable) {
// Note: the not_reusable flag has no meaning for SPDY streams.
if (stream_) {
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
stream_->SetDelegate(NULL);
stream_->Reset(QUIC_STREAM_CANCELLED);
stream_ = NULL;
@@ -246,8 +245,11 @@ bool QuicHttpStream::IsConnectionReusable() const {
}
int64 QuicHttpStream::GetTotalReceivedBytes() const {
- // TODO(eustas): Implement.
- return 0;
+ if (stream_) {
+ return stream_->stream_bytes_read();
+ }
+
+ return closed_stream_received_bytes_;
}
bool QuicHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
@@ -327,6 +329,7 @@ void QuicHttpStream::OnClose(QuicErrorCode error) {
response_status_ = ERR_ABORTED;
}
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
stream_ = NULL;
if (!callback_.is_null())
DoCallback(response_status_);
@@ -413,32 +416,23 @@ int QuicHttpStream::DoSendHeaders() {
if (!stream_)
return ERR_UNEXPECTED;
- if (request_.empty() && !stream_->CanWrite(
- base::Bind(&QuicHttpStream::OnIOComplete,
- weak_factory_.GetWeakPtr()))) {
- // Do not compress headers unless it is likely that they can be sent.
- next_state_ = STATE_SEND_HEADERS;
- return ERR_IO_PENDING;
- }
- request_ = stream_->compressor()->CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(priority_), request_headers_);
-
// Log the actual request with the URL Request's net log.
stream_net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS,
- base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_));
+ NetLog::TYPE_HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS,
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
// Also log to the QuicSession's net log.
stream_->net_log().AddEvent(
NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS,
- base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_));
- request_headers_.clear();
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
bool has_upload_data = request_body_stream_ != NULL;
next_state_ = STATE_SEND_HEADERS_COMPLETE;
- return stream_->WriteStreamData(
- request_, !has_upload_data,
- base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr()));
+ int rv = stream_->WriteHeaders(request_headers_, !has_upload_data, NULL);
+ request_headers_.clear();
+ return rv;
}
int QuicHttpStream::DoSendHeadersComplete(int rv) {
@@ -546,6 +540,8 @@ int QuicHttpStream::ParseResponseHeaders() {
.Init(*request_info_, *response_info_->headers.get());
response_info_->was_npn_negotiated = true;
response_info_->npn_negotiated_protocol = "quic/1+spdy/3";
+ response_info_->response_time = base::Time::Now();
+ response_info_->request_time = request_time_;
response_headers_received_ = true;
return OK;
diff --git a/chromium/net/quic/quic_http_stream.h b/chromium/net/quic/quic_http_stream.h
index 7a96ea3d206..5c608226315 100644
--- a/chromium/net/quic/quic_http_stream.h
+++ b/chromium/net/quic/quic_http_stream.h
@@ -41,7 +41,6 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
const CompletionCallback& callback) OVERRIDE;
virtual UploadProgress GetUploadProgress() const OVERRIDE;
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
@@ -119,6 +118,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_;
+ // Time the request was issued.
+ base::Time request_time_;
// The priority of the request.
RequestPriority priority_;
// |response_info_| is the HTTP response data object which is filled in
@@ -145,6 +146,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
// TODO(rch): This is infinite buffering, which is bad.
std::list<scoped_refptr<IOBufferWithSize> > response_body_;
+ // Number of bytes received when the stream was closed.
+ int64 closed_stream_received_bytes_;
+
// The caller's callback to be used for asynchronous operations.
CompletionCallback callback_;
@@ -160,6 +164,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
BoundNetLog stream_net_log_;
base::WeakPtrFactory<QuicHttpStream> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHttpStream);
};
} // namespace net
diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc
index 97ae97261be..c0643d4458d 100644
--- a/chromium/net/quic/quic_http_stream_test.cc
+++ b/chromium/net/quic/quic_http_stream_test.cc
@@ -16,17 +16,20 @@
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_reliable_client_stream.h"
+#include "net/quic/quic_write_blocked_list.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"
#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/test_task_runner.h"
#include "net/socket/socket_test_util.h"
@@ -34,7 +37,6 @@
#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"
@@ -47,15 +49,18 @@ namespace test {
namespace {
const char kUploadData[] = "hello world!";
+const char kServerHostname[] = "www.google.com";
+const uint16 kServerPort = 80;
class TestQuicConnection : public QuicConnection {
public:
- TestQuicConnection(QuicGuid guid,
+ TestQuicConnection(const QuicVersionVector& versions,
+ QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelper* helper,
QuicPacketWriter* writer)
- : QuicConnection(guid, address, helper, writer, false,
- QuicSupportedVersions()) {
+ : QuicConnection(connection_id, address, helper, writer, false,
+ versions) {
}
void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
@@ -69,27 +74,13 @@ class TestQuicConnection : public QuicConnection {
class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
public:
- explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
- : feedback_(feedback) {
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* /*congestion_feedback*/) {
+ return false;
}
- bool GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* congestion_feedback) {
- if (feedback_ == NULL) {
- return false;
- }
- *congestion_feedback = *feedback_;
- return true;
- }
-
- MOCK_METHOD4(RecordIncomingPacket,
- void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool));
-
- private:
- MockClock clock_;
- QuicCongestionFeedbackFrame* feedback_;
-
- DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm);
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
};
// Subclass of QuicHttpStream that closes itself when the first piece of data
@@ -116,9 +107,12 @@ class QuicHttpStreamPeer {
}
};
-class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
+class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
protected:
- const static bool kFin = true;
+ static const bool kFin = true;
+ static const bool kIncludeVersion = true;
+ static const bool kIncludeCongestionFeedback = true;
+
// Holds a packet to be written to the wire, and the IO mode that should
// be used by the mock socket when performing the write.
struct PacketToWrite {
@@ -134,10 +128,10 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
: net_log_(BoundNetLog()),
use_closing_stream_(false),
read_buffer_(new IOBufferWithSize(4096)),
- guid_(2),
- framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- random_generator_(0),
- creator_(guid_, &framer_, &random_generator_, false) {
+ connection_id_(2),
+ stream_id_(kClientDataStreamId1),
+ maker_(GetParam(), connection_id_),
+ random_generator_(0) {
IPAddressNumber ip;
CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
peer_addr_ = IPEndPoint(ip, 443);
@@ -152,8 +146,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
}
// Adds a packet to the list of expected writes.
- void AddWrite(IoMode mode, QuicEncryptedPacket* packet) {
- writes_.push_back(PacketToWrite(mode, packet));
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release()));
}
// Returns the packet to be written at position |pos|.
@@ -165,8 +159,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
return socket_data_->at_read_eof() && socket_data_->at_write_eof();
}
- void ProcessPacket(const QuicEncryptedPacket& packet) {
- connection_->ProcessUdpPacket(self_addr_, peer_addr_, packet);
+ void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) {
+ connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet);
}
// Configures the test fixture to use the list of expected writes.
@@ -186,25 +180,26 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
socket->Connect(peer_addr_);
runner_ = new TestTaskRunner(&clock_);
send_algorithm_ = new MockSendAlgorithm();
- receive_algorithm_ = new TestReceiveAlgorithm(NULL);
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).
+ receive_algorithm_ = new TestReceiveAlgorithm();
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)).
Times(AnyNumber());
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).
WillRepeatedly(Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(
- Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
Return(QuicBandwidth::Zero()));
EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_,
&random_generator_));
writer_.reset(new QuicDefaultPacketWriter(socket));
- connection_ = new TestQuicConnection(guid_, peer_addr_, helper_.get(),
- writer_.get());
+ connection_ = new TestQuicConnection(SupportedVersions(GetParam()),
+ connection_id_, peer_addr_,
+ helper_.get(), writer_.get());
connection_->set_visitor(&visitor_);
connection_->SetSendAlgorithm(send_algorithm_);
connection_->SetReceiveAlgorithm(receive_algorithm_);
@@ -214,89 +209,76 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
scoped_ptr<DatagramClientSocket>(socket),
writer_.Pass(), NULL,
&crypto_client_stream_factory_,
- "www.google.com", DefaultQuicConfig(),
- &crypto_config_, NULL));
+ make_scoped_ptr((QuicServerInfo*)NULL),
+ QuicServerId(kServerHostname, kServerPort,
+ false, PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(), &crypto_config_,
+ base::MessageLoop::current()->
+ message_loop_proxy().get(),
+ NULL));
session_->GetCryptoStream()->CryptoConnect();
EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed());
stream_.reset(use_closing_stream_ ?
new AutoClosingStream(session_->GetWeakPtr()) :
new QuicHttpStream(session_->GetWeakPtr()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
}
- 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, priority);
- }
-
- void SetResponseString(const std::string& status, const std::string& body) {
- SpdyHeaderBlock headers;
- headers[":status"] = status;
- headers[":version"] = "HTTP/1.1";
- headers["content-type"] = "text/plain";
- response_data_ = SerializeHeaderBlock(headers, false, DEFAULT_PRIORITY) +
- body;
+ void SetRequest(const std::string& method,
+ const std::string& path,
+ RequestPriority priority) {
+ request_headers_ = maker_.GetRequestHeaders(method, "http", path);
}
- std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers,
- bool write_priority,
- RequestPriority priority) {
- QuicSpdyCompressor compressor;
- if (write_priority) {
- return compressor.CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(priority), headers);
- }
- return compressor.CompressHeaders(headers);
+ void SetResponse(const std::string& status, const std::string& body) {
+ response_headers_ = maker_.GetResponseHeaders(status);
+ response_data_ = body;
}
- // Returns a newly created packet to send kData on stream 3.
- QuicEncryptedPacket* ConstructDataPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
QuicPacketSequenceNumber sequence_number,
bool should_include_version,
bool fin,
QuicStreamOffset offset,
base::StringPiece data) {
- InitializeHeader(sequence_number, should_include_version);
- QuicStreamFrame frame(3, fin, offset, MakeIOVector(data));
- return ConstructPacket(header_, QuicFrame(&frame));
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id_, should_include_version, fin, offset, data);
}
- // Returns a newly created packet to RST_STREAM stream 3.
- QuicEncryptedPacket* ConstructRstStreamPacket(
- QuicPacketSequenceNumber sequence_number) {
- InitializeHeader(sequence_number, false);
- QuicRstStreamFrame frame(3, QUIC_STREAM_CANCELLED);
- return ConstructPacket(header_, QuicFrame(&frame));
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool fin) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id_, kIncludeVersion, fin, request_headers_);
}
- // Returns a newly created packet to send ack data.
- QuicEncryptedPacket* ConstructAckPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
QuicPacketSequenceNumber sequence_number,
- QuicPacketSequenceNumber largest_received,
- QuicPacketSequenceNumber least_unacked) {
- InitializeHeader(sequence_number, false);
+ bool fin) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id_, !kIncludeVersion, fin, response_headers_);
+ }
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
- ack.sent_info.entropy_hash = 0;
- ack.received_info.entropy_hash = 0;
+ scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeRstPacket(
+ sequence_number, true, stream_id_,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
+ }
- return ConstructPacket(header_, QuicFrame(&ack));
+ scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeAckAndRstPacket(
+ sequence_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED,
+ 2, 1, !kIncludeCongestionFeedback);
}
- // Returns a newly created packet to send ack data.
- QuicEncryptedPacket* ConstructRstPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketSequenceNumber sequence_number,
- QuicStreamId stream_id) {
- InitializeHeader(sequence_number, false);
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR);
- return ConstructPacket(header_, QuicFrame(&rst));
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ return maker_.MakeAckPacket(sequence_number, largest_received,
+ least_unacked, !kIncludeCongestionFeedback);
}
BoundNetLog net_log_;
@@ -318,63 +300,44 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
HttpRequestHeaders headers_;
HttpResponseInfo response_;
scoped_refptr<IOBufferWithSize> read_buffer_;
+ SpdyHeaderBlock request_headers_;
+ SpdyHeaderBlock response_headers_;
std::string request_data_;
std::string response_data_;
private:
- void InitializeHeader(QuicPacketSequenceNumber sequence_number,
- bool should_include_version) {
- header_.public_header.guid = guid_;
- header_.public_header.reset_flag = false;
- header_.public_header.version_flag = should_include_version;
- header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header_.packet_sequence_number = sequence_number;
- header_.fec_group = 0;
- header_.entropy_flag = false;
- header_.fec_flag = false;
- }
-
- QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header_, frames).packet);
- return framer_.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet);
- }
-
- const QuicGuid guid_;
- QuicFramer framer_;
+ const QuicConnectionId connection_id_;
+ const QuicStreamId stream_id_;
+ QuicTestPacketMaker maker_;
IPEndPoint self_addr_;
IPEndPoint peer_addr_;
MockRandom random_generator_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
- QuicPacketCreator creator_;
- QuicPacketHeader header_;
scoped_ptr<StaticSocketDataProvider> socket_data_;
std::vector<PacketToWrite> writes_;
};
-TEST_F(QuicHttpStreamTest, RenewStreamForAuth) {
+INSTANTIATE_TEST_CASE_P(Version, QuicHttpStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicHttpStreamTest, RenewStreamForAuth) {
Initialize();
EXPECT_EQ(NULL, stream_->RenewStreamForAuth());
}
-TEST_F(QuicHttpStreamTest, CanFindEndOfResponse) {
+TEST_P(QuicHttpStreamTest, CanFindEndOfResponse) {
Initialize();
EXPECT_TRUE(stream_->CanFindEndOfResponse());
}
-TEST_F(QuicHttpStreamTest, IsConnectionReusable) {
+TEST_P(QuicHttpStreamTest, IsConnectionReusable) {
Initialize();
EXPECT_FALSE(stream_->IsConnectionReusable());
}
-TEST_F(QuicHttpStreamTest, GetRequest) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
- request_data_));
+TEST_P(QuicHttpStreamTest, GetRequest) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
Initialize();
request_.method = "GET";
@@ -384,26 +347,23 @@ TEST_F(QuicHttpStreamTest, GetRequest) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
- // Send the response without a body.
- SetResponseString("404 Not Found", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("404 Not Found", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
// Now that the headers have been processed, the callback will return.
EXPECT_EQ(OK, callback_.WaitForResult());
ASSERT_TRUE(response_.headers.get());
EXPECT_EQ(404, response_.headers->response_code());
EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+ EXPECT_FALSE(response_.response_time.is_null());
+ EXPECT_FALSE(response_.request_time.is_null());
// There is no body, so this should return immediately.
EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(),
@@ -414,10 +374,9 @@ TEST_F(QuicHttpStreamTest, GetRequest) {
}
// Regression test for http://crbug.com/288128
-TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
- request_data_));
+TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
Initialize();
request_.method = "GET";
@@ -427,11 +386,9 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
@@ -461,55 +418,11 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- Initialize();
-
- request_.method = "GET";
- request_.url = GURL("http://www.google.com/");
-
- EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
- net_log_, callback_.callback()));
- EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
- 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("200 OK", "hello world!");
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
- ProcessPacket(*resp);
-
- // Now that the headers have been processed, the callback will return.
- EXPECT_EQ(OK, callback_.WaitForResult());
- ASSERT_TRUE(response_.headers.get());
- EXPECT_EQ(200, response_.headers->response_code());
- EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
-
- // There is no body, so this should return immediately.
- // Since the body has already arrived, this should return immediately.
- EXPECT_EQ(12, stream_->ReadResponseBody(read_buffer_.get(),
- read_buffer_->size(),
- callback_.callback()));
- EXPECT_TRUE(stream_->IsResponseBodyComplete());
- EXPECT_TRUE(AtEof());
-}
-
-TEST_F(QuicHttpStreamTest, SendPostRequest) {
- SetRequestString("POST", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, kFin,
- request_data_.length(),
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 3, 1));
+TEST_P(QuicHttpStreamTest, SendPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData));
+ AddWrite(ConstructAckPacket(3, 3, 1));
Initialize();
@@ -526,17 +439,13 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack both packets in the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
// Send the response headers (but not the body).
- SetResponseString("200 OK", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, !kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
// Since the headers have already arrived, this should return immediately.
EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
@@ -546,11 +455,7 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
// Send the response body.
const char kResponseBody[] = "Hello world!";
- scoped_ptr<QuicEncryptedPacket> resp_body(
- ConstructDataPacket(3, false, kFin, response_data_.length(),
- kResponseBody));
- ProcessPacket(*resp_body);
-
+ ProcessPacket(ConstructDataPacket(3, false, kFin, 0, kResponseBody));
// Since the body has already arrived, this should return immediately.
EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
@@ -560,18 +465,14 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
- SetRequestString("POST", "/", DEFAULT_PRIORITY);
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
size_t chunk_size = strlen(kUploadData);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, !kFin,
- request_data_.length(),
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(3, true, kFin,
- request_data_.length() + chunk_size,
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 3, 1));
-
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
+ AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size,
+ kUploadData));
+ AddWrite(ConstructAckPacket(4, 3, 1));
Initialize();
UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
@@ -586,19 +487,15 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
net_log_, callback_.callback()));
ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
upload_data_stream.AppendChunk(kUploadData, chunk_size, true);
// Ack both packets in the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
// Send the response headers (but not the body).
- SetResponseString("200 OK", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, !kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
// Since the headers have already arrived, this should return immediately.
ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
@@ -608,10 +505,8 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
// Send the response body.
const char kResponseBody[] = "Hello world!";
- scoped_ptr<QuicEncryptedPacket> resp_body(
- ConstructDataPacket(3, false, kFin, response_data_.length(),
- kResponseBody));
- ProcessPacket(*resp_body);
+ ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(),
+ kResponseBody));
// Since the body has already arrived, this should return immediately.
ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
@@ -622,10 +517,10 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, DestroyedEarly) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
+TEST_P(QuicHttpStreamTest, DestroyedEarly) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
use_closing_stream_ = true;
Initialize();
@@ -636,29 +531,24 @@ TEST_F(QuicHttpStreamTest, DestroyedEarly) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
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_));
-
+ SetResponse("404 OK", "hello world!");
// In the course of processing this packet, the QuicHttpStream close itself.
- ProcessPacket(*resp);
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, Priority) {
- SetRequestString("GET", "/", MEDIUM);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
+TEST_P(QuicHttpStreamTest, Priority) {
+ SetRequest("GET", "/", MEDIUM);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
use_closing_stream_ = true;
Initialize();
@@ -672,38 +562,36 @@ TEST_F(QuicHttpStreamTest, Priority) {
QuicReliableClientStream* reliable_stream =
QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
DCHECK(reliable_stream);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::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);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
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_));
-
+ SetResponse("404 OK", "hello world!");
// In the course of processing this packet, the QuicHttpStream close itself.
- ProcessPacket(*resp);
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
EXPECT_TRUE(AtEof());
}
// Regression test for http://crbug.com/294870
-TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
- SetRequestString("GET", "/", MEDIUM);
+TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
+ SetRequest("GET", "/", MEDIUM);
use_closing_stream_ = true;
+
+ AddWrite(ConstructRstStreamPacket(1));
+
Initialize();
request_.method = "GET";
@@ -718,48 +606,16 @@ TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
DCHECK(reliable_stream);
QuicReliableClientStream::Delegate* delegate = reliable_stream->GetDelegate();
DCHECK(delegate);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
reliable_stream->EffectivePriority());
// Set Delegate to NULL and make sure EffectivePriority returns highest
// priority.
reliable_stream->SetDelegate(NULL);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
reliable_stream->EffectivePriority());
reliable_stream->SetDelegate(delegate);
}
-TEST_F(QuicHttpStreamTest, DontCompressHeadersWhenNotWritable) {
- SetRequestString("GET", "/", MEDIUM);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
-
- Initialize();
- request_.method = "GET";
- request_.url = GURL("http://www.google.com/");
-
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(Return(QuicTime::Delta::Infinite()));
- EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM,
- net_log_, callback_.callback()));
- EXPECT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
- callback_.callback()));
-
- // Verify that the headers have not been compressed and buffered in
- // the stream.
- QuicReliableClientStream* reliable_stream =
- QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
- EXPECT_FALSE(reliable_stream->HasBufferedData());
- EXPECT_FALSE(AtEof());
-
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(Return(QuicTime::Delta::Zero()));
-
- // Data should flush out now.
- connection_->OnCanWrite();
- EXPECT_FALSE(reliable_stream->HasBufferedData());
- EXPECT_TRUE(AtEof());
-}
-
} // namespace test
-
} // namespace net
diff --git a/chromium/net/quic/quic_http_utils.cc b/chromium/net/quic/quic_http_utils.cc
index 173403d82dc..2bad8a6a71a 100644
--- a/chromium/net/quic/quic_http_utils.cc
+++ b/chromium/net/quic/quic_http_utils.cc
@@ -20,4 +20,16 @@ NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
IDLE : static_cast<RequestPriority>(HIGHEST - priority);
}
+base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(
+ SpdyHeaderBlockNetLogCallback(headers, log_level));
+ dict->SetInteger("quic_priority", static_cast<int>(priority));
+ dict->SetInteger("quic_stream_id", static_cast<int>(stream_id));
+ return dict;
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_http_utils.h b/chromium/net/quic/quic_http_utils.h
index c7e031ae605..862b7c61214 100644
--- a/chromium/net/quic/quic_http_utils.h
+++ b/chromium/net/quic/quic_http_utils.h
@@ -5,9 +5,11 @@
#ifndef NET_QUIC_QUIC_HTTP_UTILS_H_
#define NET_QUIC_QUIC_HTTP_UTILS_H_
+#include "base/values.h"
#include "net/base/net_export.h"
#include "net/base/request_priority.h"
#include "net/quic/quic_protocol.h"
+#include "net/spdy/spdy_header_block.h"
namespace net {
@@ -17,6 +19,14 @@ NET_EXPORT_PRIVATE QuicPriority ConvertRequestPriorityToQuicPriority(
NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
QuicPriority priority);
+// Converts a SpdyHeaderBlock and priority into NetLog event parameters. Caller
+// takes ownership of returned value.
+NET_EXPORT base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level);
+
} // namespace net
#endif // NET_QUIC_QUIC_HTTP_UTILS_H_
diff --git a/chromium/net/quic/quic_in_memory_cache.cc b/chromium/net/quic/quic_in_memory_cache.cc
new file mode 100644
index 00000000000..45d25b24e40
--- /dev/null
+++ b/chromium/net/quic/quic_in_memory_cache.cc
@@ -0,0 +1,242 @@
+// Copyright 2014 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_in_memory_cache.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/tools/balsa/balsa_headers.h"
+
+using base::FilePath;
+using base::StringPiece;
+using std::string;
+
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>
+
+namespace net {
+
+namespace {
+
+const FilePath::CharType* g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
+
+// BalsaVisitor implementation (glue) which caches response bodies.
+class CachingBalsaVisitor : public NoOpBalsaVisitor {
+ public:
+ CachingBalsaVisitor() : done_framing_(false) {}
+ virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
+ AppendToBody(input, size);
+ }
+ virtual void MessageDone() OVERRIDE {
+ done_framing_ = true;
+ }
+ virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ void UnhandledError() {
+ LOG(DFATAL) << "Unhandled error framing HTTP.";
+ }
+ void AppendToBody(const char* input, size_t size) {
+ body_.append(input, size);
+ }
+ bool done_framing() const { return done_framing_; }
+ const string& body() const { return body_; }
+
+ private:
+ bool done_framing_;
+ string body_;
+};
+
+} // namespace
+
+// static
+QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
+ return Singleton<QuicInMemoryCache>::get();
+}
+
+const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
+ const BalsaHeaders& request_headers) const {
+ ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
+ if (it == responses_.end()) {
+ return NULL;
+ }
+ return it->second;
+}
+
+void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ StringPiece response_code,
+ StringPiece response_detail,
+ StringPiece body) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ response_headers.SetRequestFirstlineFromStringPieces(version,
+ response_code,
+ response_detail);
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ AddResponse(request_headers, response_headers, body);
+}
+
+void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ StringPiece response_body) {
+ VLOG(1) << "Adding response for: " << GetKey(request_headers);
+ if (ContainsKey(responses_, GetKey(request_headers))) {
+ LOG(DFATAL) << "Response for given request already exists!";
+ return;
+ }
+ Response* new_response = new Response();
+ new_response->set_headers(response_headers);
+ new_response->set_body(response_body);
+ responses_[GetKey(request_headers)] = new_response;
+}
+
+void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ SpecialResponseType response_type) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ AddResponse(request_headers, response_headers, "");
+ responses_[GetKey(request_headers)]->response_type_ = response_type;
+}
+
+QuicInMemoryCache::QuicInMemoryCache() {
+ Initialize();
+}
+
+void QuicInMemoryCache::ResetForTests() {
+ STLDeleteValues(&responses_);
+ Initialize();
+}
+
+void QuicInMemoryCache::Initialize() {
+ // If there's no defined cache dir, we have no initialization to do.
+ if (g_quic_in_memory_cache_dir[0] == '\0') {
+ VLOG(1) << "No cache directory found. Skipping initialization.";
+ return;
+ }
+ VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << g_quic_in_memory_cache_dir;
+
+ FilePath directory(g_quic_in_memory_cache_dir);
+ base::FileEnumerator file_list(directory,
+ true,
+ base::FileEnumerator::FILES);
+
+ FilePath file = file_list.Next();
+ while (!file.empty()) {
+ // Need to skip files in .svn directories
+ if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) {
+ file = file_list.Next();
+ continue;
+ }
+
+ BalsaHeaders request_headers, response_headers;
+
+ string file_contents;
+ base::ReadFileToString(file, &file_contents);
+
+ // Frame HTTP.
+ CachingBalsaVisitor caching_visitor;
+ BalsaFrame framer;
+ framer.set_balsa_headers(&response_headers);
+ framer.set_balsa_visitor(&caching_visitor);
+ size_t processed = 0;
+ while (processed < file_contents.length() &&
+ !caching_visitor.done_framing()) {
+ processed += framer.ProcessInput(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ }
+
+ string response_headers_str;
+ response_headers.DumpToString(&response_headers_str);
+ if (!caching_visitor.done_framing()) {
+ LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
+ << " (" << processed << " of " << file_contents.length()
+ << " bytes).";
+ }
+ if (processed < file_contents.length()) {
+ // Didn't frame whole file. Assume remainder is body.
+ // This sometimes happens as a result of incompatibilities between
+ // BalsaFramer and wget's serialization of HTTP sans content-length.
+ caching_visitor.AppendToBody(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ processed += file_contents.length();
+ }
+
+ StringPiece base = file.AsUTF8Unsafe();
+ if (response_headers.HasHeader("X-Original-Url")) {
+ base = response_headers.GetHeader("X-Original-Url");
+ response_headers.RemoveAllOfHeader("X-Original-Url");
+ // Remove the protocol so that the string is of the form host + path,
+ // which is parsed properly below.
+ if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
+ base.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
+ base.remove_prefix(7);
+ }
+ }
+ int path_start = base.find_first_of('/');
+ DCHECK_LT(0, path_start);
+ StringPiece host(base.substr(0, path_start));
+ StringPiece path(base.substr(path_start));
+ if (path[path.length() - 1] == ',') {
+ path.remove_suffix(1);
+ }
+ // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
+ request_headers.SetRequestFirstlineFromStringPieces("GET",
+ path,
+ "HTTP/1.1");
+ request_headers.ReplaceOrAppendHeader("host", host);
+
+ VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
+ << "' into QuicInMemoryCache.";
+
+ AddResponse(request_headers, response_headers, caching_visitor.body());
+
+ file = file_list.Next();
+ }
+}
+
+QuicInMemoryCache::~QuicInMemoryCache() {
+ STLDeleteValues(&responses_);
+}
+
+string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
+ StringPiece uri = request_headers.request_uri();
+ if (uri.size() == 0) {
+ return "";
+ }
+ StringPiece host;
+ if (uri[0] == '/') {
+ host = request_headers.GetHeader("host");
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
+ uri.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
+ uri.remove_prefix(7);
+ }
+ return host.as_string() + uri.as_string();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_in_memory_cache.h b/chromium/net/quic/quic_in_memory_cache.h
new file mode 100644
index 00000000000..752620abf2d
--- /dev/null
+++ b/chromium/net/quic/quic_in_memory_cache.h
@@ -0,0 +1,116 @@
+// Copyright 2014 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_IN_MEMORY_CACHE_H_
+#define NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/noop_balsa_visitor.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+namespace net {
+
+namespace test {
+class QuicInMemoryCachePeer;
+} // namespace
+
+class QuicServer;
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicInMemoryCache {
+ public:
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ };
+
+ // Container for response header/body pairs.
+ class Response {
+ public:
+ Response() : response_type_(REGULAR_RESPONSE) {}
+ ~Response() {}
+
+ SpecialResponseType response_type() const { return response_type_; }
+ const BalsaHeaders& headers() const { return headers_; }
+ const base::StringPiece body() const { return base::StringPiece(body_); }
+
+ private:
+ friend class QuicInMemoryCache;
+
+ void set_headers(const BalsaHeaders& headers) {
+ headers_.CopyFrom(headers);
+ }
+ void set_body(base::StringPiece body) {
+ body.CopyToString(&body_);
+ }
+
+ SpecialResponseType response_type_;
+ BalsaHeaders headers_;
+ std::string body_;
+
+ DISALLOW_COPY_AND_ASSIGN(Response);
+ };
+
+ // Returns the singleton instance of the cache.
+ static QuicInMemoryCache* GetInstance();
+
+ // Retrieve a response from this cache for a given request.
+ // If no appropriate response exists, NULL is returned.
+ // Currently, responses are selected based on request URI only.
+ const Response* GetResponse(const BalsaHeaders& request_headers) const;
+
+ // Adds a simple response to the cache. The response headers will
+ // only contain the "content-length" header with the lenght of |body|.
+ void AddSimpleResponse(base::StringPiece method,
+ base::StringPiece path,
+ base::StringPiece version,
+ base::StringPiece response_code,
+ base::StringPiece response_detail,
+ base::StringPiece body);
+
+ // Add a response to the cache.
+ void AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ base::StringPiece response_body);
+
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(base::StringPiece method,
+ base::StringPiece path,
+ base::StringPiece version,
+ SpecialResponseType response_type);
+
+ private:
+ typedef base::hash_map<std::string, Response*> ResponseMap;
+ friend struct DefaultSingletonTraits<QuicInMemoryCache>;
+ friend class test::QuicInMemoryCachePeer;
+
+ QuicInMemoryCache();
+ ~QuicInMemoryCache();
+
+ void ResetForTests();
+
+ void Initialize();
+
+ std::string GetKey(const BalsaHeaders& response_headers) const;
+
+ // Cached responses.
+ ResponseMap responses_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc
index 2e4610ce5e7..bd3e3cd94b2 100644
--- a/chromium/net/quic/quic_network_transaction_unittest.cc
+++ b/chromium/net/quic/quic_network_transaction_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <vector>
+
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
@@ -17,7 +19,7 @@
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_stream.h"
#include "net/http/http_stream_factory.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_resolver.h"
@@ -30,6 +32,7 @@
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/mock_client_socket_pool_manager.h"
@@ -50,18 +53,62 @@ static const char kQuicAlternateProtocolHttpHeader[] =
"Alternate-Protocol: 80:quic\r\n\r\n";
static const char kQuicAlternateProtocolHttpsHeader[] =
"Alternate-Protocol: 443:quic\r\n\r\n";
+
} // namespace
namespace net {
namespace test {
-class QuicNetworkTransactionTest : public PlatformTest {
+// Helper class to encapsulate MockReads and MockWrites for QUIC.
+// Simplify ownership issues and the interaction with the MockSocketFactory.
+class MockQuicData {
+ public:
+ ~MockQuicData() {
+ STLDeleteElements(&packets_);
+ }
+
+ void AddRead(scoped_ptr<QuicEncryptedPacket> packet) {
+ reads_.push_back(MockRead(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddRead(IoMode mode, int rv) {
+ reads_.push_back(MockRead(mode, rv));
+ }
+
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddDelayedSocketDataToFactory(MockClientSocketFactory* factory,
+ size_t delay) {
+ MockRead* reads = reads_.empty() ? NULL : &reads_[0];
+ MockWrite* writes = writes_.empty() ? NULL : &writes_[0];
+ socket_data_.reset(new DelayedSocketData(
+ delay, reads, reads_.size(), writes, writes_.size()));
+ factory->AddSocketDataProvider(socket_data_.get());
+ }
+
+ private:
+ std::vector<QuicEncryptedPacket*> packets_;
+ std::vector<MockWrite> writes_;
+ std::vector<MockRead> reads_;
+ size_t sequence_number_;
+ scoped_ptr<SocketDataProvider> socket_data_;
+};
+
+class QuicNetworkTransactionTest
+ : public PlatformTest,
+ public ::testing::WithParamInterface<QuicVersion> {
protected:
QuicNetworkTransactionTest()
- : clock_(new MockClock),
+ : maker_(GetParam(), 0),
+ clock_(new MockClock),
ssl_config_service_(new SSLConfigServiceDefaults),
proxy_service_(ProxyService::CreateDirect()),
- compressor_(new QuicSpdyCompressor()),
auth_handler_factory_(
HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
random_generator_(0),
@@ -69,6 +116,7 @@ class QuicNetworkTransactionTest : public PlatformTest {
request_.method = "GET";
request_.url = GURL("http://www.google.com/");
request_.load_flags = 0;
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
}
virtual void SetUp() {
@@ -83,146 +131,71 @@ class QuicNetworkTransactionTest : public PlatformTest {
PlatformTest::TearDown();
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
base::MessageLoop::current()->RunUntilIdle();
- HttpStreamFactory::set_use_alternate_protocols(false);
- HttpStreamFactory::SetNextProtos(std::vector<NextProto>());
- }
-
- scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
- QuicPacketSequenceNumber num,
- QuicStreamId stream_id) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = num;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR);
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&rst)));
}
scoped_ptr<QuicEncryptedPacket> ConstructConnectionClosePacket(
QuicPacketSequenceNumber num) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = num;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicConnectionCloseFrame close;
- close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
- close.error_details = "Time to panic!";
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&close)));
+ return maker_.MakeConnectionClosePacket(num);
}
scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketSequenceNumber largest_received,
QuicPacketSequenceNumber least_unacked) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = 2;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
-
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kTCP;
- feedback.tcp.accumulated_number_of_lost_packets = 0;
- feedback.tcp.receive_window = 256000;
-
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(QuicFrame(&ack));
- frames.push_back(QuicFrame(&feedback));
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ return maker_.MakeAckPacket(2, largest_received, least_unacked, true);
}
- std::string GetRequestString(const std::string& method,
- const std::string& scheme,
- const std::string& path) {
- SpdyHeaderBlock headers;
- headers[":method"] = method;
- headers[":host"] = "www.google.com";
- headers[":path"] = path;
- headers[":scheme"] = scheme;
- headers[":version"] = "HTTP/1.1";
- return SerializeHeaderBlock(headers);
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ return maker_.GetRequestHeaders(method, scheme, path);
}
- std::string GetResponseString(const std::string& status,
- const std::string& body) {
- SpdyHeaderBlock headers;
- headers[":status"] = status;
- headers[":version"] = "HTTP/1.1";
- headers["content-type"] = "text/plain";
- return compressor_->CompressHeaders(headers) + body;
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status) {
+ return maker_.GetResponseHeaders(status);
}
- std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
- QuicSpdyCompressor compressor;
- return compressor.CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY), headers);
- }
-
- // Returns a newly created packet to send kData on stream 1.
- QuicEncryptedPacket* ConstructDataPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
QuicPacketSequenceNumber sequence_number,
QuicStreamId stream_id,
bool should_include_version,
bool fin,
QuicStreamOffset offset,
base::StringPiece data) {
- InitializeHeader(sequence_number, should_include_version);
- QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data));
- return ConstructPacket(header_, QuicFrame(&frame)).release();
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id, should_include_version, fin, offset, data);
}
- scoped_ptr<QuicEncryptedPacket> ConstructPacket(
- const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
}
- void InitializeHeader(QuicPacketSequenceNumber sequence_number,
- bool should_include_version) {
- header_.public_header.guid = random_generator_.RandUint64();
- header_.public_header.reset_flag = false;
- header_.public_header.version_flag = should_include_version;
- header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header_.packet_sequence_number = sequence_number;
- header_.fec_group = 0;
- header_.entropy_flag = false;
- header_.fec_flag = false;
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
}
void CreateSession() {
- CreateSessionWithFactory(&socket_factory_);
+ CreateSessionWithFactory(&socket_factory_, false);
+ }
+
+ void CreateSessionWithNextProtos() {
+ CreateSessionWithFactory(&socket_factory_, true);
}
- void CreateSessionWithFactory(ClientSocketFactory* socket_factory) {
+ // If |use_next_protos| is true, enables SPDY and QUIC.
+ void CreateSessionWithFactory(ClientSocketFactory* socket_factory,
+ bool use_next_protos) {
params_.enable_quic = true;
params_.quic_clock = clock_;
params_.quic_random = &random_generator_;
@@ -235,6 +208,12 @@ class QuicNetworkTransactionTest : public PlatformTest {
params_.ssl_config_service = ssl_config_service_.get();
params_.http_auth_handler_factory = auth_handler_factory_.get();
params_.http_server_properties = http_server_properties.GetWeakPtr();
+ params_.quic_supported_versions = SupportedVersions(GetParam());
+
+ if (use_next_protos) {
+ params_.use_alternate_protocols = true;
+ params_.next_protos = NextProtosSpdy3();
+ }
session_ = new HttpNetworkSession(params_);
session_->quic_stream_factory()->set_require_confirmation(false);
@@ -308,13 +287,22 @@ class QuicNetworkTransactionTest : public PlatformTest {
EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
}
+ void ExpectQuicAlternateProtocolMapping() {
+ ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol(
+ HostPortPair::FromURL(request_.url)));
+ const PortAlternateProtocolPair alternate =
+ session_->http_server_properties()->GetAlternateProtocol(
+ HostPortPair::FromURL(request_.url));
+ EXPECT_EQ(QUIC, alternate.protocol);
+ }
+
void AddHangingNonAlternateProtocolSocketData() {
MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING);
hanging_data_.set_connect_data(hanging_connect);
socket_factory_.AddSocketDataProvider(&hanging_data_);
}
- QuicPacketHeader header_;
+ QuicTestPacketMaker maker_;
scoped_refptr<HttpNetworkSession> session_;
MockClientSocketFactory socket_factory_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
@@ -324,7 +312,6 @@ class QuicNetworkTransactionTest : public PlatformTest {
TransportSecurityState transport_security_state_;
scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
scoped_ptr<ProxyService> proxy_service_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
MockRandom random_generator_;
HttpServerPropertiesImpl http_server_properties;
@@ -334,35 +321,26 @@ class QuicNetworkTransactionTest : public PlatformTest {
StaticSocketDataProvider hanging_data_;
};
-TEST_F(QuicNetworkTransactionTest, ForceQuic) {
+INSTANTIATE_TEST_CASE_P(Version, QuicNetworkTransactionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicNetworkTransactionTest, ForceQuic) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:80");
- QuicStreamId stream_id = 3;
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, stream_id, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, stream_id, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
@@ -405,19 +383,43 @@ TEST_F(QuicNetworkTransactionTest, ForceQuic) {
int log_stream_id;
ASSERT_TRUE(entries[pos].GetIntegerValue("stream_id", &log_stream_id));
- EXPECT_EQ(stream_id, static_cast<QuicStreamId>(log_stream_id));
+ EXPECT_EQ(3, log_stream_id);
}
-TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
+TEST_P(QuicNetworkTransactionTest, QuicProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("QUIC myproxy:70"));
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // There is no need to set up an alternate protocol job, because
+ // no attempt will be made to speak to the proxy over TCP.
+
+ CreateSession();
+
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:80");
- MockRead quic_reads[] = {
- MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
- };
- StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
- NULL, 0);
- socket_factory_.AddSocketDataProvider(&quic_data);
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
CreateSession();
@@ -429,7 +431,7 @@ TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
}
-TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
+TEST_P(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
// Attempt to "force" quic on 443, which will not be honored.
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:443");
@@ -451,9 +453,7 @@ TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
SendRequestAndExpectHttpResponse("hello world");
}
-TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead(kQuicAlternateProtocolHttpHeader),
@@ -466,46 +466,34 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
SendRequestAndExpectHttpResponse("hello world");
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:443");
params_.enable_quic_https = true;
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"),
@@ -519,43 +507,31 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "https", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
// TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo().
SendRequestAndExpectHttpResponse("hello world");
}
-TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
+TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) {
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START);
@@ -594,7 +570,7 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
http_writes, arraysize(http_writes));
socket_factory.AddSocketDataProvider(&http_data2);
- CreateSessionWithFactory(&socket_factory);
+ CreateSessionWithFactory(&socket_factory, true);
// Run the first request.
http_data.StopAfter(arraysize(http_reads) + arraysize(http_writes));
@@ -613,70 +589,43 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
ASSERT_TRUE(!quic_data.at_write_eof());
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// In order for a new QUIC session to be established via alternate-protocol
// without racing an HTTP connection, we need the host resolution to happen
@@ -692,38 +641,65 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
NULL,
net_log_.bound());
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("PROXY myproxy:70"));
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
+ // Since we are using a proxy, the QUIC job will not succeed.
+ MockWrite http_writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET http://www.google.com/ HTTP/1.1\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "Host: www.google.com\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "Proxy-Connection: keep-alive\r\n\r\n")
};
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
+ MockRead http_reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 4, kQuicAlternateProtocolHttpHeader),
+ MockRead(SYNCHRONOUS, 5, "hello world"),
+ MockRead(SYNCHRONOUS, OK, 6)
};
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ http_writes, arraysize(http_writes));
+ socket_factory_.AddSocketDataProvider(&http_data);
- socket_factory_.AddSocketDataProvider(&quic_data);
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ NULL,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
@@ -741,7 +717,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
CompletionCallback(), NULL, net_log_.bound());
- CreateSession();
+ CreateSessionWithNextProtos();
session_->quic_stream_factory()->set_require_confirmation(true);
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
@@ -756,9 +732,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
EXPECT_EQ(OK, callback.WaitForResult());
}
-TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
// Alternate-protocol job
scoped_ptr<QuicEncryptedPacket> close(ConstructConnectionClosePacket(1));
MockRead quic_reads[] = {
@@ -781,15 +755,13 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
SendRequestAndExpectHttpResponse("hello from http");
ExpectBrokenAlternateProtocolMapping();
}
-TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
// Alternate-protocol job
MockRead quic_reads[] = {
MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
@@ -810,16 +782,45 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
SendRequestAndExpectHttpResponse("hello from http");
ExpectBrokenAlternateProtocolMapping();
}
-TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
+TEST_P(QuicNetworkTransactionTest, NoBrokenAlternateProtocolIfTcpFails) {
+ // Alternate-protocol job will fail when the session attempts to read.
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+ // Main job will also fail.
+ MockRead http_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ http_data.set_connect_data(MockConnect(ASYNC, ERR_SOCKET_NOT_CONNECTED));
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
+ ExpectQuicAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
// Alternate-protocol job
MockRead quic_reads[] = {
MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
@@ -830,6 +831,11 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
AddHangingNonAlternateProtocolSocketData();
+ // Second Alternate-protocol job which will race with the TCP job.
+ StaticSocketDataProvider quic_data2(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data2);
+
// Final job that will proceed when the QUIC job fails.
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n\r\n"),
@@ -842,7 +848,7 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
@@ -854,5 +860,100 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
EXPECT_TRUE(quic_data.at_write_eof());
}
+TEST_P(QuicNetworkTransactionTest, DISABLED_HangingZeroRttFallback) {
+ // Alternate-protocol job
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_IO_PENDING),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job that will proceed when the QUIC job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ SendRequestAndExpectHttpResponse("hello from http");
+}
+
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolOnConnectFailure) {
+ // Alternate-protocol job will fail before creating a QUIC session.
+ StaticSocketDataProvider quic_data(NULL, 0, NULL, 0);
+ quic_data.set_connect_data(MockConnect(SYNCHRONOUS,
+ ERR_INTERNET_DISCONNECTED));
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job which will succeed even though the alternate job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ SendRequestAndExpectHttpResponse("hello from http");
+
+ ExpectBrokenAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ConstructConnectionClosePacket(1));
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
+
+ // When the QUIC connection fails, we will try the request again over HTTP.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ NULL,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_packet_creator.cc b/chromium/net/quic/quic_packet_creator.cc
index cb37cc271de..4eb0ea99a83 100644
--- a/chromium/net/quic/quic_packet_creator.cc
+++ b/chromium/net/quic/quic_packet_creator.cc
@@ -4,6 +4,7 @@
#include "net/quic/quic_packet_creator.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_ack_notifier.h"
@@ -54,18 +55,22 @@ class QuicRandomBoolSource {
DISALLOW_COPY_AND_ASSIGN(QuicRandomBoolSource);
};
-QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
+QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id,
QuicFramer* framer,
- QuicRandom* random_generator,
- bool is_server)
- : guid_(guid),
+ QuicRandom* random_generator)
+ : connection_id_(connection_id),
+ encryption_level_(ENCRYPTION_NONE),
framer_(framer),
random_bool_source_(new QuicRandomBoolSource(random_generator)),
sequence_number_(0),
+ should_fec_protect_(false),
fec_group_number_(0),
- is_server_(is_server),
- send_version_in_packet_(!is_server),
- sequence_number_length_(options_.send_sequence_number_length),
+ send_version_in_packet_(!framer->is_server()),
+ max_packet_length_(kDefaultMaxPacketSize),
+ max_packets_per_fec_group_(0),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
+ next_sequence_number_length_(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sequence_number_length_(next_sequence_number_length_),
packet_size_(0) {
framer_->set_fec_builder(this);
}
@@ -77,23 +82,79 @@ void QuicPacketCreator::OnBuiltFecProtectedPayload(
const QuicPacketHeader& header, StringPiece payload) {
if (fec_group_.get()) {
DCHECK_NE(0u, header.fec_group);
- fec_group_->Update(header, payload);
+ fec_group_->Update(encryption_level_, header, payload);
}
}
bool QuicPacketCreator::ShouldSendFec(bool force_close) const {
+ DCHECK(!HasPendingFrames());
return fec_group_.get() != NULL && fec_group_->NumReceivedPackets() > 0 &&
- (force_close ||
- fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group);
+ (force_close || fec_group_->NumReceivedPackets() >=
+ max_packets_per_fec_group_);
}
-void QuicPacketCreator::MaybeStartFEC() {
- if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) {
- DCHECK(queued_frames_.empty());
- // Set the fec group number to the sequence number of the next packet.
- fec_group_number_ = sequence_number() + 1;
- fec_group_.reset(new QuicFecGroup());
+bool QuicPacketCreator::IsFecGroupOpen() const {
+ return ShouldSendFec(true);
+}
+
+void QuicPacketCreator::StartFecProtectingPackets() {
+ if (!IsFecEnabled()) {
+ LOG(DFATAL) << "Cannot start FEC protection when FEC is not enabled.";
+ return;
+ }
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ if (HasPendingFrames()) {
+ LOG(DFATAL) << "Cannot start FEC protection with pending frames.";
+ return;
+ }
+ DCHECK(!should_fec_protect_);
+ should_fec_protect_ = true;
+}
+
+void QuicPacketCreator::StopFecProtectingPackets() {
+ if (fec_group_.get() != NULL) {
+ LOG(DFATAL) << "Cannot stop FEC protection with open FEC group.";
+ return;
}
+ DCHECK(should_fec_protect_);
+ should_fec_protect_ = false;
+ fec_group_number_ = 0;
+}
+
+bool QuicPacketCreator::IsFecProtected() const {
+ return should_fec_protect_;
+}
+
+bool QuicPacketCreator::IsFecEnabled() const {
+ return max_packets_per_fec_group_ > 0;
+}
+
+InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() {
+ if (fec_group_.get() != NULL) {
+ // Don't update any lengths when an FEC group is open, to ensure same
+ // packet header size in all packets within a group.
+ return IN_FEC_GROUP;
+ }
+ if (!queued_frames_.empty()) {
+ // Don't change creator state if there are frames queued.
+ return fec_group_.get() == NULL ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ }
+
+ // Update sequence number length only on packet and FEC group boundaries.
+ sequence_number_length_ = next_sequence_number_length_;
+
+ if (!should_fec_protect_) {
+ return NOT_IN_FEC_GROUP;
+ }
+ // Start a new FEC group since protection is on. Set the fec group number to
+ // the sequence number of the next packet.
+ fec_group_number_ = sequence_number() + 1;
+ fec_group_.reset(new QuicFecGroup());
+ return IN_FEC_GROUP;
}
// Stops serializing version of the protocol in packets sent after this call.
@@ -110,38 +171,44 @@ void QuicPacketCreator::StopSendingVersion() {
void QuicPacketCreator::UpdateSequenceNumberLength(
QuicPacketSequenceNumber least_packet_awaited_by_peer,
- QuicByteCount bytes_per_second) {
+ QuicByteCount congestion_window) {
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
+ 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);
-
- options_.send_sequence_number_length =
+ const uint64 congestion_window_packets =
+ congestion_window / max_packet_length_;
+ const uint64 delta = max(current_delta, congestion_window_packets);
+ next_sequence_number_length_ =
QuicFramer::GetMinSequenceNumberLength(delta * 4);
}
bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
QuicStreamOffset offset) const {
+ // TODO(jri): This is a simple safe decision for now, but make
+ // is_in_fec_group a parameter. Same as with all public methods in
+ // QuicPacketCreator.
return BytesFree() >
- QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true);
+ QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true,
+ should_fec_protect_ ? IN_FEC_GROUP :
+ NOT_IN_FEC_GROUP);
}
// static
size_t QuicPacketCreator::StreamFramePacketOverhead(
QuicVersion version,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
InFecGroup is_in_fec_group) {
- return GetPacketHeaderSize(guid_length, include_version,
+ return GetPacketHeaderSize(connection_id_length, include_version,
sequence_number_length, is_in_fec_group) +
// Assumes this is a stream with a single lone packet.
- QuicFramer::GetMinStreamFrameSize(version, 1u, 0u, true);
+ QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true,
+ is_in_fec_group);
}
size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
@@ -149,51 +216,31 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
QuicStreamOffset offset,
bool fin,
QuicFrame* frame) {
- DCHECK_GT(options_.max_packet_length,
- StreamFramePacketOverhead(
- framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP));
- if (!HasRoomForStreamFrame(id, offset)) {
- LOG(DFATAL) << "No room for Stream frame, BytesFree: " << BytesFree()
- << " MinStreamFrameSize: "
- << QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, true);
- }
+ DCHECK_GT(max_packet_length_, StreamFramePacketOverhead(
+ framer_->version(), PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, offset, IN_FEC_GROUP));
+
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
+ LOG_IF(DFATAL, !HasRoomForStreamFrame(id, offset))
+ << "No room for Stream frame, BytesFree: " << BytesFree()
+ << " MinStreamFrameSize: "
+ << QuicFramer::GetMinStreamFrameSize(
+ framer_->version(), id, offset, true, is_in_fec_group);
if (data.Empty()) {
- if (!fin) {
- LOG(DFATAL) << "Creating a stream frame with no data or fin.";
- }
+ LOG_IF(DFATAL, !fin)
+ << "Creating a stream frame with no data or fin.";
// Create a new packet for the fin, if necessary.
*frame = QuicFrame(new QuicStreamFrame(id, true, offset, data));
return 0;
}
- const size_t free_bytes = BytesFree();
- size_t bytes_consumed = 0;
const size_t data_size = data.TotalBufferSize();
-
- // When a STREAM frame is the last frame in a packet, it consumes two fewer
- // bytes of framing overhead.
- // Anytime more data is available than fits in with the extra two bytes,
- // the frame will be the last, and up to two extra bytes are consumed.
- // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when
- // 1 byte is available, because then the STREAM frame isn't the last.
-
- // The minimum frame size(0 bytes of data) if it's not the last frame.
size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, false);
- // Check if it's the last frame in the packet.
- if (data_size + min_frame_size > free_bytes) {
- // The minimum frame size(0 bytes of data) if it is the last frame.
- size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, true);
- bytes_consumed =
- min<size_t>(free_bytes - min_last_frame_size, data_size);
- } else {
- DCHECK_LT(data_size, BytesFree());
- bytes_consumed = data_size;
- }
+ framer_->version(), id, offset, /*last_frame_in_packet=*/ true,
+ is_in_fec_group);
+ size_t bytes_consumed = min<size_t>(BytesFree() - min_frame_size, data_size);
bool set_fin = fin && bytes_consumed == data_size; // Last frame.
IOVector frame_data;
@@ -224,25 +271,22 @@ size_t QuicPacketCreator::CreateStreamFrameWithNotifier(
SerializedPacket QuicPacketCreator::ReserializeAllFrames(
const QuicFrames& frames,
QuicSequenceNumberLength original_length) {
- const QuicSequenceNumberLength start_length = sequence_number_length_;
- const QuicSequenceNumberLength start_options_length =
- options_.send_sequence_number_length;
- const QuicFecGroupNumber start_fec_group = fec_group_number_;
- const size_t start_max_packets_per_fec_group =
- options_.max_packets_per_fec_group;
-
- // Temporarily set the sequence number length and disable FEC.
+ DCHECK(fec_group_.get() == NULL);
+ const QuicSequenceNumberLength saved_length = sequence_number_length_;
+ const QuicSequenceNumberLength saved_next_length =
+ next_sequence_number_length_;
+ const bool saved_should_fec_protect = should_fec_protect_;
+
+ // Temporarily set the sequence number length and stop FEC protection.
sequence_number_length_ = original_length;
- options_.send_sequence_number_length = original_length;
- fec_group_number_ = 0;
- options_.max_packets_per_fec_group = 0;
+ next_sequence_number_length_ = original_length;
+ should_fec_protect_ = false;
- // Serialize the packet and restore the fec and sequence number length state.
+ // Serialize the packet and restore the FEC and sequence number length state.
SerializedPacket serialized_packet = SerializeAllFrames(frames);
- sequence_number_length_ = start_length;
- options_.send_sequence_number_length = start_options_length;
- fec_group_number_ = start_fec_group;
- options_.max_packets_per_fec_group = start_max_packets_per_fec_group;
+ sequence_number_length_ = saved_length;
+ next_sequence_number_length_ = saved_next_length;
+ should_fec_protect_ = saved_should_fec_protect;
return serialized_packet;
}
@@ -253,9 +297,8 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames(
// frames from SendStreamData()[send_stream_should_flush_ == false &&
// data.empty() == true] and retransmit due to RTO.
DCHECK_EQ(0u, queued_frames_.size());
- if (frames.empty()) {
- LOG(DFATAL) << "Attempt to serialize empty packet";
- }
+ LOG_IF(DFATAL, frames.empty())
+ << "Attempt to serialize empty packet";
for (size_t i = 0; i < frames.size(); ++i) {
bool success = AddFrame(frames[i], false);
DCHECK(success);
@@ -265,43 +308,46 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames(
return packet;
}
-bool QuicPacketCreator::HasPendingFrames() {
+bool QuicPacketCreator::HasPendingFrames() const {
return !queued_frames_.empty();
}
-size_t QuicPacketCreator::BytesFree() const {
- const size_t max_plaintext_size =
- framer_->GetMaxPlaintextSize(options_.max_packet_length);
- DCHECK_GE(max_plaintext_size, PacketSize());
+bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
+ return queued_retransmittable_frames_.get() != NULL &&
+ !queued_retransmittable_frames_->frames().empty();
+}
- // If the last frame in the packet is a stream frame, then it can be
- // two bytes smaller than if it were not the last. So this means that
- // there are two fewer bytes available to the next frame in this case.
+size_t QuicPacketCreator::ExpansionOnNewFrame() const {
+ // If packet is FEC protected, there's no expansion.
+ if (should_fec_protect_) {
+ return 0;
+ }
+ // If the last frame in the packet is a stream frame, then it will expand to
+ // include the stream_length field when a new frame is added.
bool has_trailing_stream_frame =
!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME;
- size_t expanded_packet_size = PacketSize() +
- (has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0);
+ return has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0;
+}
- if (expanded_packet_size >= max_plaintext_size) {
- return 0;
- }
- return max_plaintext_size - expanded_packet_size;
+size_t QuicPacketCreator::BytesFree() const {
+ const size_t max_plaintext_size =
+ framer_->GetMaxPlaintextSize(max_packet_length_);
+ DCHECK_GE(max_plaintext_size, PacketSize());
+ return max_plaintext_size - min(max_plaintext_size, PacketSize()
+ + ExpansionOnNewFrame());
}
size_t QuicPacketCreator::PacketSize() const {
- if (queued_frames_.empty()) {
- // Only adjust the sequence number length when the FEC group is not open,
- // to ensure no packets in a group are too large.
- if (fec_group_.get() == NULL ||
- fec_group_->NumReceivedPackets() == 0) {
- sequence_number_length_ = options_.send_sequence_number_length;
- }
- packet_size_ = GetPacketHeaderSize(options_.send_guid_length,
- send_version_in_packet_,
- sequence_number_length_,
- options_.max_packets_per_fec_group == 0 ?
- NOT_IN_FEC_GROUP : IN_FEC_GROUP);
+ if (!queued_frames_.empty()) {
+ return packet_size_;
}
+ if (fec_group_.get() == NULL) {
+ // Update sequence number length on packet and FEC boundary.
+ sequence_number_length_ = next_sequence_number_length_;
+ }
+ packet_size_ = GetPacketHeaderSize(
+ connection_id_length_, send_version_in_packet_, sequence_number_length_,
+ should_fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP);
return packet_size_;
}
@@ -310,36 +356,32 @@ bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) {
}
SerializedPacket QuicPacketCreator::SerializePacket() {
- if (queued_frames_.empty()) {
- LOG(DFATAL) << "Attempt to serialize empty packet";
- }
+ LOG_IF(DFATAL, queued_frames_.empty())
+ << "Attempt to serialize empty packet";
+ DCHECK_GE(sequence_number_ + 1, fec_group_number_);
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, false, false, &header);
+ FillPacketHeader(should_fec_protect_ ? fec_group_number_ : 0, false, &header);
MaybeAddPadding();
size_t max_plaintext_size =
- framer_->GetMaxPlaintextSize(options_.max_packet_length);
+ framer_->GetMaxPlaintextSize(max_packet_length_);
DCHECK_GE(max_plaintext_size, packet_size_);
- // ACK and CONNECTION_CLOSE Frames will be truncated only if they're
- // the first frame in the packet. If truncation is to occur, then
- // GetSerializedFrameLength will have returned all bytes free.
- bool possibly_truncated =
- packet_size_ != max_plaintext_size ||
- queued_frames_.size() != 1 ||
- (queued_frames_.back().type == ACK_FRAME ||
- queued_frames_.back().type == CONNECTION_CLOSE_FRAME);
+ // ACK Frames will be truncated only if they're the only frame in the packet,
+ // and if packet_size_ was set to max_plaintext_size. If truncation occurred,
+ // then GetSerializedFrameLength will have returned all bytes free.
+ bool possibly_truncated = packet_size_ == max_plaintext_size &&
+ queued_frames_.size() == 1 &&
+ queued_frames_.back().type == ACK_FRAME;
SerializedPacket serialized =
framer_->BuildDataPacket(header, queued_frames_, packet_size_);
- if (!serialized.packet) {
- LOG(DFATAL) << "Failed to serialize " << queued_frames_.size()
- << " frames.";
- }
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize " << queued_frames_.size() << " frames.";
// Because of possible truncation, we can't be confident that our
// packet size calculation worked correctly.
- if (!possibly_truncated)
+ if (!possibly_truncated) {
DCHECK_EQ(packet_size_, serialized.packet->length());
-
+ }
packet_size_ = 0;
queued_frames_.clear();
serialized.retransmittable_frames = queued_retransmittable_frames_.release();
@@ -347,23 +389,24 @@ SerializedPacket QuicPacketCreator::SerializePacket() {
}
SerializedPacket QuicPacketCreator::SerializeFec() {
- DCHECK_LT(0u, fec_group_->NumReceivedPackets());
+ if (fec_group_.get() == NULL || fec_group_->NumReceivedPackets() <= 0) {
+ LOG(DFATAL) << "SerializeFEC called but no group or zero packets in group.";
+ // TODO(jri): Make this a public method of framer?
+ SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
+ return kNoPacket;
+ }
DCHECK_EQ(0u, queued_frames_.size());
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, true,
- fec_group_->entropy_parity(), &header);
+ FillPacketHeader(fec_group_number_, true, &header);
QuicFecData fec_data;
fec_data.fec_group = fec_group_->min_protected_packet();
fec_data.redundancy = fec_group_->payload_parity();
SerializedPacket serialized = framer_->BuildFecPacket(header, fec_data);
fec_group_.reset(NULL);
- fec_group_number_ = 0;
packet_size_ = 0;
- if (!serialized.packet) {
- LOG(DFATAL) << "Failed to serialize fec packet for group:"
- << fec_data.fec_group;
- }
- DCHECK_GE(options_.max_packet_length, serialized.packet->length());
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize fec packet for group:" << fec_data.fec_group;
+ DCHECK_GE(max_packet_length_, serialized.packet->length());
return serialized;
}
@@ -376,64 +419,59 @@ SerializedPacket QuicPacketCreator::SerializeConnectionClose(
QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket(
const QuicVersionVector& supported_versions) {
- DCHECK(is_server_);
+ DCHECK(framer_->is_server());
QuicPacketPublicHeader header;
- header.guid = guid_;
+ header.connection_id = connection_id_;
header.reset_flag = false;
header.version_flag = true;
header.versions = supported_versions;
QuicEncryptedPacket* encrypted =
framer_->BuildVersionNegotiationPacket(header, supported_versions);
DCHECK(encrypted);
- DCHECK_GE(options_.max_packet_length, encrypted->length());
+ DCHECK_GE(max_packet_length_, encrypted->length());
return encrypted;
}
void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group,
bool fec_flag,
- bool fec_entropy_flag,
QuicPacketHeader* header) {
- header->public_header.guid = guid_;
+ header->public_header.connection_id = connection_id_;
header->public_header.reset_flag = false;
header->public_header.version_flag = send_version_in_packet_;
header->fec_flag = fec_flag;
header->packet_sequence_number = ++sequence_number_;
header->public_header.sequence_number_length = sequence_number_length_;
-
- bool entropy_flag;
- if (fec_flag) {
- // FEC packets don't have an entropy of their own. Entropy flag for FEC
- // packets is the XOR of entropy of previous packets.
- entropy_flag = fec_entropy_flag;
- } else {
- entropy_flag = random_bool_source_->RandBool();
- }
- header->entropy_flag = entropy_flag;
+ header->entropy_flag = random_bool_source_->RandBool();
header->is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
header->fec_group = fec_group;
}
bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) {
- return frame.type != ACK_FRAME && frame.type != CONGESTION_FEEDBACK_FRAME &&
- frame.type != PADDING_FRAME;
+ switch (frame.type) {
+ case ACK_FRAME:
+ case CONGESTION_FEEDBACK_FRAME:
+ case PADDING_FRAME:
+ case STOP_WAITING_FRAME:
+ return false;
+ default:
+ return true;
+ }
}
bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
bool save_retransmittable_frames) {
+ DVLOG(1) << "Adding frame: " << frame;
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
size_t frame_len = framer_->GetSerializedFrameLength(
- frame, BytesFree(), queued_frames_.empty(), true,
- options()->send_sequence_number_length);
+ frame, BytesFree(), queued_frames_.empty(), true, is_in_fec_group,
+ sequence_number_length_);
if (frame_len == 0) {
return false;
}
DCHECK_LT(0u, packet_size_);
- MaybeStartFEC();
- packet_size_ += frame_len;
- // If the last frame in the packet was a stream frame, then once we add the
- // new frame it's serialization will be two bytes larger.
- if (!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME) {
- packet_size_ += kQuicStreamPayloadLengthSize;
- }
+ packet_size_ += ExpansionOnNewFrame() + frame_len;
+
if (save_retransmittable_frames && ShouldRetransmit(frame)) {
if (queued_retransmittable_frames_.get() == NULL) {
queued_retransmittable_frames_.reset(new RetransmittableFrames());
diff --git a/chromium/net/quic/quic_packet_creator.h b/chromium/net/quic/quic_packet_creator.h
index b6756af4059..10b8e8883b8 100644
--- a/chromium/net/quic/quic_packet_creator.h
+++ b/chromium/net/quic/quic_packet_creator.h
@@ -29,27 +29,10 @@ class QuicRandomBoolSource;
class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
public:
- // Options for controlling how packets are created.
- struct Options {
- Options()
- : max_packet_length(kDefaultMaxPacketSize),
- max_packets_per_fec_group(0),
- send_guid_length(PACKET_8BYTE_GUID),
- send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {}
-
- size_t max_packet_length;
- // 0 indicates fec is disabled.
- size_t max_packets_per_fec_group;
- // Length of guid to send over the wire.
- QuicGuidLength send_guid_length;
- QuicSequenceNumberLength send_sequence_number_length;
- };
-
// QuicRandom* required for packet entropy.
- QuicPacketCreator(QuicGuid guid,
+ QuicPacketCreator(QuicConnectionId connection_id,
QuicFramer* framer,
- QuicRandom* random_generator,
- bool is_server);
+ QuicRandom* random_generator);
virtual ~QuicPacketCreator();
@@ -57,10 +40,24 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header,
base::StringPiece payload) OVERRIDE;
+ // Turn on FEC protection for subsequently created packets. FEC should be
+ // enabled first (max_packets_per_fec_group should be non-zero) for FEC
+ // protection to start.
+ void StartFecProtectingPackets();
+
+ // Turn off FEC protection for subsequently created packets. If the creator
+ // has any open FEC group, call will fail. It is the caller's responsibility
+ // to flush out FEC packets in generation, and to verify with ShouldSendFec()
+ // that there is no open FEC group.
+ void StopFecProtectingPackets();
+
// Checks if it's time to send an FEC packet. |force_close| forces this to
- // return true if an fec group is open.
+ // return true if an FEC group is open.
bool ShouldSendFec(bool force_close) const;
+ // Returns true if an FEC packet is under construction.
+ bool IsFecGroupOpen() const;
+
// Makes the framer not serialize the protocol version in sent packets.
void StopSendingVersion();
@@ -68,14 +65,15 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// can be safely changed.
void UpdateSequenceNumberLength(
QuicPacketSequenceNumber least_packet_awaited_by_peer,
- QuicByteCount bytes_per_second);
+ QuicByteCount congestion_window);
// The overhead the framing will add for a packet with one frame.
static size_t StreamFramePacketOverhead(
QuicVersion version,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
InFecGroup is_in_fec_group);
bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const;
@@ -109,11 +107,27 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// Re-serializes frames with the original packet's sequence number length.
// Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
SerializedPacket ReserializeAllFrames(
- const QuicFrames& frames, QuicSequenceNumberLength original_length);
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
// Returns true if there are frames pending to be serialized.
- bool HasPendingFrames();
+ bool HasPendingFrames() const;
+
+ // Returns true if there are retransmittable frames pending to be serialized.
+ bool HasPendingRetransmittableFrames() const;
+
+ // Returns whether FEC protection is currently enabled. Note: Enabled does not
+ // mean that an FEC group is currently active; i.e., IsFecProtected() may
+ // still return false.
+ bool IsFecEnabled() const;
+
+ // Returns true if subsequent packets will be FEC protected. Note: True does
+ // not mean that an FEC packet is currently under construction; i.e.,
+ // fec_group_.get() may still be NULL, until MaybeStartFec() is called.
+ bool IsFecProtected() const;
// Returns the number of bytes which are available to be used by additional
// frames in the packet. Since stream frames are slightly smaller when they
@@ -121,6 +135,13 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// value than max_packet_size - PacketSize(), in this case.
size_t BytesFree() const;
+ // Returns the number of bytes that the packet will expand by if a new frame
+ // is added to the packet. If the last frame was a stream frame, it will
+ // expand slightly when a new frame is added, and this method returns the
+ // amount of expected expansion. If the packet is in an FEC group, no
+ // expansion happens and this method always returns zero.
+ size_t ExpansionOnNewFrame() const;
+
// Returns the number of bytes in the current packet, including the header,
// if serialized with the current frames. Adding a frame to the packet
// may change the serialized length of existing frames, as per the comment
@@ -161,6 +182,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
QuicEncryptedPacket* SerializeVersionNegotiationPacket(
const QuicVersionVector& supported_versions);
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level) {
+ encryption_level_ = level;
+ }
+
// Sequence number of the last created packet, or 0 if no packets have been
// created.
QuicPacketSequenceNumber sequence_number() const {
@@ -171,8 +197,39 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
sequence_number_ = s;
}
- Options* options() {
- return &options_;
+ QuicConnectionIdLength connection_id_length() const {
+ return connection_id_length_;
+ }
+
+ QuicSequenceNumberLength next_sequence_number_length() const {
+ return next_sequence_number_length_;
+ }
+
+ void set_next_sequence_number_length(QuicSequenceNumberLength length) {
+ next_sequence_number_length_ = length;
+ }
+
+ size_t max_packet_length() const {
+ return max_packet_length_;
+ }
+
+ void set_max_packet_length(size_t length) {
+ // |max_packet_length_| should not be changed mid-packet or mid-FEC group.
+ DCHECK(fec_group_.get() == NULL && queued_frames_.empty());
+ max_packet_length_ = length;
+ }
+
+ // Returns current max number of packets covered by an FEC group.
+ size_t max_packets_per_fec_group() const {
+ return max_packets_per_fec_group_;
+ }
+
+ // Sets creator's max number of packets covered by an FEC group.
+ void set_max_packets_per_fec_group(
+ size_t max_packets_per_fec_group) {
+ // To turn off FEC protection, use StopFecProtectingPackets().
+ DCHECK_NE(0u, max_packets_per_fec_group);
+ max_packets_per_fec_group_ = max_packets_per_fec_group;
}
private:
@@ -180,13 +237,16 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
static bool ShouldRetransmit(const QuicFrame& frame);
- // Starts a new FEC group with the next serialized packet, if FEC is enabled
- // and there is not already an FEC group open.
- void MaybeStartFEC();
+ // Updates sequence number and max packet lengths on a packet or FEC group
+ // boundary.
+ void MaybeUpdateLengths();
+
+ // Updates lengths and also starts an FEC group if FEC protection is on and
+ // there is not already an FEC group open.
+ InFecGroup MaybeUpdateLengthsAndStartFec();
void FillPacketHeader(QuicFecGroupNumber fec_group,
bool fec_flag,
- bool fec_entropy_flag,
QuicPacketHeader* header);
// Allows a frame to be added without creating retransmittable frames.
@@ -198,21 +258,32 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// padding frame.
void MaybeAddPadding();
- Options options_;
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
+ EncryptionLevel encryption_level_;
QuicFramer* framer_;
scoped_ptr<QuicRandomBoolSource> random_bool_source_;
QuicPacketSequenceNumber sequence_number_;
+ // If true, any created packets will be FEC protected.
+ bool should_fec_protect_;
QuicFecGroupNumber fec_group_number_;
scoped_ptr<QuicFecGroup> fec_group_;
- // bool to keep track if this packet creator is being used the server.
- bool is_server_;
// Controls whether protocol version should be included while serializing the
// packet.
bool send_version_in_packet_;
- // The sequence number length for the current packet and the current FEC group
- // if FEC is enabled.
- // Mutable so PacketSize() can adjust it when the packet is empty.
+ // Maximum length including headers and encryption (UDP payload length.)
+ size_t max_packet_length_;
+ // 0 indicates FEC is disabled.
+ size_t max_packets_per_fec_group_;
+ // Length of connection_id to send over the wire.
+ QuicConnectionIdLength connection_id_length_;
+ // Staging variable to hold next packet sequence number length. When sequence
+ // number length is to be changed, this variable holds the new length until
+ // a packet or FEC group boundary, when the creator's sequence_number_length_
+ // can be changed to this new value.
+ QuicSequenceNumberLength next_sequence_number_length_;
+ // Sequence number length for the current packet and for the current FEC group
+ // when FEC is enabled. Mutable so PacketSize() can adjust it when the packet
+ // is empty.
mutable QuicSequenceNumberLength sequence_number_length_;
// packet_size_ is mutable because it's just a cache of the current size.
// packet_size should never be read directly, use PacketSize() instead.
diff --git a/chromium/net/quic/quic_packet_creator_test.cc b/chromium/net/quic/quic_packet_creator_test.cc
index 33f2e5c3010..af9b53afecf 100644
--- a/chromium/net/quic/quic_packet_creator_test.cc
+++ b/chromium/net/quic/quic_packet_creator_test.cc
@@ -10,11 +10,14 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_packet_creator_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::StringPiece;
+using std::ostream;
using std::string;
using std::vector;
using testing::DoAll;
@@ -27,19 +30,52 @@ namespace net {
namespace test {
namespace {
-class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
+// Run tests with combinations of {QuicVersion, ToggleVersionSerialization}.
+struct TestParams {
+ TestParams(QuicVersion version,
+ bool version_serialization)
+ : version(version),
+ version_serialization(version_serialization) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ client_version: " << QuicVersionToString(p.version)
+ << " include version: " << p.version_serialization << " }";
+ return os;
+ }
+
+ QuicVersion version;
+ bool version_serialization;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ QuicVersionVector all_supported_versions = QuicSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ params.push_back(TestParams(all_supported_versions[i], true));
+ params.push_back(TestParams(all_supported_versions[i], false));
+ }
+ return params;
+}
+
+class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> {
protected:
QuicPacketCreatorTest()
- : server_framer_(QuicSupportedVersions(), QuicTime::Zero(), true),
- client_framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
+ : server_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ true),
+ client_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ false),
sequence_number_(0),
- guid_(2),
+ connection_id_(2),
data_("foo"),
- creator_(guid_, &client_framer_, &mock_random_, false) {
+ creator_(connection_id_, &client_framer_, &mock_random_) {
client_framer_.set_visitor(&framer_visitor_);
+ client_framer_.set_received_entropy_calculator(&entropy_calculator_);
server_framer_.set_visitor(&framer_visitor_);
}
- ~QuicPacketCreatorTest() {
+
+ virtual ~QuicPacketCreatorTest() OVERRIDE {
}
void ProcessPacket(QuicPacket* packet) {
@@ -64,30 +100,36 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
}
// Returns the number of bytes consumed by the header of packet, including
- // the version, that is not in an FEC group.
- size_t GetPacketHeaderOverhead() {
- return GetPacketHeaderSize(creator_.options()->send_guid_length,
+ // the version.
+ size_t GetPacketHeaderOverhead(InFecGroup is_in_fec_group) {
+ return GetPacketHeaderSize(creator_.connection_id_length(),
kIncludeVersion,
- creator_.options()->send_sequence_number_length,
- NOT_IN_FEC_GROUP);
+ creator_.next_sequence_number_length(),
+ is_in_fec_group);
}
// Returns the number of bytes of overhead that will be added to a packet
// of maximum length.
size_t GetEncryptionOverhead() {
- return creator_.options()->max_packet_length -
- client_framer_.GetMaxPlaintextSize(
- creator_.options()->max_packet_length);
+ return creator_.max_packet_length() - client_framer_.GetMaxPlaintextSize(
+ creator_.max_packet_length());
}
// Returns the number of bytes consumed by the non-data fields of a stream
// frame, assuming it is the last frame in the packet
- size_t GetStreamFrameOverhead() {
- return QuicFramer::GetMinStreamFrameSize(
- client_framer_.version(), kStreamId, kOffset, true);
+ size_t GetStreamFrameOverhead(InFecGroup is_in_fec_group) {
+ return QuicFramer::GetMinStreamFrameSize(client_framer_.version(),
+ kClientDataStreamId1, kOffset,
+ true, is_in_fec_group);
+ }
+
+ // Enables and turns on FEC protection. Returns true if FEC protection is on.
+ bool SwitchFecProtectionOn(size_t max_packets_per_fec_group) {
+ creator_.set_max_packets_per_fec_group(max_packets_per_fec_group);
+ creator_.StartFecProtectingPackets();
+ return creator_.IsFecProtected();
}
- static const QuicStreamId kStreamId = 1u;
static const QuicStreamOffset kOffset = 1u;
QuicFrames frames_;
@@ -95,14 +137,21 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
QuicFramer client_framer_;
testing::StrictMock<MockFramerVisitor> framer_visitor_;
QuicPacketSequenceNumber sequence_number_;
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
string data_;
MockRandom mock_random_;
QuicPacketCreator creator_;
+ MockEntropyCalculator entropy_calculator_;
};
-TEST_F(QuicPacketCreatorTest, SerializeFrames) {
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+// Run all packet creator tests with all supported versions of QUIC, and with
+// and without version in the packet header.
+INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests,
+ QuicPacketCreatorTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicPacketCreatorTest, SerializeFrames) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, true, 0u, IOVector())));
SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
@@ -113,7 +162,9 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
@@ -124,9 +175,12 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
- creator_.options()->max_packets_per_fec_group = 6;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
+TEST_P(QuicPacketCreatorTest, SerializeWithFEC) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
@@ -135,7 +189,9 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
@@ -144,16 +200,20 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
ProcessPacket(serialized.packet);
delete serialized.packet;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
- ASSERT_TRUE(creator_.ShouldSendFec(true));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
serialized = creator_.SerializeFec();
ASSERT_EQ(2u, serialized.sequence_number);
-
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecData(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -162,11 +222,10 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+TEST_P(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
creator_.AddSavedFrame(frames_[0]);
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
SerializedPacket serialized = creator_.SerializePacket();
// The sequence number length will not change mid-packet.
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
@@ -174,7 +233,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -191,7 +252,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -200,22 +263,130 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
- creator_.options()->max_packets_per_fec_group = 6;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
+TEST_P(QuicPacketCreatorTest, ChangeSequenceNumberLengthMidPacket) {
+ if (GetParam().version <= QUIC_VERSION_15) {
+ return;
+ }
+ // Changing the sequence number length with queued frames in the creator
+ // should hold the change until after any currently queued frames are
+ // serialized.
+
+ // Packet 1.
+ // Queue a frame in the creator.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ QuicFrame ack_frame = QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u)));
+ creator_.AddSavedFrame(ack_frame);
+
+ // Now change sequence number length.
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+
+ // Add a STOP_WAITING frame since it contains a packet sequence number,
+ // whose length should be 1.
+ QuicStopWaitingFrame stop_waiting_frame;
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+ // Ensure the packet is successfully created.
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 1 byte sequence length.
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+ delete serialized.packet;
+
+ // Packet 2.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ // Generate Packet 2 with one frame -- sequence number length should now
+ // change to 4 bytes.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created.
+ serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 4 byte sequence length.
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+
+ delete serialized.packet;
+ delete ack_frame.ack_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
+ // Test goal is to test the following sequence (P1 => generate Packet 1):
+ // P1 <change seq num length> P2 FEC,
+ // and we expect that sequence number length should not change until the end
+ // of the open FEC group.
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
+
+ // Generate Packet 1.
creator_.AddSavedFrame(frames_[0]);
// Change the sequence number length mid-FEC group and it should not change.
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
SerializedPacket serialized = creator_.SerializePacket();
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Generate Packet 2.
+ creator_.AddSavedFrame(frames_[0]);
+ serialized = creator_.SerializePacket();
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
@@ -224,17 +395,23 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
ProcessPacket(serialized.packet);
delete serialized.packet;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
- ASSERT_TRUE(creator_.ShouldSendFec(true));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+ // Force generation of FEC packet.
serialized = creator_.SerializeFec();
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
- ASSERT_EQ(2u, serialized.sequence_number);
+ ASSERT_EQ(3u, serialized.sequence_number);
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecData(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -249,19 +426,18 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
// If the original packet sequence number length, the current sequence number
// length, and the configured send sequence number length are different, the
// retransmit must sent with the original length and the others do not change.
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
QuicPacketCreatorPeer::SetSequenceNumberLength(&creator_,
PACKET_2BYTE_SEQUENCE_NUMBER);
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
SerializedPacket serialized =
creator_.ReserializeAllFrames(frames_, PACKET_1BYTE_SEQUENCE_NUMBER);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
QuicPacketCreatorPeer::GetSequenceNumberLength(&creator_));
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
@@ -270,7 +446,9 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -279,7 +457,7 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
+TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
QuicConnectionCloseFrame frame;
frame.error_code = QUIC_NO_ERROR;
frame.error_details = "error";
@@ -290,7 +468,9 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -299,7 +479,84 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrame) {
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithNoGroup) {
+ // Enable FEC protection.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Turn on FEC protection.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We have no packets in the FEC group, so no FEC packet can be created.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/true));
+ // Since no packets are in FEC group yet, we should be able to turn FEC
+ // off with no trouble.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithGroupInProgress) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
+ delete serialized.packet;
+
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We do not have enough packets in the FEC group to trigger an FEC packet.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ // Switching FEC off should not change creator state, since there is an
+ // FEC packet under construction.
+ EXPECT_DFATAL(creator_.StopFecProtectingPackets(),
+ "Cannot stop FEC protection with open FEC group.");
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // Confirm that FEC packet is still under construction.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ serialized = creator_.SerializeFec();
+ delete serialized.packet;
+
+ // Switching FEC on/off should work now.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) {
+ // Add a stream frame to the creator.
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 1u, MakeIOVector("test"), 0u, false, &frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_DFATAL(creator_.StartFecProtectingPackets(),
+ "Cannot start FEC protection with pending frames.");
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Serialize packet for transmission.
+ SerializedPacket serialized = creator_.SerializePacket();
+ delete serialized.packet;
+ delete serialized.retransmittable_frames;
+ EXPECT_FALSE(creator_.HasPendingFrames());
+
+ // Since all pending frames have been serialized, turning FEC on should work.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrame) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 0u,
false, &frame);
@@ -308,7 +565,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrame) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 10u,
true, &frame);
@@ -317,7 +574,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, IOVector(), 0u, true,
&frame);
@@ -326,17 +583,20 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead();
+TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead();
for (size_t i = overhead; i < overhead + 100; ++i) {
- creator_.options()->max_packet_length = i;
- const bool should_have_room = i > overhead + GetStreamFrameOverhead();
- ASSERT_EQ(should_have_room,
- creator_.HasRoomForStreamFrame(kStreamId, kOffset));
+ creator_.set_max_packet_length(i);
+ const bool should_have_room = i > overhead + GetStreamFrameOverhead(
+ NOT_IN_FEC_GROUP);
+ ASSERT_EQ(should_have_room, creator_.HasRoomForStreamFrame(
+ kClientDataStreamId1, kOffset));
if (should_have_room) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector("testdata"), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector("testdata"), kOffset, false,
+ &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -347,10 +607,10 @@ TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
}
}
-TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) {
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
for (int delta = -5; delta <= 5; ++delta) {
@@ -358,12 +618,13 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
size_t bytes_free = delta > 0 ? 0 : 0 - delta;
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector(data), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
EXPECT_EQ(capacity - bytes_free, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
// BytesFree() returns bytes available for the next frame, which will
// be two bytes smaller since the stream frame would need to be grown.
+ EXPECT_EQ(2u, creator_.ExpansionOnNewFrame());
size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2;
EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -373,10 +634,40 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
}
}
-TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumptionWithFec) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(IN_FEC_GROUP);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_EQ(capacity - bytes_free, bytes_consumed);
+
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ // BytesFree() returns bytes available for the next frame. Since stream
+ // frame does not grow for FEC protected packets, this should be the same
+ // as bytes_free (bound by 0).
+ EXPECT_EQ(0u, creator_.ExpansionOnNewFrame());
+ size_t expected_bytes_free = bytes_free > 0 ? bytes_free : 0;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
ASSERT_GT(kMaxPacketSize, overhead);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
@@ -386,7 +677,7 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector(data), kOffset, false, &frame);
+ kCryptoStreamId, MakeIOVector(data), kOffset, false, &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -406,10 +697,10 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
}
}
-TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
+TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
ASSERT_GT(kDefaultMaxPacketSize, overhead);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
@@ -419,7 +710,7 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId + 2, MakeIOVector(data), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -436,8 +727,8 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
}
}
-TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
- QuicPacketCreatorPeer::SetIsServer(&creator_, true);
+TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
+ QuicFramerPeer::SetIsServer(&client_framer_, true);
QuicVersionVector versions;
versions.push_back(test::QuicVersionMax());
scoped_ptr<QuicEncryptedPacket> encrypted(
@@ -446,64 +737,66 @@ TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_));
}
- client_framer_.ProcessPacket(*encrypted.get());
+ QuicFramerPeer::SetIsServer(&client_framer_, false);
+ client_framer_.ProcessPacket(*encrypted);
}
-TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_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_.next_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_.next_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_.next_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);
+ creator_.next_sequence_number_length());
}
-TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000 * 256);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000 * 256 * 256);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_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);
+ creator_.next_sequence_number_length());
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
// Ensure that if CreateStreamFrame does not consume any data (e.g. a FIN only
// frame) then any QuicAckNotifier that is passed in still gets attached to
// the frame.
- MockAckNotifierDelegate delegate;
- QuicAckNotifier notifier(&delegate);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ QuicAckNotifier notifier(delegate.get());
QuicFrame frame;
IOVector empty_iovector;
bool fin = true;
@@ -514,12 +807,8 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
delete frame.stream_frame;
}
-INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization,
- QuicPacketCreatorTest,
- ::testing::Values(false, true));
-
TEST_P(QuicPacketCreatorTest, SerializeFrame) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
@@ -530,27 +819,30 @@ TEST_P(QuicPacketCreatorTest, SerializeFrame) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
DoAll(SaveArg<0>(&header), Return(true)));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
ProcessPacket(serialized.packet);
- EXPECT_EQ(GetParam(), header.public_header.version_flag);
+ EXPECT_EQ(GetParam().version_serialization,
+ header.public_header.version_flag);
delete serialized.packet;
}
TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
// A string larger than fits into a frame.
size_t payload_length;
- creator_.options()->max_packet_length = GetPacketLengthForOneStream(
+ creator_.set_max_packet_length(GetPacketLengthForOneStream(
client_framer_.version(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
- PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length);
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length));
QuicFrame frame;
const string too_long_payload(payload_length * 2, 'a');
size_t consumed = creator_.CreateStreamFrame(
@@ -562,21 +854,21 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) {
}
TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
const size_t max_plaintext_size =
- client_framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length);
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
EXPECT_FALSE(creator_.HasPendingFrames());
EXPECT_EQ(max_plaintext_size -
GetPacketHeaderSize(
- creator_.options()->send_guid_length,
+ creator_.connection_id_length(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
creator_.BytesFree());
// Add a variety of frame types and then a padding frame.
- QuicAckFrame ack_frame(0u, QuicTime::Zero(), 0u);
+ QuicAckFrame ack_frame(MakeAckFrame(0u, 0u));
EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
EXPECT_TRUE(creator_.HasPendingFrames());
@@ -614,14 +906,94 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) {
EXPECT_FALSE(creator_.HasPendingFrames());
EXPECT_EQ(max_plaintext_size -
GetPacketHeaderSize(
- creator_.options()->send_guid_length,
+ creator_.connection_id_length(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
PACKET_1BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP),
creator_.BytesFree());
}
-TEST_F(QuicPacketCreatorTest, EntropyFlag) {
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithLargePacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(kMaxPacketSize);
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the number of nack ranges that can be fit in an ack frame.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_GT(creator_.BytesFree(), frame_len);
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+ EXPECT_LT(0u, creator_.BytesFree());
+
+ // Make sure that an additional stream frame can be added to the packet.
+ QuicFrame stream_frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 2u, MakeIOVector("test"), 0u, false, &stream_frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(stream_frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(stream_frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // matches the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(testing::Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(est_packet_size, serialized.packet->length());
+ delete serialized.retransmittable_frames;
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithSmallPacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(500u);
+
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ EXPECT_EQ(max_plaintext_size - creator_.PacketSize(), creator_.BytesFree());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the packet size.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_EQ(creator_.BytesFree(), frame_len);
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_EQ(max_plaintext_size, creator_.PacketSize());
+ EXPECT_EQ(0u, creator_.BytesFree());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // may not match the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_GE(est_packet_size, serialized.packet->length());
+ delete serialized.packet;
+}
+
+
+TEST_P(QuicPacketCreatorTest, EntropyFlag) {
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
for (int i = 0; i < 2; ++i) {
diff --git a/chromium/net/quic/quic_packet_generator.cc b/chromium/net/quic/quic_packet_generator.cc
index 3785f7d2f8d..9e7473ebbf0 100644
--- a/chromium/net/quic/quic_packet_generator.cc
+++ b/chromium/net/quic/quic_packet_generator.cc
@@ -4,6 +4,7 @@
#include "net/quic/quic_packet_generator.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/quic_fec_group.h"
#include "net/quic/quic_utils.h"
@@ -14,15 +15,18 @@ namespace net {
class QuicAckNotifier;
-QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate,
- DebugDelegateInterface* debug_delegate,
- QuicPacketCreator* creator)
+QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate)
: delegate_(delegate),
- debug_delegate_(debug_delegate),
- packet_creator_(creator),
+ debug_delegate_(NULL),
+ packet_creator_(connection_id, framer, random_generator),
batch_mode_(false),
+ should_fec_protect_(false),
should_send_ack_(false),
- should_send_feedback_(false) {
+ should_send_feedback_(false),
+ should_send_stop_waiting_(false) {
}
QuicPacketGenerator::~QuicPacketGenerator() {
@@ -50,15 +54,34 @@ QuicPacketGenerator::~QuicPacketGenerator() {
case GOAWAY_FRAME:
delete it->goaway_frame;
break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
case NUM_FRAME_TYPES:
DCHECK(false) << "Cannot delete type: " << it->type;
}
}
}
-void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) {
+void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting) {
should_send_ack_ = true;
should_send_feedback_ = also_send_feedback;
+ should_send_stop_waiting_ = also_send_stop_waiting;
+ SendQueuedFrames(false);
+}
+
+void QuicPacketGenerator::SetShouldSendStopWaiting() {
+ should_send_stop_waiting_ = true;
SendQueuedFrames(false);
}
@@ -71,20 +94,26 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
const IOVector& data_to_write,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier* notifier) {
IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
- // The caller should have flushed pending frames before sending handshake
- // messages.
- DCHECK(handshake == NOT_HANDSHAKE || !HasPendingFrames());
- SendQueuedFrames(false);
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ const bool flush = handshake == IS_HANDSHAKE &&
+ packet_creator_.HasPendingRetransmittableFrames();
+ SendQueuedFrames(flush);
size_t total_bytes_consumed = 0;
bool fin_consumed = false;
- if (!packet_creator_->HasRoomForStreamFrame(id, offset)) {
+ if (!packet_creator_.HasRoomForStreamFrame(id, offset)) {
SerializeAndSendPacket();
}
+ if (fec_protection == MUST_FEC_PROTECT) {
+ MaybeStartFecProtection();
+ }
+
IOVector data = data_to_write;
size_t data_size = data.TotalBufferSize();
while (delegate_->ShouldGeneratePacket(NOT_RETRANSMISSION,
@@ -93,10 +122,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
size_t bytes_consumed;
if (notifier != NULL) {
// We want to track which packet this stream frame ends up in.
- bytes_consumed = packet_creator_->CreateStreamFrameWithNotifier(
+ bytes_consumed = packet_creator_.CreateStreamFrameWithNotifier(
id, data, offset + total_bytes_consumed, fin, notifier, &frame);
} else {
- bytes_consumed = packet_creator_->CreateStreamFrame(
+ bytes_consumed = packet_creator_.CreateStreamFrame(
id, data, offset + total_bytes_consumed, fin, &frame);
}
if (!AddFrame(frame)) {
@@ -110,10 +139,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
total_bytes_consumed += bytes_consumed;
fin_consumed = fin && total_bytes_consumed == data_size;
data.Consume(bytes_consumed);
- DCHECK(data.Empty() || packet_creator_->BytesFree() == 0u);
+ DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u);
// TODO(ianswett): Restore packet reordering.
- if (!InBatchMode() || !packet_creator_->HasRoomForStreamFrame(id, offset)) {
+ if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) {
SerializeAndSendPacket();
}
@@ -121,27 +150,33 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
// We're done writing the data. Exit the loop.
// We don't make this a precondition because we could have 0 bytes of data
// if we're simply writing a fin.
+ if (fec_protection == MUST_FEC_PROTECT) {
+ // Turn off FEC protection when we're done writing protected data.
+ DVLOG(1) << "Turning FEC protection OFF";
+ should_fec_protect_ = false;
+ }
break;
}
}
- // Ensure the FEC group is closed at the end of this method if not in batch
- // mode.
- if (!InBatchMode() && packet_creator_->ShouldSendFec(true)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ if (handshake == IS_HANDSHAKE) {
+ SendQueuedFrames(true);
}
- DCHECK(InBatchMode() || !packet_creator_->HasPendingFrames());
+ // Try to close FEC group since we've either run out of data to send or we're
+ // blocked. If not in batch mode, force close the group.
+ MaybeSendFecPacketAndCloseGroup(!InBatchMode());
+
+ DCHECK(InBatchMode() || !packet_creator_.HasPendingFrames());
return QuicConsumedData(total_bytes_consumed, fin_consumed);
}
bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const {
DCHECK(HasPendingFrames());
HasRetransmittableData retransmittable =
- (should_send_ack_ || should_send_feedback_) ? NO_RETRANSMITTABLE_DATA
- : HAS_RETRANSMITTABLE_DATA;
+ (should_send_ack_ || should_send_feedback_ || should_send_stop_waiting_)
+ ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA;
if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
DCHECK(!queued_control_frames_.empty()); // These are retransmittable.
}
@@ -160,17 +195,58 @@ void QuicPacketGenerator::SendQueuedFrames(bool flush) {
}
if (!InBatchMode() || flush) {
- if (packet_creator_->HasPendingFrames()) {
+ if (packet_creator_.HasPendingFrames()) {
SerializeAndSendPacket();
}
-
// Ensure the FEC group is closed at the end of this method unless other
// writes are pending.
- if (packet_creator_->ShouldSendFec(true)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
- }
+ MaybeSendFecPacketAndCloseGroup(true);
+ }
+}
+
+void QuicPacketGenerator::MaybeStartFecProtection() {
+ if (!packet_creator_.IsFecEnabled()) {
+ return;
+ }
+ DVLOG(1) << "Turning FEC protection ON";
+ should_fec_protect_ = true;
+ if (packet_creator_.IsFecProtected()) {
+ // Only start creator's FEC protection if not already on.
+ return;
+ }
+ if (HasQueuedFrames()) {
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ SendQueuedFrames(true);
+ }
+ packet_creator_.StartFecProtectingPackets();
+ DCHECK(packet_creator_.IsFecProtected());
+}
+
+void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force) {
+ if (!packet_creator_.IsFecProtected() ||
+ packet_creator_.HasPendingFrames()) {
+ return;
+ }
+
+ if (packet_creator_.ShouldSendFec(force)) {
+ // TODO(jri): SerializeFec can return a NULL packet, and this should
+ // cause an early return, with a call to
+ // delegate_->OnPacketGenerationError.
+ SerializedPacket serialized_fec = packet_creator_.SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ }
+
+ // Turn FEC protection off if the creator does not have an FEC group open.
+ // Note: We only wait until the frames queued in the creator are flushed;
+ // pending frames in the generator will not keep us from turning FEC off.
+ if (!should_fec_protect_ && !packet_creator_.IsFecGroupOpen()) {
+ packet_creator_.StopFecProtectingPackets();
+ DCHECK(!packet_creator_.IsFecProtected());
}
}
@@ -192,12 +268,12 @@ void QuicPacketGenerator::FlushAllQueuedFrames() {
}
bool QuicPacketGenerator::HasQueuedFrames() const {
- return packet_creator_->HasPendingFrames() || HasPendingFrames();
+ return packet_creator_.HasPendingFrames() || HasPendingFrames();
}
bool QuicPacketGenerator::HasPendingFrames() const {
return should_send_ack_ || should_send_feedback_ ||
- !queued_control_frames_.empty();
+ should_send_stop_waiting_ || !queued_control_frames_.empty();
}
bool QuicPacketGenerator::AddNextPendingFrame() {
@@ -219,9 +295,18 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
return !should_send_feedback_;
}
- if (queued_control_frames_.empty()) {
- LOG(DFATAL) << "AddNextPendingFrame called with no queued control frames.";
+ if (should_send_stop_waiting_) {
+ pending_stop_waiting_frame_.reset(delegate_->CreateStopWaitingFrame());
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_stop_waiting_ =
+ !AddFrame(QuicFrame(pending_stop_waiting_frame_.get()));
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_stop_waiting_;
}
+
+ LOG_IF(DFATAL, queued_control_frames_.empty())
+ << "AddNextPendingFrame called with no queued control frames.";
if (!AddFrame(queued_control_frames_.back())) {
// Packet was full.
return false;
@@ -231,7 +316,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
}
bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) {
- bool success = packet_creator_->AddSavedFrame(frame);
+ bool success = packet_creator_.AddSavedFrame(frame);
if (success && debug_delegate_) {
debug_delegate_->OnFrameAddedToPacket(frame);
}
@@ -239,15 +324,48 @@ bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) {
}
void QuicPacketGenerator::SerializeAndSendPacket() {
- SerializedPacket serialized_packet = packet_creator_->SerializePacket();
+ SerializedPacket serialized_packet = packet_creator_.SerializePacket();
DCHECK(serialized_packet.packet);
delegate_->OnSerializedPacket(serialized_packet);
+ MaybeSendFecPacketAndCloseGroup(false);
+}
- if (packet_creator_->ShouldSendFec(false)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
- }
+void QuicPacketGenerator::StopSendingVersion() {
+ packet_creator_.StopSendingVersion();
+}
+
+QuicPacketSequenceNumber QuicPacketGenerator::sequence_number() const {
+ return packet_creator_.sequence_number();
+}
+
+size_t QuicPacketGenerator::max_packet_length() const {
+ return packet_creator_.max_packet_length();
+}
+
+void QuicPacketGenerator::set_max_packet_length(size_t length) {
+ packet_creator_.set_max_packet_length(length);
+}
+
+QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions) {
+ return packet_creator_.SerializeVersionNegotiationPacket(supported_versions);
+}
+
+SerializedPacket QuicPacketGenerator::ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length) {
+ return packet_creator_.ReserializeAllFrames(frames, original_length);
+}
+
+void QuicPacketGenerator::UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window) {
+ return packet_creator_.UpdateSequenceNumberLength(
+ least_packet_awaited_by_peer, congestion_window);
+}
+
+void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) {
+ packet_creator_.set_encryption_level(level);
}
} // namespace net
diff --git a/chromium/net/quic/quic_packet_generator.h b/chromium/net/quic/quic_packet_generator.h
index 1ff04882ae1..d408aeab31b 100644
--- a/chromium/net/quic/quic_packet_generator.h
+++ b/chromium/net/quic/quic_packet_generator.h
@@ -54,9 +54,14 @@
#define NET_QUIC_QUIC_PACKET_GENERATOR_H_
#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_types.h"
namespace net {
+namespace test {
+class QuicPacketGeneratorPeer;
+} // namespace test
+
class QuicAckNotifier;
class NET_EXPORT_PRIVATE QuicPacketGenerator {
@@ -69,6 +74,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
IsHandshake handshake) = 0;
virtual QuicAckFrame* CreateAckFrame() = 0;
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() = 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;
@@ -77,25 +83,33 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
// Interface which gets callbacks from the QuicPacketGenerator at interesting
// points. Implementations must not mutate the state of the generator
// as a result of these callbacks.
- class NET_EXPORT_PRIVATE DebugDelegateInterface {
+ class NET_EXPORT_PRIVATE DebugDelegate {
public:
- virtual ~DebugDelegateInterface() {}
+ virtual ~DebugDelegate() {}
// Called when a frame has been added to the current packet.
- virtual void OnFrameAddedToPacket(const QuicFrame& frame) = 0;
+ virtual void OnFrameAddedToPacket(const QuicFrame& frame) {}
};
- QuicPacketGenerator(DelegateInterface* delegate,
- DebugDelegateInterface* debug_delegate,
- QuicPacketCreator* creator);
+ QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate);
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.
+ // If |also_send_stop_waiting| is true, then it also indicates that a
+ // STOP_WAITING frame should be sent as well.
// 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 SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting);
+
+ // Indicates that a STOP_WAITING frame should be sent.
+ void SetShouldSendStopWaiting();
+
void AddControlFrame(const QuicFrame& frame);
// Given some data, may consume part or all of it and pass it to the
@@ -109,6 +123,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier* notifier);
// Indicates whether batch mode is currently enabled.
@@ -123,11 +138,63 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
bool HasQueuedFrames() const;
- void set_debug_delegate(DebugDelegateInterface* debug_delegate) {
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ // Caller owns the created packet. Also, sets the entropy hash of the
+ // serialized packet to a random bool and returns that value as a member of
+ // SerializedPacket.
+ QuicEncryptedPacket* SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions);
+
+
+ // Re-serializes frames with the original packet's sequence number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
+ SerializedPacket ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
+
+ // 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 congestion_window);
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level);
+
+ // Sequence number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketSequenceNumber sequence_number() const;
+
+ size_t max_packet_length() const;
+
+ void set_max_packet_length(size_t length);
+
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
debug_delegate_ = debug_delegate;
}
private:
+ friend class test::QuicPacketGeneratorPeer;
+
+ // Turn on FEC protection for subsequent packets in the generator.
+ // If no FEC group is currently open in the creator, this method flushes any
+ // queued frames in the generator and in the creator, and it then turns FEC on
+ // in the creator. This method may be called with an open FEC group in the
+ // creator, in which case, only the generator's state is altered.
+ void MaybeStartFecProtection();
+
+ // Serializes and calls the delegate on an FEC packet if one was under
+ // construction in the creator. When |force| is false, it relies on the
+ // creator being ready to send an FEC packet, otherwise FEC packet is sent
+ // as long as one is under construction in the creator. Also tries to turns
+ // off FEC protection in the creator if it's off in the generator.
+ void MaybeSendFecPacketAndCloseGroup(bool force);
+
void SendQueuedFrames(bool flush);
// Test to see if we have pending ack, feedback, or control frames.
@@ -144,23 +211,29 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
void SerializeAndSendPacket();
DelegateInterface* delegate_;
- DebugDelegateInterface* debug_delegate_;
+ DebugDelegate* debug_delegate_;
- QuicPacketCreator* packet_creator_;
+ QuicPacketCreator packet_creator_;
QuicFrames queued_control_frames_;
// True if batch mode is currently enabled.
bool batch_mode_;
+ // True if FEC protection is on. The creator may have an open FEC group even
+ // if this variable is false.
+ bool should_fec_protect_;
+
// Flags to indicate the need for just-in-time construction of a frame.
bool should_send_ack_;
bool should_send_feedback_;
+ bool should_send_stop_waiting_;
// If we put a non-retransmittable frame (namley ack or feedback frame) in
// this packet, then we have to hold a reference to it until we flush (and
// serialize it). Retransmittable frames are referenced elsewhere so that they
// can later be (optionally) retransmitted.
scoped_ptr<QuicAckFrame> pending_ack_frame_;
scoped_ptr<QuicCongestionFeedbackFrame> pending_feedback_frame_;
+ scoped_ptr<QuicStopWaitingFrame> pending_stop_waiting_frame_;
DISALLOW_COPY_AND_ASSIGN(QuicPacketGenerator);
};
diff --git a/chromium/net/quic/quic_packet_generator_test.cc b/chromium/net/quic/quic_packet_generator_test.cc
index 8907db95a12..331b1834667 100644
--- a/chromium/net/quic/quic_packet_generator_test.cc
+++ b/chromium/net/quic/quic_packet_generator_test.cc
@@ -11,6 +11,8 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -31,7 +33,7 @@ namespace {
class MockDelegate : public QuicPacketGenerator::DelegateInterface {
public:
MockDelegate() {}
- virtual ~MockDelegate() {}
+ virtual ~MockDelegate() OVERRIDE {}
MOCK_METHOD3(ShouldGeneratePacket,
bool(TransmissionType transmission_type,
@@ -39,6 +41,7 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface {
IsHandshake handshake));
MOCK_METHOD0(CreateAckFrame, QuicAckFrame*());
MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*());
+ MOCK_METHOD0(CreateStopWaitingFrame, QuicStopWaitingFrame*());
MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet));
MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool));
@@ -81,6 +84,7 @@ struct PacketContents {
num_feedback_frames(0),
num_goaway_frames(0),
num_rst_stream_frames(0),
+ num_stop_waiting_frames(0),
num_stream_frames(0),
fec_group(0) {
}
@@ -90,6 +94,7 @@ struct PacketContents {
size_t num_feedback_frames;
size_t num_goaway_frames;
size_t num_rst_stream_frames;
+ size_t num_stop_waiting_frames;
size_t num_stream_frames;
QuicFecGroupNumber fec_group;
@@ -101,16 +106,18 @@ class QuicPacketGeneratorTest : public ::testing::Test {
protected:
QuicPacketGeneratorTest()
: framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- creator_(42, &framer_, &random_, false),
- generator_(&delegate_, NULL, &creator_),
+ generator_(42, &framer_, &random_, &delegate_),
+ creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)),
packet_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet2_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet3_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet4_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
- packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) {
+ packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
+ packet6_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
+ packet7_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) {
}
- ~QuicPacketGeneratorTest() {
+ virtual ~QuicPacketGeneratorTest() OVERRIDE {
delete packet_.packet;
delete packet_.retransmittable_frames;
delete packet2_.packet;
@@ -121,11 +128,15 @@ class QuicPacketGeneratorTest : public ::testing::Test {
delete packet4_.retransmittable_frames;
delete packet5_.packet;
delete packet5_.retransmittable_frames;
+ delete packet6_.packet;
+ delete packet6_.retransmittable_frames;
+ delete packet7_.packet;
+ delete packet7_.retransmittable_frames;
}
QuicAckFrame* CreateAckFrame() {
// TODO(rch): Initialize this so it can be verified later.
- return new QuicAckFrame(0, QuicTime::Zero(), 0);
+ return new QuicAckFrame(MakeAckFrame(0, 0));
}
QuicCongestionFeedbackFrame* CreateFeedbackFrame() {
@@ -135,8 +146,15 @@ class QuicPacketGeneratorTest : public ::testing::Test {
return frame;
}
+ QuicStopWaitingFrame* CreateStopWaitingFrame() {
+ QuicStopWaitingFrame* frame = new QuicStopWaitingFrame();
+ frame->entropy_hash = 0;
+ frame->least_unacked = 0;
+ return frame;
+ }
+
QuicRstStreamFrame* CreateRstStreamFrame() {
- return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR);
+ return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR, 0);
}
QuicGoAwayFrame* CreateGoAwayFrame() {
@@ -149,7 +167,7 @@ class QuicPacketGeneratorTest : public ::testing::Test {
contents.num_goaway_frames + contents.num_rst_stream_frames +
contents.num_stream_frames;
size_t num_frames = contents.num_feedback_frames + contents.num_ack_frames +
- num_retransmittable_frames;
+ contents.num_stop_waiting_frames + num_retransmittable_frames;
if (num_retransmittable_frames == 0) {
ASSERT_TRUE(packet.retransmittable_frames == NULL);
@@ -173,6 +191,8 @@ class QuicPacketGeneratorTest : public ::testing::Test {
simple_framer_.rst_stream_frames().size());
EXPECT_EQ(contents.num_stream_frames,
simple_framer_.stream_frames().size());
+ EXPECT_EQ(contents.num_stop_waiting_frames,
+ simple_framer_.stop_waiting_frames().size());
EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group);
}
@@ -204,21 +224,23 @@ class QuicPacketGeneratorTest : public ::testing::Test {
QuicFramer framer_;
MockRandom random_;
- QuicPacketCreator creator_;
StrictMock<MockDelegate> delegate_;
QuicPacketGenerator generator_;
+ QuicPacketCreator* creator_;
SimpleQuicFramer simple_framer_;
SerializedPacket packet_;
SerializedPacket packet2_;
SerializedPacket packet3_;
SerializedPacket packet4_;
SerializedPacket packet5_;
+ SerializedPacket packet6_;
+ SerializedPacket packet7_;
private:
scoped_ptr<char[]> data_array_;
};
-class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface {
+class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate {
public:
MOCK_METHOD1(OnFrameAddedToPacket,
void(const QuicFrame&));
@@ -227,7 +249,7 @@ class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface {
TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -241,7 +263,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1);
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -252,7 +274,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_FALSE(generator_.HasQueuedFrames());
PacketContents contents;
@@ -269,7 +291,7 @@ TEST_F(QuicPacketGeneratorTest,
EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
Return(CreateFeedbackFrame()));
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -280,16 +302,19 @@ TEST_F(QuicPacketGeneratorTest,
EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, true);
EXPECT_FALSE(generator_.HasQueuedFrames());
PacketContents contents;
contents.num_ack_frames = 1;
contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
CheckPacketContains(contents, packet_);
}
@@ -351,8 +376,8 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
delegate_.SetCanNotWrite();
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -362,8 +387,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -374,8 +399,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -390,9 +415,10 @@ TEST_F(QuicPacketGeneratorTest,
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL);
- QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7,
- false, NULL);
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -402,9 +428,10 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL);
- QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7,
- false, NULL);
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -424,7 +451,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
delegate_.SetCanWriteAnything();
// Send FEC every two packets.
- creator_.options()->max_packets_per_fec_group = 2;
+ creator_->set_max_packets_per_fec_group(2);
{
InSequence dummy;
@@ -440,10 +467,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
DoAll(SaveArg<0>(&packet5_), Return(true)));
}
- // Send enough data to create 3 packets: two full and one partial.
+ // Send enough data to create 3 packets: two full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
size_t data_len = 2 * kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -459,9 +488,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
delegate_.SetCanWriteAnything();
- // Send FEC every six packets.
- creator_.options()->max_packets_per_fec_group = 6;
-
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(6);
{
InSequence dummy;
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
@@ -472,10 +500,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
DoAll(SaveArg<0>(&packet3_), Return(true)));
}
- // Send enough data to create 2 packets: one full and one partial.
+ // Send enough data to create 2 packets: one full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
size_t data_len = 1 * kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -488,16 +518,19 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
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 =
+ size_t length =
NullEncrypter().GetCiphertextSize(0) +
- GetPacketHeaderSize(creator_.options()->send_guid_length,
+ GetPacketHeaderSize(creator_->connection_id_length(),
true,
- creator_.options()->send_sequence_number_length,
+ creator_->next_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;
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false,
+ NOT_IN_FEC_GROUP) + 3 +
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true,
+ NOT_IN_FEC_GROUP) + 1;
+ creator_->set_max_packet_length(length);
delegate_.SetCanWriteAnything();
{
InSequence dummy;
@@ -509,8 +542,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
generator_.StartBatchOperations();
// Queue enough data to prevent a stream frame with a non-zero offset from
// fitting.
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 0,
- false, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -518,7 +551,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
// 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, MakeIOVector("bar"), 3, true, NULL);
+ consumed = generator_.ConsumeData(kHeadersStreamId, MakeIOVector("bar"), 3,
+ true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -529,10 +563,259 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
CheckPacketContains(contents, packet2_);
}
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) {
+ delegate_.SetCanWriteAnything();
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ QuicConsumedData consumed =
+ generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT,
+ NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one data packet was sent.
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet4_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet5_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet6_), Return(true)));
+ }
+ // Send enough data to create 3 packets with MUST_FEC_PROTECT flag.
+ size_t data_len = 2 * kDefaultMaxPacketSize + 100;
+ consumed = generator_.ConsumeData(7, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ // Verify that two FEC packets were sent.
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketHasSingleStreamFrame(packet3_);
+ CheckPacketIsFec(packet4_, /*fec_group=*/2u);
+ CheckPacketHasSingleStreamFrame(packet5_);
+ CheckPacketIsFec(packet6_, /*fec_group=*/5u); // Sent at the end of stream.
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet7_), Return(true)));
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one unprotected data packet was sent.
+ CheckPacketContains(contents, packet7_);
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) {
+ delegate_.SetCanWriteAnything();
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ generator_.StartBatchOperations();
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 7, CreateData(1u), 0, true, MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_FALSE(creator_->IsFecProtected());
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ // Transmitted packet was not FEC protected.
+ CheckPacketContains(contents, packet_);
+ EXPECT_TRUE(creator_->IsFecProtected());
+ EXPECT_TRUE(creator_->HasPendingFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) {
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ // Queue control frames in generator.
+ delegate_.SetCanNotWrite();
+ generator_.SetShouldSendAck(true, true);
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Set up frames to write into the creator when control frames are written.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
+
+ // Generator should have queued control frames, and creator should be empty.
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->HasPendingFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ QuicConsumedData consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ // FEC protection should be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue stream frame to be protected in creator.
+ generator_.StartBatchOperations();
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ // Creator has a pending protected frame.
+ EXPECT_TRUE(creator_->HasPendingFrames());
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add enough unprotected data to exceed size of current packet, so that
+ // current packet is sent. Both frames will be sent out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+ // FEC protection should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ generator_.StartBatchOperations();
+ // Send first packet, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ // Write enough data to cause a packet to be emitted.
+ size_t data_len = kDefaultMaxPacketSize;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Send enough unprotected data to cause second packet to be sent, which gets
+ // protected because it happens to fall within an open FEC group. Data packet
+ // will be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ contents.num_stream_frames = 2u;
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should be off in creator.
+ EXPECT_FALSE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue one byte of FEC protected data.
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Add more unprotected data causing first packet to be sent, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC group is still open in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add data that should be protected, large enough to cause second packet to
+ // be sent. Data packet should be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should remain on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -547,7 +830,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
Return(CreateFeedbackFrame()));
// Send some data and a control frame
- generator_.ConsumeData(3, MakeIOVector("quux"), 7, false, NULL);
+ generator_.ConsumeData(3, MakeIOVector("quux"), 7, false,
+ MAY_FEC_PROTECT, NULL);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
// All five frames will be flushed out in a single packet.
@@ -568,7 +852,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -594,7 +878,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
// Send enough data to exceed one packet
size_t data_len = kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
diff --git a/chromium/net/quic/quic_packet_writer.h b/chromium/net/quic/quic_packet_writer.h
index 5df30c8fa14..16b7adeadc6 100644
--- a/chromium/net/quic/quic_packet_writer.h
+++ b/chromium/net/quic/quic_packet_writer.h
@@ -10,7 +10,6 @@
namespace net {
-class QuicBlockedWriterInterface;
struct WriteResult;
// An interface between writers and the entity managing the
@@ -26,14 +25,20 @@ class NET_EXPORT_PRIVATE QuicPacketWriter {
// and error_code is populated.
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) = 0;
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) = 0;
// Returns true if the writer buffers and subsequently rewrites data
// when an attempt to write results in the underlying socket becoming
// write blocked.
virtual bool IsWriteBlockedDataBuffered() const = 0;
+
+ // Returns true if the network socket is not writable.
+ virtual bool IsWriteBlocked() const = 0;
+
+ // Records that the socket has become writable, for example when an EPOLLOUT
+ // is received or an asynchronous write completes.
+ virtual void SetWritable() = 0;
};
} // namespace net
diff --git a/chromium/net/quic/quic_protocol.cc b/chromium/net/quic/quic_protocol.cc
index cc37c111d7e..398c94d525a 100644
--- a/chromium/net/quic/quic_protocol.cc
+++ b/chromium/net/quic/quic_protocol.cc
@@ -15,48 +15,46 @@ using std::string;
namespace net {
-size_t GetPacketHeaderSize(QuicPacketHeader header) {
- return GetPacketHeaderSize(header.public_header.guid_length,
+size_t GetPacketHeaderSize(const QuicPacketHeader& header) {
+ return GetPacketHeaderSize(header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length,
header.is_in_fec_group);
}
-size_t GetPacketHeaderSize(QuicGuidLength guid_length,
+size_t GetPacketHeaderSize(QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
InFecGroup is_in_fec_group) {
- return kPublicFlagsSize + guid_length +
+ return kPublicFlagsSize + connection_id_length +
(include_version ? kQuicVersionSize : 0) + sequence_number_length +
kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0);
}
-size_t GetPublicResetPacketSize() {
- return kPublicFlagsSize + PACKET_8BYTE_GUID + kPublicResetNonceSize +
- PACKET_6BYTE_SEQUENCE_NUMBER;
-}
-
size_t GetStartOfFecProtectedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length) {
- return GetPacketHeaderSize(
- guid_length, include_version, sequence_number_length, IN_FEC_GROUP);
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ IN_FEC_GROUP);
}
size_t GetStartOfEncryptedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length) {
// Don't include the fec size, since encryption starts before private flags.
- return GetPacketHeaderSize(
- guid_length, include_version, sequence_number_length, NOT_IN_FEC_GROUP) -
- kPrivateFlagsSize;
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ NOT_IN_FEC_GROUP) - kPrivateFlagsSize;
}
QuicPacketPublicHeader::QuicPacketPublicHeader()
- : guid(0),
- guid_length(PACKET_8BYTE_GUID),
+ : connection_id(0),
+ connection_id_length(PACKET_8BYTE_CONNECTION_ID),
reset_flag(false),
version_flag(false),
sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) {
@@ -64,8 +62,8 @@ QuicPacketPublicHeader::QuicPacketPublicHeader()
QuicPacketPublicHeader::QuicPacketPublicHeader(
const QuicPacketPublicHeader& other)
- : guid(other.guid),
- guid_length(other.guid_length),
+ : connection_id(other.connection_id),
+ connection_id_length(other.connection_id_length),
reset_flag(other.reset_flag),
version_flag(other.version_flag),
sequence_number_length(other.sequence_number_length),
@@ -93,7 +91,21 @@ QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header)
fec_group(0) {
}
-QuicStreamFrame::QuicStreamFrame() {}
+QuicPublicResetPacket::QuicPublicResetPacket()
+ : nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicPublicResetPacket::QuicPublicResetPacket(
+ const QuicPacketPublicHeader& header)
+ : public_header(header),
+ nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicStreamFrame::QuicStreamFrame()
+ : stream_id(0),
+ fin(false),
+ offset(0),
+ notifier(NULL) {}
QuicStreamFrame::QuicStreamFrame(const QuicStreamFrame& frame)
: stream_id(frame.stream_id),
@@ -132,6 +144,11 @@ uint32 MakeQuicTag(char a, char b, char c, char d) {
static_cast<uint32>(d) << 24;
}
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) {
+ return std::find(tag_vector.begin(), tag_vector.end(), tag)
+ != tag_vector.end();
+}
+
QuicVersionVector QuicSupportedVersions() {
QuicVersionVector supported_versions;
for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
@@ -142,8 +159,18 @@ QuicVersionVector QuicSupportedVersions() {
QuicTag QuicVersionToQuicTag(const QuicVersion version) {
switch (version) {
- case QUIC_VERSION_12:
- return MakeQuicTag('Q', '0', '1', '2');
+ case QUIC_VERSION_15:
+ return MakeQuicTag('Q', '0', '1', '5');
+ case QUIC_VERSION_16:
+ return MakeQuicTag('Q', '0', '1', '6');
+ case QUIC_VERSION_17:
+ return MakeQuicTag('Q', '0', '1', '7');
+ case QUIC_VERSION_18:
+ return MakeQuicTag('Q', '0', '1', '8');
+ case QUIC_VERSION_19:
+ return MakeQuicTag('Q', '0', '1', '9');
+ case QUIC_VERSION_20:
+ return MakeQuicTag('Q', '0', '2', '0');
default:
// This shold be an ERROR because we should never attempt to convert an
// invalid QuicVersion to be written to the wire.
@@ -160,7 +187,7 @@ QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
}
// Reading from the client so this should not be considered an ERROR.
DVLOG(1) << "Unsupported QuicTag version: "
- << QuicUtils::TagToString(version_tag);
+ << QuicUtils::TagToString(version_tag);
return QUIC_VERSION_UNSUPPORTED;
}
@@ -170,7 +197,12 @@ return #x
string QuicVersionToString(const QuicVersion version) {
switch (version) {
- RETURN_STRING_LITERAL(QUIC_VERSION_12);
+ RETURN_STRING_LITERAL(QUIC_VERSION_15);
+ RETURN_STRING_LITERAL(QUIC_VERSION_16);
+ RETURN_STRING_LITERAL(QUIC_VERSION_17);
+ RETURN_STRING_LITERAL(QUIC_VERSION_18);
+ RETURN_STRING_LITERAL(QUIC_VERSION_19);
+ RETURN_STRING_LITERAL(QUIC_VERSION_20);
default:
return "QUIC_VERSION_UNSUPPORTED";
}
@@ -188,8 +220,8 @@ string QuicVersionVectorToString(const QuicVersionVector& versions) {
}
ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
- os << "{ guid: " << header.public_header.guid
- << ", guid_length:" << header.public_header.guid_length
+ os << "{ connection_id: " << header.public_header.connection_id
+ << ", connection_id_length:" << header.public_header.connection_id_length
<< ", sequence_number_length:"
<< header.public_header.sequence_number_length
<< ", reset_flag: " << header.public_header.reset_flag
@@ -210,10 +242,10 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
}
ReceivedPacketInfo::ReceivedPacketInfo()
- : largest_observed(0),
+ : entropy_hash(0),
+ largest_observed(0),
delta_time_largest_observed(QuicTime::Delta::Infinite()),
- is_truncated(false) {
-}
+ is_truncated(false) {}
ReceivedPacketInfo::~ReceivedPacketInfo() {}
@@ -231,21 +263,122 @@ void InsertMissingPacketsBetween(ReceivedPacketInfo* received_info,
}
}
-SentPacketInfo::SentPacketInfo() {}
+QuicStopWaitingFrame::QuicStopWaitingFrame()
+ : entropy_hash(0),
+ least_unacked(0) {
+}
+
+QuicStopWaitingFrame::~QuicStopWaitingFrame() {}
+
+QuicAckFrame::QuicAckFrame() {}
+
+CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP()
+ : receive_window(0) {
+}
+
+CongestionFeedbackMessageInterArrival::CongestionFeedbackMessageInterArrival() {
+}
+
+CongestionFeedbackMessageInterArrival::
+ ~CongestionFeedbackMessageInterArrival() {}
+
+QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {}
+
+QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {}
-SentPacketInfo::~SentPacketInfo() {}
+QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version) {
+ switch (error_code) {
+ case QUIC_RST_FLOW_CONTROL_ACCOUNTING:
+ if (version <= QUIC_VERSION_17) {
+ return QUIC_STREAM_NO_ERROR;
+ }
+ break;
+ default:
+ return error_code;
+ }
+ return error_code;
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame()
+ : stream_id(0),
+ error_code(QUIC_STREAM_NO_ERROR) {
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written)
+ : stream_id(stream_id),
+ error_code(error_code),
+ byte_offset(bytes_written) {
+ DCHECK_LE(error_code, numeric_limits<uint8>::max());
+}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame()
+ : error_code(QUIC_NO_ERROR) {
+}
+
+QuicFrame::QuicFrame() {}
+
+QuicFrame::QuicFrame(QuicPaddingFrame* padding_frame)
+ : type(PADDING_FRAME),
+ padding_frame(padding_frame) {
+}
+
+QuicFrame::QuicFrame(QuicStreamFrame* stream_frame)
+ : type(STREAM_FRAME),
+ stream_frame(stream_frame) {
+}
-// Testing convenience method.
-QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed,
- QuicTime largest_observed_receive_time,
- QuicPacketSequenceNumber least_unacked) {
- received_info.largest_observed = largest_observed;
- received_info.entropy_hash = 0;
- sent_info.least_unacked = least_unacked;
- sent_info.entropy_hash = 0;
+QuicFrame::QuicFrame(QuicAckFrame* frame)
+ : type(ACK_FRAME),
+ ack_frame(frame) {
}
-ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) {
+QuicFrame::QuicFrame(QuicCongestionFeedbackFrame* frame)
+ : type(CONGESTION_FEEDBACK_FRAME),
+ congestion_feedback_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicStopWaitingFrame* frame)
+ : type(STOP_WAITING_FRAME),
+ stop_waiting_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicPingFrame* frame)
+ : type(PING_FRAME),
+ ping_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicRstStreamFrame* frame)
+ : type(RST_STREAM_FRAME),
+ rst_stream_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame)
+ : type(CONNECTION_CLOSE_FRAME),
+ connection_close_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicGoAwayFrame* frame)
+ : type(GOAWAY_FRAME),
+ goaway_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame)
+ : type(WINDOW_UPDATE_FRAME),
+ window_update_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicBlockedFrame* frame)
+ : type(BLOCKED_FRAME),
+ blocked_frame(frame) {
+}
+
+QuicFecData::QuicFecData() : fec_group(0) {}
+
+ostream& operator<<(ostream& os, const QuicStopWaitingFrame& sent_info) {
os << "entropy_hash: " << static_cast<int>(sent_info.entropy_hash)
<< " least_unacked: " << sent_info.least_unacked;
return os;
@@ -253,21 +386,125 @@ ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) {
ostream& operator<<(ostream& os, const ReceivedPacketInfo& received_info) {
os << "entropy_hash: " << static_cast<int>(received_info.entropy_hash)
+ << " is_truncated: " << received_info.is_truncated
<< " largest_observed: " << received_info.largest_observed
+ << " delta_time_largest_observed: "
+ << received_info.delta_time_largest_observed.ToMicroseconds()
<< " missing_packets: [ ";
for (SequenceNumberSet::const_iterator it =
received_info.missing_packets.begin();
it != received_info.missing_packets.end(); ++it) {
os << *it << " ";
}
- os << " ] ";
+ os << " ] revived_packets: [ ";
+ for (SequenceNumberSet::const_iterator it =
+ received_info.revived_packets.begin();
+ it != received_info.revived_packets.end(); ++it) {
+ os << *it << " ";
+ }
+ os << " ]";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicFrame& frame) {
+ switch (frame.type) {
+ case PADDING_FRAME: {
+ os << "type { PADDING_FRAME } ";
+ break;
+ }
+ case RST_STREAM_FRAME: {
+ os << "type { " << RST_STREAM_FRAME << " } " << *(frame.rst_stream_frame);
+ break;
+ }
+ case CONNECTION_CLOSE_FRAME: {
+ os << "type { CONNECTION_CLOSE_FRAME } "
+ << *(frame.connection_close_frame);
+ break;
+ }
+ case GOAWAY_FRAME: {
+ os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame);
+ break;
+ }
+ case WINDOW_UPDATE_FRAME: {
+ os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame);
+ break;
+ }
+ case BLOCKED_FRAME: {
+ os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame);
+ break;
+ }
+ case STREAM_FRAME: {
+ os << "type { STREAM_FRAME } " << *(frame.stream_frame);
+ break;
+ }
+ case ACK_FRAME: {
+ os << "type { ACK_FRAME } " << *(frame.ack_frame);
+ break;
+ }
+ case CONGESTION_FEEDBACK_FRAME: {
+ os << "type { CONGESTION_FEEDBACK_FRAME } "
+ << *(frame.congestion_feedback_frame);
+ break;
+ }
+ case STOP_WAITING_FRAME: {
+ os << "type { STOP_WAITING_FRAME } " << *(frame.stop_waiting_frame);
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unknown frame type: " << frame.type;
+ break;
+ }
+ }
return os;
}
-QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() {
+ostream& operator<<(ostream& os, const QuicRstStreamFrame& rst_frame) {
+ os << "stream_id { " << rst_frame.stream_id << " } "
+ << "error_code { " << rst_frame.error_code << " } "
+ << "error_details { " << rst_frame.error_details << " }\n";
+ return os;
}
-QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {
+ostream& operator<<(ostream& os,
+ const QuicConnectionCloseFrame& connection_close_frame) {
+ os << "error_code { " << connection_close_frame.error_code << " } "
+ << "error_details { " << connection_close_frame.error_details << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicGoAwayFrame& goaway_frame) {
+ os << "error_code { " << goaway_frame.error_code << " } "
+ << "last_good_stream_id { " << goaway_frame.last_good_stream_id << " } "
+ << "reason_phrase { " << goaway_frame.reason_phrase << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os,
+ const QuicWindowUpdateFrame& window_update_frame) {
+ os << "stream_id { " << window_update_frame.stream_id << " } "
+ << "byte_offset { " << window_update_frame.byte_offset << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicBlockedFrame& blocked_frame) {
+ os << "stream_id { " << blocked_frame.stream_id << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicStreamFrame& stream_frame) {
+ os << "stream_id { " << stream_frame.stream_id << " } "
+ << "fin { " << stream_frame.fin << " } "
+ << "offset { " << stream_frame.offset << " } "
+ << "data { "
+ << QuicUtils::StringToHexASCIIDump(*(stream_frame.GetDataAsString()))
+ << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) {
+ os << "sent info { " << ack_frame.sent_info << " } "
+ << "received info { " << ack_frame.received_info << " }\n";
+ return os;
}
ostream& operator<<(ostream& os,
@@ -277,8 +514,6 @@ ostream& operator<<(ostream& os,
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
congestion_frame.inter_arrival;
- os << " accumulated_number_of_lost_packets: "
- << inter_arrival.accumulated_number_of_lost_packets;
os << " received packets: [ ";
for (TimeMap::const_iterator it =
inter_arrival.received_packet_times.begin();
@@ -295,30 +530,25 @@ ostream& operator<<(ostream& os,
}
case kTCP: {
const CongestionFeedbackMessageTCP& tcp = congestion_frame.tcp;
- os << " accumulated_number_of_lost_packets: "
- << congestion_frame.tcp.accumulated_number_of_lost_packets;
os << " receive_window: " << tcp.receive_window;
break;
}
+ case kTCPBBR: {
+ LOG(DFATAL) << "TCPBBR is not yet supported.";
+ break;
+ }
}
return os;
}
-ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) {
- os << "sent info { " << ack_frame.sent_info << " } "
- << "received info { " << ack_frame.received_info << " }\n";
- return os;
-}
-
CongestionFeedbackMessageFixRate::CongestionFeedbackMessageFixRate()
: bitrate(QuicBandwidth::Zero()) {
}
-CongestionFeedbackMessageInterArrival::
-CongestionFeedbackMessageInterArrival() {}
-
-CongestionFeedbackMessageInterArrival::
-~CongestionFeedbackMessageInterArrival() {}
+QuicGoAwayFrame::QuicGoAwayFrame()
+ : error_code(QUIC_NO_ERROR),
+ last_good_stream_id(0) {
+}
QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
QuicStreamId last_good_stream_id,
@@ -329,7 +559,20 @@ QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
DCHECK_LE(error_code, numeric_limits<uint8>::max());
}
-QuicFecData::QuicFecData() {}
+QuicData::QuicData(const char* buffer,
+ size_t length)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(false) {
+}
+
+QuicData::QuicData(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(owns_buffer) {
+}
QuicData::~QuicData() {
if (owns_buffer_) {
@@ -337,9 +580,43 @@ QuicData::~QuicData() {
}
}
+QuicWindowUpdateFrame::QuicWindowUpdateFrame(QuicStreamId stream_id,
+ QuicStreamOffset byte_offset)
+ : stream_id(stream_id),
+ byte_offset(byte_offset) {}
+
+QuicBlockedFrame::QuicBlockedFrame(QuicStreamId stream_id)
+ : stream_id(stream_id) {}
+
+QuicPacket::QuicPacket(char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length,
+ bool is_fec_packet)
+ : QuicData(buffer, length, owns_buffer),
+ buffer_(buffer),
+ is_fec_packet_(is_fec_packet),
+ connection_id_length_(connection_id_length),
+ includes_version_(includes_version),
+ sequence_number_length_(sequence_number_length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer,
+ size_t length)
+ : QuicData(buffer, length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : QuicData(buffer, length, owns_buffer) {
+}
+
StringPiece QuicPacket::FecProtectedData() const {
const size_t start_of_fec = GetStartOfFecProtectedData(
- guid_length_, includes_version_, sequence_number_length_);
+ connection_id_length_, includes_version_, sequence_number_length_);
return StringPiece(data() + start_of_fec, length() - start_of_fec);
}
@@ -347,12 +624,12 @@ StringPiece QuicPacket::AssociatedData() const {
return StringPiece(
data() + kStartOfHashData,
GetStartOfEncryptedData(
- guid_length_, includes_version_, sequence_number_length_) -
+ connection_id_length_, includes_version_, sequence_number_length_) -
kStartOfHashData);
}
StringPiece QuicPacket::BeforePlaintext() const {
- return StringPiece(data(), GetStartOfEncryptedData(guid_length_,
+ return StringPiece(data(), GetStartOfEncryptedData(connection_id_length_,
includes_version_,
sequence_number_length_));
}
@@ -360,7 +637,7 @@ StringPiece QuicPacket::BeforePlaintext() const {
StringPiece QuicPacket::Plaintext() const {
const size_t start_of_encrypted_data =
GetStartOfEncryptedData(
- guid_length_, includes_version_, sequence_number_length_);
+ connection_id_length_, includes_version_, sequence_number_length_);
return StringPiece(data() + start_of_encrypted_data,
length() - start_of_encrypted_data);
}
@@ -384,6 +661,12 @@ RetransmittableFrames::~RetransmittableFrames() {
case CONGESTION_FEEDBACK_FRAME:
delete it->congestion_feedback_frame;
break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
case RST_STREAM_FRAME:
delete it->rst_stream_frame;
break;
@@ -393,6 +676,12 @@ RetransmittableFrames::~RetransmittableFrames() {
case GOAWAY_FRAME:
delete it->goaway_frame;
break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
case NUM_FRAME_TYPES:
DCHECK(false) << "Cannot delete type: " << it->type;
}
@@ -419,6 +708,16 @@ const QuicFrame& RetransmittableFrames::AddNonStreamFrame(
return frames_.back();
}
+IsHandshake RetransmittableFrames::HasCryptoHandshake() const {
+ for (size_t i = 0; i < frames().size(); ++i) {
+ if (frames()[i].type == STREAM_FRAME &&
+ frames()[i].stream_frame->stream_id == kCryptoStreamId) {
+ return IS_HANDSHAKE;
+ }
+ }
+ return NOT_HANDSHAKE;
+}
+
void RetransmittableFrames::set_encryption_level(EncryptionLevel level) {
encryption_level_ = level;
}
@@ -449,10 +748,46 @@ ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) {
return os;
}
-ostream& operator<<(ostream& os, const QuicConsumedData& s) {
- os << "bytes_consumed: " << s.bytes_consumed
- << " fin_consumed: " << s.fin_consumed;
- return os;
+TransmissionInfo::TransmissionInfo()
+ : retransmittable_frames(NULL),
+ sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(NULL),
+ in_flight(false) {}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(new SequenceNumberSet),
+ in_flight(false) {
+ all_transmissions->insert(sequence_number);
+}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberSet* all_transmissions)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(transmission_type),
+ all_transmissions(all_transmissions),
+ in_flight(false) {
+ all_transmissions->insert(sequence_number);
}
} // namespace net
diff --git a/chromium/net/quic/quic_protocol.h b/chromium/net/quic/quic_protocol.h
index e97e2fc77aa..e84d338f4ad 100644
--- a/chromium/net/quic/quic_protocol.h
+++ b/chromium/net/quic/quic_protocol.h
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/base/int128.h"
+#include "net/base/ip_endpoint.h"
#include "net/base/net_export.h"
#include "net/quic/iovector.h"
#include "net/quic/quic_bandwidth.h"
@@ -32,7 +33,7 @@ class QuicAckNotifier;
class QuicPacket;
struct QuicPacketHeader;
-typedef uint64 QuicGuid;
+typedef uint64 QuicConnectionId;
typedef uint32 QuicStreamId;
typedef uint64 QuicStreamOffset;
typedef uint64 QuicPacketSequenceNumber;
@@ -43,6 +44,9 @@ typedef uint32 QuicHeaderId;
// QuicTag is the type of a tag in the wire protocol.
typedef uint32 QuicTag;
typedef std::vector<QuicTag> QuicTagVector;
+typedef std::map<QuicTag, std::string> QuicTagValueMap;
+// TODO(rtenneti): Didn't use SpdyPriority because SpdyPriority is uint8 and
+// QuicPriority is uint32. Use SpdyPriority when we change the QUIC_VERSION.
typedef uint32 QuicPriority;
// TODO(rch): Consider Quic specific names for these constants.
@@ -53,19 +57,22 @@ const QuicByteCount kDefaultMaxPacketSize = 1200;
// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
// max packet size is 1500 bytes, 1500 - 48 = 1452.
const QuicByteCount kMaxPacketSize = 1452;
+// Default maximum packet size used in Linux TCP implementations.
+const QuicByteCount kDefaultTCPMSS = 1460;
// Maximum size of the initial congestion window in packets.
const size_t kDefaultInitialWindow = 10;
-// TODO(ianswett): Temporarily changed to 10 due to a large number of clients
-// mistakenly negotiating 100 initially and suffering the consequences.
-const size_t kMaxInitialWindow = 10;
+const uint32 kMaxInitialWindow = 100;
+
+// Default size of initial flow control window, for both stream and session.
+const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB
// Maximum size of the congestion window, in packets, for TCP congestion control
// algorithms.
const size_t kMaxTcpCongestionWindow = 200;
// Don't allow a client to suggest an RTT longer than 15 seconds.
-const size_t kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
+const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
// Maximum number of open streams per connection.
const size_t kDefaultMaxStreamsPerConnection = 100;
@@ -78,8 +85,6 @@ const size_t kQuicVersionSize = 4;
const size_t kPrivateFlagsSize = 1;
// Number of bytes reserved for FEC group in the packet header.
const size_t kFecGroupSize = 1;
-// Number of bytes reserved for the nonce proof in public reset packet.
-const size_t kPublicResetNonceSize = 8;
// Signifies that the QuicPacket will contain version of the protocol.
const bool kIncludeVersion = true;
@@ -88,20 +93,25 @@ const bool kIncludeVersion = true;
const size_t kStartOfHashData = 0;
// Limit on the delta between stream IDs.
-const QuicStreamId kMaxStreamIdDelta = 100;
+const QuicStreamId kMaxStreamIdDelta = 200;
// Limit on the delta between header IDs.
-const QuicHeaderId kMaxHeaderIdDelta = 100;
+const QuicHeaderId kMaxHeaderIdDelta = 200;
// Reserved ID for the crypto stream.
-// TODO(rch): ensure that this is not usable by any other streams.
const QuicStreamId kCryptoStreamId = 1;
+// Reserved ID for the headers stream.
+const QuicStreamId kHeadersStreamId = 3;
+
// This is the default network timeout a for connection till the crypto
// handshake succeeds and the negotiated timeout from the handshake is received.
const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins.
const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes.
const int64 kDefaultMaxTimeForCryptoHandshakeSecs = 5; // 5 secs.
+// Default ping timeout.
+const int64 kPingTimeoutSecs = 15; // 15 secs.
+
// We define an unsigned 16-bit floating point value, inspired by IEEE floats
// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format),
// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden
@@ -121,8 +131,13 @@ const uint64 kUFloat16MaxValue = // 0x3FFC0000000
enum TransmissionType {
NOT_RETRANSMISSION,
- NACK_RETRANSMISSION,
- RTO_RETRANSMISSION,
+ FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION,
+ HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts.
+ ALL_UNACKED_RETRANSMISSION, // Retransmits of all unacked packets.
+ LOSS_RETRANSMISSION, // Retransmits due to loss detection.
+ RTO_RETRANSMISSION, // Retransmits due to retransmit time out.
+ TLP_RETRANSMISSION, // Tail loss probes.
+ LAST_TRANSMISSION_TYPE = TLP_RETRANSMISSION,
};
enum RetransmissionType {
@@ -140,22 +155,43 @@ enum IsHandshake {
IS_HANDSHAKE
};
+// Indicates FEC protection level for data being written.
+enum FecProtection {
+ MUST_FEC_PROTECT, // Callee must FEC protect this data.
+ MAY_FEC_PROTECT // Callee does not have to but may FEC protect this data.
+};
+
+// Indicates FEC policy.
+enum FecPolicy {
+ FEC_PROTECT_ALWAYS, // All data in the stream should be FEC protected.
+ FEC_PROTECT_OPTIONAL // Data in the stream does not need FEC protection.
+};
+
enum QuicFrameType {
+ // Regular frame types. The values set here cannot change without the
+ // introduction of a new QUIC version.
PADDING_FRAME = 0,
- RST_STREAM_FRAME,
- CONNECTION_CLOSE_FRAME,
- GOAWAY_FRAME,
+ RST_STREAM_FRAME = 1,
+ CONNECTION_CLOSE_FRAME = 2,
+ GOAWAY_FRAME = 3,
+ WINDOW_UPDATE_FRAME = 4,
+ BLOCKED_FRAME = 5,
+ STOP_WAITING_FRAME = 6,
+ PING_FRAME = 7,
+
+ // STREAM, ACK, and CONGESTION_FEEDBACK frames are special frames. They are
+ // encoded differently on the wire and their values do not need to be stable.
STREAM_FRAME,
ACK_FRAME,
CONGESTION_FEEDBACK_FRAME,
NUM_FRAME_TYPES
};
-enum QuicGuidLength {
- PACKET_0BYTE_GUID = 0,
- PACKET_1BYTE_GUID = 1,
- PACKET_4BYTE_GUID = 4,
- PACKET_8BYTE_GUID = 8
+enum QuicConnectionIdLength {
+ PACKET_0BYTE_CONNECTION_ID = 0,
+ PACKET_1BYTE_CONNECTION_ID = 1,
+ PACKET_4BYTE_CONNECTION_ID = 4,
+ PACKET_8BYTE_CONNECTION_ID = 8
};
enum InFecGroup {
@@ -188,15 +224,15 @@ enum QuicPacketPublicFlags {
// Bit 1: Is this packet a public reset packet?
PACKET_PUBLIC_FLAGS_RST = 1 << 1,
- // Bits 2 and 3 specify the length of the GUID as follows:
+ // Bits 2 and 3 specify the length of the ConnectionId as follows:
// ----00--: 0 bytes
// ----01--: 1 byte
// ----10--: 4 bytes
// ----11--: 8 bytes
- PACKET_PUBLIC_FLAGS_0BYTE_GUID = 0,
- PACKET_PUBLIC_FLAGS_1BYTE_GUID = 1 << 2,
- PACKET_PUBLIC_FLAGS_4BYTE_GUID = 1 << 3,
- PACKET_PUBLIC_FLAGS_8BYTE_GUID = 1 << 3 | 1 << 2,
+ PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0,
+ PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID = 1 << 2,
+ PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID = 1 << 3,
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3 | 1 << 2,
// Bits 4 and 5 describe the packet sequence number length as follows:
// --00----: 1 byte
@@ -239,7 +275,12 @@ enum QuicVersion {
// Special case to indicate unknown/unsupported QUIC version.
QUIC_VERSION_UNSUPPORTED = 0,
- QUIC_VERSION_12 = 12, // Current version.
+ QUIC_VERSION_15 = 15,
+ QUIC_VERSION_16 = 16,
+ QUIC_VERSION_17 = 17,
+ QUIC_VERSION_18 = 18,
+ QUIC_VERSION_19 = 19,
+ QUIC_VERSION_20 = 20, // Current version.
};
// This vector contains QUIC versions which we currently support.
@@ -249,7 +290,12 @@ enum QuicVersion {
//
// IMPORTANT: if you are addding to this list, follow the instructions at
// http://sites/quic/adding-and-removing-versions
-static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_12};
+static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_20,
+ QUIC_VERSION_19,
+ QUIC_VERSION_18,
+ QUIC_VERSION_17,
+ QUIC_VERSION_16,
+ QUIC_VERSION_15};
typedef std::vector<QuicVersion> QuicVersionVector;
@@ -286,26 +332,26 @@ NET_EXPORT_PRIVATE std::string QuicVersionVectorToString(
// MakeQuicTag('C', 'H', 'L', 'O');
NET_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d);
+// Returns true if the tag vector contains the specified tag.
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag);
+
// Size in bytes of the data or fec packet header.
-NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicPacketHeader header);
+NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(const QuicPacketHeader& header);
NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
InFecGroup is_in_fec_group);
-// Size in bytes of the public reset packet.
-NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize();
-
// Index of the first byte in a QUIC packet of FEC protected data.
NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length);
// Index of the first byte in a QUIC packet of encrypted data.
NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length);
@@ -325,11 +371,20 @@ enum QuicRstStreamErrorCode {
QUIC_STREAM_PEER_GOING_AWAY,
// The stream has been cancelled.
QUIC_STREAM_CANCELLED,
+ // Sending a RST to allow for proper flow control accounting.
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
// No error. Used as bound while iterating.
QUIC_STREAM_LAST_ERROR,
};
+// Because receiving an unknown QuicRstStreamErrorCode results in connection
+// teardown, we use this to make sure any errors predating a given version are
+// downgraded to the most appropriate existing error.
+NET_EXPORT_PRIVATE QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version);
+
// These values must remain stable as they are uploaded to UMA histograms.
// To add a new error code, use the current value of QUIC_LAST_ERROR and
// increment QUIC_LAST_ERROR.
@@ -350,12 +405,20 @@ enum QuicErrorCode {
QUIC_INVALID_FEC_DATA = 5,
// STREAM frame data is malformed.
QUIC_INVALID_STREAM_DATA = 46,
+ // STREAM frame data is not encrypted.
+ QUIC_UNENCRYPTED_STREAM_DATA = 61,
// RST_STREAM frame data is malformed.
QUIC_INVALID_RST_STREAM_DATA = 6,
// CONNECTION_CLOSE frame data is malformed.
QUIC_INVALID_CONNECTION_CLOSE_DATA = 7,
// GOAWAY frame data is malformed.
QUIC_INVALID_GOAWAY_DATA = 8,
+ // WINDOW_UPDATE frame data is malformed.
+ QUIC_INVALID_WINDOW_UPDATE_DATA = 57,
+ // BLOCKED frame data is malformed.
+ QUIC_INVALID_BLOCKED_DATA = 58,
+ // STOP_WAITING frame data is malformed.
+ QUIC_INVALID_STOP_WAITING_DATA = 60,
// ACK frame data is malformed.
QUIC_INVALID_ACK_DATA = 9,
// CONGESTION_FEEDBACK frame data is malformed.
@@ -384,8 +447,9 @@ enum QuicErrorCode {
QUIC_PUBLIC_RESET = 19,
// Invalid protocol version.
QUIC_INVALID_VERSION = 20,
- // Stream reset before headers decompressed.
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21,
+
+ // deprecated: QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21
+
// The Header ID for a stream was too far from the previous.
QUIC_INVALID_HEADER_ID = 22,
// Negotiable parameter received during handshake had invalid value.
@@ -402,7 +466,16 @@ enum QuicErrorCode {
QUIC_PACKET_READ_ERROR = 51,
// We received a STREAM_FRAME with no data and no fin flag set.
QUIC_INVALID_STREAM_FRAME = 50,
-
+ // We received invalid data on the headers stream.
+ QUIC_INVALID_HEADERS_STREAM_DATA = 56,
+ // The peer received too much data, violating flow control.
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59,
+ // The peer sent too much data, violating flow control.
+ QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63,
+ // The peer received an invalid flow control window.
+ QUIC_FLOW_CONTROL_INVALID_WINDOW = 64,
+ // The connection has been IP pooled into an existing connection.
+ QUIC_CONNECTION_IP_POOLED = 62,
// Crypto errors.
@@ -458,7 +531,7 @@ enum QuicErrorCode {
QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 56,
+ QUIC_LAST_ERROR = 65,
};
struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
@@ -466,9 +539,10 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other);
~QuicPacketPublicHeader();
- // Universal header. All QuicPacket headers will have a guid and public flags.
- QuicGuid guid;
- QuicGuidLength guid_length;
+ // Universal header. All QuicPacket headers will have a connection_id and
+ // public flags.
+ QuicConnectionId connection_id;
+ QuicConnectionIdLength connection_id_length;
bool reset_flag;
bool version_flag;
QuicSequenceNumberLength sequence_number_length;
@@ -493,12 +567,13 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader {
};
struct NET_EXPORT_PRIVATE QuicPublicResetPacket {
- QuicPublicResetPacket() {}
- explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header)
- : public_header(header) {}
+ QuicPublicResetPacket();
+ explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header);
+
QuicPacketPublicHeader public_header;
- QuicPacketSequenceNumber rejected_sequence_number;
QuicPublicResetNonceProof nonce_proof;
+ QuicPacketSequenceNumber rejected_sequence_number;
+ IPEndPoint client_address;
};
enum QuicVersionNegotiationState {
@@ -521,6 +596,11 @@ typedef QuicPacketPublicHeader QuicVersionNegotiationPacket;
struct NET_EXPORT_PRIVATE QuicPaddingFrame {
};
+// A ping frame contains no payload, though it is retransmittable,
+// and ACK'd just like other normal frames.
+struct NET_EXPORT_PRIVATE QuicPingFrame {
+};
+
struct NET_EXPORT_PRIVATE QuicStreamFrame {
QuicStreamFrame();
QuicStreamFrame(const QuicStreamFrame& frame);
@@ -529,6 +609,9 @@ struct NET_EXPORT_PRIVATE QuicStreamFrame {
QuicStreamOffset offset,
IOVector data);
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicStreamFrame& s);
+
// Returns a copy of the IOVector |data| as a heap-allocated string.
// Caller must take ownership of the returned string.
std::string* GetDataAsString() const;
@@ -552,6 +635,7 @@ typedef std::map<QuicPacketSequenceNumber, QuicTime> TimeMap;
struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
ReceivedPacketInfo();
~ReceivedPacketInfo();
+
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const ReceivedPacketInfo& s);
@@ -580,6 +664,10 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
// Whether the ack had to be truncated when sent.
bool is_truncated;
+
+ // Packets which have been revived via FEC.
+ // All of these must also be in missing_packets.
+ SequenceNumberSet revived_packets;
};
// True if the sequence number is greater than largest_observed or is listed
@@ -595,11 +683,12 @@ void NET_EXPORT_PRIVATE InsertMissingPacketsBetween(
QuicPacketSequenceNumber lower,
QuicPacketSequenceNumber higher);
-struct NET_EXPORT_PRIVATE SentPacketInfo {
- SentPacketInfo();
- ~SentPacketInfo();
+struct NET_EXPORT_PRIVATE QuicStopWaitingFrame {
+ QuicStopWaitingFrame();
+ ~QuicStopWaitingFrame();
+
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
- std::ostream& os, const SentPacketInfo& s);
+ std::ostream& os, const QuicStopWaitingFrame& s);
// Entropy hash of all packets up to, but not including, the least unacked
// packet.
@@ -609,17 +698,12 @@ struct NET_EXPORT_PRIVATE SentPacketInfo {
};
struct NET_EXPORT_PRIVATE QuicAckFrame {
- QuicAckFrame() {}
- // Testing convenience method to construct a QuicAckFrame with all packets
- // from least_unacked to largest_observed acked.
- QuicAckFrame(QuicPacketSequenceNumber largest_observed,
- QuicTime largest_observed_receive_time,
- QuicPacketSequenceNumber least_unacked);
+ QuicAckFrame();
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const QuicAckFrame& s);
- SentPacketInfo sent_info;
+ QuicStopWaitingFrame sent_info;
ReceivedPacketInfo received_info;
};
@@ -630,17 +714,24 @@ enum CongestionFeedbackType {
kTCP, // Used to mimic TCP.
kInterArrival, // Use additional inter arrival information.
kFixRate, // Provided for testing.
+ kTCPBBR, // BBR implementation based on TCP congestion feedback.
+};
+
+enum LossDetectionType {
+ kNack, // Used to mimic TCP's loss detection.
+ kTime, // Time based loss detection.
};
struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP {
- uint16 accumulated_number_of_lost_packets;
+ CongestionFeedbackMessageTCP();
+
QuicByteCount receive_window;
};
struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival {
CongestionFeedbackMessageInterArrival();
~CongestionFeedbackMessageInterArrival();
- uint16 accumulated_number_of_lost_packets;
+
// The set of received packets since the last feedback was sent, along with
// their arrival times.
TimeMap received_packet_times;
@@ -667,33 +758,86 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame {
};
struct NET_EXPORT_PRIVATE QuicRstStreamFrame {
- QuicRstStreamFrame() {}
- QuicRstStreamFrame(QuicStreamId stream_id, QuicRstStreamErrorCode error_code)
- : stream_id(stream_id), error_code(error_code) {
- DCHECK_LE(error_code, std::numeric_limits<uint8>::max());
- }
+ QuicRstStreamFrame();
+ QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicRstStreamFrame& r);
QuicStreamId stream_id;
QuicRstStreamErrorCode error_code;
std::string error_details;
+
+ // Used to update flow control windows. On termination of a stream, both
+ // endpoints must inform the peer of the number of bytes they have sent on
+ // that stream. This can be done through normal termination (data packet with
+ // FIN) or through a RST.
+ QuicStreamOffset byte_offset;
};
struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame {
+ QuicConnectionCloseFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConnectionCloseFrame& c);
+
QuicErrorCode error_code;
std::string error_details;
};
struct NET_EXPORT_PRIVATE QuicGoAwayFrame {
- QuicGoAwayFrame() {}
+ QuicGoAwayFrame();
QuicGoAwayFrame(QuicErrorCode error_code,
QuicStreamId last_good_stream_id,
const std::string& reason);
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicGoAwayFrame& g);
+
QuicErrorCode error_code;
QuicStreamId last_good_stream_id;
std::string reason_phrase;
};
+// Flow control updates per-stream and at the connection levoel.
+// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather
+// than a window delta.
+// TODO(rjshade): A possible future optimization is to make stream_id and
+// byte_offset variable length, similar to stream frames.
+struct NET_EXPORT_PRIVATE QuicWindowUpdateFrame {
+ QuicWindowUpdateFrame() {}
+ QuicWindowUpdateFrame(QuicStreamId stream_id, QuicStreamOffset byte_offset);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicWindowUpdateFrame& w);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+
+ // Byte offset in the stream or connection. The receiver of this frame must
+ // not send data which would result in this offset being exceeded.
+ QuicStreamOffset byte_offset;
+};
+
+// The BLOCKED frame is used to indicate to the remote endpoint that this
+// endpoint believes itself to be flow-control blocked but otherwise ready to
+// send data. The BLOCKED frame is purely advisory and optional.
+// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28).
+struct NET_EXPORT_PRIVATE QuicBlockedFrame {
+ QuicBlockedFrame() {}
+ explicit QuicBlockedFrame(QuicStreamId stream_id);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicBlockedFrame& b);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+};
+
// EncryptionLevel enumerates the stages of encryption that a QUIC connection
// progresses through. When retransmitting a packet, the encryption level needs
// to be specified so that it is retransmitted at a level which the peer can
@@ -707,35 +851,21 @@ enum EncryptionLevel {
};
struct NET_EXPORT_PRIVATE QuicFrame {
- QuicFrame() {}
- explicit QuicFrame(QuicPaddingFrame* padding_frame)
- : type(PADDING_FRAME),
- padding_frame(padding_frame) {
- }
- explicit QuicFrame(QuicStreamFrame* stream_frame)
- : type(STREAM_FRAME),
- stream_frame(stream_frame) {
- }
- explicit QuicFrame(QuicAckFrame* frame)
- : type(ACK_FRAME),
- ack_frame(frame) {
- }
- explicit QuicFrame(QuicCongestionFeedbackFrame* frame)
- : type(CONGESTION_FEEDBACK_FRAME),
- congestion_feedback_frame(frame) {
- }
- explicit QuicFrame(QuicRstStreamFrame* frame)
- : type(RST_STREAM_FRAME),
- rst_stream_frame(frame) {
- }
- explicit QuicFrame(QuicConnectionCloseFrame* frame)
- : type(CONNECTION_CLOSE_FRAME),
- connection_close_frame(frame) {
- }
- explicit QuicFrame(QuicGoAwayFrame* frame)
- : type(GOAWAY_FRAME),
- goaway_frame(frame) {
- }
+ QuicFrame();
+ explicit QuicFrame(QuicPaddingFrame* padding_frame);
+ explicit QuicFrame(QuicStreamFrame* stream_frame);
+ explicit QuicFrame(QuicAckFrame* frame);
+ explicit QuicFrame(QuicCongestionFeedbackFrame* frame);
+ explicit QuicFrame(QuicRstStreamFrame* frame);
+ explicit QuicFrame(QuicConnectionCloseFrame* frame);
+ explicit QuicFrame(QuicStopWaitingFrame* frame);
+ explicit QuicFrame(QuicPingFrame* frame);
+ explicit QuicFrame(QuicGoAwayFrame* frame);
+ explicit QuicFrame(QuicWindowUpdateFrame* frame);
+ explicit QuicFrame(QuicBlockedFrame* frame);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicFrame& frame);
QuicFrameType type;
union {
@@ -743,9 +873,13 @@ struct NET_EXPORT_PRIVATE QuicFrame {
QuicStreamFrame* stream_frame;
QuicAckFrame* ack_frame;
QuicCongestionFeedbackFrame* congestion_feedback_frame;
+ QuicStopWaitingFrame* stop_waiting_frame;
+ QuicPingFrame* ping_frame;
QuicRstStreamFrame* rst_stream_frame;
QuicConnectionCloseFrame* connection_close_frame;
QuicGoAwayFrame* goaway_frame;
+ QuicWindowUpdateFrame* window_update_frame;
+ QuicBlockedFrame* blocked_frame;
};
};
@@ -763,16 +897,8 @@ struct NET_EXPORT_PRIVATE QuicFecData {
class NET_EXPORT_PRIVATE QuicData {
public:
- QuicData(const char* buffer, size_t length)
- : buffer_(buffer),
- length_(length),
- owns_buffer_(false) {}
-
- QuicData(char* buffer, size_t length, bool owns_buffer)
- : buffer_(buffer),
- length_(length),
- owns_buffer_(owns_buffer) {}
-
+ QuicData(const char* buffer, size_t length);
+ QuicData(char* buffer, size_t length, bool owns_buffer);
virtual ~QuicData();
base::StringPiece AsStringPiece() const {
@@ -796,10 +922,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return new QuicPacket(buffer, length, owns_buffer, guid_length,
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
includes_version, sequence_number_length, false);
}
@@ -807,10 +933,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return new QuicPacket(buffer, length, owns_buffer, guid_length,
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
includes_version, sequence_number_length, true);
}
@@ -827,20 +953,14 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
QuicPacket(char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length,
- bool is_fec_packet)
- : QuicData(buffer, length, owns_buffer),
- buffer_(buffer),
- is_fec_packet_(is_fec_packet),
- guid_length_(guid_length),
- includes_version_(includes_version),
- sequence_number_length_(sequence_number_length) {}
+ bool is_fec_packet);
char* buffer_;
const bool is_fec_packet_;
- const QuicGuidLength guid_length_;
+ const QuicConnectionIdLength connection_id_length_;
const bool includes_version_;
const QuicSequenceNumberLength sequence_number_length_;
@@ -849,11 +969,8 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
public:
- QuicEncryptedPacket(const char* buffer, size_t length)
- : QuicData(buffer, length) {}
-
- QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer)
- : QuicData(buffer, length, owns_buffer) {}
+ QuicEncryptedPacket(const char* buffer, size_t length);
+ QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer);
// Clones the packet into a new packet which owns the buffer.
QuicEncryptedPacket* Clone() const;
@@ -882,6 +999,8 @@ class NET_EXPORT_PRIVATE RetransmittableFrames {
const QuicFrame& AddNonStreamFrame(const QuicFrame& frame);
const QuicFrames& frames() const { return frames_; }
+ IsHandshake HasCryptoHandshake() const;
+
void set_encryption_level(EncryptionLevel level);
EncryptionLevel encryption_level() const {
return encryption_level_;
@@ -914,43 +1033,38 @@ struct NET_EXPORT_PRIVATE SerializedPacket {
std::set<QuicAckNotifier*> notifiers;
};
-// A struct for functions which consume data payloads and fins.
-struct QuicConsumedData {
- QuicConsumedData(size_t bytes_consumed, bool fin_consumed)
- : bytes_consumed(bytes_consumed),
- fin_consumed(fin_consumed) {}
- // By default, gtest prints the raw bytes of an object. The bool data
- // member causes this object to have padding bytes, which causes the
- // default gtest object printer to read uninitialize memory. So we need
- // to teach gtest how to print this object.
- NET_EXPORT_PRIVATE friend std::ostream& operator<<(
- std::ostream& os, const QuicConsumedData& s);
-
- // How many bytes were consumed.
- size_t bytes_consumed;
+struct NET_EXPORT_PRIVATE TransmissionInfo {
+ // Used by STL when assigning into a map.
+ TransmissionInfo();
- // True if an incoming fin was consumed.
- bool fin_consumed;
-};
-
-enum WriteStatus {
- WRITE_STATUS_OK,
- WRITE_STATUS_BLOCKED,
- WRITE_STATUS_ERROR,
-};
+ // Constructs a Transmission with a new all_tranmissions set
+ // containing |sequence_number|.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length);
-// A struct used to return the result of write calls including either the number
-// of bytes written or the error code, depending upon the status.
-struct NET_EXPORT_PRIVATE WriteResult {
- WriteResult(WriteStatus status, int bytes_written_or_error_code) :
- status(status), bytes_written(bytes_written_or_error_code) {
- }
+ // Constructs a Transmission with the specified |all_tranmissions| set
+ // and inserts |sequence_number| into it.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberSet* all_transmissions);
- WriteStatus status;
- union {
- int bytes_written; // only valid when status is OK
- int error_code; // only valid when status is ERROR
- };
+ RetransmittableFrames* retransmittable_frames;
+ QuicSequenceNumberLength sequence_number_length;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicTime sent_time;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicByteCount bytes_sent;
+ size_t nack_count;
+ // Reason why this packet was transmitted.
+ TransmissionType transmission_type;
+ // Stores the sequence numbers of all transmissions of this packet.
+ // Can never be null.
+ SequenceNumberSet* all_transmissions;
+ // In flight packets have not been abandoned or lost.
+ bool in_flight;
};
} // namespace net
diff --git a/chromium/net/quic/quic_protocol_test.cc b/chromium/net/quic/quic_protocol_test.cc
index bf1fece981c..b1eae45d251 100644
--- a/chromium/net/quic/quic_protocol_test.cc
+++ b/chromium/net/quic/quic_protocol_test.cc
@@ -11,6 +11,19 @@ namespace net {
namespace test {
namespace {
+TEST(QuicProtocolTest, AdjustErrorForVersion) {
+ ASSERT_EQ(8, QUIC_STREAM_LAST_ERROR)
+ << "Any additions to QuicRstStreamErrorCode require an addition to "
+ << "AdjustErrorForVersion and this associated test.";
+
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ QUIC_VERSION_17));
+ EXPECT_EQ(QUIC_RST_FLOW_CONTROL_ACCOUNTING, AdjustErrorForVersion(
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ static_cast<QuicVersion>(QUIC_VERSION_17 + 1)));
+}
+
TEST(QuicProtocolTest, MakeQuicTag) {
QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D');
char bytes[4];
@@ -56,8 +69,8 @@ TEST(QuicProtocolTest, QuicVersionToQuicTag) {
#endif
// Explicitly test a specific version.
- EXPECT_EQ(MakeQuicTag('Q', '0', '1', '2'),
- QuicVersionToQuicTag(QUIC_VERSION_12));
+ EXPECT_EQ(MakeQuicTag('Q', '0', '1', '6'),
+ QuicVersionToQuicTag(QUIC_VERSION_16));
// 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 +108,8 @@ TEST(QuicProtocolTest, QuicTagToQuicVersion) {
#endif
// Explicitly test specific versions.
- EXPECT_EQ(QUIC_VERSION_12,
- QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '2')));
+ EXPECT_EQ(QUIC_VERSION_16,
+ QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '6')));
for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
QuicVersion version = kSupportedQuicVersions[i];
@@ -127,23 +140,23 @@ TEST(QuicProtocolTest, QuicTagToQuicVersionUnsupported) {
}
TEST(QuicProtocolTest, QuicVersionToString) {
- EXPECT_EQ("QUIC_VERSION_12", QuicVersionToString(QUIC_VERSION_12));
+ EXPECT_EQ("QUIC_VERSION_16", QuicVersionToString(QUIC_VERSION_16));
EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
- QuicVersion single_version[] = {QUIC_VERSION_12};
+ QuicVersion single_version[] = {QUIC_VERSION_16};
QuicVersionVector versions_vector;
for (size_t i = 0; i < arraysize(single_version); ++i) {
versions_vector.push_back(single_version[i]);
}
- EXPECT_EQ("QUIC_VERSION_12", QuicVersionVectorToString(versions_vector));
+ EXPECT_EQ("QUIC_VERSION_16", QuicVersionVectorToString(versions_vector));
- QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_12};
+ QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_16};
versions_vector.clear();
for (size_t i = 0; i < arraysize(multiple_versions); ++i) {
versions_vector.push_back(multiple_versions[i]);
}
- EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_12",
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_16",
QuicVersionVectorToString(versions_vector));
// Make sure that all supported versions are present in QuicVersionToString.
diff --git a/chromium/net/quic/quic_received_packet_manager.cc b/chromium/net/quic/quic_received_packet_manager.cc
index b24ac7c671e..3b47687beb6 100644
--- a/chromium/net/quic/quic_received_packet_manager.cc
+++ b/chromium/net/quic/quic_received_packet_manager.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_connection_stats.h"
using std::make_pair;
using std::max;
@@ -25,15 +26,115 @@ const size_t kMaxPacketsAfterNewMissing = 4;
}
+QuicReceivedPacketManager::EntropyTracker::EntropyTracker()
+ : packets_entropy_hash_(0),
+ first_gap_(1),
+ largest_observed_(0) {
+}
+
+QuicReceivedPacketManager::EntropyTracker::~EntropyTracker() {}
+
+QuicPacketEntropyHash QuicReceivedPacketManager::EntropyTracker::EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number == largest_observed_) {
+ return packets_entropy_hash_;
+ }
+
+ DCHECK_GE(sequence_number, first_gap_);
+ ReceivedEntropyMap::const_iterator it =
+ packets_entropy_.upper_bound(sequence_number);
+ // When this map is empty we should only query entropy for
+ // largest_observed_, since no other entropy can be correctly
+ // calculated, because we're not storing the entropy for any prior packets.
+ // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium.
+ // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10)
+ LOG_IF(DFATAL, it == packets_entropy_.end())
+ << "EntropyHash may be unknown. largest_received: "
+ << largest_observed_
+ << " sequence_number: " << sequence_number;
+
+ // TODO(satyamshekhar): Make this O(1).
+ QuicPacketEntropyHash hash = packets_entropy_hash_;
+ for (; it != packets_entropy_.end(); ++it) {
+ hash ^= it->second;
+ }
+ return hash;
+}
+
+void QuicReceivedPacketManager::EntropyTracker::RecordPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring received packet entropy for sequence_number:"
+ << sequence_number << " less than largest_peer_sequence_number:"
+ << first_gap_;
+ return;
+ }
+
+ if (sequence_number > largest_observed_) {
+ largest_observed_ = sequence_number;
+ }
+
+ packets_entropy_hash_ ^= entropy_hash;
+ DVLOG(2) << "setting cumulative received entropy hash to: "
+ << static_cast<int>(packets_entropy_hash_)
+ << " updated with sequence number " << sequence_number
+ << " entropy hash: " << static_cast<int>(entropy_hash);
+
+ packets_entropy_.insert(make_pair(sequence_number, entropy_hash));
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+}
+
+void QuicReceivedPacketManager::EntropyTracker::SetCumulativeEntropyUpTo(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring set entropy at:" << sequence_number
+ << " less than first_gap_:" << first_gap_;
+ return;
+ }
+ // Compute the current entropy based on the hash.
+ packets_entropy_hash_ = entropy_hash;
+ ReceivedEntropyMap::iterator it =
+ packets_entropy_.lower_bound(sequence_number);
+ // TODO(satyamshekhar): Make this O(1).
+ for (; it != packets_entropy_.end(); ++it) {
+ packets_entropy_hash_ ^= it->second;
+ }
+ // Update first_gap_ and discard old entropies.
+ first_gap_ = sequence_number;
+ packets_entropy_.erase(
+ packets_entropy_.begin(),
+ packets_entropy_.lower_bound(sequence_number));
+
+ // Garbage collect entries from the beginning of the map.
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+}
+
+void QuicReceivedPacketManager::EntropyTracker::
+AdvanceFirstGapAndGarbageCollectEntropyMap() {
+ while (!packets_entropy_.empty()) {
+ ReceivedEntropyMap::iterator it = packets_entropy_.begin();
+ if (it->first != first_gap_) {
+ DCHECK_GT(it->first, first_gap_);
+ break;
+ }
+ packets_entropy_.erase(it);
+ ++first_gap_;
+ }
+}
+
QuicReceivedPacketManager::QuicReceivedPacketManager(
- CongestionFeedbackType congestion_type)
- : packets_entropy_hash_(0),
- largest_sequence_number_(0),
- peer_largest_observed_packet_(0),
+ CongestionFeedbackType congestion_type,
+ QuicConnectionStats* stats)
+ : peer_largest_observed_packet_(0),
least_packet_awaited_by_peer_(1),
peer_least_packet_awaiting_ack_(0),
time_largest_observed_(QuicTime::Zero()),
- receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)) {
+ receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)),
+ stats_(stats) {
received_info_.largest_observed = 0;
received_info_.entropy_hash = 0;
}
@@ -43,33 +144,46 @@ QuicReceivedPacketManager::~QuicReceivedPacketManager() {}
void QuicReceivedPacketManager::RecordPacketReceived(
QuicByteCount bytes,
const QuicPacketHeader& header,
- QuicTime receipt_time,
- bool revived) {
+ QuicTime receipt_time) {
QuicPacketSequenceNumber sequence_number = header.packet_sequence_number;
DCHECK(IsAwaitingPacket(sequence_number));
InsertMissingPacketsBetween(
&received_info_,
max(received_info_.largest_observed + 1, peer_least_packet_awaiting_ack_),
- header.packet_sequence_number);
+ sequence_number);
- if (received_info_.largest_observed > header.packet_sequence_number) {
+ if (received_info_.largest_observed > sequence_number) {
// We've gotten one of the out of order packets - remove it from our
// "missing packets" list.
DVLOG(1) << "Removing " << sequence_number << " from missing list";
received_info_.missing_packets.erase(sequence_number);
+
+ // Record how out of order stats.
+ ++stats_->packets_reordered;
+ uint32 sequence_gap = received_info_.largest_observed - sequence_number;
+ stats_->max_sequence_reordering =
+ max(stats_->max_sequence_reordering, sequence_gap);
+ uint32 reordering_time_us =
+ receipt_time.Subtract(time_largest_observed_).ToMicroseconds();
+ stats_->max_time_reordering_us = max(stats_->max_time_reordering_us,
+ reordering_time_us);
}
- if (header.packet_sequence_number > received_info_.largest_observed) {
- received_info_.largest_observed = header.packet_sequence_number;
+ if (sequence_number > received_info_.largest_observed) {
+ received_info_.largest_observed = sequence_number;
time_largest_observed_ = receipt_time;
}
- RecordPacketEntropyHash(sequence_number, header.entropy_hash);
+ entropy_tracker_.RecordPacketEntropyHash(sequence_number,
+ header.entropy_hash);
- // Don't update the receive algorithm for revived packets.
- if (!revived) {
- receive_algorithm_->RecordIncomingPacket(
- bytes, sequence_number, receipt_time, revived);
- }
+ receive_algorithm_->RecordIncomingPacket(
+ bytes, sequence_number, receipt_time);
+}
+
+void QuicReceivedPacketManager::RecordPacketRevived(
+ QuicPacketSequenceNumber sequence_number) {
+ LOG_IF(DFATAL, !IsAwaitingPacket(sequence_number));
+ received_info_.revived_packets.insert(sequence_number);
}
bool QuicReceivedPacketManager::IsMissing(
@@ -104,23 +218,6 @@ void QuicReceivedPacketManager::UpdateReceivedPacketInfo(
approximate_now.Subtract(time_largest_observed_);
}
-void QuicReceivedPacketManager::RecordPacketEntropyHash(
- QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash) {
- if (sequence_number < largest_sequence_number_) {
- DVLOG(1) << "Ignoring received packet entropy for sequence_number:"
- << sequence_number << " less than largest_peer_sequence_number:"
- << largest_sequence_number_;
- return;
- }
- packets_entropy_.insert(make_pair(sequence_number, entropy_hash));
- packets_entropy_hash_ ^= entropy_hash;
- DVLOG(2) << "setting cumulative received entropy hash to: "
- << static_cast<int>(packets_entropy_hash_)
- << " updated with sequence number " << sequence_number
- << " entropy hash: " << static_cast<int>(entropy_hash);
-}
-
bool QuicReceivedPacketManager::GenerateCongestionFeedback(
QuicCongestionFeedbackFrame* feedback) {
return receive_algorithm_->GenerateCongestionFeedback(feedback);
@@ -128,74 +225,27 @@ bool QuicReceivedPacketManager::GenerateCongestionFeedback(
QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash(
QuicPacketSequenceNumber sequence_number) const {
- DCHECK_LE(sequence_number, received_info_.largest_observed);
- DCHECK_GE(sequence_number, largest_sequence_number_);
- if (sequence_number == received_info_.largest_observed) {
- return packets_entropy_hash_;
- }
-
- ReceivedEntropyMap::const_iterator it =
- packets_entropy_.upper_bound(sequence_number);
- // When this map is empty we should only query entropy for
- // received_info_.largest_observed, since no other entropy can be correctly
- // calculated, because we're not storing the entropy for any prior packets.
- // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium.
- // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10)
- LOG_IF(DFATAL, it == packets_entropy_.end())
- << "EntropyHash may be unknown. largest_received: "
- << received_info_.largest_observed
- << " sequence_number: " << sequence_number;
-
- // TODO(satyamshekhar): Make this O(1).
- QuicPacketEntropyHash hash = packets_entropy_hash_;
- for (; it != packets_entropy_.end(); ++it) {
- hash ^= it->second;
- }
- return hash;
-}
-
-void QuicReceivedPacketManager::RecalculateEntropyHash(
- QuicPacketSequenceNumber peer_least_unacked,
- QuicPacketEntropyHash entropy_hash) {
- DCHECK_LE(peer_least_unacked, received_info_.largest_observed);
- if (peer_least_unacked < largest_sequence_number_) {
- DVLOG(1) << "Ignoring received peer_least_unacked:" << peer_least_unacked
- << " less than largest_peer_sequence_number:"
- << largest_sequence_number_;
- return;
- }
- largest_sequence_number_ = peer_least_unacked;
- packets_entropy_hash_ = entropy_hash;
- ReceivedEntropyMap::iterator it =
- packets_entropy_.lower_bound(peer_least_unacked);
- // TODO(satyamshekhar): Make this O(1).
- for (; it != packets_entropy_.end(); ++it) {
- packets_entropy_hash_ ^= it->second;
- }
- // Discard entropies before least unacked.
- packets_entropy_.erase(
- packets_entropy_.begin(),
- packets_entropy_.lower_bound(
- min(peer_least_unacked, received_info_.largest_observed)));
+ return entropy_tracker_.EntropyHash(sequence_number);
}
void QuicReceivedPacketManager::UpdatePacketInformationReceivedByPeer(
- const QuicAckFrame& incoming_ack) {
+ const ReceivedPacketInfo& received_info) {
// ValidateAck should fail if largest_observed ever shrinks.
- DCHECK_LE(peer_largest_observed_packet_,
- incoming_ack.received_info.largest_observed);
- peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed;
+ DCHECK_LE(peer_largest_observed_packet_, received_info.largest_observed);
+ peer_largest_observed_packet_ = received_info.largest_observed;
- if (incoming_ack.received_info.missing_packets.empty()) {
+ if (received_info.missing_packets.empty()) {
least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1;
} else {
- least_packet_awaited_by_peer_ =
- *(incoming_ack.received_info.missing_packets.begin());
+ least_packet_awaited_by_peer_ = *(received_info.missing_packets.begin());
}
}
bool QuicReceivedPacketManager::DontWaitForPacketsBefore(
QuicPacketSequenceNumber least_unacked) {
+ received_info_.revived_packets.erase(
+ received_info_.revived_packets.begin(),
+ received_info_.revived_packets.lower_bound(least_unacked));
size_t missing_packets_count = received_info_.missing_packets.size();
received_info_.missing_packets.erase(
received_info_.missing_packets.begin(),
@@ -204,22 +254,19 @@ bool QuicReceivedPacketManager::DontWaitForPacketsBefore(
}
void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer(
- const QuicAckFrame& incoming_ack) {
+ const QuicStopWaitingFrame& stop_waiting) {
// ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks.
- DCHECK_LE(peer_least_packet_awaiting_ack_,
- incoming_ack.sent_info.least_unacked);
- if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) {
- bool missed_packets =
- DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked);
- if (missed_packets || incoming_ack.sent_info.least_unacked >
- received_info_.largest_observed + 1) {
+ DCHECK_LE(peer_least_packet_awaiting_ack_, stop_waiting.least_unacked);
+ if (stop_waiting.least_unacked > peer_least_packet_awaiting_ack_) {
+ bool missed_packets = DontWaitForPacketsBefore(stop_waiting.least_unacked);
+ if (missed_packets) {
DVLOG(1) << "Updating entropy hashed since we missed packets";
// There were some missing packets that we won't ever get now. Recalculate
// the received entropy hash.
- RecalculateEntropyHash(incoming_ack.sent_info.least_unacked,
- incoming_ack.sent_info.entropy_hash);
+ entropy_tracker_.SetCumulativeEntropyUpTo(stop_waiting.least_unacked,
+ stop_waiting.entropy_hash);
}
- peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked;
+ peer_least_packet_awaiting_ack_ = stop_waiting.least_unacked;
}
DCHECK(received_info_.missing_packets.empty() ||
*received_info_.missing_packets.begin() >=
diff --git a/chromium/net/quic/quic_received_packet_manager.h b/chromium/net/quic/quic_received_packet_manager.h
index 9e2fb59c241..1b283316e33 100644
--- a/chromium/net/quic/quic_received_packet_manager.h
+++ b/chromium/net/quic/quic_received_packet_manager.h
@@ -15,29 +15,98 @@
namespace net {
namespace test {
+class EntropyTrackerPeer;
class QuicConnectionPeer;
class QuicReceivedPacketManagerPeer;
} // namespace test
+struct QuicConnectionStats;
+
// Records all received packets by a connection and tracks their entropy.
// Also calculates the correct entropy for the framer when it truncates an ack
// frame being serialized.
class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
public QuicReceivedEntropyHashCalculatorInterface {
public:
- explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type);
+ class NET_EXPORT_PRIVATE EntropyTracker {
+ public:
+ EntropyTracker();
+ ~EntropyTracker();
+
+ // Compute the XOR of the entropy of all received packets up to
+ // and including sequence_number.
+ // Requires that either:
+ // sequence_number == largest_observed_
+ // or:
+ // sequence_number > first_gap_ &&
+ // sequence_number < largest_observed_ &&
+ // sequence_number in packets_entropy_
+ QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Record the received entropy hash against |sequence_number|.
+ // Performs garbage collection to advance first_gap_ if
+ // sequence_number == first_gap_.
+ void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Sets the entropy hash up to but not including a sequence number based
+ // on the hash provided by a StopWaiting frame. Clears older packet
+ // entropy entries and performs garbage collection up to the first gap.
+ void SetCumulativeEntropyUpTo(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ private:
+ friend class test::EntropyTrackerPeer;
+
+ typedef std::map<QuicPacketSequenceNumber,
+ QuicPacketEntropyHash> ReceivedEntropyMap;
+
+ // Recomputes first_gap_ and removes packets_entropy_ entries that are no
+ // longer needed to compute EntropyHash.
+ void AdvanceFirstGapAndGarbageCollectEntropyMap();
+
+ // TODO(satyamshekhar): Can be optimized using an interval set like data
+ // structure.
+ // Map of received sequence numbers to their corresponding entropy.
+ // Stores an entry for every received packet whose sequence_number is larger
+ // than first_gap_. Packets without the entropy bit set have an entropy
+ // value of 0.
+ // TODO(ianswett): When the entropy flag is off, the entropy
+ // should not be 0.
+ ReceivedEntropyMap packets_entropy_;
+
+ // Cumulative hash of entropy of all received packets.
+ QuicPacketEntropyHash packets_entropy_hash_;
+
+ // Sequence number of the first packet that we do not know the entropy of.
+ // If there are no gaps in the received packet sequence,
+ // packets_entropy_ will be empty and first_gap_ will be equal to
+ // 'largest_observed_ + 1' since that's the first packet for which
+ // entropy is unknown. If there are gaps, packets_entropy_ will
+ // contain entries for all received packets with sequence_number >
+ // first_gap_.
+ QuicPacketSequenceNumber first_gap_;
+
+ // Sequence number of the largest observed packet.
+ QuicPacketSequenceNumber largest_observed_;
+
+ DISALLOW_COPY_AND_ASSIGN(EntropyTracker);
+ };
+
+ explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type,
+ QuicConnectionStats* stats);
virtual ~QuicReceivedPacketManager();
// Updates the internal state concerning which packets have been received.
// bytes: the packet size in bytes including Quic Headers.
// header: the packet header.
// timestamp: the arrival time of the packet.
- // revived: true if the packet was lost and then recovered with help of a
- // FEC packet.
void RecordPacketReceived(QuicByteCount bytes,
const QuicPacketHeader& header,
- QuicTime receipt_time,
- bool revived);
+ QuicTime receipt_time);
+
+ void RecordPacketRevived(QuicPacketSequenceNumber sequence_number);
// Checks whether |sequence_number| is missing and less than largest observed.
bool IsMissing(QuicPacketSequenceNumber sequence_number);
@@ -62,12 +131,12 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
virtual QuicPacketEntropyHash EntropyHash(
QuicPacketSequenceNumber sequence_number) const OVERRIDE;
- // These two are called by OnAckFrame.
- //
- // Updates internal state based on |incoming_ack.received_info|.
- void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& incoming_ack);
- // Updates internal state based on |incoming_ack.sent_info|.
- void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack);
+ // Updates internal state based on |received_info|.
+ void UpdatePacketInformationReceivedByPeer(
+ const ReceivedPacketInfo& received_nfo);
+ // Updates internal state based on |stop_waiting|.
+ void UpdatePacketInformationSentByPeer(
+ const QuicStopWaitingFrame& stop_waiting);
// Returns whether the peer is missing packets.
bool HasMissingPackets();
@@ -92,40 +161,14 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
friend class test::QuicConnectionPeer;
friend class test::QuicReceivedPacketManagerPeer;
- typedef std::map<QuicPacketSequenceNumber,
- QuicPacketEntropyHash> ReceivedEntropyMap;
-
- // Record the received entropy hash against |sequence_number|.
- void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash);
-
- // Recalculate the entropy hash and clears old packet entropies,
- // now that the sender sent us the |entropy_hash| for packets up to,
- // but not including, |peer_least_unacked|.
- void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked,
- QuicPacketEntropyHash entropy_hash);
-
// Deletes all missing packets before least unacked. The connection won't
// process any packets with sequence number before |least_unacked| that it
// received after this call. Returns true if there were missing packets before
// |least_unacked| unacked, false otherwise.
bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked);
- // TODO(satyamshekhar): Can be optimized using an interval set like data
- // structure.
- // Map of received sequence numbers to their corresponding entropy.
- // Every received packet has an entry, and packets without the entropy bit set
- // have an entropy value of 0.
- // TODO(ianswett): When the entropy flag is off, the entropy should not be 0.
- ReceivedEntropyMap packets_entropy_;
-
- // Cumulative hash of entropy of all received packets.
- QuicPacketEntropyHash packets_entropy_hash_;
-
- // The largest sequence number cleared by RecalculateEntropyHash.
- // Received entropy cannot be calculated for numbers less than it.
- QuicPacketSequenceNumber largest_sequence_number_;
-
+ // Tracks entropy hashes of received packets.
+ EntropyTracker entropy_tracker_;
// Track some peer state so we can do less bookkeeping.
// Largest sequence number that the peer has observed. Mostly received,
@@ -146,6 +189,10 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
QuicTime time_largest_observed_;
scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_;
+
+ QuicConnectionStats* stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_received_packet_manager_test.cc b/chromium/net/quic/quic_received_packet_manager_test.cc
index 9d11129f0ff..b5793092980 100644
--- a/chromium/net/quic/quic_received_packet_manager_test.cc
+++ b/chromium/net/quic/quic_received_packet_manager_test.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <vector>
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/test_tools/quic_received_packet_manager_peer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,20 +18,191 @@ using std::vector;
namespace net {
namespace test {
+
+class EntropyTrackerPeer {
+ public:
+ static QuicPacketSequenceNumber first_gap(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.first_gap_;
+ }
+ static QuicPacketSequenceNumber largest_observed(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.largest_observed_;
+ }
+ static int packets_entropy_size(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.packets_entropy_.size();
+ }
+ static bool IsTrackingPacket(
+ const QuicReceivedPacketManager::EntropyTracker& tracker,
+ QuicPacketSequenceNumber sequence_number) {
+ return tracker.packets_entropy_.find(sequence_number) !=
+ tracker.packets_entropy_.end();
+ }
+};
+
namespace {
+// Entropy of individual packets is not tracked if there are no gaps.
+TEST(EntropyTrackerTest, NoGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(1, 23);
+ tracker.RecordPacketEntropyHash(2, 42);
+
+ EXPECT_EQ(23 ^ 42, tracker.EntropyHash(2));
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+
+ EXPECT_EQ(2u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+}
+
+// Entropy of individual packets is tracked as long as there are gaps.
+// Filling the first gap results in entropy getting garbage collected.
+TEST(EntropyTrackerTest, FillGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(2));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 1.
+ tracker.RecordPacketEntropyHash(1, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 4.
+ tracker.RecordPacketEntropyHash(4, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(4));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 3. Entropy for packets 3 to 6 are forgotten.
+ tracker.RecordPacketEntropyHash(3, 2);
+
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill in the rest.
+ tracker.RecordPacketEntropyHash(7, 2);
+ tracker.RecordPacketEntropyHash(8, 2);
+
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+}
+
+TEST(EntropyTrackerTest, SetCumulativeEntropyUpTo) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ // Inform the tracker about value of the hash at a gap.
+ tracker.SetCumulativeEntropyUpTo(3, 7);
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(7 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(7 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(7 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at a known location.
+ tracker.SetCumulativeEntropyUpTo(6, 1);
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(1 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at the last location.
+ tracker.SetCumulativeEntropyUpTo(9, 21);
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(42 ^ 21, tracker.EntropyHash(9));
+}
+
class QuicReceivedPacketManagerTest : public ::testing::Test {
protected:
- QuicReceivedPacketManagerTest() : received_manager_(kTCP) { }
+ QuicReceivedPacketManagerTest() : received_manager_(kTCP, &stats_) {}
- void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash) {
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ RecordPacketReceipt(sequence_number, entropy_hash, QuicTime::Zero());
+ }
+
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash,
+ QuicTime receipt_time) {
QuicPacketHeader header;
header.packet_sequence_number = sequence_number;
header.entropy_hash = entropy_hash;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, receipt_time);
}
+ QuicConnectionStats stats_;
QuicReceivedPacketManager received_manager_;
};
@@ -43,8 +215,7 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) {
entropies.push_back(make_pair(8, 34));
for (size_t i = 0; i < entropies.size(); ++i) {
- RecordPacketEntropyHash(entropies[i].first,
- entropies[i].second);
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
}
sort(entropies.begin(), entropies.end());
@@ -56,58 +227,68 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) {
hash ^= entropies[index].second;
++index;
}
+ if (i < 3) continue;
EXPECT_EQ(hash, received_manager_.EntropyHash(i));
}
+ // Reorder by 5 when 2 is received after 7.
+ EXPECT_EQ(5u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(2u, stats_.packets_reordered);
}
TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) {
EXPECT_EQ(0, received_manager_.EntropyHash(0));
- RecordPacketEntropyHash(4, 5);
+ RecordPacketReceipt(4, 5);
EXPECT_EQ(0, received_manager_.EntropyHash(3));
}
TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) {
EXPECT_EQ(0, received_manager_.EntropyHash(0));
- RecordPacketEntropyHash(4, 5);
+ RecordPacketReceipt(4, 5);
EXPECT_EQ(0, received_manager_.EntropyHash(3));
}
-TEST_F(QuicReceivedPacketManagerTest, RecalculateEntropyHash) {
+TEST_F(QuicReceivedPacketManagerTest, SetCumulativeEntropyUpTo) {
vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
entropies.push_back(make_pair(1, 12));
entropies.push_back(make_pair(2, 1));
entropies.push_back(make_pair(3, 33));
entropies.push_back(make_pair(4, 3));
- entropies.push_back(make_pair(5, 34));
- entropies.push_back(make_pair(6, 29));
+ entropies.push_back(make_pair(6, 34));
+ entropies.push_back(make_pair(7, 29));
QuicPacketEntropyHash entropy_hash = 0;
for (size_t i = 0; i < entropies.size(); ++i) {
- RecordPacketEntropyHash(entropies[i].first, entropies[i].second);
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
entropy_hash ^= entropies[i].second;
}
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
- // Now set the entropy hash up to 4 to be 100.
+ // Now set the entropy hash up to 5 to be 100.
entropy_hash ^= 100;
- for (size_t i = 0; i < 3; ++i) {
+ for (size_t i = 0; i < 4; ++i) {
entropy_hash ^= entropies[i].second;
}
- QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
- &received_manager_, 4, 100);
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
+ &received_manager_, 5, 100);
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
- QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
&received_manager_, 1, 50);
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
+
+ // No reordering.
+ EXPECT_EQ(0u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(0u, stats_.packets_reordered);
}
TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
QuicPacketHeader header;
header.packet_sequence_number = 2u;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
header.packet_sequence_number = 7u;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u));
EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
EXPECT_TRUE(QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore(
@@ -120,7 +301,7 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) {
QuicPacketHeader header;
header.packet_sequence_number = 2u;
QuicTime two_ms = QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(2));
- received_manager_.RecordPacketReceived(0u, header, two_ms, false);
+ received_manager_.RecordPacketReceived(0u, header, two_ms);
ReceivedPacketInfo info;
received_manager_.UpdateReceivedPacketInfo(&info, QuicTime::Zero());
@@ -136,6 +317,17 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) {
info.delta_time_largest_observed);
}
+TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) {
+ RecordPacketReceipt(1, 0);
+ RecordPacketReceipt(6, 0);
+ RecordPacketReceipt(
+ 2, 0, QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1)));
+
+ EXPECT_EQ(4u, stats_.max_sequence_reordering);
+ EXPECT_EQ(1000u, stats_.max_time_reordering_us);
+ EXPECT_EQ(1u, stats_.packets_reordered);
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_reliable_client_stream.cc b/chromium/net/quic/quic_reliable_client_stream.cc
index af12b8b222b..4eb74d6a31d 100644
--- a/chromium/net/quic/quic_reliable_client_stream.cc
+++ b/chromium/net/quic/quic_reliable_client_stream.cc
@@ -7,7 +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"
+#include "net/quic/quic_write_blocked_list.h"
namespace net {
@@ -59,7 +59,7 @@ QuicPriority QuicReliableClientStream::EffectivePriority() const {
if (delegate_ && delegate_->HasSendHeadersComplete()) {
return QuicDataStream::EffectivePriority();
}
- return kHighestPriority;
+ return QuicWriteBlockedList::kHighestPriority;
}
int QuicReliableClientStream::WriteStreamData(
@@ -69,7 +69,7 @@ int QuicReliableClientStream::WriteStreamData(
// We should not have data buffered.
DCHECK(!HasBufferedData());
// Writes the data, or buffers it.
- WriteOrBufferData(data, fin);
+ WriteOrBufferData(data, fin, NULL);
if (!HasBufferedData()) {
return OK;
}
@@ -93,9 +93,7 @@ void QuicReliableClientStream::OnError(int error) {
}
bool QuicReliableClientStream::CanWrite(const CompletionCallback& callback) {
- bool can_write = session()->connection()->CanWrite(
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- id() == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE);
+ bool can_write = session()->connection()->CanWrite(HAS_RETRANSMITTABLE_DATA);
if (!can_write) {
session()->MarkWriteBlocked(id(), EffectivePriority());
DCHECK(callback_.is_null());
diff --git a/chromium/net/quic/quic_reliable_client_stream_test.cc b/chromium/net/quic/quic_reliable_client_stream_test.cc
index 081186ec846..caa697aa484 100644
--- a/chromium/net/quic/quic_reliable_client_stream_test.cc
+++ b/chromium/net/quic/quic_reliable_client_stream_test.cc
@@ -12,6 +12,7 @@
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
+using testing::AnyNumber;
using testing::Return;
using testing::StrEq;
using testing::_;
@@ -20,7 +21,7 @@ namespace net {
namespace test {
namespace {
-const QuicGuid kStreamId = 3;
+const QuicConnectionId kStreamId = 3;
class MockDelegate : public QuicReliableClientStream::Delegate {
public:
@@ -37,12 +38,14 @@ class MockDelegate : public QuicReliableClientStream::Delegate {
DISALLOW_COPY_AND_ASSIGN(MockDelegate);
};
-class QuicReliableClientStreamTest : public ::testing::Test {
+class QuicReliableClientStreamTest
+ : public ::testing::TestWithParam<QuicVersion> {
public:
QuicReliableClientStreamTest()
- : session_(new MockConnection(false)),
- stream_(kStreamId, &session_, BoundNetLog()) {
- stream_.SetDelegate(&delegate_);
+ : session_(new MockConnection(false, SupportedVersions(GetParam()))) {
+ stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog());
+ session_.ActivateStream(stream_);
+ stream_->SetDelegate(&delegate_);
}
void InitializeHeaders() {
@@ -77,37 +80,39 @@ class QuicReliableClientStreamTest : public ::testing::Test {
testing::StrictMock<MockDelegate> delegate_;
MockSession session_;
- QuicReliableClientStream stream_;
+ QuicReliableClientStream* stream_;
QuicCryptoClientConfig crypto_config_;
SpdyHeaderBlock headers_;
};
-TEST_F(QuicReliableClientStreamTest, OnFinRead) {
+INSTANTIATE_TEST_CASE_P(Version, QuicReliableClientStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicReliableClientStreamTest, OnFinRead) {
InitializeHeaders();
- QuicSpdyCompressor compressor;
- string compressed_headers = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(compressed_headers));
string uncompressed_headers =
SpdyUtils::SerializeUncompressedHeaders(headers_);
EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()),
uncompressed_headers.size()));
- stream_.OnStreamFrame(frame1);
+ QuicStreamOffset offset = 0;
+ stream_->OnStreamHeaders(uncompressed_headers);
+ stream_->OnStreamHeadersComplete(false, uncompressed_headers.length());
IOVector iov;
- QuicStreamFrame frame2(kStreamId, true, compressed_headers.length(), iov);
+ QuicStreamFrame frame2(kStreamId, true, offset, iov);
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- stream_.OnStreamFrame(frame2);
+ stream_->OnStreamFrame(frame2);
}
-TEST_F(QuicReliableClientStreamTest, ProcessData) {
+TEST_P(QuicReliableClientStreamTest, ProcessData) {
const char data[] = "hello world!";
EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data)));
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- EXPECT_EQ(arraysize(data), stream_.ProcessData(data, arraysize(data)));
+ EXPECT_EQ(arraysize(data), stream_->ProcessData(data, arraysize(data)));
}
-TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) {
+TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) {
const char data[] = "hello world!";
EXPECT_CALL(delegate_,
OnDataReceived(StrEq(data),
@@ -115,50 +120,50 @@ TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) {
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- EXPECT_EQ(0u, stream_.ProcessData(data, arraysize(data)));
+ EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data)));
}
-TEST_F(QuicReliableClientStreamTest, OnError) {
+TEST_P(QuicReliableClientStreamTest, OnError) {
EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED));
- stream_.OnError(ERR_INTERNET_DISCONNECTED);
- EXPECT_FALSE(stream_.GetDelegate());
+ stream_->OnError(ERR_INTERNET_DISCONNECTED);
+ EXPECT_FALSE(stream_->GetDelegate());
}
-TEST_F(QuicReliableClientStreamTest, WriteStreamData) {
+TEST_P(QuicReliableClientStreamTest, WriteStreamData) {
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
const char kData1[] = "hello world";
const size_t kDataLen = arraysize(kData1);
// All data written.
- EXPECT_CALL(session_, WritevData(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),
- true, callback.callback()));
+ EXPECT_EQ(OK, stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
}
-TEST_F(QuicReliableClientStreamTest, WriteStreamDataAsync) {
- EXPECT_CALL(delegate_, HasSendHeadersComplete());
+TEST_P(QuicReliableClientStreamTest, WriteStreamDataAsync) {
+ EXPECT_CALL(delegate_, HasSendHeadersComplete()).Times(AnyNumber());
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
const char kData1[] = "hello world";
const size_t kDataLen = arraysize(kData1);
// No data written.
- EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(0, false)));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING,
- stream_.WriteStreamData(base::StringPiece(kData1, kDataLen),
- true, callback.callback()));
+ stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
ASSERT_FALSE(callback.have_result());
// All data written.
- EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
- stream_.OnCanWrite();
+ stream_->OnCanWrite();
ASSERT_TRUE(callback.have_result());
EXPECT_EQ(OK, callback.WaitForResult());
}
diff --git a/chromium/net/quic/quic_sent_entropy_manager.h b/chromium/net/quic/quic_sent_entropy_manager.h
index a101e738d06..c89d97730de 100644
--- a/chromium/net/quic/quic_sent_entropy_manager.h
+++ b/chromium/net/quic/quic_sent_entropy_manager.h
@@ -51,6 +51,8 @@ class NET_EXPORT_PRIVATE QuicSentEntropyManager {
// Cumulative hash of entropy of all sent packets.
QuicPacketEntropyHash packets_entropy_hash_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentEntropyManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_sent_packet_manager.cc b/chromium/net/quic/quic_sent_packet_manager.cc
index bda30c72a1f..806297e3575 100644
--- a/chromium/net/quic/quic_sent_packet_manager.cc
+++ b/chromium/net/quic/quic_sent_packet_manager.cc
@@ -4,38 +4,23 @@
#include "net/quic/quic_sent_packet_manager.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/quic/congestion_control/pacing_sender.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_ack_notifier_manager.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils_chromium.h"
using std::make_pair;
+using std::max;
using std::min;
-// TODO(rtenneti): Remove this.
-// Do not flip this flag until the flakiness of the
-// net/tools/quic/end_to_end_test is fixed.
-// If true, then QUIC connections will track the retransmission history of a
-// packet so that an ack of a previous transmission will ack the data of all
-// other transmissions.
-bool FLAGS_track_retransmission_history = false;
-
-// A test-only flag to prevent the RTO from backing off when multiple sequential
-// tail drops occur.
-bool FLAGS_limit_rto_increase_for_tests = false;
-
-// Do not remove this flag until the Finch-trials described in b/11706275
-// are complete.
-// If true, QUIC connections will support the use of a pacing algorithm when
-// sending packets, in an attempt to reduce packet loss. The client must also
-// request pacing for the server to enable it.
-bool FLAGS_enable_quic_pacing = false;
-
namespace net {
namespace {
-static const int kBitrateSmoothingPeriodMs = 1000;
-static const int kHistoryPeriodMs = 5000;
-
static const int kDefaultRetransmissionTimeMs = 500;
// TCP RFC calls for 1 second RTO however Linux differs from this default and
// define the minimum RTO to 200ms, we will use the same until we have data to
@@ -44,272 +29,282 @@ static const int kMinRetransmissionTimeMs = 200;
static const int kMaxRetransmissionTimeMs = 60000;
static const size_t kMaxRetransmissions = 10;
-// We only retransmit 2 packets per ack.
-static const size_t kMaxRetransmissionsPerAck = 2;
+// Only exponentially back off the handshake timer 5 times due to a timeout.
+static const size_t kMaxHandshakeRetransmissionBackoffs = 5;
+static const size_t kMinHandshakeTimeoutMs = 10;
+
+// Sends up to two tail loss probes before firing an RTO,
+// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe.
+static const size_t kDefaultMaxTailLossProbes = 2;
+static const int64 kMinTailLossProbeTimeoutMs = 10;
-// TCP retransmits after 3 nacks.
-static const size_t kNumberOfNacksBeforeRetransmission = 3;
+// Number of samples before we force a new recent min rtt to be captured.
+static const size_t kNumMinRttSamplesAfterQuiescence = 2;
+
+bool HasCryptoHandshake(const TransmissionInfo& transmission_info) {
+ if (transmission_info.retransmittable_frames == NULL) {
+ return false;
+ }
+ return transmission_info.retransmittable_frames->HasCryptoHandshake() ==
+ IS_HANDSHAKE;
+}
-COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs,
- history_must_be_longer_or_equal_to_the_smoothing_period);
} // namespace
#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
-QuicSentPacketManager::HelperInterface::~HelperInterface() {
-}
-
QuicSentPacketManager::QuicSentPacketManager(bool is_server,
- HelperInterface* helper,
const QuicClock* clock,
- CongestionFeedbackType type)
- : is_server_(is_server),
- helper_(helper),
+ QuicConnectionStats* stats,
+ CongestionFeedbackType type,
+ LossDetectionType loss_type)
+ : unacked_packets_(),
+ is_server_(is_server),
clock_(clock),
- send_algorithm_(SendAlgorithmInterface::Create(clock, type)),
- rtt_sample_(QuicTime::Delta::Infinite()),
+ stats_(stats),
+ debug_delegate_(NULL),
+ send_algorithm_(
+ SendAlgorithmInterface::Create(clock, &rtt_stats_, type, stats)),
+ loss_algorithm_(LossDetectionInterface::Create(loss_type)),
+ largest_observed_(0),
consecutive_rto_count_(0),
+ consecutive_tlp_count_(0),
+ consecutive_crypto_retransmission_count_(0),
+ pending_tlp_transmission_(false),
+ max_tail_loss_probes_(kDefaultMaxTailLossProbes),
using_pacing_(false) {
}
QuicSentPacketManager::~QuicSentPacketManager() {
- for (UnackedPacketMap::iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- delete it->second.retransmittable_frames;
- // Only delete previous_transmissions once, for the newest packet.
- if (it->second.previous_transmissions != NULL &&
- it->first == *it->second.previous_transmissions->rbegin()) {
- delete it->second.previous_transmissions;
- }
- }
- STLDeleteValues(&packet_history_map_);
}
void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
- if (config.initial_round_trip_time_us() > 0 &&
- rtt_sample_.IsInfinite()) {
- // The initial rtt should already be set on the client side.
- DVLOG_IF(1, !is_server_)
- << "Client did not set an initial RTT, but did negotiate one.";
- rtt_sample_ =
- QuicTime::Delta::FromMicroseconds(config.initial_round_trip_time_us());
- }
- if (config.congestion_control() == kPACE) {
+ if (config.HasReceivedInitialRoundTripTimeUs() &&
+ config.ReceivedInitialRoundTripTimeUs() > 0) {
+ rtt_stats_.set_initial_rtt_us(min(kMaxInitialRoundTripTimeUs,
+ config.ReceivedInitialRoundTripTimeUs()));
+ }
+ // TODO(ianswett): BBR is currently a server only feature.
+ if (config.HasReceivedCongestionOptions() &&
+ ContainsQuicTag(config.ReceivedCongestionOptions(), kTBBR)) {
+ send_algorithm_.reset(
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kTCPBBR, stats_));
+ }
+ if (config.congestion_feedback() == kPACE) {
MaybeEnablePacing();
}
+ if (config.HasReceivedLossDetection() &&
+ config.ReceivedLossDetection() == kTIME) {
+ loss_algorithm_.reset(LossDetectionInterface::Create(kTime));
+ }
send_algorithm_->SetFromConfig(config, is_server_);
}
-void QuicSentPacketManager::SetMaxPacketSize(QuicByteCount max_packet_size) {
- send_algorithm_->SetMaxPacketSize(max_packet_size);
-}
-
+// TODO(ianswett): Combine this method with OnPacketSent once packets are always
+// sent in order and the connection tracks RetransmittableFrames for longer.
void QuicSentPacketManager::OnSerializedPacket(
const SerializedPacket& serialized_packet) {
- if (serialized_packet.retransmittable_frames == NULL &&
- !serialized_packet.packet->is_fec_packet()) {
- // Don't track ack/congestion feedback packets.
- return;
+ if (serialized_packet.retransmittable_frames) {
+ ack_notifier_manager_.OnSerializedPacket(serialized_packet);
}
- ack_notifier_manager_.OnSerializedPacket(serialized_packet);
-
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < serialized_packet.sequence_number);
- unacked_packets_[serialized_packet.sequence_number] =
- TransmissionInfo(serialized_packet.retransmittable_frames,
- serialized_packet.sequence_number_length);
+ unacked_packets_.AddPacket(serialized_packet);
}
void QuicSentPacketManager::OnRetransmittedPacket(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
- DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
- DCHECK(ContainsKey(pending_retransmissions_, old_sequence_number));
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < new_sequence_number);
-
- pending_retransmissions_.erase(old_sequence_number);
-
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(old_sequence_number);
- RetransmittableFrames* frames = unacked_it->second.retransmittable_frames;
- DCHECK(frames);
+ TransmissionType transmission_type;
+ PendingRetransmissionMap::iterator it =
+ pending_retransmissions_.find(old_sequence_number);
+ if (it != pending_retransmissions_.end()) {
+ transmission_type = it->second;
+ pending_retransmissions_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Expected sequence number to be in "
+ "pending_retransmissions_. sequence_number: " << old_sequence_number;
+ transmission_type = NOT_RETRANSMISSION;
+ }
// A notifier may be waiting to hear about ACKs for the original sequence
// number. Inform them that the sequence number has changed.
ack_notifier_manager_.UpdateSequenceNumber(old_sequence_number,
new_sequence_number);
- // We keep the old packet in the unacked packet list until it, or one of
- // the retransmissions of it are acked.
- unacked_it->second.retransmittable_frames = NULL;
- unacked_packets_[new_sequence_number] =
- TransmissionInfo(frames, GetSequenceNumberLength(old_sequence_number));
-
- // Keep track of all sequence numbers that this packet
- // has been transmitted as.
- SequenceNumberSet* previous_transmissions =
- unacked_it->second.previous_transmissions;
- if (previous_transmissions == NULL) {
- // This is the first retransmission of this packet, so create a new entry.
- previous_transmissions = new SequenceNumberSet;
- unacked_it->second.previous_transmissions = previous_transmissions;
- previous_transmissions->insert(old_sequence_number);
- }
- previous_transmissions->insert(new_sequence_number);
- unacked_packets_[new_sequence_number].previous_transmissions =
- previous_transmissions;
-
- DCHECK(HasRetransmittableFrames(new_sequence_number));
-}
-
-bool QuicSentPacketManager::OnIncomingAck(
- const ReceivedPacketInfo& received_info, QuicTime ack_receive_time) {
- // Determine if the least unacked sequence number is being acked.
- QuicPacketSequenceNumber least_unacked_sent_before =
- GetLeastUnackedSentPacket();
- bool new_least_unacked = !IsAwaitingPacket(received_info,
- least_unacked_sent_before);
+ unacked_packets_.OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number,
+ transmission_type);
+}
- HandleAckForSentPackets(received_info);
+void QuicSentPacketManager::OnIncomingAck(
+ const ReceivedPacketInfo& received_info,
+ QuicTime ack_receive_time) {
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
- SequenceNumberSet retransmission_packets =
- OnIncomingAckFrame(received_info, ack_receive_time);
+ // We rely on delta_time_largest_observed to compute an RTT estimate, so
+ // we only update rtt when the largest observed gets acked.
+ bool largest_observed_acked = MaybeUpdateRTT(received_info, ack_receive_time);
+ if (largest_observed_ < received_info.largest_observed) {
+ largest_observed_ = received_info.largest_observed;
+ unacked_packets_.IncreaseLargestObserved(largest_observed_);
+ }
+ HandleAckForSentPackets(received_info);
+ InvokeLossDetection(ack_receive_time);
+ MaybeInvokeCongestionEvent(largest_observed_acked, bytes_in_flight);
- for (SequenceNumberSet::const_iterator it = retransmission_packets.begin();
- it != retransmission_packets.end(); ++it) {
- DCHECK(!ContainsKey(pending_packets_, *it));
- MarkForRetransmission(*it, NACK_RETRANSMISSION);
+ // If we have received a truncated ack, then we need to clear out some
+ // previous transmissions to allow the peer to actually ACK new packets.
+ if (received_info.is_truncated) {
+ unacked_packets_.ClearPreviousRetransmissions(
+ received_info.missing_packets.size() / 2);
}
- if (new_least_unacked) {
+ // Anytime we are making forward progress and have a new RTT estimate, reset
+ // the backoff counters.
+ if (largest_observed_acked) {
+ // Reset all retransmit counters any time a new packet is acked.
consecutive_rto_count_ = 0;
+ consecutive_tlp_count_ = 0;
+ consecutive_crypto_retransmission_count_ = 0;
}
-
- return new_least_unacked;
}
-void QuicSentPacketManager::DiscardUnackedPacket(
- QuicPacketSequenceNumber sequence_number) {
- MarkPacketReceivedByPeer(sequence_number);
+void QuicSentPacketManager::MaybeInvokeCongestionEvent(
+ bool rtt_updated, QuicByteCount bytes_in_flight) {
+ if (rtt_updated || !packets_acked_.empty() ||
+ !packets_lost_.empty()) {
+ send_algorithm_->OnCongestionEvent(
+ rtt_updated, bytes_in_flight, packets_acked_, packets_lost_);
+ packets_acked_.clear();
+ packets_lost_.clear();
+ }
}
void QuicSentPacketManager::HandleAckForSentPackets(
const ReceivedPacketInfo& received_info) {
// 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();
+ QuicTime::Delta delta_largest_observed =
+ received_info.delta_time_largest_observed;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
while (it != unacked_packets_.end()) {
QuicPacketSequenceNumber sequence_number = it->first;
if (sequence_number > received_info.largest_observed) {
- // These are very new sequence_numbers.
+ // These packets are still in flight.
break;
}
if (IsAwaitingPacket(received_info, sequence_number)) {
+ // Consider it multiple nacks when there is a gap between the missing
+ // packet and the largest observed, since the purpose of a nack
+ // threshold is to tolerate re-ordering. This handles both StretchAcks
+ // and Forward Acks.
+ // The nack count only increases when the largest observed increases.
+ size_t min_nacks = received_info.largest_observed - sequence_number;
+ // Truncated acks can nack the largest observed, so use a min of 1.
+ if (min_nacks == 0) {
+ min_nacks = 1;
+ }
+ unacked_packets_.NackPacket(sequence_number, min_nacks);
++it;
continue;
}
-
// Packet was acked, so remove it from our unacked packet list.
- DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
+ DVLOG(1) << ENDPOINT << "Got an ack for packet " << sequence_number;
// If data is associated with the most recent transmission of this
// packet, then inform the caller.
- it = MarkPacketReceivedByPeer(sequence_number);
-
- // The AckNotifierManager is informed of every ACKed sequence number.
- ack_notifier_manager_.OnPacketAcked(sequence_number);
- }
-
- // If we have received a truncated ack, then we need to
- // clear out some previous transmissions to allow the peer
- // to actually ACK new packets.
- if (received_info.is_truncated) {
- ClearPreviousRetransmissions(received_info.missing_packets.size() / 2);
- }
-}
-
-void QuicSentPacketManager::ClearPreviousRetransmissions(size_t num_to_clear) {
- UnackedPacketMap::iterator it = unacked_packets_.begin();
- while (it != unacked_packets_.end() && num_to_clear > 0) {
- QuicPacketSequenceNumber sequence_number = it->first;
- // If this is not a previous transmission then there is no point
- // in clearing out any further packets, because it will not affect
- // the high water mark.
- SequenceNumberSet* previous_transmissions =
- it->second.previous_transmissions;
- if (previous_transmissions == NULL) {
- break;
- }
- QuicPacketSequenceNumber newest_transmission =
- *previous_transmissions->rbegin();
- if (sequence_number == newest_transmission) {
- break;
+ if (it->second.in_flight) {
+ packets_acked_[sequence_number] = it->second;
}
+ it = MarkPacketHandled(it, delta_largest_observed);
+ }
- DCHECK(it->second.retransmittable_frames == NULL);
- previous_transmissions->erase(sequence_number);
- if (previous_transmissions->size() == 1) {
- unacked_packets_[newest_transmission].previous_transmissions = NULL;
- delete previous_transmissions;
- }
- unacked_packets_.erase(it++);
- --num_to_clear;
+ // Discard any retransmittable frames associated with revived packets.
+ for (SequenceNumberSet::const_iterator revived_it =
+ received_info.revived_packets.begin();
+ revived_it != received_info.revived_packets.end(); ++revived_it) {
+ MarkPacketRevived(*revived_it, delta_largest_observed);
}
}
bool QuicSentPacketManager::HasRetransmittableFrames(
QuicPacketSequenceNumber sequence_number) const {
- if (!ContainsKey(unacked_packets_, sequence_number)) {
- return false;
- }
-
- return unacked_packets_.find(
- sequence_number)->second.retransmittable_frames != NULL;
+ return unacked_packets_.HasRetransmittableFrames(sequence_number);
}
void QuicSentPacketManager::RetransmitUnackedPackets(
RetransmissionType retransmission_type) {
- if (unacked_packets_.empty()) {
- return;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // TODO(ianswett): Consider adding a new retransmission type which removes
+ // all these old packets from unacked and retransmits them as new sequence
+ // numbers with no connection to the previous ones.
+ if (frames != NULL && (retransmission_type == ALL_PACKETS ||
+ frames->encryption_level() == ENCRYPTION_INITIAL)) {
+ MarkForRetransmission(it->first, ALL_UNACKED_RETRANSMISSION);
+ }
+ ++it;
}
+}
- for (UnackedPacketMap::const_iterator unacked_it = unacked_packets_.begin();
- unacked_it != unacked_packets_.end(); ++unacked_it) {
- const RetransmittableFrames* frames =
- unacked_it->second.retransmittable_frames;
- if (frames == NULL) {
- continue;
- }
- if (retransmission_type == ALL_PACKETS ||
- 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.
- OnPacketAbandoned(unacked_it->first);
- if (!MarkForRetransmission(unacked_it->first, NACK_RETRANSMISSION)) {
- DiscardUnackedPacket(unacked_it->first);
- }
+void QuicSentPacketManager::NeuterUnencryptedPackets() {
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ if (frames != NULL && frames->encryption_level() == ENCRYPTION_NONE) {
+ // Once you're forward secure, no unencrypted packets will be sent, crypto
+ // or otherwise. Unencrypted packets are neutered and abandoned, to ensure
+ // they are not retransmitted or considered lost from a congestion control
+ // perspective.
+ pending_retransmissions_.erase(sequence_number);
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ // RemoveRetransmittibility is safe because only the newest sequence
+ // number can have frames.
+ unacked_packets_.RemoveRetransmittability(sequence_number);
}
}
}
-bool QuicSentPacketManager::MarkForRetransmission(
+void QuicSentPacketManager::MarkForRetransmission(
QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type) {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- if (!HasRetransmittableFrames(sequence_number)) {
- return false;
- }
- // If it's already in the retransmission map, don't add it again, just let
- // the prior retransmission request win out.
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ LOG_IF(DFATAL, transmission_info.retransmittable_frames == NULL);
+ if (transmission_type != TLP_RETRANSMISSION) {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
+ // TODO(ianswett): Currently the RTO can fire while there are pending NACK
+ // retransmissions for the same data, which is not ideal.
if (ContainsKey(pending_retransmissions_, sequence_number)) {
- return true;
+ return;
}
pending_retransmissions_[sequence_number] = transmission_type;
- return true;
+}
+
+void QuicSentPacketManager::RecordSpuriousRetransmissions(
+ const SequenceNumberSet& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number) {
+ for (SequenceNumberSet::const_iterator
+ it = all_transmissions.upper_bound(acked_sequence_number),
+ end = all_transmissions.end();
+ it != end;
+ ++it) {
+ const TransmissionInfo& retransmit_info =
+ unacked_packets_.GetTransmissionInfo(*it);
+
+ stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent;
+ ++stats_->packets_spuriously_retransmitted;
+ if (debug_delegate_ != NULL) {
+ debug_delegate_->OnSpuriousPacketRetransmition(
+ retransmit_info.transmission_type,
+ retransmit_info.bytes_sent);
+ }
+ }
}
bool QuicSentPacketManager::HasPendingRetransmissions() const {
@@ -321,353 +316,347 @@ QuicSentPacketManager::PendingRetransmission
DCHECK(!pending_retransmissions_.empty());
QuicPacketSequenceNumber sequence_number =
pending_retransmissions_.begin()->first;
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- const RetransmittableFrames* retransmittable_frames =
- unacked_packets_[sequence_number].retransmittable_frames;
- DCHECK(retransmittable_frames);
+ TransmissionType transmission_type = pending_retransmissions_.begin()->second;
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ // Ensure crypto packets are retransmitted before other packets.
+ PendingRetransmissionMap::const_iterator it =
+ pending_retransmissions_.begin();
+ do {
+ if (HasCryptoHandshake(unacked_packets_.GetTransmissionInfo(it->first))) {
+ sequence_number = it->first;
+ transmission_type = it->second;
+ break;
+ }
+ ++it;
+ } while (it != pending_retransmissions_.end());
+ }
+ DCHECK(unacked_packets_.IsUnacked(sequence_number)) << sequence_number;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ DCHECK(transmission_info.retransmittable_frames);
return PendingRetransmission(sequence_number,
- pending_retransmissions_.begin()->second,
- *retransmittable_frames,
- GetSequenceNumberLength(sequence_number));
+ transmission_type,
+ *transmission_info.retransmittable_frames,
+ transmission_info.sequence_number_length);
}
-bool QuicSentPacketManager::IsPreviousTransmission(
- QuicPacketSequenceNumber sequence_number) const {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- UnackedPacketMap::const_iterator it = unacked_packets_.find(sequence_number);
- if (it->second.previous_transmissions == NULL) {
- return false;
+void QuicSentPacketManager::MarkPacketRevived(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
+ if (!unacked_packets_.IsUnacked(sequence_number)) {
+ return;
}
- SequenceNumberSet* previous_transmissions = it->second.previous_transmissions;
- DCHECK(!previous_transmissions->empty());
- return *previous_transmissions->rbegin() != sequence_number;
-}
-
-QuicSentPacketManager::UnackedPacketMap::iterator
-QuicSentPacketManager::MarkPacketReceivedByPeer(
- QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- // If this packet has never been retransmitted, then simply drop it.
- UnackedPacketMap::const_iterator previous_it =
- unacked_packets_.find(sequence_number);
- if (previous_it->second.previous_transmissions == NULL) {
- UnackedPacketMap::iterator next_unacked =
- unacked_packets_.find(sequence_number);
- ++next_unacked;
- DiscardPacket(sequence_number);
- return next_unacked;
- }
-
- SequenceNumberSet* previous_transmissions =
- previous_it->second.previous_transmissions;
- DCHECK(!previous_transmissions->empty());
- SequenceNumberSet::reverse_iterator previous_transmissions_it =
- previous_transmissions->rbegin();
- QuicPacketSequenceNumber newest_transmission = *previous_transmissions_it;
- if (newest_transmission == sequence_number) {
- DiscardPacket(newest_transmission);
- } else {
- // If we have received an ack for a previous transmission of a packet,
- // we want to keep the "new" transmission of the packet unacked,
- // but prevent the data from being retransmitted.
- delete unacked_packets_[newest_transmission].retransmittable_frames;
- unacked_packets_[newest_transmission].retransmittable_frames = NULL;
- unacked_packets_[newest_transmission].previous_transmissions = NULL;
- pending_retransmissions_.erase(newest_transmission);
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ QuicPacketSequenceNumber newest_transmission =
+ *transmission_info.all_transmissions->rbegin();
+ // This packet has been revived at the receiver. If we were going to
+ // retransmit it, do not retransmit it anymore.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // The AckNotifierManager needs to be notified for revived packets,
+ // since it indicates the packet arrived from the appliction's perspective.
+ if (transmission_info.retransmittable_frames) {
+ ack_notifier_manager_.OnPacketAcked(
+ newest_transmission, delta_largest_observed);
+ }
+
+ unacked_packets_.RemoveRetransmittability(sequence_number);
+}
+
+QuicUnackedPacketMap::const_iterator QuicSentPacketManager::MarkPacketHandled(
+ QuicUnackedPacketMap::const_iterator it,
+ QuicTime::Delta delta_largest_observed) {
+ LOG_IF(DFATAL, it == unacked_packets_.end())
+ << "MarkPacketHandled must be passed a valid iterator entry.";
+ const QuicPacketSequenceNumber sequence_number = it->first;
+ const TransmissionInfo& transmission_info = it->second;
+
+ QuicPacketSequenceNumber newest_transmission =
+ *transmission_info.all_transmissions->rbegin();
+ // Remove the most recent packet, if it is pending retransmission.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // Notify observers about the ACKed packet.
+ {
+ // The AckNotifierManager needs to be notified about the most recent
+ // transmission, since that's the one only one it tracks.
+ ack_notifier_manager_.OnPacketAcked(newest_transmission,
+ delta_largest_observed);
+ if (newest_transmission != sequence_number) {
+ RecordSpuriousRetransmissions(*transmission_info.all_transmissions,
+ sequence_number);
+ }
}
- // Clear out information all previous transmissions.
- ++previous_transmissions_it;
- while (previous_transmissions_it != previous_transmissions->rend()) {
- QuicPacketSequenceNumber previous_transmission = *previous_transmissions_it;
- ++previous_transmissions_it;
- DiscardPacket(previous_transmission);
- }
+ // Two cases for MarkPacketHandled:
+ // 1) Handle the most recent or a crypto packet, so remove all transmissions.
+ // 2) Handle old transmission, keep all other pending transmissions,
+ // but disassociate them from one another.
- delete previous_transmissions;
+ // If it's a crypto handshake packet, discard it and all retransmissions,
+ // since they won't be acked now that one has been processed.
+ // TODO(ianswett): Instead of handling all crypto packets in a special way,
+ // only handle NULL encrypted packets in a special way.
+ if (HasCryptoHandshake(
+ unacked_packets_.GetTransmissionInfo(newest_transmission))) {
+ unacked_packets_.RemoveFromInFlight(newest_transmission);
+ }
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ unacked_packets_.RemoveRetransmittability(sequence_number);
- UnackedPacketMap::iterator next_unacked = unacked_packets_.begin();
+ QuicUnackedPacketMap::const_iterator next_unacked = unacked_packets_.begin();
while (next_unacked != unacked_packets_.end() &&
- next_unacked->first < sequence_number) {
+ next_unacked->first <= sequence_number) {
++next_unacked;
}
return next_unacked;
}
-void QuicSentPacketManager::DiscardPacket(
- 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()) {
- return;
- }
-
- // Delete the retransmittable frames.
- delete unacked_it->second.retransmittable_frames;
- unacked_packets_.erase(unacked_it);
- pending_retransmissions_.erase(sequence_number);
- return;
-}
-
bool QuicSentPacketManager::IsUnacked(
QuicPacketSequenceNumber sequence_number) const {
- return ContainsKey(unacked_packets_, sequence_number);
-}
-
-QuicSequenceNumberLength QuicSentPacketManager::GetSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number) const {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- return unacked_packets_.find(sequence_number)->second.sequence_number_length;
+ return unacked_packets_.IsUnacked(sequence_number);
}
bool QuicSentPacketManager::HasUnackedPackets() const {
- return !unacked_packets_.empty();
-}
-
-size_t QuicSentPacketManager::GetNumRetransmittablePackets() const {
- size_t num_unacked_packets = 0;
- for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (HasRetransmittableFrames(sequence_number)) {
- ++num_unacked_packets;
- }
- }
- return num_unacked_packets;
+ return unacked_packets_.HasUnackedPackets();
}
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;
+ return unacked_packets_.GetLeastUnackedSentPacket();
}
-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;
-}
-
-void QuicSentPacketManager::OnPacketSent(
+bool QuicSentPacketManager::OnPacketSent(
QuicPacketSequenceNumber sequence_number,
QuicTime sent_time,
QuicByteCount bytes,
TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) {
DCHECK_LT(0u, sequence_number);
- DCHECK(!ContainsKey(pending_packets_, sequence_number));
- if (ContainsKey(unacked_packets_, sequence_number)) {
- unacked_packets_[sequence_number].sent_time = sent_time;
+ LOG_IF(DFATAL, bytes == 0) << "Cannot send empty packets.";
+ pending_tlp_transmission_ = false;
+ // In rare circumstances, the packet could be serialized, sent, and then acked
+ // before OnPacketSent is called.
+ if (!unacked_packets_.IsUnacked(sequence_number)) {
+ return false;
}
- // Only track packets the send algorithm wants us to track.
- if (!send_algorithm_->OnPacketSent(sent_time, sequence_number, bytes,
- transmission_type,
- has_retransmittable_data)) {
- return;
+ if (unacked_packets_.bytes_in_flight() == 0) {
+ // TODO(ianswett): Consider being less aggressive to force a new
+ // recent_min_rtt, likely by not discarding a relatively new sample.
+ DVLOG(1) << "Sampling a new recent min rtt within 2 samples. currently:"
+ << rtt_stats_.recent_min_rtt().ToMilliseconds() << "ms";
+ rtt_stats_.SampleNewRecentMinRtt(kNumMinRttSamplesAfterQuiescence);
}
- packet_history_map_[sequence_number] = new SendAlgorithmInterface::SentPacket(
- bytes, sent_time, has_retransmittable_data);
- pending_packets_.insert(sequence_number);
- CleanupPacketHistory();
+
+ // Only track packets as in flight that the send algorithm wants us to track.
+ const bool in_flight =
+ send_algorithm_->OnPacketSent(sent_time,
+ unacked_packets_.bytes_in_flight(),
+ sequence_number,
+ bytes,
+ has_retransmittable_data);
+ unacked_packets_.SetSent(sequence_number, sent_time, bytes, in_flight);
+
+ // Reset the retransmission timer anytime a pending packet is sent.
+ return in_flight;
}
void QuicSentPacketManager::OnRetransmissionTimeout() {
- // Abandon all pending packets to ensure the congestion window
- // opens up before we attempt to retransmit packets.
- QuicTime::Delta retransmission_delay = GetRetransmissionDelay();
- QuicTime max_send_time =
- clock_->ApproximateNow().Subtract(retransmission_delay);
- for (SequenceNumberSet::iterator it = pending_packets_.begin();
- it != pending_packets_.end();) {
- QuicPacketSequenceNumber sequence_number = *it;
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- const TransmissionInfo& transmission_info =
- unacked_packets_.find(sequence_number)->second;
- // Abandon retransmittable packet and old non-retransmittable packets.
- if (transmission_info.retransmittable_frames ||
- transmission_info.sent_time <= max_send_time) {
- pending_packets_.erase(it++);
- send_algorithm_->OnPacketAbandoned(
- sequence_number, packet_history_map_[sequence_number]->bytes_sent());
- } else {
- ++it;
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ DCHECK(!pending_tlp_transmission_);
+ // Handshake retransmission, timer based loss detection, TLP, and RTO are
+ // implemented with a single alarm. The handshake alarm is set when the
+ // handshake has not completed, the loss alarm is set when the loss detection
+ // algorithm says to, and the TLP and RTO alarms are set after that.
+ // The TLP alarm is always set to run for under an RTO.
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ ++stats_->crypto_retransmit_count;
+ RetransmitCryptoPackets();
+ return;
+ case LOSS_MODE: {
+ ++stats_->loss_timeout_count;
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
+ InvokeLossDetection(clock_->Now());
+ MaybeInvokeCongestionEvent(false, bytes_in_flight);
+ return;
}
+ case TLP_MODE:
+ // If no tail loss probe can be sent, because there are no retransmittable
+ // packets, execute a conventional RTO to abandon old packets.
+ ++stats_->tlp_count;
+ ++consecutive_tlp_count_;
+ pending_tlp_transmission_ = true;
+ // TLPs prefer sending new data instead of retransmitting data, so
+ // give the connection a chance to write before completing the TLP.
+ return;
+ case RTO_MODE:
+ ++stats_->rto_count;
+ RetransmitAllPackets();
+ return;
}
+}
- // Attempt to send all the unacked packets when the RTO fires, let the
- // congestion manager decide how many to send immediately and the remaining
- // packets will be queued for future sending.
- DVLOG(1) << "OnRetransmissionTimeout() fired with "
- << unacked_packets_.size() << " unacked packets.";
+void QuicSentPacketManager::RetransmitCryptoPackets() {
+ DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode());
+ // TODO(ianswett): Typical TCP implementations only retransmit 5 times.
+ consecutive_crypto_retransmission_count_ =
+ min(kMaxHandshakeRetransmissionBackoffs,
+ consecutive_crypto_retransmission_count_ + 1);
+ bool packet_retransmitted = false;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->second.in_flight || frames == NULL ||
+ frames->HasCryptoHandshake() != IS_HANDSHAKE) {
+ continue;
+ }
+ packet_retransmitted = true;
+ MarkForRetransmission(sequence_number, HANDSHAKE_RETRANSMISSION);
+ }
+ DCHECK(packet_retransmitted) << "No crypto packets found to retransmit.";
+}
- // Retransmit any packet with retransmittable frames.
- bool packets_retransmitted = false;
- for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
+ if (!pending_tlp_transmission_) {
+ return false;
+ }
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it) {
- if (it->second.retransmittable_frames != NULL) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->second.in_flight || frames == NULL) {
+ continue;
+ }
+ DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ MarkForRetransmission(sequence_number, TLP_RETRANSMISSION);
+ return true;
+ }
+ DLOG(FATAL)
+ << "No retransmittable packets, so RetransmitOldestPacket failed.";
+ return false;
+}
+
+void QuicSentPacketManager::RetransmitAllPackets() {
+ DVLOG(1) << "RetransmitAllPackets() called with "
+ << unacked_packets_.GetNumUnackedPackets() << " unacked packets.";
+ // Request retransmission of all retransmittable packets when the RTO
+ // fires, and let the congestion manager decide how many to send
+ // immediately and the remaining packets will be queued.
+ // Abandon any non-retransmittable packets that are sufficiently old.
+ bool packets_retransmitted = false;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ if (frames != NULL) {
packets_retransmitted = true;
- MarkForRetransmission(it->first, RTO_RETRANSMISSION);
+ MarkForRetransmission(sequence_number, RTO_RETRANSMISSION);
+ } else {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
}
}
- // Only inform the sent packet manager of an RTO if data was retransmitted.
+ send_algorithm_->OnRetransmissionTimeout(packets_retransmitted);
if (packets_retransmitted) {
++consecutive_rto_count_;
- send_algorithm_->OnRetransmissionTimeout();
}
}
-void QuicSentPacketManager::OnPacketAbandoned(
- QuicPacketSequenceNumber sequence_number) {
- SequenceNumberSet::iterator it = pending_packets_.find(sequence_number);
- if (it != pending_packets_.end()) {
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- send_algorithm_->OnPacketAbandoned(
- sequence_number, packet_history_map_[sequence_number]->bytes_sent());
- pending_packets_.erase(it);
+QuicSentPacketManager::RetransmissionTimeoutMode
+ QuicSentPacketManager::GetRetransmissionMode() const {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ return HANDSHAKE_MODE;
+ }
+ if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
+ return LOSS_MODE;
+ }
+ if (consecutive_tlp_count_ < max_tail_loss_probes_) {
+ if (unacked_packets_.HasUnackedRetransmittableFrames()) {
+ return TLP_MODE;
+ }
}
+ return RTO_MODE;
}
void QuicSentPacketManager::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
const QuicTime& feedback_receive_time) {
send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(
- frame, feedback_receive_time, packet_history_map_);
+ frame, feedback_receive_time);
}
-SequenceNumberSet QuicSentPacketManager::OnIncomingAckFrame(
- const ReceivedPacketInfo& received_info,
- const QuicTime& ack_receive_time) {
- MaybeUpdateRTT(received_info, ack_receive_time);
-
- // We want to.
- // * Get all packets lower(including) than largest_observed
- // from pending_packets_.
- // * Remove all packets no longer being waited for(ie: acked).
- // * Send each ACK in the list to send_algorithm_.
- SequenceNumberSet::iterator it = pending_packets_.begin();
- SequenceNumberSet::iterator it_upper =
- pending_packets_.upper_bound(received_info.largest_observed);
-
- SequenceNumberSet retransmission_packets;
- SequenceNumberSet lost_packets;
- while (it != it_upper) {
- QuicPacketSequenceNumber sequence_number = *it;
- const SendAlgorithmInterface::SentPacket* sent_packet =
- packet_history_map_[sequence_number];
- if (!IsAwaitingPacket(received_info, sequence_number)) {
- // Not missing, hence implicitly acked.
- size_t bytes_sent = sent_packet->bytes_sent();
- send_algorithm_->OnPacketAcked(sequence_number, bytes_sent, rtt_sample_);
- pending_packets_.erase(it++); // Must be incremented post to work.
- continue;
- }
-
- // The peer got packets after this sequence number. This is an explicit
- // nack.
- DVLOG(1) << "still missing packet " << sequence_number;
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- // Consider it multiple nacks when there is a gap between the missing packet
- // and the largest observed, since the purpose of a nack threshold is to
- // tolerate re-ordering. This handles both StretchAcks and Forward Acks.
- // TODO(ianswett): This relies heavily on sequential reception of packets,
- // and makes an assumption that the congestion control uses TCP style nacks.
- size_t min_nacks = received_info.largest_observed - sequence_number;
- packet_history_map_[sequence_number]->Nack(min_nacks);
-
- size_t num_nacks_needed = kNumberOfNacksBeforeRetransmission;
- // Check for early retransmit(RFC5827) when the last packet gets acked and
- // the there are fewer than 4 pending packets.
- if (pending_packets_.size() <= kNumberOfNacksBeforeRetransmission &&
- sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA &&
- *pending_packets_.rbegin() == received_info.largest_observed) {
- num_nacks_needed = received_info.largest_observed - sequence_number;
- }
-
- if (sent_packet->nack_count() < num_nacks_needed) {
- ++it;
- continue;
- }
-
- // If the number of retransmissions has maxed out, don't lose or retransmit
- // any more packets.
- if (retransmission_packets.size() >= kMaxRetransmissionsPerAck) {
- ++it;
- continue;
- }
-
- lost_packets.insert(sequence_number);
- if (sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA) {
- retransmission_packets.insert(sequence_number);
- }
-
- ++it;
- }
- // Abandon packets after the loop over pending packets, because otherwise it
- // changes the early retransmit logic and iteration.
+void QuicSentPacketManager::InvokeLossDetection(QuicTime time) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_->DetectLostPackets(unacked_packets_,
+ time,
+ largest_observed_,
+ rtt_stats_);
for (SequenceNumberSet::const_iterator it = lost_packets.begin();
it != lost_packets.end(); ++it) {
- // TODO(ianswett): OnPacketLost is also called from TCPCubicSender when
- // an FEC packet is lost, but FEC loss information should be shared among
- // congestion managers. Additionally, if it's expected the FEC packet may
- // repair the loss, it should be recorded as a loss to the congestion
- // manager, but not retransmitted until it's known whether the FEC packet
- // arrived.
- send_algorithm_->OnPacketLost(*it, ack_receive_time);
- OnPacketAbandoned(*it);
+ QuicPacketSequenceNumber sequence_number = *it;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ // TODO(ianswett): If it's expected the FEC packet may repair the loss, it
+ // should be recorded as a loss to the send algorithm, but not retransmitted
+ // until it's known whether the FEC packet arrived.
+ ++stats_->packets_lost;
+ packets_lost_[sequence_number] = transmission_info;
+ DVLOG(1) << ENDPOINT << "Lost packet " << sequence_number;
+
+ if (transmission_info.retransmittable_frames != NULL) {
+ MarkForRetransmission(sequence_number, LOSS_RETRANSMISSION);
+ } else {
+ // Since we will not retransmit this, we need to remove it from
+ // unacked_packets_. This is either the current transmission of
+ // a packet whose previous transmission has been acked, a packet that has
+ // been TLP retransmitted, or an FEC packet.
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
}
-
- return retransmission_packets;
}
-void QuicSentPacketManager::MaybeUpdateRTT(
+bool QuicSentPacketManager::MaybeUpdateRTT(
const ReceivedPacketInfo& received_info,
const QuicTime& ack_receive_time) {
+ if (!unacked_packets_.IsUnacked(received_info.largest_observed)) {
+ return false;
+ }
// We calculate the RTT based on the highest ACKed sequence number, the lower
// sequence numbers will include the ACK aggregation delay.
- SendAlgorithmInterface::SentPacketsMap::iterator history_it =
- packet_history_map_.find(received_info.largest_observed);
- // TODO(satyamshekhar): largest_observed might be missing.
- if (history_it == packet_history_map_.end()) {
- return;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(received_info.largest_observed);
+ // Don't update the RTT if it hasn't been sent.
+ if (transmission_info.sent_time == QuicTime::Zero()) {
+ return false;
}
- QuicTime::Delta send_delta = ack_receive_time.Subtract(
- history_it->second->send_timestamp());
- if (send_delta > received_info.delta_time_largest_observed) {
- rtt_sample_ = send_delta.Subtract(
- received_info.delta_time_largest_observed);
- } else if (rtt_sample_.IsInfinite()) {
- // Even though we received information from the peer suggesting
- // an invalid (negative) RTT, we can use the send delta as an
- // approximation until we get a better estimate.
- rtt_sample_ = send_delta;
- }
+ QuicTime::Delta send_delta =
+ ack_receive_time.Subtract(transmission_info.sent_time);
+ rtt_stats_.UpdateRtt(
+ send_delta, received_info.delta_time_largest_observed, ack_receive_time);
+ return true;
}
QuicTime::Delta QuicSentPacketManager::TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake) {
- return send_algorithm_->TimeUntilSend(now, transmission_type, retransmittable,
- handshake);
+ HasRetransmittableData retransmittable) {
+ // The TLP logic is entirely contained within QuicSentPacketManager, so the
+ // send algorithm does not need to be consulted.
+ if (pending_tlp_transmission_) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_algorithm_->TimeUntilSend(
+ now, unacked_packets_.bytes_in_flight(), retransmittable);
}
// Ensures that the Delayed Ack timer is always set to a value lesser
@@ -682,52 +671,91 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend(
// 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 QuicSentPacketManager::DelayedAckTime() {
+const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const {
return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
}
-const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const {
- size_t number_retransmissions = consecutive_rto_count_;
- if (FLAGS_limit_rto_increase_for_tests) {
- const size_t kTailDropWindowSize = 5;
- const size_t kTailDropMaxRetransmissions = 4;
- if (pending_packets_.size() <= kTailDropWindowSize) {
- // Avoid exponential backoff of RTO when there are only a few packets
- // outstanding. This helps avoid the situation where fake packet loss
- // causes a packet and it's retransmission to be dropped causing
- // test timouts.
- if (number_retransmissions <= kTailDropMaxRetransmissions) {
- number_retransmissions = 0;
- } else {
- number_retransmissions -= kTailDropMaxRetransmissions;
- }
+const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
+ // Don't set the timer if there are no packets in flight or we've already
+ // queued a tlp transmission and it hasn't been sent yet.
+ if (!unacked_packets_.HasInFlightPackets() || pending_tlp_transmission_) {
+ return QuicTime::Zero();
+ }
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ return clock_->ApproximateNow().Add(GetCryptoRetransmissionDelay());
+ case LOSS_MODE:
+ return loss_algorithm_->GetLossTimeout();
+ case TLP_MODE: {
+ // TODO(ianswett): When CWND is available, it would be preferable to
+ // set the timer based on the earliest retransmittable packet.
+ // Base the updated timer on the send time of the last packet.
+ const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
+ const QuicTime tlp_time = sent_time.Add(GetTailLossProbeDelay());
+ // Ensure the TLP timer never gets set to a time in the past.
+ return QuicTime::Max(clock_->ApproximateNow(), tlp_time);
}
+ case RTO_MODE: {
+ // The RTO is based on the first outstanding packet.
+ const QuicTime sent_time =
+ unacked_packets_.GetFirstInFlightPacketSentTime();
+ QuicTime rto_timeout = sent_time.Add(GetRetransmissionDelay());
+ // Always wait at least 1.5 * RTT from now.
+ QuicTime min_timeout = clock_->ApproximateNow().Add(
+ rtt_stats_.SmoothedRtt().Multiply(1.5));
+
+ return QuicTime::Max(min_timeout, rto_timeout);
+ }
+ }
+ DCHECK(false);
+ return QuicTime::Zero();
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay()
+ const {
+ // This is equivalent to the TailLossProbeDelay, but slightly more aggressive
+ // because crypto handshake messages don't incur a delayed ack time.
+ int64 delay_ms = max<int64>(kMinHandshakeTimeoutMs,
+ 1.5 * rtt_stats_.SmoothedRtt().ToMilliseconds());
+ return QuicTime::Delta::FromMilliseconds(
+ delay_ms << consecutive_crypto_retransmission_count_);
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay() const {
+ QuicTime::Delta srtt = rtt_stats_.SmoothedRtt();
+ if (!unacked_packets_.HasMultipleInFlightPackets()) {
+ return QuicTime::Delta::Max(
+ srtt.Multiply(1.5).Add(DelayedAckTime()), srtt.Multiply(2));
}
+ return QuicTime::Delta::FromMilliseconds(
+ max(kMinTailLossProbeTimeoutMs,
+ static_cast<int64>(2 * srtt.ToMilliseconds())));
+}
+const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const {
QuicTime::Delta retransmission_delay = send_algorithm_->RetransmissionDelay();
+ // TODO(rch): This code should move to |send_algorithm_|.
if (retransmission_delay.IsZero()) {
// We are in the initial state, use default timeout values.
retransmission_delay =
QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ } else if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) {
+ retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
}
+
// Calculate exponential back off.
- retransmission_delay = QuicTime::Delta::FromMilliseconds(
- retransmission_delay.ToMilliseconds() * static_cast<size_t>(
- (1 << min<size_t>(number_retransmissions, kMaxRetransmissions))));
+ retransmission_delay = retransmission_delay.Multiply(
+ 1 << min<size_t>(consecutive_rto_count_, kMaxRetransmissions));
- // TODO(rch): This code should move to |send_algorithm_|.
- if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) {
- return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
- }
if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) {
return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs);
}
return retransmission_delay;
}
-const QuicTime::Delta QuicSentPacketManager::SmoothedRtt() const {
- return send_algorithm_->SmoothedRtt();
+const RttStats* QuicSentPacketManager::GetRttStats() const {
+ return &rtt_stats_;
}
QuicBandwidth QuicSentPacketManager::BandwidthEstimate() const {
@@ -738,27 +766,6 @@ QuicByteCount QuicSentPacketManager::GetCongestionWindow() const {
return send_algorithm_->GetCongestionWindow();
}
-void QuicSentPacketManager::CleanupPacketHistory() {
- const QuicTime::Delta kHistoryPeriod =
- QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs);
- QuicTime now = clock_->ApproximateNow();
-
- SendAlgorithmInterface::SentPacketsMap::iterator history_it =
- packet_history_map_.begin();
- for (; history_it != packet_history_map_.end(); ++history_it) {
- if (now.Subtract(history_it->second->send_timestamp()) <= kHistoryPeriod) {
- return;
- }
- // Don't remove packets which have not been acked.
- if (ContainsKey(pending_packets_, history_it->first)) {
- continue;
- }
- delete history_it->second;
- packet_history_map_.erase(history_it);
- history_it = packet_history_map_.begin();
- }
-}
-
void QuicSentPacketManager::MaybeEnablePacing() {
if (!FLAGS_enable_quic_pacing) {
return;
@@ -768,10 +775,11 @@ void QuicSentPacketManager::MaybeEnablePacing() {
return;
}
+ // Set up a pacing sender with a 5 millisecond alarm granularity.
using_pacing_ = true;
send_algorithm_.reset(
new PacingSender(send_algorithm_.release(),
- QuicTime::Delta::FromMicroseconds(1)));
+ QuicTime::Delta::FromMilliseconds(5)));
}
} // namespace net
diff --git a/chromium/net/quic/quic_sent_packet_manager.h b/chromium/net/quic/quic_sent_packet_manager.h
index 71c61e93c03..2e75786119a 100644
--- a/chromium/net/quic/quic_sent_packet_manager.h
+++ b/chromium/net/quic/quic_sent_packet_manager.h
@@ -14,14 +14,14 @@
#include <vector>
#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
#include "net/base/linked_hash_map.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_ack_notifier_manager.h"
#include "net/quic/quic_protocol.h"
-
-NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
-NET_EXPORT_PRIVATE extern bool FLAGS_limit_rto_increase_for_tests;
-NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing;
+#include "net/quic/quic_unacked_packet_map.h"
namespace net {
@@ -32,6 +32,7 @@ class QuicSentPacketManagerPeer;
class QuicClock;
class QuicConfig;
+struct QuicConnectionStats;
// Class which tracks the set of packets sent on a QUIC connection and contains
// a send algorithm to decide when to send new packets. It keeps track of any
@@ -40,6 +41,19 @@ class QuicConfig;
// previous transmission is acked, the data will not be retransmitted.
class NET_EXPORT_PRIVATE QuicSentPacketManager {
public:
+ // Interface which gets callbacks from the QuicSentPacketManager at
+ // interesting points. Implementations must not mutate the state of
+ // the packet manager or connection as a result of these callbacks.
+ class NET_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a spurious retransmission is detected.
+ virtual void OnSpuriousPacketRetransmition(
+ TransmissionType transmission_type,
+ QuicByteCount byte_size) {}
+ };
+
// Struct to store the pending retransmission information.
struct PendingRetransmission {
PendingRetransmission(QuicPacketSequenceNumber sequence_number,
@@ -58,25 +72,15 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
QuicSequenceNumberLength sequence_number_length;
};
- // Interface which provides callbacks that the manager needs.
- class NET_EXPORT_PRIVATE HelperInterface {
- public:
- virtual ~HelperInterface();
-
- // Called to return the sequence number of the next packet to be sent.
- virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() = 0;
- };
-
QuicSentPacketManager(bool is_server,
- HelperInterface* helper,
const QuicClock* clock,
- CongestionFeedbackType congestion_type);
+ QuicConnectionStats* stats,
+ CongestionFeedbackType congestion_type,
+ LossDetectionType loss_type);
virtual ~QuicSentPacketManager();
virtual void SetFromConfig(const QuicConfig& config);
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size);
-
// 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);
@@ -87,22 +91,24 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number);
- // Processes the incoming ack and returns true if the retransmission or ack
- // alarm should be reset.
- bool OnIncomingAck(const ReceivedPacketInfo& received_info,
+ // Processes the incoming ack.
+ void OnIncomingAck(const ReceivedPacketInfo& received_info,
QuicTime ack_receive_time);
- // Discards any information for the packet corresponding to |sequence_number|.
- // If this packet has been retransmitted, information on those packets
- // will be discarded as well.
- void DiscardUnackedPacket(QuicPacketSequenceNumber sequence_number);
-
// Returns true if the non-FEC packet |sequence_number| is unacked.
bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
// Requests retransmission of all unacked packets of |retransmission_type|.
void RetransmitUnackedPackets(RetransmissionType retransmission_type);
+ // Retransmits the oldest pending packet there is still a tail loss probe
+ // pending. Invoked after OnRetransmissionTimeout.
+ bool MaybeRetransmitTailLossProbe();
+
+ // Removes the retransmittable frames from all unencrypted packets to ensure
+ // they don't get retransmitted.
+ void NeuterUnencryptedPackets();
+
// Returns true if the unacked packet |sequence_number| has retransmittable
// frames. This will only return false if the packet has been acked, if a
// previous transmission of this packet was ACK'd, or if this packet has been
@@ -117,39 +123,19 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
bool HasUnackedPackets() const;
- // Returns the number of unacked packets which have retransmittable frames.
- size_t GetNumRetransmittablePackets() const;
-
- // Returns the smallest sequence number of a sent packet which has not been
- // acked by the peer. Excludes any packets which have been retransmitted
- // with a new sequence number. If all packets have been acked, returns the
- // sequence number of the next packet that will be sent.
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
- // Returns the set of sequence numbers of all unacked packets.
- // Test only.
- SequenceNumberSet GetUnackedPackets() const;
-
- // Returns true if |sequence_number| is a previous transmission of packet.
- bool IsPreviousTransmission(QuicPacketSequenceNumber sequence_number) const;
-
- // TODO(ianswett): Combine the congestion control related methods below with
- // some of the methods above and cleanup the resulting code.
- // Called when we have received an ack frame from peer.
- // Returns a set containing all the sequence numbers to be nack retransmitted
- // as a result of the ack.
- virtual SequenceNumberSet OnIncomingAckFrame(
- const ReceivedPacketInfo& received_info,
- const QuicTime& ack_receive_time);
-
// Called when a congestion feedback frame is received from peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
const QuicTime& feedback_receive_time);
// Called when we have sent bytes to the peer. This informs the manager both
- // the number of bytes sent and if they were retransmitted.
- virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
+ // the number of bytes sent and if they were retransmitted. Returns true if
+ // the sender should reset the retransmission timer.
+ virtual bool OnPacketSent(QuicPacketSequenceNumber sequence_number,
QuicTime sent_time,
QuicByteCount bytes,
TransmissionType transmission_type,
@@ -158,28 +144,23 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// Called when the retransmission timer expires.
virtual void OnRetransmissionTimeout();
- // Called when a packet is timed out, such as an RTO. Removes the bytes from
- // the congestion manager, but does not change the congestion window size.
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number);
-
// Calculate the time until we can send the next packet to the wire.
// Note 1: When kUnknownWaitTime is returned, there is no need to poll
// TimeUntilSend again until we receive an OnIncomingAckFrame event.
// Note 2: Send algorithms may or may not use |retransmit| in their
// calculations.
virtual QuicTime::Delta TimeUntilSend(QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake);
+ HasRetransmittableData retransmittable);
// Returns amount of time for delayed ack timer.
- const QuicTime::Delta DelayedAckTime();
+ const QuicTime::Delta DelayedAckTime() const;
- // Returns the current RTO delay.
- const QuicTime::Delta GetRetransmissionDelay() const;
+ // Returns the current delay for the retransmission timer, which may send
+ // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if
+ // there are no retransmittable packets.
+ const QuicTime GetRetransmissionTime() const;
- // Returns the estimated smoothed RTT calculated by the congestion algorithm.
- const QuicTime::Delta SmoothedRtt() const;
+ const RttStats* GetRttStats() const;
// Returns the estimated bandwidth calculated by the congestion algorithm.
QuicBandwidth BandwidthEstimate() const;
@@ -195,73 +176,91 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
bool using_pacing() const { return using_pacing_; }
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+ }
private:
friend class test::QuicConnectionPeer;
friend class test::QuicSentPacketManagerPeer;
- struct TransmissionInfo {
- TransmissionInfo()
- : retransmittable_frames(NULL),
- sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER),
- sent_time(QuicTime::Zero()),
- previous_transmissions(NULL) { }
- TransmissionInfo(RetransmittableFrames* retransmittable_frames,
- QuicSequenceNumberLength sequence_number_length)
- : retransmittable_frames(retransmittable_frames),
- sequence_number_length(sequence_number_length),
- sent_time(QuicTime::Zero()),
- previous_transmissions(NULL) {
- }
-
- RetransmittableFrames* retransmittable_frames;
- QuicSequenceNumberLength sequence_number_length;
- // Zero when the packet is serialized, non-zero once it's sent.
- QuicTime sent_time;
- // Stores all previous transmissions if the packet has been retransmitted,
- // and is NULL otherwise.
- SequenceNumberSet* previous_transmissions;
+ // The retransmission timer is a single timer which switches modes depending
+ // upon connection state.
+ enum RetransmissionTimeoutMode {
+ // A conventional TCP style RTO.
+ RTO_MODE,
+ // A tail loss probe. By default, QUIC sends up to two before RTOing.
+ TLP_MODE,
+ // Retransmission of handshake packets prior to handshake completion.
+ HANDSHAKE_MODE,
+ // Re-invoke the loss detection when a packet is not acked before the
+ // loss detection algorithm expects.
+ LOSS_MODE,
};
typedef linked_hash_map<QuicPacketSequenceNumber,
- TransmissionInfo> UnackedPacketMap;
- typedef linked_hash_map<QuicPacketSequenceNumber,
TransmissionType> PendingRetransmissionMap;
- typedef base::hash_map<QuicPacketSequenceNumber, SequenceNumberSet*>
- PreviousTransmissionMap;
// Process the incoming ack looking for newly ack'd data packets.
void HandleAckForSentPackets(const ReceivedPacketInfo& received_info);
+ // Returns the current retransmission mode.
+ RetransmissionTimeoutMode GetRetransmissionMode() const;
+
+ // Retransmits all crypto stream packets.
+ void RetransmitCryptoPackets();
+
+ // Retransmits all the packets and abandons by invoking a full RTO.
+ void RetransmitAllPackets();
+
+ // Returns the timer for retransmitting crypto handshake packets.
+ const QuicTime::Delta GetCryptoRetransmissionDelay() const;
+
+ // Returns the timer for a new tail loss probe.
+ const QuicTime::Delta GetTailLossProbeDelay() const;
+
+ // Returns the retransmission timeout, after which a full RTO occurs.
+ const QuicTime::Delta GetRetransmissionDelay() const;
+
// Update the RTT if the ack is for the largest acked sequence number.
- void MaybeUpdateRTT(const ReceivedPacketInfo& received_info,
+ // Returns true if the rtt was updated.
+ bool MaybeUpdateRTT(const ReceivedPacketInfo& received_info,
const QuicTime& ack_receive_time);
- // Marks |sequence_number| as having been seen by the peer. Returns an
- // iterator to the next remaining unacked packet.
- UnackedPacketMap::iterator MarkPacketReceivedByPeer(
- QuicPacketSequenceNumber sequence_number);
-
- // Simply removes the entries, if any, from the unacked packet map
- // and the retransmission map.
- void DiscardPacket(QuicPacketSequenceNumber sequence_number);
+ // Invokes the loss detection algorithm and loses and retransmits packets if
+ // necessary.
+ void InvokeLossDetection(QuicTime time);
+
+ // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks,
+ // or pending losses. Clears pending acks and pending losses afterwards.
+ // |bytes_in_flight| is the number of bytes in flight before the losses or
+ // acks.
+ void MaybeInvokeCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight);
+
+ // Marks |sequence_number| as having been revived by the peer, but not
+ // received, so the packet remains pending if it is and the congestion control
+ // does not consider the packet acked.
+ void MarkPacketRevived(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
+
+ // Removes the retransmittability and pending properties from the packet at
+ // |it| due to receipt by the peer. Returns an iterator to the next remaining
+ // unacked packet.
+ QuicUnackedPacketMap::const_iterator MarkPacketHandled(
+ QuicUnackedPacketMap::const_iterator it,
+ QuicTime::Delta delta_largest_observed);
// Request that |sequence_number| be retransmitted after the other pending
- // retransmissions. Returns false if there are no retransmittable frames for
- // |sequence_number| and true if it will be retransmitted.
- bool MarkForRetransmission(QuicPacketSequenceNumber sequence_number,
+ // retransmissions. Does not add it to the retransmissions if it's already
+ // a pending retransmission.
+ void MarkForRetransmission(QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type);
- // Returns the length of the serialized sequence number for
- // the packet |sequence_number|.
- QuicSequenceNumberLength GetSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number) const;
-
- // Clears up to |num_to_clear| previous transmissions in order to make room
- // in the ack frame for new acks.
- void ClearPreviousRetransmissions(size_t num_to_clear);
-
- void CleanupPacketHistory();
+ // Notify observers about spurious retransmits.
+ void RecordSpuriousRetransmissions(
+ const SequenceNumberSet& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number);
// Newly serialized retransmittable and fec packets are added to this map,
// which contains owning pointers to any contained frames. If a packet is
@@ -271,7 +270,7 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// If the old packet is acked before the new packet, then the old entry will
// be removed from the map and the new entry's retransmittable frames will be
// set to NULL.
- UnackedPacketMap unacked_packets_;
+ QuicUnackedPacketMap unacked_packets_;
// Pending retransmissions which have not been packetized and sent yet.
PendingRetransmissionMap pending_retransmissions_;
@@ -279,25 +278,35 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// Tracks if the connection was created by the server.
bool is_server_;
- HelperInterface* helper_;
-
// An AckNotifier can register to be informed when ACKs have been received for
// all packets that a given block of data was sent in. The AckNotifierManager
// maintains the currently active notifiers.
AckNotifierManager ack_notifier_manager_;
const QuicClock* clock_;
+ QuicConnectionStats* stats_;
+ DebugDelegate* debug_delegate_;
+ RttStats rtt_stats_;
scoped_ptr<SendAlgorithmInterface> send_algorithm_;
- // Tracks the send time, size, and nack count of sent packets. Packets are
- // removed after 5 seconds and they've been removed from pending_packets_.
- SendAlgorithmInterface::SentPacketsMap packet_history_map_;
- // Packets that are outstanding and have not been abandoned or lost.
- SequenceNumberSet pending_packets_;
- QuicTime::Delta rtt_sample_; // RTT estimate from the most recent ACK.
+ scoped_ptr<LossDetectionInterface> loss_algorithm_;
+
+ QuicPacketSequenceNumber largest_observed_; // From the most recent ACK.
// Number of times the RTO timer has fired in a row without receiving an ack.
size_t consecutive_rto_count_;
+ // Number of times the tail loss probe has been sent.
+ size_t consecutive_tlp_count_;
+ // Number of times the crypto handshake has been retransmitted.
+ size_t consecutive_crypto_retransmission_count_;
+ // Whether a tlp packet can be sent even if the send algorithm says not to.
+ bool pending_tlp_transmission_;
+ // Maximum number of tail loss probes to send before firing an RTO.
+ size_t max_tail_loss_probes_;
bool using_pacing_;
+ // Sets of packets acked and lost as a result of the last congestion event.
+ SendAlgorithmInterface::CongestionMap packets_acked_;
+ SendAlgorithmInterface::CongestionMap packets_lost_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
};
diff --git a/chromium/net/quic/quic_sent_packet_manager_test.cc b/chromium/net/quic/quic_sent_packet_manager_test.cc
index 168cbf378cf..28f433216d9 100644
--- a/chromium/net/quic/quic_sent_packet_manager_test.cc
+++ b/chromium/net/quic/quic_sent_packet_manager_test.cc
@@ -5,42 +5,65 @@
#include "net/quic/quic_sent_packet_manager.h"
#include "base/stl_util.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::vector;
-using testing::_;
+using testing::ElementsAre;
+using testing::Pair;
+using testing::Pointwise;
using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-class MockHelper : public QuicSentPacketManager::HelperInterface {
+// Default packet length.
+const uint32 kDefaultLength = 1000;
+
+// Matcher to check the key of the key-value pair it receives as first argument
+// equals its second argument.
+MATCHER(KeyEq, "") {
+ return std::tr1::get<0>(arg).first == std::tr1::get<1>(arg);
+}
+
+class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate {
public:
- MOCK_METHOD0(GetNextPacketSequenceNumber, QuicPacketSequenceNumber());
+ MOCK_METHOD2(OnSpuriousPacketRetransmition,
+ void(TransmissionType transmission_type,
+ QuicByteCount byte_size));
};
class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> {
protected:
QuicSentPacketManagerTest()
- : manager_(true, &helper_, &clock_, kFixRate),
+ : manager_(true, &clock_, &stats_, kFixRate, kNack),
send_algorithm_(new StrictMock<MockSendAlgorithm>) {
QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_);
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0);
+ // Advance the time 1s so the send times are never QuicTime::Zero.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
}
- ~QuicSentPacketManagerTest() {
+ virtual ~QuicSentPacketManagerTest() OVERRIDE {
STLDeleteElements(&packets_);
}
+ QuicByteCount BytesInFlight() {
+ return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_);
+ }
void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
size_t num_packets) {
if (num_packets == 0) {
EXPECT_FALSE(manager_.HasUnackedPackets());
- EXPECT_EQ(0u, manager_.GetNumRetransmittablePackets());
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
return;
}
@@ -53,102 +76,188 @@ class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> {
void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
size_t num_packets) {
- SequenceNumberSet unacked = manager_.GetUnackedPackets();
+ EXPECT_EQ(num_packets,
+ QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
for (size_t i = 0; i < num_packets; ++i) {
- EXPECT_TRUE(ContainsKey(unacked, packets[i])) << packets[i];
- }
- size_t num_retransmittable = 0;
- for (SequenceNumberSet::const_iterator it = unacked.begin();
- it != unacked.end(); ++it) {
- if (manager_.HasRetransmittableFrames(*it)) {
- ++num_retransmittable;
- }
+ EXPECT_TRUE(manager_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
}
- EXPECT_EQ(num_packets, manager_.GetNumRetransmittablePackets());
- EXPECT_EQ(num_packets, num_retransmittable);
}
- void VerifyAckedPackets(QuicPacketSequenceNumber* expected,
- size_t num_expected,
- const SequenceNumberSet& actual) {
- if (num_expected == 0) {
- EXPECT_TRUE(actual.empty());
- return;
- }
+ void ExpectAck(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ true, _, ElementsAre(Pair(largest_observed, _)), _));
+ }
+
+ void ExpectUpdatedRtt(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _, _));
+ }
+
+ void ExpectAckAndLoss(bool rtt_updated,
+ QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber lost_packet) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ rtt_updated, _, ElementsAre(Pair(largest_observed, _)),
+ ElementsAre(Pair(lost_packet, _))));
+ }
- EXPECT_EQ(num_expected, actual.size());
- for (size_t i = 0; i < num_expected; ++i) {
- EXPECT_TRUE(ContainsKey(actual, expected[i])) << expected[i];
+ // |packets_acked| and |packets_lost| should be in sequence number order.
+ void ExpectAcksAndLosses(bool rtt_updated,
+ QuicPacketSequenceNumber* packets_acked,
+ size_t num_packets_acked,
+ QuicPacketSequenceNumber* packets_lost,
+ size_t num_packets_lost) {
+ vector<QuicPacketSequenceNumber> ack_vector;
+ for (size_t i = 0; i < num_packets_acked; ++i) {
+ ack_vector.push_back(packets_acked[i]);
+ }
+ vector<QuicPacketSequenceNumber> lost_vector;
+ for (size_t i = 0; i < num_packets_lost; ++i) {
+ lost_vector.push_back(packets_lost[i]);
}
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(rtt_updated, _,
+ Pointwise(KeyEq(), ack_vector),
+ Pointwise(KeyEq(), lost_vector)));
}
+ // Retransmits a packet as though it was a TLP retransmission, because TLP
+ // leaves the |old_sequence_number| pending.
+ // TODO(ianswett): Test with transmission types besides TLP.
void RetransmitPacket(QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
QuicSentPacketManagerPeer::MarkForRetransmission(
- &manager_, old_sequence_number, NACK_RETRANSMISSION);
+ &manager_, old_sequence_number, TLP_RETRANSMISSION);
EXPECT_TRUE(manager_.HasPendingRetransmissions());
QuicSentPacketManager::PendingRetransmission next_retransmission =
manager_.NextPendingRetransmission();
EXPECT_EQ(old_sequence_number, next_retransmission.sequence_number);
- EXPECT_EQ(NACK_RETRANSMISSION, next_retransmission.transmission_type);
- manager_.OnRetransmittedPacket(old_sequence_number, new_sequence_number);
+ EXPECT_EQ(TLP_RETRANSMISSION,
+ next_retransmission.transmission_type);
+ manager_.OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number);
EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(
&manager_, new_sequence_number));
}
- SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number) {
+ void RetransmitAndSendPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ RetransmitPacket(old_sequence_number, new_sequence_number);
+
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), new_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(true));
+ manager_.OnPacketSent(new_sequence_number,
+ clock_.Now(),
+ kDefaultLength,
+ LOSS_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ SerializedPacket CreateDataPacket(QuicPacketSequenceNumber sequence_number) {
+ return CreatePacket(sequence_number, true);
+ }
+
+ SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number,
+ bool retransmittable) {
packets_.push_back(QuicPacket::NewDataPacket(
- NULL, 0, false, PACKET_8BYTE_GUID, false,
+ NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
PACKET_6BYTE_SEQUENCE_NUMBER));
- return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
- packets_.back(), 0u, new RetransmittableFrames());
+ return SerializedPacket(
+ sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packets_.back(), 0u,
+ retransmittable ? new RetransmittableFrames() : NULL);
}
SerializedPacket CreateFecPacket(QuicPacketSequenceNumber sequence_number) {
packets_.push_back(QuicPacket::NewFecPacket(
- NULL, 0, false, PACKET_8BYTE_GUID, false,
+ NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
PACKET_6BYTE_SEQUENCE_NUMBER));
return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
packets_.back(), 0u, NULL);
}
void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, sequence_number, _, _, _))
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number, _, _))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void SendCryptoPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
.Times(1).WillOnce(Return(true));
- SerializedPacket packet(CreatePacket(sequence_number));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ packet.retransmittable_frames->AddStreamFrame(
+ new QuicStreamFrame(1, false, 0, IOVector()));
+ packet.retransmittable_frames->set_encryption_level(ENCRYPTION_NONE);
manager_.OnSerializedPacket(packet);
manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(),
packet.packet->length(), NOT_RETRANSMISSION,
HAS_RETRANSMITTABLE_DATA);
}
+ void SendFecPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateFecPacket(sequence_number));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
+ void SendAckPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(false));
+ SerializedPacket packet(CreatePacket(sequence_number, false));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
// Based on QuicConnection's WritePendingRetransmissions.
void RetransmitNextPacket(
QuicPacketSequenceNumber retransmission_sequence_number) {
EXPECT_TRUE(manager_.HasPendingRetransmissions());
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, retransmission_sequence_number, _, _, _))
+ OnPacketSent(_, _, retransmission_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
.Times(1).WillOnce(Return(true));
const QuicSentPacketManager::PendingRetransmission pending =
manager_.NextPendingRetransmission();
- manager_.OnRetransmittedPacket(
- pending.sequence_number, retransmission_sequence_number);
- manager_.OnPacketSent(retransmission_sequence_number,
- clock_.ApproximateNow(), 1000,
- pending.transmission_type, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnRetransmittedPacket(pending.sequence_number,
+ retransmission_sequence_number);
+ manager_.OnPacketSent(retransmission_sequence_number, clock_.Now(),
+ kDefaultLength, pending.transmission_type,
+ HAS_RETRANSMITTABLE_DATA);
}
- testing::StrictMock<MockHelper> helper_;
QuicSentPacketManager manager_;
vector<QuicPacket*> packets_;
MockClock clock_;
+ QuicConnectionStats stats_;
MockSendAlgorithm* send_algorithm_;
};
TEST_F(QuicSentPacketManagerTest, IsUnacked) {
VerifyUnackedPackets(NULL, 0);
- SerializedPacket serialized_packet(CreatePacket(1));
+ SerializedPacket serialized_packet(CreateDataPacket(1));
manager_.OnSerializedPacket(serialized_packet);
@@ -159,9 +268,7 @@ TEST_F(QuicSentPacketManagerTest, IsUnacked) {
}
TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
RetransmitPacket(1, 2);
EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2));
@@ -172,34 +279,34 @@ TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAck) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
// Ack 2 but not 1.
ReceivedPacketInfo received_info;
received_info.largest_observed = 2;
received_info.missing_packets.insert(1);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
- // No unacked packets remain.
- VerifyUnackedPackets(NULL, 0);
+ // Packet 1 is unacked, pending, but not retransmittable.
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
QuicSentPacketManagerPeer::MarkForRetransmission(
- &manager_, 1, NACK_RETRANSMISSION);
+ &manager_, 1, TLP_RETRANSMISSION);
EXPECT_TRUE(manager_.HasPendingRetransmissions());
// Ack 1.
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
// There should no longer be a pending retransmission.
EXPECT_FALSE(manager_.HasPendingRetransmissions());
@@ -207,211 +314,311 @@ TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
// No unacked packets remain.
VerifyUnackedPackets(NULL, 0);
VerifyRetransmittablePackets(NULL, 0);
+ EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPrevious) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
RetransmitPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2.
+ ExpectAck(1);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // 2 should be unacked, since it may provide an RTT measurement.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitAndSendThenAckPrevious) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
// Ack 1 but not 2.
+ ExpectAck(1);
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
// 2 remains unacked, but no packets have retransmittable data.
QuicPacketSequenceNumber unacked[] = { 2 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
- // Verify that if the retransmission alarm does fire to abandon packet 2,
- // the sent packet manager is not notified, since there is no retransmittable
- // data outstanding.
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) {
+ SendDataPacket(1);
+ RetransmitPacket(1, 2);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _))
+ .WillOnce(Return(true));
+ manager_.OnPacketSent(2, clock_.ApproximateNow(), kDefaultLength,
+ LOSS_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // First, ACK packet 1 which makes packet 2 non-retransmittable.
+ ExpectAck(1);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendDataPacket(5);
+ clock_.AdvanceTime(rtt);
+
+ // Next, NACK packet 2 three times.
+ received_info.largest_observed = 3;
+ received_info.missing_packets.insert(2);
+ ExpectAck(3);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ received_info.largest_observed = 4;
+ ExpectAck(4);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ received_info.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // No packets remain unacked.
+ VerifyUnackedPackets(NULL, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckPreviousBeforeSend) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Fire the RTO, which will mark 2 for retransmission (but will not send it).
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // Ack 1 but not 2, before 2 is able to be sent.
+ // Since 1 has been retransmitted, it has already been lost, and so the
+ // send algorithm is not informed that it has been ACK'd.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ ExpectUpdatedRtt(1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
}
TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ StrictMock<MockDebugDelegate> debug_delegate;
+ EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmition(
+ TLP_RETRANSMISSION, kDefaultLength)).Times(2);
+ manager_.set_debug_delegate(&debug_delegate);
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
- RetransmitPacket(2, 3);
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
// Ack 1 but not 2 or 3.
+ ExpectAck(1);
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // 3 remains unacked, but no packets have retransmittable data.
- QuicPacketSequenceNumber unacked[] = { 3 };
+ // 2 and 3 remain unacked, but no packets have retransmittable data.
+ QuicPacketSequenceNumber unacked[] = { 2, 3 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
- // Verify that if the retransmission alarm does fire to abandon packet 3,
- // the sent packet manager is not notified, since there is no retransmittable
- // data outstanding.
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
- manager_.OnRetransmissionTimeout();
-}
+ // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked.
+ SendDataPacket(4);
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(2);
+ QuicPacketSequenceNumber acked[] = { 3, 4 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
-TEST_F(QuicSentPacketManagerTest, TruncatedAck) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ QuicPacketSequenceNumber unacked2[] = { 2 };
+ VerifyUnackedPackets(unacked2, arraysize(unacked2));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
- RetransmitPacket(2, 3);
- RetransmitPacket(3, 4);
+ SendDataPacket(5);
+ received_info.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ VerifyUnackedPackets(NULL, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, LoseButDontRetransmitRevivedPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendFecPacket(3);
+ SendDataPacket(4);
- // Truncated ack with 2 NACKs
+ // Ack 2 and 3, and mark 1 as revived.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 2;
+ received_info.largest_observed = 3;
received_info.missing_packets.insert(1);
- received_info.missing_packets.insert(2);
- received_info.is_truncated = true;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ received_info.revived_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // High water mark will be raised.
- QuicPacketSequenceNumber unacked[] = { 2, 3, 4 };
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 4 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
QuicPacketSequenceNumber retransmittable[] = { 4 };
VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
-}
-TEST_F(QuicSentPacketManagerTest, SendDropAckRetransmitManyPackets) {
- manager_.OnSerializedPacket(CreatePacket(1));
- manager_.OnSerializedPacket(CreatePacket(2));
- manager_.OnSerializedPacket(CreatePacket(3));
+ // Ack the 4th packet and expect the 1st to be considered lost.
+ received_info.largest_observed = 4;
+ ExpectAckAndLoss(true, 4, 1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- {
- // Ack packets 1 and 3.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 3;
- received_info.missing_packets.insert(2);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
-
- manager_.OnSerializedPacket(CreatePacket(4));
- manager_.OnSerializedPacket(CreatePacket(5));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(NULL, 0);
+}
- {
- // Ack packets 5.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 5;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+TEST_F(QuicSentPacketManagerTest, MarkLostThenReviveAndDontRetransmitPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendFecPacket(5);
- QuicPacketSequenceNumber unacked[] = { 2, 4 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2, 4 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ // Ack 2, 3, and 4, and expect the 1st to be considered lost.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3, 4 };
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- manager_.OnSerializedPacket(CreatePacket(6));
- manager_.OnSerializedPacket(CreatePacket(7));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- {
- // Ack packets 7.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 7;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ // Ack 5th packet (FEC) and revive 1st packet. 1st packet should now be
+ // removed from pending retransmissions map.
+ received_info.largest_observed = 5;
+ received_info.revived_packets.insert(1);
+ ExpectAck(5);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2, 4, 6 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(NULL, 0);
+}
- RetransmitPacket(2, 8);
- manager_.OnSerializedPacket(CreatePacket(9));
- manager_.OnSerializedPacket(CreatePacket(10));
+TEST_F(QuicSentPacketManagerTest, TruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ RetransmitAndSendPacket(4, 5);
- {
- // Ack packet 10.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 10;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 4, 6, 8, 9 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ // Truncated ack with 4 NACKs, so the first packet is lost.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(1);
+ received_info.missing_packets.insert(2);
+ received_info.missing_packets.insert(3);
+ received_info.missing_packets.insert(4);
+ received_info.is_truncated = true;
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, NULL, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.Now());
- RetransmitPacket(4, 11);
- manager_.OnSerializedPacket(CreatePacket(12));
- manager_.OnSerializedPacket(CreatePacket(13));
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 2, 3, 4, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+TEST_F(QuicSentPacketManagerTest, AckPreviousTransmissionThenTruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ manager_.OnSerializedPacket(CreateDataPacket(5));
+ manager_.OnSerializedPacket(CreateDataPacket(6));
+ manager_.OnSerializedPacket(CreateDataPacket(7));
+ manager_.OnSerializedPacket(CreateDataPacket(8));
+ manager_.OnSerializedPacket(CreateDataPacket(9));
+
+ // Ack previous transmission
{
- // Ack packet 13.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 13;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- received_info.missing_packets.insert(11);
- received_info.missing_packets.insert(12);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9, 11, 12 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 6, 8, 9, 11, 12 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_TRUE(manager_.IsUnacked(4));
}
- RetransmitPacket(6, 14);
- manager_.OnSerializedPacket(CreatePacket(15));
- manager_.OnSerializedPacket(CreatePacket(16));
-
+ // Truncated ack with 4 NACKs
{
- // Ack packet 16.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 13;
- received_info.missing_packets.insert(2);
+ received_info.largest_observed = 6;
+ received_info.missing_packets.insert(3);
received_info.missing_packets.insert(4);
+ received_info.missing_packets.insert(5);
received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- received_info.missing_packets.insert(11);
- received_info.missing_packets.insert(12);
received_info.is_truncated = true;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- // Truncated ack raises the high water mark by clearing out 2, 4, and 6.
- QuicPacketSequenceNumber unacked[] = { 8, 9, 11, 12, 14, 15, 16 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 8, 9, 11, 12, 14, 15, 16 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ ExpectAckAndLoss(false, 1, 3);
+ manager_.OnIncomingAck(received_info, clock_.Now());
}
+
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 4, 5, 6, 7, 8, 9 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5, 6, 7, 8, 9 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
}
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacket) {
- EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(1u));
- EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+ EXPECT_EQ(0u, manager_.GetLeastUnackedSentPacket());
}
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnacked) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ SerializedPacket serialized_packet(CreateDataPacket(1));
manager_.OnSerializedPacket(serialized_packet);
EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
@@ -424,15 +631,6 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnackedFec) {
EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
}
-TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketDiscardUnacked) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
- manager_.DiscardUnackedPacket(1u);
- EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
- EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
-}
-
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) {
VerifyUnackedPackets(NULL, 0);
@@ -452,19 +650,12 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) {
VerifyUnackedPackets(unacked, arraysize(unacked));
VerifyRetransmittablePackets(NULL, 0);
- manager_.DiscardUnackedPacket(1);
- EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
-
- // Ack 2.
+ // Ack 2, which has never been sent, so there's no rtt update.
ReceivedPacketInfo received_info;
received_info.largest_observed = 2;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.Now());
EXPECT_EQ(3u, manager_.GetLeastUnackedSentPacket());
-
- // Discard the 3rd packet and ensure there are no FEC packets.
- manager_.DiscardUnackedPacket(3);
- EXPECT_FALSE(manager_.HasUnackedPackets());
}
TEST_F(QuicSentPacketManagerTest, GetSentTime) {
@@ -472,17 +663,18 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) {
SerializedPacket serialized_packet(CreateFecPacket(1));
manager_.OnSerializedPacket(serialized_packet);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _))
.Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(
- 1, QuicTime::Zero(), 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnPacketSent(1, QuicTime::Zero(), kDefaultLength, NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+
SerializedPacket serialized_packet2(CreateFecPacket(2));
QuicTime sent_time = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(1));
manager_.OnSerializedPacket(serialized_packet2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2, _, _, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _))
.Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(
- 2, sent_time, 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnPacketSent(2, sent_time, kDefaultLength, NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
QuicPacketSequenceNumber unacked[] = { 1, 2 };
VerifyUnackedPackets(unacked, arraysize(unacked));
@@ -494,332 +686,431 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) {
EXPECT_EQ(sent_time, QuicSentPacketManagerPeer::GetSentTime(&manager_, 2));
}
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1Packet) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, AckAckAndUpdateRtt) {
+ SendDataPacket(1);
+ SendAckPacket(2);
- // Nack the first packet 3 times with increasing largest observed.
+ // Now ack the ack and expect an RTT update.
ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
received_info.delta_time_largest_observed =
QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- for (QuicPacketSequenceNumber i = 1; i <= 3; ++i) {
- received_info.largest_observed = i + 1;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(i + 1, _, _)).Times(1);
- if (i == 3) {
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- }
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(i == 3 ? 1u : 0u, retransmissions.size());
- EXPECT_EQ(i, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
- }
+
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ SendAckPacket(3);
+
+ // Now ack the ack and expect only an RTT update.
+ received_info.largest_observed = 3;
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(received_info, clock_.Now());
}
-// A stretch ack is an ack that covers more than 1 packet of previously
-// unacknowledged data.
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketWith1StretchAck) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, Rtt) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
- // Nack the first packet 3 times in a single StretchAck.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-// Ack a packet 3 packets ahead, causing a retransmit.
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketSingleAck) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ QuicTime::Delta::FromMilliseconds(5);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
- // Nack the first packet 3 times in an AckFrame with three missing packets.
+TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is larger than the local time elapsed
+ // and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(3);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, EarlyRetransmit1Packet) {
- const size_t kNumSentPackets = 2;
- // Transmit 2 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ QuicTime::Delta::FromMilliseconds(11);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is infinite, and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
- // Early retransmit when the final packet gets acked and the first is nacked.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, DontEarlyRetransmitPacket) {
- const size_t kNumSentPackets = 4;
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ received_info.largest_observed = sequence_number;
+ received_info.delta_time_largest_observed = QuicTime::Delta::Infinite();
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttZeroDelta) {
+ // Expect that the RTT is the time between send and receive since the
+ // delta_time_largest_observed is zero.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
- // Fast retransmit when the final packet gets acked, but don't early
- // retransmit as well, because there are 4 packets outstanding when the ack
- // arrives.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
+ received_info.largest_observed = sequence_number;
+ received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeout) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 1 packet.
+ QuicPacketSequenceNumber sequence_number = 1;
+ SendDataPacket(sequence_number);
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // Ack the third and ensure the first two are still pending.
+ ExpectAck(3);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 3;
received_info.missing_packets.insert(1);
received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(3);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, NackRetransmit2Packets) {
- const size_t kNumSentPackets = 20;
- // Transmit 20 packets.
- for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // Nack the first 19 packets 3 times.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = kNumSentPackets;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- received_info.missing_packets.insert(i);
- }
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(2u, retransmissions.size());
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- EXPECT_EQ(kNumSentPackets - i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, i));
- }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Acking two more packets will lose both of them due to nacks.
+ received_info.largest_observed = 5;
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ ExpectAcksAndLosses(false, NULL, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(0u, stats_.rto_count);
}
-TEST_F(QuicSentPacketManagerTest, NackRetransmit2PacketsAlternateAcks) {
- const size_t kNumSentPackets = 30;
- // Transmit 15 packets of data and 15 ack packets. The send algorithm will
- // inform the congestion manager not to save the acks by returning false.
- for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(i % 2 == 0 ? false : true));
- manager_.OnPacketSent(
- i, clock_.Now(), 1000, NOT_RETRANSMISSION,
- i % 2 == 0 ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
- // Nack the first 29 packets 3 times.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = kNumSentPackets;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- received_info.missing_packets.insert(i);
- }
- // We never actually get an ack call, since the kNumSentPackets packet was
- // not saved.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(2u, retransmissions.size());
- // Only non-ack packets have a nack count.
- for (size_t i = 1; i < kNumSentPackets; i += 2) {
- EXPECT_EQ(kNumSentPackets - i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, i));
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
}
- // Ensure only the odd packets were retransmitted, since the others were not
- // retransmittable(ie: acks).
- for (SequenceNumberSet::const_iterator it = retransmissions.begin();
- it != retransmissions.end(); ++it) {
- EXPECT_EQ(1u, *it % 2);
- }
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(101);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(102);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+
+ // Advance the time enough to ensure all packets are RTO'd.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+
+ // The final RTO abandons all of them.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(1u, stats_.rto_count);
}
-TEST_F(QuicSentPacketManagerTest, NackTwiceThenAck) {
- // Transmit 4 packets.
- for (QuicPacketSequenceNumber i = 1; i <= 4; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
}
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- // Nack the first packet 2 times, then ack it.
+ // The second retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(8);
+ RetransmitNextPacket(9);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now ack the two crypto packets and the speculatively encrypted request,
+ // and ensure the first four crypto packets get abandoned, but not lost.
+ QuicPacketSequenceNumber acked[] = { 3, 4, 5, 8, 9 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = 9;
received_info.missing_packets.insert(1);
- for (size_t i = 1; i <= 3; ++i) {
- if (i == 3) {
- received_info.missing_packets.clear();
- }
- received_info.largest_observed = i + 1;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(_, _, _)).Times(i == 3 ? 2 : 1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(0u, retransmissions.size());
- // The nack count remains at 2 when the packet is acked.
- EXPECT_EQ(i == 3 ? 2u : i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
+ received_info.missing_packets.insert(2);
+ received_info.missing_packets.insert(6);
+ received_info.missing_packets.insert(7);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
}
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmission timeout retransmits 2 crypto packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now act like a version negotiation packet arrived, which would cause all
+ // unacked packets to be retransmitted.
+ manager_.RetransmitUnackedPackets(ALL_PACKETS);
+
+ // Ensure the first two pending packets are the crypto retransmits.
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(6u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(8);
+ EXPECT_EQ(7u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(9);
+
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
}
-TEST_F(QuicSentPacketManagerTest, Rtt) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ // Now ack the second crypto packet, and ensure the first gets removed, but
+ // the third does not.
+ ExpectUpdatedRtt(2);
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ QuicPacketSequenceNumber unacked[] = { 3 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
}
-TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) {
- // Expect that the RTT is equal to the local time elapsed, since the
- // delta_time_largest_observed is larger than the local time elapsed
- // and is hence invalid.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) {
+ // Send 2 crypto packets and serialize 1 data packet.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ SerializedPacket packet(CreateDataPacket(3));
+ manager_.OnSerializedPacket(packet);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit 2 crypto packets, but not the serialized packet.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenRetransmitAll) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(11);
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ // Now retransmit all the unacked packets, which occurs when there is a
+ // version negotiation.
+ manager_.RetransmitUnackedPackets(ALL_PACKETS);
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
}
-TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
- // Expect that the RTT is equal to the local time elapsed, since the
- // delta_time_largest_observed is infinite, and is hence invalid.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenNeuterAndAck) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ // Now neuter all unacked unencrypted packets, which occurs when the
+ // connection goes forward secure.
+ manager_.NeuterUnencryptedPackets();
+ QuicPacketSequenceNumber unacked[] = { 1, 2, 3};
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Ensure both packets get discarded when packet 2 is acked.
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed = QuicTime::Delta::Infinite();
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 3;
+ received_info.missing_packets.insert(1);
+ received_info.missing_packets.insert(2);
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+ VerifyUnackedPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
}
-TEST_F(QuicSentPacketManagerTest, RttZeroDelta) {
- // Expect that the RTT is the time between send and receive since the
- // delta_time_largest_observed is zero.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeoutUnsentDataPacket) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ // Serialize two data packets and send the latter.
+ SerializedPacket packet(CreateDataPacket(1));
+ manager_.OnSerializedPacket(packet);
+ SendDataPacket(2);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(sequence_number, _, expected_rtt))
- .Times(1);
+ // Retransmit 1 unacked packets, but not the first serialized packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+}
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(expected_rtt);
+TEST_F(QuicSentPacketManagerTest, ResetRecentMinRTTWithEmptyWindow) {
+ QuicTime::Delta min_rtt = QuicTime::Delta::FromMilliseconds(50);
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ min_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ // Send two packets with no prior bytes in flight.
+ SendDataPacket(1);
+ SendDataPacket(2);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ // Ack two packets with 100ms RTT observations.
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 1;
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ // First ack does not change recent min rtt.
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ received_info.largest_observed = 2;
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
}
TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) {
@@ -829,20 +1120,134 @@ TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) {
SendDataPacket(i);
}
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(kNumSentPackets);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTime) {
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) {
+ SendCryptoPacket(1);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime expected_time = clock_.Now().Add(srtt.Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(srtt.Multiply(1.5));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // The retransmission time should now be twice as far in the future.
+ expected_time = clock_.Now().Add(srtt.Multiply(2).Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime::Delta expected_tlp_delay = srtt.Multiply(2);
+ QuicTime expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(expected_tlp_delay);
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeRTO) {
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ QuicTime::Delta expected_rto_delay = QuicTime::Delta::FromMilliseconds(500);
EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ .WillRepeatedly(Return(expected_rto_delay));
+ QuicTime expected_time = clock_.Now().Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(expected_rto_delay);
manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ RetransmitNextPacket(4);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The delay should double the second time.
+ expected_time = clock_.Now().Add(expected_rto_delay).Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Ack a packet and ensure the RTO goes back to the original value.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ ExpectUpdatedRtt(2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ expected_time = clock_.Now().Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMin) {
+ SendDataPacket(1);
EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
+ .WillRepeatedly(Return(QuicTime::Delta::FromMilliseconds(1)));
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
- manager_.GetRetransmissionDelay());
+ // If the delay is smaller than the min, ensure it exponentially backs off
+ // from the min.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ delay = delay.Add(delay);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(i + 2);
+ }
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
@@ -850,7 +1255,7 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
.WillOnce(Return(QuicTime::Delta::FromSeconds(500)));
EXPECT_EQ(QuicTime::Delta::FromSeconds(60),
- manager_.GetRetransmissionDelay());
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) {
@@ -861,36 +1266,62 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) {
// Delay should back off exponentially.
for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(delay, manager_.GetRetransmissionDelay());
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
delay = delay.Add(delay);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
manager_.OnRetransmissionTimeout();
RetransmitNextPacket(i + 2);
}
}
-TEST_F(QuicSentPacketManagerTest, GetTestTransmissionDelayTailDrop) {
- FLAGS_limit_rto_increase_for_tests = true;
+TEST_F(QuicSentPacketManagerTest, GetLossDelay) {
+ MockLossAlgorithm* loss_algorithm = new MockLossAlgorithm();
+ QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm);
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
SendDataPacket(1);
- QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500);
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillRepeatedly(Return(delay));
+ SendDataPacket(2);
- // No backoff for the first 5 retransmissions.
- for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(delay, manager_.GetRetransmissionDelay());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- manager_.OnRetransmissionTimeout();
- RetransmitNextPacket(i + 2);
- }
+ // Handle an ack which causes the loss algorithm to be evaluated and
+ // set the loss timeout.
+ ExpectAck(2);
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ QuicTime timeout(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(timeout));
+ EXPECT_EQ(timeout, manager_.GetRetransmissionTime());
- // Then backoff starts
- EXPECT_EQ(delay.Add(delay), manager_.GetRetransmissionDelay());
+ // Fire the retransmission timeout and ensure the loss detection algorithm
+ // is invoked.
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiateTimeLossDetection) {
+ EXPECT_EQ(kNack,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
+
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedLossDetection(&config, kTIME);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(kTime,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_server_id.cc b/chromium/net/quic/quic_server_id.cc
new file mode 100644
index 00000000000..ff870997df7
--- /dev/null
+++ b/chromium/net/quic/quic_server_id.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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_server_id.h"
+
+using std::string;
+
+namespace net {
+
+QuicServerId::QuicServerId() {}
+
+QuicServerId::QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host_port_pair),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(PRIVACY_MODE_DISABLED) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::~QuicServerId() {}
+
+bool QuicServerId::operator<(const QuicServerId& other) const {
+ if (!host_port_pair_.Equals(other.host_port_pair_)) {
+ return host_port_pair_ < other.host_port_pair_;
+ }
+ if (is_https_ != other.is_https_) {
+ return is_https_ < other.is_https_;
+ }
+ return privacy_mode_ < other.privacy_mode_;
+}
+
+bool QuicServerId::operator==(const QuicServerId& other) const {
+ return is_https_ == other.is_https_ &&
+ privacy_mode_ == other.privacy_mode_ &&
+ host_port_pair_.Equals(other.host_port_pair_);
+}
+
+string QuicServerId::ToString() const {
+ return (is_https_ ? "https://" : "http://") + host_port_pair_.ToString() +
+ (privacy_mode_ == PRIVACY_MODE_ENABLED ? "/private" : "");
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_id.h b/chromium/net/quic/quic_server_id.h
new file mode 100644
index 00000000000..6d016705cd8
--- /dev/null
+++ b/chromium/net/quic/quic_server_id.h
@@ -0,0 +1,60 @@
+// Copyright 2014 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_SERVER_ID_H_
+#define NET_QUIC_QUIC_SERVER_ID_H_
+
+#include <string>
+
+#include "net/base/host_port_pair.h"
+#include "net/base/net_export.h"
+#include "net/base/privacy_mode.h"
+
+namespace net {
+
+// The id used to identify sessions. Includes the hostname, port, scheme and
+// privacy_mode.
+class NET_EXPORT_PRIVATE QuicServerId {
+ public:
+ QuicServerId();
+ QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ ~QuicServerId();
+
+ // Needed to be an element of std::set.
+ bool operator<(const QuicServerId& other) const;
+ bool operator==(const QuicServerId& other) const;
+
+ // ToString() will convert the QuicServerId to "scheme:hostname:port" or
+ // "scheme:hostname:port/private". "scheme" would either be "http" or "https"
+ // based on |is_https|.
+ std::string ToString() const;
+
+ const HostPortPair& host_port_pair() const { return host_port_pair_; }
+
+ const std::string& host() const { return host_port_pair_.host(); }
+
+ uint16 port() const { return host_port_pair_.port(); }
+
+ bool is_https() const { return is_https_; }
+
+ PrivacyMode privacy_mode() const { return privacy_mode_; }
+
+ private:
+ HostPortPair host_port_pair_;
+ bool is_https_;
+ PrivacyMode privacy_mode_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_ID_H_
diff --git a/chromium/net/quic/quic_server_id_test.cc b/chromium/net/quic/quic_server_id_test.cc
new file mode 100644
index 00000000000..4a9c0f3cd80
--- /dev/null
+++ b/chromium/net/quic/quic_server_id_test.cc
@@ -0,0 +1,319 @@
+// Copyright 2014 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_server_id.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+TEST(QuicServerIdTest, ToString) {
+ HostPortPair google_host_port_pair("google.com", 10);
+
+ QuicServerId google_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_DISABLED);
+ string google_http_server_id_str = google_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10", google_http_server_id_str);
+
+ QuicServerId google_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_DISABLED);
+ string google_https_server_id_str = google_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10", google_https_server_id_str);
+
+ QuicServerId private_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_ENABLED);
+ string private_http_server_id_str = private_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10/private", private_http_server_id_str);
+
+ QuicServerId private_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_ENABLED);
+ string private_https_server_id_str = private_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10/private", private_https_server_id_str);
+}
+
+TEST(QuicServerIdTest, LessThan) {
+ QuicServerId a_10_http(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_10_https(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_http(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_https(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_http(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_https(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_http(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_https(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_10_https_private(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_http_private(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_https_private(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_http_private(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_https_private(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_http_private(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_https_private(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+
+ // Test combinations of host, port, https and privacy being same on left and
+ // right side of less than.
+ EXPECT_FALSE(a_10_http < a_10_http);
+ EXPECT_TRUE(a_10_http < a_10_https);
+ EXPECT_FALSE(a_10_https < a_10_http);
+ EXPECT_FALSE(a_10_https < a_10_https);
+
+ EXPECT_TRUE(a_10_http < a_10_http_private);
+ EXPECT_TRUE(a_10_http < a_10_https_private);
+ EXPECT_FALSE(a_10_https < a_10_http_private);
+ EXPECT_TRUE(a_10_https < a_10_https_private);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http);
+ EXPECT_TRUE(a_10_http_private < a_10_https);
+ EXPECT_FALSE(a_10_https_private < a_10_http);
+ EXPECT_FALSE(a_10_https_private < a_10_https);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http_private);
+ EXPECT_TRUE(a_10_http_private < a_10_https_private);
+ EXPECT_FALSE(a_10_https_private < a_10_http_private);
+ EXPECT_FALSE(a_10_https_private < a_10_https_private);
+
+ // Test with either host, port or https being different on left and right side
+ // of less than.
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 4; i++) {
+ switch (i) {
+ case 0:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ case 2:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 3:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+
+ QuicServerId b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ EXPECT_TRUE(a_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_10_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_11_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_10_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_11_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private);
+ }
+}
+
+TEST(QuicServerIdTest, Equals) {
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId new_a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId new_a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId new_a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId new_b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId new_b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId new_b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId new_b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+
+ EXPECT_EQ(new_a_10_http_left_private, a_10_http_right_private);
+ EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private);
+ EXPECT_EQ(new_a_11_http_left_private, a_11_http_right_private);
+ EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private);
+ EXPECT_EQ(new_b_10_http_left_private, b_10_http_right_private);
+ EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private);
+ EXPECT_EQ(new_b_11_http_left_private, b_11_http_right_private);
+ EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+
+ EXPECT_FALSE(new_a_10_http_left_private == a_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_https_right_private);
+ }
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId new_a_10_http_no_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_FALSE(new_a_10_http_no_private == a_10_http_private);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_packet_writer.cc b/chromium/net/quic/quic_server_packet_writer.cc
new file mode 100644
index 00000000000..c2635b699eb
--- /dev/null
+++ b/chromium/net/quic/quic_server_packet_writer.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 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_server_packet_writer.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+QuicServerPacketWriter::QuicServerPacketWriter() : weak_factory_(this) {
+}
+
+QuicServerPacketWriter::QuicServerPacketWriter(UDPServerSocket* socket)
+ : weak_factory_(this),
+ socket_(socket),
+ write_blocked_(false) {
+}
+
+QuicServerPacketWriter::~QuicServerPacketWriter() {
+}
+
+WriteResult QuicServerPacketWriter::WritePacket(
+ const char* buffer, size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) {
+ scoped_refptr<StringIOBuffer> buf(
+ new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
+ int rv = socket_->SendTo(buf.get(),
+ buf_len,
+ peer_address,
+ base::Bind(&QuicServerPacketWriter::OnWriteComplete,
+ weak_factory_.GetWeakPtr()));
+ WriteStatus status = WRITE_STATUS_OK;
+ if (rv < 0) {
+ if (rv != ERR_IO_PENDING) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.WriteError", -rv);
+ status = WRITE_STATUS_ERROR;
+ } else {
+ status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
+ }
+ }
+
+ return WriteResult(status, rv);
+}
+
+bool QuicServerPacketWriter::IsWriteBlockedDataBuffered() const {
+ // UDPServerSocket::SendTo buffers the data until the Write is permitted.
+ return true;
+}
+
+bool QuicServerPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicServerPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
+void QuicServerPacketWriter::OnWriteComplete(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
+ WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
+ connection_->OnPacketSent(result);
+ connection_->OnCanWrite();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_packet_writer.h b/chromium/net/quic/quic_server_packet_writer.h
new file mode 100644
index 00000000000..63829935521
--- /dev/null
+++ b/chromium/net/quic/quic_server_packet_writer.h
@@ -0,0 +1,53 @@
+// Copyright 2014 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_SERVER_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/udp/udp_server_socket.h"
+
+namespace net {
+
+struct WriteResult;
+
+// Chrome specific packet writer which uses a UDPServerSocket for writing
+// data.
+class QuicServerPacketWriter : public QuicPacketWriter {
+ public:
+ QuicServerPacketWriter();
+ explicit QuicServerPacketWriter(UDPServerSocket* socket);
+ virtual ~QuicServerPacketWriter();
+
+ // QuicPacketWriter
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ void OnWriteComplete(int rv);
+ void SetConnection(QuicConnection* connection) {
+ connection_ = connection;
+ }
+
+ private:
+ base::WeakPtrFactory<QuicServerPacketWriter> weak_factory_;
+ UDPServerSocket* socket_;
+ QuicConnection* connection_;
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerPacketWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
diff --git a/chromium/net/quic/quic_server_session.cc b/chromium/net/quic/quic_server_session.cc
new file mode 100644
index 00000000000..1de91a44aa1
--- /dev/null
+++ b/chromium/net/quic/quic_server_session.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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_server_session.h"
+
+#include "base/logging.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_spdy_server_stream.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+
+QuicServerSession::QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor)
+ : QuicSession(connection, config),
+ visitor_(visitor) {}
+
+QuicServerSession::~QuicServerSession() {}
+
+void QuicServerSession::InitializeSession(
+ const QuicCryptoServerConfig& crypto_config) {
+ crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config));
+}
+
+QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config) {
+ return new QuicCryptoServerStream(crypto_config, this);
+}
+
+void QuicServerSession::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ QuicSession::OnConnectionClosed(error, from_peer);
+ // In the unlikely event we get a connection close while doing an asynchronous
+ // crypto event, make sure we cancel the callback.
+ if (crypto_stream_.get() != NULL) {
+ crypto_stream_->CancelOutstandingCallbacks();
+ }
+ visitor_->OnConnectionClosed(connection()->connection_id(), error);
+}
+
+void QuicServerSession::OnWriteBlocked() {
+ QuicSession::OnWriteBlocked();
+ visitor_->OnWriteBlocked(connection());
+}
+
+bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) {
+ if (id % 2 == 0) {
+ DVLOG(1) << "Invalid incoming even stream_id:" << id;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ return false;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DVLOG(1) << "Failed to create a new incoming stream with id:" << id
+ << " Already " << GetNumOpenStreams() << " open.";
+ connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
+ return false;
+ }
+ return true;
+}
+
+QuicDataStream* QuicServerSession::CreateIncomingDataStream(
+ QuicStreamId id) {
+ if (!ShouldCreateIncomingDataStream(id)) {
+ return NULL;
+ }
+
+ return new QuicSpdyServerStream(id, this);
+}
+
+QuicDataStream* QuicServerSession::CreateOutgoingDataStream() {
+ DLOG(ERROR) << "Server push not yet supported";
+ return NULL;
+}
+
+QuicCryptoServerStream* QuicServerSession::GetCryptoStream() {
+ return crypto_stream_.get();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_session.h b/chromium/net/quic/quic_server_session.h
new file mode 100644
index 00000000000..30ac08c80df
--- /dev/null
+++ b/chromium/net/quic/quic_server_session.h
@@ -0,0 +1,87 @@
+// Copyright 2014 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.
+//
+// A server specific QuicSession subclass.
+
+#ifndef NET_QUIC_QUIC_SERVER_SESSION_H_
+#define NET_QUIC_QUIC_SERVER_SESSION_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_crypto_server_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+class QuicBlockedWriterInterface;
+class QuicConfig;
+class QuicConnection;
+class QuicCryptoServerConfig;
+class ReliableQuicStream;
+
+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 or blocked.
+class QuicServerSessionVisitor {
+ public:
+ virtual ~QuicServerSessionVisitor() {}
+
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) = 0;
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) = 0;
+};
+
+class QuicServerSession : public QuicSession {
+ public:
+ QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor);
+
+ // Override the base class to notify the owner of the connection close.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE;
+
+ virtual ~QuicServerSession();
+
+ virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config);
+
+ const QuicCryptoServerStream* crypto_stream() const {
+ return crypto_stream_.get();
+ }
+
+ protected:
+ // QuicSession methods:
+ virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
+ virtual QuicDataStream* CreateOutgoingDataStream() OVERRIDE;
+ virtual QuicCryptoServerStream* GetCryptoStream() OVERRIDE;
+
+ // If we should create an incoming stream, returns true. Otherwise
+ // does error handling, including communicating the error to the client and
+ // possibly closing the connection, and returns false.
+ virtual bool ShouldCreateIncomingDataStream(QuicStreamId id);
+
+ virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config);
+
+ private:
+ friend class test::QuicServerSessionPeer;
+
+ scoped_ptr<QuicCryptoServerStream> crypto_stream_;
+ QuicServerSessionVisitor* visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_SESSION_H_
diff --git a/chromium/net/quic/quic_session.cc b/chromium/net/quic/quic_session.cc
index 67cdf54d1d1..4cd9a335718 100644
--- a/chromium/net/quic/quic_session.cc
+++ b/chromium/net/quic/quic_session.cc
@@ -7,6 +7,9 @@
#include "base/stl_util.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_headers_stream.h"
#include "net/ssl/ssl_info.h"
using base::StringPiece;
@@ -17,9 +20,6 @@ using std::vector;
namespace net {
-const size_t kMaxPrematurelyClosedStreamsTracked = 20;
-const size_t kMaxZombieStreams = 20;
-
#define ENDPOINT (is_server() ? "Server: " : " Client: ")
// We want to make sure we delete any closed streams in a safe manner.
@@ -33,10 +33,9 @@ class VisitorShim : public QuicConnectionVisitorInterface {
public:
explicit VisitorShim(QuicSession* session) : session_(session) {}
- virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
- bool accepted = session_->OnStreamFrames(frames);
+ virtual void OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
+ session_->OnStreamFrames(frames);
session_->PostProcessAfterData();
- return accepted;
}
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE {
session_->OnRstStream(frame);
@@ -48,10 +47,21 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->PostProcessAfterData();
}
- virtual bool OnCanWrite() OVERRIDE {
- bool rc = session_->OnCanWrite();
+ virtual void OnWindowUpdateFrames(const vector<QuicWindowUpdateFrame>& frames)
+ OVERRIDE {
+ session_->OnWindowUpdateFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnBlockedFrames(const vector<QuicBlockedFrame>& frames)
+ OVERRIDE {
+ session_->OnBlockedFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnCanWrite() OVERRIDE {
+ session_->OnCanWrite();
session_->PostProcessAfterData();
- return rc;
}
virtual void OnSuccessfulVersionNegotiation(
@@ -59,26 +69,33 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->OnSuccessfulVersionNegotiation(version);
}
- virtual void OnConfigNegotiated() OVERRIDE {
- session_->OnConfigNegotiated();
- }
-
- virtual void OnConnectionClosed(QuicErrorCode error,
- bool from_peer) OVERRIDE {
+ virtual void OnConnectionClosed(
+ QuicErrorCode error, bool from_peer) OVERRIDE {
session_->OnConnectionClosed(error, from_peer);
// The session will go away, so don't bother with cleanup.
}
+ virtual void OnWriteBlocked() OVERRIDE {
+ session_->OnWriteBlocked();
+ }
+
+ virtual bool WillingAndAbleToWrite() const OVERRIDE {
+ return session_->WillingAndAbleToWrite();
+ }
+
virtual bool HasPendingHandshake() const OVERRIDE {
return session_->HasPendingHandshake();
}
+ virtual bool HasOpenDataStreams() const OVERRIDE {
+ return session_->HasOpenDataStreams();
+ }
+
private:
QuicSession* session_;
};
-QuicSession::QuicSession(QuicConnection* connection,
- const QuicConfig& config)
+QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config)
: connection_(connection),
visitor_shim_(new VisitorShim(this)),
config_(config),
@@ -89,6 +106,17 @@ QuicSession::QuicSession(QuicConnection* connection,
goaway_received_(false),
goaway_sent_(false),
has_pending_handshake_(false) {
+ if (connection_->version() <= QUIC_VERSION_19) {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialFlowControlWindowToSend(),
+ config_.GetInitialFlowControlWindowToSend()));
+ } else {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialSessionFlowControlWindowToSend(),
+ config_.GetInitialSessionFlowControlWindowToSend()));
+ }
connection_->set_visitor(visitor_shim_.get());
connection_->SetFromConfig(config_);
@@ -96,70 +124,77 @@ QuicSession::QuicSession(QuicConnection* connection,
connection_->SetOverallConnectionTimeout(
config_.max_time_before_crypto_handshake());
}
+ headers_stream_.reset(new QuicHeadersStream(this));
+ if (!is_server()) {
+ // For version above QUIC v12, the headers stream is stream 3, so the
+ // next available local stream ID should be 5.
+ DCHECK_EQ(kHeadersStreamId, next_stream_id_);
+ next_stream_id_ += 2;
+ }
}
QuicSession::~QuicSession() {
STLDeleteElements(&closed_streams_);
STLDeleteValues(&stream_map_);
-}
-
-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)) {
- // If we get additional frames for a stream where we didn't process
- // headers, it's highly likely our compression context will end up
- // permanently out of sync with the peer's, so we give up and close the
- // connection.
- if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- return false;
- }
- continue;
- }
-
- ReliableQuicStream* stream = GetStream(frames[i].stream_id);
- if (stream == NULL) return false;
- if (!stream->WillAcceptStreamFrame(frames[i])) return false;
- // TODO(alyssar) check against existing connection address: if changed, make
- // sure we update the connection.
- }
+ DLOG_IF(WARNING,
+ locally_closed_streams_highest_offset_.size() > max_open_streams_)
+ << "Surprisingly high number of locally closed streams still waiting for "
+ "final byte offset: " << locally_closed_streams_highest_offset_.size();
+}
+void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
for (size_t i = 0; i < frames.size(); ++i) {
- QuicStreamId stream_id = frames[i].stream_id;
+ // TODO(rch) deal with the error case of stream id 0.
+ const QuicStreamFrame& frame = frames[i];
+ QuicStreamId stream_id = frame.stream_id;
ReliableQuicStream* stream = GetStream(stream_id);
if (!stream) {
+ // The stream no longer exists, but we may still be interested in the
+ // final stream byte offset sent by the peer. A frame with a FIN can give
+ // us this offset.
+ if (frame.fin) {
+ QuicStreamOffset final_byte_offset =
+ frame.offset + frame.data.TotalBufferSize();
+ UpdateFlowControlOnFinalReceivedByteOffset(stream_id,
+ final_byte_offset);
+ }
+
continue;
}
stream->OnStreamFrame(frames[i]);
+ }
+}
- // If the stream is a data stream had been prematurely closed, and the
- // headers are now decompressed, then we are finally finished
- // with this stream.
- if (ContainsKey(zombie_streams_, stream_id) &&
- static_cast<QuicDataStream*>(stream)->headers_decompressed()) {
- CloseZombieStream(stream_id);
- }
+void QuicSession::OnStreamHeaders(QuicStreamId stream_id,
+ StringPiece headers_data) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
}
+ stream->OnStreamHeaders(headers_data);
+}
- while (!decompression_blocked_streams_.empty()) {
- QuicHeaderId header_id = decompression_blocked_streams_.begin()->first;
- if (header_id != decompressor_.current_header_id()) {
- break;
- }
- QuicStreamId stream_id = decompression_blocked_streams_.begin()->second;
- decompression_blocked_streams_.erase(header_id);
- QuicDataStream* stream = GetDataStream(stream_id);
- if (!stream) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- return false;
- }
- stream->OnDecompressorAvailable();
+void QuicSession::OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
}
- return true;
+ stream->OnStreamHeadersPriority(priority);
+}
+
+void QuicSession::OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeadersComplete(fin, frame_len);
}
void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
@@ -169,25 +204,23 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
"Attempt to reset the crypto stream");
return;
}
+ if (frame.stream_id == kHeadersStreamId) {
+ connection()->SendConnectionCloseWithDetails(
+ QUIC_INVALID_STREAM_ID,
+ "Attempt to reset the headers stream");
+ return;
+ }
+
QuicDataStream* stream = GetDataStream(frame.stream_id);
if (!stream) {
+ // The RST frame contains the final byte offset for the stream: we can now
+ // update the connection level flow controller if needed.
+ UpdateFlowControlOnFinalReceivedByteOffset(frame.stream_id,
+ frame.byte_offset);
return; // Errors are handled by GetStream.
}
- if (ContainsKey(zombie_streams_, stream->id())) {
- // If this was a zombie stream then we close it out now.
- CloseZombieStream(stream->id());
- // However, since the headers still have not been decompressed, we want to
- // mark it a prematurely closed so that if we ever receive frames
- // for this stream we can close the connection.
- DCHECK(!stream->headers_decompressed());
- AddPrematurelyClosedStream(frame.stream_id);
- return;
- }
- if (stream->stream_bytes_read() > 0 && !stream->headers_decompressed()) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- }
- stream->OnStreamReset(frame.error_code);
+
+ stream->OnStreamReset(frame);
}
void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
@@ -214,60 +247,148 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) {
}
}
-bool QuicSession::OnCanWrite() {
- // We latch this here rather than doing a traditional loop, because streams
- // may be modifying the list as we loop.
- int remaining_writes = write_blocked_streams_.NumBlockedStreams();
+void QuicSession::OnWindowUpdateFrames(
+ const vector<QuicWindowUpdateFrame>& frames) {
+ bool connection_window_updated = false;
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't
+ // assume that it still exists.
+ QuicStreamId stream_id = frames[i].stream_id;
+ if (stream_id == 0) {
+ // This is a window update that applies to the connection, rather than an
+ // individual stream.
+ DVLOG(1) << ENDPOINT
+ << "Received connection level flow control window update with "
+ "byte offset: " << frames[i].byte_offset;
+ if (FLAGS_enable_quic_connection_flow_control_2 &&
+ flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) {
+ connection_window_updated = true;
+ }
+ continue;
+ }
+
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (stream) {
+ stream->OnWindowUpdateFrame(frames[i]);
+ }
+ }
+
+ // Connection level flow control window has increased, so blocked streams can
+ // write again.
+ if (connection_window_updated) {
+ OnCanWrite();
+ }
+}
+
+void QuicSession::OnBlockedFrames(const vector<QuicBlockedFrame>& frames) {
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // TODO(rjshade): Compare our flow control receive windows for specified
+ // streams: if we have a large window then maybe something
+ // had gone wrong with the flow control accounting.
+ DVLOG(1) << ENDPOINT << "Received BLOCKED frame with stream id: "
+ << frames[i].stream_id;
+ }
+}
+
+void QuicSession::OnCanWrite() {
+ // We limit the number of writes to the number of pending streams. If more
+ // streams become pending, WillingAndAbleToWrite will be true, which will
+ // cause the connection to request resumption before yielding to other
+ // connections.
+ size_t num_writes = write_blocked_streams_.NumBlockedStreams();
+ if (flow_controller_->IsBlocked()) {
+ // If we are connection level flow control blocked, then only allow the
+ // crypto and headers streams to try writing as all other streams will be
+ // blocked.
+ num_writes = 0;
+ if (write_blocked_streams_.crypto_stream_blocked()) {
+ num_writes += 1;
+ }
+ if (write_blocked_streams_.headers_stream_blocked()) {
+ num_writes += 1;
+ }
+ }
+ if (num_writes == 0) {
+ return;
+ }
- while (!connection_->HasQueuedData() &&
- remaining_writes > 0) {
- DCHECK(write_blocked_streams_.HasWriteBlockedStreams());
- if (!write_blocked_streams_.HasWriteBlockedStreams()) {
+ QuicConnection::ScopedPacketBundler ack_bundler(
+ connection_.get(), QuicConnection::NO_ACK);
+ for (size_t i = 0; i < num_writes; ++i) {
+ if (!(write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams())) {
+ // Writing one stream removed another!? Something's broken.
LOG(DFATAL) << "WriteBlockedStream is missing";
connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
- return true; // We have no write blocked streams.
+ return;
+ }
+ if (!connection_->CanWriteStreamData()) {
+ return;
}
- int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList();
- QuicStreamId stream_id = write_blocked_streams_.PopFront(index);
+ QuicStreamId stream_id = write_blocked_streams_.PopFront();
if (stream_id == kCryptoStreamId) {
has_pending_handshake_ = false; // We just popped it.
}
ReliableQuicStream* stream = GetStream(stream_id);
- if (stream != NULL) {
+ if (stream != NULL && !stream->flow_controller()->IsBlocked()) {
// If the stream can't write all bytes, it'll re-add itself to the blocked
// list.
stream->OnCanWrite();
}
- --remaining_writes;
}
+}
- return !write_blocked_streams_.HasWriteBlockedStreams();
+bool QuicSession::WillingAndAbleToWrite() const {
+ // If the crypto or headers streams are blocked, we want to schedule a write -
+ // they don't get blocked by connection level flow control. Otherwise only
+ // schedule a write if we are not flow control blocked at the connection
+ // level.
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ (!flow_controller_->IsBlocked() &&
+ write_blocked_streams_.HasWriteBlockedDataStreams());
}
bool QuicSession::HasPendingHandshake() const {
return has_pending_handshake_;
}
+bool QuicSession::HasOpenDataStreams() const {
+ return GetNumOpenStreams() > 0;
+}
+
QuicConsumedData QuicSession::WritevData(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
- IOVector data;
- data.AppendIovec(iov, iov_count);
- return connection_->SendStreamData(id, data, offset, fin,
+ return connection_->SendStreamData(id, data, offset, fin, fec_protection,
ack_notifier_delegate);
}
+size_t QuicSession::WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ return headers_stream_->WriteHeaders(id, headers, fin, ack_notifier_delegate);
+}
+
void QuicSession::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- connection_->SendRstStream(id, error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ if (connection()->connected()) {
+ // Only send a RST_STREAM frame if still connected.
+ connection_->SendRstStream(id, error, bytes_written);
+ }
CloseStreamInner(id, true);
}
void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
+ if (goaway_sent_) {
+ return;
+ }
goaway_sent_ = true;
connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason);
}
@@ -286,63 +407,55 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id,
return;
}
QuicDataStream* stream = it->second;
- if (connection_->connected() && !stream->headers_decompressed()) {
- // If the stream is being closed locally (for example a client cancelling
- // a request before receiving the response) then we need to make sure that
- // we keep the stream alive long enough to process any response or
- // RST_STREAM frames.
- if (locally_reset && !is_server()) {
- AddZombieStream(stream_id);
- return;
- }
- // This stream has been closed before the headers were decompressed.
- // This might cause problems with head of line blocking of headers.
- // If the peer sent headers which were lost but we now close the stream
- // we will never be able to decompress headers for other streams.
- // To deal with this, we keep track of streams which have been closed
- // prematurely. If we ever receive data frames for this steam, then we
- // know there actually has been a problem and we close the connection.
- AddPrematurelyClosedStream(stream->id());
+ // Tell the stream that a RST has been sent.
+ if (locally_reset) {
+ stream->set_rst_sent(true);
}
+
closed_streams_.push_back(it->second);
- if (ContainsKey(zombie_streams_, stream->id())) {
- zombie_streams_.erase(stream->id());
+
+ // If we haven't received a FIN or RST for this stream, we need to keep track
+ // of the how many bytes the stream's flow controller believes it has
+ // received, for accurate connection level flow control accounting.
+ if (!stream->HasFinalReceivedByteOffset() &&
+ stream->flow_controller()->IsEnabled() &&
+ FLAGS_enable_quic_connection_flow_control_2) {
+ locally_closed_streams_highest_offset_[stream_id] =
+ stream->flow_controller()->highest_received_byte_offset();
}
+
stream_map_.erase(it);
stream->OnClose();
}
-void QuicSession::AddZombieStream(QuicStreamId stream_id) {
- if (zombie_streams_.size() == kMaxZombieStreams) {
- QuicStreamId oldest_zombie_stream_id = zombie_streams_.begin()->first;
- CloseZombieStream(oldest_zombie_stream_id);
- // However, since the headers still have not been decompressed, we want to
- // mark it a prematurely closed so that if we ever receive frames
- // for this stream we can close the connection.
- AddPrematurelyClosedStream(oldest_zombie_stream_id);
+void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId stream_id, QuicStreamOffset final_byte_offset) {
+ if (!FLAGS_enable_quic_connection_flow_control_2) {
+ return;
}
- zombie_streams_.insert(make_pair(stream_id, true));
-}
-void QuicSession::CloseZombieStream(QuicStreamId stream_id) {
- DCHECK(ContainsKey(zombie_streams_, stream_id));
- zombie_streams_.erase(stream_id);
- QuicDataStream* stream = GetDataStream(stream_id);
- if (!stream) {
+ map<QuicStreamId, QuicStreamOffset>::iterator it =
+ locally_closed_streams_highest_offset_.find(stream_id);
+ if (it == locally_closed_streams_highest_offset_.end()) {
return;
}
- stream_map_.erase(stream_id);
- stream->OnClose();
- closed_streams_.push_back(stream);
-}
-void QuicSession::AddPrematurelyClosedStream(QuicStreamId stream_id) {
- if (prematurely_closed_streams_.size() ==
- kMaxPrematurelyClosedStreamsTracked) {
- prematurely_closed_streams_.erase(prematurely_closed_streams_.begin());
+ DVLOG(1) << ENDPOINT << "Received final byte offset " << final_byte_offset
+ << " for stream " << stream_id;
+ uint64 offset_diff = final_byte_offset - it->second;
+ if (flow_controller_->UpdateHighestReceivedOffset(
+ flow_controller_->highest_received_byte_offset() + offset_diff)) {
+ // If the final offset violates flow control, close the connection now.
+ if (flow_controller_->FlowControlViolation()) {
+ connection_->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return;
+ }
}
- prematurely_closed_streams_.insert(make_pair(stream_id, true));
+
+ flow_controller_->AddBytesConsumed(offset_diff);
+ locally_closed_streams_highest_offset_.erase(it);
}
bool QuicSession::IsEncryptionEstablished() {
@@ -355,6 +468,68 @@ bool QuicSession::IsCryptoHandshakeConfirmed() {
void QuicSession::OnConfigNegotiated() {
connection_->SetFromConfig(config_);
+ QuicVersion version = connection()->version();
+ if (version < QUIC_VERSION_17) {
+ return;
+ }
+
+ if (version <= QUIC_VERSION_19) {
+ // QUIC_VERSION_17,18,19 don't support independent stream/session flow
+ // control windows.
+ if (config_.HasReceivedInitialFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
+ OnNewStreamFlowControlWindow(new_window);
+ OnNewSessionFlowControlWindow(new_window);
+ }
+
+ return;
+ }
+
+ // QUIC_VERSION_20 and higher can have independent stream and session flow
+ // control windows.
+ if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ OnNewStreamFlowControlWindow(
+ config_.ReceivedInitialStreamFlowControlWindowBytes());
+ }
+ if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
+ OnNewSessionFlowControlWindow(
+ config_.ReceivedInitialSessionFlowControlWindowBytes());
+ }
+}
+
+void QuicSession::OnNewStreamFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid stream flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ for (DataStreamMap::iterator it = stream_map_.begin();
+ it != stream_map_.end(); ++it) {
+ it->second->flow_controller()->UpdateSendWindowOffset(new_window);
+ }
+}
+
+void QuicSession::OnNewSessionFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid session flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ flow_controller_->UpdateSendWindowOffset(new_window);
}
void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
@@ -373,6 +548,9 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
case HANDSHAKE_CONFIRMED:
LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT
<< "Handshake confirmed without parameter negotiation.";
+ // Discard originally encrypted packets, since they can't be decrypted by
+ // the peer.
+ connection_->NeuterUnencryptedPackets();
connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite());
max_open_streams_ = config_.max_streams_per_connection();
break;
@@ -396,7 +574,7 @@ QuicConfig* QuicSession::config() {
void QuicSession::ActivateStream(QuicDataStream* stream) {
DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size()
- << ". activating " << stream->id();
+ << ". activating " << stream->id();
DCHECK_EQ(stream_map_.count(stream->id()), 0u);
stream_map_[stream->id()] = stream;
}
@@ -411,6 +589,9 @@ ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) {
if (stream_id == kCryptoStreamId) {
return GetCryptoStream();
}
+ if (stream_id == kHeadersStreamId) {
+ return headers_stream_.get();
+ }
return GetDataStream(stream_id);
}
@@ -419,6 +600,10 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) {
DLOG(FATAL) << "Attempt to call GetDataStream with the crypto stream id";
return NULL;
}
+ if (stream_id == kHeadersStreamId) {
+ DLOG(FATAL) << "Attempt to call GetDataStream with the headers stream id";
+ return NULL;
+ }
DataStreamMap::iterator it = stream_map_.find(stream_id);
if (it != stream_map_.end()) {
@@ -432,34 +617,40 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) {
if (stream_id % 2 == next_stream_id_ % 2) {
// We've received a frame for a locally-created stream that is not
// currently active. This is an error.
- connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ if (connection()->connected()) {
+ connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ }
return NULL;
}
- return GetIncomingReliableStream(stream_id);
+ return GetIncomingDataStream(stream_id);
}
-QuicDataStream* QuicSession::GetIncomingReliableStream(
- QuicStreamId stream_id) {
+QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) {
if (IsClosedStream(stream_id)) {
return NULL;
}
- if (goaway_sent_) {
- // We've already sent a GoAway
- SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY);
- return NULL;
- }
-
implicitly_created_streams_.erase(stream_id);
if (stream_id > largest_peer_created_stream_id_) {
- // TODO(rch) add unit test for this
if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) {
- connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ // We may already have sent a connection close due to multiple reset
+ // streams in the same packet.
+ if (connection()->connected()) {
+ LOG(ERROR) << "Trying to get stream: " << stream_id
+ << ", largest peer created stream: "
+ << largest_peer_created_stream_id_
+ << ", max delta: " << kMaxStreamIdDelta;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ }
return NULL;
}
if (largest_peer_created_stream_id_ == 0) {
- largest_peer_created_stream_id_= 1;
+ if (is_server()) {
+ largest_peer_created_stream_id_= 3;
+ } else {
+ largest_peer_created_stream_id_= 1;
+ }
}
for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
id < stream_id;
@@ -481,8 +672,8 @@ bool QuicSession::IsClosedStream(QuicStreamId id) {
if (id == kCryptoStreamId) {
return false;
}
- if (ContainsKey(zombie_streams_, id)) {
- return true;
+ if (id == kHeadersStreamId) {
+ return false;
}
if (ContainsKey(stream_map_, id)) {
// Stream is active
@@ -500,11 +691,22 @@ bool QuicSession::IsClosedStream(QuicStreamId id) {
}
size_t QuicSession::GetNumOpenStreams() const {
- return stream_map_.size() + implicitly_created_streams_.size() -
- zombie_streams_.size();
+ return stream_map_.size() + implicitly_created_streams_.size();
}
void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
+#ifndef NDEBUG
+ ReliableQuicStream* stream = GetStream(id);
+ if (stream != NULL) {
+ LOG_IF(DFATAL, priority != stream->EffectivePriority())
+ << ENDPOINT << "Stream " << id
+ << "Priorities do not match. Got: " << priority
+ << " Expected: " << stream->EffectivePriority();
+ } else {
+ LOG(DFATAL) << "Marking unknown stream " << id << " blocked.";
+ }
+#endif
+
if (id == kCryptoStreamId) {
DCHECK(!has_pending_handshake_);
has_pending_handshake_ = true;
@@ -516,17 +718,13 @@ void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
write_blocked_streams_.PushBack(id, priority);
}
-bool QuicSession::HasQueuedData() const {
- return write_blocked_streams_.NumBlockedStreams() ||
- connection_->HasQueuedData();
-}
-
-void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id,
- QuicStreamId stream_id) {
- decompression_blocked_streams_[header_id] = stream_id;
+bool QuicSession::HasDataToWrite() const {
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams() ||
+ connection_->HasQueuedData();
}
-bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) {
+bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) const {
NOTIMPLEMENTED();
return false;
}
@@ -536,4 +734,19 @@ void QuicSession::PostProcessAfterData() {
closed_streams_.clear();
}
+void QuicSession::OnSuccessfulVersionNegotiation(const QuicVersion& version) {
+ if (version < QUIC_VERSION_19) {
+ flow_controller_->Disable();
+ }
+
+ // Inform all streams about the negotiated version. They may have been created
+ // with a different version.
+ for (DataStreamMap::iterator it = stream_map_.begin();
+ it != stream_map_.end(); ++it) {
+ if (version < QUIC_VERSION_17) {
+ it->second->flow_controller()->Disable();
+ }
+ }
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_session.h b/chromium/net/quic/quic_session.h
index 083990b55f6..1b31280020c 100644
--- a/chromium/net/quic/quic_session.h
+++ b/chromium/net/quic/quic_session.h
@@ -16,16 +16,16 @@
#include "net/quic/quic_connection.h"
#include "net/quic/quic_crypto_stream.h"
#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_headers_stream.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/reliable_quic_stream.h"
-#include "net/spdy/write_blocked_list.h"
namespace net {
class QuicCryptoStream;
+class QuicFlowController;
class ReliableQuicStream;
class SSLInfo;
class VisitorShim;
@@ -53,42 +53,75 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
HANDSHAKE_CONFIRMED,
};
- QuicSession(QuicConnection* connection,
- const QuicConfig& config);
+ QuicSession(QuicConnection* connection, const QuicConfig& config);
virtual ~QuicSession();
// QuicConnectionVisitorInterface methods:
- virtual bool OnStreamFrames(
+ virtual void OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) OVERRIDE;
+ virtual void OnBlockedFrames(
+ const std::vector<QuicBlockedFrame>& frames) OVERRIDE;
virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE {}
virtual void OnSuccessfulVersionNegotiation(
- const QuicVersion& version) OVERRIDE {}
- virtual void OnConfigNegotiated() OVERRIDE;
- // Not needed for HTTP.
- virtual bool OnCanWrite() OVERRIDE;
+ const QuicVersion& version) OVERRIDE;
+ virtual void OnCanWrite() OVERRIDE;
+ virtual bool WillingAndAbleToWrite() const OVERRIDE;
virtual bool HasPendingHandshake() const OVERRIDE;
+ virtual bool HasOpenDataStreams() const OVERRIDE;
+
+ // Called by the headers stream when headers have been received for a stream.
+ virtual void OnStreamHeaders(QuicStreamId stream_id,
+ base::StringPiece headers_data);
+ // Called by the headers stream when headers with a priority have been
+ // received for this stream. This method will only be called for server
+ // streams.
+ virtual void OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority);
+ // Called by the headers stream when headers have been completely received
+ // for a stream. |fin| will be true if the fin flag was set in the headers
+ // frame.
+ virtual void OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len);
// 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.
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
// If provided, |ack_notifier_delegate| will be registered to be notified when
- // we have seen ACKs for all packets resulting from this call. Not owned by
- // this class.
+ // we have seen ACKs for all packets resulting from this call.
virtual QuicConsumedData WritevData(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Writes |headers| for the stream |id| to the dedicated headers stream.
+ // If |fin| is true, then no more data will be sent for the stream |id|.
+ // If provided, |ack_notifier_delegate| will be registered to be notified when
+ // we have seen ACKs for all packets resulting from this call.
+ size_t WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
// Called by streams when they want to close the stream in both directions.
- virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error);
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
// Called when the session wants to go away and not accept any new streams.
void SendGoAway(QuicErrorCode error_code, const std::string& reason);
@@ -104,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// a server, returns true if a full, valid client hello has been received.
virtual bool IsCryptoHandshakeConfirmed();
+ // Called by the QuicCryptoStream when a new QuicConfig has been negotiated.
+ virtual void OnConfigNegotiated();
+
// Called by the QuicCryptoStream when the handshake enters a new state.
//
// Clients will call this function in the order:
@@ -137,24 +173,20 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
const IPEndPoint& peer_address() const {
return connection_->peer_address();
}
- QuicGuid guid() const { return connection_->guid(); }
-
- QuicPacketCreator::Options* options() { return connection()->options(); }
+ QuicConnectionId connection_id() const {
+ return connection_->connection_id();
+ }
// Returns the number of currently open streams, including those which have
- // been implicitly created.
+ // been implicitly created, but excluding the reserved headers and crypto
+ // streams.
virtual size_t GetNumOpenStreams() const;
void MarkWriteBlocked(QuicStreamId id, QuicPriority priority);
// Returns true if the session has data to be sent, either queued in the
// connection, or in a write-blocked stream.
- bool HasQueuedData() const;
-
- // Marks that |stream_id| is blocked waiting to decompress the
- // headers identified by |decompression_id|.
- void MarkDecompressionBlocked(QuicHeaderId decompression_id,
- QuicStreamId stream_id);
+ bool HasDataToWrite() const;
bool goaway_received() const {
return goaway_received_;
@@ -164,16 +196,15 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
return goaway_sent_;
}
- QuicSpdyDecompressor* decompressor() { return &decompressor_; }
- QuicSpdyCompressor* compressor() { return &compressor_; }
-
// Gets the SSL connection information.
- virtual bool GetSSLInfo(SSLInfo* ssl_info);
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const;
QuicErrorCode error() const { return error_; }
bool is_server() const { return connection_->is_server(); }
+ QuicFlowController* flow_controller() { return flow_controller_.get(); }
+
protected:
typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap;
@@ -195,7 +226,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Returns the stream id for a new stream.
QuicStreamId GetNextStreamId();
- QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id);
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id);
QuicDataStream* GetDataStream(const QuicStreamId stream_id);
@@ -226,35 +257,32 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
friend class VisitorShim;
// Performs the work required to close |stream_id|. If |locally_reset|
- // then the stream has been reset by this endpoint, not by the peer. This
- // means the stream may become a zombie stream which needs to stay
- // around until headers have been decompressed.
+ // then the stream has been reset by this endpoint, not by the peer.
void CloseStreamInner(QuicStreamId stream_id, bool locally_reset);
- // Adds |stream_id| to the zobmie stream map, closing the oldest
- // zombie stream if the set is full.
- void AddZombieStream(QuicStreamId stream_id);
+ // When a stream is closed locally, it may not yet know how many bytes the
+ // peer sent on that stream.
+ // When this data arrives (via stream frame w. FIN, or RST) this method
+ // is called, and correctly updates the connection level flow controller.
+ void UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId id, QuicStreamOffset final_byte_offset);
- // Closes the zombie stream |stream_id| and removes it from the zombie
- // stream map.
- void CloseZombieStream(QuicStreamId stream_id);
+ // Called in OnConfigNegotiated when we receive a new stream level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewStreamFlowControlWindow(uint32 new_window);
- // Adds |stream_id| to the prematurely closed stream map, removing the
- // oldest prematurely closed stream if the set is full.
- void AddPrematurelyClosedStream(QuicStreamId stream_id);
+ // Called in OnConfigNegotiated when we receive a new session level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewSessionFlowControlWindow(uint32 new_window);
- scoped_ptr<QuicConnection> connection_;
+ // Keep track of highest received byte offset of locally closed streams, while
+ // waiting for a definitive final highest offset from the peer.
+ std::map<QuicStreamId, QuicStreamOffset>
+ locally_closed_streams_highest_offset_;
- // Tracks the last 20 streams which closed without decompressing headers.
- // This is for best-effort detection of an unrecoverable compression context.
- // Ideally this would be a linked_hash_set as the boolean is unused.
- linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_;
+ scoped_ptr<QuicConnection> connection_;
- // Streams which have been locally reset before decompressing headers
- // from the peer. These streams need to stay open long enough to
- // process any headers from the peer.
- // Ideally this would be a linked_hash_set as the boolean is unused.
- linked_hash_map<QuicStreamId, bool> zombie_streams_;
+ scoped_ptr<QuicHeadersStream> headers_stream_;
// A shim to stand between the connection and the session, to handle stream
// deletions.
@@ -262,9 +290,6 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
std::vector<QuicDataStream*> closed_streams_;
- QuicSpdyDecompressor decompressor_;
- QuicSpdyCompressor compressor_;
-
QuicConfig config_;
// Returns the maximum number of streams this connection can open.
@@ -279,11 +304,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
base::hash_set<QuicStreamId> implicitly_created_streams_;
// A list of streams which need to write more data.
- WriteBlockedList<QuicStreamId> write_blocked_streams_;
-
- // A map of headers waiting to be compressed, and the streams
- // they are associated with.
- map<uint32, QuicStreamId> decompression_blocked_streams_;
+ QuicWriteBlockedList write_blocked_streams_;
QuicStreamId largest_peer_created_stream_id_;
@@ -298,6 +319,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Indicate if there is pending data for the crypto stream.
bool has_pending_handshake_;
+ // Used for session level flow control.
+ scoped_ptr<QuicFlowController> flow_controller_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSession);
};
diff --git a/chromium/net/quic/quic_session_test.cc b/chromium/net/quic/quic_session_test.cc
index ecbafb5c6a2..aeb41037bee 100644
--- a/chromium/net/quic/quic_session_test.cc
+++ b/chromium/net/quic/quic_session_test.cc
@@ -7,31 +7,43 @@
#include <set>
#include <vector>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
-#include "net/quic/crypto/crypto_handshake.h"
-#include "net/quic/quic_connection.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_data_stream_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
#include "net/spdy/spdy_framer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::hash_map;
using std::set;
using std::vector;
-using testing::_;
+using testing::CreateFunctor;
using testing::InSequence;
-using testing::InvokeWithoutArgs;
+using testing::Invoke;
+using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-const QuicPriority kSomeMiddlePriority = 2;
+const QuicPriority kHighestPriority = 0;
+const QuicPriority kSomeMiddlePriority = 3;
class TestCryptoStream : public QuicCryptoStream {
public:
@@ -45,9 +57,15 @@ class TestCryptoStream : public QuicCryptoStream {
handshake_confirmed_ = true;
CryptoHandshakeMessage msg;
string error_details;
+ session()->config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session()->config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
session()->config()->ToHandshakeMessage(&msg);
- const QuicErrorCode error = session()->config()->ProcessClientHello(
- msg, &error_details);
+ const QuicErrorCode error = session()->config()->ProcessPeerHello(
+ msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
session()->OnConfigNegotiated();
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
@@ -56,6 +74,15 @@ class TestCryptoStream : public QuicCryptoStream {
MOCK_METHOD0(OnCanWrite, void());
};
+class TestHeadersStream : public QuicHeadersStream {
+ public:
+ explicit TestHeadersStream(QuicSession* session)
+ : QuicHeadersStream(session) {
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
class TestStream : public QuicDataStream {
public:
TestStream(QuicStreamId id, QuicSession* session)
@@ -64,10 +91,14 @@ class TestStream : public QuicDataStream {
using ReliableQuicStream::CloseWriteSide;
- virtual uint32 ProcessData(const char* data, uint32 data_len) {
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE {
return data_len;
}
+ void SendBody(const string& data, bool fin) {
+ WriteOrBufferData(data, fin, NULL);
+ }
+
MOCK_METHOD0(OnCanWrite, void());
};
@@ -91,9 +122,10 @@ class StreamBlocker {
class TestSession : public QuicSession {
public:
explicit TestSession(QuicConnection* connection)
- : QuicSession(connection, DefaultQuicConfig()),
- crypto_stream_(this) {
- }
+ : QuicSession(connection,
+ DefaultQuicConfig()),
+ crypto_stream_(this),
+ writev_consumes_all_data_(false) {}
virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
return &crypto_stream_;
@@ -113,18 +145,53 @@ class TestSession : public QuicSession {
return QuicSession::IsClosedStream(id);
}
- QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id) {
- return QuicSession::GetIncomingReliableStream(stream_id);
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id) {
+ return QuicSession::GetIncomingDataStream(stream_id);
}
- TestCryptoStream crypto_stream_;
+ virtual QuicConsumedData WritevData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) OVERRIDE {
+ // Always consumes everything.
+ if (writev_consumes_all_data_) {
+ return QuicConsumedData(data.TotalBufferSize(), fin);
+ } else {
+ return QuicSession::WritevData(id, data, offset, fin, fec_protection,
+ ack_notifier_delegate);
+ }
+ }
+
+ void set_writev_consumes_all_data(bool val) {
+ writev_consumes_all_data_ = val;
+ }
+
+ QuicConsumedData SendStreamData(QuicStreamId id) {
+ return WritevData(id, IOVector(), 0, true, MAY_FEC_PROTECT, NULL);
+ }
+
+ using QuicSession::PostProcessAfterData;
+
+ private:
+ StrictMock<TestCryptoStream> crypto_stream_;
+
+ bool writev_consumes_all_data_;
};
-class QuicSessionTest : public ::testing::Test {
+class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicSessionTest()
- : connection_(new MockConnection(true)),
+ : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
session_(connection_) {
+ session_.config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
headers_[":host"] = "www.google.com";
headers_[":path"] = "/index.hml";
headers_[":scheme"] = "http";
@@ -169,46 +236,49 @@ class QuicSessionTest : public ::testing::Test {
closed_streams_.insert(id);
}
+ QuicVersion version() const { return connection_->version(); }
+
MockConnection* connection_;
TestSession session_;
set<QuicStreamId> closed_streams_;
SpdyHeaderBlock headers_;
};
-TEST_F(QuicSessionTest, PeerAddress) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicSessionTest, PeerAddress) {
EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address());
}
-TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) {
+TEST_P(QuicSessionTest, IsCryptoHandshakeConfirmed) {
EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
CryptoHandshakeMessage message;
- session_.crypto_stream_.OnHandshakeMessage(message);
+ session_.GetCryptoStream()->OnHandshakeMessage(message);
EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
}
-TEST_F(QuicSessionTest, IsClosedStreamDefault) {
+TEST_P(QuicSessionTest, IsClosedStreamDefault) {
// Ensure that no streams are initially closed.
for (int i = kCryptoStreamId; i < 100; i++) {
- EXPECT_FALSE(session_.IsClosedStream(i));
+ EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i;
}
}
-TEST_F(QuicSessionTest, ImplicitlyCreatedStreams) {
- ASSERT_TRUE(session_.GetIncomingReliableStream(7) != NULL);
+TEST_P(QuicSessionTest, ImplicitlyCreatedStreams) {
+ ASSERT_TRUE(session_.GetIncomingDataStream(7) != NULL);
// Both 3 and 5 should be implicitly created.
EXPECT_FALSE(session_.IsClosedStream(3));
EXPECT_FALSE(session_.IsClosedStream(5));
- ASSERT_TRUE(session_.GetIncomingReliableStream(5) != NULL);
- ASSERT_TRUE(session_.GetIncomingReliableStream(3) != NULL);
+ ASSERT_TRUE(session_.GetIncomingDataStream(5) != NULL);
+ ASSERT_TRUE(session_.GetIncomingDataStream(3) != NULL);
}
-TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
+TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
EXPECT_EQ(2u, stream2->id());
- QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
TestStream* stream4 = session_.CreateOutgoingDataStream();
EXPECT_EQ(4u, stream4->id());
- QuicDataStreamPeer::SetHeadersDecompressed(stream4, true);
CheckClosedStreams();
CloseStream(4);
@@ -217,43 +287,78 @@ TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
CheckClosedStreams();
}
-TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) {
- QuicDataStream* stream3 = session_.GetIncomingReliableStream(3);
- QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
- QuicDataStream* stream5 = session_.GetIncomingReliableStream(5);
- QuicDataStreamPeer::SetHeadersDecompressed(stream5, true);
+TEST_P(QuicSessionTest, IsClosedStreamPeerCreated) {
+ QuicStreamId stream_id1 = kClientDataStreamId1;
+ QuicStreamId stream_id2 = kClientDataStreamId2;
+ QuicDataStream* stream1 = session_.GetIncomingDataStream(stream_id1);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream1, true);
+ QuicDataStream* stream2 = session_.GetIncomingDataStream(stream_id2);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
CheckClosedStreams();
- CloseStream(3);
+ CloseStream(stream_id1);
CheckClosedStreams();
- CloseStream(5);
- // Create stream id 9, and implicitly 7
- QuicDataStream* stream9 = session_.GetIncomingReliableStream(9);
- QuicDataStreamPeer::SetHeadersDecompressed(stream9, true);
+ CloseStream(stream_id2);
+ // Create a stream explicitly, and another implicitly.
+ QuicDataStream* stream3 = session_.GetIncomingDataStream(stream_id2 + 4);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
CheckClosedStreams();
- // Close 9, but make sure 7 is still not closed
- CloseStream(9);
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
CheckClosedStreams();
}
-TEST_F(QuicSessionTest, StreamIdTooLarge) {
- session_.GetIncomingReliableStream(3);
+TEST_P(QuicSessionTest, StreamIdTooLarge) {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ session_.GetIncomingDataStream(stream_id);
EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
- session_.GetIncomingReliableStream(105);
+ session_.GetIncomingDataStream(stream_id + kMaxStreamIdDelta + 2);
+}
+
+TEST_P(QuicSessionTest, DecompressionError) {
+ QuicHeadersStream* stream = QuicSessionPeer::GetHeadersStream(&session_);
+ const unsigned char data[] = {
+ 0x80, 0x03, 0x00, 0x01, // SPDY/3 SYN_STREAM frame
+ 0x00, 0x00, 0x00, 0x25, // flags/length
+ 0x00, 0x00, 0x00, 0x05, // stream id
+ 0x00, 0x00, 0x00, 0x00, // associated stream id
+ 0x00, 0x00,
+ 'a', 'b', 'c', 'd' // invalid compressed data
+ };
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY framing error."));
+ stream->ProcessRawData(reinterpret_cast<const char*>(data),
+ arraysize(data));
}
-TEST_F(QuicSessionTest, DecompressionError) {
- ReliableQuicStream* stream = session_.GetIncomingReliableStream(3);
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
- const char data[] =
- "\0\0\0\0" // priority
- "\1\0\0\0" // headers id
- "\0\0\0\4" // length
- "abcd"; // invalid compressed data
- stream->ProcessRawData(data, arraysize(data));
+TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ // Close the stream.
+ stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ QuicStreamId kClosedStreamId = stream2->id();
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(kClosedStreamId, kSomeMiddlePriority),
+ "Marking unknown stream 2 blocked.");
+ */
}
-TEST_F(QuicSessionTest, OnCanWrite) {
+TEST_P(QuicSessionTest, DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) {
+ const QuicPriority kDifferentPriority = 0;
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ EXPECT_NE(kDifferentPriority, stream2->EffectivePriority());
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(stream2->id(), kDifferentPriority),
+ "Priorities do not match. Got: 0 Expected: 3");
+ */
+}
+
+TEST_P(QuicSessionTest, OnCanWrite) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
TestStream* stream4 = session_.CreateOutgoingDataStream();
TestStream* stream6 = session_.CreateOutgoingDataStream();
@@ -264,16 +369,99 @@ TEST_F(QuicSessionTest, OnCanWrite) {
InSequence s;
StreamBlocker stream2_blocker(&session_, stream2->id());
- EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
- // Reregister, to test the loop limit.
- InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ // Reregister, to test the loop limit.
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
EXPECT_CALL(*stream6, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) {
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
- EXPECT_FALSE(session_.OnCanWrite());
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm, GetCongestionWindow())
+ .WillOnce(Return(kMaxPacketSize * 10));
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream2->id()))));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream4->id()))));
+ EXPECT_CALL(*stream6, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream6->id()))));
+
+ // Expect that we only send one packet, the writes from different streams
+ // should be bundled together.
+ MockPacketWriter* writer =
+ static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _)).WillOnce(
+ Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)).Times(1);
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-TEST_F(QuicSessionTest, BufferedHandshake) {
+TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) {
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ // stream4->OnCanWrite is not called.
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Still congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // stream4->OnCanWrite is called once the connection stops being
+ // congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, BufferedHandshake) {
EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
// Test that blocking other streams does not change our status.
@@ -288,7 +476,7 @@ TEST_F(QuicSessionTest, BufferedHandshake) {
EXPECT_FALSE(session_.HasPendingHandshake());
// Blocking (due to buffering of) the Crypto stream is detected.
- session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority);
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
EXPECT_TRUE(session_.HasPendingHandshake());
TestStream* stream4 = session_.CreateOutgoingDataStream();
@@ -307,20 +495,19 @@ TEST_F(QuicSessionTest, BufferedHandshake) {
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_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream3, OnCanWrite())
+ .WillOnce(Invoke(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(Invoke(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
}
-TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
+TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
TestStream* stream4 = session_.CreateOutgoingDataStream();
TestStream* stream6 = session_.CreateOutgoingDataStream();
@@ -333,146 +520,410 @@ TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
InSequence s;
EXPECT_CALL(*stream2, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
- EXPECT_TRUE(session_.OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-// Regression test for http://crbug.com/248737
-TEST_F(QuicSessionTest, OutOfOrderHeaders) {
- QuicSpdyCompressor compressor;
- vector<QuicStreamFrame> frames;
- QuicPacketHeader header;
- header.public_header.guid = session_.guid();
-
- TestStream* stream2 = session_.CreateOutgoingDataStream();
- TestStream* stream4 = session_.CreateOutgoingDataStream();
- stream2->CloseWriteSide();
- stream4->CloseWriteSide();
-
- // Create frame with headers for stream2.
- string compressed_headers1 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(
- stream2->id(), false, 0, MakeIOVector(compressed_headers1));
-
- // Create frame with headers for stream4.
- string compressed_headers2 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame2(
- stream4->id(), true, 0, MakeIOVector(compressed_headers2));
-
- // 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_.OnStreamFrames(frames);
+TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
- // Process the first frame, and un-cork the buffered headers.
- frames[0] = frame1;
- session_.OnStreamFrames(frames);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Ensure connection level flow control blockage.
+ QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
+ EXPECT_TRUE(session_.flow_controller()->IsBlocked());
+
+ // Mark the crypto and headers streams as write blocked, we expect them to be
+ // allowed to write later.
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
+ session_.MarkWriteBlocked(kHeadersStreamId, kHighestPriority);
+
+ // Create a data stream, and although it is write blocked we never expect it
+ // to be allowed to write as we are connection level flow control blocked.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ session_.MarkWriteBlocked(stream->id(), kSomeMiddlePriority);
+ EXPECT_CALL(*stream, OnCanWrite()).Times(0);
+
+ // The crypto and headers streams should be called even though we are
+ // connection flow control blocked.
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite()).Times(1);
+ TestHeadersStream* headers_stream = new TestHeadersStream(&session_);
+ QuicSessionPeer::SetHeadersStream(&session_, headers_stream);
+ EXPECT_CALL(*headers_stream, OnCanWrite()).Times(1);
- // Ensure that the streams actually close and we don't DCHECK.
- connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-TEST_F(QuicSessionTest, SendGoAway) {
- // After sending a GoAway, ensure new incoming streams cannot be created and
- // result in a RST being sent.
+TEST_P(QuicSessionTest, SendGoAway) {
EXPECT_CALL(*connection_,
SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away."));
session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
EXPECT_TRUE(session_.goaway_sent());
- EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY));
- EXPECT_FALSE(session_.GetIncomingReliableStream(3u));
+ EXPECT_CALL(*connection_,
+ SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY, 0)).Times(0);
+ EXPECT_TRUE(session_.GetIncomingDataStream(3u));
+}
+
+TEST_P(QuicSessionTest, DoNotSendGoAwayTwice) {
+ EXPECT_CALL(*connection_,
+ SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")).Times(1);
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
}
-TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
+TEST_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
EXPECT_EQ(kDefaultInitialTimeoutSecs,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
CryptoHandshakeMessage msg;
- session_.crypto_stream_.OnHandshakeMessage(msg);
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
EXPECT_EQ(kDefaultTimeoutSecs,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
}
-TEST_F(QuicSessionTest, ZombieStream) {
- StrictMock<MockConnection>* connection =
- new StrictMock<MockConnection>(false);
- TestSession session(connection);
-
- TestStream* stream3 = session.CreateOutgoingDataStream();
- EXPECT_EQ(3u, stream3->id());
- TestStream* stream5 = session.CreateOutgoingDataStream();
- EXPECT_EQ(5u, stream5->id());
- EXPECT_EQ(2u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
- // Reset the stream, but since the headers have not been decompressed
- // it will become a zombie and will continue to process data
- // until the headers are decompressed.
- EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
- session.SendRstStream(3, QUIC_STREAM_CANCELLED);
+ QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ EXPECT_EQ(0u, session_.GetNumOpenStreams());
+ // Connection should remain alive.
+ EXPECT_TRUE(connection_->connected());
+}
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) {
+ // If multiple invalid reset stream frames arrive in a single packet, this
+ // should trigger a connection close. However there is no need to send
+ // multiple connection close frames.
+ // Create valid stream.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
vector<QuicStreamFrame> frames;
- QuicPacketHeader header;
- header.public_header.guid = session_.guid();
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
- // Create frame with headers for stream2.
- QuicSpdyCompressor compressor;
- string compressed_headers1 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(
- stream3->id(), false, 0, MakeIOVector(compressed_headers1));
+ // Process first invalid stream reset, resulting in the connection being
+ // closed.
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID))
+ .Times(1);
+ QuicStreamId kLargeInvalidStreamId = 99999999;
+ QuicRstStreamFrame rst1(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ QuicConnectionPeer::CloseConnection(connection_);
- // 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(frame1);
- EXPECT_FALSE(stream3->headers_decompressed());
+ // Processing of second invalid stream reset should not result in the
+ // connection being closed for a second time.
+ QuicRstStreamFrame rst2(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst2);
+}
+
+TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) {
+ // Test that if a stream is flow control blocked, then on receipt of the SHLO
+ // containing a suitable send window offset, the stream becomes unblocked.
+ if (version() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- session.OnStreamFrames(frames);
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+ // Ensure that Writev consumes all the data it is given (simulate no socket
+ // blocking).
+ session_.set_writev_consumes_all_data(true);
+
+ // Create a stream, and send enough data to make it flow control blocked.
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ string body(kDefaultFlowControlSendWindow, '.');
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ stream2->SendBody(body, false);
+ EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
- EXPECT_TRUE(connection->connected());
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
+
+ // Stream is now unblocked.
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
}
-TEST_F(QuicSessionTest, ZombieStreamConnectionClose) {
- StrictMock<MockConnection>* connection =
- new StrictMock<MockConnection>(false);
- TestSession session(connection);
+TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) {
+ // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
+ // Test that receipt of an invalid (< default) flow control window from
+ // the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_16 || version() > QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- TestStream* stream3 = session.CreateOutgoingDataStream();
- EXPECT_EQ(3u, stream3->id());
- TestStream* stream5 = session.CreateOutgoingDataStream();
- EXPECT_EQ(5u, stream5->id());
- EXPECT_EQ(2u, session.GetNumOpenStreams());
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(session_.config(),
+ kInvalidWindow);
- stream3->CloseWriteSide();
- // Reset the stream, but since the headers have not been decompressed
- // it will become a zombie and will continue to process data
- // until the headers are decompressed.
- EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
- session.SendRstStream(3, QUIC_STREAM_CANCELLED);
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)).Times(2);
+ session_.OnConfigNegotiated();
+}
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) stream flow control window from
+ // the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- connection->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false);
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+ kInvalidWindow);
- EXPECT_EQ(0u, session.GetNumOpenStreams());
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
}
-TEST_F(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
- // Send two bytes of payload.
- QuicStreamFrame data1(3, false, 0, MakeIOVector("HT"));
+TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) session flow control window
+ // from the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test that when we receive an out of order stream RST we correctly adjust
+ // our connection level flow control receive window.
+ // On close, the stream should mark as consumed all bytes between the highest
+ // byte consumed so far and the final byte offset from the RST frame.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+ session_.PostProcessAfterData();
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test the situation where we receive a FIN on a stream, and before we fully
+ // consume all the data from the sequencer buffer we locally RST the stream.
+ // The bytes between highest consumed byte, and the final byte offset that we
+ // determined when the FIN arrived, should be marked as consumed at the
+ // connection level flow controller when the stream is reset.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector());
vector<QuicStreamFrame> frames;
- frames.push_back(data1);
- EXPECT_TRUE(session_.OnStreamFrames(frames));
- EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+ session_.PostProcessAfterData();
+
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
+ EXPECT_EQ(kByteOffset,
+ stream->flow_controller()->highest_received_byte_offset());
+
+ // We only expect to see a connection WINDOW_UPDATE when talking
+ // QUIC_VERSION_19, as in this case both stream and session flow control
+ // windows are the same size. In later versions we will not see a connection
+ // level WINDOW_UPDATE when exhausting a stream, as the stream flow control
+ // limit is much lower than the connection flow control limit.
+ if (version() == QUIC_VERSION_19) {
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+ }
- // Send a reset before the headers have been decompressed. This causes
- // an unrecoverable compression context state.
- EXPECT_CALL(*connection_, SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
+ // Reset stream locally.
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
- QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
- session_.OnRstStream(rst1);
- EXPECT_EQ(0u, session_.GetNumOpenStreams());
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a FIN from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a response from the peer with a FIN. We should handle this by
+ // adjusting the connection level flow control receive window to take into
+ // account the total number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ string body = "hello";
+ IOVector data = MakeIOVector(body);
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, data);
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ QuicStreamOffset total_stream_bytes_sent_by_peer =
+ kByteOffset + body.length();
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a RST from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a RST from the peer. We should handle this by adjusting the
+ // connection level flow control receive window to take into account the total
+ // number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) {
+ // Test that if we receive a stream RST with a highest byte offset that
+ // violates flow control, that we close the connection.
+ if (version() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
+ const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA))
+ .Times(2);
+
+ // Check that stream frame + FIN results in connection close.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicStreamFrame frame(stream->id(), true, kLargeOffset, IOVector());
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ // Check that RST results in connection close.
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kLargeOffset);
+ session_.OnRstStream(rst_frame);
+}
+
+TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test that after successful version negotiation, flow control is disabled
+ // appropriately at both the connection and stream level.
+
+ // Initially both stream and connection flow control are enabled.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+ EXPECT_TRUE(session_.flow_controller()->IsEnabled());
+
+ // Version 17 implies that stream flow control is enabled, but connection
+ // level is disabled.
+ session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_17);
+ EXPECT_FALSE(session_.flow_controller()->IsEnabled());
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+
+ // Version 16 means all flow control is disabled.
+ session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_16);
+ EXPECT_FALSE(session_.flow_controller()->IsEnabled());
+ EXPECT_FALSE(stream->flow_controller()->IsEnabled());
}
} // namespace
diff --git a/chromium/net/quic/quic_socket_address_coder.cc b/chromium/net/quic/quic_socket_address_coder.cc
new file mode 100644
index 00000000000..77e595f2d65
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder.cc
@@ -0,0 +1,89 @@
+// Copyright 2014 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_socket_address_coder.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+// For convenience, the values of these constants match the values of AF_INET
+// and AF_INET6 on Linux.
+const uint16 kIPv4 = 2;
+const uint16 kIPv6 = 10;
+
+} // namespace
+
+QuicSocketAddressCoder::QuicSocketAddressCoder() {
+}
+
+QuicSocketAddressCoder::QuicSocketAddressCoder(const IPEndPoint& address)
+ : address_(address) {
+}
+
+QuicSocketAddressCoder::~QuicSocketAddressCoder() {
+}
+
+string QuicSocketAddressCoder::Encode() const {
+ string serialized;
+ uint16 address_family;
+ switch (address_.GetSockAddrFamily()) {
+ case AF_INET:
+ address_family = kIPv4;
+ break;
+ case AF_INET6:
+ address_family = kIPv6;
+ break;
+ default:
+ return serialized;
+ }
+ serialized.append(reinterpret_cast<const char*>(&address_family),
+ sizeof(address_family));
+ serialized.append(IPAddressToPackedString(address_.address()));
+ uint16 port = address_.port();
+ serialized.append(reinterpret_cast<const char*>(&port), sizeof(port));
+ return serialized;
+}
+
+bool QuicSocketAddressCoder::Decode(const char* data, size_t length) {
+ uint16 address_family;
+ if (length < sizeof(address_family)) {
+ return false;
+ }
+ memcpy(&address_family, data, sizeof(address_family));
+ data += sizeof(address_family);
+ length -= sizeof(address_family);
+
+ size_t ip_length;
+ switch (address_family) {
+ case kIPv4:
+ ip_length = kIPv4AddressSize;
+ break;
+ case kIPv6:
+ ip_length = kIPv6AddressSize;
+ break;
+ default:
+ return false;
+ }
+ if (length < ip_length) {
+ return false;
+ }
+ IPAddressNumber ip(ip_length);
+ memcpy(&ip[0], data, ip_length);
+ data += ip_length;
+ length -= ip_length;
+
+ uint16 port;
+ if (length != sizeof(port)) {
+ return false;
+ }
+ memcpy(&port, data, length);
+
+ address_ = IPEndPoint(ip, port);
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_socket_address_coder.h b/chromium/net/quic/quic_socket_address_coder.h
new file mode 100644
index 00000000000..36ad1d03620
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder.h
@@ -0,0 +1,45 @@
+// Copyright 2014 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_SOCKET_ADDRESS_CODER_H_
+#define NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Serializes and parses a socket address (IP address and port), to be used in
+// the kCADR tag in the ServerHello handshake message and the Public Reset
+// packet.
+class NET_EXPORT_PRIVATE QuicSocketAddressCoder {
+ public:
+ QuicSocketAddressCoder();
+ explicit QuicSocketAddressCoder(const IPEndPoint& address);
+ ~QuicSocketAddressCoder();
+
+ std::string Encode() const;
+
+ bool Decode(const char* data, size_t length);
+
+ IPAddressNumber ip() const {
+ return address_.address();
+ }
+
+ uint16 port() const {
+ return address_.port();
+ }
+
+ private:
+ IPEndPoint address_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSocketAddressCoder);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
diff --git a/chromium/net/quic/quic_socket_address_coder_test.cc b/chromium/net/quic/quic_socket_address_coder_test.cc
new file mode 100644
index 00000000000..56bed58b488
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 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_socket_address_coder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv4) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("4.31.198.44", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x1234));
+ string serialized = coder.Encode();
+ string expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv6) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("2001:700:300:1800::f", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x5678));
+ string serialized = coder.Encode();
+ string expected("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv4) {
+ string serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x04\x1f\xc6\x2c", 4);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x1234, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv6) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET6, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f", 16);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x5678, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeBad) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ // Append junk.
+ serialized.push_back('\0');
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized.resize(20);
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Set an unknown address family.
+ serialized[0] = '\x03';
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized[0] = '\x0a';
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Truncate.
+ size_t len = serialized.length();
+ for (size_t i = 0; i < len; i++) {
+ ASSERT_FALSE(serialized.empty());
+ serialized.erase(serialized.length() - 1);
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ }
+ EXPECT_TRUE(serialized.empty());
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeAndDecode) {
+ struct {
+ const char* ip_literal;
+ uint16 port;
+ } test_case[] = {
+ { "93.184.216.119", 0x1234 },
+ { "199.204.44.194", 80 },
+ { "149.20.4.69", 443 },
+ { "127.0.0.1", 8080 },
+ { "2001:700:300:1800::", 0x5678 },
+ { "::1", 65534 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_case); i++) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber(test_case[i].ip_literal, &ip));
+ QuicSocketAddressCoder encoder(IPEndPoint(ip, test_case[i].port));
+ string serialized = encoder.Encode();
+
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(encoder.ip(), decoder.ip());
+ EXPECT_EQ(encoder.port(), decoder.port());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_spdy_compressor.cc b/chromium/net/quic/quic_spdy_compressor.cc
deleted file mode 100644
index 55015b0b42d..00000000000
--- a/chromium/net/quic/quic_spdy_compressor.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_spdy_compressor.h"
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-
-using std::string;
-
-namespace net {
-
-QuicSpdyCompressor::QuicSpdyCompressor()
- : spdy_framer_(SPDY3),
- header_sequence_id_(1) {
- spdy_framer_.set_enable_compression(true);
-}
-
-QuicSpdyCompressor::~QuicSpdyCompressor() {
-}
-
-string QuicSpdyCompressor::CompressHeadersWithPriority(
- QuicPriority priority,
- const SpdyHeaderBlock& headers) {
- return CompressHeadersInternal(priority, headers, true);
-}
-
-string QuicSpdyCompressor::CompressHeaders(
- const SpdyHeaderBlock& headers) {
- // CompressHeadersInternal ignores priority when write_priority is false.
- return CompressHeadersInternal(0 /* ignored */, headers, false);
-}
-
-string QuicSpdyCompressor::CompressHeadersInternal(
- QuicPriority priority,
- const SpdyHeaderBlock& headers,
- bool write_priority) {
- // TODO(rch): Modify the SpdyFramer to expose a
- // CreateCompressedHeaderBlock method, or some such.
- SpdyStreamId stream_id = 3; // unused.
- scoped_ptr<SpdyFrame> frame(spdy_framer_.CreateHeaders(
- stream_id, CONTROL_FLAG_NONE, &headers));
-
- // The size of the spdy HEADER frame's fixed prefix which
- // needs to be stripped off from the resulting frame.
- const size_t header_frame_prefix_len = 12;
- string serialized = string(frame->data() + header_frame_prefix_len,
- frame->size() - header_frame_prefix_len);
- uint32 serialized_len = serialized.length();
- char priority_str[sizeof(priority)];
- memcpy(&priority_str, &priority, sizeof(priority));
- char id_str[sizeof(header_sequence_id_)];
- memcpy(&id_str, &header_sequence_id_, sizeof(header_sequence_id_));
- char len_str[sizeof(serialized_len)];
- memcpy(&len_str, &serialized_len, sizeof(serialized_len));
- string compressed;
- int priority_len = write_priority ? arraysize(priority_str) : 0;
- compressed.reserve(
- priority_len + arraysize(id_str) + arraysize(len_str) + serialized_len);
- if (write_priority) {
- compressed.append(priority_str, arraysize(priority_str));
- }
- compressed.append(id_str, arraysize(id_str));
- compressed.append(len_str, arraysize(len_str));
- compressed.append(serialized);
- ++header_sequence_id_;
- return compressed;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_compressor.h b/chromium/net/quic/quic_spdy_compressor.h
deleted file mode 100644
index 6efb73c74bd..00000000000
--- a/chromium/net/quic/quic_spdy_compressor.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
-#define NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
-
-#include <string>
-
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/spdy/spdy_framer.h"
-
-namespace net {
-
-// Handles the compression of request/response headers blocks. The
-// serialized format is:
-// uint32 - Priority
-// uint32 - Header ID
-// uint32 - Compressed header length
-// ... - Compressed data
-//
-class NET_EXPORT_PRIVATE QuicSpdyCompressor {
- public:
- QuicSpdyCompressor();
- ~QuicSpdyCompressor();
-
- // Returns a string comprised of [header_sequence_id, compressed_headers].
- std::string CompressHeaders(const SpdyHeaderBlock& headers);
-
- // Returns a string comprised of
- // [priority, header_sequence_id, compressed_headers]
- std::string CompressHeadersWithPriority(QuicPriority priority,
- const SpdyHeaderBlock& headers);
-
- private:
- std::string CompressHeadersInternal(QuicPriority priority,
- const SpdyHeaderBlock& headers,
- bool write_priority);
-
- SpdyFramer spdy_framer_;
- QuicHeaderId header_sequence_id_;
-
- DISALLOW_COPY_AND_ASSIGN(QuicSpdyCompressor);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
diff --git a/chromium/net/quic/quic_spdy_compressor_test.cc b/chromium/net/quic/quic_spdy_compressor_test.cc
deleted file mode 100644
index 93066f29091..00000000000
--- a/chromium/net/quic/quic_spdy_compressor_test.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/quic/spdy_utils.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::string;
-
-namespace net {
-namespace test {
-namespace {
-
-class QuicSpdyCompressorTest : public ::testing::Test {
- protected:
- TestDecompressorVisitor visitor_;
-};
-
-TEST_F(QuicSpdyCompressorTest, Compress) {
- QuicSpdyCompressor compressor;
- QuicSpdyDecompressor decompressor;
-
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- string compressed_headers = compressor.CompressHeaders(headers);
- EXPECT_EQ('\1', compressed_headers[0]);
- EXPECT_EQ('\0', compressed_headers[1]);
- EXPECT_EQ('\0', compressed_headers[2]);
- EXPECT_EQ('\0', compressed_headers[3]);
- string compressed_data = compressed_headers.substr(4);
- decompressor.DecompressData(compressed_data, &visitor_);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers),
- visitor_.data());
-}
-
-} // namespace
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_decompressor.cc b/chromium/net/quic/quic_spdy_decompressor.cc
deleted file mode 100644
index f8bd4c142a8..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_spdy_decompressor.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-
-using base::StringPiece;
-using std::min;
-
-namespace net {
-
-class SpdyFramerVisitor : public SpdyFramerVisitorInterface {
- public:
- explicit SpdyFramerVisitor(QuicSpdyDecompressor::Visitor* visitor)
- : visitor_(visitor),
- error_(false) {
- }
-
- virtual void OnError(SpdyFramer* framer) OVERRIDE {
- error_ = true;
- }
- virtual void OnDataFrameHeader(SpdyStreamId stream_id,
- size_t length,
- bool fin) OVERRIDE {}
- virtual void OnStreamFrameData(SpdyStreamId stream_id,
- const char* data,
- size_t len,
- bool fin) OVERRIDE {}
- virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
- const char* header_data,
- size_t len) OVERRIDE;
- virtual void OnSynStream(SpdyStreamId stream_id,
- SpdyStreamId associated_stream_id,
- SpdyPriority priority,
- uint8 credential_slot,
- bool fin,
- bool unidirectional) OVERRIDE {}
- virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {}
- virtual void OnRstStream(SpdyStreamId stream_id,
- SpdyRstStreamStatus status) OVERRIDE {}
- virtual void OnSetting(SpdySettingsIds id,
- uint8 flags,
- uint32 value) OVERRIDE {}
- virtual void OnPing(uint32 unique_id) OVERRIDE {}
- virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) OVERRIDE {}
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {}
- virtual void OnWindowUpdate(SpdyStreamId stream_id,
- uint32 delta_window_size) OVERRIDE {}
- virtual bool OnCredentialFrameData(const char* credential_data,
- size_t len) OVERRIDE {
- return false;
- }
- virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {}
- void set_visitor(QuicSpdyDecompressor::Visitor* visitor) {
- DCHECK(visitor);
- visitor_ = visitor;
- }
-
- private:
- QuicSpdyDecompressor::Visitor* visitor_;
- bool error_;
-};
-
-bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id,
- const char* header_data,
- size_t len) {
- DCHECK(visitor_);
- return visitor_->OnDecompressedData(StringPiece(header_data, len));
-}
-
-QuicSpdyDecompressor::QuicSpdyDecompressor()
- : spdy_framer_(SPDY3),
- spdy_visitor_(new SpdyFramerVisitor(NULL)),
- current_header_id_(1),
- has_current_compressed_size_(false),
- current_compressed_size_(0),
- compressed_bytes_consumed_(0) {
- spdy_framer_.set_visitor(spdy_visitor_.get());
-}
-
-QuicSpdyDecompressor::~QuicSpdyDecompressor() {
-}
-
-size_t QuicSpdyDecompressor::DecompressData(StringPiece data,
- Visitor* visitor) {
- spdy_visitor_->set_visitor(visitor);
- size_t bytes_consumed = 0;
-
- if (!has_current_compressed_size_) {
- const size_t kCompressedBufferSizeSize = sizeof(uint32);
- DCHECK_GT(kCompressedBufferSizeSize, compressed_size_buffer_.length());
- size_t missing_size =
- kCompressedBufferSizeSize - compressed_size_buffer_.length();
- if (data.length() < missing_size) {
- data.AppendToString(&compressed_size_buffer_);
- return data.length();
- }
- bytes_consumed += missing_size;
- data.substr(0, missing_size).AppendToString(&compressed_size_buffer_);
- DCHECK_EQ(kCompressedBufferSizeSize, compressed_size_buffer_.length());
- memcpy(&current_compressed_size_, compressed_size_buffer_.data(),
- kCompressedBufferSizeSize);
- compressed_size_buffer_.clear();
- has_current_compressed_size_ = true;
- data = data.substr(missing_size);
- compressed_bytes_consumed_ = 0;
- }
-
- size_t bytes_to_consume =
- min(current_compressed_size_ - compressed_bytes_consumed_,
- static_cast<uint32>(data.length()));
- if (bytes_to_consume > 0) {
- if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData(
- current_header_id_, data.data(), bytes_to_consume)) {
- visitor->OnDecompressionError();
- return bytes_consumed;
- }
- compressed_bytes_consumed_ += bytes_to_consume;
- bytes_consumed += bytes_to_consume;
- }
- if (current_compressed_size_ - compressed_bytes_consumed_ == 0) {
- ResetForNextHeaders();
- }
- return bytes_consumed;
-}
-
-void QuicSpdyDecompressor::ResetForNextHeaders() {
- has_current_compressed_size_ = false;
- current_compressed_size_ = 0;
- compressed_bytes_consumed_ = 0;
- ++current_header_id_;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_decompressor.h b/chromium/net/quic/quic_spdy_decompressor.h
deleted file mode 100644
index a56c4799015..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
-#define NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_piece.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/spdy/spdy_framer.h"
-
-namespace net {
-
-class SpdyFramerVisitor;
-
-// Handles the compression of request/response headers blocks.
-class NET_EXPORT_PRIVATE QuicSpdyDecompressor {
- public:
- // Interface that receives callbacks with decompressed data as it
- // becomes available.
- class NET_EXPORT_PRIVATE Visitor {
- public:
- virtual ~Visitor() {}
- virtual bool OnDecompressedData(base::StringPiece data) = 0;
- virtual void OnDecompressionError() = 0;
- };
-
- QuicSpdyDecompressor();
- ~QuicSpdyDecompressor();
-
- // Decompresses the data in |data| and invokes |OnDecompressedData|,
- // possibly multiple times, on |visitor|. Returns number of bytes
- // consumed from |data|.
- size_t DecompressData(base::StringPiece data, Visitor* visitor);
-
- QuicHeaderId current_header_id() { return current_header_id_; }
-
- private:
- void ResetForNextHeaders();
-
- SpdyFramer spdy_framer_;
- scoped_ptr<SpdyFramerVisitor> spdy_visitor_;
- // ID of the header currently being parsed.
- QuicHeaderId current_header_id_;
- // True when the size of the headers has been parsed.
- bool has_current_compressed_size_;
- // Size of the headers being parsed.
- uint32 current_compressed_size_;
- // Buffer into which the partial compressed size is written until
- // it is fully parsed.
- std::string compressed_size_buffer_;
- // Number of compressed bytes consumed, out of the total in
- // |current_compressed_size_|.
- uint32 compressed_bytes_consumed_;
- DISALLOW_COPY_AND_ASSIGN(QuicSpdyDecompressor);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
diff --git a/chromium/net/quic/quic_spdy_decompressor_test.cc b/chromium/net/quic/quic_spdy_decompressor_test.cc
deleted file mode 100644
index de9156bdfa4..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor_test.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/quic/spdy_utils.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::string;
-
-namespace net {
-namespace test {
-namespace {
-
-class QuicSpdyDecompressorTest : public ::testing::Test {
- protected:
- QuicSpdyDecompressor decompressor_;
- QuicSpdyCompressor compressor_;
- TestDecompressorVisitor visitor_;
-};
-
-TEST_F(QuicSpdyDecompressorTest, Decompress) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- EXPECT_EQ(1u, decompressor_.current_header_id());
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- EXPECT_EQ(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers, &visitor_));
-
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data());
- EXPECT_EQ(2u, decompressor_.current_header_id());
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressPartial) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
-
- for (size_t i = 0; i < compressed_headers.length(); ++i) {
- QuicSpdyDecompressor decompressor;
- TestDecompressorVisitor visitor;
-
- EXPECT_EQ(1u, decompressor.current_header_id());
-
- string partial_compressed_headers = compressed_headers.substr(0, i);
- EXPECT_EQ(partial_compressed_headers.length(),
- decompressor.DecompressData(partial_compressed_headers,
- &visitor));
- EXPECT_EQ(1u, decompressor.current_header_id()) << "i: " << i;
-
- string remaining_compressed_headers =
- compressed_headers.substr(partial_compressed_headers.length());
- EXPECT_EQ(remaining_compressed_headers.length(),
- decompressor.DecompressData(remaining_compressed_headers,
- &visitor));
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor.data());
-
- EXPECT_EQ(2u, decompressor.current_header_id());
- }
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- EXPECT_EQ(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers + "abc123",
- &visitor_));
-
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data());
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressError) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- EXPECT_EQ(1u, decompressor_.current_header_id());
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- compressed_headers[compressed_headers.length() - 1] ^= 0x01;
- EXPECT_NE(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers, &visitor_));
-
- EXPECT_TRUE(visitor_.error());
- EXPECT_EQ("", visitor_.data());
-}
-
-} // namespace
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_server_stream.cc b/chromium/net/quic/quic_spdy_server_stream.cc
new file mode 100644
index 00000000000..81fc2e064e7
--- /dev/null
+++ b/chromium/net/quic/quic_spdy_server_stream.cc
@@ -0,0 +1,143 @@
+// Copyright 2014 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_spdy_server_stream.h"
+
+#include "base/memory/singleton.h"
+#include "net/quic/quic_in_memory_cache.h"
+#include "net/quic/quic_session.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/spdy_utils.h"
+
+using base::StringPiece;
+using std::string;
+using net::tools::SpdyUtils;
+
+namespace net {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
+ QuicSession* session)
+ : QuicDataStream(id, session),
+ read_buf_(new GrowableIOBuffer()),
+ request_headers_received_(false) {
+}
+
+QuicSpdyServerStream::~QuicSpdyServerStream() {
+}
+
+uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
+ uint32 total_bytes_processed = 0;
+
+ // Are we still reading the request headers.
+ if (!request_headers_received_) {
+ // Grow the read buffer if necessary.
+ if (read_buf_->RemainingCapacity() < (int)data_len) {
+ read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
+ }
+ memcpy(read_buf_->data(), data, data_len);
+ read_buf_->set_offset(read_buf_->offset() + data_len);
+ ParseRequestHeaders();
+ } else {
+ body_.append(data + total_bytes_processed,
+ data_len - total_bytes_processed);
+ }
+ return data_len;
+}
+
+void QuicSpdyServerStream::OnFinRead() {
+ ReliableQuicStream::OnFinRead();
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ if (!request_headers_received_) {
+ SendErrorResponse(); // We're not done reading headers.
+ } else if ((headers_.content_length_status() ==
+ BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
+ body_.size() != headers_.content_length()) {
+ SendErrorResponse(); // Invalid content length
+ } else {
+ SendResponse();
+ }
+}
+
+int QuicSpdyServerStream::ParseRequestHeaders() {
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ SpdyFramer framer(SPDY3);
+ SpdyHeaderBlock headers;
+ char* data = read_buf_->StartOfBuffer();
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
+ &headers);
+ if (len == 0) {
+ return -1;
+ }
+
+ if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) {
+ SendErrorResponse();
+ return -1;
+ }
+
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ body_.append(data + len, delta);
+ }
+
+ request_headers_received_ = true;
+ return len;
+}
+
+void QuicSpdyServerStream::SendResponse() {
+ // Find response in cache. If not found, send error response.
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(headers_);
+ if (response == NULL) {
+ SendErrorResponse();
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
+ DVLOG(1) << "Special response: closing connection.";
+ CloseConnection(QUIC_NO_ERROR);
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
+ DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ DVLOG(1) << "Sending response for stream " << id();
+ SendHeadersAndBody(response->headers(), response->body());
+}
+
+void QuicSpdyServerStream::SendErrorResponse() {
+ DVLOG(1) << "Sending error response for stream " << id();
+ BalsaHeaders headers;
+ headers.SetResponseFirstlineFromStringPieces(
+ "HTTP/1.1", "500", "Server Error");
+ headers.ReplaceOrAppendHeader("content-length", "3");
+ SendHeadersAndBody(headers, "bad");
+}
+
+void QuicSpdyServerStream::SendHeadersAndBody(
+ const BalsaHeaders& response_headers,
+ StringPiece body) {
+ // We only support SPDY and HTTP, and neither handles bidirectional streaming.
+ if (!read_side_closed()) {
+ CloseReadSide();
+ }
+
+ SpdyHeaderBlock header_block =
+ SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
+
+ WriteHeaders(header_block, body.empty(), NULL);
+
+ if (!body.empty()) {
+ WriteOrBufferData(body, true, NULL);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_spdy_server_stream.h b/chromium/net/quic/quic_spdy_server_stream.h
new file mode 100644
index 00000000000..8a6808bd3bc
--- /dev/null
+++ b/chromium/net/quic/quic_spdy_server_stream.h
@@ -0,0 +1,64 @@
+// Copyright 2014 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_SPDY_SERVER_STREAM_H_
+#define NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/io_buffer.h"
+#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/balsa/balsa_headers.h"
+
+namespace net {
+
+class QuicSession;
+
+namespace test {
+class QuicSpdyServerStreamPeer;
+} // namespace test
+
+// All this does right now is aggregate data, and on fin, send an HTTP
+// response.
+class QuicSpdyServerStream : public QuicDataStream {
+ public:
+ QuicSpdyServerStream(QuicStreamId id, QuicSession* session);
+ virtual ~QuicSpdyServerStream();
+
+ // ReliableQuicStream implementation called by the session when there's
+ // data for us.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+ virtual void OnFinRead() OVERRIDE;
+
+ int ParseRequestHeaders();
+
+ private:
+ friend class test::QuicSpdyServerStreamPeer;
+
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendResponse();
+
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body
+ void SendErrorResponse();
+
+ void SendHeadersAndBody(const BalsaHeaders& response_headers,
+ base::StringPiece body);
+
+ BalsaHeaders headers_;
+ string body_;
+
+ // Buffer into which response header data is read.
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+ bool request_headers_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc
index 036a91d38aa..2fc1b9e334c 100644
--- a/chromium/net/quic/quic_stream_factory.cc
+++ b/chromium/net/quic/quic_stream_factory.cc
@@ -6,7 +6,7 @@
#include <set>
-#include "base/logging.h"
+#include "base/cpu.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
@@ -22,6 +22,7 @@
#include "net/quic/congestion_control/tcp_receiver.h"
#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/port_suggester.h"
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_clock.h"
@@ -31,24 +32,117 @@
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_http_stream.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/socket/client_socket_factory.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
using std::string;
using std::vector;
namespace net {
+namespace {
+
+enum CreateSessionFailure {
+ CREATION_ERROR_CONNECTING_SOCKET,
+ CREATION_ERROR_SETTING_RECEIVE_BUFFER,
+ CREATION_ERROR_SETTING_SEND_BUFFER,
+ CREATION_ERROR_MAX
+};
+
+// When a connection is idle for 30 seconds it will be closed.
+const int kIdleConnectionTimeoutSeconds = 30;
+
+// The initial receive window size for both streams and sessions.
+const int32 kInitialReceiveWindowSize = 10 * 1024 * 1024; // 10MB
+
+// The suggested initial congestion windows for a server to use.
+// TODO: This should be tested and optimized, and even better, suggest a window
+// that corresponds to historical bandwidth and min-RTT.
+// Larger initial congestion windows can, if we don't overshoot, reduce latency
+// by avoiding the RTT needed for slow start to double (and re-double) from a
+// default of 10.
+// We match SPDY's use of 32 when secure (since we'd compete with SPDY).
+const int32 kServerSecureInitialCongestionWindow = 32;
+// Be conservative, and just use double a typical TCP ICWND for HTTP.
+const int32 kServerInecureInitialCongestionWindow = 20;
+
+void HistogramCreateSessionFailure(enum CreateSessionFailure error) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error,
+ CREATION_ERROR_MAX);
+}
+
+bool IsEcdsaSupported() {
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+#endif
+
+ return true;
+}
+
+QuicConfig InitializeQuicConfig(bool enable_pacing,
+ bool enable_time_based_loss_detection) {
+ QuicConfig config;
+ config.SetDefaults();
+ config.EnablePacing(enable_pacing);
+ if (enable_time_based_loss_detection)
+ config.SetLossDetectionToSend(kTIME);
+ config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds),
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds));
+ return config;
+}
+
+} // namespace
+
+QuicStreamFactory::IpAliasKey::IpAliasKey() {}
+
+QuicStreamFactory::IpAliasKey::IpAliasKey(IPEndPoint ip_endpoint,
+ bool is_https)
+ : ip_endpoint(ip_endpoint),
+ is_https(is_https) {}
+
+QuicStreamFactory::IpAliasKey::~IpAliasKey() {}
+
+bool QuicStreamFactory::IpAliasKey::operator<(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ if (!(ip_endpoint == other.ip_endpoint)) {
+ return ip_endpoint < other.ip_endpoint;
+ }
+ return is_https < other.is_https;
+}
+
+bool QuicStreamFactory::IpAliasKey::operator==(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ return is_https == other.is_https &&
+ ip_endpoint == other.ip_endpoint;
+};
+
// Responsible for creating a new QUIC session to the specified server, and
// for notifying any associated requests when complete.
class QuicStreamFactory::Job {
public:
Job(QuicStreamFactory* factory,
HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
+ const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
const BoundNetLog& net_log);
+ // Creates a new job to handle the resumption of for connecting an
+ // existing session.
+ Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id);
+
~Job();
int Run(const CompletionCallback& callback);
@@ -56,7 +150,10 @@ class QuicStreamFactory::Job {
int DoLoop(int rv);
int DoResolveHost();
int DoResolveHostComplete(int rv);
+ int DoLoadServerInfo();
+ int DoLoadServerInfoComplete(int rv);
int DoConnect();
+ int DoResumeConnect();
int DoConnectComplete(int rv);
void OnIOComplete(int rv);
@@ -65,8 +162,8 @@ class QuicStreamFactory::Job {
return callback_;
}
- const HostPortProxyPair& host_port_proxy_pair() const {
- return host_port_proxy_pair_;
+ const QuicServerId server_id() const {
+ return server_id_;
}
private:
@@ -74,44 +171,68 @@ class QuicStreamFactory::Job {
STATE_NONE,
STATE_RESOLVE_HOST,
STATE_RESOLVE_HOST_COMPLETE,
+ STATE_LOAD_SERVER_INFO,
+ STATE_LOAD_SERVER_INFO_COMPLETE,
STATE_CONNECT,
+ STATE_RESUME_CONNECT,
STATE_CONNECT_COMPLETE,
};
IoState io_state_;
QuicStreamFactory* factory_;
SingleRequestHostResolver host_resolver_;
- const HostPortProxyPair host_port_proxy_pair_;
- bool is_https_;
- CertVerifier* cert_verifier_;
+ QuicServerId server_id_;
+ bool is_post_;
+ bool was_alternate_protocol_recently_broken_;
+ scoped_ptr<QuicServerInfo> server_info_;
const BoundNetLog net_log_;
QuicClientSession* session_;
CompletionCallback callback_;
AddressList address_list_;
+ base::TimeTicks disk_cache_load_start_time_;
+ base::WeakPtrFactory<Job> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Job);
};
-QuicStreamFactory::Job::Job(
- QuicStreamFactory* factory,
- HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log)
- : factory_(factory),
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
+ const BoundNetLog& net_log)
+ : io_state_(STATE_RESOLVE_HOST),
+ factory_(factory),
host_resolver_(host_resolver),
- host_port_proxy_pair_(host_port_proxy_pair),
- is_https_(is_https),
- cert_verifier_(cert_verifier),
+ server_id_(host_port_pair, is_https, privacy_mode),
+ is_post_(method == "POST"),
+ was_alternate_protocol_recently_broken_(
+ was_alternate_protocol_recently_broken),
+ server_info_(server_info),
net_log_(net_log),
- session_(NULL) {
-}
+ session_(NULL),
+ weak_factory_(this) {}
+
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id)
+ : io_state_(STATE_RESUME_CONNECT),
+ factory_(factory),
+ host_resolver_(host_resolver), // unused
+ server_id_(server_id),
+ is_post_(false), // unused
+ was_alternate_protocol_recently_broken_(false), // unused
+ net_log_(session->net_log()), // unused
+ session_(session),
+ weak_factory_(this) {}
QuicStreamFactory::Job::~Job() {
}
int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
- io_state_ = STATE_RESOLVE_HOST;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = callback;
@@ -131,10 +252,21 @@ int QuicStreamFactory::Job::DoLoop(int rv) {
case STATE_RESOLVE_HOST_COMPLETE:
rv = DoResolveHostComplete(rv);
break;
+ case STATE_LOAD_SERVER_INFO:
+ CHECK_EQ(OK, rv);
+ rv = DoLoadServerInfo();
+ break;
+ case STATE_LOAD_SERVER_INFO_COMPLETE:
+ rv = DoLoadServerInfoComplete(rv);
+ break;
case STATE_CONNECT:
CHECK_EQ(OK, rv);
rv = DoConnect();
break;
+ case STATE_RESUME_CONNECT:
+ CHECK_EQ(OK, rv);
+ rv = DoResumeConnect();
+ break;
case STATE_CONNECT_COMPLETE:
rv = DoConnectComplete(rv);
break;
@@ -155,12 +287,19 @@ void QuicStreamFactory::Job::OnIOComplete(int rv) {
}
int QuicStreamFactory::Job::DoResolveHost() {
+ // Start loading the data now, and wait for it after we resolve the host.
+ if (server_info_) {
+ disk_cache_load_start_time_ = base::TimeTicks::Now();
+ server_info_->Start();
+ }
+
io_state_ = STATE_RESOLVE_HOST_COMPLETE;
return host_resolver_.Resolve(
- HostResolver::RequestInfo(host_port_proxy_pair_.first),
+ HostResolver::RequestInfo(server_id_.host_port_pair()),
DEFAULT_PRIORITY,
&address_list_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Unretained(this)),
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()),
net_log_);
}
@@ -168,11 +307,97 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
if (rv != OK)
return rv;
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
+ DCHECK(!factory_->HasActiveSession(server_id_));
+
+ // Inform the factory of this resolution, which will set up
+ // a session alias, if possible.
+ if (factory_->OnResolution(server_id_, address_list_)) {
+ return OK;
+ }
+
+ io_state_ = STATE_LOAD_SERVER_INFO;
+ return OK;
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfo() {
+ io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE;
+
+ if (!server_info_)
+ return OK;
+
+ return server_info_->WaitForDataReady(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
+ if (server_info_) {
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime",
+ base::TimeTicks::Now() - disk_cache_load_start_time_);
+ }
+
+ if (rv != OK) {
+ server_info_.reset();
+ }
+
io_state_ = STATE_CONNECT;
return OK;
}
+int QuicStreamFactory::Job::DoConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = factory_->CreateSession(server_id_, server_info_.Pass(),
+ address_list_, net_log_, &session_);
+ if (rv != OK) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!session_);
+ return rv;
+ }
+
+ session_->StartReading();
+ if (!session_->connection()->connected()) {
+ return ERR_QUIC_PROTOCOL_ERROR;
+ }
+ bool require_confirmation =
+ factory_->require_confirmation() || is_post_ ||
+ was_alternate_protocol_recently_broken_;
+ rv = session_->CryptoConnect(
+ require_confirmation,
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoResumeConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = session_->ResumeCryptoConnect(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoConnectComplete(int rv) {
+ if (rv != OK)
+ return rv;
+
+ DCHECK(!factory_->HasActiveSession(server_id_));
+ // There may well now be an active session for this IP. If so, use the
+ // existing session instead.
+ AddressList address(session_->connection()->peer_address());
+ if (factory_->OnResolution(server_id_, address)) {
+ session_->connection()->SendConnectionClose(QUIC_CONNECTION_IP_POOLED);
+ session_ = NULL;
+ return OK;
+ }
+
+ factory_->ActivateSession(server_id_, session_);
+
+ return OK;
+}
+
QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory)
: factory_(factory) {}
@@ -181,20 +406,20 @@ QuicStreamRequest::~QuicStreamRequest() {
factory_->CancelRequest(this);
}
-int QuicStreamRequest::Request(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log,
- const CompletionCallback& callback) {
+int QuicStreamRequest::Request(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback) {
DCHECK(!stream_);
DCHECK(callback_.is_null());
- int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier,
+ DCHECK(factory_);
+ int rv = factory_->Create(host_port_pair, is_https, privacy_mode, method,
net_log, this);
if (rv == ERR_IO_PENDING) {
- host_port_proxy_pair_ = host_port_proxy_pair;
+ host_port_pair_ = host_port_pair;
is_https_ = is_https;
- cert_verifier_ = cert_verifier;
net_log_ = net_log;
callback_ = callback;
} else {
@@ -220,103 +445,131 @@ scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() {
return stream_.Pass();
}
-int QuicStreamFactory::Job::DoConnect() {
- io_state_ = STATE_CONNECT_COMPLETE;
-
- int rv = factory_->CreateSession(host_port_proxy_pair_, is_https_,
- cert_verifier_, address_list_, net_log_, &session_);
- if (rv != OK) {
- DCHECK(rv != ERR_IO_PENDING);
- DCHECK(!session_);
- return rv;
- }
-
- session_->StartReading();
- rv = session_->CryptoConnect(
- factory_->require_confirmation() || is_https_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete,
- base::Unretained(this)));
- return rv;
-}
-
-int QuicStreamFactory::Job::DoConnectComplete(int rv) {
- if (rv != OK)
- return rv;
-
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
- factory_->ActivateSession(host_port_proxy_pair_, session_);
-
- return OK;
-}
-
QuicStreamFactory::QuicStreamFactory(
HostResolver* host_resolver,
ClientSocketFactory* client_socket_factory,
base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
QuicRandom* random_generator,
QuicClock* clock,
- size_t max_packet_length)
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool enable_pacing,
+ bool enable_time_based_loss_detection)
: require_confirmation_(true),
host_resolver_(host_resolver),
client_socket_factory_(client_socket_factory),
http_server_properties_(http_server_properties),
+ cert_verifier_(cert_verifier),
+ quic_server_info_factory_(NULL),
quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
random_generator_(random_generator),
clock_(clock),
max_packet_length_(max_packet_length),
- weak_factory_(this),
- port_seed_(random_generator_->RandUint64()) {
- config_.SetDefaults();
- config_.set_idle_connection_state_lifetime(
- QuicTime::Delta::FromSeconds(30),
- QuicTime::Delta::FromSeconds(30));
-
- cannoncial_suffixes_.push_back(string(".c.youtube.com"));
- cannoncial_suffixes_.push_back(string(".googlevideo.com"));
+ config_(InitializeQuicConfig(enable_pacing,
+ enable_time_based_loss_detection)),
+ supported_versions_(supported_versions),
+ enable_port_selection_(enable_port_selection),
+ port_seed_(random_generator_->RandUint64()),
+ weak_factory_(this) {
+ crypto_config_.SetDefaults();
+ crypto_config_.set_user_agent_id(user_agent_id);
+ crypto_config_.AddCanonicalSuffix(".c.youtube.com");
+ crypto_config_.AddCanonicalSuffix(".googlevideo.com");
+ crypto_config_.SetProofVerifier(new ProofVerifierChromium(cert_verifier));
+ base::CPU cpu;
+ if (cpu.has_aesni() && cpu.has_avx())
+ crypto_config_.PreferAesGcm();
+ if (!IsEcdsaSupported())
+ crypto_config_.DisableEcdsa();
}
QuicStreamFactory::~QuicStreamFactory() {
CloseAllSessions(ERR_ABORTED);
- STLDeleteElements(&all_sessions_);
+ while (!all_sessions_.empty()) {
+ delete all_sessions_.begin()->first;
+ all_sessions_.erase(all_sessions_.begin());
+ }
STLDeleteValues(&active_jobs_);
- STLDeleteValues(&all_crypto_configs_);
}
-int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair,
+int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
QuicStreamRequest* request) {
- if (HasActiveSession(host_port_proxy_pair)) {
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ QuicServerId server_id(host_port_pair, is_https, privacy_mode);
+ if (HasActiveSession(server_id)) {
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
return OK;
}
- if (HasActiveJob(host_port_proxy_pair)) {
- Job* job = active_jobs_[host_port_proxy_pair];
+ if (HasActiveJob(server_id)) {
+ Job* job = active_jobs_[server_id];
active_requests_[request] = job;
job_requests_map_[job].insert(request);
return ERR_IO_PENDING;
}
- scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair,
- is_https, cert_verifier, net_log));
+ QuicServerInfo* quic_server_info = NULL;
+ if (quic_server_info_factory_) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ DCHECK(cached);
+ if (cached->IsEmpty()) {
+ quic_server_info = quic_server_info_factory_->GetForServer(server_id);
+ }
+ }
+ bool was_alternate_protocol_recently_broken =
+ http_server_properties_ &&
+ http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server_id.host_port_pair());
+ scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https,
+ was_alternate_protocol_recently_broken,
+ privacy_mode, method, quic_server_info, net_log));
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
base::Unretained(this), job.get()));
if (rv == ERR_IO_PENDING) {
active_requests_[request] = job.get();
job_requests_map_[job.get()].insert(request);
- active_jobs_[host_port_proxy_pair] = job.release();
+ active_jobs_[server_id] = job.release();
}
if (rv == OK) {
- DCHECK(HasActiveSession(host_port_proxy_pair));
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ DCHECK(HasActiveSession(server_id));
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
}
return rv;
}
+bool QuicStreamFactory::OnResolution(
+ const QuicServerId& server_id,
+ const AddressList& address_list) {
+ DCHECK(!HasActiveSession(server_id));
+ for (size_t i = 0; i < address_list.size(); ++i) {
+ const IPEndPoint& address = address_list[i];
+ const IpAliasKey ip_alias_key(address, server_id.is_https());
+ if (!ContainsKey(ip_aliases_, ip_alias_key))
+ continue;
+
+ const SessionSet& sessions = ip_aliases_[ip_alias_key];
+ for (SessionSet::const_iterator i = sessions.begin();
+ i != sessions.end(); ++i) {
+ QuicClientSession* session = *i;
+ if (!session->CanPool(server_id.host()))
+ continue;
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ return true;
+ }
+ }
+ return false;
+}
+
void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
if (rv == OK) {
require_confirmation_ = false;
@@ -324,8 +577,8 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Create all the streams, but do not notify them yet.
for (RequestSet::iterator it = job_requests_map_[job].begin();
it != job_requests_map_[job].end() ; ++it) {
- DCHECK(HasActiveSession(job->host_port_proxy_pair()));
- (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(),
+ DCHECK(HasActiveSession(job->server_id()));
+ (*it)->set_stream(CreateIfSessionExists(job->server_id(),
(*it)->net_log()));
}
}
@@ -339,7 +592,7 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// profile which can not be deleted via callbacks.
request->OnRequestComplete(rv);
}
- active_jobs_.erase(job->host_port_proxy_pair());
+ active_jobs_.erase(job->server_id());
job_requests_map_.erase(job);
delete job;
return;
@@ -348,14 +601,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Returns a newly created QuicHttpStream owned by the caller, if a
// matching session already exists. Returns NULL otherwise.
scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
const BoundNetLog& net_log) {
- if (!HasActiveSession(host_port_proxy_pair)) {
+ if (!HasActiveSession(server_id)) {
DVLOG(1) << "No active session";
return scoped_ptr<QuicHttpStream>();
}
- QuicClientSession* session = active_sessions_[host_port_proxy_pair];
+ QuicClientSession* session = active_sessions_[server_id];
DCHECK(session);
return scoped_ptr<QuicHttpStream>(
new QuicHttpStream(session->GetWeakPtr()));
@@ -370,13 +623,22 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
++it) {
DCHECK(active_sessions_.count(*it));
DCHECK_EQ(session, active_sessions_[*it]);
+ // Track sessions which have recently gone away so that we can disable
+ // port suggestions.
+ if (session->goaway_received()) {
+ gone_away_aliases_.insert(*it);
+ }
+
active_sessions_.erase(*it);
- if (!session->IsCryptoHandshakeConfirmed() && http_server_properties_) {
- // TODO(rch): In the special case where the session has received no
- // packets from the peer, we should consider blacklisting this
- // differently so that we still race TCP but we don't consider the
- // session connected until the handshake has been confirmed.
- http_server_properties_->SetBrokenAlternateProtocol(it->first);
+ ProcessGoingAwaySession(session, *it, true);
+ }
+ ProcessGoingAwaySession(session, all_sessions_[session], false);
+ if (!aliases.empty()) {
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
}
}
session_aliases_.erase(session);
@@ -385,8 +647,37 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
DCHECK_EQ(0u, session->GetNumOpenStreams());
OnSessionGoingAway(session);
- all_sessions_.erase(session);
delete session;
+ all_sessions_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionConnectTimeout(
+ QuicClientSession* session) {
+ const AliasSet& aliases = session_aliases_[session];
+ for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
+ ++it) {
+ DCHECK(active_sessions_.count(*it));
+ DCHECK_EQ(session, active_sessions_[*it]);
+ active_sessions_.erase(*it);
+ }
+
+ if (aliases.empty()) {
+ return;
+ }
+
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
+ }
+ QuicServerId server_id = *aliases.begin();
+ session_aliases_.erase(session);
+ Job* job = new Job(this, host_resolver_, session, server_id);
+ active_jobs_[server_id] = job;
+ int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
+ base::Unretained(this), job));
+ DCHECK_EQ(ERR_IO_PENDING, rv);
}
void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
@@ -404,7 +695,7 @@ void QuicStreamFactory::CloseAllSessions(int error) {
}
while (!all_sessions_.empty()) {
size_t initial_size = all_sessions_.size();
- (*all_sessions_.begin())->CloseSessionOnError(error);
+ all_sessions_.begin()->first->CloseSessionOnError(error);
DCHECK_NE(initial_size, all_sessions_.size());
}
DCHECK(all_sessions_.empty());
@@ -415,14 +706,26 @@ base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
for (SessionMap::const_iterator it = active_sessions_.begin();
it != active_sessions_.end(); ++it) {
- const HostPortProxyPair& pair = it->first;
- const QuicClientSession* session = it->second;
-
- list->Append(session->GetInfoAsValue(pair.first));
+ const QuicServerId& server_id = it->first;
+ QuicClientSession* session = it->second;
+ const AliasSet& aliases = session_aliases_.find(session)->second;
+ // Only add a session to the list once.
+ if (server_id == *aliases.begin()) {
+ std::set<HostPortPair> hosts;
+ for (AliasSet::const_iterator alias_it = aliases.begin();
+ alias_it != aliases.end(); ++alias_it) {
+ hosts.insert(alias_it->host_port_pair());
+ }
+ list->Append(session->GetInfoAsValue(hosts));
+ }
}
return list;
}
+void QuicStreamFactory::ClearCachedStatesInCryptoConfig() {
+ crypto_config_.ClearCachedStates();
+}
+
void QuicStreamFactory::OnIPAddressChanged() {
CloseAllSessions(ERR_NETWORK_CHANGED);
require_confirmation_ = true;
@@ -446,54 +749,69 @@ void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) {
}
bool QuicStreamFactory::HasActiveSession(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_sessions_, host_port_proxy_pair);
+ const QuicServerId& server_id) const {
+ return ContainsKey(active_sessions_, server_id);
}
int QuicStreamFactory::CreateSession(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
+ const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> server_info,
const AddressList& address_list,
const BoundNetLog& net_log,
QuicClientSession** session) {
- QuicGuid guid = random_generator_->RandUint64();
+ bool enable_port_selection = enable_port_selection_;
+ if (enable_port_selection &&
+ ContainsKey(gone_away_aliases_, server_id)) {
+ // Disable port selection when the server is going away.
+ // There is no point in trying to return to the same server, if
+ // that server is no longer handling requests.
+ enable_port_selection = false;
+ gone_away_aliases_.erase(server_id);
+ }
+
+ QuicConnectionId connection_id = random_generator_->RandUint64();
IPEndPoint addr = *address_list.begin();
scoped_refptr<PortSuggester> port_suggester =
- new PortSuggester(host_port_proxy_pair.first, port_seed_);
- DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND;
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- bind_type = DatagramSocket::DEFAULT_BIND;
-#endif
+ new PortSuggester(server_id.host_port_pair(), port_seed_);
+ DatagramSocket::BindType bind_type = enable_port_selection ?
+ DatagramSocket::RANDOM_BIND : // Use our callback.
+ DatagramSocket::DEFAULT_BIND; // Use OS to randomize.
scoped_ptr<DatagramClientSocket> socket(
client_socket_factory_->CreateDatagramClientSocket(
bind_type,
base::Bind(&PortSuggester::SuggestPort, port_suggester),
net_log.net_log(), net_log.source()));
int rv = socket->Connect(addr);
- if (rv != OK)
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_CONNECTING_SOCKET);
return rv;
+ }
UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
port_suggester->call_count());
-
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- DCHECK_EQ(0u, port_suggester->call_count());
-#else
- DCHECK_LE(1u, port_suggester->call_count());
-#endif
+ if (enable_port_selection) {
+ DCHECK_LE(1u, port_suggester->call_count());
+ } else {
+ DCHECK_EQ(0u, port_suggester->call_count());
+ }
// We should adaptively set this buffer size, but for now, we'll use a size
// that is more than large enough for a full receive window, and yet
// does not consume "too much" memory. If we see bursty packet loss, we may
// revisit this setting and test for its impact.
const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP);
- socket->SetReceiveBufferSize(kSocketBufferSize);
+ rv = socket->SetReceiveBufferSize(kSocketBufferSize);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_RECEIVE_BUFFER);
+ return rv;
+ }
// 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.
+ rv = socket->SetSendBufferSize(kMaxPacketSize * 20);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_SEND_BUFFER);
+ return rv;
+ }
scoped_ptr<QuicDefaultPacketWriter> writer(
new QuicDefaultPacketWriter(socket.get()));
@@ -504,96 +822,134 @@ int QuicStreamFactory::CreateSession(
clock_.get(), random_generator_));
}
- QuicConnection* connection = new QuicConnection(guid, addr, helper_.get(),
- writer.get(), false,
- QuicSupportedVersions());
+ QuicConnection* connection =
+ new QuicConnection(connection_id, addr, helper_.get(), writer.get(),
+ false, supported_versions_);
writer->SetConnection(connection);
- connection->options()->max_packet_length = max_packet_length_;
-
- QuicCryptoClientConfig* crypto_config =
- GetOrCreateCryptoConfig(host_port_proxy_pair);
- DCHECK(crypto_config);
+ connection->set_max_packet_length(max_packet_length_);
+
+ InitializeCachedStateInCryptoConfig(server_id, server_info);
+
+ QuicConfig config = config_;
+ config.SetInitialCongestionWindowToSend(
+ server_id.is_https() ? kServerSecureInitialCongestionWindow
+ : kServerInecureInitialCongestionWindow);
+ config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize);
+ if (http_server_properties_) {
+ const HttpServerProperties::NetworkStats* stats =
+ http_server_properties_->GetServerNetworkStats(
+ server_id.host_port_pair());
+ if (stats != NULL) {
+ config.SetInitialRoundTripTimeUsToSend(stats->srtt.InMicroseconds());
+ }
+ }
*session = new QuicClientSession(
connection, socket.Pass(), writer.Pass(), this,
- quic_crypto_client_stream_factory_, host_port_proxy_pair.first.host(),
- config_, crypto_config, net_log.net_log());
- all_sessions_.insert(*session); // owning pointer
- if (is_https) {
- crypto_config->SetProofVerifier(
- new ProofVerifierChromium(cert_verifier, net_log));
- }
+ quic_crypto_client_stream_factory_, server_info.Pass(), server_id,
+ config, &crypto_config_,
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ net_log.net_log());
+ all_sessions_[*session] = server_id; // owning pointer
return OK;
}
-bool QuicStreamFactory::HasActiveJob(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_jobs_, host_port_proxy_pair);
+bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
+ return ContainsKey(active_jobs_, key);
}
void QuicStreamFactory::ActivateSession(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
QuicClientSession* session) {
- DCHECK(!HasActiveSession(host_port_proxy_pair));
- active_sessions_[host_port_proxy_pair] = session;
- session_aliases_[session].insert(host_port_proxy_pair);
-}
+ DCHECK(!HasActiveSession(server_id));
+ UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size());
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ server_id.is_https());
+ DCHECK(!ContainsKey(ip_aliases_[ip_alias_key], session));
+ ip_aliases_[ip_alias_key].insert(session);
+}
+
+void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info) {
+ if (!server_info)
+ return;
-QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig(
- const HostPortProxyPair& host_port_proxy_pair) {
- QuicCryptoClientConfig* crypto_config;
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ if (!cached->IsEmpty())
+ return;
- if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) {
- crypto_config = all_crypto_configs_[host_port_proxy_pair];
- DCHECK(crypto_config);
- } else {
- // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
- // share the same crypto_config, will it cause issues?
- crypto_config = new QuicCryptoClientConfig();
- crypto_config->SetDefaults();
- all_crypto_configs_[host_port_proxy_pair] = crypto_config;
- PopulateFromCanonicalConfig(host_port_proxy_pair, crypto_config);
+ if (!cached->Initialize(server_info->state().server_config,
+ server_info->state().source_address_token,
+ server_info->state().certs,
+ server_info->state().server_config_sig,
+ clock_->WallNow()))
+ return;
+
+ if (!server_id.is_https()) {
+ // Don't check the certificates for insecure QUIC.
+ cached->SetProofValid();
}
- return crypto_config;
}
-void QuicStreamFactory::PopulateFromCanonicalConfig(
- const HostPortProxyPair& host_port_proxy_pair,
- QuicCryptoClientConfig* crypto_config) {
- const string server_hostname = host_port_proxy_pair.first.host();
-
- unsigned i = 0;
- for (; i < cannoncial_suffixes_.size(); ++i) {
- if (EndsWith(server_hostname, cannoncial_suffixes_[i], false)) {
- break;
- }
- }
- if (i == cannoncial_suffixes_.size())
+void QuicStreamFactory::ProcessGoingAwaySession(
+ QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool session_was_active) {
+ if (!http_server_properties_)
return;
- HostPortPair canonical_host_port(cannoncial_suffixes_[i],
- host_port_proxy_pair.first.port());
- if (!ContainsKey(canonical_hostname_to_origin_map_, canonical_host_port)) {
- // This is the first host we've seen which matches the suffix, so make it
- // canonical.
- canonical_hostname_to_origin_map_[canonical_host_port] =
- host_port_proxy_pair;
+ const QuicConnectionStats& stats = session->connection()->GetStats();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ HttpServerProperties::NetworkStats network_stats;
+ network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us);
+ network_stats.bandwidth_estimate = stats.estimated_bandwidth;
+ http_server_properties_->SetServerNetworkStats(server_id.host_port_pair(),
+ network_stats);
return;
}
- const HostPortProxyPair& canonical_host_port_proxy_pair =
- canonical_hostname_to_origin_map_[canonical_host_port];
- QuicCryptoClientConfig* canonical_crypto_config =
- all_crypto_configs_[canonical_host_port_proxy_pair];
- DCHECK(canonical_crypto_config);
+ UMA_HISTOGRAM_COUNTS("Net.QuicHandshakeNotConfirmedNumPacketsReceived",
+ stats.packets_received);
+
+ if (!session_was_active)
+ return;
+
+ const HostPortPair& server = server_id.host_port_pair();
+ // Don't try to change the alternate-protocol state, if the
+ // alternate-protocol state is unknown.
+ if (!http_server_properties_->HasAlternateProtocol(server))
+ return;
- // Copy the CachedState for the canonical server from canonical_crypto_config
- // as the initial CachedState for the server_hostname in crypto_config.
- crypto_config->InitializeFrom(server_hostname,
- canonical_host_port_proxy_pair.first.host(),
- canonical_crypto_config);
- // Update canonical version to point at the "most recent" crypto_config.
- canonical_hostname_to_origin_map_[canonical_host_port] = host_port_proxy_pair;
+ // TODO(rch): In the special case where the session has received no
+ // packets from the peer, we should consider blacklisting this
+ // differently so that we still race TCP but we don't consider the
+ // session connected until the handshake has been confirmed.
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY);
+ PortAlternateProtocolPair alternate =
+ http_server_properties_->GetAlternateProtocol(server);
+ DCHECK_EQ(QUIC, alternate.protocol);
+
+ // Since the session was active, there's no longer an
+ // HttpStreamFactoryImpl::Job running which can mark it broken, unless the
+ // TCP job also fails. So to avoid not using QUIC when we otherwise could,
+ // we mark it as broken, and then immediately re-enable it. This leaves
+ // QUIC as "recently broken" which means that 0-RTT will be disabled but
+ // we'll still race.
+ http_server_properties_->SetBrokenAlternateProtocol(server);
+ http_server_properties_->ClearAlternateProtocol(server);
+ http_server_properties_->SetAlternateProtocol(
+ server, alternate.port, alternate.protocol);
+ DCHECK_EQ(QUIC,
+ http_server_properties_->GetAlternateProtocol(server).protocol);
+ DCHECK(http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server));
}
} // namespace net
diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h
index e55c83c3cbe..eb82659bbf8 100644
--- a/chromium/net/quic/quic_stream_factory.h
+++ b/chromium/net/quic/quic_stream_factory.h
@@ -5,10 +5,12 @@
#ifndef NET_QUIC_QUIC_STREAM_FACTORY_H_
#define NET_QUIC_QUIC_STREAM_FACTORY_H_
+#include <list>
#include <map>
#include <string>
#include <vector>
+#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
@@ -33,6 +35,8 @@ class QuicClientSession;
class QuicConnectionHelper;
class QuicCryptoClientStreamFactory;
class QuicRandom;
+class QuicServerInfoFactory;
+class QuicServerId;
class QuicStreamFactory;
namespace test {
@@ -48,9 +52,10 @@ class NET_EXPORT_PRIVATE QuicStreamRequest {
~QuicStreamRequest();
// For http, |is_https| is false and |cert_verifier| can be null.
- int Request(const HostPortProxyPair& host_port_proxy_pair,
+ int Request(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
const CompletionCallback& callback);
@@ -66,9 +71,8 @@ class NET_EXPORT_PRIVATE QuicStreamRequest {
private:
QuicStreamFactory* factory_;
- HostPortProxyPair host_port_proxy_pair_;
+ HostPortPair host_port_pair_;
bool is_https_;
- CertVerifier* cert_verifier_;
BoundNetLog net_log_;
CompletionCallback callback_;
scoped_ptr<QuicHttpStream> stream_;
@@ -86,31 +90,32 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
HostResolver* host_resolver,
ClientSocketFactory* client_socket_factory,
base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
QuicRandom* random_generator,
QuicClock* clock,
- size_t max_packet_length);
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool enable_pacing,
+ bool enable_time_based_loss_detection);
virtual ~QuicStreamFactory();
- // Creates a new QuicHttpStream to |host_port_proxy_pair| which will be
+ // Creates a new QuicHttpStream to |host_port_pair| which will be
// owned by |request|. |is_https| specifies if the protocol is https or not.
// |cert_verifier| is used by ProofVerifier for verifying the certificate
// chain and signature. For http, this can be null. If a matching session
// already exists, this method will return OK. If no matching session exists,
// this will return ERR_IO_PENDING and will invoke OnRequestComplete
// asynchronously.
- int Create(const HostPortProxyPair& host_port_proxy_pair,
+ int Create(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
QuicStreamRequest* request);
- // Returns a newly created QuicHttpStream owned by the caller, if a
- // matching session already exists. Returns NULL otherwise.
- scoped_ptr<QuicHttpStream> CreateIfSessionExists(
- const HostPortProxyPair& host_port_proxy_pair,
- const BoundNetLog& net_log);
-
// Called by a session when it becomes idle.
void OnIdleSession(QuicClientSession* session);
@@ -121,6 +126,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
// Called by a session after it shuts down.
void OnSessionClosed(QuicClientSession* session);
+ // Called by a session whose connection has timed out.
+ void OnSessionConnectTimeout(QuicClientSession* session);
+
// Cancels a pending request.
void CancelRequest(QuicStreamRequest* request);
@@ -129,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
base::Value* QuicStreamFactoryInfoToValue() const;
+ // Delete all cached state objects in |crypto_config_|.
+ void ClearCachedStatesInCryptoConfig();
+
// NetworkChangeNotifier::IPAddressObserver methods:
// Until the servers support roaming, close all connections when the local
@@ -149,48 +160,83 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
QuicConnectionHelper* helper() { return helper_.get(); }
+ bool enable_port_selection() const { return enable_port_selection_; }
+
+ bool has_quic_server_info_factory() {
+ return quic_server_info_factory_ != NULL;
+ }
+
+ void set_quic_server_info_factory(
+ QuicServerInfoFactory* quic_server_info_factory) {
+ DCHECK(!quic_server_info_factory_);
+ quic_server_info_factory_ = quic_server_info_factory;
+ }
+
private:
class Job;
friend class test::QuicStreamFactoryPeer;
- typedef std::map<HostPortProxyPair, QuicClientSession*> SessionMap;
- typedef std::set<HostPortProxyPair> AliasSet;
+ // The key used to find session by ip. Includes
+ // the ip address, port, and scheme.
+ struct NET_EXPORT_PRIVATE IpAliasKey {
+ IpAliasKey();
+ IpAliasKey(IPEndPoint ip_endpoint, bool is_https);
+ ~IpAliasKey();
+
+ IPEndPoint ip_endpoint;
+ bool is_https;
+
+ // Needed to be an element of std::set.
+ bool operator<(const IpAliasKey &other) const;
+ bool operator==(const IpAliasKey &other) const;
+ };
+
+ typedef std::map<QuicServerId, QuicClientSession*> SessionMap;
+ typedef std::map<QuicClientSession*, QuicServerId> SessionIdMap;
+ typedef std::set<QuicServerId> AliasSet;
typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap;
typedef std::set<QuicClientSession*> SessionSet;
- typedef std::map<HostPortProxyPair, QuicCryptoClientConfig*> CryptoConfigMap;
- typedef std::map<HostPortPair, HostPortProxyPair> CanonicalHostMap;
- typedef std::map<HostPortProxyPair, Job*> JobMap;
+ typedef std::map<IpAliasKey, SessionSet> IPAliasMap;
+ typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap;
+ typedef std::map<QuicServerId, Job*> JobMap;
typedef std::map<QuicStreamRequest*, Job*> RequestMap;
typedef std::set<QuicStreamRequest*> RequestSet;
typedef std::map<Job*, RequestSet> JobRequestsMap;
+ // Returns a newly created QuicHttpStream owned by the caller, if a
+ // matching session already exists. Returns NULL otherwise.
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(const QuicServerId& key,
+ const BoundNetLog& net_log);
+
+ bool OnResolution(const QuicServerId& server_id,
+ const AddressList& address_list);
void OnJobComplete(Job* job, int rv);
- bool HasActiveSession(const HostPortProxyPair& host_port_proxy_pair);
- bool HasActiveJob(const HostPortProxyPair& host_port_proxy_pair);
- int CreateSession(const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
+ bool HasActiveSession(const QuicServerId& server_id) const;
+ bool HasActiveJob(const QuicServerId& server_id) const;
+ int CreateSession(const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> quic_server_info,
const AddressList& address_list,
const BoundNetLog& net_log,
QuicClientSession** session);
- void ActivateSession(const HostPortProxyPair& host_port_proxy_pair,
+ void ActivateSession(const QuicServerId& key,
QuicClientSession* session);
- QuicCryptoClientConfig* GetOrCreateCryptoConfig(
- const HostPortProxyPair& host_port_proxy_pair);
+ // Initializes the cached state associated with |server_id| in
+ // |crypto_config_| with the information in |server_info|.
+ void InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info);
- // If |host_port_proxy_pair| suffix contains ".c.youtube.com" (in future we
- // could support other suffixes), then populate |crypto_config| with a
- // canonical server config data from |canonical_hostname_to_origin_map_| for
- // that suffix.
- void PopulateFromCanonicalConfig(
- const HostPortProxyPair& host_port_proxy_pair,
- QuicCryptoClientConfig* crypto_config);
+ void ProcessGoingAwaySession(QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool was_session_active);
bool require_confirmation_;
HostResolver* host_resolver_;
ClientSocketFactory* client_socket_factory_;
base::WeakPtr<HttpServerProperties> http_server_properties_;
+ CertVerifier* cert_verifier_;
+ QuicServerInfoFactory* quic_server_info_factory_;
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_;
QuicRandom* random_generator_;
scoped_ptr<QuicClock> clock_;
@@ -200,35 +246,31 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
scoped_ptr<QuicConnectionHelper> helper_;
// Contains owning pointers to all sessions that currently exist.
- SessionSet all_sessions_;
+ SessionIdMap all_sessions_;
// Contains non-owning pointers to currently active session
// (not going away session, once they're implemented).
SessionMap active_sessions_;
+ // Map from session to set of aliases that this session is known by.
SessionAliasMap session_aliases_;
+ // Map from IP address to sessions which are connected to this address.
+ IPAliasMap ip_aliases_;
- // Contains owning pointers to QuicCryptoClientConfig. QuicCryptoClientConfig
- // contains configuration and cached state about servers.
- // TODO(rtenneti): Persist all_crypto_configs_ to disk and decide when to
- // clear the data in the map.
- CryptoConfigMap all_crypto_configs_;
+ // Origins which have gone away recently.
+ AliasSet gone_away_aliases_;
- // Contains a map of servers which could share the same server config. Map
- // from a Canonical host/port (host is some postfix of host names) to an
- // actual origin, which has a plausible set of initial certificates (or at
- // least server public key).
- CanonicalHostMap canonical_hostname_to_origin_map_;
-
- // Contains list of suffixes (for exmaple ".c.youtube.com",
- // ".googlevideo.com") of cannoncial hostnames.
- std::vector<std::string> cannoncial_suffixes_;
-
- QuicConfig config_;
+ const QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
JobMap active_jobs_;
JobRequestsMap job_requests_map_;
RequestMap active_requests_;
- base::WeakPtrFactory<QuicStreamFactory> weak_factory_;
+ QuicVersionVector supported_versions_;
+
+ // Determine if we should consistently select a client UDP port. If false,
+ // then we will just let the OS select a random client port for each new
+ // connection.
+ bool enable_port_selection_;
// Each profile will (probably) have a unique port_seed_ value. This value is
// used to help seed a pseudo-random number generator (PortSuggester) so that
@@ -238,6 +280,8 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
// port requests.
uint64 port_seed_;
+ base::WeakPtrFactory<QuicStreamFactory> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(QuicStreamFactory);
};
diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc
index 03cc6f7e8bc..e88ea4b8052 100644
--- a/chromium/net/quic/quic_stream_factory_test.cc
+++ b/chromium/net/quic/quic_stream_factory_test.cc
@@ -6,20 +6,25 @@
#include "base/run_loop.h"
#include "base/strings/string_util.h"
+#include "net/base/test_data_directory.h"
#include "net/cert/cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_util.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_http_stream.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/socket_test_util.h"
+#include "net/test/cert_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
@@ -29,152 +34,169 @@ using std::vector;
namespace net {
namespace test {
+namespace {
+const char kDefaultServerHostName[] = "www.google.com";
+const int kDefaultServerPort = 443;
+} // namespace anonymous
+
class QuicStreamFactoryPeer {
public:
- static QuicCryptoClientConfig* GetOrCreateCryptoConfig(
- QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- return factory->GetOrCreateCryptoConfig(host_port_proxy_pair);
+ static QuicCryptoClientConfig* GetCryptoConfig(QuicStreamFactory* factory) {
+ return &factory->crypto_config_;
}
static bool HasActiveSession(QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- return factory->HasActiveSession(host_port_proxy_pair);
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->HasActiveSession(server_id);
}
static QuicClientSession* GetActiveSession(
QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- DCHECK(factory->HasActiveSession(host_port_proxy_pair));
- return factory->active_sessions_[host_port_proxy_pair];
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ DCHECK(factory->HasActiveSession(server_id));
+ return factory->active_sessions_[server_id];
+ }
+
+ static scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ QuicStreamFactory* factory,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ const BoundNetLog& net_log) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->CreateIfSessionExists(server_id, net_log);
}
static bool IsLiveSession(QuicStreamFactory* factory,
QuicClientSession* session) {
- for (QuicStreamFactory::SessionSet::iterator it =
+ for (QuicStreamFactory::SessionIdMap::iterator it =
factory->all_sessions_.begin();
it != factory->all_sessions_.end(); ++it) {
- if (*it == session)
+ if (it->first == session)
return true;
}
return false;
}
};
-class QuicStreamFactoryTest : public ::testing::Test {
+class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicStreamFactoryTest()
: random_generator_(0),
+ maker_(GetParam(), 0),
clock_(new MockClock()),
+ cert_verifier_(CertVerifier::CreateDefault()),
factory_(&host_resolver_, &socket_factory_,
- base::WeakPtr<HttpServerProperties>(),
- &crypto_client_stream_factory_,
- &random_generator_, clock_, kDefaultMaxPacketSize),
- host_port_proxy_pair_(HostPortPair("www.google.com", 443),
- ProxyServer::Direct()),
+ base::WeakPtr<HttpServerProperties>(), cert_verifier_.get(),
+ &crypto_client_stream_factory_, &random_generator_, clock_,
+ kDefaultMaxPacketSize, std::string(),
+ SupportedVersions(GetParam()), true, true, true),
+ host_port_pair_(kDefaultServerHostName, kDefaultServerPort),
is_https_(false),
- cert_verifier_(CertVerifier::CreateDefault()) {
+ privacy_mode_(PRIVACY_MODE_DISABLED) {
factory_.set_require_confirmation(false);
}
- scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
- QuicPacketSequenceNumber num,
- QuicStreamId stream_id) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = true;
- header.packet_sequence_number = num;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_CANCELLED);
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&rst)));
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ const HostPortPair& host_port_pair,
+ const BoundNetLog& net_log) {
+ return QuicStreamFactoryPeer::CreateIfSessionExists(
+ &factory_, host_port_pair, false, net_log_);
}
- scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
- QuicPacketSequenceNumber largest_received,
- QuicPacketSequenceNumber least_unacked) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.packet_sequence_number = 2;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kTCP;
- feedback.tcp.accumulated_number_of_lost_packets = 0;
- feedback.tcp.receive_window = 16000;
-
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(QuicFrame(&ack));
- frames.push_back(QuicFrame(&feedback));
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ int GetSourcePortForNewSession(const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, false);
}
- // Returns a newly created packet to send congestion feedback data.
- scoped_ptr<QuicEncryptedPacket> ConstructFeedbackPacket(
- QuicPacketSequenceNumber sequence_number) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.packet_sequence_number = sequence_number;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicCongestionFeedbackFrame frame;
- frame.type = kTCP;
- frame.tcp.accumulated_number_of_lost_packets = 0;
- frame.tcp.receive_window = 16000;
-
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&frame)));
+ int GetSourcePortForNewSessionAndGoAway(
+ const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, true);
}
- scoped_ptr<QuicEncryptedPacket> ConstructPacket(
- const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ int GetSourcePortForNewSessionInner(const HostPortPair& destination,
+ bool goaway_received) {
+ // Should only be called if there is no active session for this destination.
+ EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get());
+ size_t socket_count = socket_factory_.udp_client_sockets().size();
+
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(destination,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, destination, is_https_);
+
+ if (socket_count + 1 != socket_factory_.udp_client_sockets().size()) {
+ EXPECT_TRUE(false);
+ return 0;
+ }
+
+ IPEndPoint endpoint;
+ socket_factory_.
+ udp_client_sockets()[socket_count]->GetLocalAddress(&endpoint);
+ int port = endpoint.port();
+ if (goaway_received) {
+ QuicGoAwayFrame goaway(QUIC_NO_ERROR, 1, "");
+ session->OnGoAway(goaway);
+ }
+
+ factory_.OnSessionClosed(session);
+ EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ return port;
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRstPacket() {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ return maker_.MakeRstPacket(
+ 1, true, stream_id,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
}
MockHostResolver host_resolver_;
DeterministicMockClientSocketFactory socket_factory_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
MockRandom random_generator_;
+ QuicTestPacketMaker maker_;
MockClock* clock_; // Owned by factory_.
+ scoped_ptr<CertVerifier> cert_verifier_;
QuicStreamFactory factory_;
- HostPortProxyPair host_port_proxy_pair_;
+ HostPortPair host_port_pair_;
bool is_https_;
- scoped_ptr<CertVerifier> cert_verifier_;
+ PrivacyMode privacy_mode_;
BoundNetLog net_log_;
TestCompletionCallback callback_;
};
-TEST_F(QuicStreamFactoryTest, CreateIfSessionExists) {
- EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
- net_log_).get());
+INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) {
+ EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get());
}
-TEST_F(QuicStreamFactoryTest, Create) {
+TEST_P(QuicStreamFactoryTest, Create) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
@@ -183,24 +205,32 @@ TEST_F(QuicStreamFactoryTest, Create) {
socket_data.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
EXPECT_TRUE(stream.get());
// Will reset stream 3.
- stream = factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_);
+ stream = CreateIfSessionExists(host_port_pair_, net_log_);
EXPECT_TRUE(stream.get());
// TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result
// in streams on different sessions.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(OK, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(OK,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
stream = request2.ReleaseStream(); // Will reset stream 5.
stream.reset(); // Will reset stream 7.
@@ -208,36 +238,382 @@ TEST_F(QuicStreamFactoryTest, Create) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, FailedCreate) {
- MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
- DeterministicSocketData socket_data(NULL, 0, NULL, 0);
- socket_data.set_connect_data(connect);
+TEST_P(QuicStreamFactoryTest, CreateZeroRtt) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
- EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, Goaway) {
+TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
+
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ // Posts require handshake confirmation, so this will return asynchronously.
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "POST",
+ net_log_,
+ callback_.callback()));
+
+ // Confirm the handshake and verify that the stream is created.
+ crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+ QuicSession::HANDSHAKE_CONFIRMED);
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ !is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, !is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+// TODO(rch): re-enable this.
+TEST_P(QuicStreamFactoryTest, DISABLED_Pooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
socket_data2.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ factory_.OnSessionGoingAway(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ TestCompletionCallback callback3;
+ QuicStreamRequest request3(&factory_);
+ EXPECT_EQ(OK,
+ request3.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback3.callback()));
+ scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream();
+ EXPECT_TRUE(stream3.get());
+
+ EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+// TODO(rch): re-enable this.
+TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ // www.example.com
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.google.com", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org
+ // www.example.com
+ // But is not valid for mail.google.com (server2).
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, Goaway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_data2.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -246,29 +622,33 @@ TEST_F(QuicStreamFactoryTest, Goaway) {
// Mark the session as going away. Ensure that while it is still alive
// that it is no longer active.
QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
- &factory_, host_port_proxy_pair_);
+ &factory_, host_port_pair_, is_https_);
factory_.OnSessionGoingAway(session);
EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
- EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
- host_port_proxy_pair_));
- EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
- net_log_).get());
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get());
// Create a new request for the same destination and verify that a
// new session is created.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
EXPECT_TRUE(stream2.get());
EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
- host_port_proxy_pair_));
+ host_port_pair_,
+ is_https_));
EXPECT_NE(session,
QuicStreamFactoryPeer::GetActiveSession(
- &factory_, host_port_proxy_pair_));
+ &factory_, host_port_pair_, is_https_));
EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
stream2.reset();
@@ -276,13 +656,17 @@ TEST_F(QuicStreamFactoryTest, Goaway) {
EXPECT_TRUE(socket_data.at_read_eof());
EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
+TEST_P(QuicStreamFactoryTest, MaxOpenStream) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
- scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket(1, 3));
+ QuicStreamId stream_id = kClientDataStreamId1;
+ scoped_ptr<QuicEncryptedPacket> rst(
+ maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED));
MockWrite writes[] = {
MockWrite(ASYNC, rst->data(), rst->length(), 1),
};
@@ -297,8 +681,11 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
// 2 * kDefaultMaxStreamsPerConnection.
for (size_t i = 0; i < 2 * kDefaultMaxStreamsPerConnection; i++) {
QuicStreamRequest request(&factory_);
- int rv = request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
+ int rv = request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
callback_.callback());
if (i == 0) {
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -314,9 +701,13 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
}
QuicStreamRequest request(&factory_);
- EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- CompletionCallback()));
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ CompletionCallback()));
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
EXPECT_TRUE(stream);
EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream(
@@ -334,16 +725,20 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
STLDeleteElements(&streams);
}
-TEST_F(QuicStreamFactoryTest, CreateError) {
+TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
DeterministicSocketData socket_data(NULL, 0, NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
- host_resolver_.rules()->AddSimulatedFailure("www.google.com");
+ host_resolver_.rules()->AddSimulatedFailure(kDefaultServerHostName);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
@@ -351,7 +746,29 @@ TEST_F(QuicStreamFactoryTest, CreateError) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, CancelCreate) {
+TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
+ MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
+ DeterministicSocketData socket_data(NULL, 0, NULL, 0);
+ socket_data.set_connect_data(connect);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CancelCreate) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
@@ -359,9 +776,13 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
socket_factory_.AddSocketDataProvider(&socket_data);
{
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
}
socket_data.StopAfter(1);
@@ -369,7 +790,7 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
run_loop.RunUntilIdle();
scoped_ptr<QuicHttpStream> stream(
- factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_));
+ CreateIfSessionExists(host_port_pair_, net_log_));
EXPECT_TRUE(stream.get());
stream.reset();
@@ -377,11 +798,40 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
+TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) {
+ // Sequentially connect to the default host, then another host, and then the
+ // default host. Verify that the default host gets a consistent ephemeral
+ // port, that is different from the other host's connection.
+
+ std::string other_server_name = "other.google.com";
+ EXPECT_NE(kDefaultServerHostName, other_server_name);
+ HostPortPair host_port_pair2(other_server_name, kDefaultServerPort);
+
+ int original_port = GetSourcePortForNewSession(host_port_pair_);
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair2));
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, GoAwayDisablesConsistentEphemeralPort) {
+ // Get a session to the host using the port suggester.
+ int original_port =
+ GetSourcePortForNewSessionAndGoAway(host_port_pair_);
+ // Verify that the port is different after the goaway.
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair_));
+ // Since the previous session did not goaway we should see the original port.
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, CloseAllSessions) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -393,9 +843,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -413,9 +867,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -427,11 +885,16 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
+TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -443,9 +906,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -464,9 +931,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -478,11 +949,16 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnCertAdded) {
+TEST_P(QuicStreamFactoryTest, OnCertAdded) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -494,9 +970,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -515,9 +995,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -529,11 +1013,16 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
+TEST_P(QuicStreamFactoryTest, OnCACertChanged) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -545,9 +1034,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -566,9 +1059,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -580,7 +1077,7 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
+TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) {
vector<string> cannoncial_suffixes;
cannoncial_suffixes.push_back(string(".c.youtube.com"));
cannoncial_suffixes.push_back(string(".googlevideo.com"));
@@ -591,15 +1088,12 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
r1_host_name.append(cannoncial_suffixes[i]);
r2_host_name.append(cannoncial_suffixes[i]);
- HostPortProxyPair host_port_proxy_pair1(HostPortPair(r1_host_name, 80),
- ProxyServer::Direct());
-
- QuicCryptoClientConfig* crypto_config1 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair1);
- DCHECK(crypto_config1);
+ HostPortPair host_port_pair1(r1_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached1 =
- crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
+ crypto_config->LookupOrCreate(server_id1);
EXPECT_FALSE(cached1->proof_valid());
EXPECT_TRUE(cached1->source_address_token().empty());
@@ -608,20 +1102,16 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
cached1->set_source_address_token(r1_host_name);
cached1->SetProofValid();
- HostPortProxyPair host_port_proxy_pair2(HostPortPair(r2_host_name, 80),
- ProxyServer::Direct());
- QuicCryptoClientConfig* crypto_config2 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair2);
- DCHECK(crypto_config2);
+ HostPortPair host_port_pair2(r2_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached2 =
- crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
+ crypto_config->LookupOrCreate(server_id2);
EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token());
EXPECT_TRUE(cached2->proof_valid());
}
}
-TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
+TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
vector<string> cannoncial_suffixes;
cannoncial_suffixes.push_back(string(".c.youtube.com"));
cannoncial_suffixes.push_back(string(".googlevideo.com"));
@@ -632,15 +1122,12 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
r3_host_name.append(cannoncial_suffixes[i]);
r4_host_name.append(cannoncial_suffixes[i]);
- HostPortProxyPair host_port_proxy_pair1(HostPortPair(r3_host_name, 80),
- ProxyServer::Direct());
-
- QuicCryptoClientConfig* crypto_config1 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair1);
- DCHECK(crypto_config1);
+ HostPortPair host_port_pair1(r3_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached1 =
- crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
+ crypto_config->LookupOrCreate(server_id1);
EXPECT_FALSE(cached1->proof_valid());
EXPECT_TRUE(cached1->source_address_token().empty());
@@ -649,14 +1136,10 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
cached1->set_source_address_token(r3_host_name);
cached1->SetProofInvalid();
- HostPortProxyPair host_port_proxy_pair2(HostPortPair(r4_host_name, 80),
- ProxyServer::Direct());
- QuicCryptoClientConfig* crypto_config2 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair2);
- DCHECK(crypto_config2);
+ HostPortPair host_port_pair2(r4_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached2 =
- crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
+ crypto_config->LookupOrCreate(server_id2);
EXPECT_NE(cached1->source_address_token(), cached2->source_address_token());
EXPECT_TRUE(cached2->source_address_token().empty());
EXPECT_FALSE(cached2->proof_valid());
diff --git a/chromium/net/quic/quic_stream_sequencer.cc b/chromium/net/quic/quic_stream_sequencer.cc
index 02fec67f200..3a303957c2c 100644
--- a/chromium/net/quic/quic_stream_sequencer.cc
+++ b/chromium/net/quic/quic_stream_sequencer.cc
@@ -8,8 +8,10 @@
#include <limits>
#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
#include "net/quic/reliable_quic_stream.h"
+using std::make_pair;
using std::min;
using std::numeric_limits;
@@ -18,61 +20,29 @@ namespace net {
QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream)
: stream_(quic_stream),
num_bytes_consumed_(0),
- max_frame_memory_(numeric_limits<size_t>::max()),
- close_offset_(numeric_limits<QuicStreamOffset>::max()) {
-}
-
-QuicStreamSequencer::QuicStreamSequencer(size_t max_frame_memory,
- ReliableQuicStream* quic_stream)
- : stream_(quic_stream),
- num_bytes_consumed_(0),
- max_frame_memory_(max_frame_memory),
- close_offset_(numeric_limits<QuicStreamOffset>::max()) {
- if (max_frame_memory < kMaxPacketSize) {
- LOG(DFATAL) << "Setting max frame memory to " << max_frame_memory
- << ". Some frames will be impossible to handle.";
- }
+ close_offset_(numeric_limits<QuicStreamOffset>::max()),
+ blocked_(false),
+ num_bytes_buffered_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0) {
}
QuicStreamSequencer::~QuicStreamSequencer() {
}
-bool QuicStreamSequencer::WillAcceptStreamFrame(
- const QuicStreamFrame& frame) const {
- size_t data_len = frame.data.TotalBufferSize();
- DCHECK_LE(data_len, max_frame_memory_);
-
+bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
+ ++num_frames_received_;
if (IsDuplicate(frame)) {
+ ++num_duplicate_frames_received_;
+ // Silently ignore duplicates.
return true;
}
- QuicStreamOffset byte_offset = frame.offset;
- if (data_len > max_frame_memory_) {
- // We're never going to buffer this frame and we can't pass it up.
- // The stream might only consume part of it and we'd need a partial ack.
- //
- // Ideally this should never happen, as we check that
- // max_frame_memory_ > kMaxPacketSize and lower levels should reject
- // frames larger than that.
- return false;
- }
- if (byte_offset + data_len - num_bytes_consumed_ > max_frame_memory_) {
- // We can buffer this but not right now. Toss it.
- // It might be worth trying an experiment where we try best-effort buffering
- return false;
- }
- return true;
-}
-bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
- if (!WillAcceptStreamFrame(frame)) {
- // This should not happen, as WillAcceptFrame should be called before
- // OnStreamFrame. Error handling should be done by the caller.
+ if (FrameOverlapsBufferedData(frame)) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data.");
return false;
}
- if (IsDuplicate(frame)) {
- // Silently ignore duplicates.
- return true;
- }
QuicStreamOffset byte_offset = frame.offset;
size_t data_len = frame.data.TotalBufferSize();
@@ -92,7 +62,10 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
IOVector data;
data.AppendIovec(frame.data.iovec(), frame.data.Size());
- if (byte_offset == num_bytes_consumed_) {
+
+ // If the frame has arrived in-order then we can process it immediately, only
+ // buffering if the stream is unable to process it.
+ if (!blocked_ && byte_offset == num_bytes_consumed_) {
DVLOG(1) << "Processing byte offset " << byte_offset;
size_t bytes_consumed = 0;
for (size_t i = 0; i < data.Size(); ++i) {
@@ -101,6 +74,8 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
data.iovec()[i].iov_len);
}
num_bytes_consumed_ += bytes_consumed;
+ stream_->AddBytesConsumed(bytes_consumed);
+
if (MaybeCloseStream()) {
return true;
}
@@ -111,18 +86,21 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
FlushBufferedFrames();
return true; // it's safe to ack this frame.
} else {
- // Set ourselves up to buffer what's left
+ // Set ourselves up to buffer what's left.
data_len -= bytes_consumed;
data.Consume(bytes_consumed);
byte_offset += bytes_consumed;
}
}
+
+ // Buffer any remaining data to be consumed by the stream when ready.
for (size_t i = 0; i < data.Size(); ++i) {
DVLOG(1) << "Buffering stream data at offset " << byte_offset;
- frames_.insert(make_pair(byte_offset, string(
- static_cast<char*>(data.iovec()[i].iov_base),
- data.iovec()[i].iov_len)));
- byte_offset += data.iovec()[i].iov_len;
+ const iovec& iov = data.iovec()[i];
+ buffered_frames_.insert(make_pair(
+ byte_offset, string(static_cast<char*>(iov.iov_base), iov.iov_len)));
+ byte_offset += iov.iov_len;
+ num_bytes_buffered_ += iov.iov_len;
}
return true;
}
@@ -143,23 +121,26 @@ void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
}
bool QuicStreamSequencer::MaybeCloseStream() {
- if (IsClosed()) {
+ if (!blocked_ && IsClosed()) {
DVLOG(1) << "Passing up termination, as we've processed "
<< num_bytes_consumed_ << " of " << close_offset_
<< " bytes.";
// Technically it's an error if num_bytes_consumed isn't exactly
// equal, but error handling seems silly at this point.
stream_->OnFinRead();
+ buffered_frames_.clear();
+ num_bytes_buffered_ = 0;
return true;
}
return false;
}
int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) {
- FrameMap::iterator it = frames_.begin();
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
size_t index = 0;
QuicStreamOffset offset = num_bytes_consumed_;
- while (it != frames_.end() && index < iov_len) {
+ while (it != buffered_frames_.end() && index < iov_len) {
if (it->first != offset) return index;
iov[index].iov_base = static_cast<void*>(
@@ -174,14 +155,15 @@ int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) {
}
int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
- FrameMap::iterator it = frames_.begin();
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
size_t iov_index = 0;
size_t iov_offset = 0;
size_t frame_offset = 0;
size_t initial_bytes_consumed = num_bytes_consumed_;
while (iov_index < iov_len &&
- it != frames_.end() &&
+ it != buffered_frames_.end() &&
it->first == num_bytes_consumed_) {
int bytes_to_read = min(iov[iov_index].iov_len - iov_offset,
it->second.size() - frame_offset);
@@ -199,79 +181,90 @@ int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
}
if (it->second.size() == frame_offset) {
// We've copied this whole frame
- num_bytes_consumed_ += it->second.size();
- frames_.erase(it);
- it = frames_.begin();
+ RecordBytesConsumed(it->second.size());
+ buffered_frames_.erase(it);
+ it = buffered_frames_.begin();
frame_offset = 0;
}
}
// We've finished copying. If we have a partial frame, update it.
if (frame_offset != 0) {
- frames_.insert(make_pair(it->first + frame_offset,
- it->second.substr(frame_offset)));
- frames_.erase(frames_.begin());
- num_bytes_consumed_ += frame_offset;
+ buffered_frames_.insert(
+ make_pair(it->first + frame_offset, it->second.substr(frame_offset)));
+ buffered_frames_.erase(buffered_frames_.begin());
+ RecordBytesConsumed(frame_offset);
}
return num_bytes_consumed_ - initial_bytes_consumed;
}
-void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) {
- size_t end_offset = num_bytes_consumed_ + num_bytes_consumed;
- while (!frames_.empty() && end_offset != num_bytes_consumed_) {
- FrameMap::iterator it = frames_.begin();
- if (it->first != num_bytes_consumed_) {
- LOG(DFATAL) << "Invalid argument to MarkConsumed. "
- << " num_bytes_consumed_: " << num_bytes_consumed_
- << " end_offset: " << end_offset
- << " offset: " << it->first
- << " length: " << it->second.length();
- stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
- return;
- }
-
- if (it->first + it->second.length() <= end_offset) {
- num_bytes_consumed_ += it->second.length();
- // This chunk is entirely consumed.
- frames_.erase(it);
- continue;
- }
-
- // Partially consume this frame.
- size_t delta = end_offset - it->first;
- num_bytes_consumed_ += delta;
- frames_.insert(make_pair(end_offset, it->second.substr(delta)));
- frames_.erase(it);
- break;
- }
-}
-
bool QuicStreamSequencer::HasBytesToRead() const {
- FrameMap::const_iterator it = frames_.begin();
+ FrameMap::const_iterator it = buffered_frames_.begin();
- return it != frames_.end() && it->first == num_bytes_consumed_;
+ return it != buffered_frames_.end() && it->first == num_bytes_consumed_;
}
bool QuicStreamSequencer::IsClosed() const {
return num_bytes_consumed_ >= close_offset_;
}
+bool QuicStreamSequencer::FrameOverlapsBufferedData(
+ const QuicStreamFrame& frame) const {
+ if (buffered_frames_.empty()) {
+ return false;
+ }
+
+ FrameMap::const_iterator next_frame =
+ buffered_frames_.lower_bound(frame.offset);
+ // Duplicate frames should have been dropped in IsDuplicate.
+ DCHECK(next_frame == buffered_frames_.end() ||
+ next_frame->first != frame.offset);
+
+ // If there is a buffered frame with a higher starting offset, then we check
+ // to see if the new frame runs into the higher frame.
+ if (next_frame != buffered_frames_.end() &&
+ (frame.offset + frame.data.TotalBufferSize()) > next_frame->first) {
+ DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + "
+ << frame.data.TotalBufferSize() << " > " << next_frame->first;
+ return true;
+ }
+
+ // If there is a buffered frame with a lower starting offset, then we check
+ // to see if the buffered frame runs into the new frame.
+ if (next_frame != buffered_frames_.begin()) {
+ FrameMap::const_iterator preceeding_frame = --next_frame;
+ QuicStreamOffset offset = preceeding_frame->first;
+ uint64 data_length = preceeding_frame->second.length();
+ if ((offset + data_length) > frame.offset) {
+ DVLOG(1) << "Preceeding frame overlaps new frame: " << offset << " + "
+ << data_length << " > " << frame.offset;
+ return true;
+ }
+ }
+ return false;
+}
+
bool QuicStreamSequencer::IsDuplicate(const QuicStreamFrame& frame) const {
// A frame is duplicate if the frame offset is smaller than our bytes consumed
// or we have stored the frame in our map.
// TODO(pwestin): Is it possible that a new frame contain more data even if
// the offset is the same?
return frame.offset < num_bytes_consumed_ ||
- frames_.find(frame.offset) != frames_.end();
+ buffered_frames_.find(frame.offset) != buffered_frames_.end();
+}
+
+void QuicStreamSequencer::SetBlockedUntilFlush() {
+ blocked_ = true;
}
void QuicStreamSequencer::FlushBufferedFrames() {
- FrameMap::iterator it = frames_.find(num_bytes_consumed_);
- while (it != frames_.end()) {
+ blocked_ = false;
+ FrameMap::iterator it = buffered_frames_.find(num_bytes_consumed_);
+ while (it != buffered_frames_.end()) {
DVLOG(1) << "Flushing buffered packet at offset " << it->first;
string* data = &it->second;
size_t bytes_consumed = stream_->ProcessRawData(data->c_str(),
data->size());
- num_bytes_consumed_ += bytes_consumed;
+ RecordBytesConsumed(bytes_consumed);
if (MaybeCloseStream()) {
return;
}
@@ -279,15 +272,23 @@ void QuicStreamSequencer::FlushBufferedFrames() {
stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); // Programming error
return;
} else if (bytes_consumed == data->size()) {
- frames_.erase(it);
- it = frames_.find(num_bytes_consumed_);
+ buffered_frames_.erase(it);
+ it = buffered_frames_.find(num_bytes_consumed_);
} else {
string new_data = it->second.substr(bytes_consumed);
- frames_.erase(it);
- frames_.insert(make_pair(num_bytes_consumed_, new_data));
+ buffered_frames_.erase(it);
+ buffered_frames_.insert(make_pair(num_bytes_consumed_, new_data));
return;
}
}
+ MaybeCloseStream();
+}
+
+void QuicStreamSequencer::RecordBytesConsumed(size_t bytes_consumed) {
+ num_bytes_consumed_ += bytes_consumed;
+ num_bytes_buffered_ -= bytes_consumed;
+
+ stream_->AddBytesConsumed(bytes_consumed);
}
} // namespace net
diff --git a/chromium/net/quic/quic_stream_sequencer.h b/chromium/net/quic/quic_stream_sequencer.h
index e2601246059..a4580a9ca3f 100644
--- a/chromium/net/quic/quic_stream_sequencer.h
+++ b/chromium/net/quic/quic_stream_sequencer.h
@@ -8,7 +8,6 @@
#include <map>
#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
#include "net/base/iovec.h"
#include "net/quic/quic_protocol.h"
@@ -30,21 +29,13 @@ class ReliableQuicStream;
class NET_EXPORT_PRIVATE QuicStreamSequencer {
public:
explicit QuicStreamSequencer(ReliableQuicStream* quic_stream);
- QuicStreamSequencer(size_t max_frame_memory,
- ReliableQuicStream* quic_stream);
-
virtual ~QuicStreamSequencer();
- // Returns the expected value of OnStreamFrame for this frame.
- bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const;
-
// If the frame is the next one we need in order to process in-order data,
// ProcessData will be immediately called on the stream until all buffered
// data is processed or the stream fails to consume data. Any unconsumed
- // data will be buffered.
- //
- // If the frame is not the next in line, it will either be buffered, and
- // this will return true, or it will be rejected and this will return false.
+ // data will be buffered. If the frame is not the next in line, it will be
+ // buffered.
bool OnStreamFrame(const QuicStreamFrame& frame);
// Once data is buffered, it's up to the stream to read it when the stream
@@ -58,10 +49,6 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// bytes read. Any buffered data no longer in use will be released.
int Readv(const struct iovec* iov, size_t iov_len);
- // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions|
- // to do zero-copy reads.
- void MarkConsumed(size_t num_bytes);
-
// Returns true if the sequncer has bytes available for reading.
bool HasBytesToRead() const;
@@ -71,28 +58,74 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// Returns true if the sequencer has received this frame before.
bool IsDuplicate(const QuicStreamFrame& frame) const;
+ // Returns true if |frame| contains data which overlaps buffered data
+ // (indicating an invalid stream frame has been received).
+ bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const;
+
// Calls |ProcessRawData| on |stream_| for each buffered frame that may
// be processed.
void FlushBufferedFrames();
+ // Blocks processing of frames until |FlushBufferedFrames| is called.
+ void SetBlockedUntilFlush();
+
+ size_t num_bytes_buffered() const { return num_bytes_buffered_; }
+ QuicStreamOffset num_bytes_consumed() const { return num_bytes_consumed_; }
+
+ int num_frames_received() const { return num_frames_received_; }
+
+ int num_duplicate_frames_received() const {
+ return num_duplicate_frames_received_;
+ }
+
private:
friend class test::QuicStreamSequencerPeer;
- // TODO(alyssar) use something better than strings.
- typedef map<QuicStreamOffset, string> FrameMap;
-
// Wait until we've seen 'offset' bytes, and then terminate the stream.
void CloseStreamAtOffset(QuicStreamOffset offset);
+ // If we've received a FIN and have processed all remaining data, then inform
+ // the stream of FIN, and clear buffers.
bool MaybeCloseStream();
- ReliableQuicStream* stream_; // The stream which owns this sequencer.
- QuicStreamOffset num_bytes_consumed_; // The last data consumed by the stream
- FrameMap frames_; // sequence number -> frame
- size_t max_frame_memory_; // the maximum memory the sequencer can buffer.
+ // Called whenever bytes are consumed by the stream. Updates
+ // num_bytes_consumed_ and num_bytes_buffered_.
+ void RecordBytesConsumed(size_t bytes_consumed);
+
+ // The stream which owns this sequencer.
+ ReliableQuicStream* stream_;
+
+ // The last data consumed by the stream.
+ QuicStreamOffset num_bytes_consumed_;
+
+ // TODO(alyssar) use something better than strings.
+ // TODO(rjshade): In future we may support retransmission of partial stream
+ // frames, in which case we will have to allow receipt of overlapping frames.
+ // Maybe write new frames into a ring buffer, and keep track of consumed
+ // bytes, and gaps.
+ typedef map<QuicStreamOffset, string> FrameMap;
+
+ // Stores buffered frames (maps from sequence number -> frame data as string).
+ FrameMap buffered_frames_;
+
// The offset, if any, we got a stream termination for. When this many bytes
// have been processed, the sequencer will be closed.
QuicStreamOffset close_offset_;
+
+ // If true, the sequencer is blocked from passing data to the stream and will
+ // buffer all new incoming data until FlushBufferedFrames is called.
+ bool blocked_;
+
+ // Tracks how many bytes the sequencer has buffered.
+ size_t num_bytes_buffered_;
+
+ // Count of the number of frames received.
+ int num_frames_received_;
+
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencer);
};
} // namespace net
diff --git a/chromium/net/quic/quic_stream_sequencer_test.cc b/chromium/net/quic/quic_stream_sequencer_test.cc
index f898e6611f7..0d9e82bf443 100644
--- a/chromium/net/quic/quic_stream_sequencer_test.cc
+++ b/chromium/net/quic/quic_stream_sequencer_test.cc
@@ -7,14 +7,19 @@
#include <utility>
#include <vector>
+#include "base/logging.h"
#include "base/rand_util.h"
#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
+using std::map;
using std::min;
using std::pair;
using std::vector;
@@ -27,42 +32,6 @@ using testing::StrEq;
namespace net {
namespace test {
-class QuicStreamSequencerPeer : public QuicStreamSequencer {
- public:
- explicit QuicStreamSequencerPeer(ReliableQuicStream* stream)
- : QuicStreamSequencer(stream) {
- }
-
- QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream)
- : QuicStreamSequencer(max_mem, stream) {
- }
-
- virtual bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
- QuicStreamFrame frame;
- frame.stream_id = 1;
- frame.offset = byte_offset;
- frame.data.Append(const_cast<char*>(data), strlen(data));
- frame.fin = true;
- return OnStreamFrame(frame);
- }
-
- virtual bool OnFrame(QuicStreamOffset byte_offset, const char* data) {
- QuicStreamFrame frame;
- frame.stream_id = 1;
- frame.offset = byte_offset;
- frame.data.Append(const_cast<char*>(data), strlen(data));
- frame.fin = false;
- return OnStreamFrame(frame);
- }
-
- void SetMemoryLimit(size_t limit) {
- max_frame_memory_ = limit;
- }
- uint64 num_bytes_consumed() const { return num_bytes_consumed_; }
- const FrameMap* frames() const { return &frames_; }
- QuicStreamOffset close_offset() const { return close_offset_; }
-};
-
class MockStream : public ReliableQuicStream {
public:
MockStream(QuicSession* session, QuicStreamId id)
@@ -75,9 +44,12 @@ class MockStream : public ReliableQuicStream {
const string& details));
MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
MOCK_METHOD0(OnCanWrite, void());
- virtual QuicPriority EffectivePriority() const {
+ virtual QuicPriority EffectivePriority() const OVERRIDE {
return QuicUtils::HighestPriority();
}
+ virtual bool IsFlowControlEnabled() const {
+ return true;
+ }
};
namespace {
@@ -91,7 +63,9 @@ class QuicStreamSequencerTest : public ::testing::Test {
: connection_(new MockConnection(false)),
session_(connection_),
stream_(&session_, 1),
- sequencer_(new QuicStreamSequencerPeer(&stream_)) {
+ sequencer_(new QuicStreamSequencer(&stream_)),
+ buffered_frames_(
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) {
}
bool VerifyReadableRegions(const char** expected, size_t num_expected) {
@@ -132,111 +106,145 @@ class QuicStreamSequencerTest : public ::testing::Test {
return true;
}
+ bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = true;
+ return sequencer_->OnStreamFrame(frame);
+ }
+
+ bool OnFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = false;
+ return sequencer_->OnStreamFrame(frame);
+ }
+
MockConnection* connection_;
MockSession session_;
testing::StrictMock<MockStream> stream_;
- scoped_ptr<QuicStreamSequencerPeer> sequencer_;
+ scoped_ptr<QuicStreamSequencer> sequencer_;
+ map<QuicStreamOffset, string>* buffered_frames_;
};
TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3))
- .WillOnce(Return(3));
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
// Ignore this - it matches a past sequence number and we should not see it
// again.
- EXPECT_TRUE(sequencer_->OnFrame(0, "def"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) {
- // TODO(rch): enable when chromium supports EXPECT_DFATAL.
- /*
- EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)),
- "Setting max frame memory to 2. "
- "Some frames will be impossible to handle.");
-
- EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc"), "");
- */
-}
-
-TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) {
- sequencer_->SetMemoryLimit(3);
-
- EXPECT_FALSE(sequencer_->OnFrame(3, "abc"));
+ EXPECT_TRUE(OnFrame(0, "def"));
+ EXPECT_EQ(0u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
// Ignore this - it matches a buffered frame.
// Right now there's no checking that the payload is consistent.
- EXPECT_TRUE(sequencer_->OnFrame(0, "def"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "def"));
+ EXPECT_EQ(1u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ EXPECT_TRUE(OnFinFrame(3, "def"));
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ EXPECT_TRUE(OnFinFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, EmptyFrame) {
EXPECT_CALL(stream_,
CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _));
- EXPECT_FALSE(sequencer_->OnFrame(0, ""));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_FALSE(OnFrame(0, ""));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, EmptyFinFrame) {
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFinFrame(0, ""));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFinFrame(0, ""));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(2u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("c", sequencer_->frames()->find(2)->second);
+ EXPECT_EQ("c", buffered_frames_->find(2)->second);
}
TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("abc", sequencer_->frames()->find(0)->second);
+ EXPECT_EQ("abc", buffered_frames_->find(0)->second);
}
TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
- EXPECT_TRUE(sequencer_->OnFrame(3, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(3, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("abc", sequencer_->frames()->find(3)->second);
+ EXPECT_EQ("abc", buffered_frames_->find(3)->second);
}
TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
// Buffer the first
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(6, "ghi"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(3u, sequencer_->num_bytes_buffered());
// Buffer the second
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_EQ(2u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(3, "def"));
+ EXPECT_EQ(2u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(6u, sequencer_->num_bytes_buffered());
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
@@ -244,190 +252,11 @@ TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3));
// Ack right away
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
EXPECT_EQ(9u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(0u, sequencer_->num_bytes_buffered());
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) {
- sequencer_->SetMemoryLimit(9);
-
- // Too far to buffer.
- EXPECT_FALSE(sequencer_->OnFrame(9, "jkl"));
-
- // We can afford to buffer this.
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
- EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
-
- // Ack right away
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
-
- // We should be willing to buffer this now.
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
-
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("jkl"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_EQ(12u, sequencer_->num_bytes_consumed());
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithReadv) {
- sequencer_->SetMemoryLimit(9);
- char buffer[20];
- iovec iov[2];
- iov[0].iov_base = &buffer[0];
- iov[0].iov_len = 1;
- iov[1].iov_base = &buffer[1];
- iov[1].iov_len = 2;
-
- // Push abc - process.
- // Push jkl - buffer (not next data)
- // Push def - don't process.
- // Push mno - drop (too far out)
- // Push ghi - buffer (def not processed)
- // Read 2.
- // Push mno - buffer (not all read)
- // Read all
- // Push pqr - process
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_FALSE(sequencer_->OnFrame(12, "mno"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Read 3 bytes.
- EXPECT_EQ(3, sequencer_->Readv(iov, 2));
- EXPECT_EQ(0, strncmp(buffer, "def", 3));
-
- // Now we have space to bufer this.
- EXPECT_TRUE(sequencer_->OnFrame(12, "mno"));
-
- // Read the remaining 9 bytes.
- iov[1].iov_len = 19;
- EXPECT_EQ(9, sequencer_->Readv(iov, 2));
- EXPECT_EQ(0, strncmp(buffer, "ghijklmno", 9));
-
- EXPECT_TRUE(sequencer_->OnFrame(15, "pqr"));
-}
-
-// Same as above, just using a different method for reading.
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithGetReadableRegion) {
- sequencer_->SetMemoryLimit(9);
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_FALSE(sequencer_->OnFrame(12, "mno"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Read 3 bytes.
- const char* expected[] = {"def", "ghi", "jkl"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
- char buffer[9];
- iovec read_iov = { &buffer[0], 3 };
- ASSERT_EQ(3, sequencer_->Readv(&read_iov, 1));
-
- // Now we have space to bufer this.
- EXPECT_TRUE(sequencer_->OnFrame(12, "mno"));
-
- // Read the remaining 9 bytes.
- const char* expected2[] = {"ghi", "jkl", "mno"};
- ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2)));
- read_iov.iov_len = 9;
- ASSERT_EQ(9, sequencer_->Readv(&read_iov, 1));
-
- EXPECT_TRUE(sequencer_->OnFrame(15, "pqr"));
-}
-
-// Same as above, just using a different method for reading.
-TEST_F(QuicStreamSequencerTest, MarkConsumed) {
- sequencer_->SetMemoryLimit(9);
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Peek into the data.
- const char* expected[] = {"abc", "def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- // Consume 1 byte.
- sequencer_->MarkConsumed(1);
- // Verify data.
- const char* expected2[] = {"bc", "def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2)));
-
- // Consume 2 bytes.
- sequencer_->MarkConsumed(2);
- // Verify data.
- const char* expected3[] = {"def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected3, arraysize(expected3)));
-
- // Consume 5 bytes.
- sequencer_->MarkConsumed(5);
- // Verify data.
- const char* expected4[] = {"i"};
- ASSERT_TRUE(VerifyReadableRegions(expected4, arraysize(expected4)));
-}
-
-TEST_F(QuicStreamSequencerTest, MarkConsumedError) {
- // TODO(rch): enable when chromium supports EXPECT_DFATAL.
- /*
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jklmnopqrstuvwxyz"));
-
- // Peek into the data. Only the first chunk should be readable
- // because of the missing data.
- const char* expected[] = {"abc"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- // Now, attempt to mark consumed more data than was readable
- // and expect the stream to be closed.
- EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM));
- EXPECT_DFATAL(sequencer_->MarkConsumed(4),
- "Invalid argument to MarkConsumed. num_bytes_consumed_: 3 "
- "end_offset: 4 offset: 9 length: 17");
- */
-}
-
-TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) {
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- // Missing packet: 6, ghi
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
-
- const char* expected[] = {"abc", "def"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- sequencer_->MarkConsumed(6);
+ EXPECT_EQ(0u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
@@ -435,64 +264,64 @@ TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFinFrame(0, "abc"));
+ EXPECT_TRUE(OnFinFrame(0, "abc"));
- EXPECT_EQ(3u, sequencer_->close_offset());
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
}
TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) {
- sequencer_->OnFinFrame(6, "");
- EXPECT_EQ(6u, sequencer_->close_offset());
+ OnFinFrame(6, "");
+ EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(3, "def"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
}
TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) {
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
}
TEST_F(QuicStreamSequencerTest, TerminateWithReadv) {
char buffer[3];
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_FALSE(sequencer_->IsClosed());
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
- iovec iov = { &buffer[0], 3 };
+ iovec iov = {&buffer[0], 3};
int bytes_read = sequencer_->Readv(&iov, 1);
EXPECT_EQ(3, bytes_read);
EXPECT_TRUE(sequencer_->IsClosed());
}
TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- sequencer_->OnFinFrame(5, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(5, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- sequencer_->OnFinFrame(1, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(1, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
}
class QuicSequencerRandomTest : public QuicStreamSequencerTest {
@@ -544,38 +373,71 @@ TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
while (!list_.empty()) {
int index = OneToN(list_.size()) - 1;
- LOG(ERROR) << "Sending index " << index << " "
- << list_[index].second.data();
- EXPECT_TRUE(sequencer_->OnFrame(list_[index].first,
- list_[index].second.data()));
+ LOG(ERROR) << "Sending index " << index << " " << list_[index].second;
+ EXPECT_TRUE(OnFrame(list_[index].first, list_[index].second.data()));
list_.erase(list_.begin() + index);
}
}
-// All frames are processed as soon as we have sequential data.
-// Buffering, so some frames are rejected.
-TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) {
- sequencer_->SetMemoryLimit(26);
+TEST_F(QuicStreamSequencerTest, FrameOverlapsBufferedData) {
+ // Ensure that FrameOverlapsBufferedData returns appropriate responses when
+ // there is existing data buffered.
+
+ map<QuicStreamOffset, string>* buffered_frames =
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get());
+
+ const int kBufferedOffset = 10;
+ const int kBufferedDataLength = 3;
+ const int kNewDataLength = 3;
+ IOVector data = MakeIOVector(string(kNewDataLength, '.'));
+
+ // No overlap if no buffered frames.
+ EXPECT_TRUE(buffered_frames_->empty());
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+
+ // Add a buffered frame.
+ buffered_frames->insert(
+ make_pair(kBufferedOffset, string(kBufferedDataLength, '.')));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // preceeding buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength + 1, data)));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // inside existing buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset + 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength - 1, data)));
+
+ // New byte range entirely outside of buffered frames, start offset preceeding
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength, data)));
+
+ // New byte range entirely outside of buffered frames, start offset later than
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength, data)));
+}
- InSequence s;
- for (size_t i = 0; i < list_.size(); ++i) {
- string* data = &list_[i].second;
- EXPECT_CALL(stream_, ProcessRawData(StrEq(*data), data->size()))
- .WillOnce(Return(data->size()));
- }
+TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) {
+ // The peer should never send us non-identical stream frames which contain
+ // overlapping byte ranges - if they do, we close the connection.
- while (!list_.empty()) {
- int index = OneToN(list_.size()) - 1;
- LOG(ERROR) << "Sending index " << index << " "
- << list_[index].second.data();
- bool acked = sequencer_->OnFrame(list_[index].first,
- list_[index].second.data());
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 1, MakeIOVector("hello"));
+ sequencer_->OnStreamFrame(frame1);
- if (acked) {
- list_.erase(list_.begin() + index);
- }
- }
+ QuicStreamFrame frame2(kClientDataStreamId1, false, 2, MakeIOVector("hello"));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2));
+ EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _))
+ .Times(1);
+ sequencer_->OnStreamFrame(frame2);
}
} // namespace
diff --git a/chromium/net/quic/quic_time.cc b/chromium/net/quic/quic_time.cc
index a56775663e4..d467980c43d 100644
--- a/chromium/net/quic/quic_time.cc
+++ b/chromium/net/quic/quic_time.cc
@@ -62,6 +62,20 @@ QuicTime::Delta QuicTime::Delta::Subtract(const Delta& delta) const {
delta.ToMicroseconds());
}
+QuicTime::Delta QuicTime::Delta::Multiply(int i) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * i);
+}
+
+QuicTime::Delta QuicTime::Delta::Multiply(double d) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * d);
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::Max(QuicTime::Delta delta1,
+ QuicTime::Delta delta2) {
+ return delta1 < delta2 ? delta2 : delta1;
+}
+
bool QuicTime::Delta::IsZero() const {
return delta_.InMicroseconds() == 0;
}
@@ -75,6 +89,11 @@ QuicTime QuicTime::Zero() {
return QuicTime(base::TimeTicks());
}
+// static
+QuicTime QuicTime::Max(QuicTime time1, QuicTime time2) {
+ return time1 > time2 ? time1 : time2;
+}
+
QuicTime::QuicTime(base::TimeTicks ticks)
: ticks_(ticks) {
}
diff --git a/chromium/net/quic/quic_time.h b/chromium/net/quic/quic_time.h
index cdb7f83e81e..20cc3088f72 100644
--- a/chromium/net/quic/quic_time.h
+++ b/chromium/net/quic/quic_time.h
@@ -58,6 +58,12 @@ class NET_EXPORT_PRIVATE QuicTime {
Delta Subtract(const Delta& delta) const;
+ Delta Multiply(int i) const;
+ Delta Multiply(double d) const;
+
+ // Returns the later delta of time1 and time2.
+ static Delta Max(Delta delta1, Delta delta2);
+
bool IsZero() const;
bool IsInfinite() const;
@@ -75,6 +81,9 @@ class NET_EXPORT_PRIVATE QuicTime {
// will return false for these times.
static QuicTime Zero();
+ // Returns the later time of time1 and time2.
+ static QuicTime Max(QuicTime time1, QuicTime time2);
+
// Produce the internal value to be used when logging. This value
// represents the number of microseconds since some epoch. It may
// be the UNIX epoch on some platforms. On others, it may
diff --git a/chromium/net/quic/quic_time_test.cc b/chromium/net/quic/quic_time_test.cc
index 18b1de4f3d3..a2e355050c7 100644
--- a/chromium/net/quic/quic_time_test.cc
+++ b/chromium/net/quic/quic_time_test.cc
@@ -48,6 +48,21 @@ TEST(QuicTimeDeltaTest, Subtract) {
QuicTime::Delta::FromMilliseconds(1)));
}
+TEST(QuicTimeDeltaTest, Multiply) {
+ int i = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(i));
+ double d = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(d));
+}
+
+TEST(QuicTimeDeltaTest, Max) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ QuicTime::Delta::Max(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(2000)));
+}
+
TEST(QuicTimeDeltaTest, NotEqual) {
EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) !=
QuicTime::Delta::FromSeconds(1));
@@ -95,6 +110,15 @@ TEST_F(QuicTimeTest, SubtractDelta) {
time.Subtract(QuicTime::Delta::FromMilliseconds(1)));
}
+TEST_F(QuicTimeTest, Max) {
+ QuicTime time_1 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(1));
+ QuicTime time_2 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+
+ EXPECT_EQ(time_2, QuicTime::Max(time_1, time_2));
+}
+
TEST_F(QuicTimeTest, MockClock) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
diff --git a/chromium/net/quic/quic_time_wait_list_manager.cc b/chromium/net/quic/quic_time_wait_list_manager.cc
new file mode 100644
index 00000000000..d1f3419e77d
--- /dev/null
+++ b/chromium/net/quic/quic_time_wait_list_manager.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 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_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::make_pair;
+
+namespace net {
+
+namespace {
+
+// Time period for which the connection_id should live in time wait state..
+const int kTimeWaitSeconds = 5;
+
+} // namespace
+
+// A very simple alarm that just informs the QuicTimeWaitListManager to clean
+// up old connection_ids. This alarm should be unregistered and deleted before
+// the QuicTimeWaitListManager is deleted.
+class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit ConnectionIdCleanUpAlarm(
+ QuicTimeWaitListManager* time_wait_list_manager)
+ : time_wait_list_manager_(time_wait_list_manager) {}
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ time_wait_list_manager_->CleanUpOldConnectionIds();
+ // Let the time wait manager register the alarm at appropriate time.
+ return QuicTime::Zero();
+ }
+
+ private:
+ // Not owned.
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+// This class stores pending public reset packets to be sent to clients.
+// server_address - server address on which a packet what was received for
+// a connection_id in time wait state.
+// client_address - address of the client that sent that packet. Needed to send
+// the public reset packet back to the client.
+// packet - the pending public reset packet that is to be sent to the client.
+// created instance takes the ownership of this packet.
+class QuicTimeWaitListManager::QueuedPacket {
+ public:
+ QueuedPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicEncryptedPacket* packet)
+ : server_address_(server_address),
+ client_address_(client_address),
+ packet_(packet) {}
+
+ const IPEndPoint& server_address() const { return server_address_; }
+ const IPEndPoint& client_address() const { return client_address_; }
+ QuicEncryptedPacket* packet() { return packet_.get(); }
+
+ private:
+ const IPEndPoint server_address_;
+ const IPEndPoint client_address_;
+ scoped_ptr<QuicEncryptedPacket> packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
+};
+
+QuicTimeWaitListManager::QuicTimeWaitListManager(
+ QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions)
+ : helper_(helper),
+ kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
+ connection_id_clean_up_alarm_(
+ helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
+ writer_(writer),
+ visitor_(visitor) {
+ SetConnectionIdCleanUpAlarm();
+}
+
+QuicTimeWaitListManager::~QuicTimeWaitListManager() {
+ connection_id_clean_up_alarm_->Cancel();
+ STLDeleteElements(&pending_packets_queue_);
+ for (ConnectionIdMap::iterator it = connection_id_map_.begin();
+ it != connection_id_map_.end();
+ ++it) {
+ delete it->second.close_packet;
+ }
+}
+
+void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet) {
+ DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
+ int num_packets = 0;
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
+ num_packets = it->second.num_packets;
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ ConnectionIdData data(num_packets,
+ version,
+ helper_->GetClock()->ApproximateNow(),
+ close_packet);
+ connection_id_map_.insert(make_pair(connection_id, data));
+}
+
+bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
+ QuicConnectionId connection_id) const {
+ return ContainsKey(connection_id_map_, connection_id);
+}
+
+QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
+ QuicConnectionId connection_id) {
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ return (it->second).version;
+}
+
+void QuicTimeWaitListManager::OnCanWrite() {
+ while (!pending_packets_queue_.empty()) {
+ QueuedPacket* queued_packet = pending_packets_queue_.front();
+ if (!WriteToWire(queued_packet)) {
+ return;
+ }
+ pending_packets_queue_.pop_front();
+ delete queued_packet;
+ }
+}
+
+void QuicTimeWaitListManager::ProcessPacket(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& /*packet*/) {
+ DCHECK(IsConnectionIdInTimeWait(connection_id));
+ DVLOG(1) << "Processing " << connection_id << " in time wait state.";
+ // TODO(satyamshekhar): Think about handling packets from different client
+ // addresses.
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ // Increment the received packet count.
+ ++((it->second).num_packets);
+ if (!ShouldSendResponse((it->second).num_packets)) {
+ return;
+ }
+ if (it->second.close_packet) {
+ QueuedPacket* queued_packet =
+ new QueuedPacket(server_address,
+ client_address,
+ it->second.close_packet->Clone());
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+ } else {
+ SendPublicReset(server_address,
+ client_address,
+ connection_id,
+ sequence_number);
+ }
+}
+
+// Returns true if the number of packets received for this connection_id is a
+// power of 2 to throttle the number of public reset packets we send to a
+// client.
+bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
+ return (received_packet_count & (received_packet_count - 1)) == 0;
+}
+
+void QuicTimeWaitListManager::SendPublicReset(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number) {
+ QuicPublicResetPacket packet;
+ packet.public_header.connection_id = connection_id;
+ packet.public_header.reset_flag = true;
+ packet.public_header.version_flag = false;
+ packet.rejected_sequence_number = rejected_sequence_number;
+ // TODO(satyamshekhar): generate a valid nonce for this connection_id.
+ packet.nonce_proof = 1010101;
+ packet.client_address = client_address;
+ QueuedPacket* queued_packet = new QueuedPacket(
+ server_address,
+ client_address,
+ BuildPublicReset(packet));
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+}
+
+QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
+ const QuicPublicResetPacket& packet) {
+ return QuicFramer::BuildPublicResetPacket(packet);
+}
+
+// Either sends the packet and deletes it or makes pending queue the
+// owner of the packet.
+void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
+ if (WriteToWire(packet)) {
+ delete packet;
+ } else {
+ // pending_packets_queue takes the ownership of the queued packet.
+ pending_packets_queue_.push_back(packet);
+ }
+}
+
+bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked(this);
+ return false;
+ }
+ WriteResult result = writer_->WritePacket(
+ queued_packet->packet()->data(),
+ queued_packet->packet()->length(),
+ queued_packet->server_address().address(),
+ queued_packet->client_address());
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ // If blocked and unbuffered, return false to retry sending.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked(this);
+ return writer_->IsWriteBlockedDataBuffered();
+ } else if (result.status == WRITE_STATUS_ERROR) {
+ LOG(WARNING) << "Received unknown error while sending reset packet to "
+ << queued_packet->client_address().ToString() << ": "
+ << strerror(result.error_code);
+ }
+ return true;
+}
+
+void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
+ connection_id_clean_up_alarm_->Cancel();
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ QuicTime next_alarm_time = now;
+ if (!connection_id_map_.empty()) {
+ QuicTime oldest_connection_id =
+ connection_id_map_.begin()->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
+ } else {
+ LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
+ }
+ } else {
+ // No connection_ids added so none will expire before kTimeWaitPeriod_.
+ next_alarm_time = now.Add(kTimeWaitPeriod_);
+ }
+
+ connection_id_clean_up_alarm_->Set(next_alarm_time);
+}
+
+void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ while (!connection_id_map_.empty()) {
+ ConnectionIdMap::iterator it = connection_id_map_.begin();
+ QuicTime oldest_connection_id = it->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ break;
+ }
+ // This connection_id has lived its age, retire it now.
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ SetConnectionIdCleanUpAlarm();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_time_wait_list_manager.h b/chromium/net/quic/quic_time_wait_list_manager.h
new file mode 100644
index 00000000000..4a5fd42b0a3
--- /dev/null
+++ b/chromium/net/quic/quic_time_wait_list_manager.h
@@ -0,0 +1,174 @@
+// Copyright 2014 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.
+//
+// Handles packets for connection_ids in time wait state by discarding the
+// packet and sending the clients a public reset packet with exponential
+// backoff.
+
+#ifndef NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_piece.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class ConnectionIdCleanUpAlarm;
+class QuicServerSessionVisitor;
+
+namespace test {
+class QuicTimeWaitListManagerPeer;
+} // namespace test
+
+// Maintains a list of all connection_ids that have been recently closed. A
+// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// for connection_ids in this state are handed over to the
+// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a
+// public reset packet, a copy of the previously sent connection close packet,
+// or nothing to the client which sent a packet with the connection_id in time
+// wait state. After the connection_id expires its time wait period, a new
+// connection/session will be created if a packet is received for this
+// connection_id.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface {
+ public:
+ // writer - the entity that writes to the socket. (Owned by the dispatcher)
+ // visitor - the entity that manages blocked writers. (The dispatcher)
+ // helper - used to run clean up alarms. (Owned by the owner of the server)
+ QuicTimeWaitListManager(QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions);
+ virtual ~QuicTimeWaitListManager();
+
+ // Adds the given connection_id to time wait state for kTimeWaitPeriod.
+ // Henceforth, any packet bearing this connection_id should not be processed
+ // while the connection_id remains in this list. If a non-NULL |close_packet|
+ // is provided, it is sent again when packets are received for added
+ // connection_ids. If NULL, a public reset packet is sent with the specified
+ // |version|. DCHECKs that connection_id is not already on the list.
+ void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet); // Owned.
+
+ // Returns true if the connection_id is in time wait state, false otherwise.
+ // Packets received for this connection_id should not lead to creation of new
+ // QuicSessions.
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const;
+
+ // Called when a packet is received for a connection_id that is in time wait
+ // state. Sends a public reset packet to the client which sent this
+ // connection_id. Sending of the public reset packet is throttled by using
+ // exponential back off. DCHECKs for the connection_id to be in time wait
+ // state. virtual to override in tests.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& packet);
+
+ // Called by the dispatcher when the underlying socket becomes writable again,
+ // since we might need to send pending public reset packets which we didn't
+ // send because the underlying socket was write blocked.
+ virtual void OnCanWrite() OVERRIDE;
+
+ // Used to delete connection_id entries that have outlived their time wait
+ // period.
+ void CleanUpOldConnectionIds();
+
+ // Given a ConnectionId that exists in the time wait list, returns the
+ // QuicVersion associated with it.
+ QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id);
+
+ protected:
+ virtual QuicEncryptedPacket* BuildPublicReset(
+ const QuicPublicResetPacket& packet);
+
+ private:
+ friend class test::QuicTimeWaitListManagerPeer;
+
+ // Internal structure to store pending public reset packets.
+ class QueuedPacket;
+
+ // Decides if a packet should be sent for this connection_id based on the
+ // number of received packets.
+ bool ShouldSendResponse(int received_packet_count);
+
+ // Creates a public reset packet and sends it or queues it to be sent later.
+ void SendPublicReset(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number);
+
+ // Either sends the packet and deletes it or makes pending_packets_queue_ the
+ // owner of the packet.
+ void SendOrQueuePacket(QueuedPacket* packet);
+
+ // Sends the packet out. Returns true if the packet was successfully consumed.
+ // If the writer got blocked and did not buffer the packet, we'll need to keep
+ // the packet and retry sending. In case of all other errors we drop the
+ // packet.
+ bool WriteToWire(QueuedPacket* packet);
+
+ // Register the alarm to wake up at appropriate time.
+ void SetConnectionIdCleanUpAlarm();
+
+ // A map from a recently closed connection_id to the number of packets
+ // received after the termination of the connection bound to the
+ // connection_id.
+ struct ConnectionIdData {
+ ConnectionIdData(int num_packets_,
+ QuicVersion version_,
+ QuicTime time_added_,
+ QuicEncryptedPacket* close_packet)
+ : num_packets(num_packets_),
+ version(version_),
+ time_added(time_added_),
+ close_packet(close_packet) {}
+ int num_packets;
+ QuicVersion version;
+ QuicTime time_added;
+ QuicEncryptedPacket* close_packet;
+ };
+
+ // linked_hash_map allows lookup by ConnectionId and traversal in add order.
+ typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap;
+ ConnectionIdMap connection_id_map_;
+
+ // Pending public reset packets that need to be sent out to the client
+ // when we are given a chance to write by the dispatcher.
+ std::deque<QueuedPacket*> pending_packets_queue_;
+
+ // Used to schedule alarms to delete old connection_ids which have been in the
+ // list for too long.
+ QuicConnectionHelperInterface* helper_;
+
+ // Time period for which connection_ids should remain in time wait state.
+ const QuicTime::Delta kTimeWaitPeriod_;
+
+ // Alarm registered with the connection helper to clean up connection_ids that
+ // have
+ // out lived their duration in time wait state.
+ scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_;
+
+ // Interface that writes given buffer to the socket.
+ QuicPacketWriter* writer_;
+
+ // Interface that manages blocked writers.
+ QuicServerSessionVisitor* visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/chromium/net/quic/quic_types.cc b/chromium/net/quic/quic_types.cc
new file mode 100644
index 00000000000..cdfb36dbccb
--- /dev/null
+++ b/chromium/net/quic/quic_types.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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_types.h"
+
+using std::ostream;
+
+namespace net {
+
+QuicConsumedData::QuicConsumedData(size_t bytes_consumed,
+ bool fin_consumed)
+ : bytes_consumed(bytes_consumed),
+ fin_consumed(fin_consumed) {
+}
+
+ostream& operator<<(ostream& os, const QuicConsumedData& s) {
+ os << "bytes_consumed: " << s.bytes_consumed
+ << " fin_consumed: " << s.fin_consumed;
+ return os;
+}
+
+WriteResult::WriteResult()
+ : status(WRITE_STATUS_ERROR),
+ bytes_written(0) {
+}
+
+WriteResult::WriteResult(WriteStatus status,
+ int bytes_written_or_error_code)
+ : status(status),
+ bytes_written(bytes_written_or_error_code) {
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_types.h b/chromium/net/quic/quic_types.h
new file mode 100644
index 00000000000..01415bc3607
--- /dev/null
+++ b/chromium/net/quic/quic_types.h
@@ -0,0 +1,69 @@
+// Copyright 2014 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_TYPES_H_
+#define NET_QUIC_QUIC_TYPES_H_
+
+// This header defines some basic types that don't depend on quic_protocol.h,
+// so that classes not directly related to the protocol wire format can avoid
+// including quic_protocol.h.
+
+#include <stddef.h>
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// A struct for functions which consume data payloads and fins.
+struct NET_EXPORT_PRIVATE QuicConsumedData {
+ QuicConsumedData(size_t bytes_consumed, bool fin_consumed);
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member causes this object to have padding bytes, which causes the
+ // default gtest object printer to read uninitialize memory. So we need
+ // to teach gtest how to print this object.
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConsumedData& s);
+
+ // How many bytes were consumed.
+ size_t bytes_consumed;
+
+ // True if an incoming fin was consumed.
+ bool fin_consumed;
+};
+
+// QuicAsyncStatus enumerates the possible results of an asynchronous
+// operation.
+enum QuicAsyncStatus {
+ QUIC_SUCCESS = 0,
+ QUIC_FAILURE = 1,
+ // QUIC_PENDING results from an operation that will occur asynchonously. When
+ // the operation is complete, a callback's |Run| method will be called.
+ QUIC_PENDING = 2,
+};
+
+// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus.
+enum WriteStatus {
+ WRITE_STATUS_OK,
+ WRITE_STATUS_BLOCKED,
+ WRITE_STATUS_ERROR,
+};
+
+// A struct used to return the result of write calls including either the number
+// of bytes written or the error code, depending upon the status.
+struct NET_EXPORT_PRIVATE WriteResult {
+ WriteResult(WriteStatus status, int bytes_written_or_error_code);
+ WriteResult();
+
+ WriteStatus status;
+ union {
+ int bytes_written; // only valid when status is WRITE_STATUS_OK
+ int error_code; // only valid when status is WRITE_STATUS_ERROR
+ };
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TYPES_H_
diff --git a/chromium/net/quic/quic_unacked_packet_map.cc b/chromium/net/quic/quic_unacked_packet_map.cc
new file mode 100644
index 00000000000..e9dd1368f6a
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 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_unacked_packet_map.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_utils_chromium.h"
+
+using std::max;
+
+namespace net {
+
+QuicUnackedPacketMap::QuicUnackedPacketMap()
+ : largest_sent_packet_(0),
+ largest_observed_(0),
+ bytes_in_flight_(0),
+ pending_crypto_packet_count_(0) {
+}
+
+QuicUnackedPacketMap::~QuicUnackedPacketMap() {
+ for (UnackedPacketMap::iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ delete it->second.retransmittable_frames;
+ // Only delete all_transmissions once, for the newest packet.
+ if (it->first == *it->second.all_transmissions->rbegin()) {
+ delete it->second.all_transmissions;
+ }
+ }
+}
+
+// TODO(ianswett): Combine this method with OnPacketSent once packets are always
+// sent in order and the connection tracks RetransmittableFrames for longer.
+void QuicUnackedPacketMap::AddPacket(
+ const SerializedPacket& serialized_packet) {
+ if (!unacked_packets_.empty()) {
+ bool is_old_packet = unacked_packets_.rbegin()->first >=
+ serialized_packet.sequence_number;
+ LOG_IF(DFATAL, is_old_packet) << "Old packet serialized: "
+ << serialized_packet.sequence_number
+ << " vs: "
+ << unacked_packets_.rbegin()->first;
+ }
+
+ unacked_packets_[serialized_packet.sequence_number] =
+ TransmissionInfo(serialized_packet.retransmittable_frames,
+ serialized_packet.sequence_number,
+ serialized_packet.sequence_number_length);
+ if (serialized_packet.retransmittable_frames != NULL &&
+ serialized_packet.retransmittable_frames->HasCryptoHandshake()
+ == IS_HANDSHAKE) {
+ ++pending_crypto_packet_count_;
+ }
+}
+
+void QuicUnackedPacketMap::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type) {
+ DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first < new_sequence_number);
+
+ // TODO(ianswett): Discard and lose the packet lazily instead of immediately.
+ TransmissionInfo* transmission_info =
+ FindOrNull(unacked_packets_, old_sequence_number);
+ RetransmittableFrames* frames = transmission_info->retransmittable_frames;
+ LOG_IF(DFATAL, frames == NULL) << "Attempt to retransmit packet with no "
+ << "retransmittable frames: "
+ << old_sequence_number;
+
+ // We keep the old packet in the unacked packet list until it, or one of
+ // the retransmissions of it are acked.
+ transmission_info->retransmittable_frames = NULL;
+ unacked_packets_[new_sequence_number] =
+ TransmissionInfo(frames,
+ new_sequence_number,
+ transmission_info->sequence_number_length,
+ transmission_type,
+ transmission_info->all_transmissions);
+}
+
+void QuicUnackedPacketMap::ClearPreviousRetransmissions(size_t num_to_clear) {
+ UnackedPacketMap::iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && num_to_clear > 0) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ // If this packet is in flight, or has retransmittable data, then there is
+ // no point in clearing out any further packets, because they would not
+ // affect the high water mark.
+ if (it->second.in_flight || it->second.retransmittable_frames != NULL) {
+ break;
+ }
+
+ it->second.all_transmissions->erase(sequence_number);
+ LOG_IF(DFATAL, it->second.all_transmissions->empty())
+ << "Previous retransmissions must have a newer transmission.";
+ ++it;
+ unacked_packets_.erase(sequence_number);
+ --num_to_clear;
+ }
+}
+
+bool QuicUnackedPacketMap::HasRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ const TransmissionInfo* transmission_info =
+ FindOrNull(unacked_packets_, sequence_number);
+ if (transmission_info == NULL) {
+ return false;
+ }
+
+ return transmission_info->retransmittable_frames != NULL;
+}
+
+void QuicUnackedPacketMap::NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "NackPacket called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+
+ it->second.nack_count = max(min_nacks, it->second.nack_count);
+}
+
+void QuicUnackedPacketMap::RemoveRetransmittability(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ DVLOG(1) << "packet is not in unacked_packets: " << sequence_number;
+ return;
+ }
+ SequenceNumberSet* all_transmissions = it->second.all_transmissions;
+ // TODO(ianswett): Consider optimizing this for lone packets.
+ // TODO(ianswett): Consider adding a check to ensure there are retransmittable
+ // frames associated with this packet.
+ for (SequenceNumberSet::reverse_iterator it = all_transmissions->rbegin();
+ it != all_transmissions->rend(); ++it) {
+ TransmissionInfo* transmission_info = FindOrNull(unacked_packets_, *it);
+ if (transmission_info == NULL) {
+ LOG(DFATAL) << "All transmissions in all_transmissions must be present "
+ << "in the unacked packet map.";
+ continue;
+ }
+ MaybeRemoveRetransmittableFrames(transmission_info);
+ if (*it <= largest_observed_ && !transmission_info->in_flight) {
+ unacked_packets_.erase(*it);
+ } else {
+ transmission_info->all_transmissions = new SequenceNumberSet();
+ transmission_info->all_transmissions->insert(*it);
+ }
+ }
+
+ delete all_transmissions;
+}
+
+void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames(
+ TransmissionInfo* transmission_info) {
+ if (transmission_info->retransmittable_frames != NULL) {
+ if (transmission_info->retransmittable_frames->HasCryptoHandshake()
+ == IS_HANDSHAKE) {
+ --pending_crypto_packet_count_;
+ }
+ delete transmission_info->retransmittable_frames;
+ transmission_info->retransmittable_frames = NULL;
+ }
+}
+
+void QuicUnackedPacketMap::IncreaseLargestObserved(
+ QuicPacketSequenceNumber largest_observed) {
+ DCHECK_LT(largest_observed_, largest_observed);
+ largest_observed_ = largest_observed;
+ UnackedPacketMap::iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && it->first <= largest_observed_) {
+ if (!IsPacketUseless(it)) {
+ ++it;
+ continue;
+ }
+ delete it->second.all_transmissions;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ unacked_packets_.erase(sequence_number);
+ }
+}
+
+bool QuicUnackedPacketMap::IsPacketUseless(
+ UnackedPacketMap::const_iterator it) const {
+ return it->first <= largest_observed_ &&
+ !it->second.in_flight &&
+ it->second.retransmittable_frames == NULL &&
+ it->second.all_transmissions->size() == 1;
+}
+
+bool QuicUnackedPacketMap::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_packets_, sequence_number);
+}
+
+void QuicUnackedPacketMap::RemoveFromInFlight(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "RemoveFromFlight called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+ if (it->second.in_flight) {
+ LOG_IF(DFATAL, bytes_in_flight_ < it->second.bytes_sent);
+ bytes_in_flight_ -= it->second.bytes_sent;
+ it->second.in_flight = false;
+ }
+ if (IsPacketUseless(it)) {
+ delete it->second.all_transmissions;
+ unacked_packets_.erase(it);
+ }
+}
+
+bool QuicUnackedPacketMap::HasUnackedPackets() const {
+ return !unacked_packets_.empty();
+}
+
+bool QuicUnackedPacketMap::HasInFlightPackets() const {
+ return bytes_in_flight_ > 0;
+}
+
+const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const {
+ return unacked_packets_.find(sequence_number)->second;
+}
+
+QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const {
+ UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ while (it != unacked_packets_.rend()) {
+ if (it->second.in_flight) {
+ LOG_IF(DFATAL, it->second.sent_time == QuicTime::Zero())
+ << "Sent time can never be zero for a packet in flight.";
+ return it->second.sent_time;
+ }
+ ++it;
+ }
+ LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+}
+
+QuicTime QuicUnackedPacketMap::GetFirstInFlightPacketSentTime() const {
+ UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && !it->second.in_flight) {
+ ++it;
+ }
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "GetFirstInFlightPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+ }
+ return it->second.sent_time;
+}
+
+size_t QuicUnackedPacketMap::GetNumUnackedPackets() const {
+ return unacked_packets_.size();
+}
+
+bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const {
+ size_t num_in_flight = 0;
+ for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ it != unacked_packets_.rend(); ++it) {
+ if (it->second.in_flight) {
+ ++num_in_flight;
+ }
+ if (num_in_flight > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicUnackedPacketMap::HasPendingCryptoPackets() const {
+ return pending_crypto_packet_count_ > 0;
+}
+
+bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const {
+ for (UnackedPacketMap::const_reverse_iterator it =
+ unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) {
+ if (it->second.in_flight && it->second.retransmittable_frames) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicPacketSequenceNumber
+QuicUnackedPacketMap::GetLeastUnackedSentPacket() const {
+ if (unacked_packets_.empty()) {
+ // If there are no unacked packets, return 0.
+ return 0;
+ }
+
+ return unacked_packets_.begin()->first;
+}
+
+void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight) {
+ DCHECK_LT(0u, sequence_number);
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "OnPacketSent called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+ DCHECK(!it->second.in_flight);
+
+ largest_sent_packet_ = max(sequence_number, largest_sent_packet_);
+ it->second.sent_time = sent_time;
+ if (set_in_flight) {
+ bytes_in_flight_ += bytes_sent;
+ it->second.bytes_sent = bytes_sent;
+ it->second.in_flight = true;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_unacked_packet_map.h b/chromium/net/quic/quic_unacked_packet_map.h
new file mode 100644
index 00000000000..ae72548b9b4
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map.h
@@ -0,0 +1,152 @@
+// Copyright 2014 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_UNACKED_PACKET_MAP_H_
+#define NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
+
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// Class which tracks unacked packets for three purposes:
+// 1) Track retransmittable data, including multiple transmissions of frames.
+// 2) Track packets and bytes in flight for congestion control.
+// 3) Track sent time of packets to provide RTT measurements from acks.
+class NET_EXPORT_PRIVATE QuicUnackedPacketMap {
+ public:
+ QuicUnackedPacketMap();
+ ~QuicUnackedPacketMap();
+
+ // Adds |serialized_packet| to the map. Does not mark it in flight.
+ void AddPacket(const SerializedPacket& serialized_packet);
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // |old_sequence_number| will remain unacked, but will have no
+ // retransmittable data associated with it. |new_sequence_number| will
+ // be both unacked and associated with retransmittable data.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type);
+
+ // Returns true if the packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Sets the nack count to the max of the current nack count and |min_nacks|.
+ void NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks);
+
+ // Marks |sequence_number| as no longer in flight.
+ void RemoveFromInFlight(QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if the unacked packet |sequence_number| has retransmittable
+ // frames. This will return false if the packet has been acked, if a
+ // previous transmission of this packet was ACK'd, or if this packet has been
+ // retransmitted as with different sequence number, or if the packet never
+ // had any retransmittable packets in the first place.
+ bool HasRetransmittableFrames(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are any unacked packets.
+ bool HasUnackedPackets() const;
+
+ // Returns true if there are any unacked packets which have retransmittable
+ // frames.
+ bool HasUnackedRetransmittableFrames() const;
+
+ // Returns the largest sequence number that has been sent.
+ QuicPacketSequenceNumber largest_sent_packet() const {
+ return largest_sent_packet_;
+ }
+
+ // Returns the sum of bytes from all packets in flight.
+ QuicByteCount bytes_in_flight() const {
+ return bytes_in_flight_;
+ }
+
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
+ QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
+
+ // Sets a packet as sent with the sent time |sent_time|. Marks the packet
+ // as in flight if |set_in_flight| is true.
+ // Packets marked as in flight are expected to be marked as missing when they
+ // don't arrive, indicating the need for retransmission.
+ void SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight);
+
+ // Clears up to |num_to_clear| previous transmissions in order to make room
+ // in the ack frame for new acks.
+ void ClearPreviousRetransmissions(size_t num_to_clear);
+
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ TransmissionInfo> UnackedPacketMap;
+
+ typedef UnackedPacketMap::const_iterator const_iterator;
+
+ const_iterator begin() const { return unacked_packets_.begin(); }
+ const_iterator end() const { return unacked_packets_.end(); }
+
+ // Returns true if there are unacked packets that are in flight.
+ bool HasInFlightPackets() const;
+
+ // Returns the TransmissionInfo associated with |sequence_number|, which
+ // must be unacked.
+ const TransmissionInfo& GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the time that the last unacked packet was sent.
+ QuicTime GetLastPacketSentTime() const;
+
+ // Returns the time that the first in flight packet was sent.
+ QuicTime GetFirstInFlightPacketSentTime() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPackets() const;
+
+ // Returns true if there are multiple packets in flight.
+ bool HasMultipleInFlightPackets() const;
+
+ // Returns true if there are any pending crypto packets.
+ bool HasPendingCryptoPackets() const;
+
+ // Removes any retransmittable frames from this transmission or an associated
+ // transmission. It removes now useless transmissions, and disconnects any
+ // other packets from other transmissions.
+ void RemoveRetransmittability(QuicPacketSequenceNumber sequence_number);
+
+ // Increases the largest observed. Any packets less or equal to
+ // |largest_acked_packet| are discarded if they are only for the RTT purposes.
+ void IncreaseLargestObserved(QuicPacketSequenceNumber largest_observed);
+
+ private:
+ void MaybeRemoveRetransmittableFrames(TransmissionInfo* transmission_info);
+
+ // Returns true if the packet no longer has a purpose in the map.
+ bool IsPacketUseless(UnackedPacketMap::const_iterator it) const;
+
+ QuicPacketSequenceNumber largest_sent_packet_;
+ QuicPacketSequenceNumber largest_observed_;
+
+ // Newly serialized retransmittable and fec packets are added to this map,
+ // which contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be NULL, while
+ // the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to NULL.
+ UnackedPacketMap unacked_packets_;
+
+ size_t bytes_in_flight_;
+ // Number of retransmittable crypto handshake packets.
+ size_t pending_crypto_packet_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
diff --git a/chromium/net/quic/quic_unacked_packet_map_test.cc b/chromium/net/quic/quic_unacked_packet_map_test.cc
new file mode 100644
index 00000000000..c5264fd16e9
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map_test.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 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_unacked_packet_map.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32 kDefaultAckLength = 50;
+const uint32 kDefaultLength = 1000;
+
+class QuicUnackedPacketMapTest : public ::testing::Test {
+ protected:
+ QuicUnackedPacketMapTest()
+ : now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) {
+ }
+
+ SerializedPacket CreateRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL,
+ 0, new RetransmittableFrames());
+ }
+
+ SerializedPacket CreateNonRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(
+ sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
+ }
+
+ void VerifyPendingPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ return;
+ }
+ if (num_packets == 1) {
+ EXPECT_TRUE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ }
+ for (size_t i = 0; i < num_packets; ++i) {
+ ASSERT_TRUE(unacked_packets_.IsUnacked(packets[i]));
+ EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[i]).in_flight);
+ }
+ }
+
+ void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasUnackedPackets());
+ EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames());
+ return;
+ }
+ EXPECT_TRUE(unacked_packets_.HasUnackedPackets());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i];
+ }
+ }
+
+ void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ size_t num_retransmittable_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (it->second.retransmittable_frames != NULL) {
+ ++num_retransmittable_packets;
+ }
+ }
+ EXPECT_EQ(num_packets, num_retransmittable_packets);
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ QuicTime now_;
+};
+
+TEST_F(QuicUnackedPacketMapTest, RttOnly) {
+ // Acks are only tracked for RTT measurement purposes.
+ unacked_packets_.AddPacket(CreateNonRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultAckLength, false);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) {
+ // Simulate a retransmittable packet being sent and acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(unacked, arraysize(unacked));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittedPacket) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.OnRetransmittedPacket(1, 2, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(2);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(2);
+ QuicPacketSequenceNumber unacked2[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked2));
+ VerifyPendingPackets(unacked, arraysize(unacked2));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_utils.cc b/chromium/net/quic/quic_utils.cc
index 0aff3772c26..ae4bb2eedc9 100644
--- a/chromium/net/quic/quic_utils.cc
+++ b/chromium/net/quic/quic_utils.cc
@@ -8,11 +8,12 @@
#include <algorithm>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/port.h"
#include "base/strings/stringprintf.h"
#include "base/strings/string_number_conversions.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::string;
@@ -137,6 +138,7 @@ const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) {
RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_RST_FLOW_CONTROL_ACCOUNTING);
RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR);
}
// Return a default value so that we return this when |error| doesn't match
@@ -156,9 +158,13 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD);
RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
@@ -187,7 +193,6 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
- RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE);
RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE);
@@ -196,6 +201,11 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
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_INVALID_HEADERS_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED);
RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
@@ -226,6 +236,19 @@ const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) {
}
// static
+const char* QuicUtils::TransmissionTypeToString(TransmissionType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(NOT_RETRANSMISSION);
+ RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION);
+ RETURN_STRING_LITERAL(LOSS_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION);
+ RETURN_STRING_LITERAL(RTO_RETRANSMISSION);
+ RETURN_STRING_LITERAL(TLP_RETRANSMISSION);
+ }
+ return "INVALID_TRANSMISSION_TYPE";
+}
+
+// static
string QuicUtils::TagToString(QuicTag tag) {
char chars[4];
bool ascii = true;
@@ -284,12 +307,12 @@ string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) {
// static
QuicPriority QuicUtils::LowestPriority() {
- return static_cast<QuicPriority>(kLowestPriority);
+ return QuicWriteBlockedList::kLowestPriority;
}
// static
QuicPriority QuicUtils::HighestPriority() {
- return static_cast<QuicPriority>(kHighestPriority);
+ return QuicWriteBlockedList::kHighestPriority;
}
} // namespace net
diff --git a/chromium/net/quic/quic_utils.h b/chromium/net/quic/quic_utils.h
index 23f3b53a611..43f8d350ea6 100644
--- a/chromium/net/quic/quic_utils.h
+++ b/chromium/net/quic/quic_utils.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Some helpers for quic
+// Some helpers for quic.
#ifndef NET_QUIC_QUIC_UTILS_H_
#define NET_QUIC_QUIC_UTILS_H_
@@ -20,7 +20,7 @@ class NET_EXPORT_PRIVATE QuicUtils {
PEER_PRIORITY,
};
- // returns the 64 bit FNV1a hash of the data. See
+ // Returns the 64 bit FNV1a hash of the data. See
// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
static uint64 FNV1a_64_Hash(const char* data, int len);
@@ -59,6 +59,9 @@ class NET_EXPORT_PRIVATE QuicUtils {
// Returns the level of encryption as a char*
static const char* EncryptionLevelToString(EncryptionLevel level);
+ // Returns TransmissionType as a char*
+ static const char* TransmissionTypeToString(TransmissionType type);
+
// TagToString is a utility function for pretty-printing handshake messages
// that converts a tag to a string. It will try to maintain the human friendly
// name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number
@@ -79,6 +82,9 @@ class NET_EXPORT_PRIVATE QuicUtils {
static QuicPriority LowestPriority();
static QuicPriority HighestPriority();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicUtils);
};
// Utility function that returns an IOVector object wrapped around |str|.
diff --git a/chromium/net/quic/quic_utils_chromium.h b/chromium/net/quic/quic_utils_chromium.h
new file mode 100644
index 00000000000..0229d1d0400
--- /dev/null
+++ b/chromium/net/quic/quic_utils_chromium.h
@@ -0,0 +1,80 @@
+// Copyright 2014 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.
+//
+// Some helpers for quic that are for chromium codebase.
+
+#ifndef NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+#define NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+//
+// Find*()
+//
+
+// Returns a const reference to the value associated with the given key if it
+// exists. Crashes otherwise.
+//
+// This is intended as a replacement for operator[] as an rvalue (for reading)
+// when the key is guaranteed to exist.
+//
+// operator[] for lookup is discouraged for several reasons:
+// * It has a side-effect of inserting missing keys
+// * It is not thread-safe (even when it is not inserting, it can still
+// choose to resize the underlying storage)
+// * It invalidates iterators (when it chooses to resize)
+// * It default constructs a value object even if it doesn't need to
+//
+// This version assumes the key is printable, and includes it in the fatal log
+// message.
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindOrDie(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Same as above, but returns a non-const reference.
+template <class Collection>
+typename Collection::value_type::second_type&
+FindOrDie(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Returns a pointer to the const value associated with the given key if it
+// exists, or NULL otherwise.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// Same as above but returns a pointer to the non-const value.
+template <class Collection>
+typename Collection::value_type::second_type*
+FindOrNull(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UTILS_CHROMIUM_H_
diff --git a/chromium/net/quic/quic_utils_chromium_test.cc b/chromium/net/quic/quic_utils_chromium_test.cc
new file mode 100644
index 00000000000..247b8613c49
--- /dev/null
+++ b/chromium/net/quic/quic_utils_chromium_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 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_utils_chromium.h"
+
+#include <map>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::map;
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicUtilsChromiumTest, FindOrNullTest) {
+ map<int, int> m;
+ m[0] = 2;
+
+ // Check FindOrNull
+ int* p1 = FindOrNull(m, 0);
+ CHECK_EQ(*p1, 2);
+ ++(*p1);
+ const map<int, int>& const_m = m;
+ const int* p2 = FindOrNull(const_m, 0);
+ CHECK_EQ(*p2, 3);
+ CHECK(FindOrNull(m, 1) == NULL);
+}
+
+TEST(QuicUtilsChromiumTest, FindOrDieTest) {
+ std::map<int, int> m;
+ m[10] = 15;
+ EXPECT_EQ(15, FindOrDie(m, 10));
+ // TODO(rtenneti): Use the latest DEATH macros after merging with latest rch's
+ // changes.
+ // ASSERT_DEATH(FindOrDie(m, 8), "Map key not found: 8");
+
+ // Make sure the non-const reference returning version works.
+ FindOrDie(m, 10) = 20;
+ EXPECT_EQ(20, FindOrDie(m, 10));
+
+ // Make sure we can lookup values in a const map.
+ const map<int, int>& const_m = m;
+ EXPECT_EQ(20, FindOrDie(const_m, 10));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_write_blocked_list.cc b/chromium/net/quic/quic_write_blocked_list.cc
new file mode 100644
index 00000000000..10f9c343bfa
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 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_write_blocked_list.h"
+
+namespace net {
+
+const QuicPriority QuicWriteBlockedList::kHighestPriority =
+ static_cast<QuicPriority>(net::kHighestPriority);
+const QuicPriority QuicWriteBlockedList::kLowestPriority =
+ static_cast<QuicPriority>(net::kLowestPriority);
+
+QuicWriteBlockedList::QuicWriteBlockedList()
+ : crypto_stream_blocked_(false),
+ headers_stream_blocked_(false) {}
+
+QuicWriteBlockedList::~QuicWriteBlockedList() {}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_write_blocked_list.h b/chromium/net/quic/quic_write_blocked_list.h
new file mode 100644
index 00000000000..6727fb4e826
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list.h
@@ -0,0 +1,113 @@
+// Copyright 2014 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_WRITE_BLOCKED_LIST_H_
+#define NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
+
+#include <set>
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/spdy/write_blocked_list.h"
+
+namespace net {
+
+// Keeps tracks of the QUIC streams that have data to write, sorted by
+// priority. QUIC stream priority order is:
+// Crypto stream > Headers stream > Data streams by requested priority.
+class NET_EXPORT_PRIVATE QuicWriteBlockedList {
+ private:
+ typedef WriteBlockedList<QuicStreamId> QuicWriteBlockedListBase;
+
+ public:
+ static const QuicPriority kHighestPriority;
+ static const QuicPriority kLowestPriority;
+
+ QuicWriteBlockedList();
+ ~QuicWriteBlockedList();
+
+ bool HasWriteBlockedDataStreams() const {
+ return base_write_blocked_list_.HasWriteBlockedStreams();
+ }
+
+ bool HasWriteBlockedCryptoOrHeadersStream() const {
+ return crypto_stream_blocked_ || headers_stream_blocked_;
+ }
+
+ size_t NumBlockedStreams() const {
+ size_t num_blocked = base_write_blocked_list_.NumBlockedStreams();
+ if (crypto_stream_blocked_) {
+ ++num_blocked;
+ }
+ if (headers_stream_blocked_) {
+ ++num_blocked;
+ }
+
+ return num_blocked;
+ }
+
+ QuicStreamId PopFront() {
+ if (crypto_stream_blocked_) {
+ crypto_stream_blocked_ = false;
+ return kCryptoStreamId;
+ }
+
+ if (headers_stream_blocked_) {
+ headers_stream_blocked_ = false;
+ return kHeadersStreamId;
+ }
+
+ SpdyPriority priority =
+ base_write_blocked_list_.GetHighestPriorityWriteBlockedList();
+ QuicStreamId id = base_write_blocked_list_.PopFront(priority);
+ blocked_streams_.erase(id);
+ return id;
+ }
+
+ void PushBack(QuicStreamId stream_id, QuicPriority priority) {
+ if (stream_id == kCryptoStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!crypto_stream_blocked_)
+ crypto_stream_blocked_ = true;
+ return;
+ }
+
+ if (stream_id == kHeadersStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!headers_stream_blocked_);
+ headers_stream_blocked_ = true;
+ return;
+ }
+
+ if (blocked_streams_.find(stream_id) != blocked_streams_.end()) {
+ DVLOG(1) << "Stream " << stream_id << " already in write blocked list.";
+ return;
+ }
+
+ base_write_blocked_list_.PushBack(
+ stream_id, static_cast<SpdyPriority>(priority));
+ blocked_streams_.insert(stream_id);
+ return;
+ }
+
+ bool crypto_stream_blocked() const { return crypto_stream_blocked_; }
+ bool headers_stream_blocked() const { return headers_stream_blocked_; }
+
+ private:
+ QuicWriteBlockedListBase base_write_blocked_list_;
+ bool crypto_stream_blocked_;
+ bool headers_stream_blocked_;
+
+ // Keep track of write blocked streams in a set for faster membership checking
+ // than iterating over the base_write_blocked_list_. The contents of this set
+ // should mirror the contents of base_write_blocked_list_.
+ std::set<QuicStreamId> blocked_streams_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicWriteBlockedList);
+};
+
+} // namespace net
+
+
+#endif // NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
diff --git a/chromium/net/quic/quic_write_blocked_list_test.cc b/chromium/net/quic/quic_write_blocked_list_test.cc
new file mode 100644
index 00000000000..0633f633636
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 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_write_blocked_list.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicWriteBlockedListTest, PriorityOrder) {
+ QuicWriteBlockedList write_blocked_list;
+
+ // Mark streams blocked in roughly reverse priority order, and
+ // verify that streams are sorted.
+ write_blocked_list.PushBack(40,
+ QuicWriteBlockedList::kLowestPriority);
+ write_blocked_list.PushBack(23,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(17,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // The Crypto stream is highest priority.
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ // Followed by the Headers stream.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ // Streams with same priority are popped in the order they were inserted.
+ EXPECT_EQ(23u, write_blocked_list.PopFront());
+ EXPECT_EQ(17u, write_blocked_list.PopFront());
+ // Low priority stream appears last.
+ EXPECT_EQ(40u, write_blocked_list.PopFront());
+
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, CryptoStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, HeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, VerifyHeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(5,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // In newer QUIC versions, there is a headers stream which is
+ // higher priority than data streams.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(5u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, NoDuplicateEntries) {
+ // Test that QuicWriteBlockedList doesn't allow duplicate entries.
+ QuicWriteBlockedList write_blocked_list;
+
+ // Try to add a stream to the write blocked list multiple times at the same
+ // priority.
+ const QuicStreamId kBlockedId = kClientDataStreamId1;
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ // This should only result in one blocked stream being added.
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+
+ // There should only be one stream to pop off the front.
+ EXPECT_EQ(kBlockedId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/reliable_quic_stream.cc b/chromium/net/quic/reliable_quic_stream.cc
index 5d9c3cce49c..44d3a036fdd 100644
--- a/chromium/net/quic/reliable_quic_stream.cc
+++ b/chromium/net/quic/reliable_quic_stream.cc
@@ -4,9 +4,11 @@
#include "net/quic/reliable_quic_stream.h"
+#include "base/logging.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_flow_controller.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::min;
@@ -23,10 +25,119 @@ struct iovec MakeIovec(StringPiece data) {
return iov;
}
+size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ return session->config()->GetInitialFlowControlWindowToSend();
+ }
+
+ return session->config()->GetInitialStreamFlowControlWindowToSend();
+}
+
+size_t GetReceivedFlowControlWindow(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ if (session->config()->HasReceivedInitialFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+ }
+
+ // Version must be >= QUIC_VERSION_20, so we check for stream specific flow
+ // control window.
+ if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialStreamFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+}
+
} // namespace
-ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
- QuicSession* session)
+// Wrapper that aggregates OnAckNotifications for packets sent using
+// WriteOrBufferData and delivers them to the original
+// QuicAckNotifier::DelegateInterface after all bytes written using
+// WriteOrBufferData are acked. This level of indirection is
+// necessary because the delegate interface provides no mechanism that
+// WriteOrBufferData can use to inform it that the write required
+// multiple WritevData calls or that only part of the data has been
+// sent out by the time ACKs start arriving.
+class ReliableQuicStream::ProxyAckNotifierDelegate
+ : public QuicAckNotifier::DelegateInterface {
+ public:
+ explicit ProxyAckNotifierDelegate(DelegateInterface* delegate)
+ : delegate_(delegate),
+ pending_acks_(0),
+ wrote_last_data_(false),
+ num_original_packets_(0),
+ num_original_bytes_(0),
+ num_retransmitted_packets_(0),
+ num_retransmitted_bytes_(0) {
+ }
+
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed)
+ OVERRIDE {
+ DCHECK_LT(0, pending_acks_);
+ --pending_acks_;
+ num_original_packets_ += num_original_packets;
+ num_original_bytes_ += num_original_bytes;
+ num_retransmitted_packets_ += num_retransmitted_packets;
+ num_retransmitted_bytes_ += num_retransmitted_bytes;
+
+ if (wrote_last_data_ && pending_acks_ == 0) {
+ delegate_->OnAckNotification(num_original_packets_,
+ num_original_bytes_,
+ num_retransmitted_packets_,
+ num_retransmitted_bytes_,
+ delta_largest_observed);
+ }
+ }
+
+ void WroteData(bool last_data) {
+ DCHECK(!wrote_last_data_);
+ ++pending_acks_;
+ wrote_last_data_ = last_data;
+ }
+
+ protected:
+ // Delegates are ref counted.
+ virtual ~ProxyAckNotifierDelegate() OVERRIDE {
+ }
+
+ private:
+ // Original delegate. delegate_->OnAckNotification will be called when:
+ // wrote_last_data_ == true and pending_acks_ == 0
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Number of outstanding acks.
+ int pending_acks_;
+
+ // True if no pending writes remain.
+ bool wrote_last_data_;
+
+ // Accumulators.
+ int num_original_packets_;
+ int num_original_bytes_;
+ int num_retransmitted_packets_;
+ int num_retransmitted_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyAckNotifierDelegate);
+};
+
+ReliableQuicStream::PendingData::PendingData(
+ string data_in, scoped_refptr<ProxyAckNotifierDelegate> delegate_in)
+ : data(data_in), delegate(delegate_in) {
+}
+
+ReliableQuicStream::PendingData::~PendingData() {
+}
+
+ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session)
: sequencer_(this),
id_(id),
session_(session),
@@ -38,41 +149,70 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
write_side_closed_(false),
fin_buffered_(false),
fin_sent_(false),
- is_server_(session_->is_server()) {
+ fin_received_(false),
+ rst_sent_(false),
+ rst_received_(false),
+ fec_policy_(FEC_PROTECT_OPTIONAL),
+ is_server_(session_->is_server()),
+ flow_controller_(
+ session_->connection(), id_, is_server_,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ GetInitialStreamFlowControlWindowToSend(session)),
+ connection_flow_controller_(session_->flow_controller()) {
}
ReliableQuicStream::~ReliableQuicStream() {
}
-bool ReliableQuicStream::WillAcceptStreamFrame(
- const QuicStreamFrame& frame) const {
+bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
+ // We don't want to be reading: blackhole the data.
return true;
}
+
if (frame.stream_id != id_) {
LOG(ERROR) << "Error!";
return false;
}
- return sequencer_.WillAcceptStreamFrame(frame);
-}
-bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
- DCHECK_EQ(frame.stream_id, id_);
- if (read_side_closed_) {
- DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
- // We don't want to be reading: blackhole the data.
- return true;
+ if (frame.fin) {
+ fin_received_ = true;
}
- // Note: This count include duplicate data received.
- stream_bytes_read_ += frame.data.TotalBufferSize();
- bool accepted = sequencer_.OnStreamFrame(frame);
+ // This count include duplicate data received.
+ size_t frame_payload_size = frame.data.TotalBufferSize();
+ stream_bytes_read_ += frame_payload_size;
+
+ // Flow control is interested in tracking highest received offset.
+ if (MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
+ // As the highest received offset has changed, we should check to see if
+ // this is a violation of flow control.
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ session_->connection()->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return false;
+ }
+ }
- return accepted;
+ return sequencer_.OnStreamFrame(frame);
}
-void ReliableQuicStream::OnStreamReset(QuicRstStreamErrorCode error) {
- stream_error_ = error;
+int ReliableQuicStream::num_frames_received() const {
+ return sequencer_.num_frames_received();
+}
+
+int ReliableQuicStream::num_duplicate_frames_received() const {
+ return sequencer_.num_duplicate_frames_received();
+}
+
+void ReliableQuicStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ rst_received_ = true;
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+
+ stream_error_ = frame.error_code;
CloseWriteSide();
CloseReadSide();
}
@@ -100,7 +240,8 @@ void ReliableQuicStream::Reset(QuicRstStreamErrorCode error) {
DCHECK_NE(QUIC_STREAM_NO_ERROR, error);
stream_error_ = error;
// Sending a RstStream results in calling CloseStream.
- session()->SendRstStream(id(), error);
+ session()->SendRstStream(id(), error, stream_bytes_written_);
+ rst_sent_ = true;
}
void ReliableQuicStream::CloseConnection(QuicErrorCode error) {
@@ -112,51 +253,95 @@ void ReliableQuicStream::CloseConnectionWithDetails(QuicErrorCode error,
session()->connection()->SendConnectionCloseWithDetails(error, details);
}
-QuicVersion ReliableQuicStream::version() {
+QuicVersion ReliableQuicStream::version() const {
return session()->connection()->version();
}
-void ReliableQuicStream::WriteOrBufferData(StringPiece data, bool fin) {
- DCHECK(data.size() > 0 || fin);
- DCHECK(!fin_buffered_);
+void ReliableQuicStream::WriteOrBufferData(
+ StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ if (data.empty() && !fin) {
+ LOG(DFATAL) << "data.empty() && !fin";
+ return;
+ }
+
+ if (fin_buffered_) {
+ LOG(DFATAL) << "Fin already buffered";
+ return;
+ }
+
+ scoped_refptr<ProxyAckNotifierDelegate> proxy_delegate;
+ if (ack_notifier_delegate != NULL) {
+ proxy_delegate = new ProxyAckNotifierDelegate(ack_notifier_delegate);
+ }
QuicConsumedData consumed_data(0, false);
fin_buffered_ = fin;
if (queued_data_.empty()) {
struct iovec iov(MakeIovec(data));
- consumed_data = WritevData(&iov, 1, fin, NULL);
+ consumed_data = WritevData(&iov, 1, fin, proxy_delegate.get());
DCHECK_LE(consumed_data.bytes_consumed, data.length());
}
+ bool write_completed;
// If there's unconsumed data or an unconsumed fin, queue it.
if (consumed_data.bytes_consumed < data.length() ||
(fin && !consumed_data.fin_consumed)) {
- queued_data_.push_back(
- string(data.data() + consumed_data.bytes_consumed,
- data.length() - consumed_data.bytes_consumed));
+ StringPiece remainder(data.substr(consumed_data.bytes_consumed));
+ queued_data_.push_back(PendingData(remainder.as_string(), proxy_delegate));
+ write_completed = false;
+ } else {
+ write_completed = true;
+ }
+
+ if ((proxy_delegate.get() != NULL) &&
+ (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed)) {
+ proxy_delegate->WroteData(write_completed);
}
}
void ReliableQuicStream::OnCanWrite() {
bool fin = false;
while (!queued_data_.empty()) {
- const string& data = queued_data_.front();
+ PendingData* pending_data = &queued_data_.front();
+ ProxyAckNotifierDelegate* delegate = pending_data->delegate.get();
if (queued_data_.size() == 1 && fin_buffered_) {
fin = true;
}
- struct iovec iov(MakeIovec(data));
- QuicConsumedData consumed_data = WritevData(&iov, 1, fin, NULL);
- if (consumed_data.bytes_consumed == data.size() &&
+ struct iovec iov(MakeIovec(pending_data->data));
+ QuicConsumedData consumed_data = WritevData(&iov, 1, fin, delegate);
+ if (consumed_data.bytes_consumed == pending_data->data.size() &&
fin == consumed_data.fin_consumed) {
queued_data_.pop_front();
+ if (delegate != NULL) {
+ delegate->WroteData(true);
+ }
} else {
- queued_data_.front().erase(0, consumed_data.bytes_consumed);
+ if (consumed_data.bytes_consumed > 0) {
+ pending_data->data.erase(0, consumed_data.bytes_consumed);
+ if (delegate != NULL) {
+ delegate->WroteData(false);
+ }
+ }
break;
}
}
}
+void ReliableQuicStream::MaybeSendBlocked() {
+ flow_controller_.MaybeSendBlocked();
+ connection_flow_controller_->MaybeSendBlocked();
+ // If we are connection level flow control blocked, then add the stream
+ // to the write blocked list. It will be given a chance to write when a
+ // connection level WINDOW_UPDATE arrives.
+ if (connection_flow_controller_->IsBlocked() &&
+ !flow_controller_.IsBlocked()) {
+ session_->MarkWriteBlocked(id(), EffectivePriority());
+ }
+}
+
QuicConsumedData ReliableQuicStream::WritevData(
const struct iovec* iov,
int iov_count,
@@ -167,14 +352,50 @@ QuicConsumedData ReliableQuicStream::WritevData(
return QuicConsumedData(0, false);
}
- size_t write_length = 0u;
- for (int i = 0; i < iov_count; ++i) {
- write_length += iov[i].iov_len;
+ // How much data we want to write.
+ size_t write_length = TotalIovecLength(iov, iov_count);
+
+ // A FIN with zero data payload should not be flow control blocked.
+ bool fin_with_zero_data = (fin && write_length == 0);
+
+ if (flow_controller_.IsEnabled()) {
+ // How much data we are allowed to write from flow control.
+ uint64 send_window = flow_controller_.SendWindowSize();
+ if (connection_flow_controller_->IsEnabled()) {
+ send_window =
+ min(send_window, connection_flow_controller_->SendWindowSize());
+ }
+
+ if (send_window == 0 && !fin_with_zero_data) {
+ // Quick return if we can't send anything.
+ MaybeSendBlocked();
+ return QuicConsumedData(0, false);
+ }
+
+ if (write_length > send_window) {
+ // Don't send the FIN if we aren't going to send all the data.
+ fin = false;
+
+ // Writing more data would be a violation of flow control.
+ write_length = send_window;
+ }
}
+
+ // Fill an IOVector with bytes from the iovec.
+ IOVector data;
+ data.AppendIovecAtMostBytes(iov, iov_count, write_length);
+
QuicConsumedData consumed_data = session()->WritevData(
- id(), iov, iov_count, stream_bytes_written_, fin, ack_notifier_delegate);
+ id(), data, stream_bytes_written_, fin, GetFecProtection(),
+ ack_notifier_delegate);
stream_bytes_written_ += consumed_data.bytes_consumed;
+
+ AddBytesSent(consumed_data.bytes_consumed);
+
if (consumed_data.bytes_consumed == write_length) {
+ if (!fin_with_zero_data) {
+ MaybeSendBlocked();
+ }
if (fin && consumed_data.fin_consumed) {
fin_sent_ = true;
CloseWriteSide();
@@ -187,6 +408,10 @@ QuicConsumedData ReliableQuicStream::WritevData(
return consumed_data;
}
+FecProtection ReliableQuicStream::GetFecProtection() {
+ return fec_policy_ == FEC_PROTECT_ALWAYS ? MUST_FEC_PROTECT : MAY_FEC_PROTECT;
+}
+
void ReliableQuicStream::CloseReadSide() {
if (read_side_closed_) {
return;
@@ -213,13 +438,88 @@ void ReliableQuicStream::CloseWriteSide() {
}
}
-bool ReliableQuicStream::HasBufferedData() {
+bool ReliableQuicStream::HasBufferedData() const {
return !queued_data_.empty();
}
void ReliableQuicStream::OnClose() {
CloseReadSide();
CloseWriteSide();
+
+ if (!fin_sent_ && !rst_sent_) {
+ // For flow control accounting, we must tell the peer how many bytes we have
+ // written on this stream before termination. Done here if needed, using a
+ // RST frame.
+ DVLOG(1) << ENDPOINT << "Sending RST in OnClose: " << id();
+ session_->SendRstStream(id(), QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ stream_bytes_written_);
+ rst_sent_ = true;
+ }
+
+ // We are closing the stream and will not process any further incoming bytes.
+ // As there may be more bytes in flight and we need to ensure that both
+ // endpoints have the same connection level flow control state, mark all
+ // unreceived or buffered bytes as consumed.
+ uint64 bytes_to_consume = flow_controller_.highest_received_byte_offset() -
+ flow_controller_.bytes_consumed();
+ AddBytesConsumed(bytes_to_consume);
+}
+
+void ReliableQuicStream::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ if (!flow_controller_.IsEnabled()) {
+ DLOG(DFATAL) << "Flow control not enabled! " << version();
+ return;
+ }
+
+ if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) {
+ // We can write again!
+ // TODO(rjshade): This does not respect priorities (e.g. multiple
+ // outstanding POSTs are unblocked on arrival of
+ // SHLO with initial window).
+ // As long as the connection is not flow control blocked, we can write!
+ OnCanWrite();
+ }
+}
+
+bool ReliableQuicStream::MaybeIncreaseHighestReceivedOffset(uint64 new_offset) {
+ if (flow_controller_.IsEnabled()) {
+ uint64 increment =
+ new_offset - flow_controller_.highest_received_byte_offset();
+ if (flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ // If |new_offset| increased the stream flow controller's highest received
+ // offset, then we need to increase the connection flow controller's value
+ // by the incremental difference.
+ connection_flow_controller_->UpdateHighestReceivedOffset(
+ connection_flow_controller_->highest_received_byte_offset() +
+ increment);
+ return true;
+ }
+ }
+ return false;
+}
+
+void ReliableQuicStream::AddBytesSent(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ flow_controller_.AddBytesSent(bytes);
+ connection_flow_controller_->AddBytesSent(bytes);
+ }
+}
+
+void ReliableQuicStream::AddBytesConsumed(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ // Only adjust stream level flow controller if we are still reading.
+ if (!read_side_closed_) {
+ flow_controller_.AddBytesConsumed(bytes);
+ }
+
+ connection_flow_controller_->AddBytesConsumed(bytes);
+ }
+}
+
+bool ReliableQuicStream::IsFlowControlBlocked() {
+ return flow_controller_.IsBlocked() ||
+ connection_flow_controller_->IsBlocked();
}
} // namespace net
diff --git a/chromium/net/quic/reliable_quic_stream.h b/chromium/net/quic/reliable_quic_stream.h
index 7b0a79068d5..be89a284e4a 100644
--- a/chromium/net/quic/reliable_quic_stream.h
+++ b/chromium/net/quic/reliable_quic_stream.h
@@ -11,13 +11,16 @@
#include <list>
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "net/base/iovec.h"
#include "net/base/net_export.h"
#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_flow_controller.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_spdy_compressor.h"
#include "net/quic/quic_stream_sequencer.h"
+#include "net/quic/quic_types.h"
namespace net {
@@ -25,9 +28,7 @@ namespace test {
class ReliableQuicStreamPeer;
} // namespace test
-class IPEndPoint;
class QuicSession;
-class SSLInfo;
class NET_EXPORT_PRIVATE ReliableQuicStream {
public:
@@ -36,8 +37,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual ~ReliableQuicStream();
- bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const;
-
// Called when a (potentially duplicate) stream frame has been received
// for this stream. Returns false if this frame can not be accepted
// because there is too much data already buffered.
@@ -51,7 +50,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual void OnClose();
// Called when we get a stream reset from the peer.
- virtual void OnStreamReset(QuicRstStreamErrorCode error);
+ virtual void OnStreamReset(const QuicRstStreamFrame& frame);
// Called when we get or send a connection close, and should immediately
// close the stream. This is not passed through the sequencer,
@@ -83,15 +82,52 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool read_side_closed() const { return read_side_closed_; }
bool write_side_closed() const { return write_side_closed_; }
- uint64 stream_bytes_read() { return stream_bytes_read_; }
- uint64 stream_bytes_written() { return stream_bytes_written_; }
+ uint64 stream_bytes_read() const { return stream_bytes_read_; }
+ uint64 stream_bytes_written() const { return stream_bytes_written_; }
+
+ QuicVersion version() const;
+
+ void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; }
+ void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; }
+
+ // Adjust our flow control windows according to new offset in |frame|.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame);
+
+ int num_frames_received() const;
+
+ int num_duplicate_frames_received() const;
- QuicVersion version();
+ QuicFlowController* flow_controller() { return &flow_controller_; }
+
+ // Called when we see a frame which could increase the highest offset.
+ // Returns true if the highest offset did increase.
+ bool MaybeIncreaseHighestReceivedOffset(uint64 new_offset);
+ // Called when bytese are sent to the peer.
+ void AddBytesSent(uint64 bytes);
+ // Called by the stream sequencer as bytes are consumed from the buffer.
+ // If our receive window has dropped below the threshold, then send a
+ // WINDOW_UPDATE frame.
+ void AddBytesConsumed(uint64 bytes);
+
+ // Returns true if the stream is flow control blocked, by the stream flow
+ // control window or the connection flow control window.
+ bool IsFlowControlBlocked();
+
+ // Returns true if we have received either a RST or a FIN - either of which
+ // gives a definitive number of bytes which the peer has sent. If this is not
+ // true on stream termination the session must keep track of the stream's byte
+ // offset until a definitive final value arrives.
+ bool HasFinalReceivedByteOffset() const {
+ return fin_received_ || rst_received_;
+ }
protected:
// Sends as much of 'data' to the connection as the connection will consume,
// and then buffers any remaining data in queued_data_.
- void WriteOrBufferData(base::StringPiece data, bool fin);
+ void WriteOrBufferData(
+ base::StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
// Sends as many bytes in the first |count| buffers of |iov| to the connection
// as the connection will consume.
@@ -104,26 +140,53 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+ // Helper method that returns FecProtection to use for writes to the session.
+ FecProtection GetFecProtection();
+
// Close the read side of the socket. Further frames will not be accepted.
virtual void CloseReadSide();
// Close the write side of the socket. Further writes will fail.
void CloseWriteSide();
- bool HasBufferedData();
+ bool HasBufferedData() const;
- bool fin_buffered() { return fin_buffered_; }
+ bool fin_buffered() const { return fin_buffered_; }
+ void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; }
+
+ const QuicSession* session() const { return session_; }
QuicSession* session() { return session_; }
const QuicStreamSequencer* sequencer() const { return &sequencer_; }
QuicStreamSequencer* sequencer() { return &sequencer_; }
+ void DisableFlowControl() {
+ flow_controller_.Disable();
+ }
+
private:
friend class test::ReliableQuicStreamPeer;
friend class QuicStreamUtils;
+ class ProxyAckNotifierDelegate;
+
+ struct PendingData {
+ PendingData(string data_in,
+ scoped_refptr<ProxyAckNotifierDelegate> delegate_in);
+ ~PendingData();
- std::list<string> queued_data_;
+ string data;
+ // Delegate that should be notified when the pending data is acked.
+ // Can be nullptr.
+ scoped_refptr<ProxyAckNotifierDelegate> delegate;
+ };
+
+ // Calls MaybeSendBlocked on our flow controller, and connection level flow
+ // controller. If we are flow control blocked, marks this stream as write
+ // blocked.
+ void MaybeSendBlocked();
+
+ std::list<PendingData> queued_data_;
QuicStreamSequencer sequencer_;
QuicStreamId id_;
@@ -149,9 +212,28 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin_buffered_;
bool fin_sent_;
+ // True if this stream has received (and the sequencer has accepted) a
+ // StreamFrame with the FIN set.
+ bool fin_received_;
+
+ // In combination with fin_sent_, used to ensure that a FIN and/or a RST is
+ // always sent before stream termination.
+ bool rst_sent_;
+
+ // True if this stream has received a RST stream frame.
+ bool rst_received_;
+
+ // FEC policy to be used for this stream.
+ FecPolicy fec_policy_;
+
// True if the session this stream is running under is a server session.
bool is_server_;
+ QuicFlowController flow_controller_;
+
+ // The connection level flow controller. Not owned.
+ QuicFlowController* connection_flow_controller_;
+
DISALLOW_COPY_AND_ASSIGN(ReliableQuicStream);
};
diff --git a/chromium/net/quic/reliable_quic_stream_test.cc b/chromium/net/quic/reliable_quic_stream_test.cc
index 7e82f533142..b673a4eef5d 100644
--- a/chromium/net/quic/reliable_quic_stream_test.cc
+++ b/chromium/net/quic/reliable_quic_stream_test.cc
@@ -6,22 +6,30 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
using base::StringPiece;
using std::min;
-using testing::_;
+using testing::CreateFunctor;
using testing::InSequence;
+using testing::Invoke;
using testing::Return;
using testing::SaveArg;
-using testing::StrEq;
using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
namespace net {
namespace test {
@@ -30,7 +38,6 @@ namespace {
const char kData1[] = "FooAndBar";
const char kData2[] = "EepAndBaz";
const size_t kDataLen = 9;
-const QuicGuid kStreamId = 3;
const bool kIsServer = true;
const bool kShouldProcessData = true;
@@ -56,8 +63,7 @@ class TestStream : public ReliableQuicStream {
using ReliableQuicStream::WriteOrBufferData;
using ReliableQuicStream::CloseReadSide;
using ReliableQuicStream::CloseWriteSide;
-
- const string& data() const { return data_; }
+ using ReliableQuicStream::OnClose;
private:
bool should_process_data_;
@@ -66,7 +72,10 @@ class TestStream : public ReliableQuicStream {
class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> {
public:
- ReliableQuicStreamTest() {
+ ReliableQuicStreamTest()
+ : initial_flow_control_window_bytes_(kMaxPacketSize),
+ zero_(QuicTime::Delta::Zero()),
+ supported_versions_(QuicSupportedVersions()) {
headers_[":host"] = "www.google.com";
headers_[":path"] = "/index.hml";
headers_[":scheme"] = "https";
@@ -96,71 +105,86 @@ class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> {
"JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
}
+ void set_supported_versions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
void Initialize(bool stream_should_process_data) {
- connection_ = new StrictMock<MockConnection>(kIsServer);
+ connection_ =
+ new StrictMock<MockConnection>(kIsServer, supported_versions_);
session_.reset(new StrictMock<MockSession>(connection_));
- stream_.reset(new TestStream(kStreamId, session_.get(),
- stream_should_process_data));
- stream2_.reset(new TestStream(kStreamId + 2, session_.get(),
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+
+ stream_.reset(new TestStream(kHeadersStreamId, session_.get(),
stream_should_process_data));
- compressor_.reset(new QuicSpdyCompressor());
- decompressor_.reset(new QuicSpdyDecompressor);
write_blocked_list_ =
- QuicSessionPeer::GetWriteblockedStreams(session_.get());
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
+ }
+
+ bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_.get()); }
+ bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_.get()); }
+
+ void set_initial_flow_control_window_bytes(uint32 val) {
+ initial_flow_control_window_bytes_ = val;
+ }
+
+ bool HasWriteBlockedStreams() {
+ return write_blocked_list_->HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_list_->HasWriteBlockedDataStreams();
}
protected:
MockConnection* connection_;
scoped_ptr<MockSession> session_;
scoped_ptr<TestStream> stream_;
- scoped_ptr<TestStream> stream2_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
- scoped_ptr<QuicSpdyDecompressor> decompressor_;
SpdyHeaderBlock headers_;
- WriteBlockedList<QuicStreamId>* write_blocked_list_;
+ QuicWriteBlockedList* write_blocked_list_;
+ uint32 initial_flow_control_window_bytes_;
+ QuicTime::Delta zero_;
+ QuicVersionVector supported_versions_;
};
TEST_F(ReliableQuicStreamTest, WriteAllData) {
Initialize(kShouldProcessData);
- connection_->options()->max_packet_length =
- 1 + QuicPacketCreator::StreamFramePacketOverhead(
- connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
- stream_->WriteOrBufferData(kData1, false);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_FALSE(HasWriteBlockedStreams());
}
-// TODO(rtenneti): Death tests crash on OS_ANDROID.
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID)
TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) {
Initialize(kShouldProcessData);
// Write no data and no fin. If we consume nothing we should not be write
// blocked.
- EXPECT_DEBUG_DEATH({
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(0, false)));
- stream_->WriteOrBufferData(StringPiece(), false);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
- }, "");
+ EXPECT_DFATAL(stream_->WriteOrBufferData(StringPiece(), false, NULL), "");
+ EXPECT_FALSE(HasWriteBlockedStreams());
}
-#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID)
TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) {
Initialize(kShouldProcessData);
// 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_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(1, false)));
- stream_->WriteOrBufferData(StringPiece(kData1, 2), false);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), false, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
-
TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) {
Initialize(kShouldProcessData);
@@ -168,9 +192,9 @@ 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_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(2, false)));
- stream_->WriteOrBufferData(StringPiece(kData1, 2), true);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(2, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), true, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
@@ -179,38 +203,112 @@ 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_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(0, false)));
- stream_->WriteOrBufferData(StringPiece(), true);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(StringPiece(), true, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
TEST_F(ReliableQuicStreamTest, WriteOrBufferData) {
Initialize(kShouldProcessData);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
- connection_->options()->max_packet_length =
- 1 + QuicPacketCreator::StreamFramePacketOverhead(
- connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).WillOnce(
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen - 1, false)));
- stream_->WriteOrBufferData(kData1, false);
- EXPECT_TRUE(write_blocked_list_->HasWriteBlockedStreams());
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
// Queue a bytes_consumed write.
- stream_->WriteOrBufferData(kData2, false);
+ stream_->WriteOrBufferData(kData2, false, NULL);
// Make sure we get the tail of the first write followed by the bytes_consumed
InSequence s;
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
WillOnce(Return(QuicConsumedData(1, false)));
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
stream_->OnCanWrite();
// And finally the end of the bytes_consumed.
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, NULL);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, NULL);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
WillOnce(Return(QuicConsumedData(2, true)));
stream_->OnCanWrite();
}
@@ -227,6 +325,363 @@ TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) {
EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
}
+TEST_F(ReliableQuicStreamTest, RstAlwaysSentIfNoFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if no FIN has been sent, we send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with no FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), false, NULL);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we send a RST.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, RstNotSentIfFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a FIN has been sent, we don't also send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, true)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), true, NULL);
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we do not send a RST.
+ stream_->OnClose();
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, OnlySendOneRst) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a stream sends a RST, it doesn't send an additional RST during
+ // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...)
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Reset the stream.
+ const int expected_resets = 1;
+ EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets);
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+
+ // Now close the stream (any further resets being sent would break the
+ // expectation above).
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, StreamFlowControlMultipleWindowUpdates) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ set_initial_flow_control_window_bytes(1000);
+
+ Initialize(kShouldProcessData);
+
+ // If we receive multiple WINDOW_UPDATES (potentially out of order), then we
+ // want to make sure we latch the largest offset we see.
+
+ // Initially should be default.
+ EXPECT_EQ(
+ initial_flow_control_window_bytes_,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Check a single WINDOW_UPDATE results in correct offset.
+ QuicWindowUpdateFrame window_update_1(stream_->id(), 1234);
+ stream_->OnWindowUpdateFrame(window_update_1);
+ EXPECT_EQ(
+ window_update_1.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Now send a few more WINDOW_UPDATES and make sure that only the largest is
+ // remembered.
+ QuicWindowUpdateFrame window_update_2(stream_->id(), 1);
+ QuicWindowUpdateFrame window_update_3(stream_->id(), 9999);
+ QuicWindowUpdateFrame window_update_4(stream_->id(), 5678);
+ stream_->OnWindowUpdateFrame(window_update_2);
+ stream_->OnWindowUpdateFrame(window_update_3);
+ stream_->OnWindowUpdateFrame(window_update_4);
+ EXPECT_EQ(
+ window_update_3.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+}
+
+TEST_F(ReliableQuicStreamTest, StreamFlowControlShouldNotBlockInLessThanQ017) {
+ // TODO(rjshade): Remove this test when we no longer have any versions <
+ // QUIC_VERSION_17.
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Make sure we are using a version which does not support flow control.
+ QuicVersion kTestQuicVersions[] = {QUIC_VERSION_16};
+ QuicVersionVector versions;
+ for (size_t i = 0; i < arraysize(kTestQuicVersions); ++i) {
+ versions.push_back(kTestQuicVersions[i]);
+ }
+ set_supported_versions(versions);
+
+ // Peer is not talking QUIC_VERSION_17 so assumes that it can send a zero
+ // length flow control receive window with no consequences.
+ set_initial_flow_control_window_bytes(0);
+
+ Initialize(kShouldProcessData);
+
+ // The stream should _not_ be flow control blocked, because we are not talking
+ // a version which has flow control enabled.
+ EXPECT_FALSE(stream_->flow_controller()->IsBlocked());
+}
+
+void SaveProxyAckNotifierDelegate(
+ scoped_refptr<QuicAckNotifier::DelegateInterface>* delegate_out,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ *delegate_out = delegate;
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kFirstWriteSize = 100;
+ const int kSecondWriteSize = 50;
+ const int kLastWriteSize = kDataSize - kFirstWriteSize - kSecondWriteSize;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ if (FLAGS_enable_quic_connection_flow_control_2) {
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ }
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kFirstWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kSecondWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // No ack expected for an empty write.
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->OnCanWrite();
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kLastWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // There were two writes, so OnAckNotification is not propagated
+ // until the third Ack arrives.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+
+ // The arguments to delegate->OnAckNotification are the sum of the
+ // arguments to proxy_delegate OnAckNotification calls.
+ EXPECT_CALL(*delegate, OnAckNotification(111, 222, 333, 444, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when packets are acked before the
+// WritevData call that sends out the last byte.
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kInitialWriteSize = 100;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ if (FLAGS_enable_quic_connection_flow_control_2) {
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ }
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kInitialWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Handle the ack of the first write.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate = NULL;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
+ DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataSize - kInitialWriteSize, false))));
+ stream_->OnCanWrite();
+
+ // Handle the ack for the second write.
+ EXPECT_CALL(*delegate, OnAckNotification(101, 202, 303, 404, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData does not buffer.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferNoBuffer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_FALSE(HasWriteBlockedStreams());
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData buffers all the data.
+TEST_F(ReliableQuicStreamTest, BufferOnWriteAndBufferDataWithAckNotifer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->OnCanWrite();
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData when the FIN is
+// sent out in a different packet.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, false))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(0, true))));
+ stream_->OnCanWrite();
+
+ // Handle the acks.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ EXPECT_CALL(*delegate, OnAckNotification(11, 22, 33, 44, zero_));
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+}
+
+// Verify that when we receive a packet which violates flow control (i.e. sends
+// too much data on the stream) that the stream sequencer never sees this frame,
+// as we check for violation and close the connection early.
+TEST_F(ReliableQuicStreamTest,
+ StreamSequencerNeverSeesPacketsViolatingFlowControl) {
+ ValueRestore<bool> old_stream_flag(
+ &FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_connection_flag(
+ &FLAGS_enable_quic_connection_flow_control_2, true);
+
+ Initialize(kShouldProcessData);
+
+ // Receive a stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicStreamFrame frame(stream_->id(), false,
+ kInitialSessionFlowControlWindowForTest + 1,
+ MakeIOVector("."));
+ EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ EXPECT_FALSE(stream_->OnStreamFrame(frame));
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_no_fin);
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromRst) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/spdy_utils.cc b/chromium/net/quic/spdy_utils.cc
index 350819e9143..c0894d54e6d 100644
--- a/chromium/net/quic/spdy_utils.cc
+++ b/chromium/net/quic/spdy_utils.cc
@@ -16,7 +16,7 @@ namespace net {
// static
string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
int length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
- SpdyFrameBuilder builder(length);
+ SpdyFrameBuilder builder(length, SPDY3);
SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
scoped_ptr<SpdyFrame> block(builder.take());
return string(block->data(), length);
diff --git a/chromium/net/quic/spdy_utils.h b/chromium/net/quic/spdy_utils.h
index ffc78f08da2..14d33002216 100644
--- a/chromium/net/quic/spdy_utils.h
+++ b/chromium/net/quic/spdy_utils.h
@@ -16,6 +16,9 @@ class NET_EXPORT_PRIVATE SpdyUtils {
public:
static std::string SerializeUncompressedHeaders(
const SpdyHeaderBlock& headers);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SpdyUtils);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/crypto_test_utils.cc b/chromium/net/quic/test_tools/crypto_test_utils.cc
index a6b6cc1ed85..0dbf906c868 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils.cc
@@ -15,6 +15,7 @@
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
@@ -30,6 +31,9 @@ namespace test {
namespace {
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
// CryptoFramerVisitor is a framer visitor that records handshake messages.
class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
public:
@@ -37,9 +41,7 @@ class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
: error_(false) {
}
- virtual void OnError(CryptoFramer* framer) OVERRIDE {
- error_ = true;
- }
+ virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE {
@@ -67,7 +69,7 @@ void MovePackets(PacketSavingConnection* source_conn,
size_t *inout_packet_index,
QuicCryptoStream* dest_stream,
PacketSavingConnection* dest_conn) {
- SimpleQuicFramer framer;
+ SimpleQuicFramer framer(source_conn->supported_versions());
CryptoFramer crypto_framer;
CryptoFramerVisitor crypto_visitor;
@@ -133,7 +135,8 @@ CryptoTestUtils::FakeClientOptions::FakeClientOptions()
int CryptoTestUtils::HandshakeWithFakeServer(
PacketSavingConnection* client_conn,
QuicCryptoClientStream* client) {
- PacketSavingConnection* server_conn = new PacketSavingConnection(true);
+ PacketSavingConnection* server_conn =
+ new PacketSavingConnection(true, client_conn->supported_versions());
TestSession server_session(server_conn, DefaultQuicConfig());
QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING,
@@ -162,7 +165,7 @@ int CryptoTestUtils::HandshakeWithFakeClient(
QuicCryptoServerStream* server,
const FakeClientOptions& options) {
PacketSavingConnection* client_conn = new PacketSavingConnection(false);
- TestSession client_session(client_conn, DefaultQuicConfig());
+ TestClientSession client_session(client_conn, DefaultQuicConfig());
QuicCryptoClientConfig crypto_config;
client_session.config()->SetDefaults();
@@ -172,9 +175,11 @@ int CryptoTestUtils::HandshakeWithFakeClient(
// crypto_config.SetProofVerifier(ProofVerifierForTesting());
// }
if (options.channel_id_enabled) {
- crypto_config.SetChannelIDSigner(ChannelIDSignerForTesting());
+ crypto_config.SetChannelIDSource(ChannelIDSourceForTesting());
}
- QuicCryptoClientStream client("test.example.com", &client_session,
+ QuicServerId server_id(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientStream client(server_id, &client_session, NULL,
&crypto_config);
client_session.SetCryptoStream(&client);
@@ -186,8 +191,13 @@ int CryptoTestUtils::HandshakeWithFakeClient(
CompareClientAndServerKeys(&client, server);
if (options.channel_id_enabled) {
- EXPECT_EQ(crypto_config.channel_id_signer()->GetKeyForHostname(
- "test.example.com"),
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ crypto_config.channel_id_source()->GetChannelIDKey(kServerHostname,
+ &channel_id_key,
+ NULL);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
server->crypto_negotiated_params().channel_id);
}
@@ -230,6 +240,7 @@ void CryptoTestUtils::CommunicateHandshakeMessages(
}
}
+// static
pair<size_t, size_t> CryptoTestUtils::AdvanceHandshake(
PacketSavingConnection* a_conn,
QuicCryptoStream* a,
@@ -486,7 +497,7 @@ CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag,
valuestr++;
len--;
- CHECK(len % 2 == 0);
+ CHECK_EQ(0u, len % 2);
scoped_ptr<uint8[]> buf(new uint8[len/2]);
for (size_t i = 0; i < len/2; i++) {
diff --git a/chromium/net/quic/test_tools/crypto_test_utils.h b/chromium/net/quic/test_tools/crypto_test_utils.h
index 60674a9e63d..a226c85ad9c 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils.h
+++ b/chromium/net/quic/test_tools/crypto_test_utils.h
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -18,10 +19,11 @@
namespace net {
-class ChannelIDSigner;
+class ChannelIDSource;
class CommonCertSets;
class ProofSource;
class ProofVerifier;
+class ProofVerifyContext;
class QuicClock;
class QuicConfig;
class QuicCryptoClientStream;
@@ -46,8 +48,7 @@ class CryptoTestUtils {
bool dont_verify_certs;
// If channel_id_enabled is true then the client will attempt to send a
- // ChannelID. The key will be the same as is returned by
- // ChannelIDSigner's |GetKeyForHostname|.
+ // ChannelID.
bool channel_id_enabled;
};
@@ -95,6 +96,10 @@ class CryptoTestUtils {
// Returns a |ProofVerifier| that uses the QUIC testing root CA.
static ProofVerifier* ProofVerifierForTesting();
+ // Returns a |ProofVerifyContext| that must be used with the verifier
+ // returned by |ProofVerifierForTesting|.
+ static ProofVerifyContext* ProofVerifyContextForTesting();
+
// MockCommonCertSets returns a CommonCertSets that contains a single set with
// hash |hash|, consisting of the certificate |cert| at index |index|.
static CommonCertSets* MockCommonCertSets(base::StringPiece cert,
@@ -127,13 +132,17 @@ class CryptoTestUtils {
static CryptoHandshakeMessage BuildMessage(const char* message_tag,
va_list ap);
- // ChannelIDSignerForTesting returns a ChannelIDSigner that generates keys
- // deterministically based on the hostname given in the Sign call.
- static ChannelIDSigner* ChannelIDSignerForTesting();
+ // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
+ // deterministically based on the hostname given in the GetChannelIDKey call.
+ // This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
+ // method never returns QUIC_PENDING.
+ static ChannelIDSource* ChannelIDSourceForTesting();
private:
static void CompareClientAndServerKeys(QuicCryptoClientStream* client,
QuicCryptoServerStream* server);
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoTestUtils);
};
} // namespace test
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
index 8aaef425dac..9deee8388b7 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
@@ -22,14 +22,14 @@ class TestProofVerifierChromium : public ProofVerifierChromium {
public:
TestProofVerifierChromium(CertVerifier* cert_verifier,
const std::string& cert_file)
- : ProofVerifierChromium(cert_verifier, BoundNetLog()),
+ : ProofVerifierChromium(cert_verifier),
cert_verifier_(cert_verifier) {
// Load and install the root for the validated chain.
scoped_refptr<X509Certificate> root_cert =
ImportCertFromFile(GetTestCertsDirectory(), cert_file);
scoped_root_.Reset(root_cert.get());
}
- virtual ~TestProofVerifierChromium() { }
+ virtual ~TestProofVerifierChromium() {}
private:
ScopedTestRoot scoped_root_;
@@ -48,6 +48,11 @@ ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() {
return proof_verifier;
}
+// static
+ProofVerifyContext* CryptoTestUtils::ProofVerifyContextForTesting() {
+ return new ProofVerifyContextChromium(BoundNetLog());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
index 88c87679b93..c5aede89efd 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
@@ -20,31 +20,18 @@ namespace net {
namespace test {
-// TODO(rtenneti): Implement NSS support ChannelIDSigner. Convert Sign() to be
-// asynchronous using completion callback. After porting TestChannelIDSigner,
-// implement real ChannelIDSigner.
-class TestChannelIDSigner : public ChannelIDSigner {
+// TODO(rtenneti): Convert Sign() to be asynchronous using a completion
+// callback.
+class TestChannelIDKey : public ChannelIDKey {
public:
- virtual ~TestChannelIDSigner() {
- STLDeleteValues(&hostname_to_key_);
- }
-
- // ChannelIDSigner implementation.
-
- virtual bool Sign(const string& hostname,
- StringPiece signed_data,
- string* out_key,
- string* out_signature) OVERRIDE {
- crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname);
- if (!ecdsa_keypair) {
- return false;
- }
+ explicit TestChannelIDKey(crypto::ECPrivateKey* ecdsa_keypair)
+ : ecdsa_keypair_(ecdsa_keypair) {}
+ virtual ~TestChannelIDKey() {}
- *out_key = SerializeKey(ecdsa_keypair->public_key());
- if (out_key->empty()) {
- return false;
- }
+ // ChannelIDKey implementation.
+ virtual bool Sign(StringPiece signed_data,
+ string* out_signature) const OVERRIDE {
unsigned char hash_buf[SHA256_LENGTH];
SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) };
@@ -77,19 +64,49 @@ class TestChannelIDSigner : public ChannelIDSigner {
kSignatureLength
};
- if (PK11_Sign(ecdsa_keypair->key(), &sig_item, &hash_item) != SECSuccess) {
+ if (PK11_Sign(ecdsa_keypair_->key(), &sig_item, &hash_item) != SECSuccess) {
return false;
}
*out_signature = signature;
return true;
}
- virtual string GetKeyForHostname(const string& hostname) OVERRIDE {
- crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname);
- if (!ecdsa_keypair) {
+ virtual string SerializeKey() const OVERRIDE {
+ const SECKEYPublicKey* public_key = ecdsa_keypair_->public_key();
+
+ // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for
+ // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const unsigned int kExpectedKeyLength = 65;
+
+ const unsigned char* const data = public_key->u.ec.publicValue.data;
+ const unsigned int len = public_key->u.ec.publicValue.len;
+ if (len != kExpectedKeyLength || data[0] != 0x04) {
return "";
}
- return SerializeKey(ecdsa_keypair->public_key());
+
+ string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1);
+ return key;
+ }
+
+ private:
+ crypto::ECPrivateKey* ecdsa_keypair_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {
+ STLDeleteValues(&hostname_to_key_);
+ }
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
}
private:
@@ -109,28 +126,12 @@ class TestChannelIDSigner : public ChannelIDSigner {
return keypair;
}
- static string SerializeKey(const SECKEYPublicKey* public_key) {
- // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for
- // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
- // elements as 32-byte, big-endian numbers.
- static const unsigned int kExpectedKeyLength = 65;
-
- const unsigned char* const data = public_key->u.ec.publicValue.data;
- const unsigned int len = public_key->u.ec.publicValue.len;
- if (len != kExpectedKeyLength || data[0] != 0x04) {
- return "";
- }
-
- string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1);
- return key;
- }
-
HostnameToKeyMap hostname_to_key_;
};
// static
-ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() {
- return new TestChannelIDSigner();
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
index bb08a044aa4..388d257001f 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
@@ -30,31 +30,22 @@ namespace net {
namespace test {
-class TestChannelIDSigner : public ChannelIDSigner {
+class TestChannelIDKey : public ChannelIDKey {
public:
- virtual ~TestChannelIDSigner() { }
+ explicit TestChannelIDKey(EVP_PKEY* ecdsa_key) : ecdsa_key_(ecdsa_key) {}
+ virtual ~TestChannelIDKey() OVERRIDE {}
- // ChannelIDSigner implementation.
-
- virtual bool Sign(const string& hostname,
- StringPiece signed_data,
- string* out_key,
- string* out_signature) OVERRIDE {
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key(
- HostnameToKey(hostname));
-
- *out_key = SerializeKey(ecdsa_key.get());
- if (out_key->empty()) {
- return false;
- }
+ // ChannelIDKey implementation.
+ virtual bool Sign(StringPiece signed_data,
+ string* out_signature) const OVERRIDE {
EVP_MD_CTX md_ctx;
EVP_MD_CTX_init(&md_ctx);
crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp>
md_ctx_cleanup(&md_ctx);
if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL,
- ecdsa_key.get()) != 1) {
+ ecdsa_key_.get()) != 1) {
return false;
}
@@ -94,10 +85,40 @@ class TestChannelIDSigner : public ChannelIDSigner {
return true;
}
- virtual string GetKeyForHostname(const string& hostname) OVERRIDE {
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key(
- HostnameToKey(hostname));
- return SerializeKey(ecdsa_key.get());
+ virtual string SerializeKey() const OVERRIDE {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), NULL);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8 buf[kExpectedKeyLength];
+ uint8* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+ }
+
+ private:
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {}
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
}
private:
@@ -143,29 +164,11 @@ class TestChannelIDSigner : public ChannelIDSigner {
return pkey.release();
}
-
- static string SerializeKey(EVP_PKEY* key) {
- // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
- // key, is 0x04 (meaning uncompressed) followed by the x and y field
- // elements as 32-byte, big-endian numbers.
- static const int kExpectedKeyLength = 65;
-
- int len = i2d_PublicKey(key, NULL);
- if (len != kExpectedKeyLength) {
- return "";
- }
-
- uint8 buf[kExpectedKeyLength];
- uint8* derp = buf;
- i2d_PublicKey(key, &derp);
-
- return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
- }
};
// static
-ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() {
- return new TestChannelIDSigner();
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
index eec16b16e8d..b14a1180296 100644
--- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
+++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
@@ -29,7 +29,7 @@ void DelayedVerifyStrikeRegisterClient::VerifyNonceIsValidAndUnique(
QuicWallTime now,
ResultCallback* cb) {
if (delay_verifications_) {
- pending_verifications_.push_back(VerifyArgs(nonce.as_string(), now, cb));
+ pending_verifications_.push_back(VerifyArgs(nonce, now, cb));
} else {
LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(nonce, now, cb);
}
diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
index 69de1d077d3..6ae7ba1ee2a 100644
--- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
+++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
@@ -43,7 +43,9 @@ class DelayedVerifyStrikeRegisterClient : public LocalStrikeRegisterClient {
VerifyArgs(base::StringPiece in_nonce,
QuicWallTime in_now,
ResultCallback* in_cb)
- : nonce(in_nonce.as_string()), now(in_now), cb(in_cb) {
+ : nonce(in_nonce.as_string()),
+ now(in_now),
+ cb(in_cb) {
}
std::string nonce;
diff --git a/chromium/net/quic/test_tools/mock_clock.h b/chromium/net/quic/test_tools/mock_clock.h
index 5c9631ae84c..d6e490f8961 100644
--- a/chromium/net/quic/test_tools/mock_clock.h
+++ b/chromium/net/quic/test_tools/mock_clock.h
@@ -16,7 +16,6 @@ namespace net {
class MockClock : public QuicClock {
public:
MockClock();
-
virtual ~MockClock();
void AdvanceTime(QuicTime::Delta delta);
@@ -31,6 +30,8 @@ class MockClock : public QuicClock {
private:
QuicTime now_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockClock);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
index bfeb06f0629..a4b3ab0289d 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -3,17 +3,25 @@
// found in the LICENSE file.
#include "net/quic/test_tools/mock_crypto_client_stream.h"
+
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_server_id.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
MockCryptoClientStream::MockCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config,
- HandshakeMode handshake_mode)
- : QuicCryptoClientStream(server_hostname, session, crypto_config),
- handshake_mode_(handshake_mode) {
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details)
+ : QuicCryptoClientStream(server_id, session, verify_context,
+ crypto_config),
+ handshake_mode_(handshake_mode),
+ proof_verify_details_(proof_verify_details) {
}
MockCryptoClientStream::~MockCryptoClientStream() {
@@ -29,6 +37,8 @@ bool MockCryptoClientStream::CryptoConnect() {
case ZERO_RTT: {
encryption_established_ = true;
handshake_confirmed_ = false;
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_INITIAL);
session()->OnCryptoHandshakeEvent(
QuicSession::ENCRYPTION_FIRST_ESTABLISHED);
break;
@@ -37,7 +47,14 @@ bool MockCryptoClientStream::CryptoConnect() {
case CONFIRM_HANDSHAKE: {
encryption_established_ = true;
handshake_confirmed_ = true;
+ crypto_negotiated_params_.key_exchange = kC255;
+ crypto_negotiated_params_.aead = kAESG;
+ if (proof_verify_details_) {
+ client_session()->OnProofVerifyDetailsAvailable(*proof_verify_details_);
+ }
SetConfigNegotiated();
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_FORWARD_SECURE);
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
break;
}
@@ -66,7 +83,7 @@ void MockCryptoClientStream::SetConfigNegotiated() {
QuicTagVector cgst;
cgst.push_back(kINAR);
cgst.push_back(kQBIC);
- session()->config()->set_congestion_control(cgst, kQBIC);
+ session()->config()->set_congestion_feedback(cgst, kQBIC);
session()->config()->set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs));
@@ -77,9 +94,13 @@ void MockCryptoClientStream::SetConfigNegotiated() {
session()->config()->ToHandshakeMessage(&msg);
string error_details;
const QuicErrorCode error =
- session()->config()->ProcessClientHello(msg, &error_details);
+ session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
ASSERT_EQ(QUIC_NO_ERROR, error);
ASSERT_TRUE(session()->config()->negotiated());
}
+QuicClientSessionBase* MockCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.h b/chromium/net/quic/test_tools/mock_crypto_client_stream.h
index ada1b6883ba..e940a1e940c 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream.h
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.h
@@ -14,6 +14,8 @@
namespace net {
+class QuicServerId;
+
class MockCryptoClientStream : public QuicCryptoClientStream {
public:
// HandshakeMode enumerates the handshake mode MockCryptoClientStream should
@@ -34,10 +36,12 @@ class MockCryptoClientStream : public QuicCryptoClientStream {
};
MockCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config,
- HandshakeMode handshake_mode);
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details_);
virtual ~MockCryptoClientStream();
// CryptoFramerVisitorInterface implementation.
@@ -55,6 +59,11 @@ class MockCryptoClientStream : public QuicCryptoClientStream {
private:
void SetConfigNegotiated();
+ QuicClientSessionBase* client_session();
+
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStream);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
index e54fb41e2f5..302ed07e9aa 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
@@ -5,7 +5,9 @@
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "base/lazy_instance.h"
+#include "net/quic/quic_client_session.h"
#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_server_id.h"
using std::string;
@@ -13,16 +15,18 @@ namespace net {
MockCryptoClientStreamFactory::MockCryptoClientStreamFactory()
: handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE),
- last_stream_(NULL) {
+ last_stream_(NULL),
+ proof_verify_details_(NULL) {
}
QuicCryptoClientStream*
MockCryptoClientStreamFactory::CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) {
- last_stream_ = new MockCryptoClientStream(server_hostname, session,
- crypto_config, handshake_mode_);
+ last_stream_ = new MockCryptoClientStream(
+ server_id, session, NULL, crypto_config, handshake_mode_,
+ proof_verify_details_);
return last_stream_;
}
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
index 9d056cbc72d..721ec258cee 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
@@ -13,15 +13,16 @@
namespace net {
+class QuicServerId;
+
class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
public:
MockCryptoClientStreamFactory();
-
virtual ~MockCryptoClientStreamFactory() {}
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) OVERRIDE;
void set_handshake_mode(
@@ -29,6 +30,11 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
handshake_mode_ = handshake_mode;
}
+ void set_proof_verify_details(
+ const ProofVerifyDetails* proof_verify_details) {
+ proof_verify_details_ = proof_verify_details;
+ }
+
MockCryptoClientStream* last_stream() const {
return last_stream_;
}
@@ -36,6 +42,9 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
private:
MockCryptoClientStream::HandshakeMode handshake_mode_;
MockCryptoClientStream* last_stream_;
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStreamFactory);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 00000000000..f3f41a5ae10
--- /dev/null
+++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_quic_dispatcher.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicConnectionHelperInterface* helper)
+ : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), helper) {
+}
+
+MockQuicDispatcher::~MockQuicDispatcher() {
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 00000000000..93ecda4ce6d
--- /dev/null
+++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_dispatcher.h"
+#include "net/quic/quic_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace test {
+
+class MockQuicDispatcher : public QuicDispatcher {
+ public:
+ MockQuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~MockQuicDispatcher();
+
+ MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/chromium/net/quic/test_tools/mock_random.h b/chromium/net/quic/test_tools/mock_random.h
index 3a3c87f4497..53b24b74eeb 100644
--- a/chromium/net/quic/test_tools/mock_random.h
+++ b/chromium/net/quic/test_tools/mock_random.h
@@ -14,7 +14,7 @@ class MockRandom : public QuicRandom {
public:
// Initializes base_ to 0xDEADBEEF.
MockRandom();
- MockRandom(uint32 base);
+ explicit MockRandom(uint32 base);
// QuicRandom:
// Fills the |data| buffer with a repeating byte, initially 'r'.
@@ -32,6 +32,8 @@ class MockRandom : public QuicRandom {
private:
uint32 base_;
uint8 increment_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRandom);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_config_peer.cc b/chromium/net/quic/test_tools/quic_config_peer.cc
new file mode 100644
index 00000000000..798209c1855
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_config_peer.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_config_peer.h"
+
+#include "net/quic/quic_config.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicConfigPeer::SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window) {
+ config->initial_congestion_window_.SetReceivedValue(initial_window);
+}
+
+// static
+void QuicConfigPeer::SetReceivedLossDetection(QuicConfig* config,
+ QuicTag loss_detection) {
+ config->loss_detection_.SetReceivedValue(loss_detection);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes) {
+ config->initial_flow_control_window_bytes_.SetReceivedValue(window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ QuicConfig* config, uint32 window_bytes) {
+ config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ QuicConfig* config, uint32 window_bytes) {
+ config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_config_peer.h b/chromium/net/quic/test_tools/quic_config_peer.h
new file mode 100644
index 00000000000..53abb987635
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_config_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicConfig;
+
+namespace test {
+
+class QuicConfigPeer {
+ public:
+ static void SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window);
+
+ static void SetReceivedLossDetection(QuicConfig* config,
+ QuicTag loss_detection);
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ static void SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicConfigPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_connection_peer.cc b/chromium/net/quic/test_tools/quic_connection_peer.cc
index 54410b3bec1..e51fb52480a 100644
--- a/chromium/net/quic/test_tools/quic_connection_peer.cc
+++ b/chromium/net/quic/test_tools/quic_connection_peer.cc
@@ -11,8 +11,8 @@
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
-#include "net/quic/test_tools/quic_test_writer.h"
namespace net {
namespace test {
@@ -51,7 +51,20 @@ QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor(
// static
QuicPacketCreator* QuicConnectionPeer::GetPacketCreator(
QuicConnection* connection) {
- return &connection->packet_creator_;
+ return QuicPacketGeneratorPeer::GetPacketCreator(
+ &connection->packet_generator_);
+}
+
+// static
+QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator(
+ QuicConnection* connection) {
+ return &connection->packet_generator_;
+}
+
+// static
+QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager(
+ QuicConnection* connection) {
+ return &connection->sent_packet_manager_;
}
// static
@@ -110,17 +123,6 @@ QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash(
}
// static
-bool QuicConnectionPeer::IsWriteBlocked(QuicConnection* connection) {
- return connection->write_blocked_;
-}
-
-// static
-void QuicConnectionPeer::SetIsWriteBlocked(QuicConnection* connection,
- bool write_blocked) {
- connection->write_blocked_ = write_blocked;
-}
-
-// static
bool QuicConnectionPeer::IsServer(QuicConnection* connection) {
return connection->is_server_;
}
@@ -147,7 +149,7 @@ void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
// static
void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
QuicFramer* framer) {
- framer->SwapCryptersForTest(&connection->framer_);
+ QuicFramerPeer::SwapCrypters(framer, &connection->framer_);
}
// static
@@ -173,6 +175,17 @@ QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
}
// static
+QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ return connection->ping_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm(
+ QuicConnection* connection) {
+ return connection->resume_writes_alarm_.get();
+}
+
+// static
QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
QuicConnection* connection) {
return connection->retransmission_alarm_.get();
@@ -184,12 +197,6 @@ QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
}
// static
-QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm(
- QuicConnection* connection) {
- return connection->resume_writes_alarm_.get();
-}
-
-// static
QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) {
return connection->timeout_alarm_.get();
}
@@ -201,9 +208,26 @@ QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) {
// static
void QuicConnectionPeer::SetWriter(QuicConnection* connection,
- QuicTestWriter* writer) {
+ QuicPacketWriter* writer) {
connection->writer_ = writer;
}
+// static
+void QuicConnectionPeer::CloseConnection(QuicConnection* connection) {
+ connection->connected_ = false;
+}
+
+// static
+QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket(
+ QuicConnection* connection) {
+ return connection->connection_close_packet_.get();
+}
+
+// static
+void QuicConnectionPeer::SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions) {
+ connection->framer_.SetSupportedVersions(versions);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_connection_peer.h b/chromium/net/quic/test_tools/quic_connection_peer.h
index fd43661dae5..0cc6fbbdda2 100644
--- a/chromium/net/quic/test_tools/quic_connection_peer.h
+++ b/chromium/net/quic/test_tools/quic_connection_peer.h
@@ -18,18 +18,19 @@ class QuicAlarm;
class QuicConnection;
class QuicConnectionHelperInterface;
class QuicConnectionVisitorInterface;
+class QuicEncryptedPacket;
class QuicFecGroup;
class QuicFramer;
class QuicPacketCreator;
+class QuicPacketGenerator;
class QuicPacketWriter;
class QuicReceivedPacketManager;
+class QuicSentPacketManager;
class ReceiveAlgorithmInterface;
class SendAlgorithmInterface;
namespace test {
-class QuicTestWriter;
-
// Peer to make public a number of otherwise private QuicConnection methods.
class QuicConnectionPeer {
public:
@@ -48,6 +49,11 @@ class QuicConnectionPeer {
static QuicPacketCreator* GetPacketCreator(QuicConnection* connection);
+ static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection);
+
+ static QuicSentPacketManager* GetSentPacketManager(
+ QuicConnection* connection);
+
static QuicReceivedPacketManager* GetReceivedPacketManager(
QuicConnection* connection);
@@ -73,10 +79,6 @@ class QuicConnectionPeer {
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number);
- static bool IsWriteBlocked(QuicConnection* connection);
-
- static void SetIsWriteBlocked(QuicConnection* connection, bool write_blocked);
-
static bool IsServer(QuicConnection* connection);
static void SetIsServer(QuicConnection* connection, bool is_server);
@@ -97,19 +99,27 @@ class QuicConnectionPeer {
static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group);
static QuicAlarm* GetAckAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection);
static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
static QuicAlarm* GetSendAlarm(QuicConnection* connection);
- static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection);
static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection);
static QuicPacketWriter* GetWriter(QuicConnection* connection);
- static void SetWriter(QuicConnection* connection, QuicTestWriter* writer);
+ static void SetWriter(QuicConnection* connection, QuicPacketWriter* writer);
+ static void CloseConnection(QuicConnection* connection);
+ static QuicEncryptedPacket* GetConnectionClosePacket(
+ QuicConnection* connection);
+
+ static void SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions);
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer);
};
} // namespace test
+
} // namespace net
-#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PEER_H_
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_data_stream_peer.h b/chromium/net/quic/test_tools/quic_data_stream_peer.h
index bfaf826dcdc..914b87d8686 100644
--- a/chromium/net/quic/test_tools/quic_data_stream_peer.h
+++ b/chromium/net/quic/test_tools/quic_data_stream_peer.h
@@ -6,7 +6,6 @@
#define NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_
#include "base/basictypes.h"
-#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.cc b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc
new file mode 100644
index 00000000000..35882ede4bf
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+
+#include <list>
+
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicFlowControllerPeer::SetSendWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->send_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetReceiveWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->receive_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetMaxReceiveWindow(
+ QuicFlowController* flow_controller, uint64 window_size) {
+ flow_controller->max_receive_window_ = window_size;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->send_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->SendWindowSize();
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_ -
+ flow_controller->highest_received_byte_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.h b/chromium/net/quic/test_tools/quic_flow_controller_peer.h
new file mode 100644
index 00000000000..213d40d6fa6
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicFlowController;
+
+namespace test {
+
+class QuicFlowControllerPeer {
+ public:
+ static void SetSendWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetReceiveWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetMaxReceiveWindow(QuicFlowController* flow_controller,
+ uint64 window_size);
+
+ static uint64 SendWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 SendWindowSize(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowSize(QuicFlowController* flow_controller);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowControllerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_framer_peer.cc b/chromium/net/quic/test_tools/quic_framer_peer.cc
index e8d43cdc10e..c6464cc70f7 100644
--- a/chromium/net/quic/test_tools/quic_framer_peer.cc
+++ b/chromium/net/quic/test_tools/quic_framer_peer.cc
@@ -20,8 +20,9 @@ QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire(
}
// static
-void QuicFramerPeer::SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid) {
- framer->last_serialized_guid_ = guid;
+void QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicFramer* framer, QuicConnectionId connection_id) {
+ framer->last_serialized_connection_id_ = connection_id;
}
void QuicFramerPeer::SetLastSequenceNumber(
@@ -34,5 +35,26 @@ void QuicFramerPeer::SetIsServer(QuicFramer* framer, bool is_server) {
framer->is_server_ = is_server;
}
+void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) {
+ for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
+ framer1->encrypter_[i].swap(framer2->encrypter_[i]);
+ }
+ framer1->decrypter_.swap(framer2->decrypter_);
+ framer1->alternative_decrypter_.swap(framer2->alternative_decrypter_);
+
+ EncryptionLevel framer2_level = framer2->decrypter_level_;
+ framer2->decrypter_level_ = framer1->decrypter_level_;
+ framer1->decrypter_level_ = framer2_level;
+ framer2_level = framer2->alternative_decrypter_level_;
+ framer2->alternative_decrypter_level_ =
+ framer1->alternative_decrypter_level_;
+ framer1->alternative_decrypter_level_ = framer2_level;
+
+ const bool framer2_latch = framer2->alternative_decrypter_latch_;
+ framer2->alternative_decrypter_latch_ =
+ framer1->alternative_decrypter_latch_;
+ framer1->alternative_decrypter_latch_ = framer2_latch;
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_framer_peer.h b/chromium/net/quic/test_tools/quic_framer_peer.h
index acb45ecb173..9b9c9fa140f 100644
--- a/chromium/net/quic/test_tools/quic_framer_peer.h
+++ b/chromium/net/quic/test_tools/quic_framer_peer.h
@@ -19,12 +19,17 @@ class QuicFramerPeer {
QuicFramer* framer,
QuicSequenceNumberLength sequence_number_length,
QuicPacketSequenceNumber packet_sequence_number);
- static void SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid);
+ static void SetLastSerializedConnectionId(QuicFramer* framer,
+ QuicConnectionId connection_id);
static void SetLastSequenceNumber(
QuicFramer* framer,
QuicPacketSequenceNumber packet_sequence_number);
static void SetIsServer(QuicFramer* framer, bool is_server);
+ // SwapCrypters exchanges the state of the crypters of |framer1| with
+ // |framer2|.
+ static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
index 1acdf244f2f..a8014479478 100644
--- a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
+++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
@@ -33,11 +33,5 @@ QuicSequenceNumberLength QuicPacketCreatorPeer::GetSequenceNumberLength(
return creator->sequence_number_length_;
}
-// static
-void QuicPacketCreatorPeer::SetIsServer(QuicPacketCreator* creator,
- bool is_server) {
- creator->is_server_ = is_server;
-}
-
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.h b/chromium/net/quic/test_tools/quic_packet_creator_peer.h
index 12ae676664b..b4bf8a73ba0 100644
--- a/chromium/net/quic/test_tools/quic_packet_creator_peer.h
+++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.h
@@ -24,8 +24,6 @@ class QuicPacketCreatorPeer {
static QuicSequenceNumberLength GetSequenceNumberLength(
QuicPacketCreator* creator);
- static void SetIsServer(QuicPacketCreator* creator, bool is_server);
-
private:
DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.cc b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc
new file mode 100644
index 00000000000..ad0693a355f
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
+
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_packet_generator.h"
+
+namespace net {
+namespace test {
+
+// static
+QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator(
+ QuicPacketGenerator* generator) {
+ return &generator->packet_creator_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.h b/chromium/net/quic/test_tools/quic_packet_generator_peer.h
new file mode 100644
index 00000000000..0762d1f6c58
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicPacketCreator;
+class QuicPacketGenerator;
+
+namespace test {
+
+class QuicPacketGeneratorPeer {
+ public:
+ static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketGeneratorPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
index d25a209b5f4..2faf84e644e 100644
--- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
+++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
@@ -11,12 +11,12 @@ namespace net {
namespace test {
// static
-void QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
+void QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
QuicReceivedPacketManager* received_packet_manager,
QuicPacketSequenceNumber peer_least_unacked,
QuicPacketEntropyHash entropy_hash) {
- received_packet_manager->RecalculateEntropyHash(peer_least_unacked,
- entropy_hash);
+ received_packet_manager->entropy_tracker_.SetCumulativeEntropyUpTo(
+ peer_least_unacked, entropy_hash);
}
// static
diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
index 4607a0c902d..c3cb2d3bc2b 100644
--- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
+++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
@@ -15,7 +15,7 @@ namespace test {
class QuicReceivedPacketManagerPeer {
public:
- static void RecalculateEntropyHash(
+ static void SetCumulativeEntropyUpTo(
QuicReceivedPacketManager* received_packet_manager,
QuicPacketSequenceNumber peer_least_unacked,
QuicPacketEntropyHash entropy_hash);
diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
index e2e7c02c1a2..786e63c9b47 100644
--- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -5,6 +5,7 @@
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "base/stl_util.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_sent_packet_manager.h"
@@ -13,6 +14,12 @@ namespace net {
namespace test {
// static
+void QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes) {
+ sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes;
+}
+
+// static
void QuicSentPacketManagerPeer::SetSendAlgorithm(
QuicSentPacketManager* sent_packet_manager,
SendAlgorithmInterface* send_algorithm) {
@@ -20,27 +27,52 @@ void QuicSentPacketManagerPeer::SetSendAlgorithm(
}
// static
+const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->loss_algorithm_.get();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector) {
+ sent_packet_manager->loss_algorithm_.reset(loss_detector);
+}
+
+// static
+RttStats* QuicSentPacketManagerPeer::GetRttStats(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->rtt_stats_;
+}
+
+// static
size_t QuicSentPacketManagerPeer::GetNackCount(
const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number) {
- return sent_packet_manager->packet_history_map_.find(
- sequence_number)->second->nack_count();
+ return sent_packet_manager->unacked_packets_.
+ GetTransmissionInfo(sequence_number).nack_count;
}
// static
-QuicTime QuicSentPacketManagerPeer::GetSentTime(
- const QuicSentPacketManager* sent_packet_manager,
- QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(sent_packet_manager->unacked_packets_, sequence_number));
+size_t QuicSentPacketManagerPeer::GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->pending_retransmissions_.size();
+}
- return sent_packet_manager->unacked_packets_
- .find(sequence_number)->second.sent_time;
+// static
+bool QuicSentPacketManagerPeer::HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasInFlightPackets();
}
// static
-QuicTime::Delta QuicSentPacketManagerPeer::rtt(
- QuicSentPacketManager* sent_packet_manager) {
- return sent_packet_manager->rtt_sample_;
+QuicTime QuicSentPacketManagerPeer::GetSentTime(
+ const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK(sent_packet_manager->unacked_packets_.IsUnacked(sequence_number));
+
+ return sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).sent_time;
}
// static
@@ -49,8 +81,8 @@ bool QuicSentPacketManagerPeer::IsRetransmission(
QuicPacketSequenceNumber sequence_number) {
DCHECK(sent_packet_manager->HasRetransmittableFrames(sequence_number));
return sent_packet_manager->HasRetransmittableFrames(sequence_number) &&
- sent_packet_manager->
- unacked_packets_[sequence_number].previous_transmissions != NULL;
+ sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).all_transmissions->size() > 1;
}
// static
@@ -62,5 +94,37 @@ void QuicSentPacketManagerPeer::MarkForRetransmission(
transmission_type);
}
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetRetransmissionDelay();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets();
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ size_t num_unacked_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it =
+ sent_packet_manager->unacked_packets_.begin();
+ it != sent_packet_manager->unacked_packets_.end(); ++it) {
+ if (it->second.retransmittable_frames != NULL) {
+ ++num_unacked_packets;
+ }
+ }
+ return num_unacked_packets;
+}
+
+// static
+QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.bytes_in_flight();
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
index 2ed9b218621..fed80153d77 100644
--- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -16,18 +16,33 @@ namespace test {
class QuicSentPacketManagerPeer {
public:
+ static void SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes);
+
static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager,
SendAlgorithmInterface* send_algorithm);
+ static const LossDetectionInterface* GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector);
+
+ static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager);
+
static size_t GetNackCount(
const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
+ static size_t GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
static QuicTime GetSentTime(const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
- static QuicTime::Delta rtt(QuicSentPacketManager* sent_packet_manager);
-
// Returns true if |sequence_number| is a retransmission of a packet.
static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
@@ -36,6 +51,18 @@ class QuicSentPacketManagerPeer {
QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type);
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicByteCount GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManagerPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_session_peer.cc b/chromium/net/quic/test_tools/quic_session_peer.cc
index c25b42fb216..39663fbb7eb 100644
--- a/chromium/net/quic/test_tools/quic_session_peer.cc
+++ b/chromium/net/quic/test_tools/quic_session_peer.cc
@@ -22,10 +22,28 @@ void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session,
}
// static
-WriteBlockedList<QuicStreamId>* QuicSessionPeer::GetWriteblockedStreams(
+QuicHeadersStream* QuicSessionPeer::GetHeadersStream(QuicSession* session) {
+ return session->headers_stream_.get();
+}
+
+// static
+void QuicSessionPeer::SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream) {
+ session->headers_stream_.reset(headers_stream);
+}
+
+// static
+QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams(
QuicSession* session) {
return &session->write_blocked_streams_;
}
+// static
+QuicDataStream* QuicSessionPeer::GetIncomingDataStream(
+ QuicSession* session,
+ QuicStreamId stream_id) {
+ return session->GetIncomingDataStream(stream_id);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_session_peer.h b/chromium/net/quic/test_tools/quic_session_peer.h
index fb4529cc0ab..77b376c590b 100644
--- a/chromium/net/quic/test_tools/quic_session_peer.h
+++ b/chromium/net/quic/test_tools/quic_session_peer.h
@@ -6,12 +6,13 @@
#define NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
#include "net/quic/quic_protocol.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
namespace net {
+class QuicDataStream;
+class QuicHeadersStream;
class QuicSession;
-class ReliableQuicStream;
namespace test {
@@ -19,8 +20,12 @@ class QuicSessionPeer {
public:
static void SetNextStreamId(QuicSession* session, QuicStreamId id);
static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams);
- static WriteBlockedList<QuicStreamId>* GetWriteblockedStreams(
- QuicSession* session);
+ static QuicHeadersStream* GetHeadersStream(QuicSession* session);
+ static void SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream);
+ static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
+ static QuicDataStream* GetIncomingDataStream(QuicSession* session,
+ QuicStreamId stream_id);
private:
DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer);
diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc
new file mode 100644
index 00000000000..08a5c5b0720
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
+
+#include "net/quic/quic_stream_sequencer.h"
+
+using std::map;
+using std::string;
+
+namespace net {
+namespace test {
+
+// static
+map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames(
+ QuicStreamSequencer* sequencer) {
+ return &(sequencer->buffered_frames_);
+}
+
+// static
+QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->close_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h
new file mode 100644
index 00000000000..7b2b28aad46
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicStreamSequencer;
+
+namespace test {
+
+class QuicStreamSequencerPeer {
+ public:
+ static std::map<QuicStreamOffset, std::string>* GetBufferedFrames(
+ QuicStreamSequencer* sequencer);
+
+ static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.cc b/chromium/net/quic/test_tools/quic_test_packet_maker.cc
new file mode 100644
index 00000000000..7f0835f8bdf
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_test_packet_maker.cc
@@ -0,0 +1,249 @@
+// 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/test_tools/quic_test_packet_maker.h"
+
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+namespace test {
+
+QuicTestPacketMaker::QuicTestPacketMaker(QuicVersion version,
+ QuicConnectionId connection_id)
+ : version_(version),
+ connection_id_(connection_id),
+ spdy_request_framer_(SPDY3),
+ spdy_response_framer_(SPDY3) {
+}
+
+QuicTestPacketMaker::~QuicTestPacketMaker() {
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&rst)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked));
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ QuicCongestionFeedbackFrame feedback;
+ if (send_feedback) {
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ if (version_ > QUIC_VERSION_15) {
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+ }
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ frames.push_back(QuicFrame(&rst));
+
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicConnectionCloseFrame close;
+ close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
+ close.error_details = "Time to panic!";
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&close)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked));
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ if (send_feedback) {
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ if (version_ > QUIC_VERSION_15) {
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+ }
+
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+// Returns a newly created packet to send kData on stream 1.
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ InitializeHeader(sequence_number, should_include_version);
+ QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ syn_stream.set_priority(0);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_request_framer_.SerializeSynStream(syn_stream));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_response_framer_.SerializeSynReply(syn_reply));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetRequestHeaders(
+ const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = method;
+ headers[":host"] = "www.google.com";
+ headers[":path"] = path;
+ headers[":scheme"] = scheme;
+ headers[":version"] = "HTTP/1.1";
+ return headers;
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetResponseHeaders(
+ const std::string& status) {
+ SpdyHeaderBlock headers;
+ headers[":status"] = status;
+ headers[":version"] = "HTTP/1.1";
+ headers["content-type"] = "text/plain";
+ return headers;
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame) {
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ QuicFrames frames;
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+void QuicTestPacketMaker::InitializeHeader(
+ QuicPacketSequenceNumber sequence_number,
+ bool should_include_version) {
+ header_.public_header.connection_id = connection_id_;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = should_include_version;
+ header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header_.packet_sequence_number = sequence_number;
+ header_.fec_group = 0;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.h b/chromium/net/quic/test_tools/quic_test_packet_maker.h
new file mode 100644
index 00000000000..e4f3ec03300
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_test_packet_maker.h
@@ -0,0 +1,91 @@
+// 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.
+//
+// Provides a simple interface for QUIC tests to create a variety of packets.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/request_priority.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net {
+namespace test {
+
+class QuicTestPacketMaker {
+ public:
+ QuicTestPacketMaker(QuicVersion version, QuicConnectionId connection_id);
+ ~QuicTestPacketMaker();
+
+ scoped_ptr<QuicEncryptedPacket> MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code);
+ scoped_ptr<QuicEncryptedPacket> MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num);
+ scoped_ptr<QuicEncryptedPacket> MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data);
+ scoped_ptr<QuicEncryptedPacket> MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+ scoped_ptr<QuicEncryptedPacket> MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path);
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status);
+
+ private:
+ scoped_ptr<QuicEncryptedPacket> MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame);
+
+ void InitializeHeader(QuicPacketSequenceNumber sequence_number,
+ bool should_include_version);
+
+ QuicVersion version_;
+ QuicConnectionId connection_id_;
+ SpdyFramer spdy_request_framer_;
+ SpdyFramer spdy_response_framer_;
+ MockRandom random_generator_;
+ QuicPacketHeader header_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTestPacketMaker);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
diff --git a/chromium/net/quic/test_tools/quic_test_utils.cc b/chromium/net/quic/test_tools/quic_test_utils.cc
index d85f92fa07e..b6a0744379a 100644
--- a/chromium/net/quic/test_tools/quic_test_utils.cc
+++ b/chromium/net/quic/test_tools/quic_test_utils.cc
@@ -4,6 +4,7 @@
#include "net/quic/test_tools/quic_test_utils.h"
+#include "base/sha1.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -14,6 +15,7 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/spdy/spdy_frame_builder.h"
@@ -21,6 +23,7 @@ using base::StringPiece;
using std::max;
using std::min;
using std::string;
+using testing::AnyNumber;
using testing::_;
namespace net {
@@ -40,6 +43,54 @@ class TestAlarm : public QuicAlarm {
} // namespace
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = largest_observed;
+ ack.received_info.entropy_hash = 0;
+ ack.sent_info.least_unacked = least_unacked;
+ ack.sent_info.entropy_hash = 0;
+ return ack;
+}
+
+QuicAckFrame MakeAckFrameWithNackRanges(
+ size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked,
+ least_unacked);
+ // Add enough missing packets to get num_nack_ranges nack ranges.
+ for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) {
+ ack.received_info.missing_packets.insert(least_unacked + i);
+ }
+ return ack;
+}
+
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ const size_t max_plaintext_size = framer->GetMaxPlaintextSize(kMaxPacketSize);
+ size_t packet_size = GetPacketHeaderSize(header);
+ for (size_t i = 0; i < frames.size(); ++i) {
+ DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
+ const size_t frame_size = framer->GetSerializedFrameLength(
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
+ header.is_in_fec_group,
+ header.public_header.sequence_number_length);
+ DCHECK(frame_size);
+ packet_size += frame_size;
+ }
+ return framer->BuildDataPacket(header, frames, packet_size);
+}
+
+uint64 SimpleRandom::RandUint64() {
+ unsigned char hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
+ hash);
+ memcpy(&seed_, hash, sizeof(seed_));
+ return seed_;
+}
+
MockFramerVisitor::MockFramerVisitor() {
// By default, we want to accept packets.
ON_CALL(*this, OnProtocolVersionMismatch(_))
@@ -49,6 +100,9 @@ MockFramerVisitor::MockFramerVisitor() {
ON_CALL(*this, OnUnauthenticatedHeader(_))
.WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnUnauthenticatedPublicHeader(_))
+ .WillByDefault(testing::Return(true));
+
ON_CALL(*this, OnPacketHeader(_))
.WillByDefault(testing::Return(true));
@@ -61,6 +115,12 @@ MockFramerVisitor::MockFramerVisitor() {
ON_CALL(*this, OnCongestionFeedbackFrame(_))
.WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnStopWaitingFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPingFrame(_))
+ .WillByDefault(testing::Return(true));
+
ON_CALL(*this, OnRstStreamFrame(_))
.WillByDefault(testing::Return(true));
@@ -78,6 +138,11 @@ bool NoOpFramerVisitor::OnProtocolVersionMismatch(QuicVersion version) {
return false;
}
+bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
bool NoOpFramerVisitor::OnUnauthenticatedHeader(
const QuicPacketHeader& header) {
return true;
@@ -100,107 +165,36 @@ bool NoOpFramerVisitor::OnCongestionFeedbackFrame(
return true;
}
-bool NoOpFramerVisitor::OnRstStreamFrame(
- const QuicRstStreamFrame& frame) {
- return true;
-}
-
-bool NoOpFramerVisitor::OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) {
+bool NoOpFramerVisitor::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
return true;
}
-bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
return true;
}
-FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) {
-}
-
-FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() {
- Reset();
-}
-
-void FramerVisitorCapturingFrames::Reset() {
- STLDeleteElements(&stream_data_);
- stream_frames_.clear();
- frame_count_ = 0;
- ack_.reset();
- feedback_.reset();
- rst_.reset();
- close_.reset();
- goaway_.reset();
- version_negotiation_packet_.reset();
-}
-
-bool FramerVisitorCapturingFrames::OnPacketHeader(
- const QuicPacketHeader& header) {
- header_ = header;
- frame_count_ = 0;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) {
- // Make a copy of the frame and store a copy of underlying string, since
- // frame.data may not exist outside this callback.
- stream_data_.push_back(frame.GetDataAsString());
- QuicStreamFrame frame_copy = frame;
- frame_copy.data.Clear();
- frame_copy.data.Append(const_cast<char*>(stream_data_.back()->data()),
- stream_data_.back()->size());
- stream_frames_.push_back(frame_copy);
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnAckFrame(const QuicAckFrame& frame) {
- ack_.reset(new QuicAckFrame(frame));
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) {
- feedback_.reset(new QuicCongestionFeedbackFrame(frame));
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnRstStreamFrame(
+bool NoOpFramerVisitor::OnRstStreamFrame(
const QuicRstStreamFrame& frame) {
- rst_.reset(new QuicRstStreamFrame(frame));
- ++frame_count_;
return true;
}
-bool FramerVisitorCapturingFrames::OnConnectionCloseFrame(
+bool NoOpFramerVisitor::OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) {
- close_.reset(new QuicConnectionCloseFrame(frame));
- ++frame_count_;
return true;
}
-bool FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
- goaway_.reset(new QuicGoAwayFrame(frame));
- ++frame_count_;
+bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
return true;
}
-void FramerVisitorCapturingFrames::OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) {
- version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet));
- frame_count_ = 0;
-}
-
-FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() {
-}
-
-FramerVisitorCapturingPublicReset::~FramerVisitorCapturingPublicReset() {
+bool NoOpFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
}
-void FramerVisitorCapturingPublicReset::OnPublicResetPacket(
- const QuicPublicResetPacket& public_reset) {
- public_reset_packet_ = public_reset;
+bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
}
MockConnectionVisitor::MockConnectionVisitor() {
@@ -232,8 +226,8 @@ void MockHelper::AdvanceTime(QuicTime::Delta delta) {
}
MockConnection::MockConnection(bool is_server)
- : QuicConnection(kTestGuid,
- IPEndPoint(Loopback4(), kTestPort),
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -243,7 +237,7 @@ MockConnection::MockConnection(bool is_server)
MockConnection::MockConnection(IPEndPoint address,
bool is_server)
- : QuicConnection(kTestGuid, address,
+ : QuicConnection(kTestConnectionId, address,
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -251,10 +245,10 @@ MockConnection::MockConnection(IPEndPoint address,
helper_(helper()) {
}
-MockConnection::MockConnection(QuicGuid guid,
+MockConnection::MockConnection(QuicConnectionId connection_id,
bool is_server)
- : QuicConnection(guid,
- IPEndPoint(Loopback4(), kTestPort),
+ : QuicConnection(connection_id,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -262,6 +256,17 @@ MockConnection::MockConnection(QuicGuid guid,
helper_(helper()) {
}
+MockConnection::MockConnection(bool is_server,
+ const QuicVersionVector& supported_versions)
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ new testing::NiceMock<MockPacketWriter>(),
+ is_server, supported_versions),
+ writer_(QuicConnectionPeer::GetWriter(this)),
+ helper_(helper()) {
+}
+
MockConnection::~MockConnection() {
}
@@ -273,6 +278,12 @@ PacketSavingConnection::PacketSavingConnection(bool is_server)
: MockConnection(is_server) {
}
+PacketSavingConnection::PacketSavingConnection(
+ bool is_server,
+ const QuicVersionVector& supported_versions)
+ : MockConnection(is_server, supported_versions) {
+}
+
PacketSavingConnection::~PacketSavingConnection() {
STLDeleteElements(&packets_);
STLDeleteElements(&encrypted_packets_);
@@ -283,8 +294,8 @@ bool PacketSavingConnection::SendOrQueuePacket(
const SerializedPacket& packet,
TransmissionType transmission_type) {
packets_.push_back(packet.packet);
- QuicEncryptedPacket* encrypted =
- framer_.EncryptPacket(level, packet.sequence_number, *packet.packet);
+ QuicEncryptedPacket* encrypted = QuicConnectionPeer::GetFramer(this)->
+ EncryptPacket(level, packet.sequence_number, *packet.packet);
encrypted_packets_.push_back(encrypted);
return true;
}
@@ -298,11 +309,9 @@ MockSession::MockSession(QuicConnection* connection)
MockSession::~MockSession() {
}
-TestSession::TestSession(QuicConnection* connection,
- const QuicConfig& config)
+TestSession::TestSession(QuicConnection* connection, const QuicConfig& config)
: QuicSession(connection, config),
- crypto_stream_(NULL) {
-}
+ crypto_stream_(NULL) {}
TestSession::~TestSession() {}
@@ -314,6 +323,24 @@ QuicCryptoStream* TestSession::GetCryptoStream() {
return crypto_stream_;
}
+TestClientSession::TestClientSession(QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicClientSessionBase(connection,
+ config),
+ crypto_stream_(NULL) {
+ EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber());
+}
+
+TestClientSession::~TestClientSession() {}
+
+void TestClientSession::SetCryptoStream(QuicCryptoStream* stream) {
+ crypto_stream_ = stream;
+}
+
+QuicCryptoStream* TestClientSession::GetCryptoStream() {
+ return crypto_stream_;
+}
+
MockPacketWriter::MockPacketWriter() {
}
@@ -326,6 +353,12 @@ MockSendAlgorithm::MockSendAlgorithm() {
MockSendAlgorithm::~MockSendAlgorithm() {
}
+MockLossAlgorithm::MockLossAlgorithm() {
+}
+
+MockLossAlgorithm::~MockLossAlgorithm() {
+}
+
MockAckNotifierDelegate::MockAckNotifierDelegate() {
}
@@ -373,16 +406,65 @@ string HexDumpWithMarks(const char* data, int length,
} // namespace
+IPAddressNumber TestPeerIPAddress() { return Loopback4(); }
+
QuicVersion QuicVersionMax() { return QuicSupportedVersions().front(); }
QuicVersion QuicVersionMin() { return QuicSupportedVersions().back(); }
IPAddressNumber Loopback4() {
- net::IPAddressNumber addr;
- CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &addr));
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("127.0.0.1", &addr));
+ return addr;
+}
+
+IPAddressNumber Loopback6() {
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("::1", &addr));
return addr;
}
+void GenerateBody(string* body, int length) {
+ body->clear();
+ body->reserve(length);
+ for (int i = 0; i < length; ++i) {
+ body->append(1, static_cast<char>(32 + i % (126 - 32)));
+ }
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const string& data) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id;
+ header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+ header.public_header.version_flag = version_flag;
+ header.public_header.reset_flag = reset_flag;
+ 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, MakeIOVector(data));
+ QuicFrame frame(&stream_frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ EXPECT_TRUE(packet != NULL);
+ QuicEncryptedPacket* encrypted = framer.EncryptPacket(ENCRYPTION_NONE,
+ sequence_number,
+ *packet);
+ EXPECT_TRUE(encrypted != NULL);
+ return encrypted;
+}
+
void CompareCharArraysWithHexError(
const string& description,
const char* actual,
@@ -428,7 +510,7 @@ bool DecodeHexString(const base::StringPiece& hex, std::string* bytes) {
}
static QuicPacket* ConstructPacketFromHandshakeMessage(
- QuicGuid guid,
+ QuicConnectionId connection_id,
const CryptoHandshakeMessage& message,
bool should_include_version) {
CryptoFramer crypto_framer;
@@ -436,7 +518,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicFramer quic_framer(QuicSupportedVersions(), QuicTime::Zero(), false);
QuicPacketHeader header;
- header.public_header.guid = guid;
+ header.public_header.connection_id = connection_id;
header.public_header.reset_flag = false;
header.public_header.version_flag = should_include_version;
header.packet_sequence_number = 1;
@@ -451,13 +533,14 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicFrame frame(&stream_frame);
QuicFrames frames;
frames.push_back(frame);
- return quic_framer.BuildUnsizedDataPacket(header, frames).packet;
+ return BuildUnsizedDataPacket(&quic_framer, header, frames).packet;
}
-QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) {
+QuicPacket* ConstructHandshakePacket(QuicConnectionId connection_id,
+ QuicTag tag) {
CryptoHandshakeMessage message;
message.set_tag(tag);
- return ConstructPacketFromHandshakeMessage(guid, message, false);
+ return ConstructPacketFromHandshakeMessage(connection_id, message, false);
}
size_t GetPacketLengthForOneStream(
@@ -470,12 +553,12 @@ size_t GetPacketLengthForOneStream(
const size_t stream_length =
NullEncrypter().GetCiphertextSize(*payload_length) +
QuicPacketCreator::StreamFramePacketOverhead(
- version, PACKET_8BYTE_GUID, include_version,
- sequence_number_length, is_in_fec_group);
+ version, PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
const size_t ack_length = NullEncrypter().GetCiphertextSize(
QuicFramer::GetMinAckFrameSize(
version, sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) +
- GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
sequence_number_length, is_in_fec_group);
if (stream_length < ack_length) {
*payload_length = 1 + ack_length - stream_length;
@@ -483,42 +566,39 @@ size_t GetPacketLengthForOneStream(
return NullEncrypter().GetCiphertextSize(*payload_length) +
QuicPacketCreator::StreamFramePacketOverhead(
- version, PACKET_8BYTE_GUID, include_version,
- sequence_number_length, is_in_fec_group);
-}
-
-// Size in bytes of the stream frame fields for an arbitrary StreamID and
-// offset and the last frame in a packet.
-size_t GetMinStreamFrameSize(QuicVersion version) {
- return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+ version, PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
}
-TestEntropyCalculator::TestEntropyCalculator() { }
+TestEntropyCalculator::TestEntropyCalculator() {}
-TestEntropyCalculator::~TestEntropyCalculator() { }
+TestEntropyCalculator::~TestEntropyCalculator() {}
QuicPacketEntropyHash TestEntropyCalculator::EntropyHash(
QuicPacketSequenceNumber sequence_number) const {
return 1u;
}
-MockEntropyCalculator::MockEntropyCalculator() { }
+MockEntropyCalculator::MockEntropyCalculator() {}
-MockEntropyCalculator::~MockEntropyCalculator() { }
+MockEntropyCalculator::~MockEntropyCalculator() {}
QuicConfig DefaultQuicConfig() {
QuicConfig config;
config.SetDefaults();
+ config.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
return config;
}
-bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
- data.AppendToString(&data_);
- return true;
-}
-
-void TestDecompressorVisitor::OnDecompressionError() {
- error_ = true;
+QuicVersionVector SupportedVersions(QuicVersion version) {
+ QuicVersionVector versions;
+ versions.push_back(version);
+ return versions;
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/quic_test_utils.h b/chromium/net/quic/test_tools/quic_test_utils.h
index 06c343189b5..574e7d15c67 100644
--- a/chromium/net/quic/test_tools/quic_test_utils.h
+++ b/chromium/net/quic/test_tools/quic_test_utils.h
@@ -11,12 +11,13 @@
#include <vector>
#include "base/strings/string_piece.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/spdy/spdy_framer.h"
@@ -26,8 +27,21 @@ namespace net {
namespace test {
-static const QuicGuid kTestGuid = 42;
+static const QuicConnectionId kTestConnectionId = 42;
static const int kTestPort = 123;
+static const uint32 kInitialStreamFlowControlWindowForTest =
+ 32 * 1024; // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+ 64 * 1024; // 64 KB
+
+// Data stream IDs start at 5: the crypto stream is 1, headers stream is 3.
+static const QuicStreamId kClientDataStreamId1 = 5;
+static const QuicStreamId kClientDataStreamId2 = 7;
+static const QuicStreamId kClientDataStreamId3 = 9;
+static const QuicStreamId kClientDataStreamId4 = 11;
+
+// Returns the test peer IP address.
+IPAddressNumber TestPeerIPAddress();
// Upper limit on versions we support.
QuicVersion QuicVersionMax();
@@ -38,6 +52,19 @@ QuicVersion QuicVersionMin();
// Returns an address for 127.0.0.1.
IPAddressNumber Loopback4();
+// Returns an address for ::1.
+IPAddressNumber Loopback6();
+
+void GenerateBody(std::string* body, int length);
+
+// Create an encrypted packet for testing.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const std::string& data);
+
void CompareCharArraysWithHexError(const std::string& description,
const char* actual,
const int actual_len,
@@ -56,13 +83,29 @@ size_t GetPacketLengthForOneStream(
InFecGroup is_in_fec_group,
size_t* payload_length);
-// Size in bytes of the stream frame fields for an arbitrary StreamID and
-// offset and the last frame in a packet.
-size_t GetMinStreamFrameSize(QuicVersion version);
-
// Returns QuicConfig set to default values.
QuicConfig DefaultQuicConfig();
+// Returns a version vector consisting of |version|.
+QuicVersionVector SupportedVersions(QuicVersion version);
+
+// Testing convenience method to construct a QuicAckFrame with all packets
+// from least_unacked to largest_observed acked.
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked);
+
+// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges|
+// nack ranges of width 1 packet, starting from |least_unacked|.
+QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges,
+ QuicPacketSequenceNumber least_unacked);
+
+// Returns a SerializedPacket whose |packet| member is owned by the caller, and
+// is populated with the fields in |header| and |frames|, or is NULL if the
+// packet could not be created.
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames);
+
template<typename SaveType>
class ValueRestore {
public:
@@ -82,10 +125,29 @@ class ValueRestore {
DISALLOW_COPY_AND_ASSIGN(ValueRestore);
};
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests. It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom {
+ public:
+ SimpleRandom() : seed_(0) {}
+
+ // Returns a random number in the range [0, kuint64max].
+ uint64 RandUint64();
+
+ void set_seed(uint64 seed) { seed_ = seed; }
+
+ private:
+ uint64 seed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleRandom);
+};
+
class MockFramerVisitor : public QuicFramerVisitorInterface {
public:
MockFramerVisitor();
- ~MockFramerVisitor();
+ virtual ~MockFramerVisitor();
MOCK_METHOD1(OnError, void(QuicFramer* framer));
// The constructor sets this up to return false by default.
@@ -97,17 +159,25 @@ class MockFramerVisitor : public QuicFramerVisitorInterface {
MOCK_METHOD0(OnRevivedPacket, void());
// The constructor sets this up to return true by default.
MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedPublicHeader, bool(
+ const QuicPacketPublicHeader& header));
+ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload));
MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
MOCK_METHOD1(OnAckFrame, bool(const QuicAckFrame& frame));
MOCK_METHOD1(OnCongestionFeedbackFrame,
bool(const QuicCongestionFeedbackFrame& frame));
+ MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
+ MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
MOCK_METHOD1(OnFecData, void(const QuicFecData& fec));
MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnConnectionCloseFrame,
bool(const QuicConnectionCloseFrame& frame));
MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame));
MOCK_METHOD0(OnPacketComplete, void());
private:
@@ -127,103 +197,49 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface {
virtual void OnRevivedPacket() OVERRIDE {}
virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE;
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {}
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {}
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual bool OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
virtual void OnPacketComplete() OVERRIDE {}
private:
DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor);
};
-class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
- public:
- FramerVisitorCapturingPublicReset();
- virtual ~FramerVisitorCapturingPublicReset();
-
- virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& packet) OVERRIDE;
-
- const QuicPublicResetPacket public_reset_packet() {
- return public_reset_packet_;
- }
-
- private:
- QuicPublicResetPacket public_reset_packet_;
-};
-
-class FramerVisitorCapturingFrames : public NoOpFramerVisitor {
- public:
- FramerVisitorCapturingFrames();
- virtual ~FramerVisitorCapturingFrames();
-
- // Reset the visitor to it's initial state.
- void Reset();
-
- // NoOpFramerVisitor
- virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) OVERRIDE;
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
- virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
- virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
- virtual bool OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) OVERRIDE;
- virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
- virtual bool OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) OVERRIDE;
- virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
-
- size_t frame_count() const { return frame_count_; }
- QuicPacketHeader* header() { return &header_; }
- const std::vector<QuicStreamFrame>* stream_frames() const {
- return &stream_frames_;
- }
- const std::vector<string*>& stream_data() const {
- return stream_data_;
- }
- QuicAckFrame* ack() { return ack_.get(); }
- QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); }
- QuicRstStreamFrame* rst() { return rst_.get(); }
- QuicConnectionCloseFrame* close() { return close_.get(); }
- QuicGoAwayFrame* goaway() { return goaway_.get(); }
- QuicVersionNegotiationPacket* version_negotiation_packet() {
- return version_negotiation_packet_.get();
- }
-
- private:
- size_t frame_count_;
- QuicPacketHeader header_;
- std::vector<QuicStreamFrame> stream_frames_;
- std::vector<std::string*> stream_data_;
- scoped_ptr<QuicAckFrame> ack_;
- scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
- scoped_ptr<QuicRstStreamFrame> rst_;
- scoped_ptr<QuicConnectionCloseFrame> close_;
- scoped_ptr<QuicGoAwayFrame> goaway_;
- scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
-
- DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames);
-};
-
class MockConnectionVisitor : public QuicConnectionVisitorInterface {
public:
MockConnectionVisitor();
virtual ~MockConnectionVisitor();
- MOCK_METHOD1(OnStreamFrames, bool(const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnStreamFrames, void(const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnWindowUpdateFrames,
+ void(const std::vector<QuicWindowUpdateFrame>& frame));
+ MOCK_METHOD1(OnBlockedFrames,
+ void(const std::vector<QuicBlockedFrame>& frame));
MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
- MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_METHOD0(OnWriteBlocked, void());
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool());
MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_CONST_METHOD0(HasOpenDataStreams, bool());
MOCK_METHOD1(OnSuccessfulVersionNegotiation,
void(const QuicVersion& version));
MOCK_METHOD0(OnConfigNegotiated, void());
@@ -236,30 +252,31 @@ class MockHelper : public QuicConnectionHelperInterface {
public:
MockHelper();
virtual ~MockHelper();
-
- MOCK_METHOD1(SetConnection, void(QuicConnection* connection));
- const QuicClock* GetClock() const;
- QuicRandom* GetRandomGenerator();
+ virtual const QuicClock* GetClock() const OVERRIDE;
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE;
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE;
void AdvanceTime(QuicTime::Delta delta);
- virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate);
private:
MockClock clock_;
MockRandom random_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockHelper);
};
class MockConnection : public QuicConnection {
public:
- // Uses a MockHelper, GUID of 42, and 127.0.0.1:123.
+ // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123.
explicit MockConnection(bool is_server);
- // Uses a MockHelper, GUID of 42.
- MockConnection(IPEndPoint address,
- bool is_server);
+ // Uses a MockHelper, ConnectionId of 42.
+ MockConnection(IPEndPoint address, bool is_server);
// Uses a MockHelper, and 127.0.0.1:123
- MockConnection(QuicGuid guid,
- bool is_server);
+ MockConnection(QuicConnectionId connection_id, bool is_server);
+
+ // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123.
+ MockConnection(bool is_server, const QuicVersionVector& supported_versions);
virtual ~MockConnection();
@@ -273,13 +290,18 @@ class MockConnection : public QuicConnection {
MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error,
const string& details));
- MOCK_METHOD2(SendRstStream, void(QuicStreamId id,
- QuicRstStreamErrorCode error));
+ MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error,
+ const string& details));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const string& reason));
- MOCK_METHOD0(OnCanWrite, bool());
- MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id,
+ QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
void ProcessUdpPacketInternal(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
@@ -301,6 +323,10 @@ class MockConnection : public QuicConnection {
class PacketSavingConnection : public MockConnection {
public:
explicit PacketSavingConnection(bool is_server);
+
+ PacketSavingConnection(bool is_server,
+ const QuicVersionVector& supported_versions);
+
virtual ~PacketSavingConnection();
virtual bool SendOrQueuePacket(EncryptionLevel level,
@@ -318,25 +344,31 @@ class MockSession : public QuicSession {
public:
explicit MockSession(QuicConnection* connection);
virtual ~MockSession();
-
- MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame));
MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*());
MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
MOCK_METHOD6(WritevData,
QuicConsumedData(QuicStreamId id,
- const struct iovec* iov,
- int count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface*));
- MOCK_METHOD0(IsHandshakeComplete, bool());
+ MOCK_METHOD2(OnStreamHeaders, void(QuicStreamId stream_id,
+ base::StringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority, void(QuicStreamId stream_id,
+ QuicPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete, void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
MOCK_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ using QuicSession::ActivateStream;
+
private:
DISALLOW_COPY_AND_ASSIGN(MockSession);
};
@@ -351,25 +383,55 @@ class TestSession : public QuicSession {
void SetCryptoStream(QuicCryptoStream* stream);
- virtual QuicCryptoStream* GetCryptoStream();
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
private:
QuicCryptoStream* crypto_stream_;
+
DISALLOW_COPY_AND_ASSIGN(TestSession);
};
+class TestClientSession : public QuicClientSessionBase {
+ public:
+ TestClientSession(QuicConnection* connection, const QuicConfig& config);
+ virtual ~TestClientSession();
+
+ // QuicClientSessionBase
+ MOCK_METHOD1(OnProofValid,
+ void(const QuicCryptoClientConfig::CachedState& cached));
+ MOCK_METHOD1(OnProofVerifyDetailsAvailable,
+ void(const ProofVerifyDetails& verify_details));
+
+ // TestClientSession
+ MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+ MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+
+ void SetCryptoStream(QuicCryptoStream* stream);
+
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
+
+ private:
+ QuicCryptoStream* crypto_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestClientSession);
+};
+
class MockPacketWriter : public QuicPacketWriter {
public:
MockPacketWriter();
virtual ~MockPacketWriter();
- MOCK_METHOD5(WritePacket,
+ MOCK_METHOD4(WritePacket,
WriteResult(const char* buffer,
size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer));
+ const IPEndPoint& peer_address));
MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool());
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPacketWriter);
};
class MockSendAlgorithm : public SendAlgorithmInterface {
@@ -379,24 +441,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server));
MOCK_METHOD1(SetMaxPacketSize, void(QuicByteCount max_packet_size));
- MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame,
+ MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame,
void(const QuicCongestionFeedbackFrame&,
- QuicTime feedback_receive_time,
- const SentPacketsMap&));
- MOCK_METHOD3(OnPacketAcked,
- void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta));
- MOCK_METHOD2(OnPacketLost, void(QuicPacketSequenceNumber, QuicTime));
+ QuicTime feedback_receive_time));
+ MOCK_METHOD4(OnCongestionEvent, void(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets));
MOCK_METHOD5(OnPacketSent,
- bool(QuicTime sent_time, QuicPacketSequenceNumber, QuicByteCount,
- TransmissionType, HasRetransmittableData));
- MOCK_METHOD0(OnRetransmissionTimeout, void());
- MOCK_METHOD2(OnPacketAbandoned, void(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes));
- MOCK_METHOD4(TimeUntilSend, QuicTime::Delta(QuicTime now, TransmissionType,
- HasRetransmittableData,
- IsHandshake));
+ bool(QuicTime, QuicByteCount, QuicPacketSequenceNumber,
+ QuicByteCount, HasRetransmittableData));
+ MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
+ MOCK_CONST_METHOD3(TimeUntilSend,
+ QuicTime::Delta(QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData));
MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void));
- MOCK_CONST_METHOD0(SmoothedRtt, QuicTime::Delta(void));
+ MOCK_METHOD1(OnRttUpdated, void(QuicPacketSequenceNumber));
MOCK_CONST_METHOD0(RetransmissionDelay, QuicTime::Delta(void));
MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount());
@@ -404,6 +465,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm);
};
+class MockLossAlgorithm : public LossDetectionInterface {
+ public:
+ MockLossAlgorithm();
+ virtual ~MockLossAlgorithm();
+
+ MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType());
+ MOCK_METHOD4(DetectLostPackets,
+ SequenceNumberSet(const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats));
+ MOCK_CONST_METHOD0(GetLossTimeout, QuicTime());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockLossAlgorithm);
+};
+
class TestEntropyCalculator :
public QuicReceivedEntropyHashCalculatorInterface {
public:
@@ -412,6 +490,9 @@ class TestEntropyCalculator :
virtual QuicPacketEntropyHash EntropyHash(
QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestEntropyCalculator);
};
class MockEntropyCalculator : public TestEntropyCalculator {
@@ -422,28 +503,27 @@ class MockEntropyCalculator : public TestEntropyCalculator {
MOCK_CONST_METHOD1(
EntropyHash,
QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number));
-};
-
-class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
- public:
- virtual ~TestDecompressorVisitor() {}
- virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
- virtual void OnDecompressionError() OVERRIDE;
-
- string data() { return data_; }
- bool error() { return error_; }
private:
- string data_;
- bool error_;
+ DISALLOW_COPY_AND_ASSIGN(MockEntropyCalculator);
};
class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
public:
MockAckNotifierDelegate();
+
+ MOCK_METHOD5(OnAckNotification, void(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed));
+
+ protected:
+ // Object is ref counted.
virtual ~MockAckNotifierDelegate();
- MOCK_METHOD0(OnAckNotification, void());
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate);
};
} // namespace test
diff --git a/chromium/net/quic/test_tools/quic_test_writer.cc b/chromium/net/quic/test_tools/quic_test_writer.cc
deleted file mode 100644
index 6755b2cfa8c..00000000000
--- a/chromium/net/quic/test_tools/quic_test_writer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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/test_tools/quic_test_writer.h"
-
-namespace net {
-namespace test {
-
-QuicTestWriter::QuicTestWriter() {}
-
-QuicTestWriter::~QuicTestWriter() {}
-
-QuicPacketWriter* QuicTestWriter::writer() {
- return writer_.get();
-}
-
-void QuicTestWriter::set_writer(QuicPacketWriter* writer) {
- writer_.reset(writer);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_test_writer.h b/chromium/net/quic/test_tools/quic_test_writer.h
deleted file mode 100644
index ee33b7abe2a..00000000000
--- a/chromium/net/quic/test_tools/quic_test_writer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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_TEST_TOOLS_QUIC_TEST_WRITER_H_
-#define NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/quic_packet_writer.h"
-
-namespace net {
-namespace test {
-
-// Allows setting a writer for a QuicConnection, to allow
-// fine-grained control of writes.
-class QuicTestWriter : public QuicPacketWriter {
- public:
- QuicTestWriter();
- virtual ~QuicTestWriter();
- // Takes ownership of |writer|.
- void set_writer(QuicPacketWriter* writer);
-
- protected:
- QuicPacketWriter* writer();
-
- private:
- scoped_ptr<QuicPacketWriter> writer_;
-};
-
-} // namespace test
-} // namespace net
-
-#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_
diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
index 5119d035faa..914609ee1d5 100644
--- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
+++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -4,6 +4,8 @@
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include <list>
+
#include "net/quic/reliable_quic_stream.h"
namespace net {
@@ -22,5 +24,38 @@ void ReliableQuicStreamPeer::SetStreamBytesWritten(
stream->stream_bytes_written_ = stream_bytes_written;
}
+// static
+void ReliableQuicStreamPeer::CloseReadSide(ReliableQuicStream* stream) {
+ stream->CloseReadSide();
+}
+
+// static
+bool ReliableQuicStreamPeer::FinSent(ReliableQuicStream* stream) {
+ return stream->fin_sent_;
+}
+
+// static
+bool ReliableQuicStreamPeer::RstSent(ReliableQuicStream* stream) {
+ return stream->rst_sent_;
+}
+
+// static
+uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) {
+ uint32 total = 0;
+ std::list<ReliableQuicStream::PendingData>::iterator it =
+ stream->queued_data_.begin();
+ while (it != stream->queued_data_.end()) {
+ total += it->data.size();
+ ++it;
+ }
+ return total;
+}
+
+// static
+void ReliableQuicStreamPeer::SetFecPolicy(ReliableQuicStream* stream,
+ FecPolicy fec_policy) {
+ stream->set_fec_policy(fec_policy);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
index da229da44e0..1f5467d00ee 100644
--- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
+++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -19,6 +19,14 @@ class ReliableQuicStreamPeer {
static void SetWriteSideClosed(bool value, ReliableQuicStream* stream);
static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
ReliableQuicStream* stream);
+ static void CloseReadSide(ReliableQuicStream* stream);
+
+ static bool FinSent(ReliableQuicStream* stream);
+ static bool RstSent(ReliableQuicStream* stream);
+
+ static uint32 SizeOfQueuedData(ReliableQuicStream* stream);
+
+ static void SetFecPolicy(ReliableQuicStream* stream, FecPolicy fec_policy);
private:
DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer);
diff --git a/chromium/net/quic/test_tools/simple_quic_framer.cc b/chromium/net/quic/test_tools/simple_quic_framer.cc
index 7d6439d2dcb..a08d5555663 100644
--- a/chromium/net/quic/test_tools/simple_quic_framer.cc
+++ b/chromium/net/quic/test_tools/simple_quic_framer.cc
@@ -22,7 +22,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
: error_(QUIC_NO_ERROR) {
}
- virtual ~SimpleFramerVisitor() {
+ virtual ~SimpleFramerVisitor() OVERRIDE {
STLDeleteElements(&stream_data_);
}
@@ -36,21 +36,30 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
virtual void OnPacket() OVERRIDE {}
virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& packet) OVERRIDE {}
+ const QuicPublicResetPacket& packet) OVERRIDE {
+ public_reset_packet_.reset(new QuicPublicResetPacket(packet));
+ }
virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) OVERRIDE {}
+ const QuicVersionNegotiationPacket& packet) OVERRIDE {
+ version_negotiation_packet_.reset(
+ new QuicVersionNegotiationPacket(packet));
+ }
virtual void OnRevivedPacket() OVERRIDE {}
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
- has_header_ = true;
- header_ = header;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
return true;
}
-
virtual bool OnUnauthenticatedHeader(
const QuicPacketHeader& header) OVERRIDE {
return true;
}
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
virtual void OnFecProtectedPayload(StringPiece payload) OVERRIDE {}
@@ -77,6 +86,16 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ stop_waiting_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ping_frames_.push_back(frame);
+ return true;
+ }
+
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
fec_data_ = fec;
fec_redundancy_ = fec_data_.redundancy.as_string();
@@ -99,6 +118,17 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) OVERRIDE {
+ window_update_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frames_.push_back(frame);
+ return true;
+ }
+
virtual void OnPacketComplete() OVERRIDE {}
const QuicPacketHeader& header() const { return header_; }
@@ -118,22 +148,40 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
const vector<QuicStreamFrame>& stream_frames() const {
return stream_frames_;
}
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return stop_waiting_frames_;
+ }
+ const vector<QuicPingFrame>& ping_frames() const {
+ return ping_frames_;
+ }
const QuicFecData& fec_data() const {
return fec_data_;
}
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const {
+ return version_negotiation_packet_.get();
+ }
+ const QuicPublicResetPacket* public_reset_packet() const {
+ return public_reset_packet_.get();
+ }
private:
QuicErrorCode error_;
bool has_header_;
QuicPacketHeader header_;
QuicFecData fec_data_;
+ scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
string fec_redundancy_;
vector<QuicAckFrame> ack_frames_;
vector<QuicCongestionFeedbackFrame> feedback_frames_;
+ vector<QuicStopWaitingFrame> stop_waiting_frames_;
+ vector<QuicPingFrame> ping_frames_;
vector<QuicStreamFrame> stream_frames_;
vector<QuicRstStreamFrame> rst_stream_frames_;
vector<QuicGoAwayFrame> goaway_frames_;
vector<QuicConnectionCloseFrame> connection_close_frames_;
+ vector<QuicWindowUpdateFrame> window_update_frames_;
+ vector<QuicBlockedFrame> blocked_frames_;
vector<string*> stream_data_;
DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor);
@@ -143,6 +191,10 @@ SimpleQuicFramer::SimpleQuicFramer()
: framer_(QuicSupportedVersions(), QuicTime::Zero(), true) {
}
+SimpleQuicFramer::SimpleQuicFramer(const QuicVersionVector& supported_versions)
+ : framer_(supported_versions, QuicTime::Zero(), true) {
+}
+
SimpleQuicFramer::~SimpleQuicFramer() {
}
@@ -158,6 +210,11 @@ bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
return framer_.ProcessPacket(packet);
}
+void SimpleQuicFramer::Reset() {
+ visitor_.reset(new SimpleFramerVisitor);
+}
+
+
const QuicPacketHeader& SimpleQuicFramer::header() const {
return visitor_->header();
}
@@ -166,16 +223,27 @@ const QuicFecData& SimpleQuicFramer::fec_data() const {
return visitor_->fec_data();
}
+const QuicVersionNegotiationPacket*
+SimpleQuicFramer::version_negotiation_packet() const {
+ return visitor_->version_negotiation_packet();
+}
+
+const QuicPublicResetPacket* SimpleQuicFramer::public_reset_packet() const {
+ return visitor_->public_reset_packet();
+}
+
QuicFramer* SimpleQuicFramer::framer() {
return &framer_;
}
size_t SimpleQuicFramer::num_frames() const {
return ack_frames().size() +
- stream_frames().size() +
feedback_frames().size() +
- rst_stream_frames().size() +
goaway_frames().size() +
+ rst_stream_frames().size() +
+ stop_waiting_frames().size() +
+ stream_frames().size() +
+ ping_frames().size() +
connection_close_frames().size();
}
@@ -183,6 +251,15 @@ const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
return visitor_->ack_frames();
}
+const vector<QuicStopWaitingFrame>&
+SimpleQuicFramer::stop_waiting_frames() const {
+ return visitor_->stop_waiting_frames();
+}
+
+const vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const {
+ return visitor_->ping_frames();
+}
+
const vector<QuicStreamFrame>& SimpleQuicFramer::stream_frames() const {
return visitor_->stream_frames();
}
diff --git a/chromium/net/quic/test_tools/simple_quic_framer.h b/chromium/net/quic/test_tools/simple_quic_framer.h
index 4416019dba3..7a31014d41e 100644
--- a/chromium/net/quic/test_tools/simple_quic_framer.h
+++ b/chromium/net/quic/test_tools/simple_quic_framer.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
@@ -29,22 +30,33 @@ class SimpleFramerVisitor;
class SimpleQuicFramer {
public:
SimpleQuicFramer();
+ explicit SimpleQuicFramer(const QuicVersionVector& supported_versions);
~SimpleQuicFramer();
bool ProcessPacket(const QuicEncryptedPacket& packet);
bool ProcessPacket(const QuicPacket& packet);
+ void Reset();
const QuicPacketHeader& header() const;
size_t num_frames() const;
const std::vector<QuicAckFrame>& ack_frames() const;
const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
const std::vector<QuicCongestionFeedbackFrame>& feedback_frames() const;
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const;
+ const std::vector<QuicPingFrame>& ping_frames() const;
const std::vector<QuicGoAwayFrame>& goaway_frames() const;
const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
const std::vector<QuicStreamFrame>& stream_frames() const;
const QuicFecData& fec_data() const;
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const;
+ const QuicPublicResetPacket* public_reset_packet() const;
+
QuicFramer* framer();
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
private:
QuicFramer framer_;
scoped_ptr<SimpleFramerVisitor> visitor_;
diff --git a/chromium/net/server/http_server.cc b/chromium/net/server/http_server.cc
index a51feb84401..f746f066e42 100644
--- a/chromium/net/server/http_server.cc
+++ b/chromium/net/server/http_server.cc
@@ -47,6 +47,13 @@ void HttpServer::SendOverWebSocket(int connection_id,
connection->web_socket_->Send(data);
}
+void HttpServer::SendRaw(int connection_id, const std::string& data) {
+ HttpConnection* connection = FindConnection(connection_id);
+ if (connection == NULL)
+ return;
+ connection->Send(data);
+}
+
void HttpServer::SendResponse(int connection_id,
const HttpServerResponseInfo& response) {
HttpConnection* connection = FindConnection(connection_id);
@@ -132,8 +139,10 @@ void HttpServer::DidRead(StreamListenSocket* socket,
if (!ParseHeaders(connection, &request, &pos))
break;
- std::string connection_header = request.GetHeaderValue("connection");
- if (connection_header == "Upgrade") {
+ // Sets peer address if exists.
+ socket->GetPeerAddress(&request.peer);
+
+ if (request.HasHeaderValue("connection", "upgrade")) {
connection->web_socket_.reset(WebSocket::CreateWebSocket(connection,
request,
&pos));
@@ -195,7 +204,7 @@ HttpServer::~HttpServer() {
// Input character types.
enum header_parse_inputs {
- INPUT_SPACE,
+ INPUT_LWS,
INPUT_CR,
INPUT_LF,
INPUT_COLON,
@@ -234,7 +243,8 @@ int parser_state[MAX_STATES][MAX_INPUTS] = {
int charToInput(char ch) {
switch(ch) {
case ' ':
- return INPUT_SPACE;
+ case '\t':
+ return INPUT_LWS;
case '\r':
return INPUT_CR;
case '\n':
@@ -260,6 +270,7 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
int next_state = parser_state[state][input];
bool transition = (next_state != state);
+ HttpServerRequestInfo::HeadersMap::iterator it;
if (transition) {
// Do any actions based on state transitions.
switch (state) {
@@ -281,10 +292,16 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
buffer.clear();
break;
case ST_VALUE:
- TrimWhitespaceASCII(buffer, TRIM_LEADING, &header_value);
- // TODO(mbelshe): Deal better with duplicate headers
- DCHECK(info->headers.find(header_name) == info->headers.end());
- info->headers[header_name] = header_value;
+ base::TrimWhitespaceASCII(buffer, base::TRIM_LEADING, &header_value);
+ it = info->headers.find(header_name);
+ // See last paragraph ("Multiple message-header fields...")
+ // of www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+ if (it == info->headers.end()) {
+ info->headers[header_name] = header_value;
+ } else {
+ it->second.append(",");
+ it->second.append(header_value);
+ }
buffer.clear();
break;
case ST_SEPARATOR:
diff --git a/chromium/net/server/http_server.h b/chromium/net/server/http_server.h
index 51bec956889..4309d122f1e 100644
--- a/chromium/net/server/http_server.h
+++ b/chromium/net/server/http_server.h
@@ -47,6 +47,10 @@ class HttpServer : public StreamListenSocket::Delegate,
void AcceptWebSocket(int connection_id,
const HttpServerRequestInfo& request);
void SendOverWebSocket(int connection_id, const std::string& data);
+ // Sends the provided data directly to the given connection. No validation is
+ // performed that data constitutes a valid HTTP response. A valid HTTP
+ // response may be split across multiple calls to SendRaw.
+ void SendRaw(int connection_id, const std::string& data);
void SendResponse(int connection_id, const HttpServerResponseInfo& response);
void Send(int connection_id,
HttpStatusCode status_code,
diff --git a/chromium/net/server/http_server_request_info.cc b/chromium/net/server/http_server_request_info.cc
index 67965f29aa3..8b65bee50c1 100644
--- a/chromium/net/server/http_server_request_info.cc
+++ b/chromium/net/server/http_server_request_info.cc
@@ -22,4 +22,21 @@ std::string HttpServerRequestInfo::GetHeaderValue(
return std::string();
}
+bool HttpServerRequestInfo::HasHeaderValue(
+ const std::string& header_name,
+ const std::string& header_value) const {
+ DCHECK_EQ(StringToLowerASCII(header_value), header_value);
+ std::string complete_value = GetHeaderValue(header_name);
+ StringToLowerASCII(&complete_value);
+ std::vector<std::string> value_items;
+ Tokenize(complete_value, ",", &value_items);
+ for (std::vector<std::string>::iterator it = value_items.begin();
+ it != value_items.end(); ++it) {
+ base::TrimString(*it, " \t", &*it);
+ if (*it == header_value)
+ return true;
+ }
+ return false;
+}
+
} // namespace net
diff --git a/chromium/net/server/http_server_request_info.h b/chromium/net/server/http_server_request_info.h
index 62824187901..1b02655b742 100644
--- a/chromium/net/server/http_server_request_info.h
+++ b/chromium/net/server/http_server_request_info.h
@@ -8,6 +8,8 @@
#include <map>
#include <string>
+#include "net/base/ip_endpoint.h"
+
namespace net {
// Meta information about an HTTP request.
@@ -23,6 +25,15 @@ class HttpServerRequestInfo {
// lower case.
std::string GetHeaderValue(const std::string& header_name) const;
+ // Checks for item in comma-separated header value for given header name.
+ // Both |header_name| and |header_value| should be lower case.
+ bool HasHeaderValue(
+ const std::string& header_name,
+ const std::string& header_value) const;
+
+ // Request peer address.
+ IPEndPoint peer;
+
// Request method.
std::string method;
diff --git a/chromium/net/server/http_server_unittest.cc b/chromium/net/server/http_server_unittest.cc
index 0c3c97f0a90..207c454acfb 100644
--- a/chromium/net/server/http_server_unittest.cc
+++ b/chromium/net/server/http_server_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <utility>
#include <vector>
#include "base/bind.h"
@@ -23,6 +24,7 @@
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
#include "net/server/http_server.h"
#include "net/server/http_server_request_info.h"
#include "net/socket/tcp_client_socket.h"
@@ -38,6 +40,8 @@ namespace net {
namespace {
+const int kMaxExpectedResponseLength = 2048;
+
void SetTimedOutAndQuitLoop(const base::WeakPtr<bool> timed_out,
const base::Closure& quit_loop_func) {
if (timed_out) {
@@ -86,6 +90,26 @@ class TestHttpClient {
Write();
}
+ bool Read(std::string* message) {
+ return Read(message, 1);
+ }
+
+ bool Read(std::string* message, int expected_bytes) {
+ int total_bytes_received = 0;
+ message->clear();
+ while (total_bytes_received < expected_bytes) {
+ net::TestCompletionCallback callback;
+ ReadInternal(callback.callback());
+ int bytes_received = callback.WaitForResult();
+ if (bytes_received <= 0)
+ return false;
+
+ total_bytes_received += bytes_received;
+ message->append(read_buffer_->data(), bytes_received);
+ }
+ return true;
+ }
+
private:
void OnConnect(const base::Closure& quit_loop, int result) {
connect_result_ = result;
@@ -108,6 +132,16 @@ class TestHttpClient {
Write();
}
+ void ReadInternal(const net::CompletionCallback& callback) {
+ read_buffer_ = new IOBufferWithSize(kMaxExpectedResponseLength);
+ int result = socket_->Read(read_buffer_,
+ kMaxExpectedResponseLength,
+ callback);
+ if (result != ERR_IO_PENDING)
+ callback.Run(result);
+ }
+
+ scoped_refptr<IOBufferWithSize> read_buffer_;
scoped_refptr<DrainableIOBuffer> write_buffer_;
scoped_ptr<TCPClientSocket> socket_;
int connect_result_;
@@ -128,7 +162,7 @@ class HttpServerTest : public testing::Test,
virtual void OnHttpRequest(int connection_id,
const HttpServerRequestInfo& info) OVERRIDE {
- requests_.push_back(info);
+ requests_.push_back(std::make_pair(info, connection_id));
if (requests_.size() == quit_after_request_count_)
run_loop_quit_func_.Run();
}
@@ -157,25 +191,52 @@ class HttpServerTest : public testing::Test,
return success;
}
+ HttpServerRequestInfo GetRequest(size_t request_index) {
+ return requests_[request_index].first;
+ }
+
+ int GetConnectionId(size_t request_index) {
+ return requests_[request_index].second;
+ }
+
protected:
scoped_refptr<HttpServer> server_;
IPEndPoint server_address_;
base::Closure run_loop_quit_func_;
- std::vector<HttpServerRequestInfo> requests_;
+ std::vector<std::pair<HttpServerRequestInfo, int> > requests_;
private:
size_t quit_after_request_count_;
};
+class WebSocketTest : public HttpServerTest {
+ virtual void OnHttpRequest(int connection_id,
+ const HttpServerRequestInfo& info) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnWebSocketRequest(int connection_id,
+ const HttpServerRequestInfo& info) OVERRIDE {
+ HttpServerTest::OnHttpRequest(connection_id, info);
+ }
+
+ virtual void OnWebSocketMessage(int connection_id,
+ const std::string& data) OVERRIDE {
+ }
+};
+
TEST_F(HttpServerTest, Request) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
client.Send("GET /test HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ("GET", requests_[0].method);
- ASSERT_EQ("/test", requests_[0].path);
- ASSERT_EQ("", requests_[0].data);
- ASSERT_EQ(0u, requests_[0].headers.size());
+ ASSERT_EQ("GET", GetRequest(0).method);
+ ASSERT_EQ("/test", GetRequest(0).path);
+ ASSERT_EQ("", GetRequest(0).data);
+ ASSERT_EQ(0u, GetRequest(0).headers.size());
+ ASSERT_TRUE(StartsWithASCII(GetRequest(0).peer.ToString(),
+ "127.0.0.1",
+ true));
}
TEST_F(HttpServerTest, RequestWithHeaders) {
@@ -198,16 +259,81 @@ TEST_F(HttpServerTest, RequestWithHeaders) {
client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ("", requests_[0].data);
+ ASSERT_EQ("", GetRequest(0).data);
for (size_t i = 0; i < arraysize(kHeaders); ++i) {
std::string field = StringToLowerASCII(std::string(kHeaders[i][0]));
std::string value = kHeaders[i][2];
- ASSERT_EQ(1u, requests_[0].headers.count(field)) << field;
- ASSERT_EQ(value, requests_[0].headers[field]) << kHeaders[i][0];
+ ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
+ ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
}
}
+TEST_F(HttpServerTest, RequestWithDuplicateHeaders) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ const char* kHeaders[][3] = {
+ {"FirstHeader", ": ", "1"},
+ {"DuplicateHeader", ": ", "2"},
+ {"MiddleHeader", ": ", "3"},
+ {"DuplicateHeader", ": ", "4"},
+ {"LastHeader", ": ", "5"},
+ };
+ std::string headers;
+ for (size_t i = 0; i < arraysize(kHeaders); ++i) {
+ headers +=
+ std::string(kHeaders[i][0]) + kHeaders[i][1] + kHeaders[i][2] + "\r\n";
+ }
+
+ client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ("", GetRequest(0).data);
+
+ for (size_t i = 0; i < arraysize(kHeaders); ++i) {
+ std::string field = StringToLowerASCII(std::string(kHeaders[i][0]));
+ std::string value = (field == "duplicateheader") ? "2,4" : kHeaders[i][2];
+ ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
+ ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
+ }
+}
+
+TEST_F(HttpServerTest, HasHeaderValueTest) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ const char* kHeaders[] = {
+ "Header: Abcd",
+ "HeaderWithNoWhitespace:E",
+ "HeaderWithWhitespace : \t f \t ",
+ "DuplicateHeader: g",
+ "HeaderWithComma: h, i ,j",
+ "DuplicateHeader: k",
+ "EmptyHeader:",
+ "EmptyHeaderWithWhitespace: \t ",
+ "HeaderWithNonASCII: \xf7",
+ };
+ std::string headers;
+ for (size_t i = 0; i < arraysize(kHeaders); ++i) {
+ headers += std::string(kHeaders[i]) + "\r\n";
+ }
+
+ client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ ASSERT_EQ("", GetRequest(0).data);
+
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("header", "abcd"));
+ ASSERT_FALSE(GetRequest(0).HasHeaderValue("header", "bc"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnowhitespace", "e"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithwhitespace", "f"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "g"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "h"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "i"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "j"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "k"));
+ ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheader", "x"));
+ ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheaderwithwhitespace", "x"));
+ ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnonascii", "\xf7"));
+}
+
TEST_F(HttpServerTest, RequestWithBody) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
@@ -219,12 +345,25 @@ TEST_F(HttpServerTest, RequestWithBody) {
body.length(),
body.c_str()));
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ(2u, requests_[0].headers.size());
- ASSERT_EQ(body.length(), requests_[0].data.length());
+ ASSERT_EQ(2u, GetRequest(0).headers.size());
+ ASSERT_EQ(body.length(), GetRequest(0).data.length());
ASSERT_EQ('a', body[0]);
ASSERT_EQ('c', *body.rbegin());
}
+TEST_F(WebSocketTest, RequestWebSocket) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send(
+ "GET /test HTTP/1.1\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: SomethingElse, Upgrade\r\n"
+ "Sec-WebSocket-Version: 8\r\n"
+ "Sec-WebSocket-Key: key\r\n"
+ "\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+}
+
TEST_F(HttpServerTest, RequestWithTooLargeBody) {
class TestURLFetcherDelegate : public URLFetcherDelegate {
public:
@@ -260,6 +399,34 @@ TEST_F(HttpServerTest, RequestWithTooLargeBody) {
ASSERT_EQ(0u, requests_.size());
}
+TEST_F(HttpServerTest, Send200) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send("GET /test HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ server_->Send200(GetConnectionId(0), "Response!", "text/plain");
+
+ std::string response;
+ ASSERT_TRUE(client.Read(&response));
+ ASSERT_TRUE(StartsWithASCII(response, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response, "Response!", true));
+}
+
+TEST_F(HttpServerTest, SendRaw) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send("GET /test HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ server_->SendRaw(GetConnectionId(0), "Raw Data ");
+ server_->SendRaw(GetConnectionId(0), "More Data");
+ server_->SendRaw(GetConnectionId(0), "Third Piece of Data");
+
+ const std::string expected_response("Raw Data More DataThird Piece of Data");
+ std::string response;
+ ASSERT_TRUE(client.Read(&response, expected_response.length()));
+ ASSERT_EQ(expected_response, response);
+}
+
namespace {
class MockStreamListenSocket : public StreamListenSocket {
@@ -280,17 +447,17 @@ TEST_F(HttpServerTest, RequestWithBodySplitAcrossPackets) {
new MockStreamListenSocket(server_.get());
server_->DidAccept(NULL, make_scoped_ptr(socket));
std::string body("body");
- std::string request = base::StringPrintf(
+ std::string request_text = base::StringPrintf(
"GET /test HTTP/1.1\r\n"
"SomeHeader: 1\r\n"
"Content-Length: %" PRIuS "\r\n\r\n%s",
body.length(),
body.c_str());
- server_->DidRead(socket, request.c_str(), request.length() - 2);
+ server_->DidRead(socket, request_text.c_str(), request_text.length() - 2);
ASSERT_EQ(0u, requests_.size());
- server_->DidRead(socket, request.c_str() + request.length() - 2, 2);
+ server_->DidRead(socket, request_text.c_str() + request_text.length() - 2, 2);
ASSERT_EQ(1u, requests_.size());
- ASSERT_EQ(body, requests_[0].data);
+ ASSERT_EQ(body, GetRequest(0).data);
}
TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
@@ -305,15 +472,35 @@ TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
body.length(),
body.c_str()));
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ(body, requests_[0].data);
+ ASSERT_EQ(body, GetRequest(0).data);
+
+ int client_connection_id = GetConnectionId(0);
+ server_->Send200(client_connection_id, "Content for /test", "text/plain");
+ std::string response1;
+ ASSERT_TRUE(client.Read(&response1));
+ ASSERT_TRUE(StartsWithASCII(response1, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response1, "Content for /test", true));
client.Send("GET /test2 HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(2));
- ASSERT_EQ("/test2", requests_[1].path);
+ ASSERT_EQ("/test2", GetRequest(1).path);
+
+ ASSERT_EQ(client_connection_id, GetConnectionId(1));
+ server_->Send404(client_connection_id);
+ std::string response2;
+ ASSERT_TRUE(client.Read(&response2));
+ ASSERT_TRUE(StartsWithASCII(response2, "HTTP/1.1 404 Not Found", true));
client.Send("GET /test3 HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(3));
- ASSERT_EQ("/test3", requests_[2].path);
+ ASSERT_EQ("/test3", GetRequest(2).path);
+
+ ASSERT_EQ(client_connection_id, GetConnectionId(2));
+ server_->Send200(client_connection_id, "Content for /test3", "text/plain");
+ std::string response3;
+ ASSERT_TRUE(client.Read(&response3));
+ ASSERT_TRUE(StartsWithASCII(response3, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response3, "Content for /test3", true));
}
} // namespace net
diff --git a/chromium/net/socket/buffered_write_stream_socket.cc b/chromium/net/socket/buffered_write_stream_socket.cc
index 87d3c337e24..e69de29bb2d 100644
--- a/chromium/net/socket/buffered_write_stream_socket.cc
+++ b/chromium/net/socket/buffered_write_stream_socket.cc
@@ -1,161 +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/buffered_write_stream_socket.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/message_loop/message_loop.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-
-namespace net {
-
-namespace {
-
-void AppendBuffer(GrowableIOBuffer* dst, IOBuffer* src, int src_len) {
- int old_capacity = dst->capacity();
- dst->SetCapacity(old_capacity + src_len);
- memcpy(dst->StartOfBuffer() + old_capacity, src->data(), src_len);
-}
-
-} // anonymous namespace
-
-BufferedWriteStreamSocket::BufferedWriteStreamSocket(
- scoped_ptr<StreamSocket> socket_to_wrap)
- : wrapped_socket_(socket_to_wrap.Pass()),
- io_buffer_(new GrowableIOBuffer()),
- backup_buffer_(new GrowableIOBuffer()),
- callback_pending_(false),
- wrapped_write_in_progress_(false),
- error_(0),
- weak_factory_(this) {
-}
-
-BufferedWriteStreamSocket::~BufferedWriteStreamSocket() {
-}
-
-int BufferedWriteStreamSocket::Read(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- return wrapped_socket_->Read(buf, buf_len, callback);
-}
-
-int BufferedWriteStreamSocket::Write(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- if (error_) {
- return error_;
- }
- GrowableIOBuffer* idle_buffer =
- wrapped_write_in_progress_ ? backup_buffer_.get() : io_buffer_.get();
- AppendBuffer(idle_buffer, buf, buf_len);
- if (!callback_pending_) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&BufferedWriteStreamSocket::DoDelayedWrite,
- weak_factory_.GetWeakPtr()));
- callback_pending_ = true;
- }
- return buf_len;
-}
-
-bool BufferedWriteStreamSocket::SetReceiveBufferSize(int32 size) {
- return wrapped_socket_->SetReceiveBufferSize(size);
-}
-
-bool BufferedWriteStreamSocket::SetSendBufferSize(int32 size) {
- return wrapped_socket_->SetSendBufferSize(size);
-}
-
-int BufferedWriteStreamSocket::Connect(const CompletionCallback& callback) {
- return wrapped_socket_->Connect(callback);
-}
-
-void BufferedWriteStreamSocket::Disconnect() {
- wrapped_socket_->Disconnect();
-}
-
-bool BufferedWriteStreamSocket::IsConnected() const {
- return wrapped_socket_->IsConnected();
-}
-
-bool BufferedWriteStreamSocket::IsConnectedAndIdle() const {
- return wrapped_socket_->IsConnectedAndIdle();
-}
-
-int BufferedWriteStreamSocket::GetPeerAddress(IPEndPoint* address) const {
- return wrapped_socket_->GetPeerAddress(address);
-}
-
-int BufferedWriteStreamSocket::GetLocalAddress(IPEndPoint* address) const {
- return wrapped_socket_->GetLocalAddress(address);
-}
-
-const BoundNetLog& BufferedWriteStreamSocket::NetLog() const {
- return wrapped_socket_->NetLog();
-}
-
-void BufferedWriteStreamSocket::SetSubresourceSpeculation() {
- wrapped_socket_->SetSubresourceSpeculation();
-}
-
-void BufferedWriteStreamSocket::SetOmniboxSpeculation() {
- wrapped_socket_->SetOmniboxSpeculation();
-}
-
-bool BufferedWriteStreamSocket::WasEverUsed() const {
- return wrapped_socket_->WasEverUsed();
-}
-
-bool BufferedWriteStreamSocket::UsingTCPFastOpen() const {
- return wrapped_socket_->UsingTCPFastOpen();
-}
-
-bool BufferedWriteStreamSocket::WasNpnNegotiated() const {
- return wrapped_socket_->WasNpnNegotiated();
-}
-
-NextProto BufferedWriteStreamSocket::GetNegotiatedProtocol() const {
- return wrapped_socket_->GetNegotiatedProtocol();
-}
-
-bool BufferedWriteStreamSocket::GetSSLInfo(SSLInfo* ssl_info) {
- return wrapped_socket_->GetSSLInfo(ssl_info);
-}
-
-void BufferedWriteStreamSocket::DoDelayedWrite() {
- int result = wrapped_socket_->Write(
- io_buffer_.get(),
- io_buffer_->RemainingCapacity(),
- base::Bind(&BufferedWriteStreamSocket::OnIOComplete,
- base::Unretained(this)));
- if (result == ERR_IO_PENDING) {
- callback_pending_ = true;
- wrapped_write_in_progress_ = true;
- } else {
- OnIOComplete(result);
- }
-}
-
-void BufferedWriteStreamSocket::OnIOComplete(int result) {
- callback_pending_ = false;
- wrapped_write_in_progress_ = false;
- if (backup_buffer_->RemainingCapacity()) {
- AppendBuffer(io_buffer_.get(), backup_buffer_.get(),
- backup_buffer_->RemainingCapacity());
- backup_buffer_->SetCapacity(0);
- }
- if (result < 0) {
- error_ = result;
- io_buffer_->SetCapacity(0);
- } else {
- io_buffer_->set_offset(io_buffer_->offset() + result);
- if (io_buffer_->RemainingCapacity()) {
- DoDelayedWrite();
- } else {
- io_buffer_->SetCapacity(0);
- }
- }
-}
-
-} // namespace net
diff --git a/chromium/net/socket/buffered_write_stream_socket.h b/chromium/net/socket/buffered_write_stream_socket.h
index 803018eefe3..e69de29bb2d 100644
--- a/chromium/net/socket/buffered_write_stream_socket.h
+++ b/chromium/net/socket/buffered_write_stream_socket.h
@@ -1,83 +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_BUFFERED_WRITE_STREAM_SOCKET_H_
-#define NET_SOCKET_BUFFERED_WRITE_STREAM_SOCKET_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "net/base/net_log.h"
-#include "net/socket/stream_socket.h"
-
-namespace base {
-class TimeDelta;
-}
-
-namespace net {
-
-class AddressList;
-class GrowableIOBuffer;
-class IPEndPoint;
-
-// A StreamSocket decorator. All functions are passed through to the wrapped
-// socket, except for Write().
-//
-// Writes are buffered locally so that multiple Write()s to this class are
-// issued as only one Write() to the wrapped socket. This is useful to force
-// multiple requests to be issued in a single packet, as is needed to trigger
-// edge cases in HTTP pipelining.
-//
-// Note that the Write() always returns synchronously. It will either buffer the
-// entire input or return the most recently reported error.
-//
-// There are no bounds on the local buffer size. Use carefully.
-class NET_EXPORT_PRIVATE BufferedWriteStreamSocket : public StreamSocket {
- public:
- explicit BufferedWriteStreamSocket(scoped_ptr<StreamSocket> socket_to_wrap);
- virtual ~BufferedWriteStreamSocket();
-
- // Socket interface
- 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;
-
- // StreamSocket interface
- 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;
-
- private:
- void DoDelayedWrite();
- void OnIOComplete(int result);
-
- scoped_ptr<StreamSocket> wrapped_socket_;
- scoped_refptr<GrowableIOBuffer> io_buffer_;
- scoped_refptr<GrowableIOBuffer> backup_buffer_;
- bool callback_pending_;
- bool wrapped_write_in_progress_;
- int error_;
-
- base::WeakPtrFactory<BufferedWriteStreamSocket> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(BufferedWriteStreamSocket);
-};
-
-} // namespace net
-
-#endif // NET_SOCKET_STREAM_SOCKET_H_
diff --git a/chromium/net/socket/buffered_write_stream_socket_unittest.cc b/chromium/net/socket/buffered_write_stream_socket_unittest.cc
index 485295f33f6..e69de29bb2d 100644
--- a/chromium/net/socket/buffered_write_stream_socket_unittest.cc
+++ b/chromium/net/socket/buffered_write_stream_socket_unittest.cc
@@ -1,124 +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/buffered_write_stream_socket.h"
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/socket/socket_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-class BufferedWriteStreamSocketTest : public testing::Test {
- public:
- void Finish() {
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_TRUE(data_->at_read_eof());
- EXPECT_TRUE(data_->at_write_eof());
- }
-
- void Initialize(MockWrite* writes, size_t writes_count) {
- data_.reset(new DeterministicSocketData(NULL, 0, writes, writes_count));
- data_->set_connect_data(MockConnect(SYNCHRONOUS, 0));
- if (writes_count) {
- data_->StopAfter(writes_count);
- }
- scoped_ptr<DeterministicMockTCPClientSocket> wrapped_socket(
- new DeterministicMockTCPClientSocket(net_log_.net_log(), data_.get()));
- data_->set_delegate(wrapped_socket->AsWeakPtr());
- socket_.reset(new BufferedWriteStreamSocket(
- wrapped_socket.PassAs<StreamSocket>()));
- socket_->Connect(callback_.callback());
- }
-
- void TestWrite(const char* text) {
- scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(text));
- EXPECT_EQ(buf->size(),
- socket_->Write(buf.get(), buf->size(), callback_.callback()));
- }
-
- scoped_ptr<BufferedWriteStreamSocket> socket_;
- scoped_ptr<DeterministicSocketData> data_;
- BoundNetLog net_log_;
- TestCompletionCallback callback_;
-};
-
-TEST_F(BufferedWriteStreamSocketTest, SingleWrite) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "abc"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abc");
- Finish();
-}
-
-TEST_F(BufferedWriteStreamSocketTest, AsyncWrite) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "abc"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abc");
- data_->Run();
- Finish();
-}
-
-TEST_F(BufferedWriteStreamSocketTest, TwoWritesIntoOne) {
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, 0, "abcdef"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abc");
- TestWrite("def");
- Finish();
-}
-
-TEST_F(BufferedWriteStreamSocketTest, WriteWhileBlocked) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "abc"),
- MockWrite(ASYNC, 1, "def"),
- MockWrite(ASYNC, 2, "ghi"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abc");
- base::MessageLoop::current()->RunUntilIdle();
- TestWrite("def");
- data_->RunFor(1);
- TestWrite("ghi");
- data_->RunFor(1);
- Finish();
-}
-
-TEST_F(BufferedWriteStreamSocketTest, ContinuesPartialWrite) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "abc"),
- MockWrite(ASYNC, 1, "def"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abcdef");
- data_->Run();
- Finish();
-}
-
-TEST_F(BufferedWriteStreamSocketTest, TwoSeparateWrites) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, "abc"),
- MockWrite(ASYNC, 1, "def"),
- };
- Initialize(writes, arraysize(writes));
- TestWrite("abc");
- data_->RunFor(1);
- TestWrite("def");
- data_->RunFor(1);
- Finish();
-}
-
-} // anonymous namespace
-
-} // namespace net
diff --git a/chromium/net/socket/client_socket_handle.cc b/chromium/net/socket/client_socket_handle.cc
index e42e9fcada3..53bcd77499a 100644
--- a/chromium/net/socket/client_socket_handle.cc
+++ b/chromium/net/socket/client_socket_handle.cc
@@ -19,7 +19,7 @@ ClientSocketHandle::ClientSocketHandle()
: is_initialized_(false),
pool_(NULL),
higher_pool_(NULL),
- is_reused_(false),
+ reuse_type_(ClientSocketHandle::UNUSED),
callback_(base::Bind(&ClientSocketHandle::OnIOComplete,
base::Unretained(this))),
is_ssl_error_(false) {}
@@ -58,7 +58,7 @@ void ClientSocketHandle::ResetInternal(bool cancel) {
is_initialized_ = false;
socket_.reset();
group_name_.clear();
- is_reused_ = false;
+ reuse_type_ = ClientSocketHandle::UNUSED;
user_callback_.Reset();
if (higher_pool_)
RemoveHigherLayeredPool(higher_pool_);
diff --git a/chromium/net/socket/client_socket_handle.h b/chromium/net/socket/client_socket_handle.h
index 30b7c03e9dc..0899d9a9bb0 100644
--- a/chromium/net/socket/client_socket_handle.h
+++ b/chromium/net/socket/client_socket_handle.h
@@ -126,7 +126,7 @@ class NET_EXPORT ClientSocketHandle {
// SetSocket() may also be used if this handle is used as simply for
// socket storage (e.g., http://crbug.com/37810).
void SetSocket(scoped_ptr<StreamSocket> s);
- void set_is_reused(bool is_reused) { is_reused_ = is_reused; }
+ void set_reuse_type(SocketReuseType reuse_type) { reuse_type_ = reuse_type; }
void set_idle_time(base::TimeDelta idle_time) { idle_time_ = idle_time; }
void set_pool_id(int id) { pool_id_ = id; }
void set_is_ssl_error(bool is_ssl_error) { is_ssl_error_ = is_ssl_error; }
@@ -161,17 +161,9 @@ class NET_EXPORT ClientSocketHandle {
// These may only be used if is_initialized() is true.
const std::string& group_name() const { return group_name_; }
int id() const { return pool_id_; }
- bool is_reused() const { return is_reused_; }
+ bool is_reused() const { return reuse_type_ == REUSED_IDLE; }
base::TimeDelta idle_time() const { return idle_time_; }
- SocketReuseType reuse_type() const {
- if (is_reused()) {
- return REUSED_IDLE;
- } else if (idle_time() == base::TimeDelta()) {
- return UNUSED;
- } else {
- return UNUSED_IDLE;
- }
- }
+ SocketReuseType reuse_type() const { return reuse_type_; }
const LoadTimingInfo::ConnectTiming& connect_timing() const {
return connect_timing_;
}
@@ -200,7 +192,7 @@ class NET_EXPORT ClientSocketHandle {
HigherLayeredPool* higher_pool_;
scoped_ptr<StreamSocket> socket_;
std::string group_name_;
- bool is_reused_;
+ SocketReuseType reuse_type_;
CompletionCallback callback_;
CompletionCallback user_callback_;
base::TimeDelta idle_time_;
diff --git a/chromium/net/socket/client_socket_pool_base.cc b/chromium/net/socket/client_socket_pool_base.cc
index 1c79923a400..9e1abf482a5 100644
--- a/chromium/net/socket/client_socket_pool_base.cc
+++ b/chromium/net/socket/client_socket_pool_base.cc
@@ -15,7 +15,6 @@
#include "base/values.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
-#include "net/socket/client_socket_handle.h"
using base::TimeDelta;
@@ -401,7 +400,7 @@ int ClientSocketPoolBaseHelper::RequestSocketInternal(
if (rv == OK) {
LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
if (!preconnecting) {
- HandOutSocket(connect_job->PassSocket(), false /* not reused */,
+ HandOutSocket(connect_job->PassSocket(), ClientSocketHandle::UNUSED,
connect_job->connect_timing(), handle, base::TimeDelta(),
group, request.net_log());
} else {
@@ -427,7 +426,7 @@ int ClientSocketPoolBaseHelper::RequestSocketInternal(
error_socket = connect_job->PassSocket();
}
if (error_socket) {
- HandOutSocket(error_socket.Pass(), false /* not reused */,
+ HandOutSocket(error_socket.Pass(), ClientSocketHandle::UNUSED,
connect_job->connect_timing(), handle, base::TimeDelta(),
group, request.net_log());
} else if (group->IsEmpty()) {
@@ -449,7 +448,7 @@ bool ClientSocketPoolBaseHelper::AssignIdleSocketToRequest(
// the |idle_socket_it| will be set to the newest used idle socket.
for (std::list<IdleSocket>::iterator it = idle_sockets->begin();
it != idle_sockets->end();) {
- if (!it->socket->IsConnectedAndIdle()) {
+ if (!it->IsUsable()) {
DecrementIdleCount();
delete it->socket;
it = idle_sockets->erase(it);
@@ -476,9 +475,16 @@ bool ClientSocketPoolBaseHelper::AssignIdleSocketToRequest(
base::TimeTicks::Now() - idle_socket_it->start_time;
IdleSocket idle_socket = *idle_socket_it;
idle_sockets->erase(idle_socket_it);
+ // TODO(davidben): If |idle_time| is under some low watermark, consider
+ // treating as UNUSED rather than UNUSED_IDLE. This will avoid
+ // HttpNetworkTransaction retrying on some errors.
+ ClientSocketHandle::SocketReuseType reuse_type =
+ idle_socket.socket->WasEverUsed() ?
+ ClientSocketHandle::REUSED_IDLE :
+ ClientSocketHandle::UNUSED_IDLE;
HandOutSocket(
scoped_ptr<StreamSocket>(idle_socket.socket),
- idle_socket.socket->WasEverUsed(),
+ reuse_type,
LoadTimingInfo::ConnectTiming(),
request.handle(),
idle_time,
@@ -642,15 +648,19 @@ base::DictionaryValue* ClientSocketPoolBaseHelper::GetInfoAsValue(
return dict;
}
+bool ClientSocketPoolBaseHelper::IdleSocket::IsUsable() const {
+ if (socket->WasEverUsed())
+ return socket->IsConnectedAndIdle();
+ return socket->IsConnected();
+}
+
bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup(
base::TimeTicks now,
base::TimeDelta timeout) const {
bool timed_out = (now - start_time) >= timeout;
if (timed_out)
return true;
- if (socket->WasEverUsed())
- return !socket->IsConnectedAndIdle();
- return !socket->IsConnected();
+ return !IsUsable();
}
void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) {
@@ -878,7 +888,7 @@ void ClientSocketPoolBaseHelper::OnConnectJobComplete(
if (request) {
LogBoundConnectJobToRequest(job_log.source(), *request);
HandOutSocket(
- socket.Pass(), false /* unused socket */, connect_timing,
+ socket.Pass(), ClientSocketHandle::UNUSED, connect_timing,
request->handle(), base::TimeDelta(), group, request->net_log());
request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL);
InvokeUserCallbackLater(request->handle(), request->callback(), result);
@@ -898,7 +908,7 @@ void ClientSocketPoolBaseHelper::OnConnectJobComplete(
RemoveConnectJob(job, group);
if (socket.get()) {
handed_out_socket = true;
- HandOutSocket(socket.Pass(), false /* unused socket */,
+ HandOutSocket(socket.Pass(), ClientSocketHandle::UNUSED,
connect_timing, request->handle(), base::TimeDelta(),
group, request->net_log());
}
@@ -963,7 +973,7 @@ void ClientSocketPoolBaseHelper::ProcessPendingRequest(
void ClientSocketPoolBaseHelper::HandOutSocket(
scoped_ptr<StreamSocket> socket,
- bool reused,
+ ClientSocketHandle::SocketReuseType reuse_type,
const LoadTimingInfo::ConnectTiming& connect_timing,
ClientSocketHandle* handle,
base::TimeDelta idle_time,
@@ -971,12 +981,12 @@ void ClientSocketPoolBaseHelper::HandOutSocket(
const BoundNetLog& net_log) {
DCHECK(socket);
handle->SetSocket(socket.Pass());
- handle->set_is_reused(reused);
+ handle->set_reuse_type(reuse_type);
handle->set_idle_time(idle_time);
handle->set_pool_id(pool_generation_number_);
handle->set_connect_timing(connect_timing);
- if (reused) {
+ if (handle->is_reused()) {
net_log.AddEvent(
NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
NetLog::IntegerCallback(
diff --git a/chromium/net/socket/client_socket_pool_base.h b/chromium/net/socket/client_socket_pool_base.h
index 2c2ddb57abc..8079cd4e5c7 100644
--- a/chromium/net/socket/client_socket_pool_base.h
+++ b/chromium/net/socket/client_socket_pool_base.h
@@ -46,6 +46,7 @@
#include "net/base/network_change_notifier.h"
#include "net/base/priority_queue.h"
#include "net/base/request_priority.h"
+#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool.h"
#include "net/socket/stream_socket.h"
@@ -338,14 +339,19 @@ class NET_EXPORT_PRIVATE ClientSocketPoolBaseHelper
struct IdleSocket {
IdleSocket() : socket(NULL) {}
+ // An idle socket can't be used if it is disconnected or has been used
+ // before and has received data unexpectedly (hence no longer idle). The
+ // unread data would be mistaken for the beginning of the next response if
+ // we were to use the socket for a new request.
+ //
+ // Note that a socket that has never been used before (like a preconnected
+ // socket) may be used even with unread data. This may be, e.g., a SPDY
+ // SETTINGS frame.
+ bool IsUsable() const;
+
// An idle socket should be removed if it can't be reused, or has been idle
// for too long. |now| is the current time value (TimeTicks::Now()).
// |timeout| is the length of time to wait before timing out an idle socket.
- //
- // An idle socket can't be reused if it is disconnected or has received
- // data unexpectedly (hence no longer idle). The unread data would be
- // mistaken for the beginning of the next response if we were to reuse the
- // socket for a new request.
bool ShouldCleanup(base::TimeTicks now, base::TimeDelta timeout) const;
StreamSocket* socket;
@@ -528,7 +534,7 @@ class NET_EXPORT_PRIVATE ClientSocketPoolBaseHelper
// Assigns |socket| to |handle| and updates |group|'s counters appropriately.
void HandOutSocket(scoped_ptr<StreamSocket> socket,
- bool reused,
+ ClientSocketHandle::SocketReuseType reuse_type,
const LoadTimingInfo::ConnectTiming& connect_timing,
ClientSocketHandle* handle,
base::TimeDelta time_idle,
diff --git a/chromium/net/socket/client_socket_pool_base_unittest.cc b/chromium/net/socket/client_socket_pool_base_unittest.cc
index 46f4e40c0d4..5a672b44a4e 100644
--- a/chromium/net/socket/client_socket_pool_base_unittest.cc
+++ b/chromium/net/socket/client_socket_pool_base_unittest.cc
@@ -116,14 +116,26 @@ class MockClientSocket : public StreamSocket {
public:
explicit MockClientSocket(net::NetLog* net_log)
: connected_(false),
+ has_unread_data_(false),
net_log_(BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)),
was_used_to_convey_data_(false) {
}
+ // Sets whether the socket has unread data. If true, the next call to Read()
+ // will return 1 byte and IsConnectedAndIdle() will return false.
+ void set_has_unread_data(bool has_unread_data) {
+ has_unread_data_ = has_unread_data;
+ }
+
// Socket implementation.
virtual int Read(
IOBuffer* /* buf */, int len,
const CompletionCallback& /* callback */) OVERRIDE {
+ if (has_unread_data_ && len > 0) {
+ has_unread_data_ = false;
+ was_used_to_convey_data_ = true;
+ return 1;
+ }
return ERR_UNEXPECTED;
}
@@ -133,8 +145,8 @@ class MockClientSocket : public StreamSocket {
was_used_to_convey_data_ = true;
return len;
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE { return true; }
- virtual bool SetSendBufferSize(int32 size) OVERRIDE { return true; }
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE { return OK; }
+ virtual int SetSendBufferSize(int32 size) OVERRIDE { return OK; }
// StreamSocket implementation.
virtual int Connect(const CompletionCallback& callback) OVERRIDE {
@@ -144,7 +156,9 @@ class MockClientSocket : public StreamSocket {
virtual void Disconnect() OVERRIDE { connected_ = false; }
virtual bool IsConnected() const OVERRIDE { return connected_; }
- virtual bool IsConnectedAndIdle() const OVERRIDE { return connected_; }
+ virtual bool IsConnectedAndIdle() const OVERRIDE {
+ return connected_ && !has_unread_data_;
+ }
virtual int GetPeerAddress(IPEndPoint* /* address */) const OVERRIDE {
return ERR_UNEXPECTED;
@@ -176,6 +190,7 @@ class MockClientSocket : public StreamSocket {
private:
bool connected_;
+ bool has_unread_data_;
BoundNetLog net_log_;
bool was_used_to_convey_data_;
@@ -245,6 +260,7 @@ class TestConnectJob : public ConnectJob {
kMockPendingRecoverableJob,
kMockAdditionalErrorStateJob,
kMockPendingAdditionalErrorStateJob,
+ kMockUnreadDataJob,
};
// The kMockPendingJob uses a slight delay before allowing the connect
@@ -372,6 +388,12 @@ class TestConnectJob : public ConnectJob {
false /* recoverable */),
base::TimeDelta::FromMilliseconds(2));
return ERR_IO_PENDING;
+ case kMockUnreadDataJob: {
+ int ret = DoConnect(true /* successful */, false /* sync */,
+ false /* recoverable */);
+ static_cast<MockClientSocket*>(socket())->set_has_unread_data(true);
+ return ret;
+ }
default:
NOTREACHED();
SetSocket(scoped_ptr<StreamSocket>());
@@ -3541,7 +3563,7 @@ TEST_F(ClientSocketPoolBaseTest, PreconnectJobsTakenByNormalRequests) {
ASSERT_EQ(OK, callback1.WaitForResult());
- // Make sure if a preconneced socket is not fully connected when a request
+ // Make sure if a preconnected socket is not fully connected when a request
// starts, it has a connect start time.
TestLoadTimingInfoConnectedNotReused(handle1);
handle1.Reset();
@@ -3720,6 +3742,46 @@ TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) {
EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a"));
}
+// Tests that a preconnect that starts out with unread data can still be used.
+// http://crbug.com/334467
+TEST_F(ClientSocketPoolBaseTest, PreconnectWithUnreadData) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockUnreadDataJob);
+
+ pool_->RequestSockets("a", &params_, 1, BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->NumUnassignedConnectJobsInGroup("a"));
+ EXPECT_EQ(1, pool_->IdleSocketCountInGroup("a"));
+
+ // Fail future jobs to be sure that handle receives the preconnected socket
+ // rather than closing it and making a new one.
+ connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ EXPECT_EQ(OK, handle.Init("a",
+ params_,
+ DEFAULT_PRIORITY,
+ callback.callback(),
+ pool_.get(),
+ BoundNetLog()));
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->NumUnassignedConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ // Drain the pending read.
+ EXPECT_EQ(1, handle.socket()->Read(NULL, 1, CompletionCallback()));
+
+ TestLoadTimingInfoConnectedReused(handle);
+ handle.Reset();
+
+ // The socket should be usable now that it's idle again.
+ EXPECT_EQ(1, pool_->IdleSocketCountInGroup("a"));
+}
+
class MockLayeredPool : public HigherLayeredPool {
public:
MockLayeredPool(TestClientSocketPool* pool,
diff --git a/chromium/net/socket/client_socket_pool_manager.cc b/chromium/net/socket/client_socket_pool_manager.cc
index 24d6b70ced5..f81b4bc1db5 100644
--- a/chromium/net/socket/client_socket_pool_manager.cc
+++ b/chromium/net/socket/client_socket_pool_manager.cc
@@ -132,8 +132,7 @@ int InitSocketPoolHelper(const GURL& request_url,
// should be the same for all connections, whereas version_max may
// change for version fallbacks.
std::string prefix = "ssl/";
- if (ssl_config_for_origin.version_max !=
- SSLConfigService::default_version_max()) {
+ if (ssl_config_for_origin.version_max != net::kDefaultSSLVersionMax) {
switch (ssl_config_for_origin.version_max) {
case SSL_PROTOCOL_VERSION_TLS1_2:
prefix = "ssl(max:3.3)/";
@@ -182,7 +181,7 @@ int InitSocketPoolHelper(const GURL& request_url,
NULL,
*proxy_host_port.get(),
ssl_config_for_proxy,
- kPrivacyModeDisabled,
+ PRIVACY_MODE_DISABLED,
load_flags,
force_spdy_over_ssl,
want_spdy_over_npn);
@@ -216,7 +215,7 @@ int InitSocketPoolHelper(const GURL& request_url,
}
// Change group name if privacy mode is enabled.
- if (privacy_mode == kPrivacyModeEnabled)
+ if (privacy_mode == PRIVACY_MODE_ENABLED)
connection_group = "pm/" + connection_group;
// Deal with SSL - which layers on top of any given proxy.
diff --git a/chromium/net/socket/next_proto.cc b/chromium/net/socket/next_proto.cc
new file mode 100644
index 00000000000..c9172365c38
--- /dev/null
+++ b/chromium/net/socket/next_proto.cc
@@ -0,0 +1,73 @@
+// Copyright 2014 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 "next_proto.h"
+
+namespace net {
+
+NextProtoVector NextProtosHttpOnly() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ return next_protos;
+}
+
+NextProtoVector NextProtosDefaults() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ next_protos.push_back(kProtoSPDY3);
+ next_protos.push_back(kProtoSPDY31);
+ return next_protos;
+}
+
+NextProtoVector NextProtosWithSpdyAndQuic(bool spdy_enabled,
+ bool quic_enabled) {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ if (quic_enabled)
+ next_protos.push_back(kProtoQUIC1SPDY3);
+ if (spdy_enabled) {
+ next_protos.push_back(kProtoSPDY3);
+ next_protos.push_back(kProtoSPDY31);
+ }
+ return next_protos;
+}
+
+NextProtoVector NextProtosSpdy3() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ next_protos.push_back(kProtoQUIC1SPDY3);
+ next_protos.push_back(kProtoSPDY3);
+ return next_protos;
+}
+
+NextProtoVector NextProtosSpdy31() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ next_protos.push_back(kProtoQUIC1SPDY3);
+ next_protos.push_back(kProtoSPDY3);
+ next_protos.push_back(kProtoSPDY31);
+ return next_protos;
+}
+
+NextProtoVector NextProtosSpdy31WithSpdy2() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ next_protos.push_back(kProtoQUIC1SPDY3);
+ next_protos.push_back(kProtoDeprecatedSPDY2);
+ next_protos.push_back(kProtoSPDY3);
+ next_protos.push_back(kProtoSPDY31);
+ return next_protos;
+}
+
+NextProtoVector NextProtosSpdy4Http2() {
+ NextProtoVector next_protos;
+ next_protos.push_back(kProtoHTTP11);
+ next_protos.push_back(kProtoQUIC1SPDY3);
+ next_protos.push_back(kProtoSPDY3);
+ next_protos.push_back(kProtoSPDY31);
+ next_protos.push_back(kProtoSPDY4);
+ return next_protos;
+}
+
+} // namespace net
diff --git a/chromium/net/socket/next_proto.h b/chromium/net/socket/next_proto.h
index a2e5ab6902d..19ff55e0bc0 100644
--- a/chromium/net/socket/next_proto.h
+++ b/chromium/net/socket/next_proto.h
@@ -5,6 +5,10 @@
#ifndef NET_SOCKET_NEXT_PROTO_H_
#define NET_SOCKET_NEXT_PROTO_H_
+#include <vector>
+
+#include "net/base/net_export.h"
+
namespace net {
// Next Protocol Negotiation (NPN), if successful, results in agreement on an
@@ -20,16 +24,34 @@ enum NextProto {
kProtoSPDYMinimumVersion = kProtoDeprecatedSPDY2,
kProtoSPDY3,
kProtoSPDY31,
- kProtoSPDY4a2,
- // We lump in HTTP/2 with the SPDY protocols for now.
- kProtoHTTP2Draft04,
- kProtoSPDYMaximumVersion = kProtoHTTP2Draft04,
+ kProtoSPDY4, // SPDY4 is HTTP/2.
+ kProtoSPDYMaximumVersion = kProtoSPDY4,
kProtoQUIC1SPDY3,
kProtoMaximumVersion = kProtoQUIC1SPDY3,
};
+// List of protocols to use for NPN, used for configuring HttpNetworkSessions.
+typedef std::vector<NextProto> NextProtoVector;
+
+// Convenience functions to create NextProtoVector.
+
+NET_EXPORT NextProtoVector NextProtosHttpOnly();
+
+// Default values, which are subject to change over time. Currently just
+// SPDY 3 and 3.1.
+NET_EXPORT NextProtoVector NextProtosDefaults();
+
+NET_EXPORT NextProtoVector NextProtosWithSpdyAndQuic(bool spdy_enabled,
+ bool quic_enabled);
+
+// All of these also enable QUIC.
+NET_EXPORT NextProtoVector NextProtosSpdy3();
+NET_EXPORT NextProtoVector NextProtosSpdy31();
+NET_EXPORT NextProtoVector NextProtosSpdy31WithSpdy2();
+NET_EXPORT NextProtoVector NextProtosSpdy4Http2();
+
} // namespace net
#endif // NET_SOCKET_NEXT_PROTO_H_
diff --git a/chromium/net/socket/nss_ssl_util.cc b/chromium/net/socket/nss_ssl_util.cc
index 33e7e6b89ac..7b068545a55 100644
--- a/chromium/net/socket/nss_ssl_util.cc
+++ b/chromium/net/socket/nss_ssl_util.cc
@@ -201,7 +201,7 @@ class NSSSSLInitSingleton {
PRFileDesc* model_fd_;
};
-static base::LazyInstance<NSSSSLInitSingleton> g_nss_ssl_init_singleton =
+static base::LazyInstance<NSSSSLInitSingleton>::Leaky g_nss_ssl_init_singleton =
LAZY_INSTANCE_INITIALIZER;
// Initialize the NSS SSL library if it isn't already initialized. This must
@@ -330,6 +330,8 @@ int MapNSSError(PRErrorCode err) {
return ERR_SSL_BAD_RECORD_MAC_ALERT;
case SSL_ERROR_DECRYPT_ERROR_ALERT:
return ERR_SSL_DECRYPT_ERROR_ALERT;
+ case SSL_ERROR_UNRECOGNIZED_NAME_ALERT:
+ return ERR_SSL_UNRECOGNIZED_NAME_ALERT;
case SSL_ERROR_UNSAFE_NEGOTIATION:
return ERR_SSL_UNSAFE_NEGOTIATION;
case SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY:
@@ -355,12 +357,16 @@ int MapNSSError(PRErrorCode err) {
return ERR_SSL_INAPPROPRIATE_FALLBACK;
default: {
+ const char* err_name = PR_ErrorToName(err);
+ if (err_name == NULL)
+ err_name = "";
if (IS_SSL_ERROR(err)) {
- LOG(WARNING) << "Unknown SSL error " << err
+ LOG(WARNING) << "Unknown SSL error " << err << " (" << err_name << ")"
<< " mapped to net::ERR_SSL_PROTOCOL_ERROR";
return ERR_SSL_PROTOCOL_ERROR;
}
- LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ LOG(WARNING) << "Unknown error " << err << " (" << err_name << ")"
+ << " mapped to net::ERR_FAILED";
return ERR_FAILED;
}
}
diff --git a/chromium/net/socket/openssl_ssl_util.cc b/chromium/net/socket/openssl_ssl_util.cc
new file mode 100644
index 00000000000..36b8e6ca0e3
--- /dev/null
+++ b/chromium/net/socket/openssl_ssl_util.cc
@@ -0,0 +1,156 @@
+// Copyright 2014 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/openssl_ssl_util.h"
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "base/logging.h"
+#include "crypto/openssl_util.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+SslSetClearMask::SslSetClearMask()
+ : set_mask(0),
+ clear_mask(0) {
+}
+
+void SslSetClearMask::ConfigureFlag(long flag, bool state) {
+ (state ? set_mask : clear_mask) |= flag;
+ // Make sure we haven't got any intersection in the set & clear options.
+ DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state;
+}
+
+namespace {
+
+int MapOpenSSLErrorSSL() {
+ // Walk down the error stack to find the SSLerr generated reason.
+ unsigned long error_code;
+ do {
+ error_code = ERR_get_error();
+ if (error_code == 0)
+ return ERR_SSL_PROTOCOL_ERROR;
+ } while (ERR_GET_LIB(error_code) != ERR_LIB_SSL);
+
+ DVLOG(1) << "OpenSSL SSL error, reason: " << ERR_GET_REASON(error_code)
+ << ", name: " << ERR_error_string(error_code, NULL);
+ switch (ERR_GET_REASON(error_code)) {
+ case SSL_R_READ_TIMEOUT_EXPIRED:
+ return ERR_TIMED_OUT;
+ case SSL_R_BAD_RESPONSE_ARGUMENT:
+ return ERR_INVALID_ARGUMENT;
+ case SSL_R_UNKNOWN_CERTIFICATE_TYPE:
+ case SSL_R_UNKNOWN_CIPHER_TYPE:
+ case SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE:
+ case SSL_R_UNKNOWN_PKEY_TYPE:
+ case SSL_R_UNKNOWN_REMOTE_ERROR_TYPE:
+ case SSL_R_UNKNOWN_SSL_VERSION:
+ return ERR_NOT_IMPLEMENTED;
+ case SSL_R_UNSUPPORTED_SSL_VERSION:
+ case SSL_R_NO_CIPHER_MATCH:
+ case SSL_R_NO_SHARED_CIPHER:
+ case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:
+ case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
+ case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
+ case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
+ case SSL_R_TLSV1_ALERT_ACCESS_DENIED:
+ case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
+ return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+ case SSL_R_BAD_DECOMPRESSION:
+ case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:
+ return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
+ case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:
+ return ERR_SSL_BAD_RECORD_MAC_ALERT;
+ case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:
+ return ERR_SSL_DECRYPT_ERROR_ALERT;
+ case SSL_R_TLSV1_UNRECOGNIZED_NAME:
+ return ERR_SSL_UNRECOGNIZED_NAME_ALERT;
+ case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:
+ return ERR_SSL_UNSAFE_NEGOTIATION;
+ case SSL_R_WRONG_NUMBER_OF_KEY_BITS:
+ return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
+ // SSL_R_UNKNOWN_PROTOCOL is reported if premature application data is
+ // received (see http://crbug.com/42538), and also if all the protocol
+ // versions supported by the server were disabled in this socket instance.
+ // Mapped to ERR_SSL_PROTOCOL_ERROR for compatibility with other SSL sockets
+ // in the former scenario.
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_SSL_HANDSHAKE_FAILURE:
+ case SSL_R_DECRYPTION_FAILED:
+ case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:
+ case SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG:
+ case SSL_R_DIGEST_CHECK_FAILED:
+ case SSL_R_DUPLICATE_COMPRESSION_ID:
+ case SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER:
+ case SSL_R_ENCRYPTED_LENGTH_TOO_LONG:
+ case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:
+ case SSL_R_EXCESSIVE_MESSAGE_SIZE:
+ case SSL_R_EXTRA_DATA_IN_MESSAGE:
+ case SSL_R_GOT_A_FIN_BEFORE_A_CCS:
+ case SSL_R_ILLEGAL_PADDING:
+ case SSL_R_INVALID_CHALLENGE_LENGTH:
+ case SSL_R_INVALID_COMMAND:
+ case SSL_R_INVALID_PURPOSE:
+ case SSL_R_INVALID_STATUS_RESPONSE:
+ case SSL_R_INVALID_TICKET_KEYS_LENGTH:
+ case SSL_R_KEY_ARG_TOO_LONG:
+ case SSL_R_READ_WRONG_PACKET_TYPE:
+ // SSL_do_handshake reports this error when the server responds to a
+ // ClientHello with a fatal close_notify alert.
+ case SSL_AD_REASON_OFFSET + SSL_AD_CLOSE_NOTIFY:
+ case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:
+ // TODO(joth): SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE may be returned from the
+ // server after receiving ClientHello if there's no common supported cipher.
+ // Ideally we'd map that specific case to ERR_SSL_VERSION_OR_CIPHER_MISMATCH
+ // to match the NSS implementation. See also http://goo.gl/oMtZW
+ case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:
+ case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:
+ case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:
+ case SSL_R_TLSV1_ALERT_DECODE_ERROR:
+ case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:
+ case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:
+ case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:
+ case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:
+ case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:
+ case SSL_R_TLSV1_ALERT_USER_CANCELLED:
+ return ERR_SSL_PROTOCOL_ERROR;
+ case SSL_R_CERTIFICATE_VERIFY_FAILED:
+ // The only way that the certificate verify callback can fail is if
+ // the leaf certificate changed during a renegotiation.
+ return ERR_SSL_SERVER_CERT_CHANGED;
+ default:
+ LOG(WARNING) << "Unmapped error reason: " << ERR_GET_REASON(error_code);
+ return ERR_FAILED;
+ }
+}
+
+} // namespace
+
+int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer) {
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return ERR_IO_PENDING;
+ case SSL_ERROR_SYSCALL:
+ LOG(ERROR) << "OpenSSL SYSCALL error, earliest error code in "
+ "error queue: " << ERR_peek_error() << ", errno: "
+ << errno;
+ return ERR_SSL_PROTOCOL_ERROR;
+ case SSL_ERROR_SSL:
+ return MapOpenSSLErrorSSL();
+ default:
+ // TODO(joth): Implement full mapping.
+ LOG(WARNING) << "Unknown OpenSSL error " << err;
+ return ERR_SSL_PROTOCOL_ERROR;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/socket/openssl_ssl_util.h b/chromium/net/socket/openssl_ssl_util.h
new file mode 100644
index 00000000000..e459a445ef3
--- /dev/null
+++ b/chromium/net/socket/openssl_ssl_util.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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_OPENSSL_SSL_UTIL_H_
+#define NET_SOCKET_OPENSSL_SSL_UTIL_H_
+
+namespace crypto {
+class OpenSSLErrStackTracer;
+}
+
+namespace net {
+
+// Utility to construct the appropriate set & clear masks for use the OpenSSL
+// options and mode configuration functions. (SSL_set_options etc)
+struct SslSetClearMask {
+ SslSetClearMask();
+ void ConfigureFlag(long flag, bool state);
+
+ long set_mask;
+ long clear_mask;
+};
+
+// Converts an OpenSSL error code into a net error code, walking the OpenSSL
+// error stack if needed. Note that |tracer| is not currently used in the
+// implementation, but is passed in anyway as this ensures the caller will clear
+// any residual codes left on the error stack.
+int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer);
+
+} // namespace net
+
+#endif // NET_SOCKET_OPENSSL_SSL_UTIL_H_
diff --git a/chromium/net/socket/socket.h b/chromium/net/socket/socket.h
index fccb25873a4..5d9af81f205 100644
--- a/chromium/net/socket/socket.h
+++ b/chromium/net/socket/socket.h
@@ -48,13 +48,13 @@ class NET_EXPORT Socket {
// Set the receive buffer size (in bytes) for the socket.
// Note: changing this value can affect the TCP window size on some platforms.
- // Returns true on success, or false on failure.
- virtual bool SetReceiveBufferSize(int32 size) = 0;
+ // Returns a net error code.
+ virtual int SetReceiveBufferSize(int32 size) = 0;
// Set the send buffer size (in bytes) for the socket.
// Note: changing this value can affect the TCP window size on some platforms.
- // Returns true on success, or false on failure.
- virtual bool SetSendBufferSize(int32 size) = 0;
+ // Returns a net error code.
+ virtual int SetSendBufferSize(int32 size) = 0;
};
} // namespace net
diff --git a/chromium/net/socket/socket_test_util.cc b/chromium/net/socket/socket_test_util.cc
index 148b11c5698..f993801adeb 100644
--- a/chromium/net/socket/socket_test_util.cc
+++ b/chromium/net/socket/socket_test_util.cc
@@ -24,6 +24,7 @@
#include "net/socket/client_socket_pool_histograms.h"
#include "net/socket/socket.h"
#include "net/ssl/ssl_cert_request_info.h"
+#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -169,27 +170,27 @@ StaticSocketDataProvider::StaticSocketDataProvider(MockRead* reads,
StaticSocketDataProvider::~StaticSocketDataProvider() {}
const MockRead& StaticSocketDataProvider::PeekRead() const {
- DCHECK(!at_read_eof());
+ CHECK(!at_read_eof());
return reads_[read_index_];
}
const MockWrite& StaticSocketDataProvider::PeekWrite() const {
- DCHECK(!at_write_eof());
+ CHECK(!at_write_eof());
return writes_[write_index_];
}
const MockRead& StaticSocketDataProvider::PeekRead(size_t index) const {
- DCHECK_LT(index, read_count_);
+ CHECK_LT(index, read_count_);
return reads_[index];
}
const MockWrite& StaticSocketDataProvider::PeekWrite(size_t index) const {
- DCHECK_LT(index, write_count_);
+ CHECK_LT(index, write_count_);
return writes_[index];
}
MockRead StaticSocketDataProvider::GetNextRead() {
- DCHECK(!at_read_eof());
+ CHECK(!at_read_eof());
reads_[read_index_].time_stamp = base::Time::Now();
return reads_[read_index_++];
}
@@ -199,7 +200,12 @@ MockWriteResult StaticSocketDataProvider::OnWrite(const std::string& data) {
// Not using mock writes; succeed synchronously.
return MockWriteResult(SYNCHRONOUS, data.length());
}
- DCHECK(!at_write_eof());
+ EXPECT_FALSE(at_write_eof());
+ if (at_write_eof()) {
+ // Show what the extra write actually consists of.
+ EXPECT_EQ("<unexpected write>", data);
+ return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED);
+ }
// Check that what we are writing matches the expectation.
// Then give the mocked return value.
@@ -270,7 +276,12 @@ SSLSocketDataProvider::SSLSocketDataProvider(IoMode mode, int result)
protocol_negotiated(kProtoUnknown),
client_cert_sent(false),
cert_request_info(NULL),
- channel_id_sent(false) {
+ channel_id_sent(false),
+ connection_status(0) {
+ SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_TLS1_2,
+ &connection_status);
+ // Set to TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
+ SSLConnectionStatusSetCipherSuite(0xcc14, &connection_status);
}
SSLSocketDataProvider::~SSLSocketDataProvider() {
@@ -667,6 +678,8 @@ MockClientSocketFactory::CreateDatagramClientSocket(
scoped_ptr<MockUDPClientSocket> socket(
new MockUDPClientSocket(data_provider, net_log));
data_provider->set_socket(socket.get());
+ if (bind_type == DatagramSocket::RANDOM_BIND)
+ socket->set_source_port(rand_int_cb.Run(1025, 65535));
return socket.PassAs<DatagramClientSocket>();
}
@@ -706,12 +719,12 @@ MockClientSocket::MockClientSocket(const BoundNetLog& net_log)
peer_addr_ = IPEndPoint(ip, 0);
}
-bool MockClientSocket::SetReceiveBufferSize(int32 size) {
- return true;
+int MockClientSocket::SetReceiveBufferSize(int32 size) {
+ return OK;
}
-bool MockClientSocket::SetSendBufferSize(int32 size) {
- return true;
+int MockClientSocket::SetSendBufferSize(int32 size) {
+ return OK;
}
void MockClientSocket::Disconnect() {
@@ -775,6 +788,12 @@ MockClientSocket::GetNextProto(std::string* proto, std::string* server_protos) {
return SSLClientSocket::kNextProtoUnsupported;
}
+scoped_refptr<X509Certificate>
+MockClientSocket::GetUnverifiedServerCertificateChain() const {
+ NOTREACHED();
+ return NULL;
+}
+
MockClientSocket::~MockClientSocket() {}
void MockClientSocket::RunCallbackAsync(const CompletionCallback& callback,
@@ -1099,7 +1118,8 @@ DeterministicMockUDPClientSocket::DeterministicMockUDPClientSocket(
net::NetLog* net_log,
DeterministicSocketData* data)
: connected_(false),
- helper_(net_log, data) {
+ helper_(net_log, data),
+ source_port_(123) {
}
DeterministicMockUDPClientSocket::~DeterministicMockUDPClientSocket() {}
@@ -1148,12 +1168,12 @@ int DeterministicMockUDPClientSocket::Read(
return helper_.Read(buf, buf_len, callback);
}
-bool DeterministicMockUDPClientSocket::SetReceiveBufferSize(int32 size) {
- return true;
+int DeterministicMockUDPClientSocket::SetReceiveBufferSize(int32 size) {
+ return OK;
}
-bool DeterministicMockUDPClientSocket::SetSendBufferSize(int32 size) {
- return true;
+int DeterministicMockUDPClientSocket::SetSendBufferSize(int32 size) {
+ return OK;
}
void DeterministicMockUDPClientSocket::Close() {
@@ -1171,7 +1191,7 @@ int DeterministicMockUDPClientSocket::GetLocalAddress(
IPAddressNumber ip;
bool rv = ParseIPLiteralToNumber("192.0.2.33", &ip);
CHECK(rv);
- *address = IPEndPoint(ip, 123);
+ *address = IPEndPoint(ip, source_port_);
return OK;
}
@@ -1363,6 +1383,7 @@ bool MockSSLClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
ssl_info->cert = data_->cert;
ssl_info->client_cert_sent = data_->client_cert_sent;
ssl_info->channel_id_sent = data_->channel_id_sent;
+ ssl_info->connection_status = data_->connection_status;
return true;
}
@@ -1435,6 +1456,7 @@ MockUDPClientSocket::MockUDPClientSocket(SocketDataProvider* data,
read_offset_(0),
read_data_(SYNCHRONOUS, ERR_UNEXPECTED),
need_read_data_(true),
+ source_port_(123),
pending_buf_(NULL),
pending_buf_len_(0),
net_log_(BoundNetLog::Make(net_log, net::NetLog::SOURCE_NONE)),
@@ -1446,7 +1468,8 @@ MockUDPClientSocket::MockUDPClientSocket(SocketDataProvider* data,
MockUDPClientSocket::~MockUDPClientSocket() {}
-int MockUDPClientSocket::Read(IOBuffer* buf, int buf_len,
+int MockUDPClientSocket::Read(IOBuffer* buf,
+ int buf_len,
const CompletionCallback& callback) {
if (!connected_)
return ERR_UNEXPECTED;
@@ -1492,12 +1515,12 @@ int MockUDPClientSocket::Write(IOBuffer* buf, int buf_len,
return write_result.result;
}
-bool MockUDPClientSocket::SetReceiveBufferSize(int32 size) {
- return true;
+int MockUDPClientSocket::SetReceiveBufferSize(int32 size) {
+ return OK;
}
-bool MockUDPClientSocket::SetSendBufferSize(int32 size) {
- return true;
+int MockUDPClientSocket::SetSendBufferSize(int32 size) {
+ return OK;
}
void MockUDPClientSocket::Close() {
@@ -1513,7 +1536,7 @@ int MockUDPClientSocket::GetLocalAddress(IPEndPoint* address) const {
IPAddressNumber ip;
bool rv = ParseIPLiteralToNumber("192.0.2.33", &ip);
CHECK(rv);
- *address = IPEndPoint(ip, 123);
+ *address = IPEndPoint(ip, source_port_);
return OK;
}
@@ -1524,7 +1547,7 @@ const BoundNetLog& MockUDPClientSocket::NetLog() const {
int MockUDPClientSocket::Connect(const IPEndPoint& address) {
connected_ = true;
peer_addr_ = address;
- return OK;
+ return data_->connect_data().result;
}
void MockUDPClientSocket::OnReadComplete(const MockRead& data) {
@@ -1808,6 +1831,8 @@ DeterministicMockClientSocketFactory::CreateDatagramClientSocket(
new DeterministicMockUDPClientSocket(net_log, data_provider));
data_provider->set_delegate(socket->AsWeakPtr());
udp_client_sockets().push_back(socket.get());
+ if (bind_type == DatagramSocket::RANDOM_BIND)
+ socket->set_source_port(rand_int_cb.Run(1025, 65535));
return socket.PassAs<DatagramClientSocket>();
}
diff --git a/chromium/net/socket/socket_test_util.h b/chromium/net/socket/socket_test_util.h
index 8df1d69538a..2918aad2dc5 100644
--- a/chromium/net/socket/socket_test_util.h
+++ b/chromium/net/socket/socket_test_util.h
@@ -334,6 +334,7 @@ struct SSLSocketDataProvider {
scoped_refptr<X509Certificate> cert;
bool channel_id_sent;
ServerBoundCertService* server_bound_cert_service;
+ int connection_status;
};
// A DataProvider where the client must write a request before the reads (e.g.
@@ -675,8 +676,8 @@ class MockClientSocket : public SSLClientSocket {
virtual int Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) = 0;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
// StreamSocket implementation.
virtual int Connect(const CompletionCallback& callback) = 0;
@@ -707,6 +708,10 @@ class MockClientSocket : public SSLClientSocket {
void RunCallbackAsync(const CompletionCallback& callback, int result);
void RunCallback(const CompletionCallback& callback, int result);
+ // SSLClientSocket implementation.
+ virtual scoped_refptr<X509Certificate> GetUnverifiedServerCertificateChain()
+ const OVERRIDE;
+
// True if Connect completed successfully and Disconnect hasn't been called.
bool connected_;
@@ -846,8 +851,8 @@ class DeterministicMockUDPClientSocket
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
// DatagramSocket implementation.
virtual void Close() OVERRIDE;
@@ -862,10 +867,13 @@ class DeterministicMockUDPClientSocket
virtual void OnReadComplete(const MockRead& data) OVERRIDE;
virtual void OnConnectComplete(const MockConnect& data) OVERRIDE;
+ void set_source_port(int port) { source_port_ = port; }
+
private:
bool connected_;
IPEndPoint peer_address_;
DeterministicSocketHelper helper_;
+ int source_port_; // Ephemeral source port.
DISALLOW_COPY_AND_ASSIGN(DeterministicMockUDPClientSocket);
};
@@ -985,8 +993,8 @@ class MockUDPClientSocket : public DatagramClientSocket, public AsyncSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
// DatagramSocket implementation.
virtual void Close() OVERRIDE;
@@ -1001,6 +1009,8 @@ class MockUDPClientSocket : public DatagramClientSocket, public AsyncSocket {
virtual void OnReadComplete(const MockRead& data) OVERRIDE;
virtual void OnConnectComplete(const MockConnect& data) OVERRIDE;
+ void set_source_port(int port) { source_port_ = port;}
+
private:
int CompleteRead();
@@ -1012,6 +1022,7 @@ class MockUDPClientSocket : public DatagramClientSocket, public AsyncSocket {
int read_offset_;
MockRead read_data_;
bool need_read_data_;
+ int source_port_; // Ephemeral source port.
// Address of the "remote" peer we're connected to.
IPEndPoint peer_addr_;
diff --git a/chromium/net/socket/socks5_client_socket.cc b/chromium/net/socket/socks5_client_socket.cc
index 004b67c669f..681f73f26e9 100644
--- a/chromium/net/socket/socks5_client_socket.cc
+++ b/chromium/net/socket/socks5_client_socket.cc
@@ -5,6 +5,7 @@
#include "net/socket/socks5_client_socket.h"
#include "base/basictypes.h"
+#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
#include "base/debug/trace_event.h"
#include "base/format_macros.h"
@@ -38,6 +39,7 @@ SOCKS5ClientSocket::SOCKS5ClientSocket(
bytes_sent_(0),
bytes_received_(0),
read_header_size(kReadHeaderSize),
+ was_ever_used_(false),
host_request_info_(req_info),
net_log_(transport_->socket()->NetLog()) {
}
@@ -109,11 +111,7 @@ void SOCKS5ClientSocket::SetOmniboxSpeculation() {
}
bool SOCKS5ClientSocket::WasEverUsed() const {
- if (transport_.get() && transport_->socket()) {
- return transport_->socket()->WasEverUsed();
- }
- NOTREACHED();
- return false;
+ return was_ever_used_;
}
bool SOCKS5ClientSocket::UsingTCPFastOpen() const {
@@ -156,26 +154,40 @@ int SOCKS5ClientSocket::Read(IOBuffer* buf, int buf_len,
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
-
- return transport_->socket()->Read(buf, buf_len, callback);
+ DCHECK(!callback.is_null());
+
+ int rv = transport_->socket()->Read(
+ buf, buf_len,
+ base::Bind(&SOCKS5ClientSocket::OnReadWriteComplete,
+ base::Unretained(this), callback));
+ if (rv > 0)
+ was_ever_used_ = true;
+ return rv;
}
// Write is called by the transport layer. This can only be done if the
// SOCKS handshake is complete.
int SOCKS5ClientSocket::Write(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
+ const CompletionCallback& callback) {
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
-
- return transport_->socket()->Write(buf, buf_len, callback);
+ DCHECK(!callback.is_null());
+
+ int rv = transport_->socket()->Write(
+ buf, buf_len,
+ base::Bind(&SOCKS5ClientSocket::OnReadWriteComplete,
+ base::Unretained(this), callback));
+ if (rv > 0)
+ was_ever_used_ = true;
+ return rv;
}
-bool SOCKS5ClientSocket::SetReceiveBufferSize(int32 size) {
+int SOCKS5ClientSocket::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SOCKS5ClientSocket::SetSendBufferSize(int32 size) {
+int SOCKS5ClientSocket::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
@@ -185,9 +197,7 @@ void SOCKS5ClientSocket::DoCallback(int result) {
// Since Run() may result in Read being called,
// clear user_callback_ up front.
- CompletionCallback c = user_callback_;
- user_callback_.Reset();
- c.Run(result);
+ base::ResetAndReturn(&user_callback_).Run(result);
}
void SOCKS5ClientSocket::OnIOComplete(int result) {
@@ -199,6 +209,16 @@ void SOCKS5ClientSocket::OnIOComplete(int result) {
}
}
+void SOCKS5ClientSocket::OnReadWriteComplete(const CompletionCallback& callback,
+ int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK(!callback.is_null());
+
+ if (result > 0)
+ was_ever_used_ = true;
+ callback.Run(result);
+}
+
int SOCKS5ClientSocket::DoLoop(int last_io_result) {
DCHECK_NE(next_state_, STATE_NONE);
int rv = last_io_result;
diff --git a/chromium/net/socket/socks5_client_socket.h b/chromium/net/socket/socks5_client_socket.h
index 45216244f10..8da0b4da5ce 100644
--- a/chromium/net/socket/socks5_client_socket.h
+++ b/chromium/net/socket/socks5_client_socket.h
@@ -64,8 +64,8 @@ class NET_EXPORT_PRIVATE SOCKS5ClientSocket : public StreamSocket {
int buf_len,
const CompletionCallback& callback) OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
@@ -99,6 +99,7 @@ class NET_EXPORT_PRIVATE SOCKS5ClientSocket : public StreamSocket {
void DoCallback(int result);
void OnIOComplete(int result);
+ void OnReadWriteComplete(const CompletionCallback& callback, int result);
int DoLoop(int last_io_result);
int DoHandshakeRead();
@@ -143,6 +144,8 @@ class NET_EXPORT_PRIVATE SOCKS5ClientSocket : public StreamSocket {
size_t read_header_size;
+ bool was_ever_used_;
+
HostResolver::RequestInfo host_request_info_;
BoundNetLog net_log_;
diff --git a/chromium/net/socket/socks_client_socket.cc b/chromium/net/socket/socks_client_socket.cc
index 67089589cc5..f7c69f28fd6 100644
--- a/chromium/net/socket/socks_client_socket.cc
+++ b/chromium/net/socket/socks_client_socket.cc
@@ -6,6 +6,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
#include "base/sys_byteorder.h"
#include "net/base/io_buffer.h"
@@ -65,6 +66,7 @@ SOCKSClientSocket::SOCKSClientSocket(
completed_handshake_(false),
bytes_sent_(0),
bytes_received_(0),
+ was_ever_used_(false),
host_resolver_(host_resolver),
host_request_info_(req_info),
priority_(priority),
@@ -137,11 +139,7 @@ void SOCKSClientSocket::SetOmniboxSpeculation() {
}
bool SOCKSClientSocket::WasEverUsed() const {
- if (transport_.get() && transport_->socket()) {
- return transport_->socket()->WasEverUsed();
- }
- NOTREACHED();
- return false;
+ return was_ever_used_;
}
bool SOCKSClientSocket::UsingTCPFastOpen() const {
@@ -184,8 +182,15 @@ int SOCKSClientSocket::Read(IOBuffer* buf, int buf_len,
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
-
- return transport_->socket()->Read(buf, buf_len, callback);
+ DCHECK(!callback.is_null());
+
+ int rv = transport_->socket()->Read(
+ buf, buf_len,
+ base::Bind(&SOCKSClientSocket::OnReadWriteComplete,
+ base::Unretained(this), callback));
+ if (rv > 0)
+ was_ever_used_ = true;
+ return rv;
}
// Write is called by the transport layer. This can only be done if the
@@ -195,15 +200,22 @@ int SOCKSClientSocket::Write(IOBuffer* buf, int buf_len,
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
-
- return transport_->socket()->Write(buf, buf_len, callback);
+ DCHECK(!callback.is_null());
+
+ int rv = transport_->socket()->Write(
+ buf, buf_len,
+ base::Bind(&SOCKSClientSocket::OnReadWriteComplete,
+ base::Unretained(this), callback));
+ if (rv > 0)
+ was_ever_used_ = true;
+ return rv;
}
-bool SOCKSClientSocket::SetReceiveBufferSize(int32 size) {
+int SOCKSClientSocket::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SOCKSClientSocket::SetSendBufferSize(int32 size) {
+int SOCKSClientSocket::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
@@ -213,10 +225,8 @@ void SOCKSClientSocket::DoCallback(int result) {
// Since Run() may result in Read being called,
// clear user_callback_ up front.
- CompletionCallback c = user_callback_;
- user_callback_.Reset();
DVLOG(1) << "Finished setting up SOCKS handshake";
- c.Run(result);
+ base::ResetAndReturn(&user_callback_).Run(result);
}
void SOCKSClientSocket::OnIOComplete(int result) {
@@ -228,6 +238,16 @@ void SOCKSClientSocket::OnIOComplete(int result) {
}
}
+void SOCKSClientSocket::OnReadWriteComplete(const CompletionCallback& callback,
+ int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK(!callback.is_null());
+
+ if (result > 0)
+ was_ever_used_ = true;
+ callback.Run(result);
+}
+
int SOCKSClientSocket::DoLoop(int last_io_result) {
DCHECK_NE(next_state_, STATE_NONE);
int rv = last_io_result;
diff --git a/chromium/net/socket/socks_client_socket.h b/chromium/net/socket/socks_client_socket.h
index d4f058a62b1..26da332b3ea 100644
--- a/chromium/net/socket/socks_client_socket.h
+++ b/chromium/net/socket/socks_client_socket.h
@@ -61,8 +61,8 @@ class NET_EXPORT_PRIVATE SOCKSClientSocket : public StreamSocket {
int buf_len,
const CompletionCallback& callback) OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
@@ -84,6 +84,7 @@ class NET_EXPORT_PRIVATE SOCKSClientSocket : public StreamSocket {
void DoCallback(int result);
void OnIOComplete(int result);
+ void OnReadWriteComplete(const CompletionCallback& callback, int result);
int DoLoop(int last_io_result);
int DoResolveHost();
@@ -100,7 +101,7 @@ class NET_EXPORT_PRIVATE SOCKSClientSocket : public StreamSocket {
State next_state_;
- // Stores the callback to the layer above, called on completing Connect().
+ // Stores the callbacks to the layer above, called on completing Connect().
CompletionCallback user_callback_;
// This IOBuffer is used by the class to read and write
@@ -120,6 +121,9 @@ class NET_EXPORT_PRIVATE SOCKSClientSocket : public StreamSocket {
size_t bytes_sent_;
size_t bytes_received_;
+ // This becomes true when the socket is used to send or receive data.
+ bool was_ever_used_;
+
// Used to resolve the hostname to which the SOCKS proxy will connect.
SingleRequestHostResolver host_resolver_;
AddressList addresses_;
diff --git a/chromium/net/socket/ssl_client_socket.cc b/chromium/net/socket/ssl_client_socket.cc
index d96a720e428..1b2fe144304 100644
--- a/chromium/net/socket/ssl_client_socket.cc
+++ b/chromium/net/socket/ssl_client_socket.cc
@@ -32,10 +32,10 @@ NextProto SSLClientSocket::NextProtoFromString(
return kProtoSPDY3;
} else if (proto_string == "spdy/3.1") {
return kProtoSPDY31;
- } else if (proto_string == "spdy/4a2") {
- return kProtoSPDY4a2;
- } else if (proto_string == "HTTP-draft-04/2.0") {
- return kProtoHTTP2Draft04;
+ } else if (proto_string == "h2-12") {
+ // This is the HTTP/2 draft 12 identifier. For internal
+ // consistency, HTTP/2 is named SPDY4 within Chromium.
+ return kProtoSPDY4;
} else if (proto_string == "quic/1+spdy/3") {
return kProtoQUIC1SPDY3;
} else {
@@ -54,10 +54,10 @@ const char* SSLClientSocket::NextProtoToString(NextProto next_proto) {
return "spdy/3";
case kProtoSPDY31:
return "spdy/3.1";
- case kProtoSPDY4a2:
- return "spdy/4a2";
- case kProtoHTTP2Draft04:
- return "HTTP-draft-04/2.0";
+ case kProtoSPDY4:
+ // This is the HTTP/2 draft 12 identifier. For internal
+ // consistency, HTTP/2 is named SPDY4 within Chromium.
+ return "h2-12";
case kProtoQUIC1SPDY3:
return "quic/1+spdy/3";
case kProtoUnknown:
diff --git a/chromium/net/socket/ssl_client_socket.h b/chromium/net/socket/ssl_client_socket.h
index 410062dc5a9..a43e58cc26b 100644
--- a/chromium/net/socket/ssl_client_socket.h
+++ b/chromium/net/socket/ssl_client_socket.h
@@ -23,6 +23,7 @@ class SSLCertRequestInfo;
struct SSLConfig;
class SSLInfo;
class TransportSecurityState;
+class X509Certificate;
// This struct groups together several fields which are used by various
// classes related to SSLClientSocket.
@@ -154,6 +155,13 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
const SSLConfig& ssl_config,
ServerBoundCertService* server_bound_cert_service);
+ // For unit testing only.
+ // Returns the unverified certificate chain as presented by server.
+ // Note that chain may be different than the verified chain returned by
+ // StreamSocket::GetSSLInfo().
+ virtual scoped_refptr<X509Certificate> GetUnverifiedServerCertificateChain()
+ const = 0;
+
private:
// For signed_cert_timestamps_received_ and stapled_ocsp_response_received_.
FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest,
@@ -162,6 +170,8 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
ConnectSignedCertTimestampsEnabledOCSP);
FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest,
ConnectSignedCertTimestampsDisabled);
+ FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest,
+ VerifyServerChainProperlyOrdered);
// True if NPN was responded to, independent of selecting SPDY or HTTP.
bool was_npn_negotiated_;
diff --git a/chromium/net/socket/ssl_client_socket_nss.cc b/chromium/net/socket/ssl_client_socket_nss.cc
index 0c73ec6c582..9f40a78371e 100644
--- a/chromium/net/socket/ssl_client_socket_nss.cc
+++ b/chromium/net/socket/ssl_client_socket_nss.cc
@@ -271,11 +271,6 @@ BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context,
#endif
-void DestroyCertificates(CERTCertificate** certs, size_t len) {
- for (size_t i = 0; i < len; i++)
- CERT_DestroyCertificate(certs[i]);
-}
-
// Helper functions to make it possible to log events from within the
// SSLClientSocketNSS::Core.
void AddLogEvent(const base::WeakPtr<BoundNetLog>& net_log,
@@ -335,7 +330,6 @@ class PeerCertificateChain {
std::vector<base::StringPiece> AsStringPieceVector() const;
bool empty() const { return certs_.empty(); }
- size_t size() const { return certs_.size(); }
CERTCertificate* operator[](size_t index) const {
DCHECK_LT(index, certs_.size());
@@ -615,13 +609,6 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> {
bool Init(PRFileDesc* socket, memio_Private* buffers);
// Called on the network task runner.
- // Sets the predicted certificate chain that the peer will send, for use
- // with the TLS CachedInfo extension. If called, it must not be called
- // before Init() or after Connect().
- void SetPredictedCertificates(
- const std::vector<std::string>& predicted_certificates);
-
- // Called on the network task runner.
//
// Attempts to perform an SSL handshake. If the handshake cannot be
// completed synchronously, returns ERR_IO_PENDING, invoking |callback| on
@@ -648,9 +635,10 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> {
int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
// Called on the network task runner.
- bool IsConnected();
- bool HasPendingAsyncOperation();
- bool HasUnhandledReceivedData();
+ bool IsConnected() const;
+ bool HasPendingAsyncOperation() const;
+ bool HasUnhandledReceivedData() const;
+ bool WasEverUsed() const;
// Called on the network task runner.
// Causes the associated SSL/TLS session ID to be added to NSS's session
@@ -853,6 +841,10 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> {
bool nss_waiting_write_;
bool nss_is_closed_;
+ // Set when Read() or Write() successfully reads or writes data to or from the
+ // network.
+ bool was_ever_used_;
+
////////////////////////////////////////////////////////////////////////////
// Members that are ONLY accessed on the NSS task runner:
////////////////////////////////////////////////////////////////////////////
@@ -948,6 +940,7 @@ SSLClientSocketNSS::Core::Core(
nss_waiting_read_(false),
nss_waiting_write_(false),
nss_is_closed_(false),
+ was_ever_used_(false),
host_and_port_(host_and_port),
ssl_config_(ssl_config),
nss_fd_(NULL),
@@ -1015,7 +1008,13 @@ bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket,
DCHECK_EQ(dst, wire_protos.get() + wire_length);
rv = SSL_SetNextProtoNego(nss_fd_, wire_protos.get(), wire_length);
if (rv != SECSuccess)
- LogFailedNSSFunction(*weak_net_log_, "SSL_SetNextProtoCallback", "");
+ LogFailedNSSFunction(*weak_net_log_, "SSL_SetNextProtoNego", "");
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_ALPN, PR_TRUE);
+ if (rv != SECSuccess)
+ LogFailedNSSFunction(*weak_net_log_, "SSL_OptionSet", "SSL_ENABLE_ALPN");
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_NPN, PR_TRUE);
+ if (rv != SECSuccess)
+ LogFailedNSSFunction(*weak_net_log_, "SSL_OptionSet", "SSL_ENABLE_NPN");
}
rv = SSL_AuthCertificateHook(
@@ -1064,57 +1063,6 @@ bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket,
return true;
}
-void SSLClientSocketNSS::Core::SetPredictedCertificates(
- const std::vector<std::string>& predicted_certs) {
- if (predicted_certs.empty())
- return;
-
- if (!OnNSSTaskRunner()) {
- DCHECK(!detached_);
- nss_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Core::SetPredictedCertificates, this, predicted_certs));
- return;
- }
-
- DCHECK(nss_fd_);
-
- predicted_certs_ = predicted_certs;
-
- scoped_ptr<CERTCertificate*[]> certs(
- new CERTCertificate*[predicted_certs.size()]);
-
- for (size_t i = 0; i < predicted_certs.size(); i++) {
- SECItem derCert;
- derCert.data = const_cast<uint8*>(reinterpret_cast<const uint8*>(
- predicted_certs[i].data()));
- derCert.len = predicted_certs[i].size();
- certs[i] = CERT_NewTempCertificate(
- CERT_GetDefaultCertDB(), &derCert, NULL /* no nickname given */,
- PR_FALSE /* not permanent */, PR_TRUE /* copy DER data */);
- if (!certs[i]) {
- DestroyCertificates(&certs[0], i);
- NOTREACHED();
- return;
- }
- }
-
- SECStatus rv;
-#ifdef SSL_ENABLE_CACHED_INFO
- rv = SSL_SetPredictedPeerCertificates(nss_fd_, certs.get(),
- predicted_certs.size());
- DCHECK_EQ(SECSuccess, rv);
-#else
- rv = SECFailure; // Not implemented.
-#endif
- DestroyCertificates(&certs[0], predicted_certs.size());
-
- if (rv != SECSuccess) {
- LOG(WARNING) << "SetPredictedCertificates failed: "
- << host_and_port_.ToString();
- }
-}
-
int SSLClientSocketNSS::Core::Connect(const CompletionCallback& callback) {
if (!OnNSSTaskRunner()) {
DCHECK(!detached_);
@@ -1205,8 +1153,11 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len,
return ERR_IO_PENDING;
} else {
DCHECK(!nss_waiting_read_);
- if (rv <= 0)
+ if (rv <= 0) {
nss_is_closed_ = true;
+ } else {
+ was_ever_used_ = true;
+ }
}
}
@@ -1259,29 +1210,37 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len,
return ERR_IO_PENDING;
} else {
DCHECK(!nss_waiting_write_);
- if (rv < 0)
+ if (rv < 0) {
nss_is_closed_ = true;
+ } else if (rv > 0) {
+ was_ever_used_ = true;
+ }
}
}
return rv;
}
-bool SSLClientSocketNSS::Core::IsConnected() {
+bool SSLClientSocketNSS::Core::IsConnected() const {
DCHECK(OnNetworkTaskRunner());
return !nss_is_closed_;
}
-bool SSLClientSocketNSS::Core::HasPendingAsyncOperation() {
+bool SSLClientSocketNSS::Core::HasPendingAsyncOperation() const {
DCHECK(OnNetworkTaskRunner());
return nss_waiting_read_ || nss_waiting_write_;
}
-bool SSLClientSocketNSS::Core::HasUnhandledReceivedData() {
+bool SSLClientSocketNSS::Core::HasUnhandledReceivedData() const {
DCHECK(OnNetworkTaskRunner());
return unhandled_buffer_size_ != 0;
}
+bool SSLClientSocketNSS::Core::WasEverUsed() const {
+ DCHECK(OnNetworkTaskRunner());
+ return was_ever_used_;
+}
+
void SSLClientSocketNSS::Core::CacheSessionIfNecessary() {
// TODO(rsleevi): This should occur on the NSS task runner, due to the use of
// nss_fd_. However, it happens on the network task runner in order to match
@@ -2184,7 +2143,12 @@ int SSLClientSocketNSS::Core::BufferSend() {
const char* buf1;
const char* buf2;
unsigned int len1, len2;
- memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
+ if (memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2)) {
+ // It is important this return synchronously to prevent spinning infinitely
+ // in the off-thread NSS case. The error code itself is ignored, so just
+ // return ERR_ABORTED. See https://crbug.com/381160.
+ return ERR_ABORTED;
+ }
const unsigned int len = len1 + len2;
int rv = 0;
@@ -2450,9 +2414,14 @@ void SSLClientSocketNSS::Core::UpdateSignedCertTimestamps() {
}
void SSLClientSocketNSS::Core::UpdateStapledOCSPResponse() {
+ PRBool ocsp_requested = PR_FALSE;
+ SSL_OptionGet(nss_fd_, SSL_ENABLE_OCSP_STAPLING, &ocsp_requested);
const SECItemArray* ocsp_responses =
SSL_PeerStapledOCSPResponses(nss_fd_);
- if (!ocsp_responses || !ocsp_responses->len)
+ bool ocsp_responses_present = ocsp_responses && ocsp_responses->len;
+ if (ocsp_requested)
+ UMA_HISTOGRAM_BOOLEAN("Net.OCSPResponseStapled", ocsp_responses_present);
+ if (!ocsp_responses_present)
return;
nss_handshake_state_.stapled_ocsp_response = std::string(
@@ -2708,16 +2677,22 @@ void SSLClientSocketNSS::Core::DidNSSRead(int result) {
DCHECK(OnNetworkTaskRunner());
DCHECK(nss_waiting_read_);
nss_waiting_read_ = false;
- if (result <= 0)
+ if (result <= 0) {
nss_is_closed_ = true;
+ } else {
+ was_ever_used_ = true;
+ }
}
void SSLClientSocketNSS::Core::DidNSSWrite(int result) {
DCHECK(OnNetworkTaskRunner());
DCHECK(nss_waiting_write_);
nss_waiting_write_ = false;
- if (result < 0)
+ if (result < 0) {
nss_is_closed_ = true;
+ } else if (result > 0) {
+ was_ever_used_ = true;
+ }
}
void SSLClientSocketNSS::Core::BufferSendComplete(int result) {
@@ -2884,15 +2859,12 @@ bool SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
ssl_info->connection_status =
core_->state().ssl_connection_status;
ssl_info->public_key_hashes = server_cert_verify_result_.public_key_hashes;
- for (HashValueVector::const_iterator i = side_pinned_public_keys_.begin();
- i != side_pinned_public_keys_.end(); ++i) {
- ssl_info->public_key_hashes.push_back(*i);
- }
ssl_info->is_issued_by_known_root =
server_cert_verify_result_.is_issued_by_known_root;
ssl_info->client_cert_sent =
ssl_config_.send_client_cert && ssl_config_.client_cert.get();
ssl_info->channel_id_sent = WasChannelIDSent();
+ ssl_info->pinning_failure_log = pinning_failure_log_;
PRUint16 cipher_suite = SSLConnectionStatusToCipherSuite(
core_->state().ssl_connection_status);
@@ -2917,8 +2889,7 @@ bool SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
void SSLClientSocketNSS::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
EnterFunction("");
- // TODO(rch): switch SSLCertRequestInfo.host_and_port to a HostPortPair
- cert_request_info->host_and_port = host_and_port_.ToString();
+ cert_request_info->host_and_port = host_and_port_;
cert_request_info->cert_authorities = core_->state().cert_authorities;
LeaveFunction("");
}
@@ -3082,11 +3053,9 @@ void SSLClientSocketNSS::SetOmniboxSpeculation() {
}
bool SSLClientSocketNSS::WasEverUsed() const {
- if (transport_.get() && transport_->socket()) {
- return transport_->socket()->WasEverUsed();
- }
- NOTREACHED();
- return false;
+ DCHECK(core_.get());
+
+ return core_->WasEverUsed();
}
bool SSLClientSocketNSS::UsingTCPFastOpen() const {
@@ -3121,11 +3090,11 @@ int SSLClientSocketNSS::Write(IOBuffer* buf, int buf_len,
return rv;
}
-bool SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
+int SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SSLClientSocketNSS::SetSendBufferSize(int32 size) {
+int SSLClientSocketNSS::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
@@ -3273,14 +3242,6 @@ int SSLClientSocketNSS::InitializeSSLOptions() {
"SSL_ENABLE_SIGNED_CERT_TIMESTAMPS");
}
-// Chromium patch to libssl
-#ifdef SSL_ENABLE_CACHED_INFO
- rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_CACHED_INFO,
- ssl_config_.cached_info_enabled);
- if (rv != SECSuccess)
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_CACHED_INFO");
-#endif
-
rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
if (rv != SECSuccess) {
LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_CLIENT");
@@ -3308,7 +3269,7 @@ int SSLClientSocketNSS::InitializeSSLPeerName() {
SockaddrStorage storage;
if (!peer_address.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_UNEXPECTED;
+ return ERR_ADDRESS_INVALID;
PRNetAddr peername;
memset(&peername, 0, sizeof(peername));
@@ -3541,12 +3502,13 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
ssl_config_.version_fallback;
const std::string& host = host_and_port_.host();
- TransportSecurityState::DomainState domain_state;
- if (transport_security_state_->GetDomainState(host, sni_available,
- &domain_state) &&
- domain_state.HasPublicKeyPins()) {
- if (!domain_state.CheckPublicKeyPins(
- server_cert_verify_result_.public_key_hashes)) {
+ if (transport_security_state_->HasPublicKeyPins(host, sni_available)) {
+ if (!transport_security_state_->CheckPublicKeyPins(
+ host,
+ sni_available,
+ server_cert_verify_result_.public_key_hashes,
+ &pinning_failure_log_)) {
+ LOG(ERROR) << pinning_failure_log_;
result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false);
TransportSecurityState::ReportUMAOnPinFailure(host);
@@ -3654,6 +3616,11 @@ void SSLClientSocketNSS::AddSCTInfoToSSLInfo(SSLInfo* ssl_info) const {
}
}
+scoped_refptr<X509Certificate>
+SSLClientSocketNSS::GetUnverifiedServerCertificateChain() const {
+ return core_->state().server_cert.get();
+}
+
ServerBoundCertService* SSLClientSocketNSS::GetServerBoundCertService() const {
return server_bound_cert_service_;
}
diff --git a/chromium/net/socket/ssl_client_socket_nss.h b/chromium/net/socket/ssl_client_socket_nss.h
index cc1412fa80b..e8cce574b64 100644
--- a/chromium/net/socket/ssl_client_socket_nss.h
+++ b/chromium/net/socket/ssl_client_socket_nss.h
@@ -102,10 +102,15 @@ class SSLClientSocketNSS : public SSLClientSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual ServerBoundCertService* GetServerBoundCertService() const OVERRIDE;
+ protected:
+ // SSLClientSocket implementation.
+ virtual scoped_refptr<X509Certificate> GetUnverifiedServerCertificateChain()
+ const OVERRIDE;
+
private:
// Helper class to handle marshalling any NSS interaction to and from the
// NSS and network task runners. Not every call needs to happen on the Core
@@ -164,7 +169,6 @@ class SSLClientSocketNSS : public SSLClientSocket {
CompletionCallback user_connect_callback_;
CertVerifyResult server_cert_verify_result_;
- HashValueVector side_pinned_public_keys_;
CertVerifier* const cert_verifier_;
scoped_ptr<SingleRequestCertVerifier> verifier_;
@@ -197,6 +201,11 @@ class SSLClientSocketNSS : public SSLClientSocket {
TransportSecurityState* transport_security_state_;
+ // pinning_failure_log contains a message produced by
+ // TransportSecurityState::DomainState::CheckPublicKeyPins in the event of a
+ // pinning failure. It is a (somewhat) human-readable string.
+ std::string pinning_failure_log_;
+
// The following two variables are added for debugging bug 65948. Will
// remove this code after fixing bug 65948.
// Added the following code Debugging in release mode.
diff --git a/chromium/net/socket/ssl_client_socket_openssl.cc b/chromium/net/socket/ssl_client_socket_openssl.cc
index 49bdc8eb68a..4ff8d438e96 100644
--- a/chromium/net/socket/ssl_client_socket_openssl.cc
+++ b/chromium/net/socket/ssl_client_socket_openssl.cc
@@ -13,7 +13,6 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
-#include "base/debug/alias.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/lock.h"
@@ -23,6 +22,7 @@
#include "net/cert/cert_verifier.h"
#include "net/cert/single_request_cert_verifier.h"
#include "net/cert/x509_certificate_net_log_param.h"
+#include "net/socket/openssl_ssl_util.h"
#include "net/socket/ssl_error_params.h"
#include "net/socket/ssl_session_cache_openssl.h"
#include "net/ssl/openssl_client_key_store.h"
@@ -87,149 +87,6 @@ int GetNetSSLVersion(SSL* ssl) {
}
}
-int MapOpenSSLErrorSSL() {
- // Walk down the error stack to find the SSLerr generated reason.
- unsigned long error_code;
- do {
- error_code = ERR_get_error();
- if (error_code == 0)
- return ERR_SSL_PROTOCOL_ERROR;
- } while (ERR_GET_LIB(error_code) != ERR_LIB_SSL);
-
- DVLOG(1) << "OpenSSL SSL error, reason: " << ERR_GET_REASON(error_code)
- << ", name: " << ERR_error_string(error_code, NULL);
- switch (ERR_GET_REASON(error_code)) {
- case SSL_R_READ_TIMEOUT_EXPIRED:
- return ERR_TIMED_OUT;
- case SSL_R_BAD_RESPONSE_ARGUMENT:
- return ERR_INVALID_ARGUMENT;
- case SSL_R_UNKNOWN_CERTIFICATE_TYPE:
- case SSL_R_UNKNOWN_CIPHER_TYPE:
- case SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE:
- case SSL_R_UNKNOWN_PKEY_TYPE:
- case SSL_R_UNKNOWN_REMOTE_ERROR_TYPE:
- case SSL_R_UNKNOWN_SSL_VERSION:
- return ERR_NOT_IMPLEMENTED;
- case SSL_R_UNSUPPORTED_SSL_VERSION:
- case SSL_R_NO_CIPHER_MATCH:
- case SSL_R_NO_SHARED_CIPHER:
- case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:
- case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
- case SSL_R_UNSUPPORTED_PROTOCOL:
- return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
- case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
- case SSL_R_TLSV1_ALERT_ACCESS_DENIED:
- case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
- return ERR_BAD_SSL_CLIENT_AUTH_CERT;
- case SSL_R_BAD_DECOMPRESSION:
- case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:
- return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
- case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:
- return ERR_SSL_BAD_RECORD_MAC_ALERT;
- case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:
- return ERR_SSL_DECRYPT_ERROR_ALERT;
- case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:
- return ERR_SSL_UNSAFE_NEGOTIATION;
- case SSL_R_WRONG_NUMBER_OF_KEY_BITS:
- return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
- // SSL_R_UNKNOWN_PROTOCOL is reported if premature application data is
- // received (see http://crbug.com/42538), and also if all the protocol
- // versions supported by the server were disabled in this socket instance.
- // Mapped to ERR_SSL_PROTOCOL_ERROR for compatibility with other SSL sockets
- // in the former scenario.
- case SSL_R_UNKNOWN_PROTOCOL:
- case SSL_R_SSL_HANDSHAKE_FAILURE:
- case SSL_R_DECRYPTION_FAILED:
- case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:
- case SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG:
- case SSL_R_DIGEST_CHECK_FAILED:
- case SSL_R_DUPLICATE_COMPRESSION_ID:
- case SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER:
- case SSL_R_ENCRYPTED_LENGTH_TOO_LONG:
- case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:
- case SSL_R_EXCESSIVE_MESSAGE_SIZE:
- case SSL_R_EXTRA_DATA_IN_MESSAGE:
- case SSL_R_GOT_A_FIN_BEFORE_A_CCS:
- case SSL_R_ILLEGAL_PADDING:
- case SSL_R_INVALID_CHALLENGE_LENGTH:
- case SSL_R_INVALID_COMMAND:
- case SSL_R_INVALID_PURPOSE:
- case SSL_R_INVALID_STATUS_RESPONSE:
- case SSL_R_INVALID_TICKET_KEYS_LENGTH:
- case SSL_R_KEY_ARG_TOO_LONG:
- case SSL_R_READ_WRONG_PACKET_TYPE:
- // SSL_do_handshake reports this error when the server responds to a
- // ClientHello with a fatal close_notify alert.
- case SSL_AD_REASON_OFFSET + SSL_AD_CLOSE_NOTIFY:
- case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:
- // TODO(joth): SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE may be returned from the
- // server after receiving ClientHello if there's no common supported cipher.
- // Ideally we'd map that specific case to ERR_SSL_VERSION_OR_CIPHER_MISMATCH
- // to match the NSS implementation. See also http://goo.gl/oMtZW
- case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:
- case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:
- case SSL_R_TLSV1_ALERT_DECODE_ERROR:
- case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:
- case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:
- case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:
- case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:
- case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:
- case SSL_R_TLSV1_ALERT_USER_CANCELLED:
- return ERR_SSL_PROTOCOL_ERROR;
- default:
- LOG(WARNING) << "Unmapped error reason: " << ERR_GET_REASON(error_code);
- return ERR_FAILED;
- }
-}
-
-// Converts an OpenSSL error code into a net error code, walking the OpenSSL
-// error stack if needed. Note that |tracer| is not currently used in the
-// implementation, but is passed in anyway as this ensures the caller will clear
-// any residual codes left on the error stack.
-int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer) {
- switch (err) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return ERR_IO_PENDING;
- case SSL_ERROR_SYSCALL:
- LOG(ERROR) << "OpenSSL SYSCALL error, earliest error code in "
- "error queue: " << ERR_peek_error() << ", errno: "
- << errno;
- return ERR_SSL_PROTOCOL_ERROR;
- case SSL_ERROR_SSL:
- return MapOpenSSLErrorSSL();
- default:
- // TODO(joth): Implement full mapping.
- LOG(WARNING) << "Unknown OpenSSL error " << err;
- return ERR_SSL_PROTOCOL_ERROR;
- }
-}
-
-// We do certificate verification after handshake, so we disable the default
-// by registering a no-op verify function.
-int NoOpVerifyCallback(X509_STORE_CTX*, void *) {
- DVLOG(3) << "skipping cert verify";
- return 1;
-}
-
-// Utility to construct the appropriate set & clear masks for use the OpenSSL
-// options and mode configuration functions. (SSL_set_options etc)
-struct SslSetClearMask {
- SslSetClearMask() : set_mask(0), clear_mask(0) {}
- void ConfigureFlag(long flag, bool state) {
- (state ? set_mask : clear_mask) |= flag;
- // Make sure we haven't got any intersection in the set & clear options.
- DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state;
- }
- long set_mask;
- long clear_mask;
-};
-
// Compute a unique key string for the SSL session cache. |socket| is an
// input socket object. Return a string.
std::string GetSocketSessionCacheKey(const SSLClientSocketOpenSSL& socket) {
@@ -268,16 +125,15 @@ class SSLClientSocketOpenSSL::SSLContext {
DCHECK_NE(ssl_socket_data_index_, -1);
ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method()));
session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig);
- SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), NoOpVerifyCallback, NULL);
+ SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL);
SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback);
SSL_CTX_set_channel_id_cb(ssl_ctx_.get(), ChannelIDCallback);
-#if defined(OPENSSL_NPN_NEGOTIATED)
+ SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL);
// TODO(kristianm): Only select this if ssl_config_.next_proto is not empty.
// It would be better if the callback were not a global setting,
// but that is an OpenSSL issue.
SSL_CTX_set_next_proto_select_cb(ssl_ctx_.get(), SelectNextProtoCallback,
NULL);
-#endif
}
static std::string GetSessionCacheKey(const SSL* ssl) {
@@ -300,6 +156,15 @@ class SSLClientSocketOpenSSL::SSLContext {
socket->ChannelIDRequestCallback(ssl, pkey);
}
+ static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
+ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ CHECK(socket);
+
+ return socket->CertVerifyCallback(store_ctx);
+ }
+
static int SelectNextProtoCallback(SSL* ssl,
unsigned char** out, unsigned char* outlen,
const unsigned char* in,
@@ -317,6 +182,137 @@ class SSLClientSocketOpenSSL::SSLContext {
SSLSessionCacheOpenSSL session_cache_;
};
+// PeerCertificateChain is a helper object which extracts the certificate
+// chain, as given by the server, from an OpenSSL socket and performs the needed
+// resource management. The first element of the chain is the leaf certificate
+// and the other elements are in the order given by the server.
+class SSLClientSocketOpenSSL::PeerCertificateChain {
+ public:
+ explicit PeerCertificateChain(STACK_OF(X509)* chain) { Reset(chain); }
+ PeerCertificateChain(const PeerCertificateChain& other) { *this = other; }
+ ~PeerCertificateChain() {}
+ PeerCertificateChain& operator=(const PeerCertificateChain& other);
+
+ // Resets the PeerCertificateChain to the set of certificates in|chain|,
+ // which may be NULL, indicating to empty the store certificates.
+ // Note: If an error occurs, such as being unable to parse the certificates,
+ // this will behave as if Reset(NULL) was called.
+ void Reset(STACK_OF(X509)* chain);
+
+ // Note that when USE_OPENSSL is defined, OSCertHandle is X509*
+ const scoped_refptr<X509Certificate>& AsOSChain() const { return os_chain_; }
+
+ size_t size() const {
+ if (!openssl_chain_.get())
+ return 0;
+ return sk_X509_num(openssl_chain_.get());
+ }
+
+ X509* operator[](size_t index) const {
+ DCHECK_LT(index, size());
+ return sk_X509_value(openssl_chain_.get(), index);
+ }
+
+ bool IsValid() { return os_chain_.get() && openssl_chain_.get(); }
+
+ private:
+ static void FreeX509Stack(STACK_OF(X509)* cert_chain) {
+ sk_X509_pop_free(cert_chain, X509_free);
+ }
+
+ friend class crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>;
+
+ crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack> openssl_chain_;
+
+ scoped_refptr<X509Certificate> os_chain_;
+};
+
+SSLClientSocketOpenSSL::PeerCertificateChain&
+SSLClientSocketOpenSSL::PeerCertificateChain::operator=(
+ const PeerCertificateChain& other) {
+ if (this == &other)
+ return *this;
+
+ // os_chain_ is reference counted by scoped_refptr;
+ os_chain_ = other.os_chain_;
+
+ // Must increase the reference count manually for sk_X509_dup
+ openssl_chain_.reset(sk_X509_dup(other.openssl_chain_.get()));
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+ }
+ return *this;
+}
+
+#if defined(USE_OPENSSL_CERTS)
+// When OSCertHandle is typedef'ed to X509, this implementation does a short cut
+// to avoid converting back and forth between der and X509 struct.
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(
+ STACK_OF(X509)* chain) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+
+ if (!chain)
+ return;
+
+ X509Certificate::OSCertHandles intermediates;
+ for (int i = 1; i < sk_X509_num(chain); ++i)
+ intermediates.push_back(sk_X509_value(chain, i));
+
+ os_chain_ =
+ X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), intermediates);
+
+ // sk_X509_dup does not increase reference count on the certs in the stack.
+ openssl_chain_.reset(sk_X509_dup(chain));
+
+ std::vector<base::StringPiece> der_chain;
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+ // Increase the reference count for the certs in openssl_chain_.
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+ }
+}
+#else // !defined(USE_OPENSSL_CERTS)
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(
+ STACK_OF(X509)* chain) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+
+ if (!chain)
+ return;
+
+ // sk_X509_dup does not increase reference count on the certs in the stack.
+ openssl_chain_.reset(sk_X509_dup(chain));
+
+ std::vector<base::StringPiece> der_chain;
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+
+ // Increase the reference count for the certs in openssl_chain_.
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+
+ unsigned char* cert_data = NULL;
+ int cert_data_length = i2d_X509(x, &cert_data);
+ if (cert_data_length && cert_data)
+ der_chain.push_back(base::StringPiece(reinterpret_cast<char*>(cert_data),
+ cert_data_length));
+ }
+
+ os_chain_ = X509Certificate::CreateFromDERCertChain(der_chain);
+
+ for (size_t i = 0; i < der_chain.size(); ++i) {
+ OPENSSL_free(const_cast<char*>(der_chain[i].data()));
+ }
+
+ if (der_chain.size() !=
+ static_cast<size_t>(sk_X509_num(openssl_chain_.get()))) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+ }
+}
+#endif // defined(USE_OPENSSL_CERTS)
+
// static
SSLSessionCacheOpenSSL::Config
SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = {
@@ -331,6 +327,9 @@ void SSLClientSocket::ClearSessionCache() {
SSLClientSocketOpenSSL::SSLContext* context =
SSLClientSocketOpenSSL::SSLContext::GetInstance();
context->session_cache()->Flush();
+#if defined(USE_OPENSSL_CERTS)
+ OpenSSLClientKeyStore::GetInstance()->Flush();
+#endif
}
SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
@@ -344,7 +343,9 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
weak_factory_(this),
pending_read_error_(kNoPendingReadResult),
transport_write_error_(OK),
+ server_cert_chain_(new PeerCertificateChain(NULL)),
completed_handshake_(false),
+ was_ever_used_(false),
client_auth_cert_needed_(false),
cert_verifier_(context.cert_verifier),
server_bound_cert_service_(context.server_bound_cert_service),
@@ -359,8 +360,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
npn_status_(kNextProtoUnsupported),
channel_id_request_return_value_(ERR_UNEXPECTED),
channel_id_xtn_negotiated_(false),
- net_log_(transport_->socket()->NetLog()) {
-}
+ net_log_(transport_->socket()->NetLog()) {}
SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
Disconnect();
@@ -368,8 +368,9 @@ SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
- cert_request_info->host_and_port = host_and_port_.ToString();
+ cert_request_info->host_and_port = host_and_port_;
cert_request_info->cert_authorities = cert_authorities_;
+ cert_request_info->cert_key_types = cert_key_types_;
}
SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto(
@@ -391,11 +392,9 @@ int SSLClientSocketOpenSSL::ExportKeyingMaterial(
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
int rv = SSL_export_keying_material(
- ssl_, out, outlen, const_cast<char*>(label.data()),
- label.size(),
- reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())),
- context.length(),
- context.length() > 0);
+ ssl_, out, outlen, label.data(), label.size(),
+ reinterpret_cast<const unsigned char*>(context.data()),
+ context.length(), context.length() > 0);
if (rv != 1) {
int ssl_error = SSL_get_error(ssl_, rv);
@@ -408,6 +407,7 @@ int SSLClientSocketOpenSSL::ExportKeyingMaterial(
}
int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) {
+ NOTIMPLEMENTED();
return ERR_NOT_IMPLEMENTED;
}
@@ -415,17 +415,17 @@ int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT);
// Set up new ssl object.
- if (!Init()) {
- int result = ERR_UNEXPECTED;
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result);
- return result;
+ int rv = Init();
+ if (rv != OK) {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
+ return rv;
}
// Set SSL to client mode. Handshake happens in the loop below.
SSL_set_connect_state(ssl_);
GotoState(STATE_HANDSHAKE);
- int rv = DoHandshakeLoop(net::OK);
+ rv = DoHandshakeLoop(OK);
if (rv == ERR_IO_PENDING) {
user_connect_callback_ = callback;
} else {
@@ -474,6 +474,7 @@ void SSLClientSocketOpenSSL::Disconnect() {
completed_handshake_ = false;
cert_authorities_.clear();
+ cert_key_types_.clear();
client_auth_cert_needed_ = false;
}
@@ -534,11 +535,7 @@ void SSLClientSocketOpenSSL::SetOmniboxSpeculation() {
}
bool SSLClientSocketOpenSSL::WasEverUsed() const {
- if (transport_.get() && transport_->socket())
- return transport_->socket()->WasEverUsed();
-
- NOTREACHED();
- return false;
+ return was_ever_used_;
}
bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const {
@@ -609,6 +606,8 @@ int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
if (rv == ERR_IO_PENDING) {
user_read_callback_ = callback;
} else {
+ if (rv > 0)
+ was_ever_used_ = true;
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
}
@@ -627,6 +626,8 @@ int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
if (rv == ERR_IO_PENDING) {
user_write_callback_ = callback;
} else {
+ if (rv > 0)
+ was_ever_used_ = true;
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
}
@@ -634,15 +635,15 @@ int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
return rv;
}
-bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
+int SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
+int SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
-bool SSLClientSocketOpenSSL::Init() {
+int SSLClientSocketOpenSSL::Init() {
DCHECK(!ssl_);
DCHECK(!transport_bio_);
@@ -651,10 +652,10 @@ bool SSLClientSocketOpenSSL::Init() {
ssl_ = SSL_new(context->ssl_ctx());
if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this))
- return false;
+ return ERR_UNEXPECTED;
if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str()))
- return false;
+ return ERR_UNEXPECTED;
trying_cached_session_ = context->session_cache()->SetSSLSessionWithKey(
ssl_, GetSocketSessionCacheKey(*this));
@@ -662,7 +663,7 @@ bool SSLClientSocketOpenSSL::Init() {
BIO* ssl_bio = NULL;
// 0 => use default buffer sizes.
if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0))
- return false;
+ return ERR_UNEXPECTED;
DCHECK(ssl_bio);
DCHECK(transport_bio_);
@@ -677,22 +678,16 @@ bool SSLClientSocketOpenSSL::Init() {
bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1);
options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled);
-#if defined(SSL_OP_NO_TLSv1_1)
bool tls1_1_enabled =
(ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1);
options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled);
-#endif
-#if defined(SSL_OP_NO_TLSv1_2)
bool tls1_2_enabled =
(ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2);
options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled);
-#endif
-#if defined(SSL_OP_NO_COMPRESSION)
options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true);
-#endif
// TODO(joth): Set this conditionally, see http://crbug.com/55410
options.ConfigureFlag(SSL_OP_LEGACY_SERVER_CONNECT, true);
@@ -703,13 +698,10 @@ bool SSLClientSocketOpenSSL::Init() {
// Same as above, this time for the SSL mode.
SslSetClearMask mode;
-#if defined(SSL_MODE_RELEASE_BUFFERS)
mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true);
-#endif
-#if defined(SSL_MODE_SMALL_BUFFERS)
- mode.ConfigureFlag(SSL_MODE_SMALL_BUFFERS, true);
-#endif
+ mode.ConfigureFlag(SSL_MODE_HANDSHAKE_CUTTHROUGH,
+ ssl_config_.false_start_enabled);
SSL_set_mode(ssl_, mode.set_mask);
SSL_clear_mode(ssl_, mode.clear_mask);
@@ -760,12 +752,14 @@ bool SSLClientSocketOpenSSL::Init() {
SSL_enable_tls_channel_id(ssl_);
}
- return true;
+ return OK;
}
void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
// Since Run may result in Read being called, clear |user_read_callback_|
// up front.
+ if (rv > 0)
+ was_ever_used_ = true;
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
base::ResetAndReturn(&user_read_callback_).Run(rv);
@@ -774,6 +768,8 @@ void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
void SSLClientSocketOpenSSL::DoWriteCallback(int rv) {
// Since Run may result in Write being called, clear |user_write_callback_|
// up front.
+ if (rv > 0)
+ was_ever_used_ = true;
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
base::ResetAndReturn(&user_write_callback_).Run(rv);
@@ -796,7 +792,7 @@ bool SSLClientSocketOpenSSL::DoTransportIO() {
int SSLClientSocketOpenSSL::DoHandshake() {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
- int net_error = net::OK;
+ int net_error = OK;
int rv = SSL_do_handshake(ssl_);
if (client_auth_cert_needed_) {
@@ -914,25 +910,11 @@ void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
}
X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() {
- if (server_cert_.get())
- return server_cert_.get();
+ server_cert_chain_->Reset(SSL_get_peer_cert_chain(ssl_));
+ server_cert_ = server_cert_chain_->AsOSChain();
- crypto::ScopedOpenSSL<X509, X509_free> cert(SSL_get_peer_certificate(ssl_));
- if (!cert.get()) {
- LOG(WARNING) << "SSL_get_peer_certificate returned NULL";
- return NULL;
- }
-
- // Unlike SSL_get_peer_certificate, SSL_get_peer_cert_chain does not
- // increment the reference so sk_X509_free does not need to be called.
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_);
- X509Certificate::OSCertHandles intermediates;
- if (chain) {
- for (int i = 0; i < sk_X509_num(chain); ++i)
- intermediates.push_back(sk_X509_value(chain, i));
- }
- server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates);
- DCHECK(server_cert_.get());
+ if (!server_cert_chain_->IsValid())
+ DVLOG(1) << "UpdateServerCert received invalid certificate chain from peer";
return server_cert_.get();
}
@@ -1270,10 +1252,7 @@ int SSLClientSocketOpenSSL::TransportReadComplete(int result) {
DCHECK(recv_buffer_.get());
int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
// A write into a memory BIO should always succeed.
- // Force values on the stack for http://crbug.com/335557
- base::debug::Alias(&result);
- base::debug::Alias(&ret);
- CHECK_EQ(result, ret);
+ DCHECK_EQ(result, ret);
}
recv_buffer_ = NULL;
transport_recv_busy_ = false;
@@ -1287,7 +1266,6 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
DCHECK(ssl == ssl_);
DCHECK(*x509 == NULL);
DCHECK(*pkey == NULL);
-
if (!ssl_config_.send_client_cert) {
// First pass: we know that a client certificate is needed, but we do not
// have one at hand.
@@ -1303,11 +1281,21 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
OPENSSL_free(str);
}
+ const unsigned char* client_cert_types;
+ size_t num_client_cert_types;
+ SSL_get_client_certificate_types(ssl, &client_cert_types,
+ &num_client_cert_types);
+ for (size_t i = 0; i < num_client_cert_types; i++) {
+ cert_key_types_.push_back(
+ static_cast<SSLClientCertType>(client_cert_types[i]));
+ }
+
return -1; // Suspends handshake.
}
// Second pass: a client certificate should have been selected.
if (ssl_config_.client_cert.get()) {
+#if defined(USE_OPENSSL_CERTS)
// A note about ownership: FetchClientCertPrivateKey() increments
// the reference count of the EVP_PKEY. Ownership of this reference
// is passed directly to OpenSSL, which will release the reference
@@ -1323,6 +1311,10 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
return 1;
}
LOG(WARNING) << "Client cert found without private key";
+#else // !defined(USE_OPENSSL_CERTS)
+ // OS handling of client certificates is not yet implemented.
+ NOTIMPLEMENTED();
+#endif // defined(USE_OPENSSL_CERTS)
}
// Send no client certificate.
@@ -1363,10 +1355,32 @@ void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl,
ServerBoundCertService::kEPKIPassword,
encrypted_private_key_info,
subject_public_key_info));
+ if (!ec_private_key)
+ return;
set_channel_id_sent(true);
*pkey = EVP_PKEY_dup(ec_private_key->key());
}
+int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) {
+ if (!completed_handshake_) {
+ // If the first handshake hasn't completed then we accept any certificates
+ // because we verify after the handshake.
+ return 1;
+ }
+
+ CHECK(server_cert_.get());
+
+ PeerCertificateChain chain(store_ctx->untrusted);
+ if (chain.IsValid() && server_cert_->Equals(chain.AsOSChain()))
+ return 1;
+
+ if (!chain.IsValid())
+ LOG(ERROR) << "Received invalid certificate chain between handshakes";
+ else
+ LOG(ERROR) << "Server certificate changed between handshakes";
+ return 0;
+}
+
// SelectNextProtoCallback is called by OpenSSL during the handshake. If the
// server supports NPN, selects a protocol from the list that the server
// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the
@@ -1375,7 +1389,6 @@ int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen) {
-#if defined(OPENSSL_NPN_NEGOTIATED)
if (ssl_config_.next_protos.empty()) {
*out = reinterpret_cast<uint8*>(
const_cast<char*>(kDefaultSupportedNPNProtocol));
@@ -1415,8 +1428,12 @@ int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out,
npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen);
server_protos_.assign(reinterpret_cast<const char*>(in), inlen);
DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_;
-#endif
return SSL_TLSEXT_ERR_OK;
}
+scoped_refptr<X509Certificate>
+SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const {
+ return server_cert_;
+}
+
} // namespace net
diff --git a/chromium/net/socket/ssl_client_socket_openssl.h b/chromium/net/socket/ssl_client_socket_openssl.h
index 5f4800a08de..5d70c0523fa 100644
--- a/chromium/net/socket/ssl_client_socket_openssl.h
+++ b/chromium/net/socket/ssl_client_socket_openssl.h
@@ -16,6 +16,7 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
#include "net/ssl/server_bound_cert_service.h"
+#include "net/ssl/ssl_client_cert_type.h"
#include "net/ssl/ssl_config_service.h"
// Avoid including misc OpenSSL headers, i.e.:
@@ -27,6 +28,8 @@ typedef struct evp_pkey_st EVP_PKEY;
typedef struct ssl_st SSL;
// <openssl/x509.h>
typedef struct x509_st X509;
+// <openssl/ossl_type.h>
+typedef struct x509_store_ctx_st X509_STORE_CTX;
namespace net {
@@ -87,15 +90,21 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
+
+ protected:
+ // SSLClientSocket implementation.
+ virtual scoped_refptr<X509Certificate> GetUnverifiedServerCertificateChain()
+ const OVERRIDE;
private:
+ class PeerCertificateChain;
class SSLContext;
friend class SSLClientSocket;
friend class SSLContext;
- bool Init();
+ int Init();
void DoReadCallback(int result);
void DoWriteCallback(int result);
@@ -131,6 +140,11 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
// Channel IDs.
void ChannelIDRequestCallback(SSL* ssl, EVP_PKEY** pkey);
+ // CertVerifyCallback is called to verify the server's certificates. We do
+ // verification after the handshake so this function only enforces that the
+ // certificates don't change during renegotiation.
+ int CertVerifyCallback(X509_STORE_CTX *store_ctx);
+
// Callback from the SSL layer to check which NPN protocol we are supporting
int SelectNextProtoCallback(unsigned char** out, unsigned char* outlen,
const unsigned char* in, unsigned int inlen);
@@ -169,16 +183,24 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
int transport_write_error_;
// Set when handshake finishes.
+ scoped_ptr<PeerCertificateChain> server_cert_chain_;
scoped_refptr<X509Certificate> server_cert_;
CertVerifyResult server_cert_verify_result_;
bool completed_handshake_;
+ // Set when Read() or Write() successfully reads or writes data to or from the
+ // network.
+ bool was_ever_used_;
+
// Stores client authentication information between ClientAuthHandler and
// GetSSLCertRequestInfo calls.
bool client_auth_cert_needed_;
// List of DER-encoded X.509 DistinguishedName of certificate authorities
// allowed by the server.
std::vector<std::string> cert_authorities_;
+ // List of SSLClientCertType values for client certificates allowed by the
+ // server.
+ std::vector<SSLClientCertType> cert_key_types_;
CertVerifier* const cert_verifier_;
scoped_ptr<SingleRequestCertVerifier> verifier_;
diff --git a/chromium/net/socket/ssl_client_socket_openssl_unittest.cc b/chromium/net/socket/ssl_client_socket_openssl_unittest.cc
index 48a4813f159..d4e0685467e 100644
--- a/chromium/net/socket/ssl_client_socket_openssl_unittest.cc
+++ b/chromium/net/socket/ssl_client_socket_openssl_unittest.cc
@@ -16,7 +16,6 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_handle.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/values.h"
#include "crypto/openssl_util.h"
@@ -35,9 +34,7 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/tcp_client_socket.h"
-#include "net/ssl/default_server_bound_cert_store.h"
#include "net/ssl/openssl_client_key_store.h"
-#include "net/ssl/server_bound_cert_service.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_config_service.h"
#include "net/test/cert_test_util.h"
@@ -49,6 +46,8 @@ namespace net {
namespace {
+// These client auth tests are currently dependent on OpenSSL's struct X509.
+#if defined(USE_OPENSSL_CERTS)
typedef OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
// BIO_free is a macro, it can't be used as a template parameter.
@@ -62,35 +61,6 @@ typedef crypto::ScopedOpenSSL<BIGNUM, BN_free> ScopedBIGNUM;
const SSLConfig kDefaultSSLConfig;
-// A ServerBoundCertStore that always returns an error when asked for a
-// certificate.
-class FailingServerBoundCertStore : public ServerBoundCertStore {
- virtual int GetServerBoundCert(const std::string& server_identifier,
- base::Time* expiration_time,
- std::string* private_key_result,
- std::string* cert_result,
- const GetCertCallback& callback) OVERRIDE {
- return ERR_UNEXPECTED;
- }
- virtual void SetServerBoundCert(const std::string& server_identifier,
- base::Time creation_time,
- base::Time expiration_time,
- const std::string& private_key,
- const std::string& cert) OVERRIDE {}
- virtual void DeleteServerBoundCert(const std::string& server_identifier,
- const base::Closure& completion_callback)
- OVERRIDE {}
- virtual void DeleteAllCreatedBetween(base::Time delete_begin,
- base::Time delete_end,
- const base::Closure& completion_callback)
- OVERRIDE {}
- virtual void DeleteAll(const base::Closure& completion_callback) OVERRIDE {}
- virtual void GetAllServerBoundCerts(const GetCertListCallback& callback)
- OVERRIDE {}
- virtual int GetCertCount() OVERRIDE { return 0; }
- virtual void SetForceKeepSessionState() OVERRIDE {}
-};
-
// Loads a PEM-encoded private key file into a scoped EVP_PKEY object.
// |filepath| is the private key file path.
// |*pkey| is reset to the new EVP_PKEY on success, untouched otherwise.
@@ -139,20 +109,6 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest {
}
protected:
- void EnabledChannelID() {
- cert_service_.reset(
- new ServerBoundCertService(new DefaultServerBoundCertStore(NULL),
- base::MessageLoopProxy::current()));
- context_.server_bound_cert_service = cert_service_.get();
- }
-
- void EnabledFailingChannelID() {
- cert_service_.reset(
- new ServerBoundCertService(new FailingServerBoundCertStore(),
- base::MessageLoopProxy::current()));
- context_.server_bound_cert_service = cert_service_.get();
- }
-
scoped_ptr<SSLClientSocket> CreateSSLClientSocket(
scoped_ptr<StreamSocket> transport_socket,
const HostPortPair& host_and_port,
@@ -234,7 +190,6 @@ class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest {
return ssl_info.client_cert_sent;
}
- scoped_ptr<ServerBoundCertService> cert_service_;
ClientSocketFactory* socket_factory_;
scoped_ptr<MockCertVerifier> cert_verifier_;
scoped_ptr<TransportSecurityState> transport_security_state_;
@@ -321,45 +276,7 @@ TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendGoodCert) {
sock_->Disconnect();
EXPECT_FALSE(sock_->IsConnected());
}
-
-// Connect to a server using channel id. It should allow the connection.
-TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendChannelID) {
- SpawnedTestServer::SSLOptions ssl_options;
-
- ASSERT_TRUE(ConnectToTestServer(ssl_options));
-
- EnabledChannelID();
- SSLConfig ssl_config = kDefaultSSLConfig;
- ssl_config.channel_id_enabled = true;
-
- int rv;
- ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
-
- EXPECT_EQ(OK, rv);
- EXPECT_TRUE(sock_->IsConnected());
- EXPECT_TRUE(sock_->WasChannelIDSent());
-
- sock_->Disconnect();
- EXPECT_FALSE(sock_->IsConnected());
-}
-
-// Connect to a server using channel id but without sending a key. It should
-// fail.
-TEST_F(SSLClientSocketOpenSSLClientAuthTest, FailingChannelID) {
- SpawnedTestServer::SSLOptions ssl_options;
-
- ASSERT_TRUE(ConnectToTestServer(ssl_options));
-
- EnabledFailingChannelID();
- SSLConfig ssl_config = kDefaultSSLConfig;
- ssl_config.channel_id_enabled = true;
-
- int rv;
- ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
-
- EXPECT_EQ(ERR_UNEXPECTED, rv);
- EXPECT_FALSE(sock_->IsConnected());
-}
+#endif // defined(USE_OPENSSL_CERTS)
} // namespace
} // namespace net
diff --git a/chromium/net/socket/ssl_client_socket_pool.cc b/chromium/net/socket/ssl_client_socket_pool.cc
index de315fdc01a..2c704658820 100644
--- a/chromium/net/socket/ssl_client_socket_pool.cc
+++ b/chromium/net/socket/ssl_client_socket_pool.cc
@@ -124,7 +124,7 @@ SSLConnectJob::SSLConnectJob(const std::string& group_name,
context.server_bound_cert_service,
context.transport_security_state,
context.cert_transparency_verifier,
- (params->privacy_mode() == kPrivacyModeEnabled
+ (params->privacy_mode() == PRIVACY_MODE_ENABLED
? "pm/" + context.ssl_session_cache_shard
: context.ssl_session_cache_shard)),
callback_(base::Bind(&SSLConnectJob::OnIOComplete,
diff --git a/chromium/net/socket/ssl_client_socket_pool_unittest.cc b/chromium/net/socket/ssl_client_socket_pool_unittest.cc
index 92ad51a4351..6ae07ed0bda 100644
--- a/chromium/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/chromium/net/socket/ssl_client_socket_pool_unittest.cc
@@ -159,15 +159,15 @@ class SSLClientSocketPoolTest
proxy == ProxyServer::SCHEME_HTTP ? http_proxy_socket_params_ : NULL,
HostPortPair("host", 443),
ssl_config_,
- kPrivacyModeDisabled,
+ PRIVACY_MODE_DISABLED,
0,
false,
want_spdy_over_npn));
}
void AddAuthToCache() {
- const base::string16 kFoo(ASCIIToUTF16("foo"));
- const base::string16 kBar(ASCIIToUTF16("bar"));
+ const base::string16 kFoo(base::ASCIIToUTF16("foo"));
+ const base::string16 kBar(base::ASCIIToUTF16("bar"));
session_->http_auth_cache()->Add(GURL("http://proxy:443/"),
"MyRealm1",
HttpAuth::AUTH_SCHEME_BASIC,
@@ -227,8 +227,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SSLClientSocketPoolTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(SSLClientSocketPoolTest, TCPFail) {
StaticSocketDataProvider data;
@@ -799,7 +798,8 @@ TEST_P(SSLClientSocketPoolTest, NeedProxyAuth) {
EXPECT_FALSE(tunnel_handle->socket()->IsConnected());
}
-TEST_P(SSLClientSocketPoolTest, IPPooling) {
+// TODO(rch): re-enable this.
+TEST_P(SSLClientSocketPoolTest, DISABLED_IPPooling) {
const int kTestPort = 80;
struct TestHosts {
std::string name;
@@ -830,7 +830,7 @@ TEST_P(SSLClientSocketPoolTest, IPPooling) {
// Setup a SpdySessionKey
test_hosts[i].key = SpdySessionKey(
HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
}
MockRead reads[] = {
@@ -891,7 +891,7 @@ void SSLClientSocketPoolTest::TestIPPoolingDisabled(
// Setup a SpdySessionKey
test_hosts[i].key = SpdySessionKey(
HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
}
MockRead reads[] = {
diff --git a/chromium/net/socket/ssl_client_socket_unittest.cc b/chromium/net/socket/ssl_client_socket_unittest.cc
index 14633a958ba..51c67565dad 100644
--- a/chromium/net/socket/ssl_client_socket_unittest.cc
+++ b/chromium/net/socket/ssl_client_socket_unittest.cc
@@ -6,6 +6,8 @@
#include "base/callback_helpers.h"
#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
#include "net/base/address_list.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -21,6 +23,7 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/tcp_client_socket.h"
+#include "net/ssl/default_server_bound_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_config_service.h"
#include "net/test/cert_test_util.h"
@@ -101,10 +104,10 @@ class WrappedStreamSocket : public StreamSocket {
const CompletionCallback& callback) OVERRIDE {
return transport_->Write(buf, buf_len, callback);
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE {
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE {
return transport_->SetReceiveBufferSize(size);
}
- virtual bool SetSendBufferSize(int32 size) OVERRIDE {
+ virtual int SetSendBufferSize(int32 size) OVERRIDE {
return transport_->SetSendBufferSize(size);
}
@@ -327,146 +330,238 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket {
// Socket implementation:
virtual int Read(IOBuffer* buf,
int buf_len,
- const CompletionCallback& callback) OVERRIDE {
- return read_state_.RunWrappedFunction(buf, buf_len, callback);
- }
+ const CompletionCallback& callback) OVERRIDE;
virtual int Write(IOBuffer* buf,
int buf_len,
- const CompletionCallback& callback) OVERRIDE {
- return write_state_.RunWrappedFunction(buf, buf_len, callback);
- }
+ const CompletionCallback& callback) OVERRIDE;
- // Causes the next call to Read() to return ERR_IO_PENDING, not completing
- // (invoking the callback) until UnblockRead() has been called and the
- // underlying transport has completed.
- void SetNextReadShouldBlock() { read_state_.SetShouldBlock(); }
- void UnblockRead() { read_state_.Unblock(); }
+ // Blocks read results on the socket. Reads will not complete until
+ // UnblockReadResult() has been called and a result is ready from the
+ // underlying transport. Note: if BlockReadResult() is called while there is a
+ // hanging asynchronous Read(), that Read is blocked.
+ void BlockReadResult();
+ void UnblockReadResult();
- // Causes the next call to Write() to return ERR_IO_PENDING, not completing
- // (invoking the callback) until UnblockWrite() has been called and the
- // underlying transport has completed.
- void SetNextWriteShouldBlock() { write_state_.SetShouldBlock(); }
- void UnblockWrite() { write_state_.Unblock(); }
+ // Waits for the blocked Read() call to be complete at the underlying
+ // transport.
+ void WaitForReadResult();
+
+ // Causes the next call to Write() to return ERR_IO_PENDING, not beginning the
+ // underlying transport until UnblockWrite() has been called. Note: if there
+ // is a pending asynchronous write, it is NOT blocked. For purposes of
+ // blocking writes, data is considered to have reached the underlying
+ // transport as soon as Write() is called.
+ void BlockWrite();
+ void UnblockWrite();
+
+ // Waits for the blocked Write() call to be scheduled.
+ void WaitForWrite();
private:
- // Tracks the state for simulating a blocking Read/Write operation.
- class BlockingState {
- public:
- // Wrapper for the underlying Socket function to call (ie: Read/Write).
- typedef base::Callback<int(IOBuffer*, int, const CompletionCallback&)>
- WrappedSocketFunction;
-
- explicit BlockingState(const WrappedSocketFunction& function);
- ~BlockingState() {}
-
- // Sets the next call to RunWrappedFunction() to block, returning
- // ERR_IO_PENDING and not invoking the user callback until Unblock() is
- // called.
- void SetShouldBlock();
-
- // Unblocks the currently blocked pending function, invoking the user
- // callback if the results are immediately available.
- // Note: It's not valid to call this unless SetShouldBlock() has been
- // called beforehand.
- void Unblock();
-
- // Performs the wrapped socket function on the underlying transport. If
- // configured to block via SetShouldBlock(), then |user_callback| will not
- // be invoked until Unblock() has been called.
- int RunWrappedFunction(IOBuffer* buf,
- int len,
- const CompletionCallback& user_callback);
-
- private:
- // Handles completion from the underlying wrapped socket function.
- void OnCompleted(int result);
-
- WrappedSocketFunction wrapped_function_;
- bool should_block_;
- bool have_result_;
- int pending_result_;
- CompletionCallback user_callback_;
- };
+ // Handles completion from the underlying transport read.
+ void OnReadCompleted(int result);
- BlockingState read_state_;
- BlockingState write_state_;
+ // True if read callbacks are blocked.
+ bool should_block_read_;
- DISALLOW_COPY_AND_ASSIGN(FakeBlockingStreamSocket);
+ // The user callback for the pending read call.
+ CompletionCallback pending_read_callback_;
+
+ // The result for the blocked read callback, or ERR_IO_PENDING if not
+ // completed.
+ int pending_read_result_;
+
+ // WaitForReadResult() wait loop.
+ scoped_ptr<base::RunLoop> read_loop_;
+
+ // True if write calls are blocked.
+ bool should_block_write_;
+
+ // The buffer for the pending write, or NULL if not scheduled.
+ scoped_refptr<IOBuffer> pending_write_buf_;
+
+ // The callback for the pending write call.
+ CompletionCallback pending_write_callback_;
+
+ // The length for the pending write, or -1 if not scheduled.
+ int pending_write_len_;
+
+ // WaitForWrite() wait loop.
+ scoped_ptr<base::RunLoop> write_loop_;
};
FakeBlockingStreamSocket::FakeBlockingStreamSocket(
scoped_ptr<StreamSocket> transport)
: WrappedStreamSocket(transport.Pass()),
- read_state_(base::Bind(&Socket::Read,
- base::Unretained(transport_.get()))),
- write_state_(base::Bind(&Socket::Write,
- base::Unretained(transport_.get()))) {}
-
-FakeBlockingStreamSocket::BlockingState::BlockingState(
- const WrappedSocketFunction& function)
- : wrapped_function_(function),
- should_block_(false),
- have_result_(false),
- pending_result_(OK) {}
-
-void FakeBlockingStreamSocket::BlockingState::SetShouldBlock() {
- DCHECK(!should_block_);
- should_block_ = true;
+ should_block_read_(false),
+ pending_read_result_(ERR_IO_PENDING),
+ should_block_write_(false),
+ pending_write_len_(-1) {}
+
+int FakeBlockingStreamSocket::Read(IOBuffer* buf,
+ int len,
+ const CompletionCallback& callback) {
+ DCHECK(pending_read_callback_.is_null());
+ DCHECK_EQ(ERR_IO_PENDING, pending_read_result_);
+ DCHECK(!callback.is_null());
+
+ int rv = transport_->Read(buf, len, base::Bind(
+ &FakeBlockingStreamSocket::OnReadCompleted, base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ // Save the callback to be called later.
+ pending_read_callback_ = callback;
+ } else if (should_block_read_) {
+ // Save the callback and read result to be called later.
+ pending_read_callback_ = callback;
+ OnReadCompleted(rv);
+ rv = ERR_IO_PENDING;
+ }
+ return rv;
+}
+
+int FakeBlockingStreamSocket::Write(IOBuffer* buf,
+ int len,
+ const CompletionCallback& callback) {
+ DCHECK(buf);
+ DCHECK_LE(0, len);
+
+ if (!should_block_write_)
+ return transport_->Write(buf, len, callback);
+
+ // Schedule the write, but do nothing.
+ DCHECK(!pending_write_buf_);
+ DCHECK_EQ(-1, pending_write_len_);
+ DCHECK(pending_write_callback_.is_null());
+ DCHECK(!callback.is_null());
+ pending_write_buf_ = buf;
+ pending_write_len_ = len;
+ pending_write_callback_ = callback;
+
+ // Stop the write loop, if any.
+ if (write_loop_)
+ write_loop_->Quit();
+ return ERR_IO_PENDING;
+}
+
+void FakeBlockingStreamSocket::BlockReadResult() {
+ DCHECK(!should_block_read_);
+ should_block_read_ = true;
}
-void FakeBlockingStreamSocket::BlockingState::Unblock() {
- DCHECK(should_block_);
- should_block_ = false;
+void FakeBlockingStreamSocket::UnblockReadResult() {
+ DCHECK(should_block_read_);
+ should_block_read_ = false;
// If the operation is still pending in the underlying transport, immediately
- // return - OnCompleted() will handle invoking the callback once the transport
- // has completed.
- if (!have_result_)
+ // return - OnReadCompleted() will handle invoking the callback once the
+ // transport has completed.
+ if (pending_read_result_ == ERR_IO_PENDING)
return;
+ int result = pending_read_result_;
+ pending_read_result_ = ERR_IO_PENDING;
+ base::ResetAndReturn(&pending_read_callback_).Run(result);
+}
- have_result_ = false;
+void FakeBlockingStreamSocket::WaitForReadResult() {
+ DCHECK(should_block_read_);
+ DCHECK(!read_loop_);
- base::ResetAndReturn(&user_callback_).Run(pending_result_);
+ if (pending_read_result_ != ERR_IO_PENDING)
+ return;
+ read_loop_.reset(new base::RunLoop);
+ read_loop_->Run();
+ read_loop_.reset();
+ DCHECK_NE(ERR_IO_PENDING, pending_read_result_);
}
-int FakeBlockingStreamSocket::BlockingState::RunWrappedFunction(
- IOBuffer* buf,
- int len,
- const CompletionCallback& callback) {
-
- // The callback to be called by the underlying transport. Either forward
- // directly to the user's callback if not set to block, or intercept it with
- // OnCompleted so that the user's callback is not invoked until Unblock() is
- // called.
- CompletionCallback transport_callback =
- !should_block_ ? callback : base::Bind(&BlockingState::OnCompleted,
- base::Unretained(this));
- int rv = wrapped_function_.Run(buf, len, transport_callback);
- if (should_block_) {
- user_callback_ = callback;
- // May have completed synchronously.
- have_result_ = (rv != ERR_IO_PENDING);
- pending_result_ = rv;
- return ERR_IO_PENDING;
+void FakeBlockingStreamSocket::BlockWrite() {
+ DCHECK(!should_block_write_);
+ should_block_write_ = true;
+}
+
+void FakeBlockingStreamSocket::UnblockWrite() {
+ DCHECK(should_block_write_);
+ should_block_write_ = false;
+
+ // Do nothing if UnblockWrite() was called after BlockWrite(),
+ // without a Write() in between.
+ if (!pending_write_buf_)
+ return;
+
+ int rv = transport_->Write(pending_write_buf_, pending_write_len_,
+ pending_write_callback_);
+ pending_write_buf_ = NULL;
+ pending_write_len_ = -1;
+ if (rv == ERR_IO_PENDING) {
+ pending_write_callback_.Reset();
+ } else {
+ base::ResetAndReturn(&pending_write_callback_).Run(rv);
}
+}
- return rv;
+void FakeBlockingStreamSocket::WaitForWrite() {
+ DCHECK(should_block_write_);
+ DCHECK(!write_loop_);
+
+ if (pending_write_buf_)
+ return;
+ write_loop_.reset(new base::RunLoop);
+ write_loop_->Run();
+ write_loop_.reset();
+ DCHECK(pending_write_buf_);
}
-void FakeBlockingStreamSocket::BlockingState::OnCompleted(int result) {
- if (should_block_) {
+void FakeBlockingStreamSocket::OnReadCompleted(int result) {
+ DCHECK_EQ(ERR_IO_PENDING, pending_read_result_);
+ DCHECK(!pending_read_callback_.is_null());
+
+ if (should_block_read_) {
// Store the result so that the callback can be invoked once Unblock() is
// called.
- have_result_ = true;
- pending_result_ = result;
- return;
+ pending_read_result_ = result;
+
+ // Stop the WaitForReadResult() call if any.
+ if (read_loop_)
+ read_loop_->Quit();
+ } else {
+ // Either the Read() was never blocked or UnblockReadResult() was called
+ // before the Read() completed. Either way, run the callback.
+ base::ResetAndReturn(&pending_read_callback_).Run(result);
}
-
- // Otherwise, the Unblock() function was called before the underlying
- // transport completed, so run the user's callback immediately.
- base::ResetAndReturn(&user_callback_).Run(result);
}
+// CountingStreamSocket wraps an existing StreamSocket and maintains a count of
+// reads and writes on the socket.
+class CountingStreamSocket : public WrappedStreamSocket {
+ public:
+ explicit CountingStreamSocket(scoped_ptr<StreamSocket> transport)
+ : WrappedStreamSocket(transport.Pass()),
+ read_count_(0),
+ write_count_(0) {}
+ virtual ~CountingStreamSocket() {}
+
+ // Socket implementation:
+ virtual int Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE {
+ read_count_++;
+ return transport_->Read(buf, buf_len, callback);
+ }
+ virtual int Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE {
+ write_count_++;
+ return transport_->Write(buf, buf_len, callback);
+ }
+
+ int read_count() const { return read_count_; }
+ int write_count() const { return write_count_; }
+
+ private:
+ int read_count_;
+ int write_count_;
+};
+
// CompletionCallback that will delete the associated StreamSocket when
// the callback is invoked.
class DeleteSocketCallback : public TestCompletionCallbackBase {
@@ -496,6 +591,35 @@ class DeleteSocketCallback : public TestCompletionCallbackBase {
DISALLOW_COPY_AND_ASSIGN(DeleteSocketCallback);
};
+// A ServerBoundCertStore that always returns an error when asked for a
+// certificate.
+class FailingServerBoundCertStore : public ServerBoundCertStore {
+ virtual int GetServerBoundCert(const std::string& server_identifier,
+ base::Time* expiration_time,
+ std::string* private_key_result,
+ std::string* cert_result,
+ const GetCertCallback& callback) OVERRIDE {
+ return ERR_UNEXPECTED;
+ }
+ virtual void SetServerBoundCert(const std::string& server_identifier,
+ base::Time creation_time,
+ base::Time expiration_time,
+ const std::string& private_key,
+ const std::string& cert) OVERRIDE {}
+ virtual void DeleteServerBoundCert(const std::string& server_identifier,
+ const base::Closure& completion_callback)
+ OVERRIDE {}
+ virtual void DeleteAllCreatedBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& completion_callback)
+ OVERRIDE {}
+ virtual void DeleteAll(const base::Closure& completion_callback) OVERRIDE {}
+ virtual void GetAllServerBoundCerts(const GetCertListCallback& callback)
+ OVERRIDE {}
+ virtual int GetCertCount() OVERRIDE { return 0; }
+ virtual void SetForceKeepSessionState() OVERRIDE {}
+};
+
class SSLClientSocketTest : public PlatformTest {
public:
SSLClientSocketTest()
@@ -508,6 +632,44 @@ class SSLClientSocketTest : public PlatformTest {
}
protected:
+ // The address of the spawned test server, after calling StartTestServer().
+ const AddressList& addr() const { return addr_; }
+
+ // The SpawnedTestServer object, after calling StartTestServer().
+ const SpawnedTestServer* test_server() const { return test_server_.get(); }
+
+ // Starts the test server with SSL configuration |ssl_options|. Returns true
+ // on success.
+ bool StartTestServer(const SpawnedTestServer::SSLOptions& ssl_options) {
+ test_server_.reset(new SpawnedTestServer(
+ SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath()));
+ if (!test_server_->Start()) {
+ LOG(ERROR) << "Could not start SpawnedTestServer";
+ return false;
+ }
+
+ if (!test_server_->GetAddressList(&addr_)) {
+ LOG(ERROR) << "Could not get SpawnedTestServer address list";
+ return false;
+ }
+ return true;
+ }
+
+ // Sets up a TCP connection to a HTTPS server. To actually do the SSL
+ // handshake, follow up with call to CreateAndConnectSSLClientSocket() below.
+ bool ConnectToTestServer(const SpawnedTestServer::SSLOptions& ssl_options) {
+ if (!StartTestServer(ssl_options))
+ return false;
+
+ transport_.reset(new TCPClientSocket(addr_, &log_, NetLog::Source()));
+ int rv = callback_.GetResult(transport_->Connect(callback_.callback()));
+ if (rv != OK) {
+ LOG(ERROR) << "Could not connect to SpawnedTestServer";
+ return false;
+ }
+ return true;
+ }
+
scoped_ptr<SSLClientSocket> CreateSSLClientSocket(
scoped_ptr<StreamSocket> transport_socket,
const HostPortPair& host_and_port,
@@ -518,10 +680,212 @@ class SSLClientSocketTest : public PlatformTest {
connection.Pass(), host_and_port, ssl_config, context_);
}
+ // Create an SSLClientSocket object and use it to connect to a test
+ // server, then wait for connection results. This must be called after
+ // a successful ConnectToTestServer() call.
+ // |ssl_config| the SSL configuration to use.
+ // |result| will retrieve the ::Connect() result value.
+ // Returns true on success, false otherwise. Success means that the socket
+ // could be created and its Connect() was called, not that the connection
+ // itself was a success.
+ bool CreateAndConnectSSLClientSocket(SSLConfig& ssl_config, int* result) {
+ sock_ = CreateSSLClientSocket(
+ transport_.Pass(), test_server_->host_port_pair(), ssl_config);
+
+ if (sock_->IsConnected()) {
+ LOG(ERROR) << "SSL Socket prematurely connected";
+ return false;
+ }
+
+ *result = callback_.GetResult(sock_->Connect(callback_.callback()));
+ return true;
+ }
+
ClientSocketFactory* socket_factory_;
scoped_ptr<MockCertVerifier> cert_verifier_;
scoped_ptr<TransportSecurityState> transport_security_state_;
SSLClientSocketContext context_;
+ scoped_ptr<SSLClientSocket> sock_;
+ CapturingNetLog log_;
+
+ private:
+ scoped_ptr<StreamSocket> transport_;
+ scoped_ptr<SpawnedTestServer> test_server_;
+ TestCompletionCallback callback_;
+ AddressList addr_;
+};
+
+// Verifies the correctness of GetSSLCertRequestInfo.
+class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest {
+ protected:
+ // Creates a test server with the given SSLOptions, connects to it and returns
+ // the SSLCertRequestInfo reported by the socket.
+ scoped_refptr<SSLCertRequestInfo> GetCertRequest(
+ SpawnedTestServer::SSLOptions ssl_options) {
+ SpawnedTestServer test_server(
+ SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath());
+ if (!test_server.Start())
+ return NULL;
+
+ AddressList addr;
+ if (!test_server.GetAddressList(&addr))
+ return NULL;
+
+ TestCompletionCallback callback;
+ CapturingNetLog log;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr, &log, NetLog::Source()));
+ int rv = transport->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server.host_port_pair(), kDefaultSSLConfig));
+ EXPECT_FALSE(sock->IsConnected());
+
+ rv = sock->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ scoped_refptr<SSLCertRequestInfo> request_info = new SSLCertRequestInfo();
+ sock->GetSSLCertRequestInfo(request_info.get());
+ sock->Disconnect();
+ EXPECT_FALSE(sock->IsConnected());
+ EXPECT_TRUE(
+ test_server.host_port_pair().Equals(request_info->host_and_port));
+
+ return request_info;
+ }
+};
+
+class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
+ protected:
+ // Creates an SSLClientSocket with |client_config| attached to a
+ // FakeBlockingStreamSocket, returning both in |*out_raw_transport| and
+ // |*out_sock|. The FakeBlockingStreamSocket is owned by the SSLClientSocket,
+ // so |*out_raw_transport| is a raw pointer.
+ //
+ // The client socket will begin a connect using |callback| but stop before the
+ // server's finished message is received. The finished message will be blocked
+ // in |*out_raw_transport|. To complete the handshake and successfully read
+ // data, the caller must unblock reads on |*out_raw_transport|. (Note that, if
+ // the client successfully false started, |callback.WaitForResult()| will
+ // return OK without unblocking transport reads. But Read() will still block.)
+ //
+ // Must be called after StartTestServer is called.
+ void CreateAndConnectUntilServerFinishedReceived(
+ const SSLConfig& client_config,
+ TestCompletionCallback* callback,
+ FakeBlockingStreamSocket** out_raw_transport,
+ scoped_ptr<SSLClientSocket>* out_sock) {
+ CHECK(test_server());
+
+ scoped_ptr<StreamSocket> real_transport(
+ new TCPClientSocket(addr(), NULL, NetLog::Source()));
+ scoped_ptr<FakeBlockingStreamSocket> transport(
+ new FakeBlockingStreamSocket(real_transport.Pass()));
+ int rv = callback->GetResult(transport->Connect(callback->callback()));
+ EXPECT_EQ(OK, rv);
+
+ FakeBlockingStreamSocket* raw_transport = transport.get();
+ scoped_ptr<SSLClientSocket> sock =
+ CreateSSLClientSocket(transport.PassAs<StreamSocket>(),
+ test_server()->host_port_pair(),
+ client_config);
+
+ // Connect. Stop before the client processes the first server leg
+ // (ServerHello, etc.)
+ raw_transport->BlockReadResult();
+ rv = sock->Connect(callback->callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ raw_transport->WaitForReadResult();
+
+ // Release the ServerHello and wait for the client to write
+ // ClientKeyExchange, etc. (A proxy for waiting for the entirety of the
+ // server's leg to complete, since it may span multiple reads.)
+ EXPECT_FALSE(callback->have_result());
+ raw_transport->BlockWrite();
+ raw_transport->UnblockReadResult();
+ raw_transport->WaitForWrite();
+
+ // And, finally, release that and block the next server leg
+ // (ChangeCipherSpec, Finished).
+ raw_transport->BlockReadResult();
+ raw_transport->UnblockWrite();
+
+ *out_raw_transport = raw_transport;
+ *out_sock = sock.Pass();
+ }
+
+ void TestFalseStart(const SpawnedTestServer::SSLOptions& server_options,
+ const SSLConfig& client_config,
+ bool expect_false_start) {
+ ASSERT_TRUE(StartTestServer(server_options));
+
+ TestCompletionCallback callback;
+ FakeBlockingStreamSocket* raw_transport = NULL;
+ scoped_ptr<SSLClientSocket> sock;
+ ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived(
+ client_config, &callback, &raw_transport, &sock));
+
+ if (expect_false_start) {
+ // When False Starting, the handshake should complete before receiving the
+ // Change Cipher Spec and Finished messages.
+ //
+ // Note: callback.have_result() may not be true without waiting. The NSS
+ // state machine sometimes lives on a separate thread, so this thread may
+ // not yet have processed the signal that the handshake has completed.
+ int rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock->IsConnected());
+
+ const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+ static const int kRequestTextSize =
+ static_cast<int>(arraysize(request_text) - 1);
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestTextSize));
+ memcpy(request_buffer->data(), request_text, kRequestTextSize);
+
+ // Write the request.
+ rv = callback.GetResult(sock->Write(request_buffer.get(),
+ kRequestTextSize,
+ callback.callback()));
+ EXPECT_EQ(kRequestTextSize, rv);
+
+ // The read will hang; it's waiting for the peer to complete the
+ // handshake, and the handshake is still blocked.
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
+ rv = sock->Read(buf.get(), 4096, callback.callback());
+
+ // After releasing reads, the connection proceeds.
+ raw_transport->UnblockReadResult();
+ rv = callback.GetResult(rv);
+ EXPECT_LT(0, rv);
+ } else {
+ // False Start is not enabled, so the handshake will not complete because
+ // the server second leg is blocked.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(callback.have_result());
+ }
+ }
+};
+
+class SSLClientSocketChannelIDTest : public SSLClientSocketTest {
+ protected:
+ void EnableChannelID() {
+ cert_service_.reset(
+ new ServerBoundCertService(new DefaultServerBoundCertStore(NULL),
+ base::MessageLoopProxy::current()));
+ context_.server_bound_cert_service = cert_service_.get();
+ }
+
+ void EnableFailingChannelID() {
+ cert_service_.reset(new ServerBoundCertService(
+ new FailingServerBoundCertStore(), base::MessageLoopProxy::current()));
+ context_.server_bound_cert_service = cert_service_.get();
+ }
+
+ private:
+ scoped_ptr<ServerBoundCertService> cert_service_;
};
//-----------------------------------------------------------------------------
@@ -540,7 +904,8 @@ static bool LogContainsSSLConnectEndEvent(
LogContainsEvent(
log, i, NetLog::TYPE_SOCKET_BYTES_SENT, NetLog::PHASE_NONE);
}
-;
+
+} // namespace
TEST_F(SSLClientSocketTest, Connect) {
SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
@@ -955,7 +1320,7 @@ TEST_F(SSLClientSocketTest, Write_WithSynchronousError) {
// Simulate an unclean/forcible shutdown on the underlying socket.
// However, simulate this error asynchronously.
raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
- raw_transport->SetNextWriteShouldBlock();
+ raw_transport->BlockWrite();
// This write should complete synchronously, because the TLS ciphertext
// can be created and placed into the outgoing buffers independent of the
@@ -987,6 +1352,75 @@ TEST_F(SSLClientSocketTest, Write_WithSynchronousError) {
#endif
}
+// If there is a Write failure at the transport with no follow-up Read, although
+// the write error will not be returned to the client until a future Read or
+// Write operation, SSLClientSocket should not spin attempting to re-write on
+// the socket. This is a regression test for part of https://crbug.com/381160.
+TEST_F(SSLClientSocketTest, Write_WithSynchronousErrorNoRead) {
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ SpawnedTestServer::kLocalhost,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> real_transport(
+ new TCPClientSocket(addr, NULL, NetLog::Source()));
+ // Note: intermediate sockets' ownership are handed to |sock|, but a pointer
+ // is retained in order to query them.
+ scoped_ptr<SynchronousErrorStreamSocket> error_socket(
+ new SynchronousErrorStreamSocket(real_transport.Pass()));
+ SynchronousErrorStreamSocket* raw_error_socket = error_socket.get();
+ scoped_ptr<CountingStreamSocket> counting_socket(
+ new CountingStreamSocket(error_socket.PassAs<StreamSocket>()));
+ CountingStreamSocket* raw_counting_socket = counting_socket.get();
+ int rv = callback.GetResult(counting_socket->Connect(callback.callback()));
+ ASSERT_EQ(OK, rv);
+
+ // Disable TLS False Start to avoid handshake non-determinism.
+ SSLConfig ssl_config;
+ ssl_config.false_start_enabled = false;
+
+ scoped_ptr<SSLClientSocket> sock(
+ CreateSSLClientSocket(counting_socket.PassAs<StreamSocket>(),
+ test_server.host_port_pair(),
+ ssl_config));
+
+ rv = callback.GetResult(sock->Connect(callback.callback()));
+ ASSERT_EQ(OK, rv);
+ ASSERT_TRUE(sock->IsConnected());
+
+ // Simulate an unclean/forcible shutdown on the underlying socket.
+ raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
+
+ const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+ static const int kRequestTextSize =
+ static_cast<int>(arraysize(request_text) - 1);
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestTextSize));
+ memcpy(request_buffer->data(), request_text, kRequestTextSize);
+
+ // This write should complete synchronously, because the TLS ciphertext
+ // can be created and placed into the outgoing buffers independent of the
+ // underlying transport.
+ rv = callback.GetResult(
+ sock->Write(request_buffer.get(), kRequestTextSize, callback.callback()));
+ ASSERT_EQ(kRequestTextSize, rv);
+
+ // Let the event loop spin for a little bit of time. Even on platforms where
+ // pumping the state machine involve thread hops, there should be no further
+ // writes on the transport socket.
+ //
+ // TODO(davidben): Avoid the arbitrary timeout?
+ int old_write_count = raw_counting_socket->write_count();
+ base::RunLoop loop;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, loop.QuitClosure(), base::TimeDelta::FromMilliseconds(100));
+ loop.Run();
+ EXPECT_EQ(old_write_count, raw_counting_socket->write_count());
+}
+
// Test the full duplex mode, with Read and Write pending at the same time.
// This test also serves as a regression test for http://crbug.com/29815.
TEST_F(SSLClientSocketTest, Read_FullDuplex) {
@@ -1100,8 +1534,8 @@ TEST_F(SSLClientSocketTest, Read_DeleteWhilePendingFullDuplex) {
raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
// ... but have those errors returned asynchronously. Because the Write() will
// return first, this will trigger the error.
- raw_transport->SetNextReadShouldBlock();
- raw_transport->SetNextWriteShouldBlock();
+ raw_transport->BlockReadResult();
+ raw_transport->BlockWrite();
// Enqueue a Read() before calling Write(), which should "hang" due to
// the ERR_IO_PENDING caused by SetReadShouldBlock() and thus return.
@@ -1225,7 +1659,7 @@ TEST_F(SSLClientSocketTest, Read_WithWriteError) {
// Start a hanging read.
TestCompletionCallback read_callback;
- raw_transport->SetNextReadShouldBlock();
+ raw_transport->BlockReadResult();
scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
rv = sock->Read(buf.get(), 4096, read_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1269,7 +1703,7 @@ TEST_F(SSLClientSocketTest, Read_WithWriteError) {
#endif
// Release the read. Some bytes should go through.
- raw_transport->UnblockRead();
+ raw_transport->UnblockReadResult();
rv = read_callback.WaitForResult();
// Per the fix for http://crbug.com/249848, write failures currently break
@@ -1709,6 +2143,75 @@ TEST(SSLClientSocket, ClearSessionCache) {
SSLClientSocket::ClearSessionCache();
}
+// Test that the server certificates are properly retrieved from the underlying
+// SSL stack.
+TEST_F(SSLClientSocketTest, VerifyServerChainProperlyOrdered) {
+ // The connection does not have to be successful.
+ cert_verifier_->set_default_result(ERR_CERT_INVALID);
+
+ // Set up a test server with CERT_CHAIN_WRONG_ROOT.
+ // This makes the server present redundant-server-chain.pem, which contains
+ // intermediate certificates.
+ SpawnedTestServer::SSLOptions ssl_options(
+ SpawnedTestServer::SSLOptions::CERT_CHAIN_WRONG_ROOT);
+ SpawnedTestServer test_server(
+ SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr, NULL, NetLog::Source()));
+ int rv = transport->Connect(callback.callback());
+ rv = callback.GetResult(rv);
+ EXPECT_EQ(OK, rv);
+
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server.host_port_pair(), kDefaultSSLConfig));
+ EXPECT_FALSE(sock->IsConnected());
+ rv = sock->Connect(callback.callback());
+ rv = callback.GetResult(rv);
+
+ EXPECT_EQ(ERR_CERT_INVALID, rv);
+ EXPECT_TRUE(sock->IsConnected());
+
+ // When given option CERT_CHAIN_WRONG_ROOT, SpawnedTestServer will present
+ // certs from redundant-server-chain.pem.
+ CertificateList server_certs =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "redundant-server-chain.pem",
+ X509Certificate::FORMAT_AUTO);
+
+ // Get the server certificate as received client side.
+ scoped_refptr<X509Certificate> server_certificate =
+ sock->GetUnverifiedServerCertificateChain();
+
+ // Get the intermediates as received client side.
+ const X509Certificate::OSCertHandles& server_intermediates =
+ server_certificate->GetIntermediateCertificates();
+
+ // Check that the unverified server certificate chain is properly retrieved
+ // from the underlying ssl stack.
+ ASSERT_EQ(4U, server_certs.size());
+
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ server_certificate->os_cert_handle(), server_certs[0]->os_cert_handle()));
+
+ ASSERT_EQ(3U, server_intermediates.size());
+
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(server_intermediates[0],
+ server_certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(server_intermediates[1],
+ server_certs[2]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(server_intermediates[2],
+ server_certs[3]->os_cert_handle()));
+
+ sock->Disconnect();
+ EXPECT_FALSE(sock->IsConnected());
+}
+
// This tests that SSLInfo contains a properly re-constructed certificate
// chain. That, in turn, verifies that GetSSLInfo is giving us the chain as
// verified, not the chain as served by the server. (They may be different.)
@@ -1807,47 +2310,6 @@ TEST_F(SSLClientSocketTest, VerifyReturnChainProperlyOrdered) {
EXPECT_FALSE(sock->IsConnected());
}
-// Verifies the correctness of GetSSLCertRequestInfo.
-class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest {
- protected:
- // Creates a test server with the given SSLOptions, connects to it and returns
- // the SSLCertRequestInfo reported by the socket.
- scoped_refptr<SSLCertRequestInfo> GetCertRequest(
- SpawnedTestServer::SSLOptions ssl_options) {
- SpawnedTestServer test_server(
- SpawnedTestServer::TYPE_HTTPS, ssl_options, base::FilePath());
- if (!test_server.Start())
- return NULL;
-
- AddressList addr;
- if (!test_server.GetAddressList(&addr))
- return NULL;
-
- TestCompletionCallback callback;
- CapturingNetLog log;
- scoped_ptr<StreamSocket> transport(
- new TCPClientSocket(addr, &log, NetLog::Source()));
- int rv = transport->Connect(callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_EQ(OK, rv);
-
- scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
- transport.Pass(), test_server.host_port_pair(), kDefaultSSLConfig));
- EXPECT_FALSE(sock->IsConnected());
-
- rv = sock->Connect(callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- scoped_refptr<SSLCertRequestInfo> request_info = new SSLCertRequestInfo();
- sock->GetSSLCertRequestInfo(request_info.get());
- sock->Disconnect();
- EXPECT_FALSE(sock->IsConnected());
-
- return request_info;
- }
-};
-
TEST_F(SSLClientSocketCertRequestInfoTest, NoAuthorities) {
SpawnedTestServer::SSLOptions ssl_options;
ssl_options.request_client_certificate = true;
@@ -1899,7 +2361,20 @@ TEST_F(SSLClientSocketCertRequestInfoTest, TwoAuthorities) {
request_info->cert_authorities[1]);
}
-} // namespace
+// cert_key_types is currently only populated on OpenSSL.
+#if defined(USE_OPENSSL)
+TEST_F(SSLClientSocketCertRequestInfoTest, CertKeyTypes) {
+ SpawnedTestServer::SSLOptions ssl_options;
+ ssl_options.request_client_certificate = true;
+ ssl_options.client_cert_types.push_back(CLIENT_CERT_RSA_SIGN);
+ ssl_options.client_cert_types.push_back(CLIENT_CERT_ECDSA_SIGN);
+ scoped_refptr<SSLCertRequestInfo> request_info = GetCertRequest(ssl_options);
+ ASSERT_TRUE(request_info.get());
+ ASSERT_EQ(2u, request_info->cert_key_types.size());
+ EXPECT_EQ(CLIENT_CERT_RSA_SIGN, request_info->cert_key_types[0]);
+ EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, request_info->cert_key_types[1]);
+}
+#endif // defined(USE_OPENSSL)
TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabledTLSExtension) {
SpawnedTestServer::SSLOptions ssl_options;
@@ -2059,4 +2534,210 @@ TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) {
EXPECT_FALSE(sock->IsConnected());
}
+// Tests that IsConnectedAndIdle and WasEverUsed behave as expected.
+TEST_F(SSLClientSocketTest, ReuseStates) {
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ SpawnedTestServer::kLocalhost,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> transport(
+ new TCPClientSocket(addr, NULL, NetLog::Source()));
+ int rv = transport->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket(
+ transport.Pass(), test_server.host_port_pair(), kDefaultSSLConfig));
+
+ rv = sock->Connect(callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ // The socket was just connected. It should be idle because it is speaking
+ // HTTP. Although the transport has been used for the handshake, WasEverUsed()
+ // returns false.
+ EXPECT_TRUE(sock->IsConnected());
+ EXPECT_TRUE(sock->IsConnectedAndIdle());
+ EXPECT_FALSE(sock->WasEverUsed());
+
+ const char kRequestText[] = "GET / HTTP/1.0\r\n\r\n";
+ const size_t kRequestLen = arraysize(kRequestText) - 1;
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestLen));
+ memcpy(request_buffer->data(), kRequestText, kRequestLen);
+
+ rv = sock->Write(request_buffer.get(), kRequestLen, callback.callback());
+ EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING);
+
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(static_cast<int>(kRequestLen), rv);
+
+ // The socket has now been used.
+ EXPECT_TRUE(sock->WasEverUsed());
+
+ // TODO(davidben): Read one byte to ensure the test server has responded and
+ // then assert IsConnectedAndIdle is false. This currently doesn't work
+ // because neither SSLClientSocketNSS nor SSLClientSocketOpenSSL check their
+ // SSL implementation's internal buffers. Either call PR_Available and
+ // SSL_pending, although the former isn't actually implemented or perhaps
+ // attempt to read one byte extra.
+}
+
+TEST_F(SSLClientSocketFalseStartTest, FalseStartEnabled) {
+ // False Start requires NPN and a forward-secret cipher suite.
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ server_options.enable_npn = true;
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+ ASSERT_NO_FATAL_FAILURE(
+ TestFalseStart(server_options, client_config, true));
+}
+
+// Test that False Start is disabled without NPN.
+TEST_F(SSLClientSocketFalseStartTest, NoNPN) {
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ SSLConfig client_config;
+ client_config.next_protos.clear();
+ ASSERT_NO_FATAL_FAILURE(
+ TestFalseStart(server_options, client_config, false));
+}
+
+// Test that False Start is disabled without a forward-secret cipher suite.
+TEST_F(SSLClientSocketFalseStartTest, NoForwardSecrecy) {
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_RSA;
+ server_options.enable_npn = true;
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+ ASSERT_NO_FATAL_FAILURE(
+ TestFalseStart(server_options, client_config, false));
+}
+
+// Test that sessions are resumable after receiving the server Finished message.
+TEST_F(SSLClientSocketFalseStartTest, SessionResumption) {
+ // Start a server.
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ server_options.enable_npn = true;
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+
+ // Let a full handshake complete with False Start.
+ ASSERT_NO_FATAL_FAILURE(
+ TestFalseStart(server_options, client_config, true));
+
+ // Make a second connection.
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> transport2(
+ new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ EXPECT_EQ(OK, callback.GetResult(transport2->Connect(callback.callback())));
+ scoped_ptr<SSLClientSocket> sock2 = CreateSSLClientSocket(
+ transport2.Pass(), test_server()->host_port_pair(), client_config);
+ ASSERT_TRUE(sock2.get());
+ EXPECT_EQ(OK, callback.GetResult(sock2->Connect(callback.callback())));
+
+ // It should resume the session.
+ SSLInfo ssl_info;
+ EXPECT_TRUE(sock2->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+}
+
+// Test that sessions are not resumable before receiving the server Finished
+// message.
+TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) {
+ // Start a server.
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ server_options.enable_npn = true;
+ ASSERT_TRUE(StartTestServer(server_options));
+
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+
+ // Start a handshake up to the server Finished message.
+ TestCompletionCallback callback;
+ FakeBlockingStreamSocket* raw_transport1;
+ scoped_ptr<SSLClientSocket> sock1;
+ ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived(
+ client_config, &callback, &raw_transport1, &sock1));
+ // Although raw_transport1 has the server Finished blocked, the handshake
+ // still completes.
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ // Drop the old socket. This is needed because the Python test server can't
+ // service two sockets in parallel.
+ sock1.reset();
+
+ // Start a second connection.
+ scoped_ptr<StreamSocket> transport2(
+ new TCPClientSocket(addr(), &log_, NetLog::Source()));
+ EXPECT_EQ(OK, callback.GetResult(transport2->Connect(callback.callback())));
+ scoped_ptr<SSLClientSocket> sock2 = CreateSSLClientSocket(
+ transport2.Pass(), test_server()->host_port_pair(), client_config);
+ EXPECT_EQ(OK, callback.GetResult(sock2->Connect(callback.callback())));
+
+ // No session resumption because the first connection never received a server
+ // Finished message.
+ SSLInfo ssl_info;
+ EXPECT_TRUE(sock2->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+}
+
+// Connect to a server using channel id. It should allow the connection.
+TEST_F(SSLClientSocketChannelIDTest, SendChannelID) {
+ SpawnedTestServer::SSLOptions ssl_options;
+
+ ASSERT_TRUE(ConnectToTestServer(ssl_options));
+
+ EnableChannelID();
+ SSLConfig ssl_config = kDefaultSSLConfig;
+ ssl_config.channel_id_enabled = true;
+
+ int rv;
+ ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock_->IsConnected());
+ EXPECT_TRUE(sock_->WasChannelIDSent());
+
+ sock_->Disconnect();
+ EXPECT_FALSE(sock_->IsConnected());
+}
+
+// Connect to a server using channel id but without sending a key. It should
+// fail.
+TEST_F(SSLClientSocketChannelIDTest, FailingChannelID) {
+ SpawnedTestServer::SSLOptions ssl_options;
+
+ ASSERT_TRUE(ConnectToTestServer(ssl_options));
+
+ EnableFailingChannelID();
+ SSLConfig ssl_config = kDefaultSSLConfig;
+ ssl_config.channel_id_enabled = true;
+
+ int rv;
+ ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+
+ // TODO(haavardm@opera.com): Due to differences in threading, Linux returns
+ // ERR_UNEXPECTED while Mac and Windows return ERR_PROTOCOL_ERROR. Accept all
+ // error codes for now.
+ // http://crbug.com/373670
+ EXPECT_NE(OK, rv);
+ EXPECT_FALSE(sock_->IsConnected());
+}
+
} // namespace net
diff --git a/chromium/net/socket/ssl_server_socket_nss.cc b/chromium/net/socket/ssl_server_socket_nss.cc
index b95983a5159..cfdc5c545ef 100644
--- a/chromium/net/socket/ssl_server_socket_nss.cc
+++ b/chromium/net/socket/ssl_server_socket_nss.cc
@@ -29,6 +29,7 @@
#include <limits>
+#include "base/callback_helpers.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "crypto/rsa_private_key.h"
@@ -238,15 +239,17 @@ int SSLServerSocketNSS::Write(IOBuffer* buf, int buf_len,
return rv;
}
-bool SSLServerSocketNSS::SetReceiveBufferSize(int32 size) {
+int SSLServerSocketNSS::SetReceiveBufferSize(int32 size) {
return transport_socket_->SetReceiveBufferSize(size);
}
-bool SSLServerSocketNSS::SetSendBufferSize(int32 size) {
+int SSLServerSocketNSS::SetSendBufferSize(int32 size) {
return transport_socket_->SetSendBufferSize(size);
}
bool SSLServerSocketNSS::IsConnected() const {
+ // TODO(wtc): Find out if we should check transport_socket_->IsConnected()
+ // as well.
return completed_handshake_;
}
@@ -291,6 +294,7 @@ bool SSLServerSocketNSS::UsingTCPFastOpen() const {
}
bool SSLServerSocketNSS::WasNpnNegotiated() const {
+ NOTIMPLEMENTED();
return false;
}
@@ -497,6 +501,8 @@ void SSLServerSocketNSS::OnSendComplete(int result) {
return;
}
+ // TODO(byungchul): This state machine is not correct. Copy the state machine
+ // of SSLClientSocketNSS::OnSendComplete() which handles it better.
if (!completed_handshake_)
return;
@@ -529,11 +535,12 @@ void SSLServerSocketNSS::OnRecvComplete(int result) {
void SSLServerSocketNSS::OnHandshakeIOComplete(int result) {
int rv = DoHandshakeLoop(result);
- if (rv != ERR_IO_PENDING) {
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv);
- if (!user_handshake_callback_.is_null())
- DoHandshakeCallback(rv);
- }
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv);
+ if (!user_handshake_callback_.is_null())
+ DoHandshakeCallback(rv);
}
// Return 0 for EOF,
@@ -546,7 +553,10 @@ int SSLServerSocketNSS::BufferSend(void) {
const char* buf1;
const char* buf2;
unsigned int len1, len2;
- memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
+ if (memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2)) {
+ // The error code itself is ignored, so just return ERR_ABORTED.
+ return ERR_ABORTED;
+ }
const unsigned int len = len1 + len2;
int rv = 0;
@@ -725,7 +735,7 @@ int SSLServerSocketNSS::DoReadLoop(int result) {
int SSLServerSocketNSS::DoWriteLoop(int result) {
DCHECK(completed_handshake_);
- DCHECK(next_handshake_state_ == STATE_NONE);
+ DCHECK_EQ(next_handshake_state_, STATE_NONE);
if (result < 0)
return result;
@@ -772,36 +782,25 @@ int SSLServerSocketNSS::DoHandshake() {
void SSLServerSocketNSS::DoHandshakeCallback(int rv) {
DCHECK_NE(rv, ERR_IO_PENDING);
-
- CompletionCallback c = user_handshake_callback_;
- user_handshake_callback_.Reset();
- c.Run(rv > OK ? OK : rv);
+ ResetAndReturn(&user_handshake_callback_).Run(rv > OK ? OK : rv);
}
void SSLServerSocketNSS::DoReadCallback(int rv) {
DCHECK(rv != ERR_IO_PENDING);
DCHECK(!user_read_callback_.is_null());
- // Since Run may result in Read being called, clear |user_read_callback_|
- // up front.
- CompletionCallback c = user_read_callback_;
- user_read_callback_.Reset();
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
- c.Run(rv);
+ ResetAndReturn(&user_read_callback_).Run(rv);
}
void SSLServerSocketNSS::DoWriteCallback(int rv) {
DCHECK(rv != ERR_IO_PENDING);
DCHECK(!user_write_callback_.is_null());
- // Since Run may result in Write being called, clear |user_write_callback_|
- // up front.
- CompletionCallback c = user_write_callback_;
- user_write_callback_.Reset();
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
- c.Run(rv);
+ ResetAndReturn(&user_write_callback_).Run(rv);
}
// static
diff --git a/chromium/net/socket/ssl_server_socket_nss.h b/chromium/net/socket/ssl_server_socket_nss.h
index 8bbb0e338ac..bc5b65d5368 100644
--- a/chromium/net/socket/ssl_server_socket_nss.h
+++ b/chromium/net/socket/ssl_server_socket_nss.h
@@ -46,8 +46,8 @@ class SSLServerSocketNSS : public SSLServerSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
// StreamSocket implementation.
virtual int Connect(const CompletionCallback& callback) OVERRIDE;
@@ -99,7 +99,7 @@ class SSLServerSocketNSS : public SSLServerSocket {
PRBool is_server);
static void HandshakeCallback(PRFileDesc* socket, void* arg);
- virtual int Init();
+ int Init();
// Members used to send and receive buffer.
bool transport_send_busy_;
diff --git a/chromium/net/socket/ssl_server_socket_openssl.cc b/chromium/net/socket/ssl_server_socket_openssl.cc
index c327f2caf10..f6bd0cd8f81 100644
--- a/chromium/net/socket/ssl_server_socket_openssl.cc
+++ b/chromium/net/socket/ssl_server_socket_openssl.cc
@@ -2,18 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/socket/ssl_server_socket_openssl.h"
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "base/callback_helpers.h"
#include "base/logging.h"
-#include "net/socket/ssl_server_socket.h"
+#include "crypto/openssl_util.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/net_errors.h"
+#include "net/socket/openssl_ssl_util.h"
+#include "net/socket/ssl_error_params.h"
-// TODO(bulach): Provide simple stubs for EnableSSLServerSockets and
-// CreateSSLServerSocket so that when building for OpenSSL rather than NSS,
-// so that the code using SSL server sockets can be compiled and disabled
-// programatically rather than requiring to be carved out from the compile.
+#define GotoState(s) next_handshake_state_ = s
namespace net {
void EnableSSLServerSockets() {
- NOTIMPLEMENTED();
+ // No-op because CreateSSLServerSocket() calls crypto::EnsureOpenSSLInit().
}
scoped_ptr<SSLServerSocket> CreateSSLServerSocket(
@@ -21,8 +28,658 @@ scoped_ptr<SSLServerSocket> CreateSSLServerSocket(
X509Certificate* certificate,
crypto::RSAPrivateKey* key,
const SSLConfig& ssl_config) {
+ crypto::EnsureOpenSSLInit();
+ return scoped_ptr<SSLServerSocket>(
+ new SSLServerSocketOpenSSL(socket.Pass(), certificate, key, ssl_config));
+}
+
+SSLServerSocketOpenSSL::SSLServerSocketOpenSSL(
+ scoped_ptr<StreamSocket> transport_socket,
+ scoped_refptr<X509Certificate> certificate,
+ crypto::RSAPrivateKey* key,
+ const SSLConfig& ssl_config)
+ : transport_send_busy_(false),
+ transport_recv_busy_(false),
+ transport_recv_eof_(false),
+ user_read_buf_len_(0),
+ user_write_buf_len_(0),
+ transport_write_error_(OK),
+ ssl_(NULL),
+ transport_bio_(NULL),
+ transport_socket_(transport_socket.Pass()),
+ ssl_config_(ssl_config),
+ cert_(certificate),
+ next_handshake_state_(STATE_NONE),
+ completed_handshake_(false) {
+ // TODO(byungchul): Need a better way to clone a key.
+ std::vector<uint8> key_bytes;
+ CHECK(key->ExportPrivateKey(&key_bytes));
+ key_.reset(crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes));
+ CHECK(key_.get());
+}
+
+SSLServerSocketOpenSSL::~SSLServerSocketOpenSSL() {
+ if (ssl_) {
+ // Calling SSL_shutdown prevents the session from being marked as
+ // unresumable.
+ SSL_shutdown(ssl_);
+ SSL_free(ssl_);
+ ssl_ = NULL;
+ }
+ if (transport_bio_) {
+ BIO_free_all(transport_bio_);
+ transport_bio_ = NULL;
+ }
+}
+
+int SSLServerSocketOpenSSL::Handshake(const CompletionCallback& callback) {
+ net_log_.BeginEvent(NetLog::TYPE_SSL_SERVER_HANDSHAKE);
+
+ // Set up new ssl object.
+ int rv = Init();
+ if (rv != OK) {
+ LOG(ERROR) << "Failed to initialize OpenSSL: rv=" << rv;
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv);
+ return rv;
+ }
+
+ // Set SSL to server mode. Handshake happens in the loop below.
+ SSL_set_accept_state(ssl_);
+
+ GotoState(STATE_HANDSHAKE);
+ rv = DoHandshakeLoop(OK);
+ if (rv == ERR_IO_PENDING) {
+ user_handshake_callback_ = callback;
+ } else {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv);
+ }
+
+ return rv > OK ? OK : rv;
+}
+
+int SSLServerSocketOpenSSL::ExportKeyingMaterial(
+ const base::StringPiece& label,
+ bool has_context,
+ const base::StringPiece& context,
+ unsigned char* out,
+ unsigned int outlen) {
+ if (!IsConnected())
+ return ERR_SOCKET_NOT_CONNECTED;
+
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ int rv = SSL_export_keying_material(
+ ssl_, out, outlen, label.data(), label.size(),
+ reinterpret_cast<const unsigned char*>(context.data()),
+ context.length(), context.length() > 0);
+
+ if (rv != 1) {
+ int ssl_error = SSL_get_error(ssl_, rv);
+ LOG(ERROR) << "Failed to export keying material;"
+ << " returned " << rv
+ << ", SSL error code " << ssl_error;
+ return MapOpenSSLError(ssl_error, err_tracer);
+ }
+ return OK;
+}
+
+int SSLServerSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) {
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
+int SSLServerSocketOpenSSL::Read(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(user_read_callback_.is_null());
+ DCHECK(user_handshake_callback_.is_null());
+ DCHECK(!user_read_buf_.get());
+ DCHECK(!callback.is_null());
+
+ user_read_buf_ = buf;
+ user_read_buf_len_ = buf_len;
+
+ DCHECK(completed_handshake_);
+
+ int rv = DoReadLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_read_callback_ = callback;
+ } else {
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ }
+
+ return rv;
+}
+
+int SSLServerSocketOpenSSL::Write(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(user_write_callback_.is_null());
+ DCHECK(!user_write_buf_.get());
+ DCHECK(!callback.is_null());
+
+ user_write_buf_ = buf;
+ user_write_buf_len_ = buf_len;
+
+ int rv = DoWriteLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_write_callback_ = callback;
+ } else {
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ }
+ return rv;
+}
+
+int SSLServerSocketOpenSSL::SetReceiveBufferSize(int32 size) {
+ return transport_socket_->SetReceiveBufferSize(size);
+}
+
+int SSLServerSocketOpenSSL::SetSendBufferSize(int32 size) {
+ return transport_socket_->SetSendBufferSize(size);
+}
+
+int SSLServerSocketOpenSSL::Connect(const CompletionCallback& callback) {
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
+void SSLServerSocketOpenSSL::Disconnect() {
+ transport_socket_->Disconnect();
+}
+
+bool SSLServerSocketOpenSSL::IsConnected() const {
+ // TODO(wtc): Find out if we should check transport_socket_->IsConnected()
+ // as well.
+ return completed_handshake_;
+}
+
+bool SSLServerSocketOpenSSL::IsConnectedAndIdle() const {
+ return completed_handshake_ && transport_socket_->IsConnectedAndIdle();
+}
+
+int SSLServerSocketOpenSSL::GetPeerAddress(IPEndPoint* address) const {
+ if (!IsConnected())
+ return ERR_SOCKET_NOT_CONNECTED;
+ return transport_socket_->GetPeerAddress(address);
+}
+
+int SSLServerSocketOpenSSL::GetLocalAddress(IPEndPoint* address) const {
+ if (!IsConnected())
+ return ERR_SOCKET_NOT_CONNECTED;
+ return transport_socket_->GetLocalAddress(address);
+}
+
+const BoundNetLog& SSLServerSocketOpenSSL::NetLog() const {
+ return net_log_;
+}
+
+void SSLServerSocketOpenSSL::SetSubresourceSpeculation() {
+ transport_socket_->SetSubresourceSpeculation();
+}
+
+void SSLServerSocketOpenSSL::SetOmniboxSpeculation() {
+ transport_socket_->SetOmniboxSpeculation();
+}
+
+bool SSLServerSocketOpenSSL::WasEverUsed() const {
+ return transport_socket_->WasEverUsed();
+}
+
+bool SSLServerSocketOpenSSL::UsingTCPFastOpen() const {
+ return transport_socket_->UsingTCPFastOpen();
+}
+
+bool SSLServerSocketOpenSSL::WasNpnNegotiated() const {
NOTIMPLEMENTED();
- return scoped_ptr<SSLServerSocket>();
+ return false;
+}
+
+NextProto SSLServerSocketOpenSSL::GetNegotiatedProtocol() const {
+ // NPN is not supported by this class.
+ return kProtoUnknown;
+}
+
+bool SSLServerSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void SSLServerSocketOpenSSL::OnSendComplete(int result) {
+ if (next_handshake_state_ == STATE_HANDSHAKE) {
+ // In handshake phase.
+ OnHandshakeIOComplete(result);
+ return;
+ }
+
+ // TODO(byungchul): This state machine is not correct. Copy the state machine
+ // of SSLClientSocketOpenSSL::OnSendComplete() which handles it better.
+ if (!completed_handshake_)
+ return;
+
+ if (user_write_buf_.get()) {
+ int rv = DoWriteLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoWriteCallback(rv);
+ } else {
+ // Ensure that any queued ciphertext is flushed.
+ DoTransportIO();
+ }
+}
+
+void SSLServerSocketOpenSSL::OnRecvComplete(int result) {
+ if (next_handshake_state_ == STATE_HANDSHAKE) {
+ // In handshake phase.
+ OnHandshakeIOComplete(result);
+ return;
+ }
+
+ // Network layer received some data, check if client requested to read
+ // decrypted data.
+ if (!user_read_buf_.get() || !completed_handshake_)
+ return;
+
+ int rv = DoReadLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoReadCallback(rv);
+}
+
+void SSLServerSocketOpenSSL::OnHandshakeIOComplete(int result) {
+ int rv = DoHandshakeLoop(result);
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv);
+ if (!user_handshake_callback_.is_null())
+ DoHandshakeCallback(rv);
+}
+
+// Return 0 for EOF,
+// > 0 for bytes transferred immediately,
+// < 0 for error (or the non-error ERR_IO_PENDING).
+int SSLServerSocketOpenSSL::BufferSend() {
+ if (transport_send_busy_)
+ return ERR_IO_PENDING;
+
+ if (!send_buffer_.get()) {
+ // Get a fresh send buffer out of the send BIO.
+ size_t max_read = BIO_ctrl_pending(transport_bio_);
+ if (!max_read)
+ return 0; // Nothing pending in the OpenSSL write BIO.
+ send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read);
+ int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read);
+ DCHECK_GT(read_bytes, 0);
+ CHECK_EQ(static_cast<int>(max_read), read_bytes);
+ }
+
+ int rv = transport_socket_->Write(
+ send_buffer_.get(),
+ send_buffer_->BytesRemaining(),
+ base::Bind(&SSLServerSocketOpenSSL::BufferSendComplete,
+ base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ transport_send_busy_ = true;
+ } else {
+ TransportWriteComplete(rv);
+ }
+ return rv;
+}
+
+void SSLServerSocketOpenSSL::BufferSendComplete(int result) {
+ transport_send_busy_ = false;
+ TransportWriteComplete(result);
+ OnSendComplete(result);
+}
+
+void SSLServerSocketOpenSSL::TransportWriteComplete(int result) {
+ DCHECK(ERR_IO_PENDING != result);
+ if (result < 0) {
+ // Got a socket write error; close the BIO to indicate this upward.
+ //
+ // TODO(davidben): The value of |result| gets lost. Feed the error back into
+ // the BIO so it gets (re-)detected in OnSendComplete. Perhaps with
+ // BIO_set_callback.
+ DVLOG(1) << "TransportWriteComplete error " << result;
+ (void)BIO_shutdown_wr(SSL_get_wbio(ssl_));
+
+ // Match the fix for http://crbug.com/249848 in NSS by erroring future reads
+ // from the socket after a write error.
+ //
+ // TODO(davidben): Avoid having read and write ends interact this way.
+ transport_write_error_ = result;
+ (void)BIO_shutdown_wr(transport_bio_);
+ send_buffer_ = NULL;
+ } else {
+ DCHECK(send_buffer_.get());
+ send_buffer_->DidConsume(result);
+ DCHECK_GE(send_buffer_->BytesRemaining(), 0);
+ if (send_buffer_->BytesRemaining() <= 0)
+ send_buffer_ = NULL;
+ }
+}
+
+int SSLServerSocketOpenSSL::BufferRecv() {
+ if (transport_recv_busy_)
+ return ERR_IO_PENDING;
+
+ // Determine how much was requested from |transport_bio_| that was not
+ // actually available.
+ size_t requested = BIO_ctrl_get_read_request(transport_bio_);
+ if (requested == 0) {
+ // This is not a perfect match of error codes, as no operation is
+ // actually pending. However, returning 0 would be interpreted as
+ // a possible sign of EOF, which is also an inappropriate match.
+ return ERR_IO_PENDING;
+ }
+
+ // Known Issue: While only reading |requested| data is the more correct
+ // implementation, it has the downside of resulting in frequent reads:
+ // One read for the SSL record header (~5 bytes) and one read for the SSL
+ // record body. Rather than issuing these reads to the underlying socket
+ // (and constantly allocating new IOBuffers), a single Read() request to
+ // fill |transport_bio_| is issued. As long as an SSL client socket cannot
+ // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL
+ // traffic, this over-subscribed Read()ing will not cause issues.
+ size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_);
+ if (!max_write)
+ return ERR_IO_PENDING;
+
+ recv_buffer_ = new IOBuffer(max_write);
+ int rv = transport_socket_->Read(
+ recv_buffer_.get(),
+ max_write,
+ base::Bind(&SSLServerSocketOpenSSL::BufferRecvComplete,
+ base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ transport_recv_busy_ = true;
+ } else {
+ rv = TransportReadComplete(rv);
+ }
+ return rv;
+}
+
+void SSLServerSocketOpenSSL::BufferRecvComplete(int result) {
+ result = TransportReadComplete(result);
+ OnRecvComplete(result);
+}
+
+int SSLServerSocketOpenSSL::TransportReadComplete(int result) {
+ DCHECK(ERR_IO_PENDING != result);
+ if (result <= 0) {
+ DVLOG(1) << "TransportReadComplete result " << result;
+ // Received 0 (end of file) or an error. Either way, bubble it up to the
+ // SSL layer via the BIO. TODO(joth): consider stashing the error code, to
+ // relay up to the SSL socket client (i.e. via DoReadCallback).
+ if (result == 0)
+ transport_recv_eof_ = true;
+ (void)BIO_shutdown_wr(transport_bio_);
+ } else if (transport_write_error_ < 0) {
+ // Mirror transport write errors as read failures; transport_bio_ has been
+ // shut down by TransportWriteComplete, so the BIO_write will fail, failing
+ // the CHECK. http://crbug.com/335557.
+ result = transport_write_error_;
+ } else {
+ DCHECK(recv_buffer_.get());
+ int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
+ // A write into a memory BIO should always succeed.
+ DCHECK_EQ(result, ret);
+ }
+ recv_buffer_ = NULL;
+ transport_recv_busy_ = false;
+ return result;
+}
+
+// Do as much network I/O as possible between the buffer and the
+// transport socket. Return true if some I/O performed, false
+// otherwise (error or ERR_IO_PENDING).
+bool SSLServerSocketOpenSSL::DoTransportIO() {
+ bool network_moved = false;
+ int rv;
+ // Read and write as much data as possible. The loop is necessary because
+ // Write() may return synchronously.
+ do {
+ rv = BufferSend();
+ if (rv != ERR_IO_PENDING && rv != 0)
+ network_moved = true;
+ } while (rv > 0);
+ if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING)
+ network_moved = true;
+ return network_moved;
+}
+
+int SSLServerSocketOpenSSL::DoPayloadRead() {
+ DCHECK(user_read_buf_.get());
+ DCHECK_GT(user_read_buf_len_, 0);
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ int rv = SSL_read(ssl_, user_read_buf_->data(), user_read_buf_len_);
+ if (rv >= 0)
+ return rv;
+ int ssl_error = SSL_get_error(ssl_, rv);
+ int net_error = MapOpenSSLError(ssl_error, err_tracer);
+ if (net_error != ERR_IO_PENDING) {
+ net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR,
+ CreateNetLogSSLErrorCallback(net_error, ssl_error));
+ }
+ return net_error;
+}
+
+int SSLServerSocketOpenSSL::DoPayloadWrite() {
+ DCHECK(user_write_buf_.get());
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ int rv = SSL_write(ssl_, user_write_buf_->data(), user_write_buf_len_);
+ if (rv >= 0)
+ return rv;
+ int ssl_error = SSL_get_error(ssl_, rv);
+ int net_error = MapOpenSSLError(ssl_error, err_tracer);
+ if (net_error != ERR_IO_PENDING) {
+ net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR,
+ CreateNetLogSSLErrorCallback(net_error, ssl_error));
+ }
+ return net_error;
+}
+
+int SSLServerSocketOpenSSL::DoHandshakeLoop(int last_io_result) {
+ int rv = last_io_result;
+ do {
+ // Default to STATE_NONE for next state.
+ // (This is a quirk carried over from the windows
+ // implementation. It makes reading the logs a bit harder.)
+ // State handlers can and often do call GotoState just
+ // to stay in the current state.
+ State state = next_handshake_state_;
+ GotoState(STATE_NONE);
+ switch (state) {
+ case STATE_HANDSHAKE:
+ rv = DoHandshake();
+ break;
+ case STATE_NONE:
+ default:
+ rv = ERR_UNEXPECTED;
+ LOG(DFATAL) << "unexpected state " << state;
+ break;
+ }
+
+ // Do the actual network I/O
+ bool network_moved = DoTransportIO();
+ if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) {
+ // In general we exit the loop if rv is ERR_IO_PENDING. In this
+ // special case we keep looping even if rv is ERR_IO_PENDING because
+ // the transport IO may allow DoHandshake to make progress.
+ rv = OK; // This causes us to stay in the loop.
+ }
+ } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE);
+ return rv;
+}
+
+int SSLServerSocketOpenSSL::DoReadLoop(int result) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_handshake_state_ == STATE_NONE);
+
+ if (result < 0)
+ return result;
+
+ bool network_moved;
+ int rv;
+ do {
+ rv = DoPayloadRead();
+ network_moved = DoTransportIO();
+ } while (rv == ERR_IO_PENDING && network_moved);
+ return rv;
+}
+
+int SSLServerSocketOpenSSL::DoWriteLoop(int result) {
+ DCHECK(completed_handshake_);
+ DCHECK_EQ(next_handshake_state_, STATE_NONE);
+
+ if (result < 0)
+ return result;
+
+ bool network_moved;
+ int rv;
+ do {
+ rv = DoPayloadWrite();
+ network_moved = DoTransportIO();
+ } while (rv == ERR_IO_PENDING && network_moved);
+ return rv;
+}
+
+int SSLServerSocketOpenSSL::DoHandshake() {
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ int net_error = OK;
+ int rv = SSL_do_handshake(ssl_);
+
+ if (rv == 1) {
+ completed_handshake_ = true;
+ } else {
+ int ssl_error = SSL_get_error(ssl_, rv);
+ net_error = MapOpenSSLError(ssl_error, err_tracer);
+
+ // If not done, stay in this state
+ if (net_error == ERR_IO_PENDING) {
+ GotoState(STATE_HANDSHAKE);
+ } else {
+ LOG(ERROR) << "handshake failed; returned " << rv
+ << ", SSL error code " << ssl_error
+ << ", net_error " << net_error;
+ net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR,
+ CreateNetLogSSLErrorCallback(net_error, ssl_error));
+ }
+ }
+ return net_error;
+}
+
+void SSLServerSocketOpenSSL::DoHandshakeCallback(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ ResetAndReturn(&user_handshake_callback_).Run(rv > OK ? OK : rv);
+}
+
+void SSLServerSocketOpenSSL::DoReadCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!user_read_callback_.is_null());
+
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ ResetAndReturn(&user_read_callback_).Run(rv);
+}
+
+void SSLServerSocketOpenSSL::DoWriteCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!user_write_callback_.is_null());
+
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ ResetAndReturn(&user_write_callback_).Run(rv);
+}
+
+int SSLServerSocketOpenSSL::Init() {
+ DCHECK(!ssl_);
+ DCHECK(!transport_bio_);
+
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx(
+ // It support SSLv2, SSLv3, and TLSv1.
+ SSL_CTX_new(SSLv23_server_method()));
+ ssl_ = SSL_new(ssl_ctx.get());
+ if (!ssl_)
+ return ERR_UNEXPECTED;
+
+ BIO* ssl_bio = NULL;
+ // 0 => use default buffer sizes.
+ if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0))
+ return ERR_UNEXPECTED;
+ DCHECK(ssl_bio);
+ DCHECK(transport_bio_);
+
+ SSL_set_bio(ssl_, ssl_bio, ssl_bio);
+
+ // Set certificate and private key.
+ DCHECK(cert_->os_cert_handle());
+#if defined(USE_OPENSSL_CERTS)
+ if (SSL_use_certificate(ssl_, cert_->os_cert_handle()) != 1) {
+ LOG(ERROR) << "Cannot set certificate.";
+ return ERR_UNEXPECTED;
+ }
+#else
+ // Convert OSCertHandle to X509 structure.
+ std::string der_string;
+ if (!X509Certificate::GetDEREncoded(cert_->os_cert_handle(), &der_string))
+ return ERR_UNEXPECTED;
+
+ const unsigned char* der_string_array =
+ reinterpret_cast<const unsigned char*>(der_string.data());
+
+ crypto::ScopedOpenSSL<X509, X509_free>
+ x509(d2i_X509(NULL, &der_string_array, der_string.length()));
+ if (!x509.get())
+ return ERR_UNEXPECTED;
+
+ // On success, SSL_use_certificate acquires a reference to |x509|.
+ if (SSL_use_certificate(ssl_, x509.get()) != 1) {
+ LOG(ERROR) << "Cannot set certificate.";
+ return ERR_UNEXPECTED;
+ }
+#endif // USE_OPENSSL_CERTS
+
+ DCHECK(key_->key());
+ if (SSL_use_PrivateKey(ssl_, key_->key()) != 1) {
+ LOG(ERROR) << "Cannot set private key.";
+ return ERR_UNEXPECTED;
+ }
+
+ // OpenSSL defaults some options to on, others to off. To avoid ambiguity,
+ // set everything we care about to an absolute value.
+ SslSetClearMask options;
+ options.ConfigureFlag(SSL_OP_NO_SSLv2, true);
+ bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3);
+ options.ConfigureFlag(SSL_OP_NO_SSLv3, !ssl3_enabled);
+ bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 &&
+ ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1);
+ options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled);
+ bool tls1_1_enabled =
+ (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 &&
+ ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1);
+ options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled);
+ bool tls1_2_enabled =
+ (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 &&
+ ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2);
+ options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled);
+
+ options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true);
+
+ SSL_set_options(ssl_, options.set_mask);
+ SSL_clear_options(ssl_, options.clear_mask);
+
+ // Same as above, this time for the SSL mode.
+ SslSetClearMask mode;
+
+ mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true);
+
+ SSL_set_mode(ssl_, mode.set_mask);
+ SSL_clear_mode(ssl_, mode.clear_mask);
+
+ return OK;
}
} // namespace net
diff --git a/chromium/net/socket/ssl_server_socket_openssl.h b/chromium/net/socket/ssl_server_socket_openssl.h
new file mode 100644
index 00000000000..e1c8aad7aea
--- /dev/null
+++ b/chromium/net/socket/ssl_server_socket_openssl.h
@@ -0,0 +1,150 @@
+// Copyright 2014 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_SSL_SERVER_SOCKET_OPENSSL_H_
+#define NET_SOCKET_SSL_SERVER_SOCKET_OPENSSL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_log.h"
+#include "net/socket/ssl_server_socket.h"
+#include "net/ssl/ssl_config_service.h"
+
+// Avoid including misc OpenSSL headers, i.e.:
+// <openssl/bio.h>
+typedef struct bio_st BIO;
+// <openssl/ssl.h>
+typedef struct ssl_st SSL;
+
+namespace net {
+
+class SSLInfo;
+
+class SSLServerSocketOpenSSL : public SSLServerSocket {
+ public:
+ // See comments on CreateSSLServerSocket for details of how these
+ // parameters are used.
+ SSLServerSocketOpenSSL(scoped_ptr<StreamSocket> socket,
+ scoped_refptr<X509Certificate> certificate,
+ crypto::RSAPrivateKey* key,
+ const SSLConfig& ssl_config);
+ virtual ~SSLServerSocketOpenSSL();
+
+ // SSLServerSocket interface.
+ virtual int Handshake(const CompletionCallback& callback) OVERRIDE;
+
+ // SSLSocket interface.
+ virtual int ExportKeyingMaterial(const base::StringPiece& label,
+ bool has_context,
+ const base::StringPiece& context,
+ unsigned char* out,
+ unsigned int outlen) OVERRIDE;
+ virtual int GetTLSUniqueChannelBinding(std::string* out) OVERRIDE;
+
+ // Socket interface (via StreamSocket).
+ 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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
+
+ // 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;
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_HANDSHAKE,
+ };
+
+ void OnSendComplete(int result);
+ void OnRecvComplete(int result);
+ void OnHandshakeIOComplete(int result);
+
+ int BufferSend();
+ void BufferSendComplete(int result);
+ void TransportWriteComplete(int result);
+ int BufferRecv();
+ void BufferRecvComplete(int result);
+ int TransportReadComplete(int result);
+ bool DoTransportIO();
+ int DoPayloadRead();
+ int DoPayloadWrite();
+
+ int DoHandshakeLoop(int last_io_result);
+ int DoReadLoop(int result);
+ int DoWriteLoop(int result);
+ int DoHandshake();
+ void DoHandshakeCallback(int result);
+ void DoReadCallback(int result);
+ void DoWriteCallback(int result);
+
+ int Init();
+
+ // Members used to send and receive buffer.
+ bool transport_send_busy_;
+ bool transport_recv_busy_;
+ bool transport_recv_eof_;
+
+ scoped_refptr<DrainableIOBuffer> send_buffer_;
+ scoped_refptr<IOBuffer> recv_buffer_;
+
+ BoundNetLog net_log_;
+
+ CompletionCallback user_handshake_callback_;
+ CompletionCallback user_read_callback_;
+ CompletionCallback user_write_callback_;
+
+ // Used by Read function.
+ scoped_refptr<IOBuffer> user_read_buf_;
+ int user_read_buf_len_;
+
+ // Used by Write function.
+ scoped_refptr<IOBuffer> user_write_buf_;
+ int user_write_buf_len_;
+
+ // Used by TransportWriteComplete() and TransportReadComplete() to signify an
+ // error writing to the transport socket. A value of OK indicates no error.
+ int transport_write_error_;
+
+ // OpenSSL stuff
+ SSL* ssl_;
+ BIO* transport_bio_;
+
+ // StreamSocket for sending and receiving data.
+ scoped_ptr<StreamSocket> transport_socket_;
+
+ // Options for the SSL socket.
+ SSLConfig ssl_config_;
+
+ // Certificate for the server.
+ scoped_refptr<X509Certificate> cert_;
+
+ // Private key used by the server.
+ scoped_ptr<crypto::RSAPrivateKey> key_;
+
+ State next_handshake_state_;
+ bool completed_handshake_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLServerSocketOpenSSL);
+};
+
+} // namespace net
+
+#endif // NET_SOCKET_SSL_SERVER_SOCKET_OPENSSL_H_
diff --git a/chromium/net/socket/ssl_server_socket_unittest.cc b/chromium/net/socket/ssl_server_socket_unittest.cc
index d5d04b20a90..9da10f6bbd0 100644
--- a/chromium/net/socket/ssl_server_socket_unittest.cc
+++ b/chromium/net/socket/ssl_server_socket_unittest.cc
@@ -178,12 +178,12 @@ class FakeSocket : public StreamSocket {
return outgoing_->Write(buf, buf_len, callback);
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE {
- return true;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE {
+ return net::OK;
}
- virtual bool SetSendBufferSize(int32 size) OVERRIDE {
- return true;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE {
+ return net::OK;
}
virtual int Connect(const CompletionCallback& callback) OVERRIDE {
@@ -331,7 +331,6 @@ class SSLServerSocketTest : public PlatformTest {
crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
net::SSLConfig ssl_config;
- ssl_config.cached_info_enabled = false;
ssl_config.false_start_enabled = false;
ssl_config.channel_id_enabled = false;
@@ -362,9 +361,6 @@ class SSLServerSocketTest : public PlatformTest {
scoped_ptr<net::TransportSecurityState> transport_security_state_;
};
-// SSLServerSocket is only implemented using NSS.
-#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX)
-
// This test only executes creation of client and server sockets. This is to
// test that creation of sockets doesn't crash and have minimal code to run
// under valgrind in order to help debugging memory problems.
@@ -481,11 +477,17 @@ TEST_F(SSLServerSocketTest, DataTransfer) {
EXPECT_EQ(0, memcmp(write_buf->data(), read_buf->data(), write_buf->size()));
}
+// Flaky on Android: http://crbug.com/381147
+#if defined(OS_ANDROID)
+#define MAYBE_ClientWriteAfterServerClose DISABLED_ClientWriteAfterServerClose
+#else
+#define MAYBE_ClientWriteAfterServerClose ClientWriteAfterServerClose
+#endif
// A regression test for bug 127822 (http://crbug.com/127822).
// If the server closes the connection after the handshake is finished,
// the client's Write() call should not cause an infinite loop.
// NOTE: this is a test for SSLClientSocket rather than SSLServerSocket.
-TEST_F(SSLServerSocketTest, ClientWriteAfterServerClose) {
+TEST_F(SSLServerSocketTest, MAYBE_ClientWriteAfterServerClose) {
Initialize();
TestCompletionCallback connect_callback;
@@ -581,6 +583,5 @@ TEST_F(SSLServerSocketTest, ExportKeyingMaterial) {
ASSERT_EQ(rv, net::OK);
EXPECT_NE(0, memcmp(server_out, client_bad, sizeof(server_out)));
}
-#endif
} // namespace net
diff --git a/chromium/net/socket/stream_listen_socket.cc b/chromium/net/socket/stream_listen_socket.cc
index 960991b7c6d..fd164a556d5 100644
--- a/chromium/net/socket/stream_listen_socket.cc
+++ b/chromium/net/socket/stream_listen_socket.cc
@@ -96,7 +96,24 @@ int StreamListenSocket::GetLocalAddress(IPEndPoint* address) {
return MapSystemError(err);
}
if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
+ return ERR_ADDRESS_INVALID;
+ return OK;
+}
+
+int StreamListenSocket::GetPeerAddress(IPEndPoint* address) {
+ SockaddrStorage storage;
+ if (getpeername(socket_, storage.addr, &storage.addr_len)) {
+#if defined(OS_WIN)
+ int err = WSAGetLastError();
+#else
+ int err = errno;
+#endif
+ return MapSystemError(err);
+ }
+
+ if (!address->FromSockAddr(storage.addr, storage.addr_len))
+ return ERR_ADDRESS_INVALID;
+
return OK;
}
@@ -235,12 +252,14 @@ void StreamListenSocket::OnObjectSignaled(HANDLE object) {
return;
}
- if (ev.lNetworkEvents & FD_CLOSE) {
+ // If both FD_CLOSE and FD_READ are set we only call Read().
+ // This will cause OnObjectSignaled to be called immediately again
+ // unless this socket is destroyed in Read().
+ if ((ev.lNetworkEvents & (FD_CLOSE | FD_READ)) == FD_CLOSE) {
Close();
// Close might have deleted this object. We should return immediately.
return;
}
-
// The object was reset by WSAEnumNetworkEvents. Watch for the next signal.
watcher_.StartWatching(object, this);
@@ -257,8 +276,7 @@ void StreamListenSocket::OnObjectSignaled(HANDLE object) {
has_pending_reads_ = true;
} else {
Read();
- // Read() might call Close() internally and 'this' can be invalid here
- return;
+ // Read might have deleted this object. We should return immediately.
}
}
}
diff --git a/chromium/net/socket/stream_listen_socket.h b/chromium/net/socket/stream_listen_socket.h
index 3c9b984ed76..813d96a22be 100644
--- a/chromium/net/socket/stream_listen_socket.h
+++ b/chromium/net/socket/stream_listen_socket.h
@@ -72,7 +72,11 @@ class NET_EXPORT StreamListenSocket
void Send(const std::string& str, bool append_linefeed = false);
// Copies the local address to |address|. Returns a network error code.
- int GetLocalAddress(IPEndPoint* address);
+ // This method is virtual to support unit testing.
+ virtual int GetLocalAddress(IPEndPoint* address);
+ // Copies the peer address to |address|. Returns a network error code.
+ // This method is virtual to support unit testing.
+ virtual int GetPeerAddress(IPEndPoint* address);
static const int kSocketError;
diff --git a/chromium/net/socket/stream_socket.h b/chromium/net/socket/stream_socket.h
index 38eec86dd92..72088103a3d 100644
--- a/chromium/net/socket/stream_socket.h
+++ b/chromium/net/socket/stream_socket.h
@@ -70,9 +70,9 @@ class NET_EXPORT_PRIVATE StreamSocket : public Socket {
virtual void SetSubresourceSpeculation() = 0;
virtual void SetOmniboxSpeculation() = 0;
- // Returns true if the underlying transport socket ever had any reads or
- // writes. StreamSockets layered on top of transport sockets should forward
- // this call to the transport socket.
+ // Returns true if the socket ever had any reads or writes. StreamSockets
+ // layered on top of transport sockets should return if their own Read() or
+ // Write() methods had been called, not the underlying transport's.
virtual bool WasEverUsed() const = 0;
// Returns true if the underlying transport socket is using TCP FastOpen.
diff --git a/chromium/net/socket/tcp_client_socket.cc b/chromium/net/socket/tcp_client_socket.cc
index 22aea47778b..53d8d1fe259 100644
--- a/chromium/net/socket/tcp_client_socket.cc
+++ b/chromium/net/socket/tcp_client_socket.cc
@@ -269,11 +269,11 @@ int TCPClientSocket::Write(IOBuffer* buf,
return result;
}
-bool TCPClientSocket::SetReceiveBufferSize(int32 size) {
+int TCPClientSocket::SetReceiveBufferSize(int32 size) {
return socket_->SetReceiveBufferSize(size);
}
-bool TCPClientSocket::SetSendBufferSize(int32 size) {
+int TCPClientSocket::SetSendBufferSize(int32 size) {
return socket_->SetSendBufferSize(size);
}
diff --git a/chromium/net/socket/tcp_client_socket.h b/chromium/net/socket/tcp_client_socket.h
index fabcbc1b39d..970da2a026f 100644
--- a/chromium/net/socket/tcp_client_socket.h
+++ b/chromium/net/socket/tcp_client_socket.h
@@ -60,8 +60,8 @@ class NET_EXPORT TCPClientSocket : public StreamSocket {
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual bool SetKeepAlive(bool enable, int delay);
virtual bool SetNoDelay(bool no_delay);
diff --git a/chromium/net/socket/tcp_socket.cc b/chromium/net/socket/tcp_socket.cc
index fd72f6b4640..63703627134 100644
--- a/chromium/net/socket/tcp_socket.cc
+++ b/chromium/net/socket/tcp_socket.cc
@@ -6,50 +6,73 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/worker_pool.h"
namespace net {
namespace {
-#if defined(OS_LINUX)
+bool g_tcp_fastopen_enabled = false;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+
+typedef base::RefCountedData<bool> SharedBoolean;
// 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() {
+void SystemSupportsTCPFastOpen(scoped_refptr<SharedBoolean> supported) {
+ supported->data = false;
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;
+ if (!base::ReadFileToString(base::FilePath(kTCPFastOpenProcFilePath),
+ &system_enabled_tcp_fastopen)) {
+ return;
}
// 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;
}
- return true;
+ supported->data = true;
+}
+
+void EnableCallback(scoped_refptr<SharedBoolean> supported) {
+ g_tcp_fastopen_enabled = supported->data;
+}
+
+// This is asynchronous because it needs to do file IO, and it isn't allowed to
+// do that on the IO thread.
+void EnableFastOpenIfSupported() {
+ scoped_refptr<SharedBoolean> supported = new SharedBoolean;
+ base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(SystemSupportsTCPFastOpen, supported),
+ base::Bind(EnableCallback, supported),
+ false);
}
#else
-bool SystemSupportsTCPFastOpen() {
- return false;
+void EnableFastOpenIfSupported() {
+ g_tcp_fastopen_enabled = false;
}
#endif
-bool g_tcp_fastopen_enabled = false;
-
} // namespace
void SetTCPFastOpenEnabled(bool value) {
- g_tcp_fastopen_enabled = value && SystemSupportsTCPFastOpen();
+ if (value) {
+ EnableFastOpenIfSupported();
+ } else {
+ g_tcp_fastopen_enabled = false;
+ }
}
bool IsTCPFastOpenEnabled() {
diff --git a/chromium/net/socket/tcp_socket_libevent.cc b/chromium/net/socket/tcp_socket_libevent.cc
index f4e4fe861af..72ae5809e10 100644
--- a/chromium/net/socket/tcp_socket_libevent.cc
+++ b/chromium/net/socket/tcp_socket_libevent.cc
@@ -35,8 +35,6 @@ 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
@@ -54,6 +52,11 @@ bool SetTCPKeepAlive(int fd, bool enable, int delay) {
PLOG(ERROR) << "Failed to set SO_KEEPALIVE on fd: " << fd;
return false;
}
+
+ // If we disabled TCP keep alive, our work is done here.
+ if (!enable)
+ return true;
+
#if defined(OS_LINUX) || defined(OS_ANDROID)
// Set seconds until first TCP keep alive.
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) {
@@ -455,7 +458,23 @@ void TCPSocketLibevent::SetDefaultOptionsForClient() {
// 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.
+
+ // TCP keep alive wakes up the radio, which is expensive on mobile. Do not
+ // enable it there. It's useful to prevent TCP middleboxes from timing out
+ // connection mappings. Packets for timed out connection mappings at
+ // middleboxes will either lead to:
+ // a) Middleboxes sending TCP RSTs. It's up to higher layers to check for this
+ // and retry. The HTTP network transaction code does this.
+ // b) Middleboxes just drop the unrecognized TCP packet. This leads to the TCP
+ // stack retransmitting packets per TCP stack retransmission timeouts, which
+ // are very high (on the order of seconds). Given the number of
+ // retransmissions required before killing the connection, this can lead to
+ // tens of seconds or even minutes of delay, depending on OS.
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+ const int kTCPKeepAliveSeconds = 45;
+
SetTCPKeepAlive(socket_, true, kTCPKeepAliveSeconds);
+#endif
}
int TCPSocketLibevent::SetAddressReuse(bool allow) {
@@ -482,22 +501,18 @@ int TCPSocketLibevent::SetAddressReuse(bool allow) {
return OK;
}
-bool TCPSocketLibevent::SetReceiveBufferSize(int32 size) {
+int 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;
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ return (rv == 0) ? OK : MapSystemError(errno);
}
-bool TCPSocketLibevent::SetSendBufferSize(int32 size) {
+int 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;
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ return (rv == 0) ? OK : MapSystemError(errno);
}
bool TCPSocketLibevent::SetKeepAlive(bool enable, int delay) {
@@ -592,9 +607,9 @@ int TCPSocketLibevent::AcceptInternal(scoped_ptr<TCPSocketLibevent>* socket,
NOTREACHED();
if (IGNORE_EINTR(close(new_socket)) < 0)
PLOG(ERROR) << "close";
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT,
- ERR_ADDRESS_INVALID);
- return ERR_ADDRESS_INVALID;
+ int net_error = ERR_ADDRESS_INVALID;
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error);
+ return net_error;
}
scoped_ptr<TCPSocketLibevent> tcp_socket(new TCPSocketLibevent(
net_log_.net_log(), net_log_.source()));
@@ -620,7 +635,7 @@ int TCPSocketLibevent::DoConnect() {
if (!use_tcp_fastopen_) {
SockaddrStorage storage;
if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
+ return ERR_ADDRESS_INVALID;
if (!HANDLE_EINTR(connect(socket_, storage.addr, storage.addr_len))) {
// Connected without waiting!
@@ -806,7 +821,9 @@ int TCPSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) {
if (use_tcp_fastopen_ && !tcp_fastopen_connected_) {
SockaddrStorage storage;
if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len)) {
- errno = EINVAL;
+ // Set errno to EADDRNOTAVAIL so that MapSystemError will map it to
+ // ERR_ADDRESS_INVALID later.
+ errno = EADDRNOTAVAIL;
return -1;
}
diff --git a/chromium/net/socket/tcp_socket_libevent.h b/chromium/net/socket/tcp_socket_libevent.h
index a50caf0ad59..9ef235be85d 100644
--- a/chromium/net/socket/tcp_socket_libevent.h
+++ b/chromium/net/socket/tcp_socket_libevent.h
@@ -61,8 +61,8 @@ class NET_EXPORT TCPSocketLibevent : public base::NonThreadSafe {
// - SetKeepAlive(true, 45).
void SetDefaultOptionsForClient();
int SetAddressReuse(bool allow);
- bool SetReceiveBufferSize(int32 size);
- bool SetSendBufferSize(int32 size);
+ int SetReceiveBufferSize(int32 size);
+ int SetSendBufferSize(int32 size);
bool SetKeepAlive(bool enable, int delay);
bool SetNoDelay(bool no_delay);
diff --git a/chromium/net/socket/tcp_socket_unittest.cc b/chromium/net/socket/tcp_socket_unittest.cc
index a45fcba016b..198138860fc 100644
--- a/chromium/net/socket/tcp_socket_unittest.cc
+++ b/chromium/net/socket/tcp_socket_unittest.cc
@@ -64,6 +64,28 @@ class TCPSocketTest : public PlatformTest {
*address = IPEndPoint(ip_number, port);
}
+ void TestAcceptAsync() {
+ TestCompletionCallback accept_callback;
+ scoped_ptr<TCPSocket> accepted_socket;
+ IPEndPoint accepted_address;
+ ASSERT_EQ(ERR_IO_PENDING,
+ socket_.Accept(&accepted_socket, &accepted_address,
+ accept_callback.callback()));
+
+ TestCompletionCallback connect_callback;
+ TCPClientSocket connecting_socket(local_address_list(),
+ NULL, NetLog::Source());
+ connecting_socket.Connect(connect_callback.callback());
+
+ EXPECT_EQ(OK, connect_callback.WaitForResult());
+ EXPECT_EQ(OK, accept_callback.WaitForResult());
+
+ EXPECT_TRUE(accepted_socket.get());
+
+ // Both sockets should be on the loopback network interface.
+ EXPECT_EQ(accepted_address.address(), local_address_.address());
+ }
+
AddressList local_address_list() const {
return AddressList(local_address_);
}
@@ -103,27 +125,28 @@ TEST_F(TCPSocketTest, Accept) {
// Test Accept() callback.
TEST_F(TCPSocketTest, AcceptAsync) {
ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4());
+ TestAcceptAsync();
+}
- TestCompletionCallback accept_callback;
- scoped_ptr<TCPSocket> accepted_socket;
- IPEndPoint accepted_address;
- ASSERT_EQ(ERR_IO_PENDING,
- socket_.Accept(&accepted_socket, &accepted_address,
- accept_callback.callback()));
+#if defined(OS_WIN)
+// Test Accept() for AdoptListenSocket.
+TEST_F(TCPSocketTest, AcceptForAdoptedListenSocket) {
+ // Create a socket to be used with AdoptListenSocket.
+ SOCKET existing_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ASSERT_EQ(OK, socket_.AdoptListenSocket(existing_socket));
- TestCompletionCallback connect_callback;
- TCPClientSocket connecting_socket(local_address_list(),
- NULL, NetLog::Source());
- connecting_socket.Connect(connect_callback.callback());
+ IPEndPoint address;
+ ParseAddress("127.0.0.1", 0, &address);
+ SockaddrStorage storage;
+ ASSERT_TRUE(address.ToSockAddr(storage.addr, &storage.addr_len));
+ ASSERT_EQ(0, bind(existing_socket, storage.addr, storage.addr_len));
- EXPECT_EQ(OK, connect_callback.WaitForResult());
- EXPECT_EQ(OK, accept_callback.WaitForResult());
+ ASSERT_EQ(OK, socket_.Listen(kListenBacklog));
+ ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_));
- EXPECT_TRUE(accepted_socket.get());
-
- // Both sockets should be on the loopback network interface.
- EXPECT_EQ(accepted_address.address(), local_address_.address());
+ TestAcceptAsync();
}
+#endif
// Accept two connections simultaneously.
TEST_F(TCPSocketTest, Accept2Connections) {
diff --git a/chromium/net/socket/tcp_socket_win.cc b/chromium/net/socket/tcp_socket_win.cc
index 5f8cd5c3e86..88db36fd41c 100644
--- a/chromium/net/socket/tcp_socket_win.cc
+++ b/chromium/net/socket/tcp_socket_win.cc
@@ -28,18 +28,20 @@ namespace {
const int kTCPKeepAliveSeconds = 45;
-bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) {
+int 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;
+ int net_error = (rv == 0) ? OK : MapSystemError(WSAGetLastError());
+ DCHECK(!rv) << "Could not set socket receive buffer size: " << net_error;
+ return net_error;
}
-bool SetSocketSendBufferSize(SOCKET socket, int32 size) {
+int 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;
+ int net_error = (rv == 0) ? OK : MapSystemError(WSAGetLastError());
+ DCHECK(!rv) << "Could not set socket send buffer size: " << net_error;
+ return net_error;
}
// Disable Nagle.
@@ -320,6 +322,24 @@ int TCPSocketWin::AdoptConnectedSocket(SOCKET socket,
return OK;
}
+int TCPSocketWin::AdoptListenSocket(SOCKET socket) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(socket_, INVALID_SOCKET);
+
+ socket_ = socket;
+
+ if (SetNonBlocking(socket_)) {
+ int result = MapSystemError(WSAGetLastError());
+ Close();
+ return result;
+ }
+
+ // |core_| is not needed for sockets that are used to accept connections.
+ // The operation here is more like Open but with an existing socket.
+
+ return OK;
+}
+
int TCPSocketWin::Bind(const IPEndPoint& address) {
DCHECK(CalledOnValidThread());
DCHECK_NE(socket_, INVALID_SOCKET);
@@ -599,12 +619,12 @@ int TCPSocketWin::SetExclusiveAddrUse() {
return OK;
}
-bool TCPSocketWin::SetReceiveBufferSize(int32 size) {
+int TCPSocketWin::SetReceiveBufferSize(int32 size) {
DCHECK(CalledOnValidThread());
return SetSocketReceiveBufferSize(socket_, size);
}
-bool TCPSocketWin::SetSendBufferSize(int32 size) {
+int TCPSocketWin::SetSendBufferSize(int32 size) {
DCHECK(CalledOnValidThread());
return SetSocketSendBufferSize(socket_, size);
}
@@ -621,6 +641,9 @@ void TCPSocketWin::Close() {
DCHECK(CalledOnValidThread());
if (socket_ != INVALID_SOCKET) {
+ // Only log the close event if there's actually a socket to close.
+ net_log_.AddEvent(NetLog::EventType::TYPE_SOCKET_CLOSED);
+
// Note: don't use CancelIo to cancel pending IO because it doesn't work
// when there is a Winsock layered service provider.
@@ -711,8 +734,9 @@ int TCPSocketWin::AcceptInternal(scoped_ptr<TCPSocketWin>* socket,
NOTREACHED();
if (closesocket(new_socket) < 0)
PLOG(ERROR) << "closesocket";
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED);
- return ERR_FAILED;
+ int net_error = ERR_ADDRESS_INVALID;
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error);
+ return net_error;
}
scoped_ptr<TCPSocketWin> tcp_socket(new TCPSocketWin(
net_log_.net_log(), net_log_.source()));
@@ -767,7 +791,7 @@ int TCPSocketWin::DoConnect() {
SockaddrStorage storage;
if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
+ return ERR_ADDRESS_INVALID;
if (!connect(socket_, storage.addr, storage.addr_len)) {
// Connected without waiting!
//
diff --git a/chromium/net/socket/tcp_socket_win.h b/chromium/net/socket/tcp_socket_win.h
index df5fbf09aec..2ddd538f211 100644
--- a/chromium/net/socket/tcp_socket_win.h
+++ b/chromium/net/socket/tcp_socket_win.h
@@ -31,8 +31,13 @@ class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe),
virtual ~TCPSocketWin();
int Open(AddressFamily family);
- // Takes ownership of |socket|.
+
+ // Both AdoptConnectedSocket and AdoptListenSocket take ownership of an
+ // existing socket. AdoptConnectedSocket takes an already connected
+ // socket. AdoptListenSocket takes a socket that is intended to accept
+ // connection. In some sense, AdoptListenSocket is more similar to Open.
int AdoptConnectedSocket(SOCKET socket, const IPEndPoint& peer_address);
+ int AdoptListenSocket(SOCKET socket);
int Bind(const IPEndPoint& address);
@@ -63,8 +68,8 @@ class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe),
// - SetKeepAlive(true, 45).
void SetDefaultOptionsForClient();
int SetExclusiveAddrUse();
- bool SetReceiveBufferSize(int32 size);
- bool SetSendBufferSize(int32 size);
+ int SetReceiveBufferSize(int32 size);
+ int SetSendBufferSize(int32 size);
bool SetKeepAlive(bool enable, int delay);
bool SetNoDelay(bool no_delay);
diff --git a/chromium/net/socket/transport_client_socket_pool.cc b/chromium/net/socket/transport_client_socket_pool.cc
index ec56dcba65c..dc481fa4b6d 100644
--- a/chromium/net/socket/transport_client_socket_pool.cc
+++ b/chromium/net/socket/transport_client_socket_pool.cc
@@ -439,7 +439,7 @@ int TransportClientSocketPool::RequestSocket(
const scoped_refptr<TransportSocketParams>* casted_params =
static_cast<const scoped_refptr<TransportSocketParams>*>(params);
- if (net_log.IsLoggingAllEvents()) {
+ if (net_log.IsLogging()) {
// TODO(eroman): Split out the host and port parameters.
net_log.AddEvent(
NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
@@ -459,7 +459,7 @@ void TransportClientSocketPool::RequestSockets(
const scoped_refptr<TransportSocketParams>* casted_params =
static_cast<const scoped_refptr<TransportSocketParams>*>(params);
- if (net_log.IsLoggingAllEvents()) {
+ if (net_log.IsLogging()) {
// TODO(eroman): Split out the host and port parameters.
net_log.AddEvent(
NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
diff --git a/chromium/net/socket/transport_client_socket_pool_unittest.cc b/chromium/net/socket/transport_client_socket_pool_unittest.cc
index ff85847979b..425bb8cc421 100644
--- a/chromium/net/socket/transport_client_socket_pool_unittest.cc
+++ b/chromium/net/socket/transport_client_socket_pool_unittest.cc
@@ -145,8 +145,8 @@ class MockClientSocket : public StreamSocket {
const CompletionCallback& callback) OVERRIDE {
return ERR_FAILED;
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE { return true; }
- virtual bool SetSendBufferSize(int32 size) OVERRIDE { return true; }
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE { return OK; }
+ virtual int SetSendBufferSize(int32 size) OVERRIDE { return OK; }
private:
bool connected_;
@@ -210,8 +210,8 @@ class MockFailingClientSocket : public StreamSocket {
const CompletionCallback& callback) OVERRIDE {
return ERR_FAILED;
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE { return true; }
- virtual bool SetSendBufferSize(int32 size) OVERRIDE { return true; }
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE { return OK; }
+ virtual int SetSendBufferSize(int32 size) OVERRIDE { return OK; }
private:
const AddressList addrlist_;
@@ -299,8 +299,8 @@ class MockPendingClientSocket : public StreamSocket {
const CompletionCallback& callback) OVERRIDE {
return ERR_FAILED;
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE { return true; }
- virtual bool SetSendBufferSize(int32 size) OVERRIDE { return true; }
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE { return OK; }
+ virtual int SetSendBufferSize(int32 size) OVERRIDE { return OK; }
private:
void DoCallback(const CompletionCallback& callback) {
diff --git a/chromium/net/socket_stream/OWNERS b/chromium/net/socket_stream/OWNERS
index 8ef489a002e..a2bac602390 100644
--- a/chromium/net/socket_stream/OWNERS
+++ b/chromium/net/socket_stream/OWNERS
@@ -1,8 +1 @@
tyoshino@chromium.org
-
-# Have been inactive for a while.
-yutak@chromium.org
-toyoshim@chromium.org
-
-# On leave
-bashi@chromium.org
diff --git a/chromium/net/socket_stream/socket_stream.cc b/chromium/net/socket_stream/socket_stream.cc
index 64190f09715..7404ff34a4c 100644
--- a/chromium/net/socket_stream/socket_stream.cc
+++ b/chromium/net/socket_stream/socket_stream.cc
@@ -87,18 +87,20 @@ void SocketStream::ResponseHeaders::Realloc(size_t new_size) {
SocketStream::ResponseHeaders::~ResponseHeaders() { data_ = NULL; }
-SocketStream::SocketStream(const GURL& url, Delegate* delegate)
+SocketStream::SocketStream(const GURL& url, Delegate* delegate,
+ URLRequestContext* context,
+ CookieStore* cookie_store)
: delegate_(delegate),
url_(url),
max_pending_send_allowed_(kMaxPendingSendAllowed),
- context_(NULL),
+ context_(context),
next_state_(STATE_NONE),
factory_(ClientSocketFactory::GetDefaultFactory()),
proxy_mode_(kDirectConnection),
proxy_url_(url),
pac_request_(NULL),
connection_(new ClientSocketHandle),
- privacy_mode_(kPrivacyModeDisabled),
+ privacy_mode_(PRIVACY_MODE_DISABLED),
// Unretained() is required; without it, Bind() creates a circular
// dependency and the SocketStream object will not be freed.
io_callback_(base::Bind(&SocketStream::OnIOCompleted,
@@ -108,12 +110,24 @@ SocketStream::SocketStream(const GURL& url, Delegate* delegate)
waiting_for_write_completion_(false),
closing_(false),
server_closed_(false),
- metrics_(new SocketStreamMetrics(url)) {
+ metrics_(new SocketStreamMetrics(url)),
+ cookie_store_(cookie_store) {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
DCHECK(delegate_);
+
+ if (context_) {
+ if (!cookie_store_)
+ cookie_store_ = context_->cookie_store();
+
+ net_log_ = BoundNetLog::Make(
+ context->net_log(),
+ NetLog::SOURCE_SOCKET_STREAM);
+
+ net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE);
+ }
}
SocketStream::UserData* SocketStream::GetUserData(
@@ -132,35 +146,27 @@ bool SocketStream::is_secure() const {
return url_.SchemeIs("wss");
}
-void SocketStream::set_context(URLRequestContext* context) {
- const URLRequestContext* prev_context = context_;
-
- context_ = context;
-
- if (prev_context != context) {
- if (prev_context && pac_request_) {
- prev_context->proxy_service()->CancelPacRequest(pac_request_);
- pac_request_ = NULL;
- }
+void SocketStream::DetachContext() {
+ if (!context_)
+ return;
- net_log_.EndEvent(NetLog::TYPE_REQUEST_ALIVE);
- net_log_ = BoundNetLog();
+ if (pac_request_) {
+ context_->proxy_service()->CancelPacRequest(pac_request_);
+ pac_request_ = NULL;
+ }
- if (context) {
- net_log_ = BoundNetLog::Make(
- context->net_log(),
- NetLog::SOURCE_SOCKET_STREAM);
+ net_log_.EndEvent(NetLog::TYPE_REQUEST_ALIVE);
+ net_log_ = BoundNetLog();
- net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE);
- }
- }
+ context_ = NULL;
+ cookie_store_ = NULL;
}
void SocketStream::CheckPrivacyMode() {
if (context_ && context_->network_delegate()) {
bool enable = context_->network_delegate()->CanEnablePrivacyMode(url_,
url_);
- privacy_mode_ = enable ? kPrivacyModeEnabled : kPrivacyModeDisabled;
+ privacy_mode_ = enable ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED;
// Disable Channel ID if privacy mode is enabled.
if (enable)
server_ssl_config_.channel_id_enabled = false;
@@ -170,7 +176,7 @@ void SocketStream::CheckPrivacyMode() {
void SocketStream::Connect() {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
if (context_) {
context_->ssl_config_service()->GetSSLConfig(&server_ssl_config_);
@@ -203,7 +209,7 @@ size_t SocketStream::GetTotalSizeOfPendingWriteBufs() const {
bool SocketStream::SendData(const char* data, int len) {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
DCHECK_GT(len, 0);
@@ -250,7 +256,7 @@ bool SocketStream::SendData(const char* data, int len) {
void SocketStream::Close() {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
// If next_state_ is STATE_NONE, the socket was not opened, or already
// closed. So, return immediately.
@@ -265,7 +271,7 @@ void SocketStream::Close() {
void SocketStream::RestartWithAuth(const AuthCredentials& credentials) {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
DCHECK(proxy_auth_controller_.get());
if (!connection_->socket()) {
@@ -318,7 +324,7 @@ void SocketStream::ContinueDespiteError() {
}
SocketStream::~SocketStream() {
- set_context(NULL);
+ DetachContext();
DCHECK(!delegate_);
DCHECK(!pac_request_);
}
@@ -358,7 +364,7 @@ void SocketStream::DoClose() {
void SocketStream::Finish(int result) {
DCHECK(base::MessageLoop::current())
<< "The current base::MessageLoop must exist";
- DCHECK_EQ(base::MessageLoop::TYPE_IO, base::MessageLoop::current()->type())
+ DCHECK(base::MessageLoopForIO::IsCurrent())
<< "The current base::MessageLoop must be TYPE_IO";
DCHECK_LE(result, OK);
if (result == OK)
@@ -1331,15 +1337,19 @@ int SocketStream::HandleCertificateError(int result) {
SSLInfo ssl_info;
ssl_socket->GetSSLInfo(&ssl_info);
- TransportSecurityState::DomainState domain_state;
- const bool fatal = context_->transport_security_state() &&
- context_->transport_security_state()->GetDomainState(url_.host(),
- SSLConfigService::IsSNIAvailable(context_->ssl_config_service()),
- &domain_state) &&
- domain_state.ShouldSSLErrorsBeFatal();
+ TransportSecurityState* state = context_->transport_security_state();
+ const bool fatal =
+ state &&
+ state->ShouldSSLErrorsBeFatal(
+ url_.host(),
+ SSLConfigService::IsSNIAvailable(context_->ssl_config_service()));
delegate_->OnSSLCertificateError(this, ssl_info, fatal);
return ERR_IO_PENDING;
}
+CookieStore* SocketStream::cookie_store() const {
+ return cookie_store_;
+}
+
} // namespace net
diff --git a/chromium/net/socket_stream/socket_stream.h b/chromium/net/socket_stream/socket_stream.h
index a91d5cac501..cfc764db252 100644
--- a/chromium/net/socket_stream/socket_stream.h
+++ b/chromium/net/socket_stream/socket_stream.h
@@ -19,6 +19,7 @@
#include "net/base/net_export.h"
#include "net/base/net_log.h"
#include "net/base/privacy_mode.h"
+#include "net/cookies/cookie_store.h"
#include "net/proxy/proxy_service.h"
#include "net/ssl/ssl_config_service.h"
#include "net/url_request/url_request.h"
@@ -114,7 +115,8 @@ class NET_EXPORT SocketStream
virtual ~Delegate() {}
};
- SocketStream(const GURL& url, Delegate* delegate);
+ SocketStream(const GURL& url, Delegate* delegate, URLRequestContext* context,
+ CookieStore* cookie_store);
// The user data allows the clients to associate data with this job.
// Multiple user data values can be stored under different keys.
@@ -130,9 +132,6 @@ class NET_EXPORT SocketStream
int max_pending_send_allowed() const { return max_pending_send_allowed_; }
URLRequestContext* context() { return context_; }
- // There're some asynchronous operations and members that are constructed from
- // |context|. Be careful when you use this for the second time or more.
- void set_context(URLRequestContext* context);
const SSLConfig& server_ssl_config() const { return server_ssl_config_; }
PrivacyMode privacy_mode() const { return privacy_mode_; }
@@ -162,6 +161,9 @@ class NET_EXPORT SocketStream
// back.
virtual void DetachDelegate();
+ // Detach the context.
+ virtual void DetachContext();
+
const ProxyServer& proxy_server() const;
// Sets an alternative ClientSocketFactory. Doesn't take ownership of
@@ -180,6 +182,8 @@ class NET_EXPORT SocketStream
// actions on alert dialog or browser cached such kinds of user actions.
void ContinueDespiteError();
+ CookieStore* cookie_store() const;
+
protected:
friend class base::RefCountedThreadSafe<SocketStream>;
virtual ~SocketStream();
@@ -223,7 +227,7 @@ class NET_EXPORT SocketStream
private:
virtual ~ResponseHeaders();
- scoped_ptr_malloc<char> headers_;
+ scoped_ptr<char, base::FreeDeleter> headers_;
};
enum State {
@@ -389,6 +393,9 @@ class NET_EXPORT SocketStream
scoped_ptr<SocketStreamMetrics> metrics_;
+ // Cookie store to use for this socket stream.
+ scoped_refptr<CookieStore> cookie_store_;
+
DISALLOW_COPY_AND_ASSIGN(SocketStream);
};
diff --git a/chromium/net/socket_stream/socket_stream_job.cc b/chromium/net/socket_stream/socket_stream_job.cc
index 9c13a8f3a66..a481b97526e 100644
--- a/chromium/net/socket_stream/socket_stream_job.cc
+++ b/chromium/net/socket_stream/socket_stream_job.cc
@@ -24,19 +24,20 @@ SocketStreamJob* SocketStreamJob::CreateSocketStreamJob(
const GURL& url,
SocketStream::Delegate* delegate,
TransportSecurityState* sts,
- SSLConfigService* ssl) {
+ SSLConfigService* ssl,
+ URLRequestContext* context,
+ CookieStore* cookie_store) {
GURL socket_url(url);
- TransportSecurityState::DomainState domain_state;
- if (url.scheme() == "ws" && sts && sts->GetDomainState(
- url.host(), SSLConfigService::IsSNIAvailable(ssl), &domain_state) &&
- domain_state.ShouldUpgradeToSSL()) {
- url_canon::Replacements<char> replacements;
+ if (url.scheme() == "ws" && sts &&
+ sts->ShouldUpgradeToSSL(url.host(),
+ SSLConfigService::IsSNIAvailable(ssl))) {
+ url::Replacements<char> replacements;
static const char kNewScheme[] = "wss";
- replacements.SetScheme(kNewScheme,
- url_parse::Component(0, strlen(kNewScheme)));
+ replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme)));
socket_url = url.ReplaceComponents(replacements);
}
- return SocketStreamJobManager::GetInstance()->CreateJob(socket_url, delegate);
+ return SocketStreamJobManager::GetInstance()->CreateJob(
+ socket_url, delegate, context, cookie_store);
}
SocketStreamJob::SocketStreamJob() {}
@@ -82,6 +83,11 @@ void SocketStreamJob::DetachDelegate() {
socket_->DetachDelegate();
}
+void SocketStreamJob::DetachContext() {
+ if (socket_.get())
+ socket_->DetachContext();
+}
+
SocketStreamJob::~SocketStreamJob() {}
} // namespace net
diff --git a/chromium/net/socket_stream/socket_stream_job.h b/chromium/net/socket_stream/socket_stream_job.h
index d12e73aff31..9fc27d94e6b 100644
--- a/chromium/net/socket_stream/socket_stream_job.h
+++ b/chromium/net/socket_stream/socket_stream_job.h
@@ -15,6 +15,7 @@ class GURL;
namespace net {
+class CookieStore;
class SSLConfigService;
class SSLInfo;
class TransportSecurityState;
@@ -30,7 +31,9 @@ class NET_EXPORT SocketStreamJob
public:
// Callback function implemented by protocol handlers to create new jobs.
typedef SocketStreamJob* (ProtocolFactory)(const GURL& url,
- SocketStream::Delegate* delegate);
+ SocketStream::Delegate* delegate,
+ URLRequestContext* context,
+ CookieStore* cookie_store);
static ProtocolFactory* RegisterProtocolFactory(const std::string& scheme,
ProtocolFactory* factory);
@@ -39,7 +42,9 @@ class NET_EXPORT SocketStreamJob
const GURL& url,
SocketStream::Delegate* delegate,
TransportSecurityState* sts,
- SSLConfigService* ssl);
+ SSLConfigService* ssl,
+ URLRequestContext* context,
+ CookieStore* cookie_store);
SocketStreamJob();
void InitSocketStream(SocketStream* socket) {
@@ -52,9 +57,8 @@ class NET_EXPORT SocketStreamJob
URLRequestContext* context() const {
return socket_.get() ? socket_->context() : 0;
}
- void set_context(URLRequestContext* context) {
- if (socket_.get())
- socket_->set_context(context);
+ CookieStore* cookie_store() const {
+ return socket_.get() ? socket_->cookie_store() : 0;
}
virtual void Connect();
@@ -73,6 +77,8 @@ class NET_EXPORT SocketStreamJob
virtual void DetachDelegate();
+ virtual void DetachContext();
+
protected:
friend class WebSocketJobTest;
friend class base::RefCountedThreadSafe<SocketStreamJob>;
diff --git a/chromium/net/socket_stream/socket_stream_job_manager.cc b/chromium/net/socket_stream/socket_stream_job_manager.cc
index 7f66a4a4fd3..6418be4eb71 100644
--- a/chromium/net/socket_stream/socket_stream_job_manager.cc
+++ b/chromium/net/socket_stream/socket_stream_job_manager.cc
@@ -20,12 +20,14 @@ SocketStreamJobManager* SocketStreamJobManager::GetInstance() {
}
SocketStreamJob* SocketStreamJobManager::CreateJob(
- const GURL& url, SocketStream::Delegate* delegate) const {
+ const GURL& url, SocketStream::Delegate* delegate,
+ URLRequestContext* context, CookieStore* cookie_store) const {
// If url is invalid, create plain SocketStreamJob, which will close
// the socket immediately.
if (!url.is_valid()) {
SocketStreamJob* job = new SocketStreamJob();
- job->InitSocketStream(new SocketStream(url, delegate));
+ job->InitSocketStream(new SocketStream(url, delegate, context,
+ cookie_store));
return job;
}
@@ -34,12 +36,12 @@ SocketStreamJob* SocketStreamJobManager::CreateJob(
base::AutoLock locked(lock_);
FactoryMap::const_iterator found = factories_.find(scheme);
if (found != factories_.end()) {
- SocketStreamJob* job = found->second(url, delegate);
+ SocketStreamJob* job = found->second(url, delegate, context, cookie_store);
if (job)
return job;
}
SocketStreamJob* job = new SocketStreamJob();
- job->InitSocketStream(new SocketStream(url, delegate));
+ job->InitSocketStream(new SocketStream(url, delegate, context, cookie_store));
return job;
}
diff --git a/chromium/net/socket_stream/socket_stream_job_manager.h b/chromium/net/socket_stream/socket_stream_job_manager.h
index aa0cd409344..2363fb5eb9b 100644
--- a/chromium/net/socket_stream/socket_stream_job_manager.h
+++ b/chromium/net/socket_stream/socket_stream_job_manager.h
@@ -22,7 +22,8 @@ class SocketStreamJobManager {
static SocketStreamJobManager* GetInstance();
SocketStreamJob* CreateJob(
- const GURL& url, SocketStream::Delegate* delegate) const;
+ const GURL& url, SocketStream::Delegate* delegate,
+ URLRequestContext* context, CookieStore* cookie_store) const;
SocketStreamJob::ProtocolFactory* RegisterProtocolFactory(
const std::string& scheme, SocketStreamJob::ProtocolFactory* factory);
diff --git a/chromium/net/socket_stream/socket_stream_unittest.cc b/chromium/net/socket_stream/socket_stream_unittest.cc
index cbf559495c0..069f92eb9ed 100644
--- a/chromium/net/socket_stream/socket_stream_unittest.cc
+++ b/chromium/net/socket_stream/socket_stream_unittest.cc
@@ -23,6 +23,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+using base::ASCIIToUTF16;
+
namespace net {
namespace {
@@ -316,7 +318,7 @@ class SocketStreamTest : public PlatformTest {
virtual void DoCloseFlushPendingWriteTestWithSetContextNull(
SocketStreamEvent* event) {
- event->socket->set_context(NULL);
+ event->socket->DetachContext();
// handshake response received.
for (size_t i = 0; i < messages_.size(); i++) {
std::vector<char> frame;
@@ -398,9 +400,8 @@ TEST_F(SocketStreamTest, CloseFlushPendingWrite) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
MockWrite data_writes[] = {
MockWrite(SocketStreamTest::kWebSocketHandshakeRequest),
@@ -451,16 +452,16 @@ TEST_F(SocketStreamTest, ResolveFailure) {
scoped_ptr<SocketStreamEventRecorder> delegate(
new SocketStreamEventRecorder(test_callback.callback()));
- scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
// Make resolver fail.
TestURLRequestContext context;
scoped_ptr<MockHostResolver> mock_host_resolver(
new MockHostResolver());
mock_host_resolver->rules()->AddSimulatedFailure("example.com");
context.set_host_resolver(mock_host_resolver.get());
- socket_stream->set_context(&context);
+
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
// No read/write on socket is expected.
StaticSocketDataProvider data_provider(NULL, 0, NULL, 0);
@@ -491,9 +492,8 @@ TEST_F(SocketStreamTest, ExceedMaxPendingSendAllowed) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
DelayedSocketData data_provider(1, NULL, 0, NULL, 0);
@@ -564,12 +564,12 @@ TEST_F(SocketStreamTest, BasicAuthProxy) {
&SocketStreamEventRecorder::DoRestartWithAuth,
base::Unretained(delegate.get())));
- scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
TestURLRequestContextWithProxy context("myproxy:70");
- socket_stream->set_context(&context);
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
+
socket_stream->SetClientSocketFactory(&mock_socket_factory);
socket_stream->Connect();
@@ -616,9 +616,6 @@ TEST_F(SocketStreamTest, BasicAuthProxyWithAuthCache) {
delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose,
base::Unretained(delegate.get())));
- scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
TestURLRequestContextWithProxy context("myproxy:70");
HttpAuthCache* auth_cache =
context.http_transaction_factory()->GetSession()->http_auth_cache();
@@ -630,7 +627,10 @@ TEST_F(SocketStreamTest, BasicAuthProxyWithAuthCache) {
ASCIIToUTF16("bar")),
"/");
- socket_stream->set_context(&context);
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
+
socket_stream->SetClientSocketFactory(&mock_socket_factory);
socket_stream->Connect();
@@ -673,9 +673,6 @@ TEST_F(SocketStreamTest, WSSBasicAuthProxyWithAuthCache) {
delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose,
base::Unretained(delegate.get())));
- scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("wss://example.com/demo"), delegate.get()));
-
TestURLRequestContextWithProxy context("myproxy:70");
HttpAuthCache* auth_cache =
context.http_transaction_factory()->GetSession()->http_auth_cache();
@@ -687,7 +684,10 @@ TEST_F(SocketStreamTest, WSSBasicAuthProxyWithAuthCache) {
ASCIIToUTF16("bar")),
"/");
- socket_stream->set_context(&context);
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("wss://example.com/demo"), delegate.get(),
+ &context, NULL));
+
socket_stream->SetClientSocketFactory(&mock_socket_factory);
socket_stream->Connect();
@@ -719,9 +719,8 @@ TEST_F(SocketStreamTest, IOPending) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
MockWrite data_writes[] = {
MockWrite(SocketStreamTest::kWebSocketHandshakeRequest),
@@ -781,9 +780,8 @@ TEST_F(SocketStreamTest, SwitchToSpdy) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
socket_stream->Connect();
@@ -809,9 +807,8 @@ TEST_F(SocketStreamTest, SwitchAfterPending) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
socket_stream->Connect();
io_test_callback_.WaitForResult();
@@ -863,9 +860,9 @@ TEST_F(SocketStreamTest, SecureProxyConnectError) {
base::Unretained(delegate.get())));
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
- socket_stream->set_context(&context);
socket_stream->SetClientSocketFactory(&mock_socket_factory);
socket_stream->Connect();
@@ -914,9 +911,9 @@ TEST_F(SocketStreamTest, SecureProxyConnect) {
base::Unretained(delegate.get())));
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
- socket_stream->set_context(&context);
socket_stream->SetClientSocketFactory(&mock_socket_factory);
socket_stream->Connect();
@@ -946,9 +943,8 @@ TEST_F(SocketStreamTest, BeforeConnectFailed) {
context.set_network_delegate(&network_delegate);
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
-
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
socket_stream->Connect();
@@ -979,8 +975,8 @@ TEST_F(SocketStreamTest, OnErrorDetachDelegate) {
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://localhost:9998/echo"), delegate));
- socket_stream->set_context(&context);
+ new SocketStream(GURL("ws://localhost:9998/echo"), delegate,
+ &context, NULL));
socket_stream->SetClientSocketFactory(&mock_socket_factory);
delegate->set_socket_stream(socket_stream);
// The delegate pointer will become invalid during the test. Set it to NULL to
@@ -999,7 +995,8 @@ TEST_F(SocketStreamTest, NullContextSocketStreamShouldNotCrash) {
new SocketStreamEventRecorder(test_callback.callback()));
TestURLRequestContext context;
scoped_refptr<SocketStream> socket_stream(
- new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get(),
+ &context, NULL));
delegate->SetOnStartOpenConnection(base::Bind(
&SocketStreamTest::DoIOPending, base::Unretained(this)));
delegate->SetOnConnected(base::Bind(
@@ -1008,8 +1005,6 @@ TEST_F(SocketStreamTest, NullContextSocketStreamShouldNotCrash) {
&SocketStreamTest::DoCloseFlushPendingWriteTestWithSetContextNull,
base::Unretained(this)));
- socket_stream->set_context(&context);
-
MockWrite data_writes[] = {
MockWrite(SocketStreamTest::kWebSocketHandshakeRequest),
};
diff --git a/chromium/net/spdy/buffered_spdy_framer.cc b/chromium/net/spdy/buffered_spdy_framer.cc
index fee2192dc4d..8424a15e64d 100644
--- a/chromium/net/spdy/buffered_spdy_framer.cc
+++ b/chromium/net/spdy/buffered_spdy_framer.cc
@@ -15,9 +15,7 @@ SpdyMajorVersion NextProtoToSpdyMajorVersion(NextProto next_proto) {
case kProtoSPDY3:
case kProtoSPDY31:
return SPDY3;
- // SPDY/4 and HTTP/2 share the same framing for now.
- case kProtoSPDY4a2:
- case kProtoHTTP2Draft04:
+ case kProtoSPDY4:
return SPDY4;
case kProtoUnknown:
case kProtoHTTP11:
@@ -62,7 +60,6 @@ void BufferedSpdyFramer::OnError(SpdyFramer* spdy_framer) {
void BufferedSpdyFramer::OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional) {
frames_received_++;
@@ -72,7 +69,6 @@ void BufferedSpdyFramer::OnSynStream(SpdyStreamId stream_id,
control_frame_fields_->stream_id = stream_id;
control_frame_fields_->associated_stream_id = associated_stream_id;
control_frame_fields_->priority = priority;
- control_frame_fields_->credential_slot = credential_slot;
control_frame_fields_->fin = fin;
control_frame_fields_->unidirectional = unidirectional;
@@ -80,7 +76,8 @@ void BufferedSpdyFramer::OnSynStream(SpdyStreamId stream_id,
}
void BufferedSpdyFramer::OnHeaders(SpdyStreamId stream_id,
- bool fin) {
+ bool fin,
+ bool end) {
frames_received_++;
DCHECK(!control_frame_fields_.get());
control_frame_fields_.reset(new ControlFrameFields());
@@ -103,12 +100,6 @@ void BufferedSpdyFramer::OnSynReply(SpdyStreamId stream_id,
InitHeaderStreaming(stream_id);
}
-bool BufferedSpdyFramer::OnCredentialFrameData(const char* frame_data,
- size_t len) {
- DCHECK(false);
- return false;
-}
-
bool BufferedSpdyFramer::OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) {
@@ -134,7 +125,6 @@ bool BufferedSpdyFramer::OnControlFrameHeaderData(SpdyStreamId stream_id,
visitor_->OnSynStream(control_frame_fields_->stream_id,
control_frame_fields_->associated_stream_id,
control_frame_fields_->priority,
- control_frame_fields_->credential_slot,
control_frame_fields_->fin,
control_frame_fields_->unidirectional,
headers);
@@ -149,6 +139,12 @@ bool BufferedSpdyFramer::OnControlFrameHeaderData(SpdyStreamId stream_id,
control_frame_fields_->fin,
headers);
break;
+ case PUSH_PROMISE:
+ DCHECK_LT(SPDY3, protocol_version());
+ visitor_->OnPushPromise(control_frame_fields_->stream_id,
+ control_frame_fields_->promised_stream_id,
+ headers);
+ break;
default:
DCHECK(false) << "Unexpect control frame type: "
<< control_frame_fields_->type;
@@ -195,8 +191,16 @@ void BufferedSpdyFramer::OnSetting(SpdySettingsIds id,
visitor_->OnSetting(id, flags, value);
}
-void BufferedSpdyFramer::OnPing(uint32 unique_id) {
- visitor_->OnPing(unique_id);
+void BufferedSpdyFramer::OnSettingsAck() {
+ visitor_->OnSettingsAck();
+}
+
+void BufferedSpdyFramer::OnSettingsEnd() {
+ visitor_->OnSettingsEnd();
+}
+
+void BufferedSpdyFramer::OnPing(SpdyPingId unique_id, bool is_ack) {
+ visitor_->OnPing(unique_id, is_ack);
}
void BufferedSpdyFramer::OnRstStream(SpdyStreamId stream_id,
@@ -214,8 +218,20 @@ void BufferedSpdyFramer::OnWindowUpdate(SpdyStreamId stream_id,
}
void BufferedSpdyFramer::OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) {
- visitor_->OnPushPromise(stream_id, promised_stream_id);
+ SpdyStreamId promised_stream_id,
+ bool end) {
+ DCHECK_LT(SPDY3, protocol_version());
+ frames_received_++;
+ DCHECK(!control_frame_fields_.get());
+ control_frame_fields_.reset(new ControlFrameFields());
+ control_frame_fields_->type = PUSH_PROMISE;
+ control_frame_fields_->stream_id = stream_id;
+ control_frame_fields_->promised_stream_id = promised_stream_id;
+
+ InitHeaderStreaming(stream_id);
+}
+
+void BufferedSpdyFramer::OnContinuation(SpdyStreamId stream_id, bool end) {
}
SpdyMajorVersion BufferedSpdyFramer::protocol_version() {
@@ -246,64 +262,120 @@ bool BufferedSpdyFramer::HasError() {
return spdy_framer_.HasError();
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer
+// SpdySynStreamIR).
SpdyFrame* BufferedSpdyFramer::CreateSynStream(
SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
SpdyControlFlags flags,
const SpdyHeaderBlock* headers) {
- return spdy_framer_.CreateSynStream(stream_id, associated_stream_id, priority,
- credential_slot, flags, headers);
-}
-
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_associated_to_stream_id(associated_stream_id);
+ syn_stream.set_priority(priority);
+ syn_stream.set_fin((flags & CONTROL_FLAG_FIN) != 0);
+ syn_stream.set_unidirectional((flags & CONTROL_FLAG_UNIDIRECTIONAL) != 0);
+ // TODO(hkhalil): Avoid copy here.
+ syn_stream.set_name_value_block(*headers);
+ return spdy_framer_.SerializeSynStream(syn_stream);
+}
+
+// TODO(jgraettinger): Eliminate uses of this method (prefer
+// SpdySynReplyIR).
SpdyFrame* BufferedSpdyFramer::CreateSynReply(
SpdyStreamId stream_id,
SpdyControlFlags flags,
const SpdyHeaderBlock* headers) {
- return spdy_framer_.CreateSynReply(stream_id, flags, headers);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_fin(flags & CONTROL_FLAG_FIN);
+ // TODO(hkhalil): Avoid copy here.
+ syn_reply.set_name_value_block(*headers);
+ return spdy_framer_.SerializeSynReply(syn_reply);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer
+// SpdyRstStreamIR).
SpdyFrame* BufferedSpdyFramer::CreateRstStream(
SpdyStreamId stream_id,
SpdyRstStreamStatus status) const {
- return spdy_framer_.CreateRstStream(stream_id, status);
+ // RST_STREAM payloads are not part of any SPDY spec.
+ // SpdyFramer will accept them, but don't create them.
+ SpdyRstStreamIR rst_ir(stream_id, status, "");
+ return spdy_framer_.SerializeRstStream(rst_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer
+// SpdySettingsIR).
SpdyFrame* BufferedSpdyFramer::CreateSettings(
const SettingsMap& values) const {
- return spdy_framer_.CreateSettings(values);
+ SpdySettingsIR settings_ir;
+ for (SettingsMap::const_iterator it = values.begin();
+ it != values.end();
+ ++it) {
+ settings_ir.AddSetting(
+ it->first,
+ (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
+ (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
+ it->second.second);
+ }
+ return spdy_framer_.SerializeSettings(settings_ir);
}
-SpdyFrame* BufferedSpdyFramer::CreatePingFrame(
- uint32 unique_id) const {
- return spdy_framer_.CreatePingFrame(unique_id);
+// TODO(jgraettinger): Eliminate uses of this method (prefer SpdyPingIR).
+SpdyFrame* BufferedSpdyFramer::CreatePingFrame(uint32 unique_id,
+ bool is_ack) const {
+ SpdyPingIR ping_ir(unique_id);
+ ping_ir.set_is_ack(is_ack);
+ return spdy_framer_.SerializePing(ping_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer SpdyGoAwayIR).
SpdyFrame* BufferedSpdyFramer::CreateGoAway(
SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) const {
- return spdy_framer_.CreateGoAway(last_accepted_stream_id, status);
+ SpdyGoAwayIR go_ir(last_accepted_stream_id, status, "");
+ return spdy_framer_.SerializeGoAway(go_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer SpdyHeadersIR).
SpdyFrame* BufferedSpdyFramer::CreateHeaders(
SpdyStreamId stream_id,
SpdyControlFlags flags,
const SpdyHeaderBlock* headers) {
- return spdy_framer_.CreateHeaders(stream_id, flags, headers);
+ SpdyHeadersIR headers_ir(stream_id);
+ headers_ir.set_fin((flags & CONTROL_FLAG_FIN) != 0);
+ headers_ir.set_name_value_block(*headers);
+ return spdy_framer_.SerializeHeaders(headers_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer
+// SpdyWindowUpdateIR).
SpdyFrame* BufferedSpdyFramer::CreateWindowUpdate(
SpdyStreamId stream_id,
uint32 delta_window_size) const {
- return spdy_framer_.CreateWindowUpdate(stream_id, delta_window_size);
+ SpdyWindowUpdateIR update_ir(stream_id, delta_window_size);
+ return spdy_framer_.SerializeWindowUpdate(update_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method (prefer SpdyDataIR).
SpdyFrame* BufferedSpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
const char* data,
uint32 len,
SpdyDataFlags flags) {
- return spdy_framer_.CreateDataFrame(stream_id, data, len, flags);
+ SpdyDataIR data_ir(stream_id,
+ base::StringPiece(data, len));
+ data_ir.set_fin((flags & DATA_FLAG_FIN) != 0);
+ return spdy_framer_.SerializeData(data_ir);
+}
+
+// TODO(jgraettinger): Eliminate uses of this method (prefer SpdyPushPromiseIR).
+SpdyFrame* BufferedSpdyFramer::CreatePushPromise(
+ SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock* headers) {
+ SpdyPushPromiseIR push_promise_ir(stream_id, promised_stream_id);
+ push_promise_ir.set_name_value_block(*headers);
+ return spdy_framer_.SerializePushPromise(push_promise_ir);
}
SpdyPriority BufferedSpdyFramer::GetHighestPriority() const {
diff --git a/chromium/net/spdy/buffered_spdy_framer.h b/chromium/net/spdy/buffered_spdy_framer.h
index 3e10561ba93..64aa0f786e0 100644
--- a/chromium/net/spdy/buffered_spdy_framer.h
+++ b/chromium/net/spdy/buffered_spdy_framer.h
@@ -38,7 +38,6 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) = 0;
@@ -78,8 +77,14 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface {
// and validated.
virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) = 0;
+ // Called when a SETTINGS frame is received with the ACK flag set.
+ virtual void OnSettingsAck() {}
+
+ // Called at the completion of parsing SETTINGS id and value tuples.
+ virtual void OnSettingsEnd() {};
+
// Called when a PING frame has been parsed.
- virtual void OnPing(uint32 unique_id) = 0;
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) = 0;
// Called when a RST_STREAM frame has been parsed.
virtual void OnRstStream(SpdyStreamId stream_id,
@@ -95,7 +100,8 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface {
// Called when a PUSH_PROMISE frame has been parsed.
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) = 0;
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) = 0;
protected:
virtual ~BufferedSpdyFramerVisitorInterface() {}
@@ -127,13 +133,10 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional) OVERRIDE;
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE;
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE;
- virtual bool OnCredentialFrameData(const char* frame_data,
- size_t len) OVERRIDE;
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE;
virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) OVERRIDE;
@@ -144,7 +147,9 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
virtual void OnSettings(bool clear_persisted) OVERRIDE;
virtual void OnSetting(
SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE;
- virtual void OnPing(uint32 unique_id) OVERRIDE;
+ virtual void OnSettingsAck() OVERRIDE;
+ virtual void OnSettingsEnd() OVERRIDE;
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE;
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE;
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
@@ -152,10 +157,12 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE;
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE;
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE;
virtual void OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) OVERRIDE;
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE;
// SpdyFramer methods.
size_t ProcessInput(const char* data, size_t len);
@@ -168,7 +175,6 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
SpdyFrame* CreateSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
SpdyControlFlags flags,
const SpdyHeaderBlock* headers);
SpdyFrame* CreateSynReply(SpdyStreamId stream_id,
@@ -177,7 +183,7 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
SpdyFrame* CreateRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) const;
SpdyFrame* CreateSettings(const SettingsMap& values) const;
- SpdyFrame* CreatePingFrame(uint32 unique_id) const;
+ SpdyFrame* CreatePingFrame(uint32 unique_id, bool is_ack) const;
SpdyFrame* CreateGoAway(
SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) const;
@@ -191,6 +197,9 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
const char* data,
uint32 len,
SpdyDataFlags flags);
+ SpdyFrame* CreatePushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock* headers);
// Serialize a frame of unknown type.
SpdySerializedFrame* SerializeFrame(const SpdyFrameIR& frame) {
@@ -247,6 +256,7 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
SpdyFrameType type;
SpdyStreamId stream_id;
SpdyStreamId associated_stream_id;
+ SpdyStreamId promised_stream_id;
SpdyPriority priority;
uint8 credential_slot;
bool fin;
diff --git a/chromium/net/spdy/buffered_spdy_framer_unittest.cc b/chromium/net/spdy/buffered_spdy_framer_unittest.cc
index 9a11fd3d52c..30abe9601d6 100644
--- a/chromium/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/chromium/net/spdy/buffered_spdy_framer_unittest.cc
@@ -20,7 +20,9 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
- header_stream_id_(-1) {
+ push_promise_frame_count_(0),
+ header_stream_id_(-1),
+ promised_stream_id_(-1) {
}
virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {
@@ -39,7 +41,6 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) OVERRIDE {
@@ -88,7 +89,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
setting_count_++;
}
- virtual void OnPing(uint32 unique_id) OVERRIDE {}
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE {
@@ -112,8 +113,18 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
void OnPing(const SpdyFrame& frame) {}
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE {}
+
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {}
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) OVERRIDE {
+ header_stream_id_ = stream_id;
+ EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
+ push_promise_frame_count_++;
+ promised_stream_id_ = promised_stream_id;
+ EXPECT_NE(promised_stream_id_, SpdyFramer::kInvalidStream);
+ headers_ = headers;
+ }
+
void OnCredential(const SpdyFrame& frame) {}
// Convenience function which runs a framer simulation with particular input.
@@ -144,11 +155,14 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
+ int push_promise_frame_count_;
// Header block streaming state:
SpdyStreamId header_stream_id_;
+ SpdyStreamId promised_stream_id_;
- // Headers from OnSyn, OnSynReply and OnHeaders for verification.
+ // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for
+ // verification.
SpdyHeaderBlock headers_;
};
@@ -194,18 +208,14 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
BufferedSpdyFramerTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(BufferedSpdyFramerTest, OnSetting) {
SpdyFramer framer(spdy_version());
- SettingsMap settings;
- settings[SETTINGS_UPLOAD_BANDWIDTH] =
- SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000002);
- settings[SETTINGS_DOWNLOAD_BANDWIDTH] =
- SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000003);
-
- scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ SpdySettingsIR settings_ir;
+ settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2);
+ settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3);
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
TestBufferedSpdyVisitor visitor(spdy_version());
visitor.SimulateInFramer(
@@ -224,7 +234,6 @@ TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) {
framer.CreateSynStream(1, // stream_id
0, // associated_stream_id
1, // priority
- 0, // credential_slot
CONTROL_FLAG_NONE,
&headers));
EXPECT_TRUE(control_frame.get() != NULL);
@@ -237,6 +246,7 @@ TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) {
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.push_promise_frame_count_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
@@ -257,8 +267,14 @@ TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) {
control_frame.get()->size());
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(0, visitor.syn_frame_count_);
- EXPECT_EQ(1, visitor.syn_reply_frame_count_);
- EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.push_promise_frame_count_);
+ if(spdy_version() < SPDY4) {
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ } else {
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ }
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
@@ -281,7 +297,33 @@ TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) {
EXPECT_EQ(0, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.push_promise_frame_count_);
+ EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
+}
+
+TEST_P(BufferedSpdyFramerTest, ReadPushPromiseHeaderBlock) {
+ if (spdy_version() < SPDY4)
+ return;
+ SpdyHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "delta";
+ BufferedSpdyFramer framer(spdy_version(), true);
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreatePushPromise(1, 2, &headers));
+ EXPECT_TRUE(control_frame.get() != NULL);
+
+ TestBufferedSpdyVisitor visitor(spdy_version());
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame.get()->data()),
+ control_frame.get()->size());
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(0, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(1, visitor.push_promise_frame_count_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
+ EXPECT_EQ(1u, visitor.header_stream_id_);
+ EXPECT_EQ(2u, visitor.promised_stream_id_);
}
} // namespace net
diff --git a/chromium/net/spdy/fuzzing/hpack_example_generator.cc b/chromium/net/spdy/fuzzing/hpack_example_generator.cc
new file mode 100644
index 00000000000..c0d495df817
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_example_generator.cc
@@ -0,0 +1,75 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/spdy/fuzzing/hpack_fuzz_util.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_encoder.h"
+
+namespace {
+
+// Target file for generated HPACK header sets.
+const char kFileToWrite[] = "file-to-write";
+
+// Number of header sets to generate.
+const char kExampleCount[] = "example-count";
+
+} // namespace
+
+using net::HpackFuzzUtil;
+using std::map;
+using std::string;
+
+// Generates a configurable number of header sets (using HpackFuzzUtil), and
+// sequentially encodes each header set with an HpackEncoder. Encoded header
+// sets are written to the output file in length-prefixed blocks.
+int main(int argc, char** argv) {
+ base::AtExitManager exit_manager;
+
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ if (!command_line.HasSwitch(kFileToWrite) ||
+ !command_line.HasSwitch(kExampleCount)) {
+ LOG(ERROR) << "Usage: " << argv[0]
+ << " --" << kFileToWrite << "=/path/to/file.out"
+ << " --" << kExampleCount << "=1000";
+ return -1;
+ }
+ string file_to_write = command_line.GetSwitchValueASCII(kFileToWrite);
+
+ int example_count = 0;
+ base::StringToInt(command_line.GetSwitchValueASCII(kExampleCount),
+ &example_count);
+
+ DVLOG(1) << "Writing output to " << file_to_write;
+ base::File file_out(base::FilePath::FromUTF8Unsafe(file_to_write),
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ CHECK(file_out.IsValid()) << file_out.error_details();
+
+ HpackFuzzUtil::GeneratorContext context;
+ HpackFuzzUtil::InitializeGeneratorContext(&context);
+ net::HpackEncoder encoder(net::ObtainHpackHuffmanTable());
+
+ for (int i = 0; i != example_count; ++i) {
+ map<string, string> headers =
+ HpackFuzzUtil::NextGeneratedHeaderSet(&context);
+
+ string buffer;
+ CHECK(encoder.EncodeHeaderSet(headers, &buffer));
+
+ string prefix = HpackFuzzUtil::HeaderBlockPrefix(buffer.size());
+
+ CHECK_LT(0, file_out.WriteAtCurrentPos(prefix.data(), prefix.size()));
+ CHECK_LT(0, file_out.WriteAtCurrentPos(buffer.data(), buffer.size()));
+ }
+ CHECK(file_out.Flush());
+ DVLOG(1) << "Generated " << example_count << " blocks.";
+ return 0;
+}
diff --git a/chromium/net/spdy/fuzzing/hpack_fuzz_mutator.cc b/chromium/net/spdy/fuzzing/hpack_fuzz_mutator.cc
new file mode 100644
index 00000000000..37e10e299ef
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_fuzz_mutator.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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 "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/spdy/fuzzing/hpack_fuzz_util.h"
+
+namespace {
+
+// Specifies a file having HPACK header sets.
+const char kFileToParse[] = "file-to-parse";
+
+// Target file for mutated HPACK header sets.
+const char kFileToWrite[] = "file-to-write";
+
+// Number of bits to flip per 1,024 bytes of input.
+const char kFlipsPerThousand[] = "flips-per-thousand-bytes";
+
+} // namespace
+
+using base::StringPiece;
+using net::HpackFuzzUtil;
+using std::string;
+
+// Reads length-prefixed input blocks, applies a bit-flipping mutation to each
+// block, and writes length-prefixed blocks to the output file. While blocks
+// themselves are mutated, the length-prefixes of written blocks are not.
+int main(int argc, char** argv) {
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ if (!command_line.HasSwitch(kFileToParse) ||
+ !command_line.HasSwitch(kFileToWrite) ||
+ !command_line.HasSwitch(kFlipsPerThousand)) {
+ LOG(ERROR) << "Usage: " << argv[0]
+ << " --" << kFileToParse << "=/path/to/file.in"
+ << " --" << kFileToWrite << "=/path/to/file.out"
+ << " --" << kFlipsPerThousand << "=10";
+ return -1;
+ }
+ string file_to_parse = command_line.GetSwitchValueASCII(kFileToParse);
+ string file_to_write = command_line.GetSwitchValueASCII(kFileToWrite);
+
+ int flip_ratio = 0;
+ CHECK(base::StringToInt(command_line.GetSwitchValueASCII(kFlipsPerThousand),
+ &flip_ratio));
+
+ DVLOG(1) << "Reading input from " << file_to_parse;
+ HpackFuzzUtil::Input input;
+ CHECK(base::ReadFileToString(base::FilePath::FromUTF8Unsafe(file_to_parse),
+ &input.input));
+
+ DVLOG(1) << "Writing output to " << file_to_write;
+ base::File file_out(base::FilePath::FromUTF8Unsafe(file_to_write),
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ CHECK(file_out.IsValid()) << file_out.error_details();
+
+ DVLOG(1) << "Flipping " << flip_ratio << " bits per 1024 input bytes";
+
+ size_t block_count = 0;
+ StringPiece block;
+ while (HpackFuzzUtil::NextHeaderBlock(&input, &block)) {
+ HpackFuzzUtil::FlipBits(
+ reinterpret_cast<uint8*>(const_cast<char*>(block.data())),
+ block.size(),
+ flip_ratio);
+
+ string prefix = HpackFuzzUtil::HeaderBlockPrefix(block.size());
+
+ CHECK_LT(0, file_out.WriteAtCurrentPos(prefix.data(), prefix.size()));
+ CHECK_LT(0, file_out.WriteAtCurrentPos(block.data(), block.size()));
+ ++block_count;
+ }
+ CHECK(file_out.Flush());
+ DVLOG(1) << "Mutated " << block_count << " blocks.";
+ return 0;
+}
diff --git a/chromium/net/spdy/fuzzing/hpack_fuzz_util.cc b/chromium/net/spdy/fuzzing/hpack_fuzz_util.cc
new file mode 100644
index 00000000000..58f2ec1430d
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_fuzz_util.cc
@@ -0,0 +1,189 @@
+// Copyright 2014 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/spdy/fuzzing/hpack_fuzz_util.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/rand_util.h"
+#include "base/sys_byteorder.h"
+#include "net/spdy/hpack_constants.h"
+
+namespace net {
+
+namespace {
+
+// Sampled exponential distribution parameters:
+// Number of headers in each header set.
+const size_t kHeaderCountMean = 7;
+const size_t kHeaderCountMax = 50;
+// Selected index within list of headers.
+const size_t kHeaderIndexMean = 20;
+const size_t kHeaderIndexMax = 200;
+// Approximate distribution of header name lengths.
+const size_t kNameLengthMean = 5;
+const size_t kNameLengthMax = 30;
+// Approximate distribution of header value lengths.
+const size_t kValueLengthMean = 15;
+const size_t kValueLengthMax = 75;
+
+} // namespace
+
+using base::StringPiece;
+using base::RandBytesAsString;
+using std::map;
+using std::string;
+
+HpackFuzzUtil::GeneratorContext::GeneratorContext() {}
+HpackFuzzUtil::GeneratorContext::~GeneratorContext() {}
+
+HpackFuzzUtil::Input::Input() : offset(0) {}
+HpackFuzzUtil::Input::~Input() {}
+
+HpackFuzzUtil::FuzzerContext::FuzzerContext() {}
+HpackFuzzUtil::FuzzerContext::~FuzzerContext() {}
+
+// static
+void HpackFuzzUtil::InitializeGeneratorContext(GeneratorContext* context) {
+ // Seed the generator with common header fixtures.
+ context->names.push_back(":authority");
+ context->names.push_back(":path");
+ context->names.push_back(":status");
+ context->names.push_back("cookie");
+ context->names.push_back("content-type");
+ context->names.push_back("cache-control");
+ context->names.push_back("date");
+ context->names.push_back("user-agent");
+ context->names.push_back("via");
+
+ context->values.push_back("/");
+ context->values.push_back("/index.html");
+ context->values.push_back("200");
+ context->values.push_back("404");
+ context->values.push_back("");
+ context->values.push_back("baz=bing; foo=bar; garbage");
+ context->values.push_back("baz=bing; fizzle=fazzle; garbage");
+ context->values.push_back("rudolph=the-red-nosed-reindeer");
+ context->values.push_back("had=a;very_shiny=nose");
+ context->values.push_back("and\0if\0you\0ever\1saw\0it;");
+ context->values.push_back("u; would=even;say-it\xffglows");
+}
+
+// static
+map<string, string> HpackFuzzUtil::NextGeneratedHeaderSet(
+ GeneratorContext* context) {
+ map<string, string> headers;
+
+ size_t header_count = 1 + SampleExponential(kHeaderCountMean,
+ kHeaderCountMax);
+ for (size_t j = 0; j != header_count; ++j) {
+ size_t name_index = SampleExponential(kHeaderIndexMean,
+ kHeaderIndexMax);
+ size_t value_index = SampleExponential(kHeaderIndexMean,
+ kHeaderIndexMax);
+ string name, value;
+ if (name_index >= context->names.size()) {
+ context->names.push_back(
+ RandBytesAsString(1 + SampleExponential(kNameLengthMean,
+ kNameLengthMax)));
+ name = context->names.back();
+ } else {
+ name = context->names[name_index];
+ }
+ if (value_index >= context->values.size()) {
+ context->values.push_back(
+ RandBytesAsString(1 + SampleExponential(kValueLengthMean,
+ kValueLengthMax)));
+ value = context->values.back();
+ } else {
+ value = context->values[value_index];
+ }
+ headers[name] = value;
+ }
+ return headers;
+}
+
+// static
+size_t HpackFuzzUtil::SampleExponential(size_t mean, size_t sanity_bound) {
+ return std::min<size_t>(-std::log(base::RandDouble()) * mean, sanity_bound);
+}
+
+// static
+bool HpackFuzzUtil::NextHeaderBlock(Input* input,
+ StringPiece* out) {
+ // ClusterFuzz may truncate input files if the fuzzer ran out of allocated
+ // disk space. Be tolerant of these.
+ CHECK_LE(input->offset, input->input.size());
+ if (input->remaining() < sizeof(uint32)) {
+ return false;
+ }
+
+ size_t length = ntohl(*reinterpret_cast<const uint32*>(input->ptr()));
+ input->offset += sizeof(uint32);
+
+ if (input->remaining() < length) {
+ return false;
+ }
+ *out = StringPiece(input->ptr(), length);
+ input->offset += length;
+ return true;
+}
+
+// static
+string HpackFuzzUtil::HeaderBlockPrefix(size_t block_size) {
+ uint32 length = htonl(block_size);
+ return string(reinterpret_cast<char*>(&length), sizeof(uint32));
+}
+
+// static
+void HpackFuzzUtil::InitializeFuzzerContext(FuzzerContext* context) {
+ context->first_stage.reset(new HpackDecoder(ObtainHpackHuffmanTable()));
+ context->second_stage.reset(new HpackEncoder(ObtainHpackHuffmanTable()));
+ context->third_stage.reset(new HpackDecoder(ObtainHpackHuffmanTable()));
+}
+
+// static
+bool HpackFuzzUtil::RunHeaderBlockThroughFuzzerStages(FuzzerContext* context,
+ StringPiece input_block) {
+ // First stage: Decode the input header block. This may fail on invalid input.
+ if (!context->first_stage->HandleControlFrameHeadersData(
+ 1, input_block.data(), input_block.size())) {
+ return false;
+ }
+ if (!context->first_stage->HandleControlFrameHeadersComplete(1)) {
+ return false;
+ }
+ // Second stage: Re-encode the decoded header block. This must succeed.
+ string second_stage_out;
+ CHECK(context->second_stage->EncodeHeaderSet(
+ context->first_stage->decoded_block(), &second_stage_out));
+
+ // Third stage: Expect a decoding of the re-encoded block to succeed, but
+ // don't require it. It's possible for the stage-two encoder to produce an
+ // output which violates decoder size tolerances.
+ if (!context->third_stage->HandleControlFrameHeadersData(
+ 1, second_stage_out.data(), second_stage_out.length())) {
+ return false;
+ }
+ if (!context->third_stage->HandleControlFrameHeadersComplete(1)) {
+ return false;
+ }
+ return true;
+}
+
+// static
+void HpackFuzzUtil::FlipBits(uint8* buffer, size_t buffer_length,
+ size_t flip_per_thousand) {
+ uint64 buffer_bit_length = buffer_length * 8u;
+ uint64 bits_to_flip = flip_per_thousand * (1 + buffer_bit_length / 1024);
+
+ // Iteratively identify & flip offsets in the buffer bit-sequence.
+ for (uint64 i = 0; i != bits_to_flip; ++i) {
+ uint64 bit_offset = base::RandUint64() % buffer_bit_length;
+ buffer[bit_offset / 8u] ^= (1 << (bit_offset % 8u));
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/fuzzing/hpack_fuzz_util.h b/chromium/net/spdy/fuzzing/hpack_fuzz_util.h
new file mode 100644
index 00000000000..18a5f33e4fa
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_fuzz_util.h
@@ -0,0 +1,93 @@
+// Copyright 2014 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_SPDY_FUZZING_HPACK_FUZZ_UTIL_H_
+#define NET_SPDY_FUZZING_HPACK_FUZZ_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_decoder.h"
+#include "net/spdy/hpack_encoder.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackFuzzUtil {
+ public:
+ // A GeneratorContext holds ordered header names & values which are
+ // initially seeded and then expanded with dynamically generated data.
+ struct NET_EXPORT_PRIVATE GeneratorContext {
+ GeneratorContext();
+ ~GeneratorContext();
+ std::vector<std::string> names;
+ std::vector<std::string> values;
+ };
+
+ // Initializes a GeneratorContext with a random seed and name/value fixtures.
+ static void InitializeGeneratorContext(GeneratorContext* context);
+
+ // Generates a header set from the generator context.
+ static std::map<std::string, std::string> NextGeneratedHeaderSet(
+ GeneratorContext* context);
+
+ // Samples a size from the exponential distribution with mean |mean|,
+ // upper-bounded by |sanity_bound|.
+ static size_t SampleExponential(size_t mean, size_t sanity_bound);
+
+ // Holds an input string, and manages an offset into that string.
+ struct NET_EXPORT_PRIVATE Input {
+ Input(); // Initializes |offset| to zero.
+ ~Input();
+
+ size_t remaining() {
+ return input.size() - offset;
+ }
+ const char* ptr() {
+ return input.data() + offset;
+ }
+
+ std::string input;
+ size_t offset;
+ };
+
+ // Returns true if the next header block was set at |out|. Returns
+ // false if no input header blocks remain.
+ static bool NextHeaderBlock(Input* input, base::StringPiece* out);
+
+ // Returns the serialized header block length prefix for a block of
+ // |block_size| bytes.
+ static std::string HeaderBlockPrefix(size_t block_size);
+
+ // A FuzzerContext holds fuzzer input, as well as each of the decoder and
+ // encoder stages which fuzzed header blocks are processed through.
+ struct NET_EXPORT_PRIVATE FuzzerContext {
+ FuzzerContext();
+ ~FuzzerContext();
+ scoped_ptr<HpackDecoder> first_stage;
+ scoped_ptr<HpackEncoder> second_stage;
+ scoped_ptr<HpackDecoder> third_stage;
+ };
+
+ static void InitializeFuzzerContext(FuzzerContext* context);
+
+ // Runs |input_block| through |first_stage| and, iff that succeeds,
+ // |second_stage| and |third_stage| as well. Returns whether all stages
+ // processed the input without error.
+ static bool RunHeaderBlockThroughFuzzerStages(FuzzerContext* context,
+ base::StringPiece input_block);
+
+ // Flips random bits within |buffer|. The total number of flips is
+ // |flip_per_thousand| bits for every 1,024 bytes of |buffer_length|,
+ // rounding up.
+ static void FlipBits(uint8* buffer,
+ size_t buffer_length,
+ size_t flip_per_thousand);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_FUZZING_HPACK_FUZZ_UTIL_H_
diff --git a/chromium/net/spdy/fuzzing/hpack_fuzz_util_test.cc b/chromium/net/spdy/fuzzing/hpack_fuzz_util_test.cc
new file mode 100644
index 00000000000..8956684ce96
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_fuzz_util_test.cc
@@ -0,0 +1,151 @@
+// Copyright 2014 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/spdy/fuzzing/hpack_fuzz_util.h"
+
+#include <map>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/path_service.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using base::StringPiece;
+using std::map;
+using std::string;
+using test::a2b_hex;
+
+TEST(HpackFuzzUtilTest, GeneratorContextInitialization) {
+ HpackFuzzUtil::GeneratorContext context;
+ HpackFuzzUtil::InitializeGeneratorContext(&context);
+
+ // Context was seeded with initial name & value fixtures.
+ EXPECT_LT(0u, context.names.size());
+ EXPECT_LT(0u, context.values.size());
+}
+
+TEST(HpackFuzzUtil, GeneratorContextExpansion) {
+ HpackFuzzUtil::GeneratorContext context;
+
+ map<string, string> headers = HpackFuzzUtil::NextGeneratedHeaderSet(&context);
+
+ // Headers were generated, and the generator context was expanded.
+ EXPECT_LT(0u, headers.size());
+ EXPECT_LT(0u, context.names.size());
+ EXPECT_LT(0u, context.values.size());
+}
+
+// TODO(jgraettinger): A better test would mock a random generator and
+// evaluate SampleExponential along fixed points of the [0,1] domain.
+TEST(HpackFuzzUtilTest, SampleExponentialRegression) {
+ // TODO(jgraettinger): Upstream uses a seeded random generator here to pin
+ // the behavior of SampleExponential. Chromium's random generation utilities
+ // are strongly secure, but provide no way to seed the generator.
+ for (size_t i = 0; i != 100; ++i) {
+ EXPECT_GE(30u, HpackFuzzUtil::SampleExponential(10, 30));
+ }
+}
+
+TEST(HpackFuzzUtilTest, ParsesSequenceOfHeaderBlocks) {
+ char fixture[] =
+ "\x00\x00\x00\x05""aaaaa"
+ "\x00\x00\x00\x04""bbbb"
+ "\x00\x00\x00\x03""ccc"
+ "\x00\x00\x00\x02""dd"
+ "\x00\x00\x00\x01""e"
+ "\x00\x00\x00\x00"""
+ "\x00\x00\x00\x03""fin";
+
+ HpackFuzzUtil::Input input;
+ input.input.assign(fixture, arraysize(fixture) - 1);
+
+ StringPiece block;
+
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("aaaaa", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("bbbb", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("ccc", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("dd", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("e", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("", block);
+ EXPECT_TRUE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+ EXPECT_EQ("fin", block);
+ EXPECT_FALSE(HpackFuzzUtil::NextHeaderBlock(&input, &block));
+}
+
+TEST(HpackFuzzUtilTest, SerializedHeaderBlockPrefixes) {
+ EXPECT_EQ(string("\x00\x00\x00\x00", 4), HpackFuzzUtil::HeaderBlockPrefix(0));
+ EXPECT_EQ(string("\x00\x00\x00\x05", 4), HpackFuzzUtil::HeaderBlockPrefix(5));
+ EXPECT_EQ(string("\x4f\xb3\x0a\x91", 4),
+ HpackFuzzUtil::HeaderBlockPrefix(1337133713));
+}
+
+TEST(HpackFuzzUtilTest, PassValidInputThroughAllStages) {
+ // Example lifted from HpackDecoderTest.SectionD3RequestHuffmanExamples.
+ string input = a2b_hex("828786448ce7cf9bebe89b6fb16fa9b6ff");
+
+ HpackFuzzUtil::FuzzerContext context;
+ HpackFuzzUtil::InitializeFuzzerContext(&context);
+
+ EXPECT_TRUE(
+ HpackFuzzUtil::RunHeaderBlockThroughFuzzerStages(&context, input));
+
+ std::map<string, string> expect;
+ expect[":method"] = "GET";
+ expect[":scheme"] = "http";
+ expect[":path"] = "/";
+ expect[":authority"] = "www.example.com";
+ EXPECT_EQ(expect, context.third_stage->decoded_block());
+}
+
+TEST(HpackFuzzUtilTest, ValidFuzzExamplesRegressionTest) {
+ base::FilePath source_root;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &source_root));
+
+ // Load the example fixtures versioned with the source tree.
+ HpackFuzzUtil::Input input;
+ ASSERT_TRUE(base::ReadFileToString(
+ source_root.Append(FILE_PATH_LITERAL("net"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("spdy_tests"))
+ .Append(FILE_PATH_LITERAL("examples_07.hpack")),
+ &input.input));
+
+ HpackFuzzUtil::FuzzerContext context;
+ HpackFuzzUtil::InitializeFuzzerContext(&context);
+
+ StringPiece block;
+ while (HpackFuzzUtil::NextHeaderBlock(&input, &block)) {
+ // As these are valid examples, all fuzz stages should succeed.
+ EXPECT_TRUE(HpackFuzzUtil::RunHeaderBlockThroughFuzzerStages(
+ &context, block));
+ }
+}
+
+TEST(HpackFuzzUtilTest, FlipBitsMutatesBuffer) {
+ char buffer[] = "testbuffer1234567890";
+ string unmodified(buffer, arraysize(buffer) - 1);
+
+ EXPECT_EQ(unmodified, buffer);
+ HpackFuzzUtil::FlipBits(reinterpret_cast<uint8*>(buffer),
+ arraysize(buffer) - 1,
+ 1);
+ EXPECT_NE(unmodified, buffer);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/fuzzing/hpack_fuzz_wrapper.cc b/chromium/net/spdy/fuzzing/hpack_fuzz_wrapper.cc
new file mode 100644
index 00000000000..b9d7c3d40d7
--- /dev/null
+++ b/chromium/net/spdy/fuzzing/hpack_fuzz_wrapper.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "net/spdy/fuzzing/hpack_fuzz_util.h"
+
+namespace {
+
+// Specifies a file having HPACK header sets.
+const char kFileToParse[] = "file-to-parse";
+
+} // namespace
+
+using base::StringPiece;
+using net::HpackFuzzUtil;
+using std::string;
+
+// Sequentially runs each given length-prefixed header block through
+// decoding and encoding fuzzing stages (using HpackFuzzUtil).
+int main(int argc, char** argv) {
+ base::AtExitManager exit_manager;
+
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ if (!command_line.HasSwitch(kFileToParse)) {
+ LOG(ERROR) << "Usage: " << argv[0]
+ << " --" << kFileToParse << "=/path/to/file.in";
+ return -1;
+ }
+ string file_to_parse = command_line.GetSwitchValueASCII(kFileToParse);
+
+ // ClusterFuzz may invoke as --file-to-parse="". Don't crash in this case.
+ if (file_to_parse.empty()) {
+ LOG(WARNING) << "Empty file to parse given. Doing nothing.";
+ return 0;
+ }
+
+ DVLOG(1) << "Reading input from " << file_to_parse;
+ HpackFuzzUtil::Input input;
+ CHECK(base::ReadFileToString(base::FilePath::FromUTF8Unsafe(file_to_parse),
+ &input.input));
+
+ HpackFuzzUtil::FuzzerContext context;
+ HpackFuzzUtil::InitializeFuzzerContext(&context);
+
+ size_t block_count = 0;
+ StringPiece block;
+ while (HpackFuzzUtil::NextHeaderBlock(&input, &block)) {
+ HpackFuzzUtil::RunHeaderBlockThroughFuzzerStages(&context, block);
+ ++block_count;
+ }
+ DVLOG(1) << "Fuzzed " << block_count << " blocks.";
+ return 0;
+}
diff --git a/chromium/net/spdy/hpack_constants.cc b/chromium/net/spdy/hpack_constants.cc
new file mode 100644
index 00000000000..1468f91e4c9
--- /dev/null
+++ b/chromium/net/spdy/hpack_constants.cc
@@ -0,0 +1,331 @@
+// Copyright 2014 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/spdy/hpack_constants.h"
+
+#include <bitset>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "net/spdy/hpack_huffman_table.h"
+
+namespace net {
+
+namespace {
+
+uint32 bits32(const std::string& bitstring) {
+ return std::bitset<32>(bitstring).to_ulong();
+}
+
+// SharedHpackHuffmanTable is a Singleton wrapping a HpackHuffmanTable
+// instance initialized with |kHpackHuffmanCode|.
+struct SharedHpackHuffmanTable {
+ public:
+ SharedHpackHuffmanTable() {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ scoped_ptr<HpackHuffmanTable> mutable_table(new HpackHuffmanTable());
+ CHECK(mutable_table->Initialize(&code[0], code.size()));
+ CHECK(mutable_table->IsInitialized());
+ table.reset(mutable_table.release());
+ }
+
+ static SharedHpackHuffmanTable* GetInstance() {
+ return Singleton<SharedHpackHuffmanTable>::get();
+ }
+
+ scoped_ptr<const HpackHuffmanTable> table;
+};
+
+} // namespace
+
+// Produced by applying the python program [1] to tables provided by [2].
+// [1]
+// import re, sys
+// count = 0
+// for l in sys.stdin:
+// m = re.match(
+// r"^ +('.+'|EOS)? \( *(\d+)\) \|([10\|]+) +\w+ \[ ?(\d+)\]", l)
+// if m:
+// g = m.groups()
+// print(' {0b%s, %02s, %03s},%s' % (
+// g[2].replace('|','').ljust(32,'0'), g[3], g[1],
+// (' // %s' % g[0]) if g[0] else ''))
+// count += 1
+// print("Total: %s" % count)
+//
+// [2] http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+// HpackHuffmanSymbol entries are initialized as {code, length, id}.
+// Codes are specified in the |length| most-significant bits of |code|.
+std::vector<HpackHuffmanSymbol> HpackHuffmanCode() {
+ const HpackHuffmanSymbol kHpackHuffmanCode[] = {
+ {bits32("11111111111111111110111010000000"), 26, 0},
+ {bits32("11111111111111111110111011000000"), 26, 1},
+ {bits32("11111111111111111110111100000000"), 26, 2},
+ {bits32("11111111111111111110111101000000"), 26, 3},
+ {bits32("11111111111111111110111110000000"), 26, 4},
+ {bits32("11111111111111111110111111000000"), 26, 5},
+ {bits32("11111111111111111111000000000000"), 26, 6},
+ {bits32("11111111111111111111000001000000"), 26, 7},
+ {bits32("11111111111111111111000010000000"), 26, 8},
+ {bits32("11111111111111111111000011000000"), 26, 9},
+ {bits32("11111111111111111111000100000000"), 26, 10},
+ {bits32("11111111111111111111000101000000"), 26, 11},
+ {bits32("11111111111111111111000110000000"), 26, 12},
+ {bits32("11111111111111111111000111000000"), 26, 13},
+ {bits32("11111111111111111111001000000000"), 26, 14},
+ {bits32("11111111111111111111001001000000"), 26, 15},
+ {bits32("11111111111111111111001010000000"), 26, 16},
+ {bits32("11111111111111111111001011000000"), 26, 17},
+ {bits32("11111111111111111111001100000000"), 26, 18},
+ {bits32("11111111111111111111001101000000"), 26, 19},
+ {bits32("11111111111111111111001110000000"), 26, 20},
+ {bits32("11111111111111111111001111000000"), 26, 21},
+ {bits32("11111111111111111111010000000000"), 26, 22},
+ {bits32("11111111111111111111010001000000"), 26, 23},
+ {bits32("11111111111111111111010010000000"), 26, 24},
+ {bits32("11111111111111111111010011000000"), 26, 25},
+ {bits32("11111111111111111111010100000000"), 26, 26},
+ {bits32("11111111111111111111010101000000"), 26, 27},
+ {bits32("11111111111111111111010110000000"), 26, 28},
+ {bits32("11111111111111111111010111000000"), 26, 29},
+ {bits32("11111111111111111111011000000000"), 26, 30},
+ {bits32("11111111111111111111011001000000"), 26, 31},
+ {bits32("00110000000000000000000000000000"), 5, 32}, // ' '
+ {bits32("11111111111000000000000000000000"), 13, 33}, // '!'
+ {bits32("11111000000000000000000000000000"), 9, 34}, // '"'
+ {bits32("11111111111100000000000000000000"), 14, 35}, // '#'
+ {bits32("11111111111110000000000000000000"), 15, 36}, // '$'
+ {bits32("01111000000000000000000000000000"), 6, 37}, // '%'
+ {bits32("11001000000000000000000000000000"), 7, 38}, // '&'
+ {bits32("11111111111010000000000000000000"), 13, 39}, // '''
+ {bits32("11111110100000000000000000000000"), 10, 40}, // '('
+ {bits32("11111000100000000000000000000000"), 9, 41}, // ')'
+ {bits32("11111110110000000000000000000000"), 10, 42}, // '*'
+ {bits32("11111111000000000000000000000000"), 10, 43}, // '+'
+ {bits32("11001010000000000000000000000000"), 7, 44}, // ','
+ {bits32("11001100000000000000000000000000"), 7, 45}, // '-'
+ {bits32("01111100000000000000000000000000"), 6, 46}, // '.'
+ {bits32("00111000000000000000000000000000"), 5, 47}, // '/'
+ {bits32("00000000000000000000000000000000"), 4, 48}, // '0'
+ {bits32("00010000000000000000000000000000"), 4, 49}, // '1'
+ {bits32("00100000000000000000000000000000"), 4, 50}, // '2'
+ {bits32("01000000000000000000000000000000"), 5, 51}, // '3'
+ {bits32("10000000000000000000000000000000"), 6, 52}, // '4'
+ {bits32("10000100000000000000000000000000"), 6, 53}, // '5'
+ {bits32("10001000000000000000000000000000"), 6, 54}, // '6'
+ {bits32("10001100000000000000000000000000"), 6, 55}, // '7'
+ {bits32("10010000000000000000000000000000"), 6, 56}, // '8'
+ {bits32("10010100000000000000000000000000"), 6, 57}, // '9'
+ {bits32("10011000000000000000000000000000"), 6, 58}, // ':'
+ {bits32("11101100000000000000000000000000"), 8, 59}, // ';'
+ {bits32("11111111111111100000000000000000"), 17, 60}, // '<'
+ {bits32("10011100000000000000000000000000"), 6, 61}, // '='
+ {bits32("11111111111110100000000000000000"), 15, 62}, // '>'
+ {bits32("11111111010000000000000000000000"), 10, 63}, // '?'
+ {bits32("11111111111111000000000000000000"), 15, 64}, // '@'
+ {bits32("11001110000000000000000000000000"), 7, 65}, // 'A'
+ {bits32("11101101000000000000000000000000"), 8, 66}, // 'B'
+ {bits32("11101110000000000000000000000000"), 8, 67}, // 'C'
+ {bits32("11010000000000000000000000000000"), 7, 68}, // 'D'
+ {bits32("11101111000000000000000000000000"), 8, 69}, // 'E'
+ {bits32("11010010000000000000000000000000"), 7, 70}, // 'F'
+ {bits32("11010100000000000000000000000000"), 7, 71}, // 'G'
+ {bits32("11111001000000000000000000000000"), 9, 72}, // 'H'
+ {bits32("11110000000000000000000000000000"), 8, 73}, // 'I'
+ {bits32("11111001100000000000000000000000"), 9, 74}, // 'J'
+ {bits32("11111010000000000000000000000000"), 9, 75}, // 'K'
+ {bits32("11111010100000000000000000000000"), 9, 76}, // 'L'
+ {bits32("11010110000000000000000000000000"), 7, 77}, // 'M'
+ {bits32("11011000000000000000000000000000"), 7, 78}, // 'N'
+ {bits32("11110001000000000000000000000000"), 8, 79}, // 'O'
+ {bits32("11110010000000000000000000000000"), 8, 80}, // 'P'
+ {bits32("11111011000000000000000000000000"), 9, 81}, // 'Q'
+ {bits32("11111011100000000000000000000000"), 9, 82}, // 'R'
+ {bits32("11011010000000000000000000000000"), 7, 83}, // 'S'
+ {bits32("10100000000000000000000000000000"), 6, 84}, // 'T'
+ {bits32("11110011000000000000000000000000"), 8, 85}, // 'U'
+ {bits32("11111100000000000000000000000000"), 9, 86}, // 'V'
+ {bits32("11111100100000000000000000000000"), 9, 87}, // 'W'
+ {bits32("11110100000000000000000000000000"), 8, 88}, // 'X'
+ {bits32("11111101000000000000000000000000"), 9, 89}, // 'Y'
+ {bits32("11111101100000000000000000000000"), 9, 90}, // 'Z'
+ {bits32("11111111100000000000000000000000"), 11, 91}, // '['
+ {bits32("11111111111111111111011010000000"), 26, 92}, // '\'
+ {bits32("11111111101000000000000000000000"), 11, 93}, // ']'
+ {bits32("11111111111101000000000000000000"), 14, 94}, // '^'
+ {bits32("11011100000000000000000000000000"), 7, 95}, // '_'
+ {bits32("11111111111111111000000000000000"), 18, 96}, // '`'
+ {bits32("01001000000000000000000000000000"), 5, 97}, // 'a'
+ {bits32("11011110000000000000000000000000"), 7, 98}, // 'b'
+ {bits32("01010000000000000000000000000000"), 5, 99}, // 'c'
+ {bits32("10100100000000000000000000000000"), 6, 100}, // 'd'
+ {bits32("01011000000000000000000000000000"), 5, 101}, // 'e'
+ {bits32("11100000000000000000000000000000"), 7, 102}, // 'f'
+ {bits32("10101000000000000000000000000000"), 6, 103}, // 'g'
+ {bits32("10101100000000000000000000000000"), 6, 104}, // 'h'
+ {bits32("01100000000000000000000000000000"), 5, 105}, // 'i'
+ {bits32("11110101000000000000000000000000"), 8, 106}, // 'j'
+ {bits32("11110110000000000000000000000000"), 8, 107}, // 'k'
+ {bits32("10110000000000000000000000000000"), 6, 108}, // 'l'
+ {bits32("10110100000000000000000000000000"), 6, 109}, // 'm'
+ {bits32("10111000000000000000000000000000"), 6, 110}, // 'n'
+ {bits32("01101000000000000000000000000000"), 5, 111}, // 'o'
+ {bits32("10111100000000000000000000000000"), 6, 112}, // 'p'
+ {bits32("11111110000000000000000000000000"), 9, 113}, // 'q'
+ {bits32("11000000000000000000000000000000"), 6, 114}, // 'r'
+ {bits32("11000100000000000000000000000000"), 6, 115}, // 's'
+ {bits32("01110000000000000000000000000000"), 5, 116}, // 't'
+ {bits32("11100010000000000000000000000000"), 7, 117}, // 'u'
+ {bits32("11100100000000000000000000000000"), 7, 118}, // 'v'
+ {bits32("11100110000000000000000000000000"), 7, 119}, // 'w'
+ {bits32("11101000000000000000000000000000"), 7, 120}, // 'x'
+ {bits32("11101010000000000000000000000000"), 7, 121}, // 'y'
+ {bits32("11110111000000000000000000000000"), 8, 122}, // 'z'
+ {bits32("11111111111111101000000000000000"), 17, 123}, // '{'
+ {bits32("11111111110000000000000000000000"), 12, 124}, // '|'
+ {bits32("11111111111111110000000000000000"), 17, 125}, // '}'
+ {bits32("11111111110100000000000000000000"), 12, 126}, // '~'
+ {bits32("11111111111111111111011011000000"), 26, 127},
+ {bits32("11111111111111111111011100000000"), 26, 128},
+ {bits32("11111111111111111111011101000000"), 26, 129},
+ {bits32("11111111111111111111011110000000"), 26, 130},
+ {bits32("11111111111111111111011111000000"), 26, 131},
+ {bits32("11111111111111111111100000000000"), 26, 132},
+ {bits32("11111111111111111111100001000000"), 26, 133},
+ {bits32("11111111111111111111100010000000"), 26, 134},
+ {bits32("11111111111111111111100011000000"), 26, 135},
+ {bits32("11111111111111111111100100000000"), 26, 136},
+ {bits32("11111111111111111111100101000000"), 26, 137},
+ {bits32("11111111111111111111100110000000"), 26, 138},
+ {bits32("11111111111111111111100111000000"), 26, 139},
+ {bits32("11111111111111111111101000000000"), 26, 140},
+ {bits32("11111111111111111111101001000000"), 26, 141},
+ {bits32("11111111111111111111101010000000"), 26, 142},
+ {bits32("11111111111111111111101011000000"), 26, 143},
+ {bits32("11111111111111111111101100000000"), 26, 144},
+ {bits32("11111111111111111111101101000000"), 26, 145},
+ {bits32("11111111111111111111101110000000"), 26, 146},
+ {bits32("11111111111111111111101111000000"), 26, 147},
+ {bits32("11111111111111111111110000000000"), 26, 148},
+ {bits32("11111111111111111111110001000000"), 26, 149},
+ {bits32("11111111111111111111110010000000"), 26, 150},
+ {bits32("11111111111111111111110011000000"), 26, 151},
+ {bits32("11111111111111111111110100000000"), 26, 152},
+ {bits32("11111111111111111111110101000000"), 26, 153},
+ {bits32("11111111111111111111110110000000"), 26, 154},
+ {bits32("11111111111111111111110111000000"), 26, 155},
+ {bits32("11111111111111111111111000000000"), 26, 156},
+ {bits32("11111111111111111111111001000000"), 26, 157},
+ {bits32("11111111111111111111111010000000"), 26, 158},
+ {bits32("11111111111111111111111011000000"), 26, 159},
+ {bits32("11111111111111111111111100000000"), 26, 160},
+ {bits32("11111111111111111111111101000000"), 26, 161},
+ {bits32("11111111111111111111111110000000"), 26, 162},
+ {bits32("11111111111111111111111111000000"), 26, 163},
+ {bits32("11111111111111111100000000000000"), 25, 164},
+ {bits32("11111111111111111100000010000000"), 25, 165},
+ {bits32("11111111111111111100000100000000"), 25, 166},
+ {bits32("11111111111111111100000110000000"), 25, 167},
+ {bits32("11111111111111111100001000000000"), 25, 168},
+ {bits32("11111111111111111100001010000000"), 25, 169},
+ {bits32("11111111111111111100001100000000"), 25, 170},
+ {bits32("11111111111111111100001110000000"), 25, 171},
+ {bits32("11111111111111111100010000000000"), 25, 172},
+ {bits32("11111111111111111100010010000000"), 25, 173},
+ {bits32("11111111111111111100010100000000"), 25, 174},
+ {bits32("11111111111111111100010110000000"), 25, 175},
+ {bits32("11111111111111111100011000000000"), 25, 176},
+ {bits32("11111111111111111100011010000000"), 25, 177},
+ {bits32("11111111111111111100011100000000"), 25, 178},
+ {bits32("11111111111111111100011110000000"), 25, 179},
+ {bits32("11111111111111111100100000000000"), 25, 180},
+ {bits32("11111111111111111100100010000000"), 25, 181},
+ {bits32("11111111111111111100100100000000"), 25, 182},
+ {bits32("11111111111111111100100110000000"), 25, 183},
+ {bits32("11111111111111111100101000000000"), 25, 184},
+ {bits32("11111111111111111100101010000000"), 25, 185},
+ {bits32("11111111111111111100101100000000"), 25, 186},
+ {bits32("11111111111111111100101110000000"), 25, 187},
+ {bits32("11111111111111111100110000000000"), 25, 188},
+ {bits32("11111111111111111100110010000000"), 25, 189},
+ {bits32("11111111111111111100110100000000"), 25, 190},
+ {bits32("11111111111111111100110110000000"), 25, 191},
+ {bits32("11111111111111111100111000000000"), 25, 192},
+ {bits32("11111111111111111100111010000000"), 25, 193},
+ {bits32("11111111111111111100111100000000"), 25, 194},
+ {bits32("11111111111111111100111110000000"), 25, 195},
+ {bits32("11111111111111111101000000000000"), 25, 196},
+ {bits32("11111111111111111101000010000000"), 25, 197},
+ {bits32("11111111111111111101000100000000"), 25, 198},
+ {bits32("11111111111111111101000110000000"), 25, 199},
+ {bits32("11111111111111111101001000000000"), 25, 200},
+ {bits32("11111111111111111101001010000000"), 25, 201},
+ {bits32("11111111111111111101001100000000"), 25, 202},
+ {bits32("11111111111111111101001110000000"), 25, 203},
+ {bits32("11111111111111111101010000000000"), 25, 204},
+ {bits32("11111111111111111101010010000000"), 25, 205},
+ {bits32("11111111111111111101010100000000"), 25, 206},
+ {bits32("11111111111111111101010110000000"), 25, 207},
+ {bits32("11111111111111111101011000000000"), 25, 208},
+ {bits32("11111111111111111101011010000000"), 25, 209},
+ {bits32("11111111111111111101011100000000"), 25, 210},
+ {bits32("11111111111111111101011110000000"), 25, 211},
+ {bits32("11111111111111111101100000000000"), 25, 212},
+ {bits32("11111111111111111101100010000000"), 25, 213},
+ {bits32("11111111111111111101100100000000"), 25, 214},
+ {bits32("11111111111111111101100110000000"), 25, 215},
+ {bits32("11111111111111111101101000000000"), 25, 216},
+ {bits32("11111111111111111101101010000000"), 25, 217},
+ {bits32("11111111111111111101101100000000"), 25, 218},
+ {bits32("11111111111111111101101110000000"), 25, 219},
+ {bits32("11111111111111111101110000000000"), 25, 220},
+ {bits32("11111111111111111101110010000000"), 25, 221},
+ {bits32("11111111111111111101110100000000"), 25, 222},
+ {bits32("11111111111111111101110110000000"), 25, 223},
+ {bits32("11111111111111111101111000000000"), 25, 224},
+ {bits32("11111111111111111101111010000000"), 25, 225},
+ {bits32("11111111111111111101111100000000"), 25, 226},
+ {bits32("11111111111111111101111110000000"), 25, 227},
+ {bits32("11111111111111111110000000000000"), 25, 228},
+ {bits32("11111111111111111110000010000000"), 25, 229},
+ {bits32("11111111111111111110000100000000"), 25, 230},
+ {bits32("11111111111111111110000110000000"), 25, 231},
+ {bits32("11111111111111111110001000000000"), 25, 232},
+ {bits32("11111111111111111110001010000000"), 25, 233},
+ {bits32("11111111111111111110001100000000"), 25, 234},
+ {bits32("11111111111111111110001110000000"), 25, 235},
+ {bits32("11111111111111111110010000000000"), 25, 236},
+ {bits32("11111111111111111110010010000000"), 25, 237},
+ {bits32("11111111111111111110010100000000"), 25, 238},
+ {bits32("11111111111111111110010110000000"), 25, 239},
+ {bits32("11111111111111111110011000000000"), 25, 240},
+ {bits32("11111111111111111110011010000000"), 25, 241},
+ {bits32("11111111111111111110011100000000"), 25, 242},
+ {bits32("11111111111111111110011110000000"), 25, 243},
+ {bits32("11111111111111111110100000000000"), 25, 244},
+ {bits32("11111111111111111110100010000000"), 25, 245},
+ {bits32("11111111111111111110100100000000"), 25, 246},
+ {bits32("11111111111111111110100110000000"), 25, 247},
+ {bits32("11111111111111111110101000000000"), 25, 248},
+ {bits32("11111111111111111110101010000000"), 25, 249},
+ {bits32("11111111111111111110101100000000"), 25, 250},
+ {bits32("11111111111111111110101110000000"), 25, 251},
+ {bits32("11111111111111111110110000000000"), 25, 252},
+ {bits32("11111111111111111110110010000000"), 25, 253},
+ {bits32("11111111111111111110110100000000"), 25, 254},
+ {bits32("11111111111111111110110110000000"), 25, 255},
+ {bits32("11111111111111111110111000000000"), 25, 256}, // EOS
+ };
+ return std::vector<HpackHuffmanSymbol>(
+ kHpackHuffmanCode,
+ kHpackHuffmanCode + arraysize(kHpackHuffmanCode));
+}
+
+const HpackHuffmanTable& ObtainHpackHuffmanTable() {
+ return *SharedHpackHuffmanTable::GetInstance()->table;
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_constants.h b/chromium/net/spdy/hpack_constants.h
new file mode 100644
index 00000000000..7b3051631fa
--- /dev/null
+++ b/chromium/net/spdy/hpack_constants.h
@@ -0,0 +1,86 @@
+// Copyright 2014 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_SPDY_HPACK_CONSTANTS_H_
+#define NET_SPDY_HPACK_CONSTANTS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+// An HpackPrefix signifies |bits| stored in the top |bit_size| bits
+// of an octet.
+struct HpackPrefix {
+ uint8 bits;
+ size_t bit_size;
+};
+
+// Represents a symbol and its Huffman code (stored in most-significant bits).
+struct HpackHuffmanSymbol {
+ uint32 code;
+ uint8 length;
+ uint16 id;
+};
+
+class HpackHuffmanTable;
+
+const uint32 kDefaultHeaderTableSizeSetting = 4096;
+
+// Largest string literal an HpackDecoder/HpackEncoder will attempt to process
+// before returning an error.
+const uint32 kDefaultMaxStringLiteralSize = 16 * 1024;
+
+// Maximum amount of encoded header buffer HpackDecoder will retain before
+// returning an error.
+// TODO(jgraettinger): Remove with SpdyHeadersHandlerInterface switch.
+const uint32 kMaxDecodeBufferSize = 32 * 1024;
+
+// 4.1.2: Flag for a string literal that is stored unmodified (i.e.,
+// without Huffman encoding).
+const HpackPrefix kStringLiteralIdentityEncoded = { 0x0, 1 };
+
+// 4.1.2: Flag for a Huffman-coded string literal.
+const HpackPrefix kStringLiteralHuffmanEncoded = { 0x1, 1 };
+
+// 4.2: Opcode for an indexed header field.
+const HpackPrefix kIndexedOpcode = { 0x1, 1 };
+
+// 4.3.1: Opcode for a literal header field with incremental indexing.
+const HpackPrefix kLiteralIncrementalIndexOpcode = { 0x1, 2 };
+
+// 4.3.2: Opcode for a literal header field without indexing.
+const HpackPrefix kLiteralNoIndexOpcode = { 0x0, 4 };
+
+// 4.3.3: Opcode for a literal header field which is never indexed.
+const HpackPrefix kLiteralNeverIndexOpcode = { 0x1, 4 };
+
+// 4.4: Opcode for an encoding context update.
+const HpackPrefix kEncodingContextOpcode = { 0x1, 3 };
+
+// 4.4: Flag following an |kEncodingContextOpcode|, which indicates
+// the reference set should be cleared.
+const HpackPrefix kEncodingContextEmptyReferenceSet = { 0x10, 5 };
+
+// 4.4: Flag following an |kEncodingContextOpcode|, which indicates
+// the encoder is using a new maximum headers table size. Begins a
+// varint-encoded table size with a 4-bit prefix.
+const HpackPrefix kEncodingContextNewMaximumSize = { 0x0, 1 };
+
+// Returns symbol code table from "Appendix C. Huffman Codes".
+NET_EXPORT_PRIVATE std::vector<HpackHuffmanSymbol> HpackHuffmanCode();
+
+// Returns a HpackHuffmanTable instance initialized with |kHpackHuffmanCode|.
+// The instance is read-only, has static lifetime, and is safe to share amoung
+// threads. This function is thread-safe.
+NET_EXPORT_PRIVATE const HpackHuffmanTable& ObtainHpackHuffmanTable();
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_CONSTANTS_H_
diff --git a/chromium/net/spdy/hpack_decoder.cc b/chromium/net/spdy/hpack_decoder.cc
new file mode 100644
index 00000000000..abd579ad007
--- /dev/null
+++ b/chromium/net/spdy/hpack_decoder.cc
@@ -0,0 +1,240 @@
+// Copyright 2014 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/spdy/hpack_decoder.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_output_stream.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+
+namespace {
+
+const uint8 kNoState = 0;
+// Set on entries added to the reference set during this decoding.
+const uint8 kReferencedThisEncoding = 1;
+
+const char kCookieKey[] = "cookie";
+
+} // namespace
+
+HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
+ : max_string_literal_size_(kDefaultMaxStringLiteralSize),
+ huffman_table_(table) {}
+
+HpackDecoder::~HpackDecoder() {}
+
+bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id,
+ const char* headers_data,
+ size_t headers_data_length) {
+ decoded_block_.clear();
+
+ size_t new_size = headers_block_buffer_.size() + headers_data_length;
+ if (new_size > kMaxDecodeBufferSize) {
+ return false;
+ }
+ headers_block_buffer_.insert(headers_block_buffer_.end(),
+ headers_data,
+ headers_data + headers_data_length);
+ return true;
+}
+
+bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) {
+ HpackInputStream input_stream(max_string_literal_size_,
+ headers_block_buffer_);
+ while (input_stream.HasMoreData()) {
+ if (!DecodeNextOpcode(&input_stream)) {
+ headers_block_buffer_.clear();
+ return false;
+ }
+ }
+ headers_block_buffer_.clear();
+
+ // Emit everything in the reference set that hasn't already been emitted.
+ // Also clear entry state for the next decoded headers block.
+ // TODO(jgraettinger): We may need to revisit the order in which headers
+ // are emitted (b/14051713).
+ for (HpackHeaderTable::OrderedEntrySet::const_iterator it =
+ header_table_.reference_set().begin();
+ it != header_table_.reference_set().end(); ++it) {
+ HpackEntry* entry = *it;
+
+ if (entry->state() == kNoState) {
+ HandleHeaderRepresentation(entry->name(), entry->value());
+ } else {
+ entry->set_state(kNoState);
+ }
+ }
+ // Emit the Cookie header, if any crumbles were encountered.
+ if (!cookie_value_.empty()) {
+ decoded_block_[kCookieKey] = cookie_value_;
+ cookie_value_.clear();
+ }
+ return true;
+}
+
+void HpackDecoder::HandleHeaderRepresentation(StringPiece name,
+ StringPiece value) {
+ typedef std::pair<std::map<string, string>::iterator, bool> InsertResult;
+
+ if (name == kCookieKey) {
+ if (cookie_value_.empty()) {
+ cookie_value_.assign(value.data(), value.size());
+ } else {
+ cookie_value_ += "; ";
+ cookie_value_.insert(cookie_value_.end(), value.begin(), value.end());
+ }
+ } else {
+ InsertResult result = decoded_block_.insert(
+ std::make_pair(name.as_string(), value.as_string()));
+ if (!result.second) {
+ result.first->second.push_back('\0');
+ result.first->second.insert(result.first->second.end(),
+ value.begin(),
+ value.end());
+ }
+ }
+}
+
+bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) {
+ // Implements 4.2: Indexed Header Field Representation.
+ if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) {
+ return DecodeNextIndexedHeader(input_stream);
+ }
+ // Implements 4.3.1: Literal Header Field without Indexing.
+ if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
+ return DecodeNextLiteralHeader(input_stream, false);
+ }
+ // Implements 4.3.2: Literal Header Field with Incremental Indexing.
+ if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) {
+ return DecodeNextLiteralHeader(input_stream, true);
+ }
+ // Implements 4.3.3: Literal Header Field never Indexed.
+ // TODO(jgraettinger): Preserve the never-indexed bit.
+ if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) {
+ return DecodeNextLiteralHeader(input_stream, false);
+ }
+ // Implements 4.4: Encoding context update.
+ if (input_stream->MatchPrefixAndConsume(kEncodingContextOpcode)) {
+ return DecodeNextContextUpdate(input_stream);
+ }
+ // Unrecognized opcode.
+ return false;
+}
+
+bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream* input_stream) {
+ if (input_stream->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet)) {
+ header_table_.ClearReferenceSet();
+ return true;
+ }
+ if (input_stream->MatchPrefixAndConsume(kEncodingContextNewMaximumSize)) {
+ uint32 size = 0;
+ if (!input_stream->DecodeNextUint32(&size)) {
+ return false;
+ }
+ if (size > header_table_.settings_size_bound()) {
+ return false;
+ }
+ header_table_.SetMaxSize(size);
+ return true;
+ }
+ // Unrecognized encoding context update.
+ return false;
+}
+
+bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
+ uint32 index = 0;
+ if (!input_stream->DecodeNextUint32(&index))
+ return false;
+
+ HpackEntry* entry = header_table_.GetByIndex(index);
+ if (entry == NULL)
+ return false;
+
+ if (entry->IsStatic()) {
+ HandleHeaderRepresentation(entry->name(), entry->value());
+
+ HpackEntry* new_entry = header_table_.TryAddEntry(
+ entry->name(), entry->value());
+ if (new_entry) {
+ header_table_.Toggle(new_entry);
+ new_entry->set_state(kReferencedThisEncoding);
+ }
+ } else {
+ entry->set_state(kNoState);
+ if (header_table_.Toggle(entry)) {
+ HandleHeaderRepresentation(entry->name(), entry->value());
+ entry->set_state(kReferencedThisEncoding);
+ }
+ }
+ return true;
+}
+
+bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream,
+ bool should_index) {
+ StringPiece name;
+ if (!DecodeNextName(input_stream, &name))
+ return false;
+
+ StringPiece value;
+ if (!DecodeNextStringLiteral(input_stream, false, &value))
+ return false;
+
+ HandleHeaderRepresentation(name, value);
+
+ if (!should_index)
+ return true;
+
+ HpackEntry* new_entry = header_table_.TryAddEntry(name, value);
+ if (new_entry) {
+ header_table_.Toggle(new_entry);
+ new_entry->set_state(kReferencedThisEncoding);
+ }
+ return true;
+}
+
+bool HpackDecoder::DecodeNextName(
+ HpackInputStream* input_stream, StringPiece* next_name) {
+ uint32 index_or_zero = 0;
+ if (!input_stream->DecodeNextUint32(&index_or_zero))
+ return false;
+
+ if (index_or_zero == 0)
+ return DecodeNextStringLiteral(input_stream, true, next_name);
+
+ const HpackEntry* entry = header_table_.GetByIndex(index_or_zero);
+ if (entry == NULL) {
+ return false;
+ } else if (entry->IsStatic()) {
+ *next_name = entry->name();
+ } else {
+ // |entry| could be evicted as part of this insertion. Preemptively copy.
+ key_buffer_.assign(entry->name());
+ *next_name = key_buffer_;
+ }
+ return true;
+}
+
+bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream,
+ bool is_key,
+ StringPiece* output) {
+ if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) {
+ string* buffer = is_key ? &key_buffer_ : &value_buffer_;
+ bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer);
+ *output = StringPiece(*buffer);
+ return result;
+ } else if (input_stream->MatchPrefixAndConsume(
+ kStringLiteralIdentityEncoded)) {
+ return input_stream->DecodeNextIdentityString(output);
+ } else {
+ return false;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_decoder.h b/chromium/net/spdy/hpack_decoder.h
new file mode 100644
index 00000000000..70729667de7
--- /dev/null
+++ b/chromium/net/spdy/hpack_decoder.h
@@ -0,0 +1,121 @@
+// Copyright 2014 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_SPDY_HPACK_DECODER_H_
+#define NET_SPDY_HPACK_DECODER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_header_table.h"
+#include "net/spdy/hpack_input_stream.h"
+#include "net/spdy/spdy_protocol.h"
+
+// An HpackDecoder decodes header sets as outlined in
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+class HpackHuffmanTable;
+
+namespace test {
+class HpackDecoderPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE HpackDecoder {
+ public:
+ friend class test::HpackDecoderPeer;
+
+ // |table| is an initialized HPACK Huffman table, having an
+ // externally-managed lifetime which spans beyond HpackDecoder.
+ explicit HpackDecoder(const HpackHuffmanTable& table);
+ ~HpackDecoder();
+
+ // Called upon acknowledgement of SETTINGS_HEADER_TABLE_SIZE.
+ void ApplyHeaderTableSizeSetting(size_t size_setting) {
+ header_table_.SetSettingsHeaderTableSize(size_setting);
+ }
+
+ // Called as headers data arrives. Returns false if an error occurred.
+ // TODO(jgraettinger): A future version of this method will incrementally
+ // parse and deliver headers via SpdyHeadersHandlerInterface. For now,
+ // header data is buffered until HandleControlFrameHeadersComplete().
+ bool HandleControlFrameHeadersData(SpdyStreamId stream_id,
+ const char* headers_data,
+ size_t headers_data_length);
+
+ // Called after a headers block has been completely delivered via
+ // HandleControlFrameHeadersData(). Returns false if an error occurred.
+ // TODO(jgraettinger): A future version of this method will simply deliver
+ // the Cookie header (which has been incrementally reconstructed) and notify
+ // the visitor that the block is finished. For now, this method decodes the
+ // complete buffered block, and stores results to |decoded_block_|.
+ bool HandleControlFrameHeadersComplete(SpdyStreamId stream_id);
+
+ // Accessor for the most recently decoded headers block. Valid until the next
+ // call to HandleControlFrameHeadersData().
+ // TODO(jgraettinger): This was added to facilitate re-encoding the block in
+ // SPDY3 format for delivery to the SpdyFramer visitor, and will be removed
+ // with the migration to SpdyHeadersHandlerInterface.
+ const std::map<std::string, std::string>& decoded_block() {
+ return decoded_block_;
+ }
+
+ private:
+ // Adds the header representation to |decoded_block_|, applying the
+ // following rules, as per sections 8.1.3.3 & 8.1.3.4 of the HTTP2 draft
+ // specification:
+ // - Multiple values of the Cookie header are joined, delmited by '; '.
+ // This reconstruction is required to properly handle Cookie crumbling.
+ // - Multiple values of other headers are joined and delimited by '\0'.
+ // Note that this may be too accomodating, as the sender's HTTP2 layer
+ // should have already joined and delimited these values.
+ //
+ // TODO(jgraettinger): This method will eventually emit to the
+ // SpdyHeadersHandlerInterface visitor.
+ void HandleHeaderRepresentation(base::StringPiece name,
+ base::StringPiece value);
+
+ const uint32 max_string_literal_size_;
+ HpackHeaderTable header_table_;
+
+ // Incrementally reconstructed cookie value.
+ std::string cookie_value_;
+
+ // TODO(jgraettinger): Buffer for headers data, and storage for the last-
+ // processed headers block. Both will be removed with the switch to
+ // SpdyHeadersHandlerInterface.
+ std::string headers_block_buffer_;
+ std::map<std::string, std::string> decoded_block_;
+
+ // Huffman table to be applied to decoded Huffman literals,
+ // and scratch space for storing those decoded literals.
+ const HpackHuffmanTable& huffman_table_;
+ std::string key_buffer_, value_buffer_;
+
+ // Handlers for decoding HPACK opcodes and header representations
+ // (or parts thereof). These methods return true on success and
+ // false on error.
+ bool DecodeNextOpcode(HpackInputStream* input_stream);
+ bool DecodeNextContextUpdate(HpackInputStream* input_stream);
+ bool DecodeNextIndexedHeader(HpackInputStream* input_stream);
+ bool DecodeNextLiteralHeader(HpackInputStream* input_stream,
+ bool should_index);
+ bool DecodeNextName(HpackInputStream* input_stream,
+ base::StringPiece* next_name);
+ bool DecodeNextStringLiteral(HpackInputStream* input_stream,
+ bool is_header_key, // As distinct from a value.
+ base::StringPiece* output);
+
+ DISALLOW_COPY_AND_ASSIGN(HpackDecoder);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_DECODER_H_
diff --git a/chromium/net/spdy/hpack_decoder_test.cc b/chromium/net/spdy/hpack_decoder_test.cc
new file mode 100644
index 00000000000..e51ea8ddb61
--- /dev/null
+++ b/chromium/net/spdy/hpack_decoder_test.cc
@@ -0,0 +1,670 @@
+// Copyright 2014 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/spdy/hpack_decoder.h"
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/spdy/hpack_encoder.h"
+#include "net/spdy/hpack_input_stream.h"
+#include "net/spdy/hpack_output_stream.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace test {
+
+using base::StringPiece;
+using std::string;
+
+class HpackDecoderPeer {
+ public:
+ explicit HpackDecoderPeer(HpackDecoder* decoder)
+ : decoder_(decoder) {}
+
+ void HandleHeaderRepresentation(StringPiece name, StringPiece value) {
+ decoder_->HandleHeaderRepresentation(name, value);
+ }
+ bool DecodeNextName(HpackInputStream* in, StringPiece* out) {
+ return decoder_->DecodeNextName(in, out);
+ }
+ HpackHeaderTable* header_table() {
+ return &decoder_->header_table_;
+ }
+ void set_cookie_value(string value) {
+ decoder_->cookie_value_ = value;
+ }
+ string cookie_value() {
+ return decoder_->cookie_value_;
+ }
+ const std::map<string, string>& decoded_block() const {
+ return decoder_->decoded_block_;
+ }
+ const string& headers_block_buffer() const {
+ return decoder_->headers_block_buffer_;
+ }
+
+ private:
+ HpackDecoder* decoder_;
+};
+
+} // namespace test
+
+namespace {
+
+using base::StringPiece;
+using std::string;
+using test::a2b_hex;
+
+using testing::ElementsAre;
+using testing::Pair;
+
+const size_t kLiteralBound = 1024;
+
+class HpackDecoderTest : public ::testing::Test {
+ protected:
+ HpackDecoderTest()
+ : decoder_(ObtainHpackHuffmanTable()),
+ decoder_peer_(&decoder_) {}
+
+ bool DecodeHeaderBlock(StringPiece str) {
+ return decoder_.HandleControlFrameHeadersData(0, str.data(), str.size()) &&
+ decoder_.HandleControlFrameHeadersComplete(0);
+ }
+
+ const std::map<string, string>& decoded_block() const {
+ // TODO(jgraettinger): HpackDecoderTest should implement
+ // SpdyHeadersHandlerInterface, and collect headers for examination.
+ return decoder_peer_.decoded_block();
+ }
+
+ const std::map<string, string>& DecodeBlockExpectingSuccess(StringPiece str) {
+ EXPECT_TRUE(DecodeHeaderBlock(str));
+ return decoded_block();
+ }
+
+ void expectEntry(size_t index, size_t size, const string& name,
+ const string& value) {
+ HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
+ EXPECT_EQ(name, entry->name()) << "index " << index;
+ EXPECT_EQ(value, entry->value());
+ EXPECT_EQ(size, entry->Size());
+ EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry));
+ }
+
+ void expectStaticEntry(size_t index) {
+ HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
+ EXPECT_TRUE(entry->IsStatic()) << "index " << index;
+ }
+
+ HpackDecoder decoder_;
+ test::HpackDecoderPeer decoder_peer_;
+};
+
+TEST_F(HpackDecoderTest, HandleControlFrameHeadersData) {
+ // Strings under threshold are concatenated in the buffer.
+ EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(
+ 0, "small string one", 16));
+ EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(
+ 0, "small string two", 16));
+ // A string which would push the buffer over the threshold is refused.
+ EXPECT_FALSE(decoder_.HandleControlFrameHeadersData(
+ 0, "fails", kMaxDecodeBufferSize - 32 + 1));
+
+ EXPECT_EQ(decoder_peer_.headers_block_buffer(),
+ "small string onesmall string two");
+}
+
+TEST_F(HpackDecoderTest, HandleControlFrameHeadersComplete) {
+ // Decode a block which toggles two static headers into the reference set.
+ EXPECT_TRUE(DecodeHeaderBlock("\x82\x86"));
+
+ decoder_peer_.set_cookie_value("foobar=baz");
+
+ // Headers in the reference set should be emitted.
+ // Incremental cookie buffer should be emitted and cleared.
+ decoder_.HandleControlFrameHeadersData(0, NULL, 0);
+ decoder_.HandleControlFrameHeadersComplete(0);
+
+ EXPECT_THAT(decoded_block(), ElementsAre(
+ Pair(":method", "GET"),
+ Pair(":path", "/index.html"),
+ Pair("cookie", "foobar=baz")));
+ EXPECT_EQ(decoder_peer_.cookie_value(), "");
+}
+
+TEST_F(HpackDecoderTest, HandleHeaderRepresentation) {
+ // All cookie crumbs are joined.
+ decoder_peer_.HandleHeaderRepresentation("cookie", " part 1");
+ decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 ");
+ decoder_peer_.HandleHeaderRepresentation("cookie", "part3");
+
+ // Already-delimited headers are passed through.
+ decoder_peer_.HandleHeaderRepresentation("passed-through",
+ string("foo\0baz", 7));
+
+ // Other headers are joined on \0. Case matters.
+ decoder_peer_.HandleHeaderRepresentation("joined", "not joined");
+ decoder_peer_.HandleHeaderRepresentation("joineD", "value 1");
+ decoder_peer_.HandleHeaderRepresentation("joineD", "value 2");
+
+ // Empty headers remain empty.
+ decoder_peer_.HandleHeaderRepresentation("empty", "");
+
+ // Joined empty headers work as expected.
+ decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+ decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo");
+ decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+ decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+
+ // Non-contiguous cookie crumb.
+ decoder_peer_.HandleHeaderRepresentation("cookie", " fin!");
+
+ // Finish and emit all headers.
+ decoder_.HandleControlFrameHeadersComplete(0);
+
+ EXPECT_THAT(decoded_block(), ElementsAre(
+ Pair("cookie", " part 1; part 2 ; part3; fin!"),
+ Pair("empty", ""),
+ Pair("empty-joined", string("\0foo\0\0", 6)),
+ Pair("joineD", string("value 1\0value 2", 15)),
+ Pair("joined", "not joined"),
+ Pair("passed-through", string("foo\0baz", 7))));
+}
+
+// Decoding an encoded name with a valid string literal should work.
+TEST_F(HpackDecoderTest, DecodeNextNameLiteral) {
+ HpackInputStream input_stream(kLiteralBound, StringPiece("\x00\x04name", 6));
+
+ StringPiece string_piece;
+ EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece));
+ EXPECT_EQ("name", string_piece);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+TEST_F(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) {
+ string input = a2b_hex("0088571c5cdb737b2faf");
+ HpackInputStream input_stream(kLiteralBound, input);
+
+ StringPiece string_piece;
+ EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece));
+ EXPECT_EQ("custom-key", string_piece);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+// Decoding an encoded name with a valid index should work.
+TEST_F(HpackDecoderTest, DecodeNextNameIndexed) {
+ HpackInputStream input_stream(kLiteralBound, "\x01");
+
+ StringPiece string_piece;
+ EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece));
+ EXPECT_EQ(":authority", string_piece);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+// Decoding an encoded name with an invalid index should fail.
+TEST_F(HpackDecoderTest, DecodeNextNameInvalidIndex) {
+ // One more than the number of static table entries.
+ HpackInputStream input_stream(kLiteralBound, "\x3e");
+
+ StringPiece string_piece;
+ EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece));
+}
+
+// Decoding an indexed header should toggle the index's presence in
+// the reference set, making a copy of static table entries if
+// necessary. It should also emit the header if toggled on (and only
+// as many times as it was toggled on).
+TEST_F(HpackDecoderTest, IndexedHeaderBasic) {
+ // Toggle on static table entry #2 (and make a copy at index #1),
+ // then toggle on static table entry #5 (which is now #6 because of
+ // the copy of #2).
+ std::map<string, string> header_set1 =
+ DecodeBlockExpectingSuccess("\x82\x86");
+ std::map<string, string> expected_header_set1;
+ expected_header_set1[":method"] = "GET";
+ expected_header_set1[":path"] = "/index.html";
+ EXPECT_EQ(expected_header_set1, header_set1);
+
+ std::map<string, string> expected_header_set2;
+ expected_header_set2[":path"] = "/index.html";
+ // Toggle off the copy of static table entry #5.
+ std::map<string, string> header_set2 =
+ DecodeBlockExpectingSuccess("\x82");
+ EXPECT_EQ(expected_header_set2, header_set2);
+}
+
+// Test a too-large indexed header.
+TEST_F(HpackDecoderTest, InvalidIndexedHeader) {
+ // High-bit set, and a prefix of one more than the number of static entries.
+ EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\xbe", 1)));
+}
+
+TEST_F(HpackDecoderTest, ContextUpdateMaximumSize) {
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+ decoder_peer_.header_table()->max_size());
+ string input;
+ {
+ // Maximum-size update with size 126. Succeeds.
+ HpackOutputStream output_stream;
+ output_stream.AppendPrefix(kEncodingContextOpcode);
+ output_stream.AppendPrefix(kEncodingContextNewMaximumSize);
+ output_stream.AppendUint32(126);
+
+ output_stream.TakeString(&input);
+ EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input)));
+ EXPECT_EQ(126u, decoder_peer_.header_table()->max_size());
+ }
+ {
+ // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds.
+ HpackOutputStream output_stream;
+ output_stream.AppendPrefix(kEncodingContextOpcode);
+ output_stream.AppendPrefix(kEncodingContextNewMaximumSize);
+ output_stream.AppendUint32(kDefaultHeaderTableSizeSetting);
+
+ output_stream.TakeString(&input);
+ EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input)));
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+ decoder_peer_.header_table()->max_size());
+ }
+ {
+ // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails.
+ HpackOutputStream output_stream;
+ output_stream.AppendPrefix(kEncodingContextOpcode);
+ output_stream.AppendPrefix(kEncodingContextNewMaximumSize);
+ output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1);
+
+ output_stream.TakeString(&input);
+ EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input)));
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+ decoder_peer_.header_table()->max_size());
+ }
+}
+
+TEST_F(HpackDecoderTest, ContextUpdateClearReferenceSet) {
+ // Toggle on a couple of headers.
+ std::map<string, string> header_set1 =
+ DecodeBlockExpectingSuccess("\x82\x86");
+ std::map<string, string> expected_header_set1;
+ expected_header_set1[":method"] = "GET";
+ expected_header_set1[":path"] = "/index.html";
+ EXPECT_EQ(expected_header_set1, header_set1);
+
+ // Send a context update to clear the reference set.
+ std::map<string, string> header_set2 =
+ DecodeBlockExpectingSuccess("\x30");
+ std::map<string, string> expected_header_set2;
+ EXPECT_EQ(expected_header_set2, header_set2);
+}
+
+// Decoding two valid encoded literal headers with no indexing should
+// work.
+TEST_F(HpackDecoderTest, LiteralHeaderNoIndexing) {
+ // First header with indexed name, second header with string literal
+ // name.
+ const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2";
+ std::map<string, string> header_set =
+ DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1));
+
+ std::map<string, string> expected_header_set;
+ expected_header_set[":path"] = "/sample/path";
+ expected_header_set[":path2"] = "/sample/path/2";
+ EXPECT_EQ(expected_header_set, header_set);
+}
+
+// Decoding two valid encoded literal headers with incremental
+// indexing and string literal names should work and add the headers
+// to the reference set.
+TEST_F(HpackDecoderTest, LiteralHeaderIncrementalIndexing) {
+ const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2";
+ std::map<string, string> header_set =
+ DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1));
+
+ std::map<string, string> expected_header_set;
+ expected_header_set[":path"] = "/sample/path";
+ expected_header_set[":path2"] = "/sample/path/2";
+ EXPECT_EQ(expected_header_set, header_set);
+
+ // Decoding an empty string should just return the reference set.
+ std::map<string, string> header_set2 = DecodeBlockExpectingSuccess("");
+ EXPECT_EQ(expected_header_set, header_set2);
+}
+
+TEST_F(HpackDecoderTest, LiteralHeaderWithIndexingInvalidNameIndex) {
+ decoder_.ApplyHeaderTableSizeSetting(0);
+
+ // Name is the last static index. Works.
+ EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x7d\x03ooo")));
+ // Name is one beyond the last static index. Fails.
+ EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x7e\x03ooo")));
+}
+
+TEST_F(HpackDecoderTest, LiteralHeaderNoIndexingInvalidNameIndex) {
+ // Name is the last static index. Works.
+ EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x0f\x2e\x03ooo")));
+ // Name is one beyond the last static index. Fails.
+ EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x0f\x2f\x03ooo")));
+}
+
+TEST_F(HpackDecoderTest, LiteralHeaderNeverIndexedInvalidNameIndex) {
+ // Name is the last static index. Works.
+ EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x1f\x2e\x03ooo")));
+ // Name is one beyond the last static index. Fails.
+ EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x1f\x2f\x03ooo")));
+}
+
+// Round-tripping the header set from E.2.1 should work.
+TEST_F(HpackDecoderTest, BasicE21) {
+ HpackEncoder encoder(ObtainHpackHuffmanTable());
+
+ std::map<string, string> expected_header_set;
+ expected_header_set[":method"] = "GET";
+ expected_header_set[":scheme"] = "http";
+ expected_header_set[":path"] = "/";
+ expected_header_set[":authority"] = "www.example.com";
+
+ string encoded_header_set;
+ EXPECT_TRUE(encoder.EncodeHeaderSet(
+ expected_header_set, &encoded_header_set));
+
+ EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set));
+ EXPECT_EQ(expected_header_set, decoded_block());
+}
+
+TEST_F(HpackDecoderTest, SectionD3RequestHuffmanExamples) {
+ std::map<string, string> header_set;
+
+ // 82 | == Indexed - Add ==
+ // | idx = 2
+ // | -> :method: GET
+ // 87 | == Indexed - Add ==
+ // | idx = 7
+ // | -> :scheme: http
+ // 86 | == Indexed - Add ==
+ // | idx = 6
+ // | -> :path: /
+ // 44 | == Literal indexed ==
+ // | Indexed name (idx = 4)
+ // | :authority
+ // 8c | Literal value (len = 15)
+ // | Huffman encoded:
+ // e7cf 9beb e89b 6fb1 6fa9 b6ff | ......o.o...
+ // | Decoded:
+ // | www.example.com
+ // | -> :authority: www.example.com
+ string first = a2b_hex("828786448ce7cf9bebe89b6fb16fa9b6ff");
+ header_set = DecodeBlockExpectingSuccess(first);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":authority", "www.example.com"),
+ Pair(":method", "GET"),
+ Pair(":path", "/"),
+ Pair(":scheme", "http")));
+
+ expectEntry(1, 57, ":authority", "www.example.com");
+ expectEntry(2, 38, ":path", "/");
+ expectEntry(3, 43, ":scheme", "http");
+ expectEntry(4, 42, ":method", "GET");
+ expectStaticEntry(5);
+ EXPECT_EQ(180u, decoder_peer_.header_table()->size());
+
+ // 5c | == Literal indexed ==
+ // | Indexed name (idx = 28)
+ // | cache-control
+ // 86 | Literal value (len = 8)
+ // | Huffman encoded:
+ // b9b9 9495 56bf | ....V.
+ // | Decoded:
+ // | no-cache
+ // | -> cache-control: no-cache
+ string second = a2b_hex("5c86b9b9949556bf");
+ header_set = DecodeBlockExpectingSuccess(second);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":authority", "www.example.com"),
+ Pair(":method", "GET"),
+ Pair(":path", "/"),
+ Pair(":scheme", "http"),
+ Pair("cache-control", "no-cache")));
+
+ expectEntry(1, 53, "cache-control", "no-cache");
+ expectEntry(2, 57, ":authority", "www.example.com");
+ expectEntry(3, 38, ":path", "/");
+ expectEntry(4, 43, ":scheme", "http");
+ expectEntry(5, 42, ":method", "GET");
+ expectStaticEntry(6);
+ EXPECT_EQ(233u, decoder_peer_.header_table()->size());
+
+ // 30 | == Empty reference set ==
+ // | idx = 0
+ // | flag = 1
+ // 85 | == Indexed - Add ==
+ // | idx = 5
+ // | -> :method: GET
+ // 8c | == Indexed - Add ==
+ // | idx = 12
+ // | -> :scheme: https
+ // 8b | == Indexed - Add ==
+ // | idx = 11
+ // | -> :path: /index.html
+ // 84 | == Indexed - Add ==
+ // | idx = 4
+ // | -> :authority: www.example.com
+ // 40 | == Literal indexed ==
+ // 88 | Literal name (len = 10)
+ // | Huffman encoded:
+ // 571c 5cdb 737b 2faf | W.\.s{/.
+ // | Decoded:
+ // | custom-key
+ // 89 | Literal value (len = 12)
+ // | Huffman encoded:
+ // 571c 5cdb 7372 4d9c 57 | W.\.srM.W
+ // | Decoded:
+ // | custom-value
+ // | -> custom-key: custom-value
+ string third = a2b_hex("30858c8b844088571c5cdb737b2faf89"
+ "571c5cdb73724d9c57");
+ header_set = DecodeBlockExpectingSuccess(third);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":authority", "www.example.com"),
+ Pair(":method", "GET"),
+ Pair(":path", "/index.html"),
+ Pair(":scheme", "https"),
+ Pair("custom-key", "custom-value")));
+
+ expectEntry(1, 54, "custom-key", "custom-value");
+ expectEntry(2, 48, ":path", "/index.html");
+ expectEntry(3, 44, ":scheme", "https");
+ expectEntry(4, 53, "cache-control", "no-cache");
+ expectEntry(5, 57, ":authority", "www.example.com");
+ expectEntry(6, 38, ":path", "/");
+ expectEntry(7, 43, ":scheme", "http");
+ expectEntry(8, 42, ":method", "GET");
+ expectStaticEntry(9);
+ EXPECT_EQ(379u, decoder_peer_.header_table()->size());
+}
+
+TEST_F(HpackDecoderTest, SectionD5ResponseHuffmanExamples) {
+ std::map<string, string> header_set;
+ decoder_.ApplyHeaderTableSizeSetting(256);
+
+ // 48 | == Literal indexed ==
+ // | Indexed name (idx = 8)
+ // | :status
+ // 82 | Literal value (len = 3)
+ // | Huffman encoded:
+ // 4017 | @.
+ // | Decoded:
+ // | 302
+ // | -> :status: 302
+ // 59 | == Literal indexed ==
+ // | Indexed name (idx = 25)
+ // | cache-control
+ // 85 | Literal value (len = 7)
+ // | Huffman encoded:
+ // bf06 724b 97 | ..rK.
+ // | Decoded:
+ // | private
+ // | -> cache-control: private
+ // 63 | == Literal indexed ==
+ // | Indexed name (idx = 35)
+ // | date
+ // 93 | Literal value (len = 29)
+ // | Huffman encoded:
+ // d6db b298 84de 2a71 8805 0620 9851 3109 | ......*q... .Q1.
+ // b56b a3 | .k.
+ // | Decoded:
+ // | Mon, 21 Oct 2013 20:13:21
+ // | GMT
+ // | -> date: Mon, 21 Oct 2013
+ // | 20:13:21 GMT
+ // 71 | == Literal indexed ==
+ // | Indexed name (idx = 49)
+ // | location
+ // 91 | Literal value (len = 23)
+ // | Huffman encoded:
+ // adce bf19 8e7e 7cf9 bebe 89b6 fb16 fa9b | ......|.........
+ // 6f | o
+ // | Decoded:
+ // | https://www.example.com
+ // | -> location: https://www.e
+ // | xample.com
+ string first = a2b_hex("488240175985bf06724b976393d6dbb2"
+ "9884de2a718805062098513109b56ba3"
+ "7191adcebf198e7e7cf9bebe89b6fb16"
+ "fa9b6f");
+ header_set = DecodeBlockExpectingSuccess(first);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":status", "302"),
+ Pair("cache-control", "private"),
+ Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ Pair("location", "https://www.example.com")));
+
+ expectEntry(1, 63, "location", "https://www.example.com");
+ expectEntry(2, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
+ expectEntry(3, 52, "cache-control", "private");
+ expectEntry(4, 42, ":status", "302");
+ expectStaticEntry(5);
+ EXPECT_EQ(222u, decoder_peer_.header_table()->size());
+
+ // 8c | == Indexed - Add ==
+ // | idx = 12
+ // | - evict: :status: 302
+ // | -> :status: 200
+ string second = a2b_hex("8c");
+ header_set = DecodeBlockExpectingSuccess(second);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":status", "200"),
+ Pair("cache-control", "private"),
+ Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ Pair("location", "https://www.example.com")));
+
+ expectEntry(1, 42, ":status", "200");
+ expectEntry(2, 63, "location", "https://www.example.com");
+ expectEntry(3, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
+ expectEntry(4, 52, "cache-control", "private");
+ expectStaticEntry(5);
+ EXPECT_EQ(222u, decoder_peer_.header_table()->size());
+
+ // 84 | == Indexed - Remove ==
+ // | idx = 4
+ // | -> cache-control: private
+ // 84 | == Indexed - Add ==
+ // | idx = 4
+ // | -> cache-control: private
+ // 43 | == Literal indexed ==
+ // | Indexed name (idx = 3)
+ // | date
+ // 93 | Literal value (len = 29)
+ // | Huffman encoded:
+ // d6db b298 84de 2a71 8805 0620 9851 3111 | ......*q... .Q1.
+ // b56b a3 | .k.
+ // | Decoded:
+ // | Mon, 21 Oct 2013 20:13:22
+ // | GMT
+ // | - evict: cache-control: pr
+ // | ivate
+ // | -> date: Mon, 21 Oct 2013
+ // | 20:13:22 GMT
+ // 5e | == Literal indexed ==
+ // | Indexed name (idx = 30)
+ // | content-encoding
+ // 84 | Literal value (len = 4)
+ // | Huffman encoded:
+ // abdd 97ff | ....
+ // | Decoded:
+ // | gzip
+ // | - evict: date: Mon, 21 Oct
+ // | 2013 20:13:21 GMT
+ // | -> content-encoding: gzip
+ // 84 | == Indexed - Remove ==
+ // | idx = 4
+ // | -> location: https://www.e
+ // | xample.com
+ // 84 | == Indexed - Add ==
+ // | idx = 4
+ // | -> location: https://www.e
+ // | xample.com
+ // 83 | == Indexed - Remove ==
+ // | idx = 3
+ // | -> :status: 200
+ // 83 | == Indexed - Add ==
+ // | idx = 3
+ // | -> :status: 200
+ // 7b | == Literal indexed ==
+ // | Indexed name (idx = 59)
+ // | set-cookie
+ // b1 | Literal value (len = 56)
+ // | Huffman encoded:
+ // e0d6 cf9f 6e8f 9fd3 e5f6 fa76 fefd 3c7e | ....n......v....
+ // df9e ff1f 2f0f 3cfe 9f6f cf7f 8f87 9f61 | ..../....o.....a
+ // ad4f 4cc9 a973 a220 0ec3 725e 18b1 b74e | .OL..s. ..r^...N
+ // 3f | ?
+ // | Decoded:
+ // | foo=ASDJKHQKBZXOQWEOPIUAXQ
+ // | WEOIU; max-age=3600; versi
+ // | on=1
+ // | - evict: location: https:/
+ // | /www.example.com
+ // | - evict: :status: 200
+ // | -> set-cookie: foo=ASDJKHQ
+ // | KBZXOQWEOPIUAXQWEOIU; ma
+ // | x-age=3600; version=1
+ string third = a2b_hex("84844393d6dbb29884de2a7188050620"
+ "98513111b56ba35e84abdd97ff848483"
+ "837bb1e0d6cf9f6e8f9fd3e5f6fa76fe"
+ "fd3c7edf9eff1f2f0f3cfe9f6fcf7f8f"
+ "879f61ad4f4cc9a973a2200ec3725e18"
+ "b1b74e3f");
+ header_set = DecodeBlockExpectingSuccess(third);
+
+ EXPECT_THAT(header_set, ElementsAre(
+ Pair(":status", "200"),
+ Pair("cache-control", "private"),
+ Pair("content-encoding", "gzip"),
+ Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+ Pair("location", "https://www.example.com"),
+ Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
+ " max-age=3600; version=1")));
+
+ expectEntry(1, 98, "set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
+ " max-age=3600; version=1");
+ expectEntry(2, 52, "content-encoding", "gzip");
+ expectEntry(3, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
+ expectStaticEntry(4);
+ EXPECT_EQ(215u, decoder_peer_.header_table()->size());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_encoder.cc b/chromium/net/spdy/hpack_encoder.cc
new file mode 100644
index 00000000000..a70af8669f2
--- /dev/null
+++ b/chromium/net/spdy/hpack_encoder.cc
@@ -0,0 +1,291 @@
+// Copyright 2014 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/spdy/hpack_encoder.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "net/spdy/hpack_header_table.h"
+#include "net/spdy/hpack_huffman_table.h"
+#include "net/spdy/hpack_output_stream.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+
+namespace {
+
+const uint8 kNoState = 0;
+// Set on a referenced HpackEntry which is part of the current header set.
+const uint8 kReferencedImplicitOn = 1;
+// Set on a referenced HpackEntry which is not part of the current header set.
+const uint8 kReferencedExplicitOff = 2;
+// Set on a entries added to the reference set during this encoding.
+const uint8 kReferencedThisEncoding = 3;
+
+} // namespace
+
+HpackEncoder::HpackEncoder(const HpackHuffmanTable& table)
+ : output_stream_(),
+ allow_huffman_compression_(true),
+ huffman_table_(table),
+ char_counts_(NULL),
+ total_char_counts_(NULL) {}
+
+HpackEncoder::~HpackEncoder() {}
+
+bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
+ string* output) {
+ // Walk the set of entries to encode, which are not already implied by the
+ // header table's reference set. They must be explicitly emitted.
+ Representations explicit_set(DetermineEncodingDelta(header_set));
+ for (Representations::const_iterator it = explicit_set.begin();
+ it != explicit_set.end(); ++it) {
+ // Try to find an exact match. Note that dynamic entries are preferred
+ // by the header table index.
+ HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
+ if (entry != NULL && !entry->IsStatic()) {
+ // Already in the dynamic table. Simply toggle on.
+ CHECK_EQ(kNoState, entry->state());
+ EmitDynamicIndex(entry);
+ continue;
+ }
+
+ // Walk the set of entries to be evicted by this insertion.
+ HpackHeaderTable::EntryTable::iterator evict_begin, evict_end, evict_it;
+ header_table_.EvictionSet(it->first, it->second, &evict_begin, &evict_end);
+
+ for (evict_it = evict_begin; evict_it != evict_end; ++evict_it) {
+ HpackEntry* evictee = &(*evict_it);
+
+ if (evict_it->state() == kReferencedImplicitOn) {
+ // Issue twice to explicitly emit.
+ EmitDynamicIndex(evictee);
+ EmitDynamicIndex(evictee);
+ } else if (evictee->state() == kReferencedExplicitOff) {
+ // Eviction saves us from having to explicitly toggle off.
+ evictee->set_state(kNoState);
+ } else if (evictee->state() == kReferencedThisEncoding) {
+ // Already emitted. No action required.
+ evictee->set_state(kNoState);
+ }
+ }
+ if (entry != NULL) {
+ EmitStaticIndex(entry);
+ } else {
+ EmitIndexedLiteral(*it);
+ }
+ }
+ // Walk the reference set, toggling off as needed and clearing encoding state.
+ for (HpackHeaderTable::OrderedEntrySet::const_iterator it =
+ header_table_.reference_set().begin();
+ it != header_table_.reference_set().end();) {
+ HpackEntry* entry = *(it++); // Step to prevent invalidation.
+ CHECK_NE(kNoState, entry->state());
+
+ if (entry->state() == kReferencedExplicitOff) {
+ // Explicitly toggle off.
+ EmitDynamicIndex(entry);
+ }
+ entry->set_state(kNoState);
+ }
+ output_stream_.TakeString(output);
+ return true;
+}
+
+bool HpackEncoder::EncodeHeaderSetWithoutCompression(
+ const std::map<string, string>& header_set,
+ string* output) {
+
+ allow_huffman_compression_ = false;
+ for (std::map<string, string>::const_iterator it = header_set.begin();
+ it != header_set.end(); ++it) {
+ // Note that cookies are not crumbled in this case.
+ EmitNonIndexedLiteral(*it);
+ }
+ allow_huffman_compression_ = true;
+ output_stream_.TakeString(output);
+ return true;
+}
+
+void HpackEncoder::EmitDynamicIndex(HpackEntry* entry) {
+ DCHECK(!entry->IsStatic());
+ output_stream_.AppendPrefix(kIndexedOpcode);
+ output_stream_.AppendUint32(header_table_.IndexOf(entry));
+
+ entry->set_state(kNoState);
+ if (header_table_.Toggle(entry)) {
+ // Was added to the reference set.
+ entry->set_state(kReferencedThisEncoding);
+ }
+}
+
+void HpackEncoder::EmitStaticIndex(HpackEntry* entry) {
+ DCHECK(entry->IsStatic());
+ output_stream_.AppendPrefix(kIndexedOpcode);
+ output_stream_.AppendUint32(header_table_.IndexOf(entry));
+
+ HpackEntry* new_entry = header_table_.TryAddEntry(entry->name(),
+ entry->value());
+ if (new_entry) {
+ // This is a static entry: no need to pin.
+ header_table_.Toggle(new_entry);
+ new_entry->set_state(kReferencedThisEncoding);
+ }
+}
+
+void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
+ output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
+ EmitLiteral(representation);
+
+ HpackEntry* new_entry = header_table_.TryAddEntry(representation.first,
+ representation.second);
+ if (new_entry) {
+ header_table_.Toggle(new_entry);
+ new_entry->set_state(kReferencedThisEncoding);
+ }
+}
+
+void HpackEncoder::EmitNonIndexedLiteral(
+ const Representation& representation) {
+ output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
+ output_stream_.AppendUint32(0);
+ EmitString(representation.first);
+ EmitString(representation.second);
+}
+
+void HpackEncoder::EmitLiteral(const Representation& representation) {
+ const HpackEntry* name_entry = header_table_.GetByName(representation.first);
+ if (name_entry != NULL) {
+ output_stream_.AppendUint32(header_table_.IndexOf(name_entry));
+ } else {
+ output_stream_.AppendUint32(0);
+ EmitString(representation.first);
+ }
+ EmitString(representation.second);
+}
+
+void HpackEncoder::EmitString(StringPiece str) {
+ size_t encoded_size = (!allow_huffman_compression_ ? str.size()
+ : huffman_table_.EncodedSize(str));
+ if (encoded_size < str.size()) {
+ output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
+ output_stream_.AppendUint32(encoded_size);
+ huffman_table_.EncodeString(str, &output_stream_);
+ } else {
+ output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
+ output_stream_.AppendUint32(str.size());
+ output_stream_.AppendBytes(str);
+ }
+ UpdateCharacterCounts(str);
+}
+
+// static
+HpackEncoder::Representations HpackEncoder::DetermineEncodingDelta(
+ const std::map<string, string>& header_set) {
+ // Flatten & crumble headers into an ordered list of representations.
+ Representations full_set;
+ for (std::map<string, string>::const_iterator it = header_set.begin();
+ it != header_set.end(); ++it) {
+ if (it->first == "cookie") {
+ // |CookieToCrumbs()| produces ordered crumbs.
+ CookieToCrumbs(*it, &full_set);
+ } else {
+ // Note std::map guarantees representations are ordered.
+ full_set.push_back(make_pair(
+ StringPiece(it->first), StringPiece(it->second)));
+ }
+ }
+ // Perform a linear merge of ordered representations with the (also ordered)
+ // reference set. Mark each referenced entry with current membership state,
+ // and gather representations which must be explicitly emitted.
+ Representations::const_iterator r_it = full_set.begin();
+ HpackHeaderTable::OrderedEntrySet::const_iterator s_it =
+ header_table_.reference_set().begin();
+
+ Representations explicit_set;
+ while (r_it != full_set.end() &&
+ s_it != header_table_.reference_set().end()) {
+ // Compare on name, then value.
+ int result = r_it->first.compare((*s_it)->name());
+ if (result == 0) {
+ result = r_it->second.compare((*s_it)->value());
+ }
+
+ if (result < 0) {
+ explicit_set.push_back(*r_it);
+ ++r_it;
+ } else if (result > 0) {
+ (*s_it)->set_state(kReferencedExplicitOff);
+ ++s_it;
+ } else {
+ (*s_it)->set_state(kReferencedImplicitOn);
+ ++s_it;
+ ++r_it;
+ }
+ }
+ while (r_it != full_set.end()) {
+ explicit_set.push_back(*r_it);
+ ++r_it;
+ }
+ while (s_it != header_table_.reference_set().end()) {
+ (*s_it)->set_state(kReferencedExplicitOff);
+ ++s_it;
+ }
+ return explicit_set;
+}
+
+void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts,
+ size_t* total_char_counts) {
+ CHECK_LE(256u, char_counts->size());
+ char_counts_ = char_counts;
+ total_char_counts_ = total_char_counts;
+}
+
+void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) {
+ if (char_counts_ == NULL || total_char_counts_ == NULL) {
+ return;
+ }
+ for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) {
+ ++(*char_counts_)[static_cast<uint8>(*it)];
+ }
+ (*total_char_counts_) += str.size();
+}
+
+// static
+void HpackEncoder::CookieToCrumbs(const Representation& cookie,
+ Representations* out) {
+ size_t prior_size = out->size();
+
+ // See Section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
+ // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
+ // Cookie values are split into individually-encoded HPACK representations.
+ for (size_t pos = 0;;) {
+ size_t end = cookie.second.find(";", pos);
+
+ if (end == StringPiece::npos) {
+ out->push_back(make_pair(
+ cookie.first,
+ cookie.second.substr(pos)));
+ break;
+ }
+ out->push_back(make_pair(
+ cookie.first,
+ cookie.second.substr(pos, end - pos)));
+
+ // Consume next space if present.
+ pos = end + 1;
+ if (pos != cookie.second.size() && cookie.second[pos] == ' ') {
+ pos++;
+ }
+ }
+ // Sort crumbs and remove duplicates.
+ std::sort(out->begin() + prior_size, out->end());
+ out->erase(std::unique(out->begin() + prior_size, out->end()),
+ out->end());
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_encoder.h b/chromium/net/spdy/hpack_encoder.h
new file mode 100644
index 00000000000..c3ab96bcbe4
--- /dev/null
+++ b/chromium/net/spdy/hpack_encoder.h
@@ -0,0 +1,108 @@
+// Copyright 2014 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_SPDY_HPACK_ENCODER_H_
+#define NET_SPDY_HPACK_ENCODER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_header_table.h"
+#include "net/spdy/hpack_output_stream.h"
+
+// An HpackEncoder encodes header sets as outlined in
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+class HpackHuffmanTable;
+
+namespace test {
+class HpackEncoderPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE HpackEncoder {
+ public:
+ friend class test::HpackEncoderPeer;
+
+ // |table| is an initialized HPACK Huffman table, having an
+ // externally-managed lifetime which spans beyond HpackEncoder.
+ explicit HpackEncoder(const HpackHuffmanTable& table);
+ ~HpackEncoder();
+
+ // Encodes the given header set into the given string. Returns
+ // whether or not the encoding was successful.
+ bool EncodeHeaderSet(const std::map<std::string, std::string>& header_set,
+ std::string* output);
+
+ // Encodes the given header set into the given string. Only non-indexed
+ // literal representations are emitted, bypassing the header table. Huffman
+ // coding is also not used. Returns whether the encoding was successful.
+ // TODO(jgraettinger): Enable Huffman coding once the table as stablized.
+ bool EncodeHeaderSetWithoutCompression(
+ const std::map<std::string, std::string>& header_set,
+ std::string* output);
+
+ // Called upon a change to SETTINGS_HEADER_TABLE_SIZE. Specifically, this
+ // is to be called after receiving (and sending an acknowledgement for) a
+ // SETTINGS_HEADER_TABLE_SIZE update from the remote decoding endpoint.
+ void ApplyHeaderTableSizeSetting(size_t size_setting) {
+ header_table_.SetSettingsHeaderTableSize(size_setting);
+ }
+
+ // Sets externally-owned storage for aggregating character counts of emitted
+ // literal representations.
+ void SetCharCountsStorage(std::vector<size_t>* char_counts,
+ size_t* total_char_counts);
+
+ private:
+ typedef std::pair<base::StringPiece, base::StringPiece> Representation;
+ typedef std::vector<Representation> Representations;
+
+ // Emits a static/dynamic indexed representation (Section 4.2).
+ void EmitDynamicIndex(HpackEntry* entry);
+ void EmitStaticIndex(HpackEntry* entry);
+
+ // Emits a literal representation (Section 4.3).
+ void EmitIndexedLiteral(const Representation& representation);
+ void EmitNonIndexedLiteral(const Representation& representation);
+ void EmitLiteral(const Representation& representation);
+
+ // Emits a Huffman or identity string (whichever is smaller).
+ void EmitString(base::StringPiece str);
+
+ void UpdateCharacterCounts(base::StringPiece str);
+
+ // Determines the representation delta required to encode |header_set| in
+ // the current header table context. Entries in the reference set are
+ // enumerated and marked with membership in the current |header_set|.
+ // Representations which must be explicitly emitted are returned.
+ Representations DetermineEncodingDelta(
+ const std::map<std::string, std::string>& header_set);
+
+ // Crumbles a cookie header into sorted, de-duplicated crumbs.
+ static void CookieToCrumbs(const Representation& cookie,
+ Representations* crumbs_out);
+
+ HpackHeaderTable header_table_;
+ HpackOutputStream output_stream_;
+
+ bool allow_huffman_compression_;
+ const HpackHuffmanTable& huffman_table_;
+
+ // Externally-owned, nullable storage for character counts of literals.
+ std::vector<size_t>* char_counts_;
+ size_t* total_char_counts_;
+
+ DISALLOW_COPY_AND_ASSIGN(HpackEncoder);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_ENCODER_H_
diff --git a/chromium/net/spdy/hpack_encoder_test.cc b/chromium/net/spdy/hpack_encoder_test.cc
new file mode 100644
index 00000000000..af04d8f2e38
--- /dev/null
+++ b/chromium/net/spdy/hpack_encoder_test.cc
@@ -0,0 +1,453 @@
+// Copyright 2014 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/spdy/hpack_encoder.h"
+
+#include <map>
+#include <string>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+using testing::ElementsAre;
+
+namespace test {
+
+class HpackHeaderTablePeer {
+ public:
+ explicit HpackHeaderTablePeer(HpackHeaderTable* table)
+ : table_(table) {}
+
+ HpackHeaderTable::EntryTable* dynamic_entries() {
+ return &table_->dynamic_entries_;
+ }
+
+ private:
+ HpackHeaderTable* table_;
+};
+
+class HpackEncoderPeer {
+ public:
+ typedef HpackEncoder::Representation Representation;
+ typedef HpackEncoder::Representations Representations;
+
+ explicit HpackEncoderPeer(HpackEncoder* encoder)
+ : encoder_(encoder) {}
+
+ HpackHeaderTable* table() {
+ return &encoder_->header_table_;
+ }
+ HpackHeaderTablePeer table_peer() {
+ return HpackHeaderTablePeer(table());
+ }
+ bool allow_huffman_compression() {
+ return encoder_->allow_huffman_compression_;
+ }
+ void set_allow_huffman_compression(bool allow) {
+ encoder_->allow_huffman_compression_ = allow;
+ }
+ void EmitString(StringPiece str) {
+ encoder_->EmitString(str);
+ }
+ void TakeString(string* out) {
+ encoder_->output_stream_.TakeString(out);
+ }
+ void UpdateCharacterCounts(StringPiece str) {
+ encoder_->UpdateCharacterCounts(str);
+ }
+ static void CookieToCrumbs(StringPiece cookie,
+ std::vector<StringPiece>* out) {
+ Representations tmp;
+ HpackEncoder::CookieToCrumbs(make_pair("", cookie), &tmp);
+
+ out->clear();
+ for (size_t i = 0; i != tmp.size(); ++i) {
+ out->push_back(tmp[i].second);
+ }
+ }
+
+ private:
+ HpackEncoder* encoder_;
+};
+
+} // namespace test
+
+namespace {
+
+using std::map;
+using testing::ElementsAre;
+
+class HpackEncoderTest : public ::testing::Test {
+ protected:
+ typedef test::HpackEncoderPeer::Representations Representations;
+
+ HpackEncoderTest()
+ : encoder_(ObtainHpackHuffmanTable()),
+ peer_(&encoder_) {}
+
+ virtual void SetUp() {
+ static_ = peer_.table()->GetByIndex(1);
+ // Populate dynamic entries into the table fixture. For simplicity each
+ // entry has name.size() + value.size() == 10.
+ key_1_ = peer_.table()->TryAddEntry("key1", "value1");
+ key_2_ = peer_.table()->TryAddEntry("key2", "value2");
+ cookie_a_ = peer_.table()->TryAddEntry("cookie", "a=bb");
+ cookie_c_ = peer_.table()->TryAddEntry("cookie", "c=dd");
+
+ // No further insertions may occur without evictions.
+ peer_.table()->SetMaxSize(peer_.table()->size());
+
+ // Disable Huffman coding by default. Most tests don't care about it.
+ peer_.set_allow_huffman_compression(false);
+ }
+
+ void ExpectIndex(size_t index) {
+ expected_.AppendPrefix(kIndexedOpcode);
+ expected_.AppendUint32(index);
+ }
+ void ExpectIndexedLiteral(HpackEntry* key_entry, StringPiece value) {
+ expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
+ expected_.AppendUint32(IndexOf(key_entry));
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(value.size());
+ expected_.AppendBytes(value);
+ }
+ void ExpectIndexedLiteral(StringPiece name, StringPiece value) {
+ expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
+ expected_.AppendUint32(0);
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(name.size());
+ expected_.AppendBytes(name);
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(value.size());
+ expected_.AppendBytes(value);
+ }
+ void ExpectNonIndexedLiteral(StringPiece name, StringPiece value) {
+ expected_.AppendPrefix(kLiteralNoIndexOpcode);
+ expected_.AppendUint32(0);
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(name.size());
+ expected_.AppendBytes(name);
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(value.size());
+ expected_.AppendBytes(value);
+ }
+ void CompareWithExpectedEncoding(const map<string, string>& header_set) {
+ string expected_out, actual_out;
+ expected_.TakeString(&expected_out);
+ EXPECT_TRUE(encoder_.EncodeHeaderSet(header_set, &actual_out));
+ EXPECT_EQ(expected_out, actual_out);
+ }
+ size_t IndexOf(HpackEntry* entry) {
+ return peer_.table()->IndexOf(entry);
+ }
+
+ HpackEncoder encoder_;
+ test::HpackEncoderPeer peer_;
+
+ HpackEntry* static_;
+ HpackEntry* key_1_;
+ HpackEntry* key_2_;
+ HpackEntry* cookie_a_;
+ HpackEntry* cookie_c_;
+
+ HpackOutputStream expected_;
+};
+
+TEST_F(HpackEncoderTest, SingleDynamicIndex) {
+ ExpectIndex(IndexOf(key_2_));
+
+ map<string, string> headers;
+ headers[key_2_->name()] = key_2_->value();
+ CompareWithExpectedEncoding(headers);
+
+ // |key_2_| was added to the reference set.
+ EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(key_2_));
+}
+
+TEST_F(HpackEncoderTest, SingleStaticIndex) {
+ ExpectIndex(IndexOf(static_));
+
+ map<string, string> headers;
+ headers[static_->name()] = static_->value();
+ CompareWithExpectedEncoding(headers);
+
+ // A new entry copying |static_| was inserted and added to the reference set.
+ HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
+ EXPECT_NE(static_, new_entry);
+ EXPECT_EQ(static_->name(), new_entry->name());
+ EXPECT_EQ(static_->value(), new_entry->value());
+ EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
+}
+
+TEST_F(HpackEncoderTest, SingleStaticIndexTooLarge) {
+ peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
+ ExpectIndex(IndexOf(static_));
+
+ map<string, string> headers;
+ headers[static_->name()] = static_->value();
+ CompareWithExpectedEncoding(headers);
+
+ EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
+ EXPECT_EQ(0u, peer_.table()->reference_set().size());
+}
+
+TEST_F(HpackEncoderTest, SingleLiteralWithIndexName) {
+ ExpectIndexedLiteral(key_2_, "value3");
+
+ map<string, string> headers;
+ headers[key_2_->name()] = "value3";
+ CompareWithExpectedEncoding(headers);
+
+ // A new entry was inserted and added to the reference set.
+ HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
+ EXPECT_EQ(new_entry->name(), key_2_->name());
+ EXPECT_EQ(new_entry->value(), "value3");
+ EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
+}
+
+TEST_F(HpackEncoderTest, SingleLiteralWithLiteralName) {
+ ExpectIndexedLiteral("key3", "value3");
+
+ map<string, string> headers;
+ headers["key3"] = "value3";
+ CompareWithExpectedEncoding(headers);
+
+ // A new entry was inserted and added to the reference set.
+ HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
+ EXPECT_EQ(new_entry->name(), "key3");
+ EXPECT_EQ(new_entry->value(), "value3");
+ EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
+}
+
+TEST_F(HpackEncoderTest, SingleLiteralTooLarge) {
+ peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
+
+ ExpectIndexedLiteral("key3", "value3");
+
+ // A header overflowing the header table is still emitted.
+ // The header table is empty.
+ map<string, string> headers;
+ headers["key3"] = "value3";
+ CompareWithExpectedEncoding(headers);
+
+ EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
+ EXPECT_EQ(0u, peer_.table()->reference_set().size());
+}
+
+TEST_F(HpackEncoderTest, SingleInReferenceSet) {
+ peer_.table()->Toggle(key_2_);
+
+ // Nothing is emitted.
+ map<string, string> headers;
+ headers[key_2_->name()] = key_2_->value();
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, ExplicitToggleOff) {
+ peer_.table()->Toggle(key_1_);
+ peer_.table()->Toggle(key_2_);
+
+ // |key_1_| is explicitly toggled off.
+ ExpectIndex(IndexOf(key_1_));
+
+ map<string, string> headers;
+ headers[key_2_->name()] = key_2_->value();
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, ImplicitToggleOff) {
+ peer_.table()->Toggle(key_1_);
+ peer_.table()->Toggle(key_2_);
+
+ // |key_1_| is evicted. No explicit toggle required.
+ ExpectIndexedLiteral("key3", "value3");
+
+ map<string, string> headers;
+ headers[key_2_->name()] = key_2_->value();
+ headers["key3"] = "value3";
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, ExplicitDoubleToggle) {
+ peer_.table()->Toggle(key_1_);
+
+ // |key_1_| is double-toggled prior to being evicted.
+ ExpectIndex(IndexOf(key_1_));
+ ExpectIndex(IndexOf(key_1_));
+ ExpectIndexedLiteral("key3", "value3");
+
+ map<string, string> headers;
+ headers[key_1_->name()] = key_1_->value();
+ headers["key3"] = "value3";
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, EmitThanEvict) {
+ // |key_1_| is toggled and placed into the reference set,
+ // and then immediately evicted by "key3".
+ ExpectIndex(IndexOf(key_1_));
+ ExpectIndexedLiteral("key3", "value3");
+
+ map<string, string> headers;
+ headers[key_1_->name()] = key_1_->value();
+ headers["key3"] = "value3";
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, CookieHeaderIsCrumbled) {
+ peer_.table()->Toggle(cookie_a_);
+
+ // |cookie_a_| is already in the reference set. |cookie_c_| is
+ // toggled, and "e=ff" is emitted with an indexed name.
+ ExpectIndex(IndexOf(cookie_c_));
+ ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
+
+ map<string, string> headers;
+ headers["cookie"] = "e=ff; a=bb; c=dd";
+ CompareWithExpectedEncoding(headers);
+}
+
+TEST_F(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) {
+ peer_.set_allow_huffman_compression(true);
+
+ // Compactable string. Uses Huffman coding.
+ peer_.EmitString("feedbeef");
+ expected_.AppendPrefix(kStringLiteralHuffmanEncoded);
+ expected_.AppendUint32(6);
+ expected_.AppendBytes("\xE0\xB5\xD3\xBDk\xE1");
+
+ // Non-compactable. Uses identity coding.
+ peer_.EmitString("@@@@@@");
+ expected_.AppendPrefix(kStringLiteralIdentityEncoded);
+ expected_.AppendUint32(6);
+ expected_.AppendBytes("@@@@@@");
+
+ string expected_out, actual_out;
+ expected_.TakeString(&expected_out);
+ peer_.TakeString(&actual_out);
+ EXPECT_EQ(expected_out, actual_out);
+}
+
+TEST_F(HpackEncoderTest, EncodingWithoutCompression) {
+ // Implementation should internally disable.
+ peer_.set_allow_huffman_compression(true);
+
+ ExpectNonIndexedLiteral(":path", "/index.html");
+ ExpectNonIndexedLiteral("cookie", "foo=bar; baz=bing");
+ ExpectNonIndexedLiteral("hello", "goodbye");
+
+ map<string, string> headers;
+ headers[":path"] = "/index.html";
+ headers["cookie"] = "foo=bar; baz=bing";
+ headers["hello"] = "goodbye";
+
+ string expected_out, actual_out;
+ expected_.TakeString(&expected_out);
+ encoder_.EncodeHeaderSetWithoutCompression(headers, &actual_out);
+ EXPECT_EQ(expected_out, actual_out);
+}
+
+TEST_F(HpackEncoderTest, MultipleEncodingPasses) {
+ // Pass 1: key_1_ and cookie_a_ are toggled on.
+ {
+ map<string, string> headers;
+ headers["key1"] = "value1";
+ headers["cookie"] = "a=bb";
+
+ ExpectIndex(IndexOf(cookie_a_));
+ ExpectIndex(IndexOf(key_1_));
+ CompareWithExpectedEncoding(headers);
+ }
+ // Pass 2: |key_1_| is double-toggled and evicted.
+ // |key_2_| & |cookie_c_| are toggled on.
+ // |cookie_a_| is toggled off.
+ // A new cookie entry is added.
+ {
+ map<string, string> headers;
+ headers["key1"] = "value1";
+ headers["key2"] = "value2";
+ headers["cookie"] = "c=dd; e=ff";
+
+ ExpectIndex(IndexOf(cookie_c_)); // Toggle on.
+ ExpectIndex(IndexOf(key_1_)); // Double-toggle before eviction.
+ ExpectIndex(IndexOf(key_1_));
+ ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
+
+ ExpectIndex(IndexOf(key_2_) + 1); // Toggle on. Add 1 to reflect insertion.
+ ExpectIndex(IndexOf(cookie_a_) + 1); // Toggle off.
+ CompareWithExpectedEncoding(headers);
+ }
+ // Pass 3: |key_2_| is evicted and implicitly toggled off.
+ // |cookie_c_| is explicitly toggled off.
+ // "key1" is re-inserted.
+ {
+ map<string, string> headers;
+ headers["key1"] = "value1";
+ headers["key3"] = "value3";
+ headers["cookie"] = "e=ff";
+
+ ExpectIndexedLiteral("key1", "value1");
+ ExpectIndexedLiteral("key3", "value3");
+ ExpectIndex(IndexOf(cookie_c_) + 2); // Toggle off. Add 1 for insertion.
+
+ CompareWithExpectedEncoding(headers);
+ }
+}
+
+TEST_F(HpackEncoderTest, CookieToCrumbs) {
+ test::HpackEncoderPeer peer(NULL);
+ std::vector<StringPiece> out;
+
+ // A space after ';' is consumed. All other spaces remain. ';' at beginning
+ // and end of string produce empty crumbs. Duplicate crumbs are removed.
+ // See section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
+ // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
+ peer.CookieToCrumbs(" foo=1;bar=2 ; bar=3; bing=4; ", &out);
+ EXPECT_THAT(out, ElementsAre("", " bing=4", " foo=1", "bar=2 ", "bar=3"));
+
+ peer.CookieToCrumbs(";;foo = bar ;; ;baz =bing", &out);
+ EXPECT_THAT(out, ElementsAre("", "baz =bing", "foo = bar "));
+
+ peer.CookieToCrumbs("baz=bing; foo=bar; baz=bing", &out);
+ EXPECT_THAT(out, ElementsAre("baz=bing", "foo=bar"));
+
+ peer.CookieToCrumbs("baz=bing", &out);
+ EXPECT_THAT(out, ElementsAre("baz=bing"));
+
+ peer.CookieToCrumbs("", &out);
+ EXPECT_THAT(out, ElementsAre(""));
+
+ peer.CookieToCrumbs("foo;bar; baz;baz;bing;", &out);
+ EXPECT_THAT(out, ElementsAre("", "bar", "baz", "bing", "foo"));
+}
+
+TEST_F(HpackEncoderTest, UpdateCharacterCounts) {
+ std::vector<size_t> counts(256, 0);
+ size_t total_counts = 0;
+ encoder_.SetCharCountsStorage(&counts, &total_counts);
+
+ char kTestString[] = "foo\0\1\xff""boo";
+ peer_.UpdateCharacterCounts(
+ StringPiece(kTestString, arraysize(kTestString) - 1));
+
+ std::vector<size_t> expect(256, 0);
+ expect[static_cast<uint8>('f')] = 1;
+ expect[static_cast<uint8>('o')] = 4;
+ expect[static_cast<uint8>('\0')] = 1;
+ expect[static_cast<uint8>('\1')] = 1;
+ expect[static_cast<uint8>('\xff')] = 1;
+ expect[static_cast<uint8>('b')] = 1;
+
+ EXPECT_EQ(expect, counts);
+ EXPECT_EQ(9u, total_counts);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_entry.cc b/chromium/net/spdy/hpack_entry.cc
new file mode 100644
index 00000000000..f56f181deb7
--- /dev/null
+++ b/chromium/net/spdy/hpack_entry.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 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/spdy/hpack_entry.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/spdy/hpack_string_util.h"
+
+namespace net {
+
+using base::StringPiece;
+
+const size_t HpackEntry::kSizeOverhead = 32;
+
+HpackEntry::HpackEntry(StringPiece name,
+ StringPiece value,
+ bool is_static,
+ size_t insertion_index)
+ : name_(name.data(), name.size()),
+ value_(value.data(), value.size()),
+ insertion_index_(insertion_index),
+ state_(0),
+ type_(is_static ? STATIC : DYNAMIC) {
+}
+
+HpackEntry::HpackEntry(StringPiece name, StringPiece value)
+ : name_(name.data(), name.size()),
+ value_(value.data(), value.size()),
+ insertion_index_(0),
+ state_(0),
+ type_(LOOKUP) {
+}
+
+HpackEntry::HpackEntry()
+ : insertion_index_(0),
+ state_(0),
+ type_(LOOKUP) {
+}
+
+HpackEntry::~HpackEntry() {}
+
+// static
+size_t HpackEntry::Size(StringPiece name, StringPiece value) {
+ return name.size() + value.size() + kSizeOverhead;
+}
+size_t HpackEntry::Size() const {
+ return Size(name(), value());
+}
+
+std::string HpackEntry::GetDebugString() const {
+ return "{ name: \"" + name_ +
+ "\", value: \"" + value_ +
+ "\", " + (IsStatic() ? "static" : "dynamic") +
+ ", state: " + base::IntToString(state_) + " }";
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_entry.h b/chromium/net/spdy/hpack_entry.h
new file mode 100644
index 00000000000..53d7e28614d
--- /dev/null
+++ b/chromium/net/spdy/hpack_entry.h
@@ -0,0 +1,98 @@
+// Copyright 2014 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_SPDY_HPACK_ENTRY_H_
+#define NET_SPDY_HPACK_ENTRY_H_
+
+#include <cstddef>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+// A structure for an entry in the header table (3.1.2) and the
+// reference set (3.1.3).
+class NET_EXPORT_PRIVATE HpackEntry {
+ public:
+ // The constant amount added to name().size() and value().size() to
+ // get the size of an HpackEntry as defined in 3.3.1.
+ static const size_t kSizeOverhead;
+
+ // Creates an entry. Preconditions:
+ // - |is_static| captures whether this entry is a member of the static
+ // or dynamic header table.
+ // - |insertion_index| is this entry's index in the total set of entries ever
+ // inserted into the header table (including static entries).
+ //
+ // The combination of |is_static| and |insertion_index| allows an
+ // HpackEntryTable to determine the index of an HpackEntry in O(1) time.
+ HpackEntry(base::StringPiece name,
+ base::StringPiece value,
+ bool is_static,
+ size_t insertion_index);
+
+ // Create a 'lookup' entry (only) suitable for querying a HpackEntrySet. The
+ // instance InsertionIndex() always returns 0 and IsLookup() returns true.
+ HpackEntry(base::StringPiece name, base::StringPiece value);
+
+ // Creates an entry with empty name and value. Only defined so that
+ // entries can be stored in STL containers.
+ HpackEntry();
+
+ ~HpackEntry();
+
+ const std::string& name() const { return name_; }
+ const std::string& value() const { return value_; }
+
+ // Returns whether this entry is a member of the static (as opposed to
+ // dynamic) table.
+ bool IsStatic() const { return type_ == STATIC; }
+
+ // Returns whether this entry is a lookup-only entry.
+ bool IsLookup() const { return type_ == LOOKUP; }
+
+ // Returns and sets the state of the entry, or zero if never set.
+ // The semantics of |state| are specific to the encoder or decoder.
+ uint8 state() const { return state_; }
+ void set_state(uint8 state) { state_ = state; }
+
+ // Used to compute the entry's index in the header table.
+ size_t InsertionIndex() const { return insertion_index_; }
+
+ // Returns the size of an entry as defined in 3.3.1.
+ static size_t Size(base::StringPiece name, base::StringPiece value);
+ size_t Size() const;
+
+ std::string GetDebugString() const;
+
+ private:
+ enum EntryType {
+ LOOKUP,
+ DYNAMIC,
+ STATIC,
+ };
+
+ // TODO(jgraettinger): Reduce copies, possibly via SpdyPinnableBufferPiece.
+ std::string name_;
+ std::string value_;
+
+ // The entry's index in the total set of entries ever inserted into the header
+ // table.
+ size_t insertion_index_;
+
+ uint8 state_;
+ EntryType type_;
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_ENTRY_H_
diff --git a/chromium/net/spdy/hpack_entry_test.cc b/chromium/net/spdy/hpack_entry_test.cc
new file mode 100644
index 00000000000..919a316a9b3
--- /dev/null
+++ b/chromium/net/spdy/hpack_entry_test.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 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/spdy/hpack_entry.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using std::string;
+
+class HpackEntryTest : public ::testing::Test {
+ protected:
+ HpackEntryTest()
+ : name_("header-name"),
+ value_("header value"),
+ total_insertions_(0),
+ table_size_(0) {}
+
+ // These builders maintain the same external table invariants that a "real"
+ // table (ie HpackHeaderTable) would.
+ HpackEntry StaticEntry() {
+ return HpackEntry(name_, value_, true, total_insertions_++);
+ }
+ HpackEntry DynamicEntry() {
+ ++table_size_;
+ size_t index = total_insertions_++;
+ return HpackEntry(name_, value_, false, index);
+ }
+ void DropEntry() { --table_size_; }
+
+ size_t IndexOf(const HpackEntry& entry) const {
+ if (entry.IsStatic()) {
+ return 1 + entry.InsertionIndex() + table_size_;
+ } else {
+ return total_insertions_ - entry.InsertionIndex();
+ }
+ }
+
+ size_t Size() {
+ return name_.size() + value_.size() + HpackEntry::kSizeOverhead;
+ }
+
+ string name_, value_;
+
+ private:
+ // Referenced by HpackEntry instances.
+ size_t total_insertions_;
+ size_t table_size_;
+};
+
+TEST_F(HpackEntryTest, StaticConstructor) {
+ HpackEntry entry(StaticEntry());
+
+ EXPECT_EQ(name_, entry.name());
+ EXPECT_EQ(value_, entry.value());
+ EXPECT_TRUE(entry.IsStatic());
+ EXPECT_EQ(1u, IndexOf(entry));
+ EXPECT_EQ(0u, entry.state());
+ EXPECT_EQ(Size(), entry.Size());
+}
+
+TEST_F(HpackEntryTest, DynamicConstructor) {
+ HpackEntry entry(DynamicEntry());
+
+ EXPECT_EQ(name_, entry.name());
+ EXPECT_EQ(value_, entry.value());
+ EXPECT_FALSE(entry.IsStatic());
+ EXPECT_EQ(1u, IndexOf(entry));
+ EXPECT_EQ(0u, entry.state());
+ EXPECT_EQ(Size(), entry.Size());
+}
+
+TEST_F(HpackEntryTest, LookupConstructor) {
+ HpackEntry entry(name_, value_);
+
+ EXPECT_EQ(name_, entry.name());
+ EXPECT_EQ(value_, entry.value());
+ EXPECT_FALSE(entry.IsStatic());
+ EXPECT_EQ(0u, IndexOf(entry));
+ EXPECT_EQ(0u, entry.state());
+ EXPECT_EQ(Size(), entry.Size());
+}
+
+TEST_F(HpackEntryTest, DefaultConstructor) {
+ HpackEntry entry;
+
+ EXPECT_TRUE(entry.name().empty());
+ EXPECT_TRUE(entry.value().empty());
+ EXPECT_EQ(0u, entry.state());
+ EXPECT_EQ(HpackEntry::kSizeOverhead, entry.Size());
+}
+
+TEST_F(HpackEntryTest, IndexUpdate) {
+ HpackEntry static1(StaticEntry());
+ HpackEntry static2(StaticEntry());
+
+ EXPECT_EQ(1u, IndexOf(static1));
+ EXPECT_EQ(2u, IndexOf(static2));
+
+ HpackEntry dynamic1(DynamicEntry());
+ HpackEntry dynamic2(DynamicEntry());
+
+ EXPECT_EQ(1u, IndexOf(dynamic2));
+ EXPECT_EQ(2u, IndexOf(dynamic1));
+ EXPECT_EQ(3u, IndexOf(static1));
+ EXPECT_EQ(4u, IndexOf(static2));
+
+ DropEntry(); // Drops |dynamic1|.
+
+ EXPECT_EQ(1u, IndexOf(dynamic2));
+ EXPECT_EQ(2u, IndexOf(static1));
+ EXPECT_EQ(3u, IndexOf(static2));
+
+ HpackEntry dynamic3(DynamicEntry());
+
+ EXPECT_EQ(1u, IndexOf(dynamic3));
+ EXPECT_EQ(2u, IndexOf(dynamic2));
+ EXPECT_EQ(3u, IndexOf(static1));
+ EXPECT_EQ(4u, IndexOf(static2));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_header_table.cc b/chromium/net/spdy/hpack_header_table.cc
new file mode 100644
index 00000000000..130d7a56374
--- /dev/null
+++ b/chromium/net/spdy/hpack_header_table.cc
@@ -0,0 +1,306 @@
+// Copyright 2014 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/spdy/hpack_header_table.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_string_util.h"
+
+namespace net {
+
+using base::StringPiece;
+
+namespace {
+
+// An entry in the static table. Must be a POD in order to avoid
+// static initializers, i.e. no user-defined constructors or
+// destructors.
+struct StaticEntry {
+ const char* const name;
+ const size_t name_len;
+ const char* const value;
+ const size_t value_len;
+};
+
+// The "constructor" for a StaticEntry that computes the lengths at
+// compile time.
+#define STATIC_ENTRY(name, value) \
+ { name, arraysize(name) - 1, value, arraysize(value) - 1 }
+
+const StaticEntry kStaticTable[] = {
+ STATIC_ENTRY(":authority" , ""), // 1
+ STATIC_ENTRY(":method" , "GET"), // 2
+ STATIC_ENTRY(":method" , "POST"), // 3
+ STATIC_ENTRY(":path" , "/"), // 4
+ STATIC_ENTRY(":path" , "/index.html"), // 5
+ STATIC_ENTRY(":scheme" , "http"), // 6
+ STATIC_ENTRY(":scheme" , "https"), // 7
+ STATIC_ENTRY(":status" , "200"), // 8
+ STATIC_ENTRY(":status" , "204"), // 9
+ STATIC_ENTRY(":status" , "206"), // 10
+ STATIC_ENTRY(":status" , "304"), // 11
+ STATIC_ENTRY(":status" , "400"), // 12
+ STATIC_ENTRY(":status" , "404"), // 13
+ STATIC_ENTRY(":status" , "500"), // 14
+ STATIC_ENTRY("accept-charset" , ""), // 15
+ STATIC_ENTRY("accept-encoding" , ""), // 16
+ STATIC_ENTRY("accept-language" , ""), // 17
+ STATIC_ENTRY("accept-ranges" , ""), // 18
+ STATIC_ENTRY("accept" , ""), // 19
+ STATIC_ENTRY("access-control-allow-origin" , ""), // 20
+ STATIC_ENTRY("age" , ""), // 21
+ STATIC_ENTRY("allow" , ""), // 22
+ STATIC_ENTRY("authorization" , ""), // 23
+ STATIC_ENTRY("cache-control" , ""), // 24
+ STATIC_ENTRY("content-disposition" , ""), // 25
+ STATIC_ENTRY("content-encoding" , ""), // 26
+ STATIC_ENTRY("content-language" , ""), // 27
+ STATIC_ENTRY("content-length" , ""), // 28
+ STATIC_ENTRY("content-location" , ""), // 29
+ STATIC_ENTRY("content-range" , ""), // 30
+ STATIC_ENTRY("content-type" , ""), // 31
+ STATIC_ENTRY("cookie" , ""), // 32
+ STATIC_ENTRY("date" , ""), // 33
+ STATIC_ENTRY("etag" , ""), // 34
+ STATIC_ENTRY("expect" , ""), // 35
+ STATIC_ENTRY("expires" , ""), // 36
+ STATIC_ENTRY("from" , ""), // 37
+ STATIC_ENTRY("host" , ""), // 38
+ STATIC_ENTRY("if-match" , ""), // 39
+ STATIC_ENTRY("if-modified-since" , ""), // 40
+ STATIC_ENTRY("if-none-match" , ""), // 41
+ STATIC_ENTRY("if-range" , ""), // 42
+ STATIC_ENTRY("if-unmodified-since" , ""), // 43
+ STATIC_ENTRY("last-modified" , ""), // 44
+ STATIC_ENTRY("link" , ""), // 45
+ STATIC_ENTRY("location" , ""), // 46
+ STATIC_ENTRY("max-forwards" , ""), // 47
+ STATIC_ENTRY("proxy-authenticate" , ""), // 48
+ STATIC_ENTRY("proxy-authorization" , ""), // 49
+ STATIC_ENTRY("range" , ""), // 50
+ STATIC_ENTRY("referer" , ""), // 51
+ STATIC_ENTRY("refresh" , ""), // 52
+ STATIC_ENTRY("retry-after" , ""), // 53
+ STATIC_ENTRY("server" , ""), // 54
+ STATIC_ENTRY("set-cookie" , ""), // 55
+ STATIC_ENTRY("strict-transport-security" , ""), // 56
+ STATIC_ENTRY("transfer-encoding" , ""), // 57
+ STATIC_ENTRY("user-agent" , ""), // 58
+ STATIC_ENTRY("vary" , ""), // 59
+ STATIC_ENTRY("via" , ""), // 60
+ STATIC_ENTRY("www-authenticate" , ""), // 61
+};
+
+#undef STATIC_ENTRY
+
+} // namespace
+
+bool HpackHeaderTable::EntryComparator::operator() (
+ const HpackEntry* lhs, const HpackEntry* rhs) const {
+ int result = lhs->name().compare(rhs->name());
+ if (result != 0)
+ return result < 0;
+ result = lhs->value().compare(rhs->value());
+ if (result != 0)
+ return result < 0;
+ const size_t lhs_index = table_->IndexOf(lhs);
+ const size_t rhs_index = table_->IndexOf(rhs);
+ DCHECK(lhs == rhs || lhs_index != rhs_index)
+ << "lhs: (" << lhs->name() << ", " << rhs->value() << ") rhs: ("
+ << rhs->name() << ", " << rhs->value() << ")"
+ << " lhs index: " << lhs_index << " rhs index: " << rhs_index;
+ return lhs_index < rhs_index;
+}
+
+HpackHeaderTable::HpackHeaderTable()
+ : index_(EntryComparator(this)),
+ reference_set_(EntryComparator(this)),
+ settings_size_bound_(kDefaultHeaderTableSizeSetting),
+ size_(0),
+ max_size_(kDefaultHeaderTableSizeSetting),
+ total_insertions_(0) {
+ for (const StaticEntry* it = kStaticTable;
+ it != kStaticTable + arraysize(kStaticTable); ++it) {
+ static_entries_.push_back(
+ HpackEntry(StringPiece(it->name, it->name_len),
+ StringPiece(it->value, it->value_len),
+ true, // is_static
+ total_insertions_));
+ CHECK(index_.insert(&static_entries_.back()).second);
+
+ ++total_insertions_;
+ }
+}
+
+HpackHeaderTable::~HpackHeaderTable() {}
+
+HpackEntry* HpackHeaderTable::GetByIndex(size_t index) {
+ if (index == 0) {
+ return NULL;
+ }
+ index -= 1;
+ if (index < dynamic_entries_.size()) {
+ return &dynamic_entries_[index];
+ }
+ index -= dynamic_entries_.size();
+ if (index < static_entries_.size()) {
+ return &static_entries_[index];
+ }
+ return NULL;
+}
+
+HpackEntry* HpackHeaderTable::GetByName(StringPiece name) {
+ HpackEntry query(name, "");
+ OrderedEntrySet::const_iterator it = index_.lower_bound(&query);
+ if (it != index_.end() && (*it)->name() == name) {
+ return *it;
+ }
+ return NULL;
+}
+
+HpackEntry* HpackHeaderTable::GetByNameAndValue(StringPiece name,
+ StringPiece value) {
+ HpackEntry query(name, value);
+ OrderedEntrySet::const_iterator it = index_.lower_bound(&query);
+ if (it != index_.end() && (*it)->name() == name && (*it)->value() == value) {
+ return *it;
+ }
+ return NULL;
+}
+
+size_t HpackHeaderTable::IndexOf(const HpackEntry* entry) const {
+ if (entry->IsLookup()) {
+ return 0;
+ } else if (entry->IsStatic()) {
+ return 1 + entry->InsertionIndex() + dynamic_entries_.size();
+ } else {
+ return total_insertions_ - entry->InsertionIndex();
+ }
+}
+
+void HpackHeaderTable::SetMaxSize(size_t max_size) {
+ CHECK_LE(max_size, settings_size_bound_);
+
+ max_size_ = max_size;
+ if (size_ > max_size_) {
+ Evict(EvictionCountToReclaim(size_ - max_size_));
+ CHECK_LE(size_, max_size_);
+ }
+}
+
+void HpackHeaderTable::SetSettingsHeaderTableSize(size_t settings_size) {
+ settings_size_bound_ = settings_size;
+ if (settings_size_bound_ < max_size_) {
+ SetMaxSize(settings_size_bound_);
+ }
+}
+
+void HpackHeaderTable::EvictionSet(StringPiece name,
+ StringPiece value,
+ EntryTable::iterator* begin_out,
+ EntryTable::iterator* end_out) {
+ size_t eviction_count = EvictionCountForEntry(name, value);
+ *begin_out = dynamic_entries_.end() - eviction_count;
+ *end_out = dynamic_entries_.end();
+}
+
+size_t HpackHeaderTable::EvictionCountForEntry(StringPiece name,
+ StringPiece value) const {
+ size_t available_size = max_size_ - size_;
+ size_t entry_size = HpackEntry::Size(name, value);
+
+ if (entry_size <= available_size) {
+ // No evictions are required.
+ return 0;
+ }
+ return EvictionCountToReclaim(entry_size - available_size);
+}
+
+size_t HpackHeaderTable::EvictionCountToReclaim(size_t reclaim_size) const {
+ size_t count = 0;
+ for (EntryTable::const_reverse_iterator it = dynamic_entries_.rbegin();
+ it != dynamic_entries_.rend() && reclaim_size != 0; ++it, ++count) {
+ reclaim_size -= std::min(reclaim_size, it->Size());
+ }
+ return count;
+}
+
+void HpackHeaderTable::Evict(size_t count) {
+ for (size_t i = 0; i != count; ++i) {
+ CHECK(!dynamic_entries_.empty());
+ HpackEntry* entry = &dynamic_entries_.back();
+
+ size_ -= entry->Size();
+ CHECK_EQ(1u, index_.erase(entry));
+ reference_set_.erase(entry);
+ dynamic_entries_.pop_back();
+ }
+}
+
+HpackEntry* HpackHeaderTable::TryAddEntry(StringPiece name, StringPiece value) {
+ Evict(EvictionCountForEntry(name, value));
+
+ size_t entry_size = HpackEntry::Size(name, value);
+ if (entry_size > (max_size_ - size_)) {
+ // Entire table has been emptied, but there's still insufficient room.
+ DCHECK(dynamic_entries_.empty());
+ DCHECK_EQ(0u, size_);
+ return NULL;
+ }
+ dynamic_entries_.push_front(HpackEntry(name,
+ value,
+ false, // is_static
+ total_insertions_));
+ CHECK(index_.insert(&dynamic_entries_.front()).second);
+
+ size_ += entry_size;
+ ++total_insertions_;
+
+ return &dynamic_entries_.front();
+}
+
+void HpackHeaderTable::ClearReferenceSet() {
+ for (OrderedEntrySet::iterator it = reference_set_.begin();
+ it != reference_set_.end(); ++it) {
+ (*it)->set_state(0);
+ }
+ reference_set_.clear();
+}
+
+bool HpackHeaderTable::Toggle(HpackEntry* entry) {
+ CHECK(!entry->IsStatic());
+ CHECK_EQ(0u, entry->state());
+
+ std::pair<OrderedEntrySet::iterator, bool> insert_result =
+ reference_set_.insert(entry);
+ if (insert_result.second) {
+ return true;
+ } else {
+ reference_set_.erase(insert_result.first);
+ return false;
+ }
+}
+
+void HpackHeaderTable::DebugLogTableState() const {
+ DVLOG(2) << "Reference Set:";
+ for (OrderedEntrySet::const_iterator it = reference_set_.begin();
+ it != reference_set_.end(); ++it) {
+ DVLOG(2) << " " << (*it)->GetDebugString();
+ }
+ DVLOG(2) << "Dynamic table:";
+ for (EntryTable::const_iterator it = dynamic_entries_.begin();
+ it != dynamic_entries_.end(); ++it) {
+ DVLOG(2) << " " << it->GetDebugString();
+ }
+ DVLOG(2) << "Full Index:";
+ for (OrderedEntrySet::const_iterator it = index_.begin();
+ it != index_.end(); ++it) {
+ DVLOG(2) << " " << (*it)->GetDebugString();
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_header_table.h b/chromium/net/spdy/hpack_header_table.h
new file mode 100644
index 00000000000..5f70b5bab64
--- /dev/null
+++ b/chromium/net/spdy/hpack_header_table.h
@@ -0,0 +1,151 @@
+// Copyright 2014 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_SPDY_HPACK_HEADER_TABLE_H_
+#define NET_SPDY_HPACK_HEADER_TABLE_H_
+
+#include <cstddef>
+#include <deque>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_entry.h"
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+namespace test {
+class HpackHeaderTablePeer;
+} // namespace test
+
+// A data structure for both the header table (described in 3.1.2) and
+// the reference set (3.1.3).
+class NET_EXPORT_PRIVATE HpackHeaderTable {
+ public:
+ friend class test::HpackHeaderTablePeer;
+
+ // HpackHeaderTable takes advantage of the deque property that references
+ // remain valid, so long as insertions & deletions are at the head & tail.
+ // If this changes (eg we start to drop entries from the middle of the table),
+ // this needs to be a std::list, in which case |index_| can be trivially
+ // extended to map to list iterators.
+ typedef std::deque<HpackEntry> EntryTable;
+
+ // Implements a total ordering of HpackEntry on name(), value(), then index
+ // ascending. Note that index may change over the lifetime of an HpackEntry,
+ // but the relative index order of two entries will not. This comparator is
+ // composed with the 'lookup' HpackEntry constructor to allow for efficient
+ // lower-bounding of matching entries.
+ struct NET_EXPORT_PRIVATE EntryComparator {
+ explicit EntryComparator(HpackHeaderTable* table) : table_(table) {}
+
+ bool operator() (const HpackEntry* lhs, const HpackEntry* rhs) const;
+
+ private:
+ HpackHeaderTable* table_;
+ };
+ typedef std::set<HpackEntry*, EntryComparator> OrderedEntrySet;
+
+ HpackHeaderTable();
+
+ ~HpackHeaderTable();
+
+ // Last-aknowledged value of SETTINGS_HEADER_TABLE_SIZE.
+ size_t settings_size_bound() { return settings_size_bound_; }
+
+ // Current and maximum estimated byte size of the table, as described in
+ // 3.3.1. Notably, this is /not/ the number of entries in the table.
+ size_t size() const { return size_; }
+ size_t max_size() const { return max_size_; }
+
+ const OrderedEntrySet& reference_set() {
+ return reference_set_;
+ }
+
+ // Returns the entry matching the index, or NULL.
+ HpackEntry* GetByIndex(size_t index);
+
+ // Returns the lowest-value entry having |name|, or NULL.
+ HpackEntry* GetByName(base::StringPiece name);
+
+ // Returns the lowest-index matching entry, or NULL.
+ HpackEntry* GetByNameAndValue(base::StringPiece name,
+ base::StringPiece value);
+
+ // Returns the index of an entry within this header table.
+ size_t IndexOf(const HpackEntry* entry) const;
+
+ // Sets the maximum size of the header table, evicting entries if
+ // necessary as described in 3.3.2.
+ void SetMaxSize(size_t max_size);
+
+ // Sets the SETTINGS_HEADER_TABLE_SIZE bound of the table. Will call
+ // SetMaxSize() as needed to preserve max_size() <= settings_size_bound().
+ void SetSettingsHeaderTableSize(size_t settings_size);
+
+ // Determine the set of entries which would be evicted by the insertion
+ // of |name| & |value| into the table, as per section 3.3.3. No eviction
+ // actually occurs. The set is returned via range [begin_out, end_out).
+ void EvictionSet(base::StringPiece name, base::StringPiece value,
+ EntryTable::iterator* begin_out,
+ EntryTable::iterator* end_out);
+
+ // Adds an entry for the representation, evicting entries as needed. |name|
+ // and |value| must not be owned by an entry which could be evicted. The
+ // added HpackEntry is returned, or NULL is returned if all entries were
+ // evicted and the empty table is of insufficent size for the representation.
+ HpackEntry* TryAddEntry(base::StringPiece name, base::StringPiece value);
+
+ // Toggles the presence of a dynamic entry in the reference set. Returns
+ // true if the entry was added, or false if removed. It is an error to
+ // Toggle(entry) if |entry->state()| != 0.
+ bool Toggle(HpackEntry* entry);
+
+ // Removes all entries from the reference set. Sets the state of each removed
+ // entry to zero.
+ void ClearReferenceSet();
+
+ void DebugLogTableState() const;
+
+ private:
+ // Returns number of evictions required to enter |name| & |value|.
+ size_t EvictionCountForEntry(base::StringPiece name,
+ base::StringPiece value) const;
+
+ // Returns number of evictions required to reclaim |reclaim_size| table size.
+ size_t EvictionCountToReclaim(size_t reclaim_size) const;
+
+ // Evicts |count| oldest entries from the table.
+ void Evict(size_t count);
+
+ EntryTable dynamic_entries_;
+ EntryTable static_entries_;
+
+ // Full table index, over |dynamic_entries_| and |static_entries_|.
+ OrderedEntrySet index_;
+ // The reference set is strictly a subset of |dynamic_entries_|.
+ OrderedEntrySet reference_set_;
+
+ // Last acknowledged value for SETTINGS_HEADER_TABLE_SIZE.
+ size_t settings_size_bound_;
+
+ // Estimated current and maximum byte size of the table.
+ // |max_size_| <= |settings_header_table_size_|
+ size_t size_;
+ size_t max_size_;
+
+ // Total number of table insertions which have occurred. Referenced by
+ // IndexOf() for determination of an HpackEntry's table index.
+ size_t total_insertions_;
+
+ DISALLOW_COPY_AND_ASSIGN(HpackHeaderTable);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_HEADER_TABLE_H_
diff --git a/chromium/net/spdy/hpack_header_table_test.cc b/chromium/net/spdy/hpack_header_table_test.cc
new file mode 100644
index 00000000000..14c2c184e2b
--- /dev/null
+++ b/chromium/net/spdy/hpack_header_table_test.cc
@@ -0,0 +1,517 @@
+// Copyright 2014 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/spdy/hpack_header_table.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_entry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::distance;
+using std::string;
+
+namespace test {
+
+class HpackHeaderTablePeer {
+ public:
+ explicit HpackHeaderTablePeer(HpackHeaderTable* table)
+ : table_(table) {}
+
+ const HpackHeaderTable::EntryTable& dynamic_entries() {
+ return table_->dynamic_entries_;
+ }
+ const HpackHeaderTable::EntryTable& static_entries() {
+ return table_->static_entries_;
+ }
+ const HpackHeaderTable::OrderedEntrySet& index() {
+ return table_->index_;
+ }
+ std::vector<HpackEntry*> EvictionSet(StringPiece name, StringPiece value) {
+ HpackHeaderTable::EntryTable::iterator begin, end;
+ table_->EvictionSet(name, value, &begin, &end);
+ std::vector<HpackEntry*> result;
+ for (; begin != end; ++begin) {
+ result.push_back(&(*begin));
+ }
+ return result;
+ }
+ size_t total_insertions() {
+ return table_->total_insertions_;
+ }
+ size_t dynamic_entries_count() {
+ return table_->dynamic_entries_.size();
+ }
+ size_t EvictionCountForEntry(StringPiece name, StringPiece value) {
+ return table_->EvictionCountForEntry(name, value);
+ }
+ size_t EvictionCountToReclaim(size_t reclaim_size) {
+ return table_->EvictionCountToReclaim(reclaim_size);
+ }
+ void Evict(size_t count) {
+ return table_->Evict(count);
+ }
+
+ void AddStaticEntry(StringPiece name, StringPiece value) {
+ table_->static_entries_.push_back(
+ HpackEntry(name, value, true, table_->total_insertions_++));
+ }
+
+ void AddDynamicEntry(StringPiece name, StringPiece value) {
+ table_->dynamic_entries_.push_back(
+ HpackEntry(name, value, false, table_->total_insertions_++));
+ }
+
+ private:
+ HpackHeaderTable* table_;
+};
+
+} // namespace test
+
+namespace {
+
+class HpackHeaderTableTest : public ::testing::Test {
+ protected:
+ typedef std::vector<HpackEntry> HpackEntryVector;
+
+ HpackHeaderTableTest()
+ : table_(),
+ peer_(&table_),
+ name_("header-name"),
+ value_("header value") {}
+
+ // Returns an entry whose Size() is equal to the given one.
+ static HpackEntry MakeEntryOfSize(uint32 size) {
+ EXPECT_GE(size, HpackEntry::kSizeOverhead);
+ string name((size - HpackEntry::kSizeOverhead) / 2, 'n');
+ string value(size - HpackEntry::kSizeOverhead - name.size(), 'v');
+ HpackEntry entry(name, value);
+ EXPECT_EQ(size, entry.Size());
+ return entry;
+ }
+
+ // Returns a vector of entries whose total size is equal to the given
+ // one.
+ static HpackEntryVector MakeEntriesOfTotalSize(uint32 total_size) {
+ EXPECT_GE(total_size, HpackEntry::kSizeOverhead);
+ uint32 entry_size = HpackEntry::kSizeOverhead;
+ uint32 remaining_size = total_size;
+ HpackEntryVector entries;
+ while (remaining_size > 0) {
+ EXPECT_LE(entry_size, remaining_size);
+ entries.push_back(MakeEntryOfSize(entry_size));
+ remaining_size -= entry_size;
+ entry_size = std::min(remaining_size, entry_size + 32);
+ }
+ return entries;
+ }
+
+ // Adds the given vector of entries to the given header table,
+ // expecting no eviction to happen.
+ void AddEntriesExpectNoEviction(const HpackEntryVector& entries) {
+ for (HpackEntryVector::const_iterator it = entries.begin();
+ it != entries.end(); ++it) {
+ HpackHeaderTable::EntryTable::iterator begin, end;
+
+ table_.EvictionSet(it->name(), it->value(), &begin, &end);
+ EXPECT_EQ(0, distance(begin, end));
+
+ HpackEntry* entry = table_.TryAddEntry(it->name(), it->value());
+ EXPECT_NE(entry, static_cast<HpackEntry*>(NULL));
+ }
+
+ for (size_t i = 0; i != entries.size(); ++i) {
+ size_t index = entries.size() - i;
+ HpackEntry* entry = table_.GetByIndex(index);
+ EXPECT_EQ(entries[i].name(), entry->name());
+ EXPECT_EQ(entries[i].value(), entry->value());
+ EXPECT_EQ(index, table_.IndexOf(entry));
+ }
+ }
+
+ HpackEntry StaticEntry() {
+ peer_.AddStaticEntry(name_, value_);
+ return peer_.static_entries().back();
+ }
+ HpackEntry DynamicEntry() {
+ peer_.AddDynamicEntry(name_, value_);
+ return peer_.dynamic_entries().back();
+ }
+
+ HpackHeaderTable table_;
+ test::HpackHeaderTablePeer peer_;
+ string name_, value_;
+};
+
+TEST_F(HpackHeaderTableTest, StaticTableInitialization) {
+ EXPECT_EQ(0u, table_.size());
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting, table_.max_size());
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting, table_.settings_size_bound());
+
+ EXPECT_EQ(0u, peer_.dynamic_entries_count());
+ EXPECT_EQ(0u, table_.reference_set().size());
+ EXPECT_EQ(peer_.static_entries().size(), peer_.total_insertions());
+
+ // Static entries have been populated and inserted into the table & index.
+ EXPECT_NE(0u, peer_.static_entries().size());
+ EXPECT_EQ(peer_.index().size(), peer_.static_entries().size());
+ for (size_t i = 0; i != peer_.static_entries().size(); ++i) {
+ const HpackEntry* entry = &peer_.static_entries()[i];
+
+ EXPECT_TRUE(entry->IsStatic());
+ EXPECT_EQ(entry, table_.GetByIndex(i + 1));
+ EXPECT_EQ(entry, table_.GetByNameAndValue(entry->name(), entry->value()));
+ }
+}
+
+TEST_F(HpackHeaderTableTest, BasicDynamicEntryInsertionAndEviction) {
+ size_t static_count = peer_.total_insertions();
+ HpackEntry* first_static_entry = table_.GetByIndex(1);
+
+ EXPECT_EQ(1u, table_.IndexOf(first_static_entry));
+
+ HpackEntry* entry = table_.TryAddEntry("header-key", "Header Value");
+ EXPECT_EQ("header-key", entry->name());
+ EXPECT_EQ("Header Value", entry->value());
+ EXPECT_FALSE(entry->IsStatic());
+
+ // Table counts were updated appropriately.
+ EXPECT_EQ(entry->Size(), table_.size());
+ EXPECT_EQ(1u, peer_.dynamic_entries_count());
+ EXPECT_EQ(peer_.dynamic_entries().size(), peer_.dynamic_entries_count());
+ EXPECT_EQ(static_count + 1, peer_.total_insertions());
+ EXPECT_EQ(static_count + 1, peer_.index().size());
+
+ // Index() of entries reflects the insertion.
+ EXPECT_EQ(1u, table_.IndexOf(entry));
+ EXPECT_EQ(2u, table_.IndexOf(first_static_entry));
+ EXPECT_EQ(entry, table_.GetByIndex(1));
+ EXPECT_EQ(first_static_entry, table_.GetByIndex(2));
+
+ // Evict |entry|. Table counts are again updated appropriately.
+ peer_.Evict(1);
+ EXPECT_EQ(0u, table_.size());
+ EXPECT_EQ(0u, peer_.dynamic_entries_count());
+ EXPECT_EQ(peer_.dynamic_entries().size(), peer_.dynamic_entries_count());
+ EXPECT_EQ(static_count + 1, peer_.total_insertions());
+ EXPECT_EQ(static_count, peer_.index().size());
+
+ // Index() of |first_static_entry| reflects the eviction.
+ EXPECT_EQ(1u, table_.IndexOf(first_static_entry));
+ EXPECT_EQ(first_static_entry, table_.GetByIndex(1));
+}
+
+TEST_F(HpackHeaderTableTest, EntryIndexing) {
+ HpackEntry* first_static_entry = table_.GetByIndex(1);
+
+ // Static entries are queryable by name & value.
+ EXPECT_EQ(first_static_entry, table_.GetByName(first_static_entry->name()));
+ EXPECT_EQ(first_static_entry, table_.GetByNameAndValue(
+ first_static_entry->name(), first_static_entry->value()));
+
+ // Create a mix of entries which duplicate names, and names & values of both
+ // dynamic and static entries.
+ HpackEntry* entry1 = table_.TryAddEntry(first_static_entry->name(),
+ first_static_entry->value());
+ HpackEntry* entry2 = table_.TryAddEntry(first_static_entry->name(),
+ "Value Four");
+ HpackEntry* entry3 = table_.TryAddEntry("key-1", "Value One");
+ HpackEntry* entry4 = table_.TryAddEntry("key-2", "Value Three");
+ HpackEntry* entry5 = table_.TryAddEntry("key-1", "Value Two");
+ HpackEntry* entry6 = table_.TryAddEntry("key-2", "Value Three");
+ HpackEntry* entry7 = table_.TryAddEntry("key-2", "Value Four");
+
+ // Entries are queryable under their current index.
+ EXPECT_EQ(entry7, table_.GetByIndex(1));
+ EXPECT_EQ(entry6, table_.GetByIndex(2));
+ EXPECT_EQ(entry5, table_.GetByIndex(3));
+ EXPECT_EQ(entry4, table_.GetByIndex(4));
+ EXPECT_EQ(entry3, table_.GetByIndex(5));
+ EXPECT_EQ(entry2, table_.GetByIndex(6));
+ EXPECT_EQ(entry1, table_.GetByIndex(7));
+ EXPECT_EQ(first_static_entry, table_.GetByIndex(8));
+
+ // Querying by name returns the lowest-value matching entry.
+ EXPECT_EQ(entry3, table_.GetByName("key-1"));
+ EXPECT_EQ(entry7, table_.GetByName("key-2"));
+ EXPECT_EQ(entry2->name(),
+ table_.GetByName(first_static_entry->name())->name());
+ EXPECT_EQ(NULL, table_.GetByName("not-present"));
+
+ // Querying by name & value returns the lowest-index matching entry.
+ EXPECT_EQ(entry3, table_.GetByNameAndValue("key-1", "Value One"));
+ EXPECT_EQ(entry5, table_.GetByNameAndValue("key-1", "Value Two"));
+ EXPECT_EQ(entry6, table_.GetByNameAndValue("key-2", "Value Three"));
+ EXPECT_EQ(entry7, table_.GetByNameAndValue("key-2", "Value Four"));
+ EXPECT_EQ(entry1, table_.GetByNameAndValue(first_static_entry->name(),
+ first_static_entry->value()));
+ EXPECT_EQ(entry2, table_.GetByNameAndValue(first_static_entry->name(),
+ "Value Four"));
+ EXPECT_EQ(NULL, table_.GetByNameAndValue("key-1", "Not Present"));
+ EXPECT_EQ(NULL, table_.GetByNameAndValue("not-present", "Value One"));
+
+ // Evict |entry1|. Queries for its name & value now return the static entry.
+ // |entry2| remains queryable.
+ peer_.Evict(1);
+ EXPECT_EQ(first_static_entry,
+ table_.GetByNameAndValue(first_static_entry->name(),
+ first_static_entry->value()));
+ EXPECT_EQ(entry2, table_.GetByNameAndValue(first_static_entry->name(),
+ "Value Four"));
+
+ // Evict |entry2|. Queries by its name & value are not found.
+ peer_.Evict(1);
+ EXPECT_EQ(NULL, table_.GetByNameAndValue(first_static_entry->name(),
+ "Value Four"));
+}
+
+TEST_F(HpackHeaderTableTest, SetSizes) {
+ string key = "key", value = "value";
+ HpackEntry* entry1 = table_.TryAddEntry(key, value);
+ HpackEntry* entry2 = table_.TryAddEntry(key, value);
+ HpackEntry* entry3 = table_.TryAddEntry(key, value);
+
+ // Set exactly large enough. No Evictions.
+ size_t max_size = entry1->Size() + entry2->Size() + entry3->Size();
+ table_.SetMaxSize(max_size);
+ EXPECT_EQ(3u, peer_.dynamic_entries().size());
+
+ // Set just too small. One eviction.
+ max_size = entry1->Size() + entry2->Size() + entry3->Size() - 1;
+ table_.SetMaxSize(max_size);
+ EXPECT_EQ(2u, peer_.dynamic_entries().size());
+
+ // Changing SETTINGS_HEADER_TABLE_SIZE doesn't affect table_.max_size(),
+ // iff SETTINGS_HEADER_TABLE_SIZE >= |max_size|.
+ EXPECT_EQ(kDefaultHeaderTableSizeSetting, table_.settings_size_bound());
+ table_.SetSettingsHeaderTableSize(kDefaultHeaderTableSizeSetting*2);
+ EXPECT_EQ(max_size, table_.max_size());
+ table_.SetSettingsHeaderTableSize(max_size + 1);
+ EXPECT_EQ(max_size, table_.max_size());
+ EXPECT_EQ(2u, peer_.dynamic_entries().size());
+
+ // SETTINGS_HEADER_TABLE_SIZE upper-bounds |table_.max_size()|,
+ // and will force evictions.
+ max_size = entry3->Size() - 1;
+ table_.SetSettingsHeaderTableSize(max_size);
+ EXPECT_EQ(max_size, table_.max_size());
+ EXPECT_EQ(max_size, table_.settings_size_bound());
+ EXPECT_EQ(0u, peer_.dynamic_entries().size());
+}
+
+TEST_F(HpackHeaderTableTest, ToggleReferenceSet) {
+ HpackEntry* entry1 = table_.TryAddEntry("key-1", "Value One");
+ HpackEntry* entry2 = table_.TryAddEntry("key-2", "Value Two");
+
+ // Entries must be explictly toggled after creation.
+ EXPECT_EQ(0u, table_.reference_set().size());
+
+ // Add |entry1|.
+ EXPECT_TRUE(table_.Toggle(entry1));
+ EXPECT_EQ(1u, table_.reference_set().size());
+ EXPECT_EQ(1u, table_.reference_set().count(entry1));
+ EXPECT_EQ(0u, table_.reference_set().count(entry2));
+
+ // Add |entry2|.
+ EXPECT_TRUE(table_.Toggle(entry2));
+ EXPECT_EQ(2u, table_.reference_set().size());
+ EXPECT_EQ(1u, table_.reference_set().count(entry1));
+ EXPECT_EQ(1u, table_.reference_set().count(entry2));
+
+ // Remove |entry2|.
+ EXPECT_FALSE(table_.Toggle(entry2));
+ EXPECT_EQ(1u, table_.reference_set().size());
+ EXPECT_EQ(0u, table_.reference_set().count(entry2));
+
+ // Evict |entry1|. Implicit removal from reference set.
+ peer_.Evict(1);
+ EXPECT_EQ(0u, table_.reference_set().size());
+}
+
+TEST_F(HpackHeaderTableTest, ClearReferenceSet) {
+ HpackEntry* entry1 = table_.TryAddEntry("key-1", "Value One");
+ EXPECT_TRUE(table_.Toggle(entry1));
+ entry1->set_state(123);
+
+ // |entry1| state is cleared, and removed from the reference set.
+ table_.ClearReferenceSet();
+ EXPECT_EQ(0u, entry1->state());
+ EXPECT_EQ(0u, table_.reference_set().size());
+}
+
+TEST_F(HpackHeaderTableTest, EvictionCountForEntry) {
+ string key = "key", value = "value";
+ HpackEntry* entry1 = table_.TryAddEntry(key, value);
+ HpackEntry* entry2 = table_.TryAddEntry(key, value);
+ size_t entry3_size = HpackEntry::Size(key, value);
+
+ // Just enough capacity for third entry.
+ table_.SetMaxSize(entry1->Size() + entry2->Size() + entry3_size);
+ EXPECT_EQ(0u, peer_.EvictionCountForEntry(key, value));
+ EXPECT_EQ(1u, peer_.EvictionCountForEntry(key, value + "x"));
+
+ // No extra capacity. Third entry would force evictions.
+ table_.SetMaxSize(entry1->Size() + entry2->Size());
+ EXPECT_EQ(1u, peer_.EvictionCountForEntry(key, value));
+ EXPECT_EQ(2u, peer_.EvictionCountForEntry(key, value + "x"));
+}
+
+TEST_F(HpackHeaderTableTest, EvictionCountToReclaim) {
+ string key = "key", value = "value";
+ HpackEntry* entry1 = table_.TryAddEntry(key, value);
+ HpackEntry* entry2 = table_.TryAddEntry(key, value);
+
+ EXPECT_EQ(1u, peer_.EvictionCountToReclaim(1));
+ EXPECT_EQ(1u, peer_.EvictionCountToReclaim(entry1->Size()));
+ EXPECT_EQ(2u, peer_.EvictionCountToReclaim(entry1->Size() + 1));
+ EXPECT_EQ(2u, peer_.EvictionCountToReclaim(entry1->Size() + entry2->Size()));
+}
+
+// Fill a header table with entries. Make sure the entries are in
+// reverse order in the header table.
+TEST_F(HpackHeaderTableTest, TryAddEntryBasic) {
+ EXPECT_EQ(0u, table_.size());
+ EXPECT_EQ(table_.settings_size_bound(), table_.max_size());
+
+ HpackEntryVector entries = MakeEntriesOfTotalSize(table_.max_size());
+
+ // Most of the checks are in AddEntriesExpectNoEviction().
+ AddEntriesExpectNoEviction(entries);
+ EXPECT_EQ(table_.max_size(), table_.size());
+ EXPECT_EQ(table_.settings_size_bound(), table_.size());
+}
+
+// Fill a header table with entries, and then ramp the table's max
+// size down to evict an entry one at a time. Make sure the eviction
+// happens as expected.
+TEST_F(HpackHeaderTableTest, SetMaxSize) {
+ HpackEntryVector entries = MakeEntriesOfTotalSize(
+ kDefaultHeaderTableSizeSetting / 2);
+ AddEntriesExpectNoEviction(entries);
+
+ for (HpackEntryVector::iterator it = entries.begin();
+ it != entries.end(); ++it) {
+ size_t expected_count = distance(it, entries.end());
+ EXPECT_EQ(expected_count, peer_.dynamic_entries().size());
+
+ table_.SetMaxSize(table_.size() + 1);
+ EXPECT_EQ(expected_count, peer_.dynamic_entries().size());
+
+ table_.SetMaxSize(table_.size());
+ EXPECT_EQ(expected_count, peer_.dynamic_entries().size());
+
+ --expected_count;
+ table_.SetMaxSize(table_.size() - 1);
+ EXPECT_EQ(expected_count, peer_.dynamic_entries().size());
+ }
+ EXPECT_EQ(0u, table_.size());
+}
+
+// Fill a header table with entries, and then add an entry just big
+// enough to cause eviction of all but one entry. Make sure the
+// eviction happens as expected and the long entry is inserted into
+// the table.
+TEST_F(HpackHeaderTableTest, TryAddEntryEviction) {
+ HpackEntryVector entries = MakeEntriesOfTotalSize(table_.max_size());
+ AddEntriesExpectNoEviction(entries);
+
+ HpackEntry* survivor_entry = table_.GetByIndex(1);
+ HpackEntry long_entry =
+ MakeEntryOfSize(table_.max_size() - survivor_entry->Size());
+
+ // All entries but the first are to be evicted.
+ EXPECT_EQ(peer_.dynamic_entries().size() - 1, peer_.EvictionSet(
+ long_entry.name(), long_entry.value()).size());
+
+ HpackEntry* new_entry = table_.TryAddEntry(long_entry.name(),
+ long_entry.value());
+ EXPECT_EQ(1u, table_.IndexOf(new_entry));
+ EXPECT_EQ(2u, peer_.dynamic_entries().size());
+ EXPECT_EQ(table_.GetByIndex(2), survivor_entry);
+ EXPECT_EQ(table_.GetByIndex(1), new_entry);
+}
+
+// Fill a header table with entries, and then add an entry bigger than
+// the entire table. Make sure no entry remains in the table.
+TEST_F(HpackHeaderTableTest, TryAddTooLargeEntry) {
+ HpackEntryVector entries = MakeEntriesOfTotalSize(table_.max_size());
+ AddEntriesExpectNoEviction(entries);
+
+ HpackEntry long_entry = MakeEntryOfSize(table_.max_size() + 1);
+
+ // All entries are to be evicted.
+ EXPECT_EQ(peer_.dynamic_entries().size(), peer_.EvictionSet(
+ long_entry.name(), long_entry.value()).size());
+
+ HpackEntry* new_entry = table_.TryAddEntry(long_entry.name(),
+ long_entry.value());
+ EXPECT_EQ(new_entry, static_cast<HpackEntry*>(NULL));
+ EXPECT_EQ(0u, peer_.dynamic_entries().size());
+}
+
+TEST_F(HpackHeaderTableTest, ComparatorNameOrdering) {
+ HpackEntry entry1(StaticEntry());
+ name_[0]--;
+ HpackEntry entry2(StaticEntry());
+
+ HpackHeaderTable::EntryComparator comparator(&table_);
+ EXPECT_FALSE(comparator(&entry1, &entry2));
+ EXPECT_TRUE(comparator(&entry2, &entry1));
+}
+
+TEST_F(HpackHeaderTableTest, ComparatorValueOrdering) {
+ HpackEntry entry1(StaticEntry());
+ value_[0]--;
+ HpackEntry entry2(StaticEntry());
+
+ HpackHeaderTable::EntryComparator comparator(&table_);
+ EXPECT_FALSE(comparator(&entry1, &entry2));
+ EXPECT_TRUE(comparator(&entry2, &entry1));
+}
+
+TEST_F(HpackHeaderTableTest, ComparatorIndexOrdering) {
+ HpackEntry entry1(StaticEntry());
+ HpackEntry entry2(StaticEntry());
+
+ HpackHeaderTable::EntryComparator comparator(&table_);
+ EXPECT_TRUE(comparator(&entry1, &entry2));
+ EXPECT_FALSE(comparator(&entry2, &entry1));
+
+ HpackEntry entry3(DynamicEntry());
+ HpackEntry entry4(DynamicEntry());
+
+ // |entry4| has lower index than |entry3|.
+ EXPECT_TRUE(comparator(&entry4, &entry3));
+ EXPECT_FALSE(comparator(&entry3, &entry4));
+
+ // |entry3| has lower index than |entry1|.
+ EXPECT_TRUE(comparator(&entry3, &entry1));
+ EXPECT_FALSE(comparator(&entry1, &entry3));
+
+ // |entry1| & |entry2| ordering is preserved, though each Index() has changed.
+ EXPECT_TRUE(comparator(&entry1, &entry2));
+ EXPECT_FALSE(comparator(&entry2, &entry1));
+}
+
+TEST_F(HpackHeaderTableTest, ComparatorEqualityOrdering) {
+ HpackEntry entry1(StaticEntry());
+ HpackEntry entry2(DynamicEntry());
+
+ HpackHeaderTable::EntryComparator comparator(&table_);
+ EXPECT_FALSE(comparator(&entry1, &entry1));
+ EXPECT_FALSE(comparator(&entry2, &entry2));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_huffman_aggregator.cc b/chromium/net/spdy/hpack_huffman_aggregator.cc
new file mode 100644
index 00000000000..507f8725829
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_aggregator.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 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/spdy/hpack_huffman_aggregator.h"
+
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_headers.h"
+#include "net/spdy/hpack_encoder.h"
+#include "net/spdy/spdy_http_utils.h"
+
+namespace net {
+
+namespace {
+
+const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
+
+const size_t kTotalCountsPublishThreshold = 50000;
+
+// Each encoder uses the default dynamic table size of 4096 total bytes.
+const size_t kMaxEncoders = 20;
+
+} // namespace
+
+HpackHuffmanAggregator::HpackHuffmanAggregator()
+ : counts_(256, 0),
+ total_counts_(0),
+ max_encoders_(kMaxEncoders) {
+}
+
+HpackHuffmanAggregator::~HpackHuffmanAggregator() {
+ STLDeleteContainerPairSecondPointers(encoders_.begin(), encoders_.end());
+ encoders_.clear();
+}
+
+void HpackHuffmanAggregator::AggregateTransactionCharacterCounts(
+ const HttpRequestInfo& request,
+ const HttpRequestHeaders& request_headers,
+ const ProxyServer& proxy,
+ const HttpResponseHeaders& response_headers) {
+ if (IsCrossOrigin(request)) {
+ return;
+ }
+ HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
+ request.url.EffectiveIntPort());
+ HpackEncoder* encoder = ObtainEncoder(
+ SpdySessionKey(endpoint, proxy, request.privacy_mode));
+
+ // Convert and encode the request and response header sets.
+ {
+ SpdyHeaderBlock headers;
+ CreateSpdyHeadersFromHttpRequest(
+ request, request_headers, &headers, SPDY4, false);
+
+ std::string tmp_out;
+ encoder->EncodeHeaderSet(headers, &tmp_out);
+ }
+ {
+ SpdyHeaderBlock headers;
+ CreateSpdyHeadersFromHttpResponse(response_headers, &headers);
+
+ std::string tmp_out;
+ encoder->EncodeHeaderSet(headers, &tmp_out);
+ }
+ if (total_counts_ >= kTotalCountsPublishThreshold) {
+ PublishCounts();
+ }
+}
+
+// static
+bool HpackHuffmanAggregator::UseAggregator() {
+ const std::string group_name =
+ base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
+ if (group_name == "Enabled") {
+ return true;
+ }
+ return false;
+}
+
+// static
+void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
+ const HttpResponseHeaders& headers,
+ SpdyHeaderBlock* headers_out) {
+ // Lower-case header names, and coalesce multiple values delimited by \0.
+ // Also add the fixed status header.
+ std::string name, value;
+ void* it = NULL;
+ while (headers.EnumerateHeaderLines(&it, &name, &value)) {
+ StringToLowerASCII(&name);
+ if (headers_out->find(name) == headers_out->end()) {
+ (*headers_out)[name] = value;
+ } else {
+ (*headers_out)[name] += std::string(1, '\0') + value;
+ }
+ }
+ (*headers_out)[":status"] = base::IntToString(headers.response_code());
+}
+
+// static
+bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo& request) {
+ // Require that the request is top-level, or that it shares
+ // an origin with its referer.
+ HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
+ request.url.EffectiveIntPort());
+ if ((request.load_flags & LOAD_MAIN_FRAME) == 0) {
+ std::string referer_str;
+ if (!request.extra_headers.GetHeader(HttpRequestHeaders::kReferer,
+ &referer_str)) {
+ // Require a referer.
+ return true;
+ }
+ GURL referer(referer_str);
+ HostPortPair referer_endpoint = HostPortPair(referer.HostNoBrackets(),
+ referer.EffectiveIntPort());
+ if (!endpoint.Equals(referer_endpoint)) {
+ // Cross-origin request.
+ return true;
+ }
+ }
+ return false;
+}
+
+HpackEncoder* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey& key) {
+ for (OriginEncoders::iterator it = encoders_.begin();
+ it != encoders_.end(); ++it) {
+ if (key.Equals(it->first)) {
+ // Move to head of list and return.
+ OriginEncoder origin_encoder = *it;
+ encoders_.erase(it);
+ encoders_.push_front(origin_encoder);
+ return origin_encoder.second;
+ }
+ }
+ // Not found. Create a new encoder, evicting one if needed.
+ encoders_.push_front(std::make_pair(
+ key, new HpackEncoder(ObtainHpackHuffmanTable())));
+ if (encoders_.size() > max_encoders_) {
+ delete encoders_.back().second;
+ encoders_.pop_back();
+ }
+ encoders_.front().second->SetCharCountsStorage(&counts_, &total_counts_);
+ return encoders_.front().second;
+}
+
+void HpackHuffmanAggregator::PublishCounts() {
+ // base::Histogram requires that values be 1-indexed.
+ const size_t kRangeMin = 1;
+ const size_t kRangeMax = counts_.size() + 1;
+ const size_t kBucketCount = kRangeMax + 1;
+
+ base::BucketRanges ranges(kBucketCount + 1);
+ for (size_t i = 0; i != ranges.size(); ++i) {
+ ranges.set_range(i, i);
+ }
+ ranges.ResetChecksum();
+
+ // Copy |counts_| into a SampleVector.
+ base::SampleVector samples(&ranges);
+ for (size_t i = 0; i != counts_.size(); ++i) {
+ samples.Accumulate(i + 1, counts_[i]);
+ }
+
+ STATIC_HISTOGRAM_POINTER_BLOCK(
+ kHistogramName,
+ AddSamples(samples),
+ base::LinearHistogram::FactoryGet(
+ kHistogramName, kRangeMin, kRangeMax, kBucketCount,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+
+ // Clear counts.
+ counts_.assign(counts_.size(), 0);
+ total_counts_ = 0;
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_huffman_aggregator.h b/chromium/net/spdy/hpack_huffman_aggregator.h
new file mode 100644
index 00000000000..46ba0949c7a
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_aggregator.h
@@ -0,0 +1,74 @@
+// Copyright 2014 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 <list>
+#include <vector>
+
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session_key.h"
+
+namespace net {
+
+class HpackEncoder;
+class HttpRequestHeaders;
+struct HttpRequestInfo;
+class HttpResponseHeaders;
+class ProxyServer;
+
+namespace test {
+class HpackHuffmanAggregatorPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE HpackHuffmanAggregator {
+ public:
+ friend class test::HpackHuffmanAggregatorPeer;
+
+ HpackHuffmanAggregator();
+ ~HpackHuffmanAggregator();
+
+ // Encodes the request and response headers of the transaction with an
+ // HpackEncoder keyed on the transaction's SpdySessionKey. Literal headers
+ // emitted by that encoder are aggregated into internal character counts,
+ // which are periodically published to a UMA histogram.
+ void AggregateTransactionCharacterCounts(
+ const HttpRequestInfo& request,
+ const HttpRequestHeaders& request_headers,
+ const ProxyServer& proxy,
+ const HttpResponseHeaders& response_headers);
+
+ // Returns whether the aggregator is enabled for the session by a field trial.
+ static bool UseAggregator();
+
+ private:
+ typedef std::pair<SpdySessionKey, HpackEncoder*> OriginEncoder;
+ typedef std::list<OriginEncoder> OriginEncoders;
+
+ // Returns true if the request is considered cross-origin,
+ // and should not be aggregated.
+ static bool IsCrossOrigin(const HttpRequestInfo& request);
+
+ // Converts |headers| into SPDY headers block |headers_out|.
+ static void CreateSpdyHeadersFromHttpResponse(
+ const HttpResponseHeaders& headers,
+ SpdyHeaderBlock* headers_out);
+
+ // Creates or returns an encoder for the origin key.
+ HpackEncoder* ObtainEncoder(const SpdySessionKey& key);
+
+ // Publishes aggregated counts to a UMA histogram.
+ void PublishCounts();
+
+ std::vector<size_t> counts_;
+ size_t total_counts_;
+
+ OriginEncoders encoders_;
+ size_t max_encoders_;
+
+ DISALLOW_COPY_AND_ASSIGN(HpackHuffmanAggregator);
+};
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_huffman_aggregator_test.cc b/chromium/net/spdy/hpack_huffman_aggregator_test.cc
new file mode 100644
index 00000000000..23a9a43edb7
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_aggregator_test.cc
@@ -0,0 +1,204 @@
+// Copyright 2014 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/spdy/hpack_huffman_aggregator.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/statistics_recorder.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_headers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using ::testing::Each;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Pair;
+
+namespace {
+const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
+} // namespace
+
+namespace test {
+
+class HpackHuffmanAggregatorPeer {
+ public:
+ explicit HpackHuffmanAggregatorPeer(HpackHuffmanAggregator* agg)
+ : agg_(agg) {}
+
+ std::vector<size_t>* counts() {
+ return &agg_->counts_;
+ }
+ HpackHuffmanAggregator::OriginEncoders* encoders() {
+ return &agg_->encoders_;
+ }
+ size_t total_counts() {
+ return agg_->total_counts_;
+ }
+ void set_total_counts(size_t total_counts) {
+ agg_->total_counts_ = total_counts;
+ }
+ void set_max_encoders(size_t max_encoders) {
+ agg_->max_encoders_ = max_encoders;
+ }
+ static bool IsCrossOrigin(const HttpRequestInfo& request) {
+ return HpackHuffmanAggregator::IsCrossOrigin(request);
+ }
+ static void CreateSpdyHeadersFromHttpResponse(
+ const HttpResponseHeaders& headers,
+ SpdyHeaderBlock* headers_out) {
+ HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
+ headers, headers_out);
+ }
+ HpackEncoder* ObtainEncoder(const SpdySessionKey& key) {
+ return agg_->ObtainEncoder(key);
+ }
+ void PublishCounts() {
+ agg_->PublishCounts();
+ }
+
+ private:
+ HpackHuffmanAggregator* agg_;
+};
+
+} // namespace test
+
+class HpackHuffmanAggregatorTest : public ::testing::Test {
+ protected:
+ HpackHuffmanAggregatorTest()
+ : peer_(&agg_) {}
+
+ HpackHuffmanAggregator agg_;
+ test::HpackHuffmanAggregatorPeer peer_;
+};
+
+TEST_F(HpackHuffmanAggregatorTest, CrossOriginDetermination) {
+ HttpRequestInfo request;
+ request.url = GURL("https://www.foo.com/a/page");
+
+ // Main load without referer.
+ request.load_flags = LOAD_MAIN_FRAME;
+ EXPECT_FALSE(peer_.IsCrossOrigin(request));
+
+ // Non-main load without referer. Treated as cross-origin.
+ request.load_flags = 0;
+ EXPECT_TRUE(peer_.IsCrossOrigin(request));
+
+ // Main load with different referer origin.
+ request.load_flags = LOAD_MAIN_FRAME;
+ request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
+ "https://www.bar.com/other/page");
+ EXPECT_FALSE(peer_.IsCrossOrigin(request));
+
+ // Non-main load with different referer orign.
+ request.load_flags = 0;
+ EXPECT_TRUE(peer_.IsCrossOrigin(request));
+
+ // Non-main load with same referer orign.
+ request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
+ "https://www.foo.com/other/page");
+ EXPECT_FALSE(peer_.IsCrossOrigin(request));
+
+ // Non-main load with same referer host but different schemes.
+ request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
+ "http://www.foo.com/other/page");
+ EXPECT_TRUE(peer_.IsCrossOrigin(request));
+}
+
+TEST_F(HpackHuffmanAggregatorTest, EncoderLRUQueue) {
+ peer_.set_max_encoders(2);
+
+ SpdySessionKey key1(HostPortPair("one.com", 443), ProxyServer::Direct(),
+ PRIVACY_MODE_ENABLED);
+ SpdySessionKey key2(HostPortPair("two.com", 443), ProxyServer::Direct(),
+ PRIVACY_MODE_ENABLED);
+ SpdySessionKey key3(HostPortPair("three.com", 443), ProxyServer::Direct(),
+ PRIVACY_MODE_ENABLED);
+
+ // Creates one.com.
+ HpackEncoder* one = peer_.ObtainEncoder(key1);
+ EXPECT_EQ(1u, peer_.encoders()->size());
+
+ // Creates two.com. No evictions.
+ HpackEncoder* two = peer_.ObtainEncoder(key2);
+ EXPECT_EQ(2u, peer_.encoders()->size());
+ EXPECT_NE(one, two);
+
+ // Touch one.com.
+ EXPECT_EQ(one, peer_.ObtainEncoder(key1));
+
+ // Creates three.com. Evicts two.com, as it's least-recently used.
+ HpackEncoder* three = peer_.ObtainEncoder(key3);
+ EXPECT_EQ(one, peer_.ObtainEncoder(key1));
+ EXPECT_NE(one, three);
+ EXPECT_EQ(2u, peer_.encoders()->size());
+}
+
+TEST_F(HpackHuffmanAggregatorTest, PublishCounts) {
+ (*peer_.counts())[0] = 1;
+ (*peer_.counts())[255] = 10;
+ (*peer_.counts())[128] = 101;
+ peer_.set_total_counts(112);
+
+ peer_.PublishCounts();
+
+ // Internal counts were reset after being published.
+ EXPECT_THAT(*peer_.counts(), Each(Eq(0u)));
+ EXPECT_EQ(0u, peer_.total_counts());
+
+ // Verify histogram counts match the expectation.
+ scoped_ptr<base::HistogramSamples> samples =
+ base::StatisticsRecorder::FindHistogram(kHistogramName)
+ ->SnapshotSamples();
+
+ EXPECT_EQ(0, samples->GetCount(0));
+ EXPECT_EQ(1, samples->GetCount(1));
+ EXPECT_EQ(101, samples->GetCount(129));
+ EXPECT_EQ(10, samples->GetCount(256));
+ EXPECT_EQ(112, samples->TotalCount());
+
+ // Publish a second round of counts;
+ (*peer_.counts())[1] = 32;
+ (*peer_.counts())[128] = 5;
+ peer_.set_total_counts(37);
+
+ peer_.PublishCounts();
+
+ // Verify they've been aggregated into the previous counts.
+ samples = base::StatisticsRecorder::FindHistogram(kHistogramName)
+ ->SnapshotSamples();
+
+ EXPECT_EQ(0, samples->GetCount(0));
+ EXPECT_EQ(1, samples->GetCount(1));
+ EXPECT_EQ(32, samples->GetCount(2));
+ EXPECT_EQ(106, samples->GetCount(129));
+ EXPECT_EQ(10, samples->GetCount(256));
+ EXPECT_EQ(149, samples->TotalCount());
+}
+
+TEST_F(HpackHuffmanAggregatorTest, CreateSpdyResponseHeaders) {
+ char kRawHeaders[] =
+ "HTTP/1.1 202 Accepted \0"
+ "Content-TYPE : text/html; charset=utf-8 \0"
+ "Set-Cookie: foo=bar \0"
+ "Set-Cookie: baz=bing \0"
+ "Cache-Control: pragma=no-cache \0"
+ "Cache-CONTROL: expires=12345 \0\0";
+
+ scoped_refptr<HttpResponseHeaders> parsed_headers(new HttpResponseHeaders(
+ std::string(kRawHeaders, arraysize(kRawHeaders) - 1)));
+
+ SpdyHeaderBlock headers;
+ peer_.CreateSpdyHeadersFromHttpResponse(*parsed_headers, &headers);
+ EXPECT_THAT(headers, ElementsAre(
+ Pair(":status", "202"),
+ Pair("cache-control", std::string("pragma=no-cache\0expires=12345", 29)),
+ Pair("content-type", "text/html; charset=utf-8"),
+ Pair("set-cookie", std::string("foo=bar\0baz=bing", 16))));
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_huffman_table.cc b/chromium/net/spdy/hpack_huffman_table.cc
new file mode 100644
index 00000000000..9e8f6a1f783
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_table.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 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/spdy/hpack_huffman_table.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "net/spdy/hpack_input_stream.h"
+#include "net/spdy/hpack_output_stream.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+
+namespace {
+
+// How many bits to index in the root decode table.
+const uint8 kDecodeTableRootBits = 9;
+// Maximum number of bits to index in successive decode tables.
+const uint8 kDecodeTableBranchBits = 6;
+
+bool SymbolLengthAndIdCompare(const HpackHuffmanSymbol& a,
+ const HpackHuffmanSymbol& b) {
+ if (a.length == b.length) {
+ return a.id < b.id;
+ }
+ return a.length < b.length;
+}
+bool SymbolIdCompare(const HpackHuffmanSymbol& a,
+ const HpackHuffmanSymbol& b) {
+ return a.id < b.id;
+}
+
+} // namespace
+
+HpackHuffmanTable::DecodeEntry::DecodeEntry()
+ : next_table_index(0), length(0), symbol_id(0) {
+}
+HpackHuffmanTable::DecodeEntry::DecodeEntry(uint8 next_table_index,
+ uint8 length,
+ uint16 symbol_id)
+ : next_table_index(next_table_index), length(length), symbol_id(symbol_id) {
+}
+size_t HpackHuffmanTable::DecodeTable::size() const {
+ return size_t(1) << indexed_length;
+}
+
+HpackHuffmanTable::HpackHuffmanTable() {}
+
+HpackHuffmanTable::~HpackHuffmanTable() {}
+
+bool HpackHuffmanTable::Initialize(const HpackHuffmanSymbol* input_symbols,
+ size_t symbol_count) {
+ CHECK(!IsInitialized());
+
+ std::vector<Symbol> symbols(symbol_count);
+ // Validate symbol id sequence, and copy into |symbols|.
+ for (size_t i = 0; i != symbol_count; i++) {
+ if (i != input_symbols[i].id) {
+ failed_symbol_id_ = i;
+ return false;
+ }
+ symbols[i] = input_symbols[i];
+ }
+ // Order on length and ID ascending, to verify symbol codes are canonical.
+ std::sort(symbols.begin(), symbols.end(), SymbolLengthAndIdCompare);
+ if (symbols[0].code != 0) {
+ failed_symbol_id_ = 0;
+ return false;
+ }
+ for (size_t i = 1; i != symbols.size(); i++) {
+ unsigned code_shift = 32 - symbols[i-1].length;
+ uint32 code = symbols[i-1].code + (1 << code_shift);
+
+ if (code != symbols[i].code) {
+ failed_symbol_id_ = symbols[i].id;
+ return false;
+ }
+ if (code < symbols[i-1].code) {
+ // An integer overflow occurred. This implies the input
+ // lengths do not represent a valid Huffman code.
+ failed_symbol_id_ = symbols[i].id;
+ return false;
+ }
+ }
+ if (symbols.back().length < 8) {
+ // At least one code (such as an EOS symbol) must be 8 bits or longer.
+ // Without this, some inputs will not be encodable in a whole number
+ // of bytes.
+ return false;
+ }
+ pad_bits_ = static_cast<uint8>(symbols.back().code >> 24);
+
+ BuildDecodeTables(symbols);
+ // Order on symbol ID ascending.
+ std::sort(symbols.begin(), symbols.end(), SymbolIdCompare);
+ BuildEncodeTable(symbols);
+ return true;
+}
+
+void HpackHuffmanTable::BuildEncodeTable(const std::vector<Symbol>& symbols) {
+ for (size_t i = 0; i != symbols.size(); i++) {
+ const Symbol& symbol = symbols[i];
+ CHECK_EQ(i, symbol.id);
+ code_by_id_.push_back(symbol.code);
+ length_by_id_.push_back(symbol.length);
+ }
+}
+
+void HpackHuffmanTable::BuildDecodeTables(const std::vector<Symbol>& symbols) {
+ AddDecodeTable(0, kDecodeTableRootBits);
+ // We wish to maximize the flatness of the DecodeTable hierarchy (subject to
+ // the |kDecodeTableBranchBits| constraint), and to minimize the size of
+ // child tables. To achieve this, we iterate in order of descending code
+ // length. This ensures that child tables are visited with their longest
+ // entry first, and that the child can therefore be minimally sized to hold
+ // that entry without fear of introducing unneccesary branches later.
+ for (std::vector<Symbol>::const_reverse_iterator it = symbols.rbegin();
+ it != symbols.rend(); ++it) {
+ uint8 table_index = 0;
+ while (true) {
+ const DecodeTable table = decode_tables_[table_index];
+
+ // Mask and shift the portion of the code being indexed into low bits.
+ uint32 index = (it->code << table.prefix_length);
+ index = index >> (32 - table.indexed_length);
+
+ CHECK_LT(index, table.size());
+ DecodeEntry entry = Entry(table, index);
+
+ uint8 total_indexed = table.prefix_length + table.indexed_length;
+ if (total_indexed >= it->length) {
+ // We're writing a terminal entry.
+ entry.length = it->length;
+ entry.symbol_id = it->id;
+ entry.next_table_index = table_index;
+ SetEntry(table, index, entry);
+ break;
+ }
+
+ if (entry.length == 0) {
+ // First visit to this placeholder. We need to create a new table.
+ CHECK_EQ(entry.next_table_index, 0);
+ entry.length = it->length;
+ entry.next_table_index = AddDecodeTable(
+ total_indexed, // Becomes the new table prefix.
+ std::min<uint8>(kDecodeTableBranchBits,
+ entry.length - total_indexed));
+ SetEntry(table, index, entry);
+ }
+ CHECK_NE(entry.next_table_index, table_index);
+ table_index = entry.next_table_index;
+ }
+ }
+ // Fill shorter table entries into the additional entry spots they map to.
+ for (size_t i = 0; i != decode_tables_.size(); i++) {
+ const DecodeTable& table = decode_tables_[i];
+ uint8 total_indexed = table.prefix_length + table.indexed_length;
+
+ size_t j = 0;
+ while (j != table.size()) {
+ const DecodeEntry& entry = Entry(table, j);
+ if (entry.length != 0 && entry.length < total_indexed) {
+ // The difference between entry & table bit counts tells us how
+ // many additional entries map to this one.
+ size_t fill_count = 1 << (total_indexed - entry.length);
+ CHECK_LE(j + fill_count, table.size());
+
+ for (size_t k = 1; k != fill_count; k++) {
+ CHECK_EQ(Entry(table, j + k).length, 0);
+ SetEntry(table, j + k, entry);
+ }
+ j += fill_count;
+ } else {
+ j++;
+ }
+ }
+ }
+}
+
+uint8 HpackHuffmanTable::AddDecodeTable(uint8 prefix, uint8 indexed) {
+ CHECK_LT(decode_tables_.size(), 255u);
+ {
+ DecodeTable table;
+ table.prefix_length = prefix;
+ table.indexed_length = indexed;
+ table.entries_offset = decode_entries_.size();
+ decode_tables_.push_back(table);
+ }
+ decode_entries_.resize(decode_entries_.size() + (size_t(1) << indexed));
+ return static_cast<uint8>(decode_tables_.size() - 1);
+}
+
+const HpackHuffmanTable::DecodeEntry& HpackHuffmanTable::Entry(
+ const DecodeTable& table,
+ uint32 index) const {
+ DCHECK_LT(index, table.size());
+ DCHECK_LT(table.entries_offset + index, decode_entries_.size());
+ return decode_entries_[table.entries_offset + index];
+}
+
+void HpackHuffmanTable::SetEntry(const DecodeTable& table,
+ uint32 index,
+ const DecodeEntry& entry) {
+ CHECK_LT(index, table.size());
+ CHECK_LT(table.entries_offset + index, decode_entries_.size());
+ decode_entries_[table.entries_offset + index] = entry;
+}
+
+bool HpackHuffmanTable::IsInitialized() const {
+ return !code_by_id_.empty();
+}
+
+void HpackHuffmanTable::EncodeString(StringPiece in,
+ HpackOutputStream* out) const {
+ size_t bit_remnant = 0;
+ for (size_t i = 0; i != in.size(); i++) {
+ uint16 symbol_id = static_cast<uint8>(in[i]);
+ CHECK_GT(code_by_id_.size(), symbol_id);
+
+ // Load, and shift code to low bits.
+ unsigned length = length_by_id_[symbol_id];
+ uint32 code = code_by_id_[symbol_id] >> (32 - length);
+
+ bit_remnant = (bit_remnant + length) % 8;
+
+ if (length > 24) {
+ out->AppendBits(static_cast<uint8>(code >> 24), length - 24);
+ length = 24;
+ }
+ if (length > 16) {
+ out->AppendBits(static_cast<uint8>(code >> 16), length - 16);
+ length = 16;
+ }
+ if (length > 8) {
+ out->AppendBits(static_cast<uint8>(code >> 8), length - 8);
+ length = 8;
+ }
+ out->AppendBits(static_cast<uint8>(code), length);
+ }
+ if (bit_remnant != 0) {
+ // Pad current byte as required.
+ out->AppendBits(pad_bits_ >> bit_remnant, 8 - bit_remnant);
+ }
+}
+
+size_t HpackHuffmanTable::EncodedSize(StringPiece in) const {
+ size_t bit_count = 0;
+ for (size_t i = 0; i != in.size(); i++) {
+ uint16 symbol_id = static_cast<uint8>(in[i]);
+ CHECK_GT(code_by_id_.size(), symbol_id);
+
+ bit_count += length_by_id_[symbol_id];
+ }
+ if (bit_count % 8 != 0) {
+ bit_count += 8 - bit_count % 8;
+ }
+ return bit_count / 8;
+}
+
+bool HpackHuffmanTable::DecodeString(HpackInputStream* in,
+ size_t out_capacity,
+ string* out) const {
+ // Number of decode iterations required for a 32-bit code.
+ const int kDecodeIterations = static_cast<int>(
+ std::ceil((32.f - kDecodeTableRootBits) / kDecodeTableBranchBits));
+
+ out->clear();
+
+ // Current input, stored in the high |bits_available| bits of |bits|.
+ uint32 bits = 0;
+ size_t bits_available = 0;
+ bool peeked_success = in->PeekBits(&bits_available, &bits);
+
+ while (true) {
+ const DecodeTable* table = &decode_tables_[0];
+ uint32 index = bits >> (32 - kDecodeTableRootBits);
+
+ for (int i = 0; i != kDecodeIterations; i++) {
+ DCHECK_LT(index, table->size());
+ DCHECK_LT(Entry(*table, index).next_table_index, decode_tables_.size());
+
+ table = &decode_tables_[Entry(*table, index).next_table_index];
+ // Mask and shift the portion of the code being indexed into low bits.
+ index = (bits << table->prefix_length) >> (32 - table->indexed_length);
+ }
+ const DecodeEntry& entry = Entry(*table, index);
+
+ if (entry.length > bits_available) {
+ if (!peeked_success) {
+ // Unable to read enough input for a match. If only a portion of
+ // the last byte remains, this is a successful EOF condition.
+ in->ConsumeByteRemainder();
+ return !in->HasMoreData();
+ }
+ } else if (entry.length == 0) {
+ // The input is an invalid prefix, larger than any prefix in the table.
+ return false;
+ } else {
+ if (out->size() == out_capacity) {
+ // This code would cause us to overflow |out_capacity|.
+ return false;
+ }
+ if (entry.symbol_id < 256) {
+ // Assume symbols >= 256 are used for padding.
+ out->push_back(static_cast<char>(entry.symbol_id));
+ }
+
+ in->ConsumeBits(entry.length);
+ bits = bits << entry.length;
+ bits_available -= entry.length;
+ }
+ peeked_success = in->PeekBits(&bits_available, &bits);
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_huffman_table.h b/chromium/net/spdy/hpack_huffman_table.h
new file mode 100644
index 00000000000..f776ddd2887
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_table.h
@@ -0,0 +1,126 @@
+// Copyright 2014 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_SPDY_HPACK_HUFFMAN_TABLE_H_
+#define NET_SPDY_HPACK_HUFFMAN_TABLE_H_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_constants.h"
+
+namespace net {
+
+namespace test {
+class HpackHuffmanTablePeer;
+} // namespace test
+
+class HpackInputStream;
+class HpackOutputStream;
+
+// HpackHuffmanTable encodes and decodes string literals using a constructed
+// canonical Huffman code. Once initialized, an instance is read only and
+// may be accessed only through its const interface.
+class NET_EXPORT_PRIVATE HpackHuffmanTable {
+ public:
+ friend class test::HpackHuffmanTablePeer;
+
+ typedef HpackHuffmanSymbol Symbol;
+
+ // DecodeTables are multilevel indexes on code prefixes. Each table indexes
+ // a portion of the prefix mapped to DecodeEntry, which in turn either
+ // captures a terminal symbol, or points to the next DecodeTable to consult
+ // with successive portions of the prefix.
+ struct NET_EXPORT_PRIVATE DecodeEntry {
+ DecodeEntry();
+ DecodeEntry(uint8 next_table_index, uint8 length, uint16 symbol_id);
+
+ // The next table to consult. If this is a terminal,
+ // |next_table_index| will be self-referential.
+ uint8 next_table_index;
+ // Bit-length of terminal code, if this is a terminal. Length of the
+ // longest code having this prefix, if non-terminal.
+ uint8 length;
+ // Set only for terminal entries.
+ uint16 symbol_id;
+ };
+ struct NET_EXPORT_PRIVATE DecodeTable {
+ // Number of bits indexed by the chain leading to this table.
+ uint8 prefix_length;
+ // Number of additional prefix bits this table indexes.
+ uint8 indexed_length;
+ // Entries are represented as a length |size()| slice into
+ // |decode_entries_| beginning at |entries_offset|.
+ size_t entries_offset;
+ // Returns |1 << indexed_length|.
+ size_t size() const;
+ };
+
+ HpackHuffmanTable();
+ ~HpackHuffmanTable();
+
+ // Prepares HpackHuffmanTable to encode & decode the canonical Huffman
+ // code as determined by the given symbols. Must be called exactly once.
+ // Returns false if the input symbols define an invalid coding, and true
+ // otherwise. Symbols must be presented in ascending ID order with no gaps.
+ bool Initialize(const Symbol* input_symbols, size_t symbol_count);
+
+ // Returns whether Initialize() has been successfully called.
+ bool IsInitialized() const;
+
+ // Encodes the input string to the output stream using the table's Huffman
+ // context.
+ void EncodeString(base::StringPiece in, HpackOutputStream* out) const;
+
+ // Returns the encoded size of the input string.
+ size_t EncodedSize(base::StringPiece in) const;
+
+ // Decodes symbols from |in| into |out|. It is the caller's responsibility
+ // to ensure |out| has a reserved a sufficient buffer to hold decoded output.
+ // DecodeString() halts when |in| runs out of input, in which case true is
+ // returned. It also halts (returning false) if an invalid Huffman code
+ // prefix is read, or if |out_capacity| would otherwise be overflowed.
+ bool DecodeString(HpackInputStream* in,
+ size_t out_capacity,
+ std::string* out) const;
+
+ private:
+ // Expects symbols ordered on length & ID ascending.
+ void BuildDecodeTables(const std::vector<Symbol>& symbols);
+
+ // Expects symbols ordered on ID ascending.
+ void BuildEncodeTable(const std::vector<Symbol>& symbols);
+
+ // Adds a new DecodeTable with the argument prefix & indexed length.
+ // Returns the new table index.
+ uint8 AddDecodeTable(uint8 prefix, uint8 indexed);
+
+ const DecodeEntry& Entry(const DecodeTable& table, uint32 index) const;
+
+ void SetEntry(const DecodeTable& table, uint32 index,
+ const DecodeEntry& entry);
+
+ std::vector<DecodeTable> decode_tables_;
+ std::vector<DecodeEntry> decode_entries_;
+
+ // Symbol code and code length, in ascending symbol ID order.
+ // Codes are stored in the most-significant bits of the word.
+ std::vector<uint32> code_by_id_;
+ std::vector<uint8> length_by_id_;
+
+ // The first 8 bits of the longest code. Applied when generating padding bits.
+ uint8 pad_bits_;
+
+ // If initialization fails, preserve the symbol ID which failed validation
+ // for examination in tests.
+ uint16 failed_symbol_id_;
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_HUFFMAN_TABLE_H_
diff --git a/chromium/net/spdy/hpack_huffman_table_test.cc b/chromium/net/spdy/hpack_huffman_table_test.cc
new file mode 100644
index 00000000000..dd2b6230ee6
--- /dev/null
+++ b/chromium/net/spdy/hpack_huffman_table_test.cc
@@ -0,0 +1,522 @@
+// Copyright 2014 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/spdy/hpack_huffman_table.h"
+
+#include <bitset>
+#include <string>
+
+#include "base/logging.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_input_stream.h"
+#include "net/spdy/hpack_output_stream.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using net::test::a2b_hex;
+using std::string;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Pointwise;
+
+namespace net {
+
+namespace test {
+
+typedef HpackHuffmanTable::DecodeEntry DecodeEntry;
+typedef HpackHuffmanTable::DecodeTable DecodeTable;
+
+class HpackHuffmanTablePeer {
+ public:
+ explicit HpackHuffmanTablePeer(const HpackHuffmanTable& table)
+ : table_(table) { }
+
+ const std::vector<uint32>& code_by_id() const {
+ return table_.code_by_id_;
+ }
+ const std::vector<uint8>& length_by_id() const {
+ return table_.length_by_id_;
+ }
+ const std::vector<DecodeTable>& decode_tables() const {
+ return table_.decode_tables_;
+ }
+ char pad_bits() const {
+ // Cast to match signed-ness of bits8().
+ return static_cast<char>(table_.pad_bits_);
+ }
+ uint16 failed_symbol_id() const {
+ return table_.failed_symbol_id_;
+ }
+ std::vector<DecodeEntry> decode_entries(const DecodeTable& decode_table) {
+ std::vector<DecodeEntry>::const_iterator begin =
+ table_.decode_entries_.begin() + decode_table.entries_offset;
+ return std::vector<DecodeEntry>(begin, begin + decode_table.size());
+ }
+ void DumpDecodeTable(const DecodeTable& table) {
+ std::vector<DecodeEntry> entries = decode_entries(table);
+ LOG(INFO) << "Table size " << (1 << table.indexed_length)
+ << " prefix " << unsigned(table.prefix_length)
+ << " indexed " << unsigned(table.indexed_length);
+ size_t i = 0;
+ while (i != table.size()) {
+ const DecodeEntry& entry = entries[i];
+ LOG(INFO) << i << ":"
+ << " next_table " << unsigned(entry.next_table_index)
+ << " length " << unsigned(entry.length)
+ << " symbol " << unsigned(entry.symbol_id);
+ size_t j = 1;
+ for (; (i + j) != table.size(); j++) {
+ const DecodeEntry& next = entries[i + j];
+ if (next.next_table_index != entry.next_table_index ||
+ next.length != entry.length ||
+ next.symbol_id != entry.symbol_id)
+ break;
+ }
+ if (j > 1) {
+ LOG(INFO) << " (repeats " << j << " times)";
+ }
+ i += j;
+ }
+ }
+
+ private:
+ const HpackHuffmanTable& table_;
+};
+
+namespace {
+
+class HpackHuffmanTableTest : public ::testing::Test {
+ protected:
+ HpackHuffmanTableTest()
+ : table_(),
+ peer_(table_) {}
+
+ string EncodeString(StringPiece input) {
+ string result;
+ HpackOutputStream output_stream;
+ table_.EncodeString(input, &output_stream);
+
+ output_stream.TakeString(&result);
+ // Verify EncodedSize() agrees with EncodeString().
+ EXPECT_EQ(result.size(), table_.EncodedSize(input));
+ return result;
+ }
+
+ HpackHuffmanTable table_;
+ HpackHuffmanTablePeer peer_;
+};
+
+MATCHER(DecodeEntryEq, "") {
+ const DecodeEntry& lhs = std::tr1::get<0>(arg);
+ const DecodeEntry& rhs = std::tr1::get<1>(arg);
+ return lhs.next_table_index == rhs.next_table_index &&
+ lhs.length == rhs.length &&
+ lhs.symbol_id == rhs.symbol_id;
+}
+
+uint32 bits32(const string& bitstring) {
+ return std::bitset<32>(bitstring).to_ulong();
+}
+char bits8(const string& bitstring) {
+ return static_cast<char>(std::bitset<8>(bitstring).to_ulong());
+}
+
+TEST_F(HpackHuffmanTableTest, InitializeHpackCode) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+ EXPECT_TRUE(table_.IsInitialized());
+ EXPECT_EQ(peer_.pad_bits(), bits8("11111111")); // First 8 bits of EOS.
+}
+
+TEST_F(HpackHuffmanTableTest, InitializeEdgeCases) {
+ {
+ // Verify eight symbols can be encoded with 3 bits per symbol.
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 3, 0},
+ {bits32("00100000000000000000000000000000"), 3, 1},
+ {bits32("01000000000000000000000000000000"), 3, 2},
+ {bits32("01100000000000000000000000000000"), 3, 3},
+ {bits32("10000000000000000000000000000000"), 3, 4},
+ {bits32("10100000000000000000000000000000"), 3, 5},
+ {bits32("11000000000000000000000000000000"), 3, 6},
+ {bits32("11100000000000000000000000000000"), 8, 7}};
+ HpackHuffmanTable table;
+ EXPECT_TRUE(table.Initialize(code, arraysize(code)));
+ }
+ {
+ // But using 2 bits with one symbol overflows the code.
+ HpackHuffmanSymbol code[] = {
+ {bits32("01000000000000000000000000000000"), 3, 0},
+ {bits32("01100000000000000000000000000000"), 3, 1},
+ {bits32("00000000000000000000000000000000"), 2, 2},
+ {bits32("10000000000000000000000000000000"), 3, 3},
+ {bits32("10100000000000000000000000000000"), 3, 4},
+ {bits32("11000000000000000000000000000000"), 3, 5},
+ {bits32("11100000000000000000000000000000"), 3, 6},
+ {bits32("00000000000000000000000000000000"), 8, 7}}; // Overflow.
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ EXPECT_EQ(7, HpackHuffmanTablePeer(table).failed_symbol_id());
+ }
+ {
+ // Verify four symbols can be encoded with incremental bits per symbol.
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 1, 0},
+ {bits32("10000000000000000000000000000000"), 2, 1},
+ {bits32("11000000000000000000000000000000"), 3, 2},
+ {bits32("11100000000000000000000000000000"), 8, 3}};
+ HpackHuffmanTable table;
+ EXPECT_TRUE(table.Initialize(code, arraysize(code)));
+ }
+ {
+ // But repeating a length overflows the code.
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 1, 0},
+ {bits32("10000000000000000000000000000000"), 2, 1},
+ {bits32("11000000000000000000000000000000"), 2, 2},
+ {bits32("00000000000000000000000000000000"), 8, 3}}; // Overflow.
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ EXPECT_EQ(3, HpackHuffmanTablePeer(table).failed_symbol_id());
+ }
+ {
+ // Symbol IDs must be assigned sequentially with no gaps.
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 1, 0},
+ {bits32("10000000000000000000000000000000"), 2, 1},
+ {bits32("11000000000000000000000000000000"), 3, 1}, // Repeat.
+ {bits32("11100000000000000000000000000000"), 8, 3}};
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
+ }
+ {
+ // Canonical codes must begin with zero.
+ HpackHuffmanSymbol code[] = {
+ {bits32("10000000000000000000000000000000"), 4, 0},
+ {bits32("10010000000000000000000000000000"), 4, 1},
+ {bits32("10100000000000000000000000000000"), 4, 2},
+ {bits32("10110000000000000000000000000000"), 8, 3}};
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ EXPECT_EQ(0, HpackHuffmanTablePeer(table).failed_symbol_id());
+ }
+ {
+ // Codes must match the expected canonical sequence.
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 2, 0},
+ {bits32("01000000000000000000000000000000"), 2, 1},
+ {bits32("11000000000000000000000000000000"), 2, 2}, // Not canonical.
+ {bits32("10000000000000000000000000000000"), 8, 3}};
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
+ }
+ {
+ // At least one code must have a length of 8 bits (to ensure pad-ability).
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 1, 0},
+ {bits32("10000000000000000000000000000000"), 2, 1},
+ {bits32("11000000000000000000000000000000"), 3, 2},
+ {bits32("11100000000000000000000000000000"), 7, 3}};
+ HpackHuffmanTable table;
+ EXPECT_FALSE(table.Initialize(code, arraysize(code)));
+ }
+}
+
+TEST_F(HpackHuffmanTableTest, ValidateInternalsWithSmallCode) {
+ HpackHuffmanSymbol code[] = {
+ {bits32("01100000000000000000000000000000"), 4, 0}, // 3rd.
+ {bits32("01110000000000000000000000000000"), 4, 1}, // 4th.
+ {bits32("00000000000000000000000000000000"), 2, 2}, // 1st assigned code.
+ {bits32("01000000000000000000000000000000"), 3, 3}, // 2nd.
+ {bits32("10000000000000000000000000000000"), 5, 4}, // 5th.
+ {bits32("10001000000000000000000000000000"), 5, 5}, // 6th.
+ {bits32("10011000000000000000000000000000"), 8, 6}, // 8th.
+ {bits32("10010000000000000000000000000000"), 5, 7}}; // 7th.
+ EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
+
+ EXPECT_THAT(peer_.code_by_id(), ElementsAre(
+ bits32("01100000000000000000000000000000"),
+ bits32("01110000000000000000000000000000"),
+ bits32("00000000000000000000000000000000"),
+ bits32("01000000000000000000000000000000"),
+ bits32("10000000000000000000000000000000"),
+ bits32("10001000000000000000000000000000"),
+ bits32("10011000000000000000000000000000"),
+ bits32("10010000000000000000000000000000")));
+ EXPECT_THAT(peer_.length_by_id(), ElementsAre(
+ 4, 4, 2, 3, 5, 5, 8, 5));
+
+ EXPECT_EQ(1u, peer_.decode_tables().size());
+ {
+ std::vector<DecodeEntry> expected;
+ expected.resize(128, DecodeEntry(0, 2, 2)); // Fills 128.
+ expected.resize(192, DecodeEntry(0, 3, 3)); // Fills 64.
+ expected.resize(224, DecodeEntry(0, 4, 0)); // Fills 32.
+ expected.resize(256, DecodeEntry(0, 4, 1)); // Fills 32.
+ expected.resize(272, DecodeEntry(0, 5, 4)); // Fills 16.
+ expected.resize(288, DecodeEntry(0, 5, 5)); // Fills 16.
+ expected.resize(304, DecodeEntry(0, 5, 7)); // Fills 16.
+ expected.resize(306, DecodeEntry(0, 8, 6)); // Fills 2.
+ expected.resize(512, DecodeEntry()); // Remainder is empty.
+
+ EXPECT_THAT(peer_.decode_entries(peer_.decode_tables()[0]),
+ Pointwise(DecodeEntryEq(), expected));
+ }
+ EXPECT_EQ(bits8("10011000"), peer_.pad_bits());
+
+ char input_storage[] = {2, 3, 2, 7, 4};
+ StringPiece input(input_storage, arraysize(input_storage));
+ // By symbol: (2) 00 (3) 010 (2) 00 (7) 10010 (4) 10000 (6 as pad) 1001100.
+ char expect_storage[] = {
+ bits8("00010001"),
+ bits8("00101000"),
+ bits8("01001100")};
+ StringPiece expect(expect_storage, arraysize(expect_storage));
+
+ string buffer_in = EncodeString(input);
+ EXPECT_EQ(expect, buffer_in);
+
+ string buffer_out;
+ HpackInputStream input_stream(kuint32max, buffer_in);
+ EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
+ EXPECT_EQ(buffer_out, input);
+}
+
+TEST_F(HpackHuffmanTableTest, ValidateMultiLevelDecodeTables) {
+ HpackHuffmanSymbol code[] = {
+ {bits32("00000000000000000000000000000000"), 6, 0},
+ {bits32("00000100000000000000000000000000"), 6, 1},
+ {bits32("00001000000000000000000000000000"), 11, 2},
+ {bits32("00001000001000000000000000000000"), 11, 3},
+ {bits32("00001000010000000000000000000000"), 12, 4},
+ };
+ EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
+
+ EXPECT_EQ(2u, peer_.decode_tables().size());
+ {
+ std::vector<DecodeEntry> expected;
+ expected.resize(8, DecodeEntry(0, 6, 0)); // Fills 8.
+ expected.resize(16, DecodeEntry(0, 6, 1)); // Fills 8.
+ expected.resize(17, DecodeEntry(1, 12, 0)); // Pointer. Fills 1.
+ expected.resize(512, DecodeEntry()); // Remainder is empty.
+
+ const DecodeTable& decode_table = peer_.decode_tables()[0];
+ EXPECT_EQ(decode_table.prefix_length, 0);
+ EXPECT_EQ(decode_table.indexed_length, 9);
+ EXPECT_THAT(peer_.decode_entries(decode_table),
+ Pointwise(DecodeEntryEq(), expected));
+ }
+ {
+ std::vector<DecodeEntry> expected;
+ expected.resize(2, DecodeEntry(1, 11, 2)); // Fills 2.
+ expected.resize(4, DecodeEntry(1, 11, 3)); // Fills 2.
+ expected.resize(5, DecodeEntry(1, 12, 4)); // Fills 1.
+ expected.resize(8, DecodeEntry()); // Remainder is empty.
+
+ const DecodeTable& decode_table = peer_.decode_tables()[1];
+ EXPECT_EQ(decode_table.prefix_length, 9);
+ EXPECT_EQ(decode_table.indexed_length, 3);
+ EXPECT_THAT(peer_.decode_entries(decode_table),
+ Pointwise(DecodeEntryEq(), expected));
+ }
+ EXPECT_EQ(bits8("00001000"), peer_.pad_bits());
+}
+
+TEST_F(HpackHuffmanTableTest, DecodeWithBadInput) {
+ HpackHuffmanSymbol code[] = {
+ {bits32("01100000000000000000000000000000"), 4, 0},
+ {bits32("01110000000000000000000000000000"), 4, 1},
+ {bits32("00000000000000000000000000000000"), 2, 2},
+ {bits32("01000000000000000000000000000000"), 3, 3},
+ {bits32("10000000000000000000000000000000"), 5, 4},
+ {bits32("10001000000000000000000000000000"), 5, 5},
+ {bits32("10011000000000000000000000000000"), 6, 6},
+ {bits32("10010000000000000000000000000000"), 5, 7},
+ {bits32("10011100000000000000000000000000"), 16, 8}};
+ EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
+
+ string buffer;
+ const size_t capacity = 4;
+ {
+ // This example works: (2) 00 (3) 010 (2) 00 (6) 100110 (pad) 100.
+ char input_storage[] = {bits8("00010001"), bits8("00110100")};
+ StringPiece input(input_storage, arraysize(input_storage));
+
+ HpackInputStream input_stream(kuint32max, input);
+ EXPECT_TRUE(table_.DecodeString(&input_stream, capacity, &buffer));
+ EXPECT_EQ(buffer, "\x02\x03\x02\x06");
+ }
+ {
+ // Expect to fail on an invalid code prefix.
+ // (2) 00 (3) 010 (2) 00 (too-large) 101000 (pad) 100.
+ char input_storage[] = {bits8("00010001"), bits8("01000111")};
+ StringPiece input(input_storage, arraysize(input_storage));
+
+ HpackInputStream input_stream(kuint32max, input);
+ EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
+ EXPECT_EQ(buffer, "\x02\x03\x02");
+ }
+ {
+ // Repeat the shortest 0b00 code to overflow |buffer|. Expect to fail.
+ std::vector<char> input_storage(1 + capacity / 4, '\0');
+ StringPiece input(&input_storage[0], input_storage.size());
+
+ HpackInputStream input_stream(kuint32max, input);
+ EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
+
+ std::vector<char> expected(capacity, '\x02');
+ EXPECT_THAT(buffer, ElementsAreArray(expected));
+ EXPECT_EQ(capacity, buffer.size());
+ }
+ {
+ // Expect to fail if more than a byte of unconsumed input remains.
+ // (6) 100110 (8 truncated) 1001110000
+ char input_storage[] = {bits8("10011010"), bits8("01110000")};
+ StringPiece input(input_storage, arraysize(input_storage));
+
+ HpackInputStream input_stream(kuint32max, input);
+ EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
+ EXPECT_EQ(buffer, "\x06");
+ }
+}
+
+TEST_F(HpackHuffmanTableTest, SpecRequestExamples) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+
+ string buffer;
+ string test_table[] = {
+ a2b_hex("e7cf9bebe89b6fb16fa9b6ff"),
+ "www.example.com",
+ a2b_hex("b9b9949556bf"),
+ "no-cache",
+ a2b_hex("571c5cdb737b2faf"),
+ "custom-key",
+ a2b_hex("571c5cdb73724d9c57"),
+ "custom-value",
+ };
+ // Round-trip each test example.
+ for (size_t i = 0; i != arraysize(test_table); i += 2) {
+ const string& encodedFixture(test_table[i]);
+ const string& decodedFixture(test_table[i+1]);
+ HpackInputStream input_stream(kuint32max, encodedFixture);
+
+ EXPECT_TRUE(table_.DecodeString(&input_stream, decodedFixture.size(),
+ &buffer));
+ EXPECT_EQ(decodedFixture, buffer);
+ buffer = EncodeString(decodedFixture);
+ EXPECT_EQ(encodedFixture, buffer);
+ }
+}
+
+TEST_F(HpackHuffmanTableTest, SpecResponseExamples) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+
+ string buffer;
+ string test_table[] = {
+ a2b_hex("4017"),
+ "302",
+ a2b_hex("bf06724b97"),
+ "private",
+ a2b_hex("d6dbb29884de2a718805062098513109"
+ "b56ba3"),
+ "Mon, 21 Oct 2013 20:13:21 GMT",
+ a2b_hex("adcebf198e7e7cf9bebe89b6fb16fa9b"
+ "6f"),
+ "https://www.example.com",
+ a2b_hex("e0d6cf9f6e8f9fd3e5f6fa76fefd3c7e"
+ "df9eff1f2f0f3cfe9f6fcf7f8f879f61"
+ "ad4f4cc9a973a2200ec3725e18b1b74e"
+ "3f"),
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ };
+ // Round-trip each test example.
+ for (size_t i = 0; i != arraysize(test_table); i += 2) {
+ const string& encodedFixture(test_table[i]);
+ const string& decodedFixture(test_table[i+1]);
+ HpackInputStream input_stream(kuint32max, encodedFixture);
+
+ EXPECT_TRUE(table_.DecodeString(&input_stream, decodedFixture.size(),
+ &buffer));
+ EXPECT_EQ(decodedFixture, buffer);
+ buffer = EncodeString(decodedFixture);
+ EXPECT_EQ(encodedFixture, buffer);
+ }
+}
+
+TEST_F(HpackHuffmanTableTest, RoundTripIndvidualSymbols) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+
+ for (size_t i = 0; i != 256; i++) {
+ char c = static_cast<char>(i);
+ char storage[3] = {c, c, c};
+ StringPiece input(storage, arraysize(storage));
+
+ string buffer_in = EncodeString(input);
+ string buffer_out;
+
+ HpackInputStream input_stream(kuint32max, buffer_in);
+ EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
+ EXPECT_EQ(input, buffer_out);
+ }
+}
+
+TEST_F(HpackHuffmanTableTest, RoundTripSymbolSequence) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+
+
+ char storage[512];
+ for (size_t i = 0; i != 256; i++) {
+ storage[i] = static_cast<char>(i);
+ storage[511 - i] = static_cast<char>(i);
+ }
+ StringPiece input(storage, arraysize(storage));
+
+ string buffer_in = EncodeString(input);
+ string buffer_out;
+
+ HpackInputStream input_stream(kuint32max, buffer_in);
+ EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
+ EXPECT_EQ(input, buffer_out);
+}
+
+TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
+
+ string test_table[] = {
+ "",
+ "Mon, 21 Oct 2013 20:13:21 GMT",
+ "https://www.example.com",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ string(1, '\0'),
+ string("foo\0bar", 7),
+ string(256, '\0'),
+ };
+ for (size_t i = 0; i != 256; ++i) {
+ // Expand last |test_table| entry to cover all codes.
+ test_table[arraysize(test_table)-1][i] = static_cast<char>(i);
+ }
+
+ HpackOutputStream output_stream;
+ string encoding;
+ for (size_t i = 0; i != arraysize(test_table); ++i) {
+ table_.EncodeString(test_table[i], &output_stream);
+ output_stream.TakeString(&encoding);
+ EXPECT_EQ(encoding.size(), table_.EncodedSize(test_table[i]));
+ }
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_input_stream.cc b/chromium/net/spdy/hpack_input_stream.cc
new file mode 100644
index 00000000000..87a3af4ec78
--- /dev/null
+++ b/chromium/net/spdy/hpack_input_stream.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 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/spdy/hpack_input_stream.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+
+HpackInputStream::HpackInputStream(uint32 max_string_literal_size,
+ StringPiece buffer)
+ : max_string_literal_size_(max_string_literal_size),
+ buffer_(buffer),
+ bit_offset_(0) {}
+
+HpackInputStream::~HpackInputStream() {}
+
+bool HpackInputStream::HasMoreData() const {
+ return !buffer_.empty();
+}
+
+bool HpackInputStream::MatchPrefixAndConsume(HpackPrefix prefix) {
+ DCHECK_GT(prefix.bit_size, 0u);
+ DCHECK_LE(prefix.bit_size, 8u);
+
+ uint32 peeked = 0;
+ size_t peeked_count = 0;
+
+ if (!PeekBits(&peeked_count, &peeked))
+ return false;
+
+ if ((peeked >> (32 - prefix.bit_size)) == prefix.bits) {
+ ConsumeBits(prefix.bit_size);
+ return true;
+ }
+ return false;
+}
+
+bool HpackInputStream::PeekNextOctet(uint8* next_octet) {
+ if ((bit_offset_ > 0) || buffer_.empty())
+ return false;
+
+ *next_octet = buffer_[0];
+ return true;
+}
+
+bool HpackInputStream::DecodeNextOctet(uint8* next_octet) {
+ if (!PeekNextOctet(next_octet))
+ return false;
+
+ buffer_.remove_prefix(1);
+ return true;
+}
+
+bool HpackInputStream::DecodeNextUint32(uint32* I) {
+ size_t N = 8 - bit_offset_;
+ DCHECK_GT(N, 0u);
+ DCHECK_LE(N, 8u);
+
+ bit_offset_ = 0;
+
+ *I = 0;
+
+ uint8 next_marker = (1 << N) - 1;
+ uint8 next_octet = 0;
+ if (!DecodeNextOctet(&next_octet))
+ return false;
+ *I = next_octet & next_marker;
+
+ bool has_more = (*I == next_marker);
+ size_t shift = 0;
+ while (has_more && (shift < 32)) {
+ uint8 next_octet = 0;
+ if (!DecodeNextOctet(&next_octet))
+ return false;
+ has_more = (next_octet & 0x80) != 0;
+ next_octet &= 0x7f;
+ uint32 addend = next_octet << shift;
+ // Check for overflow.
+ if ((addend >> shift) != next_octet) {
+ return false;
+ }
+ *I += addend;
+ shift += 7;
+ }
+
+ return !has_more;
+}
+
+bool HpackInputStream::DecodeNextIdentityString(StringPiece* str) {
+ uint32 size = 0;
+ if (!DecodeNextUint32(&size))
+ return false;
+
+ if (size > max_string_literal_size_)
+ return false;
+
+ if (size > buffer_.size())
+ return false;
+
+ *str = StringPiece(buffer_.data(), size);
+ buffer_.remove_prefix(size);
+ return true;
+}
+
+bool HpackInputStream::DecodeNextHuffmanString(const HpackHuffmanTable& table,
+ string* str) {
+ uint32 encoded_size = 0;
+ if (!DecodeNextUint32(&encoded_size))
+ return false;
+
+ if (encoded_size > buffer_.size())
+ return false;
+
+ HpackInputStream bounded_reader(
+ max_string_literal_size_,
+ StringPiece(buffer_.data(), encoded_size));
+ buffer_.remove_prefix(encoded_size);
+
+ // HpackHuffmanTable will not decode beyond |max_string_literal_size_|.
+ return table.DecodeString(&bounded_reader, max_string_literal_size_, str);
+}
+
+bool HpackInputStream::PeekBits(size_t* peeked_count, uint32* out) {
+ size_t byte_offset = (bit_offset_ + *peeked_count) / 8;
+ size_t bit_offset = (bit_offset_ + *peeked_count) % 8;
+
+ if (*peeked_count >= 32 || byte_offset >= buffer_.size()) {
+ return false;
+ }
+ // We'll read the minimum of the current byte remainder,
+ // and the remaining unfilled bits of |out|.
+ size_t bits_to_read = std::min(32 - *peeked_count, 8 - bit_offset);
+
+ uint32 new_bits = static_cast<uint32>(buffer_[byte_offset]);
+ // Shift byte remainder to most-signifcant bits of |new_bits|.
+ // This drops the leading |bit_offset| bits of the byte.
+ new_bits = new_bits << (24 + bit_offset);
+ // Shift bits to the most-significant open bits of |out|.
+ new_bits = new_bits >> *peeked_count;
+
+ CHECK_EQ(*out & new_bits, 0u);
+ *out |= new_bits;
+
+ *peeked_count += bits_to_read;
+ return true;
+}
+
+void HpackInputStream::ConsumeBits(size_t bit_count) {
+ size_t byte_count = (bit_offset_ + bit_count) / 8;
+ bit_offset_ = (bit_offset_ + bit_count) % 8;
+ CHECK_GE(buffer_.size(), byte_count);
+ if (bit_offset_ != 0) {
+ CHECK_GT(buffer_.size(), 0u);
+ }
+ buffer_.remove_prefix(byte_count);
+}
+
+void HpackInputStream::ConsumeByteRemainder() {
+ if (bit_offset_ != 0) {
+ ConsumeBits(8 - bit_offset_);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_input_stream.h b/chromium/net/spdy/hpack_input_stream.h
new file mode 100644
index 00000000000..ec1dc03bbc7
--- /dev/null
+++ b/chromium/net/spdy/hpack_input_stream.h
@@ -0,0 +1,80 @@
+// Copyright 2014 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_SPDY_HPACK_INPUT_STREAM_H_
+#define NET_SPDY_HPACK_INPUT_STREAM_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_huffman_table.h"
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+// An HpackInputStream handles all the low-level details of decoding
+// header fields.
+class NET_EXPORT_PRIVATE HpackInputStream {
+ public:
+ // |max_string_literal_size| is the largest that any one string
+ // literal (header name or header value) can be.
+ HpackInputStream(uint32 max_string_literal_size, base::StringPiece buffer);
+ ~HpackInputStream();
+
+ // Returns whether or not there is more data to process.
+ bool HasMoreData() const;
+
+ // If the next bits of input match |prefix|, consumes them and returns true.
+ // Otherwise, consumes nothing and returns false.
+ bool MatchPrefixAndConsume(HpackPrefix prefix);
+
+ // The Decode* functions return true and fill in their arguments if
+ // decoding was successful, or false if an error was encountered.
+
+ bool DecodeNextUint32(uint32* I);
+ bool DecodeNextIdentityString(base::StringPiece* str);
+ bool DecodeNextHuffmanString(const HpackHuffmanTable& table,
+ std::string* str);
+
+ // Stores input bits into the most-significant, unfilled bits of |out|.
+ // |peeked_count| is the number of filled bits in |out| which have been
+ // previously peeked. PeekBits() will fill some number of remaining bits,
+ // returning the new total number via |peeked_count|. Returns true if one
+ // or more additional bits could be peeked, and false otherwise.
+ bool PeekBits(size_t* peeked_count, uint32* out);
+
+ // Consumes |count| bits of input. Generally paired with PeekBits().
+ void ConsumeBits(size_t count);
+
+ // If not currently on a byte boundary, consumes and discards
+ // remaining bits in the current byte.
+ void ConsumeByteRemainder();
+
+ // Accessors for testing.
+
+ void SetBitOffsetForTest(size_t bit_offset) {
+ bit_offset_ = bit_offset;
+ }
+
+ private:
+ const uint32 max_string_literal_size_;
+ base::StringPiece buffer_;
+ size_t bit_offset_;
+
+ bool PeekNextOctet(uint8* next_octet);
+
+ bool DecodeNextOctet(uint8* next_octet);
+
+ DISALLOW_COPY_AND_ASSIGN(HpackInputStream);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_INPUT_STREAM_H_
diff --git a/chromium/net/spdy/hpack_input_stream_test.cc b/chromium/net/spdy/hpack_input_stream_test.cc
new file mode 100644
index 00000000000..215d1c6959d
--- /dev/null
+++ b/chromium/net/spdy/hpack_input_stream_test.cc
@@ -0,0 +1,629 @@
+// Copyright 2014 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/spdy/hpack_input_stream.h"
+
+#include <bitset>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using base::StringPiece;
+using std::string;
+using test::a2b_hex;
+
+const size_t kLiteralBound = 1024;
+
+class HpackInputStreamTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
+ EXPECT_TRUE(huffman_table.Initialize(&code[0], code.size()));
+ }
+
+ HpackHuffmanTable huffman_table;
+};
+
+// Hex representation of encoded length and Huffman string.
+const char kEncodedHuffmanFixture[] = "31" // Length prefix.
+ "e0d6cf9f6e8f9fd3e5f6fa76fefd3c7e"
+ "df9eff1f2f0f3cfe9f6fcf7f8f879f61"
+ "ad4f4cc9a973a2200ec3725e18b1b74e"
+ "3f";
+
+const char kDecodedHuffmanFixture[] =
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1";
+
+// Utility function to decode an assumed-valid uint32 with an N-bit
+// prefix.
+uint32 DecodeValidUint32(uint8 N, StringPiece str) {
+ EXPECT_GT(N, 0);
+ EXPECT_LE(N, 8);
+ HpackInputStream input_stream(kLiteralBound, str);
+ input_stream.SetBitOffsetForTest(8 - N);
+ uint32 I;
+ EXPECT_TRUE(input_stream.DecodeNextUint32(&I));
+ return I;
+}
+
+// Utility function to decode an assumed-invalid uint32 with an N-bit
+// prefix.
+void ExpectDecodeUint32Invalid(uint8 N, StringPiece str) {
+ EXPECT_GT(N, 0);
+ EXPECT_LE(N, 8);
+ HpackInputStream input_stream(kLiteralBound, str);
+ input_stream.SetBitOffsetForTest(8 - N);
+ uint32 I;
+ EXPECT_FALSE(input_stream.DecodeNextUint32(&I));
+}
+
+uint32 bits32(const string& bitstring) {
+ return std::bitset<32>(bitstring).to_ulong();
+}
+
+// The {Number}ByteIntegersEightBitPrefix tests below test that
+// certain integers are decoded correctly with an 8-bit prefix in
+// exactly {Number} bytes.
+
+TEST_F(HpackInputStreamTest, OneByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x00u, DecodeValidUint32(8, string("\x00", 1)));
+ EXPECT_EQ(0x7fu, DecodeValidUint32(8, "\x7f"));
+ // Maximum.
+ EXPECT_EQ(0xfeu, DecodeValidUint32(8, "\xfe"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff");
+}
+
+TEST_F(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0xffu, DecodeValidUint32(8, string("\xff\x00", 2)));
+ EXPECT_EQ(0x0100u, DecodeValidUint32(8, "\xff\x01"));
+ // Maximum.
+ EXPECT_EQ(0x017eu, DecodeValidUint32(8, "\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, ThreeByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x017fu, DecodeValidUint32(8, "\xff\x80\x01"));
+ EXPECT_EQ(0x0fffu, DecodeValidUint32(8, "\xff\x80\x1e"));
+ // Maximum.
+ EXPECT_EQ(0x40feu, DecodeValidUint32(8, "\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, FourByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x40ffu, DecodeValidUint32(8, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0xffffu, DecodeValidUint32(8, "\xff\x80\xfe\x03"));
+ // Maximum.
+ EXPECT_EQ(0x002000feu, DecodeValidUint32(8, "\xff\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, FiveByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x002000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x00ffffffu, DecodeValidUint32(8, "\xff\x80\xfe\xff\x07"));
+ // Maximum.
+ EXPECT_EQ(0x100000feu, DecodeValidUint32(8, "\xff\xff\xff\xff\x7f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, SixByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(0x100000ffu, DecodeValidUint32(8, "\xff\x80\x80\x80\x80\x01"));
+ // Maximum.
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(8, "\xff\x80\xfe\xff\xff\x0f"));
+ // Invalid.
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\x80\xfe\xff\xff\x10");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff\xff");
+}
+
+// There are no valid uint32 encodings that are greater than six
+// bytes.
+TEST_F(HpackInputStreamTest, SevenByteIntegersEightBitPrefix) {
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(8, "\xff\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(8, "\xff\xff\xff\xff\xff\xff\xff");
+}
+
+// The {Number}ByteIntegersOneToSevenBitPrefix tests below test that
+// certain integers are encoded correctly with an N-bit prefix in
+// exactly {Number} bytes for N in {1, 2, ..., 7}.
+
+TEST_F(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x80", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(6, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(6, string("\xc0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(5, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(5, string("\xe0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(4, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(4, string("\xf0", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(3, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(3, string("\xf8", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(2, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(2, string("\xfc", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\xfe", 1)));
+
+ // Maximums.
+ EXPECT_EQ(0x7eu, DecodeValidUint32(7, "\x7e"));
+ EXPECT_EQ(0x7eu, DecodeValidUint32(7, "\xfe"));
+ EXPECT_EQ(0x3eu, DecodeValidUint32(6, "\x3e"));
+ EXPECT_EQ(0x3eu, DecodeValidUint32(6, "\xfe"));
+ EXPECT_EQ(0x1eu, DecodeValidUint32(5, "\x1e"));
+ EXPECT_EQ(0x1eu, DecodeValidUint32(5, "\xfe"));
+ EXPECT_EQ(0x0eu, DecodeValidUint32(4, "\x0e"));
+ EXPECT_EQ(0x0eu, DecodeValidUint32(4, "\xfe"));
+ EXPECT_EQ(0x06u, DecodeValidUint32(3, "\x06"));
+ EXPECT_EQ(0x06u, DecodeValidUint32(3, "\xfe"));
+ EXPECT_EQ(0x02u, DecodeValidUint32(2, "\x02"));
+ EXPECT_EQ(0x02u, DecodeValidUint32(2, "\xfe"));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+ EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\xfe", 1)));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f");
+ ExpectDecodeUint32Invalid(7, "\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f");
+ ExpectDecodeUint32Invalid(6, "\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f");
+ ExpectDecodeUint32Invalid(5, "\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f");
+ ExpectDecodeUint32Invalid(4, "\xff");
+ ExpectDecodeUint32Invalid(3, "\x07");
+ ExpectDecodeUint32Invalid(3, "\xff");
+ ExpectDecodeUint32Invalid(2, "\x03");
+ ExpectDecodeUint32Invalid(2, "\xff");
+ ExpectDecodeUint32Invalid(1, "\x01");
+ ExpectDecodeUint32Invalid(1, "\xff");
+}
+
+TEST_F(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\x7f\x00", 2)));
+ EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\xff\x00", 2)));
+ EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\x3f\x00", 2)));
+ EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\xff\x00", 2)));
+ EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\x1f\x00", 2)));
+ EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\xff\x00", 2)));
+ EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\x0f\x00", 2)));
+ EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\xff\x00", 2)));
+ EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\x07\x00", 2)));
+ EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\xff\x00", 2)));
+ EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\x03\x00", 2)));
+ EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\xff\x00", 2)));
+ EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\x01\x00", 2)));
+ EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\xff\x00", 2)));
+
+ // Maximums.
+ EXPECT_EQ(0xfeu, DecodeValidUint32(7, "\x7f\x7f"));
+ EXPECT_EQ(0xfeu, DecodeValidUint32(7, "\xff\x7f"));
+ EXPECT_EQ(0xbeu, DecodeValidUint32(6, "\x3f\x7f"));
+ EXPECT_EQ(0xbeu, DecodeValidUint32(6, "\xff\x7f"));
+ EXPECT_EQ(0x9eu, DecodeValidUint32(5, "\x1f\x7f"));
+ EXPECT_EQ(0x9eu, DecodeValidUint32(5, "\xff\x7f"));
+ EXPECT_EQ(0x8eu, DecodeValidUint32(4, "\x0f\x7f"));
+ EXPECT_EQ(0x8eu, DecodeValidUint32(4, "\xff\x7f"));
+ EXPECT_EQ(0x86u, DecodeValidUint32(3, "\x07\x7f"));
+ EXPECT_EQ(0x86u, DecodeValidUint32(3, "\xff\x7f"));
+ EXPECT_EQ(0x82u, DecodeValidUint32(2, "\x03\x7f"));
+ EXPECT_EQ(0x82u, DecodeValidUint32(2, "\xff\x7f"));
+ EXPECT_EQ(0x80u, DecodeValidUint32(1, "\x01\x7f"));
+ EXPECT_EQ(0x80u, DecodeValidUint32(1, "\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0xffu, DecodeValidUint32(7, "\x7f\x80\x01"));
+ EXPECT_EQ(0xffu, DecodeValidUint32(7, "\xff\x80\x01"));
+ EXPECT_EQ(0xbfu, DecodeValidUint32(6, "\x3f\x80\x01"));
+ EXPECT_EQ(0xbfu, DecodeValidUint32(6, "\xff\x80\x01"));
+ EXPECT_EQ(0x9fu, DecodeValidUint32(5, "\x1f\x80\x01"));
+ EXPECT_EQ(0x9fu, DecodeValidUint32(5, "\xff\x80\x01"));
+ EXPECT_EQ(0x8fu, DecodeValidUint32(4, "\x0f\x80\x01"));
+ EXPECT_EQ(0x8fu, DecodeValidUint32(4, "\xff\x80\x01"));
+ EXPECT_EQ(0x87u, DecodeValidUint32(3, "\x07\x80\x01"));
+ EXPECT_EQ(0x87u, DecodeValidUint32(3, "\xff\x80\x01"));
+ EXPECT_EQ(0x83u, DecodeValidUint32(2, "\x03\x80\x01"));
+ EXPECT_EQ(0x83u, DecodeValidUint32(2, "\xff\x80\x01"));
+ EXPECT_EQ(0x81u, DecodeValidUint32(1, "\x01\x80\x01"));
+ EXPECT_EQ(0x81u, DecodeValidUint32(1, "\xff\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x407eu, DecodeValidUint32(7, "\x7f\xff\x7f"));
+ EXPECT_EQ(0x407eu, DecodeValidUint32(7, "\xff\xff\x7f"));
+ EXPECT_EQ(0x403eu, DecodeValidUint32(6, "\x3f\xff\x7f"));
+ EXPECT_EQ(0x403eu, DecodeValidUint32(6, "\xff\xff\x7f"));
+ EXPECT_EQ(0x401eu, DecodeValidUint32(5, "\x1f\xff\x7f"));
+ EXPECT_EQ(0x401eu, DecodeValidUint32(5, "\xff\xff\x7f"));
+ EXPECT_EQ(0x400eu, DecodeValidUint32(4, "\x0f\xff\x7f"));
+ EXPECT_EQ(0x400eu, DecodeValidUint32(4, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4006u, DecodeValidUint32(3, "\x07\xff\x7f"));
+ EXPECT_EQ(0x4006u, DecodeValidUint32(3, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4002u, DecodeValidUint32(2, "\x03\xff\x7f"));
+ EXPECT_EQ(0x4002u, DecodeValidUint32(2, "\xff\xff\x7f"));
+ EXPECT_EQ(0x4000u, DecodeValidUint32(1, "\x01\xff\x7f"));
+ EXPECT_EQ(0x4000u, DecodeValidUint32(1, "\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, FourByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\x7f\x80\x80\x01"));
+ EXPECT_EQ(0x407fu, DecodeValidUint32(7, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x403fu, DecodeValidUint32(6, "\x3f\x80\x80\x01"));
+ EXPECT_EQ(0x403fu, DecodeValidUint32(6, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x401fu, DecodeValidUint32(5, "\x1f\x80\x80\x01"));
+ EXPECT_EQ(0x401fu, DecodeValidUint32(5, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x400fu, DecodeValidUint32(4, "\x0f\x80\x80\x01"));
+ EXPECT_EQ(0x400fu, DecodeValidUint32(4, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4007u, DecodeValidUint32(3, "\x07\x80\x80\x01"));
+ EXPECT_EQ(0x4007u, DecodeValidUint32(3, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4003u, DecodeValidUint32(2, "\x03\x80\x80\x01"));
+ EXPECT_EQ(0x4003u, DecodeValidUint32(2, "\xff\x80\x80\x01"));
+ EXPECT_EQ(0x4001u, DecodeValidUint32(1, "\x01\x80\x80\x01"));
+ EXPECT_EQ(0x4001u, DecodeValidUint32(1, "\xff\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x20007eu, DecodeValidUint32(7, "\x7f\xff\xff\x7f"));
+ EXPECT_EQ(0x20007eu, DecodeValidUint32(7, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20003eu, DecodeValidUint32(6, "\x3f\xff\xff\x7f"));
+ EXPECT_EQ(0x20003eu, DecodeValidUint32(6, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20001eu, DecodeValidUint32(5, "\x1f\xff\xff\x7f"));
+ EXPECT_EQ(0x20001eu, DecodeValidUint32(5, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x20000eu, DecodeValidUint32(4, "\x0f\xff\xff\x7f"));
+ EXPECT_EQ(0x20000eu, DecodeValidUint32(4, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200006u, DecodeValidUint32(3, "\x07\xff\xff\x7f"));
+ EXPECT_EQ(0x200006u, DecodeValidUint32(3, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200002u, DecodeValidUint32(2, "\x03\xff\xff\x7f"));
+ EXPECT_EQ(0x200002u, DecodeValidUint32(2, "\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x200000u, DecodeValidUint32(1, "\x01\xff\xff\x7f"));
+ EXPECT_EQ(0x200000u, DecodeValidUint32(1, "\xff\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\x7f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20007fu, DecodeValidUint32(7, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20003fu, DecodeValidUint32(6, "\x3f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20003fu, DecodeValidUint32(6, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20001fu, DecodeValidUint32(5, "\x1f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20001fu, DecodeValidUint32(5, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20000fu, DecodeValidUint32(4, "\x0f\x80\x80\x80\x01"));
+ EXPECT_EQ(0x20000fu, DecodeValidUint32(4, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200007u, DecodeValidUint32(3, "\x07\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200007u, DecodeValidUint32(3, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200003u, DecodeValidUint32(2, "\x03\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200003u, DecodeValidUint32(2, "\xff\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200001u, DecodeValidUint32(1, "\x01\x80\x80\x80\x01"));
+ EXPECT_EQ(0x200001u, DecodeValidUint32(1, "\xff\x80\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0x1000007eu, DecodeValidUint32(7, "\x7f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000007eu, DecodeValidUint32(7, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000003eu, DecodeValidUint32(6, "\x3f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000003eu, DecodeValidUint32(6, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000001eu, DecodeValidUint32(5, "\x1f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000001eu, DecodeValidUint32(5, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000000eu, DecodeValidUint32(4, "\x0f\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x1000000eu, DecodeValidUint32(4, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000006u, DecodeValidUint32(3, "\x07\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000006u, DecodeValidUint32(3, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000002u, DecodeValidUint32(2, "\x03\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000002u, DecodeValidUint32(2, "\xff\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000000u, DecodeValidUint32(1, "\x01\xff\xff\xff\x7f"));
+ EXPECT_EQ(0x10000000u, DecodeValidUint32(1, "\xff\xff\xff\xff\x7f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xff\xff\xff\x80");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff\xff");
+}
+
+TEST_F(HpackInputStreamTest, SixByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(0x1000007fu, DecodeValidUint32(7, "\x7f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000007fu, DecodeValidUint32(7, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000003fu, DecodeValidUint32(6, "\x3f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000003fu, DecodeValidUint32(6, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000001fu, DecodeValidUint32(5, "\x1f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000001fu, DecodeValidUint32(5, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000000fu, DecodeValidUint32(4, "\x0f\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x1000000fu, DecodeValidUint32(4, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000007u, DecodeValidUint32(3, "\x07\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000007u, DecodeValidUint32(3, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000003u, DecodeValidUint32(2, "\x03\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000003u, DecodeValidUint32(2, "\xff\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000001u, DecodeValidUint32(1, "\x01\x80\x80\x80\x80\x01"));
+ EXPECT_EQ(0x10000001u, DecodeValidUint32(1, "\xff\x80\x80\x80\x80\x01"));
+
+ // Maximums.
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(7, "\x7f\x80\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(7, "\xff\x80\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(6, "\x3f\xc0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(6, "\xff\xc0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(5, "\x1f\xe0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(5, "\xff\xe0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(4, "\x0f\xf0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(4, "\xff\xf0\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(3, "\x07\xf8\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(3, "\xff\xf8\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(2, "\x03\xfc\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(2, "\xff\xfc\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(1, "\x01\xfe\xff\xff\xff\x0f"));
+ EXPECT_EQ(0xffffffffu, DecodeValidUint32(1, "\xff\xfe\xff\xff\xff\x0f"));
+
+ // Invalid.
+ ExpectDecodeUint32Invalid(7, "\x7f\x80\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(7, "\xff\x80\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\xc0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(6, "\xff\xc0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\xe0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(5, "\xff\xe0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\xf0\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(4, "\xff\xf0\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\xf8\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(3, "\xff\xf8\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\xfc\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(2, "\xff\xfc\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\xfe\xff\xff\xff\x10");
+ ExpectDecodeUint32Invalid(1, "\xff\xfe\xff\xff\xff\xff");
+}
+
+// There are no valid uint32 encodings that are greater than six
+// bytes.
+TEST_F(HpackInputStreamTest, SevenByteIntegersOneToSevenBitPrefixes) {
+ ExpectDecodeUint32Invalid(7, "\x7f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(7, "\x7f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(7, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(6, "\x3f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(6, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(5, "\x1f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(5, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(4, "\x0f\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(4, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(3, "\x07\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(3, "\x07\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(3, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(2, "\x03\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(2, "\x03\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(2, "\xff\xff\xff\xff\xff\xff\xff");
+ ExpectDecodeUint32Invalid(1, "\x01\x80\x80\x80\x80\x80\x00");
+ ExpectDecodeUint32Invalid(1, "\x01\x80\x80\x80\x80\x80\x01");
+ ExpectDecodeUint32Invalid(1, "\xff\xff\xff\xff\xff\xff\xff");
+}
+
+// Decoding a valid encoded string literal should work.
+TEST_F(HpackInputStreamTest, DecodeNextIdentityString) {
+ HpackInputStream input_stream(kLiteralBound, "\x0estring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_TRUE(input_stream.DecodeNextIdentityString(&string_piece));
+ EXPECT_EQ("string literal", string_piece);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+// Decoding an encoded string literal with size larger than
+// |max_string_literal_size_| should fail.
+TEST_F(HpackInputStreamTest, DecodeNextIdentityStringSizeLimit) {
+ HpackInputStream input_stream(13, "\x0estring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_FALSE(input_stream.DecodeNextIdentityString(&string_piece));
+}
+
+// Decoding an encoded string literal with size larger than the
+// remainder of the buffer should fail.
+TEST_F(HpackInputStreamTest, DecodeNextIdentityStringNotEnoughInput) {
+ // Set the length to be one more than it should be.
+ HpackInputStream input_stream(kLiteralBound, "\x0fstring literal");
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ StringPiece string_piece;
+ EXPECT_FALSE(input_stream.DecodeNextIdentityString(&string_piece));
+}
+
+TEST_F(HpackInputStreamTest, DecodeNextHuffmanString) {
+ string output, input(a2b_hex(kEncodedHuffmanFixture));
+ HpackInputStream input_stream(arraysize(kDecodedHuffmanFixture)-1, input);
+
+ EXPECT_TRUE(input_stream.HasMoreData());
+ EXPECT_TRUE(input_stream.DecodeNextHuffmanString(huffman_table, &output));
+ EXPECT_EQ(kDecodedHuffmanFixture, output);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+TEST_F(HpackInputStreamTest, DecodeNextHuffmanStringSizeLimit) {
+ string output, input(a2b_hex(kEncodedHuffmanFixture));
+ // Max string literal is one byte shorter than the decoded fixture.
+ HpackInputStream input_stream(arraysize(kDecodedHuffmanFixture)-2, input);
+
+ // Decoded string overflows the max string literal.
+ EXPECT_TRUE(input_stream.HasMoreData());
+ EXPECT_FALSE(input_stream.DecodeNextHuffmanString(huffman_table, &output));
+}
+
+TEST_F(HpackInputStreamTest, DecodeNextHuffmanStringNotEnoughInput) {
+ string output, input(a2b_hex(kEncodedHuffmanFixture));
+ input[0]++; // Input prefix is one byte larger than available input.
+ HpackInputStream input_stream(arraysize(kDecodedHuffmanFixture)-1, input);
+
+ // Not enough buffer for declared encoded length.
+ EXPECT_TRUE(input_stream.HasMoreData());
+ EXPECT_FALSE(input_stream.DecodeNextHuffmanString(huffman_table, &output));
+}
+
+TEST_F(HpackInputStreamTest, PeekBitsAndConsume) {
+ HpackInputStream input_stream(kLiteralBound, "\xad\xab\xad\xab\xad");
+
+ uint32 bits = 0;
+ size_t peeked_count = 0;
+
+ // Read 0xad.
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10101101000000000000000000000000"), bits);
+ EXPECT_EQ(8u, peeked_count);
+
+ // Read 0xab.
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10101101101010110000000000000000"), bits);
+ EXPECT_EQ(16u, peeked_count);
+
+ input_stream.ConsumeBits(5);
+ bits = bits << 5;
+ peeked_count -= 5;
+ EXPECT_EQ(bits32("10110101011000000000000000000000"), bits);
+ EXPECT_EQ(11u, peeked_count);
+
+ // Read 0xad.
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10110101011101011010000000000000"), bits);
+ EXPECT_EQ(19u, peeked_count);
+
+ // Read 0xab.
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10110101011101011011010101100000"), bits);
+ EXPECT_EQ(27u, peeked_count);
+
+ // Read 0xa, and 1 bit of 0xd
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10110101011101011011010101110101"), bits);
+ EXPECT_EQ(32u, peeked_count);
+
+ // |bits| is full, and doesn't change.
+ EXPECT_FALSE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10110101011101011011010101110101"), bits);
+ EXPECT_EQ(32u, peeked_count);
+
+ input_stream.ConsumeBits(27);
+ bits = bits << 27;
+ peeked_count -= 27;
+ EXPECT_EQ(bits32("10101000000000000000000000000000"), bits);
+ EXPECT_EQ(5u, peeked_count);
+
+ // Read remaining 3 bits of 0xd.
+ EXPECT_TRUE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10101101000000000000000000000000"), bits);
+ EXPECT_EQ(8u, peeked_count);
+
+ // EOF.
+ EXPECT_FALSE(input_stream.PeekBits(&peeked_count, &bits));
+ EXPECT_EQ(bits32("10101101000000000000000000000000"), bits);
+ EXPECT_EQ(8u, peeked_count);
+
+ input_stream.ConsumeBits(8);
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+TEST_F(HpackInputStreamTest, ConsumeByteRemainder) {
+ HpackInputStream input_stream(kLiteralBound, "\xad\xab");
+ // Does nothing.
+ input_stream.ConsumeByteRemainder();
+
+ // Consumes one byte.
+ input_stream.ConsumeBits(3);
+ input_stream.ConsumeByteRemainder();
+ EXPECT_TRUE(input_stream.HasMoreData());
+
+ input_stream.ConsumeBits(6);
+ EXPECT_TRUE(input_stream.HasMoreData());
+ input_stream.ConsumeByteRemainder();
+ EXPECT_FALSE(input_stream.HasMoreData());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_output_stream.cc b/chromium/net/spdy/hpack_output_stream.cc
new file mode 100644
index 00000000000..20f901d3b39
--- /dev/null
+++ b/chromium/net/spdy/hpack_output_stream.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 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/spdy/hpack_output_stream.h"
+
+#include "base/logging.h"
+
+
+namespace net {
+
+using base::StringPiece;
+using std::string;
+
+HpackOutputStream::HpackOutputStream()
+ : bit_offset_(0) {}
+
+HpackOutputStream::~HpackOutputStream() {}
+
+void HpackOutputStream::AppendBits(uint8 bits, size_t bit_size) {
+ DCHECK_GT(bit_size, 0u);
+ DCHECK_LE(bit_size, 8u);
+ DCHECK_EQ(bits >> bit_size, 0);
+ size_t new_bit_offset = bit_offset_ + bit_size;
+ if (bit_offset_ == 0) {
+ // Buffer ends on a byte boundary.
+ DCHECK_LE(bit_size, 8u);
+ buffer_.append(1, bits << (8 - bit_size));
+ } else if (new_bit_offset <= 8) {
+ // Buffer does not end on a byte boundary but the given bits fit
+ // in the remainder of the last byte.
+ *buffer_.rbegin() |= bits << (8 - new_bit_offset);
+ } else {
+ // Buffer does not end on a byte boundary and the given bits do
+ // not fit in the remainder of the last byte.
+ *buffer_.rbegin() |= bits >> (new_bit_offset - 8);
+ buffer_.append(1, bits << (16 - new_bit_offset));
+ }
+ bit_offset_ = new_bit_offset % 8;
+}
+
+void HpackOutputStream::AppendPrefix(HpackPrefix prefix) {
+ AppendBits(prefix.bits, prefix.bit_size);
+}
+
+void HpackOutputStream::AppendBytes(StringPiece buffer) {
+ DCHECK_EQ(bit_offset_, 0u);
+ buffer_.append(buffer.data(), buffer.size());
+}
+
+void HpackOutputStream::AppendUint32(uint32 I) {
+ // The algorithm below is adapted from the pseudocode in 4.1.1.
+ size_t N = 8 - bit_offset_;
+ uint8 max_first_byte = static_cast<uint8>((1 << N) - 1);
+ if (I < max_first_byte) {
+ AppendBits(static_cast<uint8>(I), N);
+ } else {
+ AppendBits(max_first_byte, N);
+ I -= max_first_byte;
+ while ((I & ~0x7f) != 0) {
+ buffer_.append(1, (I & 0x7f) | 0x80);
+ I >>= 7;
+ }
+ AppendBits(static_cast<uint8>(I), 8);
+ }
+}
+
+void HpackOutputStream::TakeString(string* output) {
+ // This must hold, since all public functions cause the buffer to
+ // end on a byte boundary.
+ DCHECK_EQ(bit_offset_, 0u);
+ buffer_.swap(*output);
+ buffer_.clear();
+ bit_offset_ = 0;
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_output_stream.h b/chromium/net/spdy/hpack_output_stream.h
new file mode 100644
index 00000000000..fd68fd44ead
--- /dev/null
+++ b/chromium/net/spdy/hpack_output_stream.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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_SPDY_HPACK_OUTPUT_STREAM_H_
+#define NET_SPDY_HPACK_OUTPUT_STREAM_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/hpack_constants.h"
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+namespace net {
+
+// An HpackOutputStream handles all the low-level details of encoding
+// header fields.
+class NET_EXPORT_PRIVATE HpackOutputStream {
+ public:
+ explicit HpackOutputStream();
+ ~HpackOutputStream();
+
+ // Appends the lower |bit_size| bits of |bits| to the internal buffer.
+ //
+ // |bit_size| must be > 0 and <= 8. |bits| must not have any bits
+ // set other than the lower |bit_size| bits.
+ void AppendBits(uint8 bits, size_t bit_size);
+
+ // Simply forwards to AppendBits(prefix.bits, prefix.bit-size).
+ void AppendPrefix(HpackPrefix prefix);
+
+ // Directly appends |buffer|.
+ void AppendBytes(base::StringPiece buffer);
+
+ // Appends the given integer using the representation described in
+ // 4.1.1. If the internal buffer ends on a byte boundary, the prefix
+ // length N is taken to be 8; otherwise, it is taken to be the
+ // number of bits to the next byte boundary.
+ //
+ // It is guaranteed that the internal buffer will end on a byte
+ // boundary after this function is called.
+ void AppendUint32(uint32 I);
+
+ // Swaps the interal buffer with |output|.
+ void TakeString(std::string* output);
+
+ private:
+ // The internal bit buffer.
+ std::string buffer_;
+
+ // If 0, the buffer ends on a byte boundary. If non-zero, the buffer
+ // ends on the most significant nth bit. Guaranteed to be < 8.
+ size_t bit_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(HpackOutputStream);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_OUTPUT_STREAM_H_
diff --git a/chromium/net/spdy/hpack_output_stream_test.cc b/chromium/net/spdy/hpack_output_stream_test.cc
new file mode 100644
index 00000000000..f8e078065ff
--- /dev/null
+++ b/chromium/net/spdy/hpack_output_stream_test.cc
@@ -0,0 +1,260 @@
+// Copyright 2014 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/spdy/hpack_output_stream.h"
+
+#include <cstddef>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using std::string;
+
+// Make sure that AppendBits() appends bits starting from the most
+// significant bit, and that it can handle crossing a byte boundary.
+TEST(HpackOutputStreamTest, AppendBits) {
+ HpackOutputStream output_stream;
+ string expected_str;
+
+ output_stream.AppendBits(0x1, 1);
+ expected_str.append(1, 0x00);
+ *expected_str.rbegin() |= (0x1 << 7);
+
+ output_stream.AppendBits(0x0, 1);
+
+ output_stream.AppendBits(0x3, 2);
+ *expected_str.rbegin() |= (0x3 << 4);
+
+ output_stream.AppendBits(0x0, 2);
+
+ // Byte-crossing append.
+ output_stream.AppendBits(0x7, 3);
+ *expected_str.rbegin() |= (0x7 >> 1);
+ expected_str.append(1, 0x00);
+ *expected_str.rbegin() |= (0x7 << 7);
+
+ output_stream.AppendBits(0x0, 7);
+
+ string str;
+ output_stream.TakeString(&str);
+ EXPECT_EQ(expected_str, str);
+}
+
+// Utility function to return I as a string encoded with an N-bit
+// prefix.
+string EncodeUint32(uint8 N, uint32 I) {
+ HpackOutputStream output_stream;
+ if (N < 8) {
+ output_stream.AppendBits(0x00, 8 - N);
+ }
+ output_stream.AppendUint32(I);
+ string str;
+ output_stream.TakeString(&str);
+ return str;
+}
+
+// The {Number}ByteIntegersEightBitPrefix tests below test that
+// certain integers are encoded correctly with an 8-bit prefix in
+// exactly {Number} bytes.
+
+TEST(HpackOutputStreamTest, OneByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(8, 0x00));
+ EXPECT_EQ("\x7f", EncodeUint32(8, 0x7f));
+ // Maximum.
+ EXPECT_EQ("\xfe", EncodeUint32(8, 0xfe));
+}
+
+TEST(HpackOutputStreamTest, TwoByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ(string("\xff\x00", 2), EncodeUint32(8, 0xff));
+ EXPECT_EQ("\xff\x01", EncodeUint32(8, 0x0100));
+ // Maximum.
+ EXPECT_EQ("\xff\x7f", EncodeUint32(8, 0x017e));
+}
+
+TEST(HpackOutputStreamTest, ThreeByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ("\xff\x80\x01", EncodeUint32(8, 0x017f));
+ EXPECT_EQ("\xff\x80\x1e", EncodeUint32(8, 0x0fff));
+ // Maximum.
+ EXPECT_EQ("\xff\xff\x7f", EncodeUint32(8, 0x40fe));
+}
+
+TEST(HpackOutputStreamTest, FourByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ("\xff\x80\x80\x01", EncodeUint32(8, 0x40ff));
+ EXPECT_EQ("\xff\x80\xfe\x03", EncodeUint32(8, 0xffff));
+ // Maximum.
+ EXPECT_EQ("\xff\xff\xff\x7f", EncodeUint32(8, 0x002000fe));
+}
+
+TEST(HpackOutputStreamTest, FiveByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ("\xff\x80\x80\x80\x01", EncodeUint32(8, 0x002000ff));
+ EXPECT_EQ("\xff\x80\xfe\xff\x07", EncodeUint32(8, 0x00ffffff));
+ // Maximum.
+ EXPECT_EQ("\xff\xff\xff\xff\x7f", EncodeUint32(8, 0x100000fe));
+}
+
+TEST(HpackOutputStreamTest, SixByteIntegersEightBitPrefix) {
+ // Minimum.
+ EXPECT_EQ("\xff\x80\x80\x80\x80\x01", EncodeUint32(8, 0x100000ff));
+ // Maximum.
+ EXPECT_EQ("\xff\x80\xfe\xff\xff\x0f", EncodeUint32(8, 0xffffffff));
+}
+
+// The {Number}ByteIntegersOneToSevenBitPrefix tests below test that
+// certain integers are encoded correctly with an N-bit prefix in
+// exactly {Number} bytes for N in {1, 2, ..., 7}.
+
+TEST(HpackOutputStreamTest, OneByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(7, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(6, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(5, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(4, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(3, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(2, 0x00));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(1, 0x00));
+
+ // Maximums.
+ EXPECT_EQ("\x7e", EncodeUint32(7, 0x7e));
+ EXPECT_EQ("\x3e", EncodeUint32(6, 0x3e));
+ EXPECT_EQ("\x1e", EncodeUint32(5, 0x1e));
+ EXPECT_EQ("\x0e", EncodeUint32(4, 0x0e));
+ EXPECT_EQ("\x06", EncodeUint32(3, 0x06));
+ EXPECT_EQ("\x02", EncodeUint32(2, 0x02));
+ EXPECT_EQ(string("\x00", 1), EncodeUint32(1, 0x00));
+}
+
+TEST(HpackOutputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ(string("\x7f\x00", 2), EncodeUint32(7, 0x7f));
+ EXPECT_EQ(string("\x3f\x00", 2), EncodeUint32(6, 0x3f));
+ EXPECT_EQ(string("\x1f\x00", 2), EncodeUint32(5, 0x1f));
+ EXPECT_EQ(string("\x0f\x00", 2), EncodeUint32(4, 0x0f));
+ EXPECT_EQ(string("\x07\x00", 2), EncodeUint32(3, 0x07));
+ EXPECT_EQ(string("\x03\x00", 2), EncodeUint32(2, 0x03));
+ EXPECT_EQ(string("\x01\x00", 2), EncodeUint32(1, 0x01));
+
+ // Maximums.
+ EXPECT_EQ("\x7f\x7f", EncodeUint32(7, 0xfe));
+ EXPECT_EQ("\x3f\x7f", EncodeUint32(6, 0xbe));
+ EXPECT_EQ("\x1f\x7f", EncodeUint32(5, 0x9e));
+ EXPECT_EQ("\x0f\x7f", EncodeUint32(4, 0x8e));
+ EXPECT_EQ("\x07\x7f", EncodeUint32(3, 0x86));
+ EXPECT_EQ("\x03\x7f", EncodeUint32(2, 0x82));
+ EXPECT_EQ("\x01\x7f", EncodeUint32(1, 0x80));
+}
+
+TEST(HpackOutputStreamTest, ThreeByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ("\x7f\x80\x01", EncodeUint32(7, 0xff));
+ EXPECT_EQ("\x3f\x80\x01", EncodeUint32(6, 0xbf));
+ EXPECT_EQ("\x1f\x80\x01", EncodeUint32(5, 0x9f));
+ EXPECT_EQ("\x0f\x80\x01", EncodeUint32(4, 0x8f));
+ EXPECT_EQ("\x07\x80\x01", EncodeUint32(3, 0x87));
+ EXPECT_EQ("\x03\x80\x01", EncodeUint32(2, 0x83));
+ EXPECT_EQ("\x01\x80\x01", EncodeUint32(1, 0x81));
+
+ // Maximums.
+ EXPECT_EQ("\x7f\xff\x7f", EncodeUint32(7, 0x407e));
+ EXPECT_EQ("\x3f\xff\x7f", EncodeUint32(6, 0x403e));
+ EXPECT_EQ("\x1f\xff\x7f", EncodeUint32(5, 0x401e));
+ EXPECT_EQ("\x0f\xff\x7f", EncodeUint32(4, 0x400e));
+ EXPECT_EQ("\x07\xff\x7f", EncodeUint32(3, 0x4006));
+ EXPECT_EQ("\x03\xff\x7f", EncodeUint32(2, 0x4002));
+ EXPECT_EQ("\x01\xff\x7f", EncodeUint32(1, 0x4000));
+}
+
+TEST(HpackOutputStreamTest, FourByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ("\x7f\x80\x80\x01", EncodeUint32(7, 0x407f));
+ EXPECT_EQ("\x3f\x80\x80\x01", EncodeUint32(6, 0x403f));
+ EXPECT_EQ("\x1f\x80\x80\x01", EncodeUint32(5, 0x401f));
+ EXPECT_EQ("\x0f\x80\x80\x01", EncodeUint32(4, 0x400f));
+ EXPECT_EQ("\x07\x80\x80\x01", EncodeUint32(3, 0x4007));
+ EXPECT_EQ("\x03\x80\x80\x01", EncodeUint32(2, 0x4003));
+ EXPECT_EQ("\x01\x80\x80\x01", EncodeUint32(1, 0x4001));
+
+ // Maximums.
+ EXPECT_EQ("\x7f\xff\xff\x7f", EncodeUint32(7, 0x20007e));
+ EXPECT_EQ("\x3f\xff\xff\x7f", EncodeUint32(6, 0x20003e));
+ EXPECT_EQ("\x1f\xff\xff\x7f", EncodeUint32(5, 0x20001e));
+ EXPECT_EQ("\x0f\xff\xff\x7f", EncodeUint32(4, 0x20000e));
+ EXPECT_EQ("\x07\xff\xff\x7f", EncodeUint32(3, 0x200006));
+ EXPECT_EQ("\x03\xff\xff\x7f", EncodeUint32(2, 0x200002));
+ EXPECT_EQ("\x01\xff\xff\x7f", EncodeUint32(1, 0x200000));
+}
+
+TEST(HpackOutputStreamTest, FiveByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ("\x7f\x80\x80\x80\x01", EncodeUint32(7, 0x20007f));
+ EXPECT_EQ("\x3f\x80\x80\x80\x01", EncodeUint32(6, 0x20003f));
+ EXPECT_EQ("\x1f\x80\x80\x80\x01", EncodeUint32(5, 0x20001f));
+ EXPECT_EQ("\x0f\x80\x80\x80\x01", EncodeUint32(4, 0x20000f));
+ EXPECT_EQ("\x07\x80\x80\x80\x01", EncodeUint32(3, 0x200007));
+ EXPECT_EQ("\x03\x80\x80\x80\x01", EncodeUint32(2, 0x200003));
+ EXPECT_EQ("\x01\x80\x80\x80\x01", EncodeUint32(1, 0x200001));
+
+ // Maximums.
+ EXPECT_EQ("\x7f\xff\xff\xff\x7f", EncodeUint32(7, 0x1000007e));
+ EXPECT_EQ("\x3f\xff\xff\xff\x7f", EncodeUint32(6, 0x1000003e));
+ EXPECT_EQ("\x1f\xff\xff\xff\x7f", EncodeUint32(5, 0x1000001e));
+ EXPECT_EQ("\x0f\xff\xff\xff\x7f", EncodeUint32(4, 0x1000000e));
+ EXPECT_EQ("\x07\xff\xff\xff\x7f", EncodeUint32(3, 0x10000006));
+ EXPECT_EQ("\x03\xff\xff\xff\x7f", EncodeUint32(2, 0x10000002));
+ EXPECT_EQ("\x01\xff\xff\xff\x7f", EncodeUint32(1, 0x10000000));
+}
+
+TEST(HpackOutputStreamTest, SixByteIntegersOneToSevenBitPrefixes) {
+ // Minimums.
+ EXPECT_EQ("\x7f\x80\x80\x80\x80\x01", EncodeUint32(7, 0x1000007f));
+ EXPECT_EQ("\x3f\x80\x80\x80\x80\x01", EncodeUint32(6, 0x1000003f));
+ EXPECT_EQ("\x1f\x80\x80\x80\x80\x01", EncodeUint32(5, 0x1000001f));
+ EXPECT_EQ("\x0f\x80\x80\x80\x80\x01", EncodeUint32(4, 0x1000000f));
+ EXPECT_EQ("\x07\x80\x80\x80\x80\x01", EncodeUint32(3, 0x10000007));
+ EXPECT_EQ("\x03\x80\x80\x80\x80\x01", EncodeUint32(2, 0x10000003));
+ EXPECT_EQ("\x01\x80\x80\x80\x80\x01", EncodeUint32(1, 0x10000001));
+
+ // Maximums.
+ EXPECT_EQ("\x7f\x80\xff\xff\xff\x0f", EncodeUint32(7, 0xffffffff));
+ EXPECT_EQ("\x3f\xc0\xff\xff\xff\x0f", EncodeUint32(6, 0xffffffff));
+ EXPECT_EQ("\x1f\xe0\xff\xff\xff\x0f", EncodeUint32(5, 0xffffffff));
+ EXPECT_EQ("\x0f\xf0\xff\xff\xff\x0f", EncodeUint32(4, 0xffffffff));
+ EXPECT_EQ("\x07\xf8\xff\xff\xff\x0f", EncodeUint32(3, 0xffffffff));
+ EXPECT_EQ("\x03\xfc\xff\xff\xff\x0f", EncodeUint32(2, 0xffffffff));
+ EXPECT_EQ("\x01\xfe\xff\xff\xff\x0f", EncodeUint32(1, 0xffffffff));
+}
+
+// Test that encoding an integer with an N-bit prefix preserves the
+// upper (8-N) bits of the first byte.
+TEST(HpackOutputStreamTest, AppendUint32PreservesUpperBits) {
+ HpackOutputStream output_stream;
+ output_stream.AppendBits(0x7f, 7);
+ output_stream.AppendUint32(0x01);
+ string str;
+ output_stream.TakeString(&str);
+ EXPECT_EQ(string("\xff\x00", 2), str);
+}
+
+TEST(HpackOutputStreamTest, AppendBytes) {
+ HpackOutputStream output_stream;
+
+ output_stream.AppendBytes("buffer1");
+ output_stream.AppendBytes("buffer2");
+
+ string str;
+ output_stream.TakeString(&str);
+ EXPECT_EQ("buffer1buffer2", str);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_round_trip_test.cc b/chromium/net/spdy/hpack_round_trip_test.cc
new file mode 100644
index 00000000000..39be93982b6
--- /dev/null
+++ b/chromium/net/spdy/hpack_round_trip_test.cc
@@ -0,0 +1,174 @@
+// Copyright 2014 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 <cmath>
+#include <ctime>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "net/spdy/hpack_constants.h"
+#include "net/spdy/hpack_decoder.h"
+#include "net/spdy/hpack_encoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace {
+
+class HpackRoundTripTest : public ::testing::Test {
+ protected:
+ HpackRoundTripTest()
+ : encoder_(ObtainHpackHuffmanTable()),
+ decoder_(ObtainHpackHuffmanTable()) {}
+
+ virtual void SetUp() {
+ // Use a small table size to tickle eviction handling.
+ encoder_.ApplyHeaderTableSizeSetting(256);
+ decoder_.ApplyHeaderTableSizeSetting(256);
+ }
+
+ bool RoundTrip(const map<string, string>& header_set) {
+ string encoded;
+ encoder_.EncodeHeaderSet(header_set, &encoded);
+
+ bool success = decoder_.HandleControlFrameHeadersData(
+ 1, encoded.data(), encoded.size());
+ success &= decoder_.HandleControlFrameHeadersComplete(1);
+
+ EXPECT_EQ(header_set, decoder_.decoded_block());
+ return success;
+ }
+
+ size_t SampleExponential(size_t mean, size_t sanity_bound) {
+ return std::min<size_t>(-std::log(base::RandDouble()) * mean,
+ sanity_bound);
+ }
+
+ HpackEncoder encoder_;
+ HpackDecoder decoder_;
+};
+
+TEST_F(HpackRoundTripTest, ResponseFixtures) {
+ {
+ map<string, string> headers;
+ headers[":status"] = "302";
+ headers["cache-control"] = "private";
+ headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT";
+ headers["location"] = "https://www.example.com";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+ {
+ map<string, string> headers;
+ headers[":status"] = "200";
+ headers["cache-control"] = "private";
+ headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT";
+ headers["location"] = "https://www.example.com";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+ {
+ map<string, string> headers;
+ headers[":status"] = "200";
+ headers["cache-control"] = "private";
+ headers["content-encoding"] = "gzip";
+ headers["date"] = "Mon, 21 Oct 2013 20:13:22 GMT";
+ headers["location"] = "https://www.example.com";
+ headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
+ " max-age=3600; version=1";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+}
+
+TEST_F(HpackRoundTripTest, RequestFixtures) {
+ {
+ map<string, string> headers;
+ headers[":authority"] = "www.example.com";
+ headers[":method"] = "GET";
+ headers[":path"] = "/";
+ headers[":scheme"] = "http";
+ headers["cookie"] = "baz=bing; foo=bar";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+ {
+ map<string, string> headers;
+ headers[":authority"] = "www.example.com";
+ headers[":method"] = "GET";
+ headers[":path"] = "/";
+ headers[":scheme"] = "http";
+ headers["cache-control"] = "no-cache";
+ headers["cookie"] = "fizzle=fazzle; foo=bar";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+ {
+ map<string, string> headers;
+ headers[":authority"] = "www.example.com";
+ headers[":method"] = "GET";
+ headers[":path"] = "/index.html";
+ headers[":scheme"] = "https";
+ headers["custom-key"] = "custom-value";
+ headers["cookie"] = "baz=bing; fizzle=fazzle; garbage";
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+}
+
+TEST_F(HpackRoundTripTest, RandomizedExamples) {
+ // Grow vectors of names & values, which are seeded with fixtures and then
+ // expanded with dynamically generated data. Samples are taken using the
+ // exponential distribution.
+ vector<string> names;
+ names.push_back(":authority");
+ names.push_back(":path");
+ names.push_back(":status");
+ // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be
+ // reconstructed in any order, which breaks the simple validation used here.
+
+ vector<string> values;
+ values.push_back("/");
+ values.push_back("/index.html");
+ values.push_back("200");
+ values.push_back("404");
+ values.push_back("");
+ values.push_back("baz=bing; foo=bar; garbage");
+ values.push_back("baz=bing; fizzle=fazzle; garbage");
+
+ int seed = std::time(NULL);
+ LOG(INFO) << "Seeding with srand(" << seed << ")";
+ srand(seed);
+
+ for (size_t i = 0; i != 2000; ++i) {
+ map<string, string> headers;
+
+ size_t header_count = 1 + SampleExponential(7, 50);
+ for (size_t j = 0; j != header_count; ++j) {
+ size_t name_index = SampleExponential(20, 200);
+ size_t value_index = SampleExponential(20, 200);
+
+ string name, value;
+ if (name_index >= names.size()) {
+ names.push_back(base::RandBytesAsString(1 + SampleExponential(5, 30)));
+ name = names.back();
+ } else {
+ name = names[name_index];
+ }
+ if (value_index >= values.size()) {
+ values.push_back(base::RandBytesAsString(
+ 1 + SampleExponential(15, 75)));
+ value = values.back();
+ } else {
+ value = values[value_index];
+ }
+ headers[name] = value;
+ }
+ EXPECT_TRUE(RoundTrip(headers));
+ }
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_string_util.cc b/chromium/net/spdy/hpack_string_util.cc
new file mode 100644
index 00000000000..dddb85513bc
--- /dev/null
+++ b/chromium/net/spdy/hpack_string_util.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 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/spdy/hpack_string_util.h"
+
+#include "base/basictypes.h"
+
+namespace net {
+
+bool StringPiecesEqualConstantTime(base::StringPiece str1,
+ base::StringPiece str2) {
+ size_t size = str1.size();
+ if (str2.size() != size)
+ return false;
+
+ uint8 x = 0;
+ for (size_t i = 0; i < size; ++i) {
+ x |= str1[i] ^ str2[i];
+ }
+ return x == 0;
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/hpack_string_util.h b/chromium/net/spdy/hpack_string_util.h
new file mode 100644
index 00000000000..9135f9047f9
--- /dev/null
+++ b/chromium/net/spdy/hpack_string_util.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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_SPDY_HPACK_STRING_UTIL_H_
+#define NET_SPDY_HPACK_STRING_UTIL_H_
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// All section references below are to
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07
+
+// A constant-time StringPiece comparison function, as suggested
+// by section 6.
+bool NET_EXPORT_PRIVATE StringPiecesEqualConstantTime(
+ base::StringPiece str1,
+ base::StringPiece str2);
+
+} // namespace net
+
+#endif // NET_SPDY_HPACK_STRING_UTIL_H_
diff --git a/chromium/net/spdy/hpack_string_util_test.cc b/chromium/net/spdy/hpack_string_util_test.cc
new file mode 100644
index 00000000000..e73f5ce8e29
--- /dev/null
+++ b/chromium/net/spdy/hpack_string_util_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 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/spdy/hpack_string_util.h"
+
+#include <cstddef>
+#include <cstring>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using std::string;
+
+// Make sure StringPiecesEqualConstantTime() behaves like the regular
+// string equality operator.
+TEST(HpackStringUtilTest, StringPiecesEqualConstantTime) {
+ EXPECT_TRUE(StringPiecesEqualConstantTime("foo", "foo"));
+ EXPECT_FALSE(StringPiecesEqualConstantTime("foo", "foox"));
+ EXPECT_FALSE(StringPiecesEqualConstantTime("foo", "bar"));
+}
+
+// TODO(jgraettinger): Support this benchmark.
+/*
+enum BM_StringPieceEqualityType {
+ STRCMP_EQUAL,
+ STRCMP_FIRST_CHAR_DIFFERS,
+ STRING_PIECES_EQUAL_CONSTANT_TIME_EQUAL,
+ STRING_PIECES_EQUAL_CONSTANT_TIME_FIRST_CHAR_DIFFERS,
+};
+
+void BM_StringPieceEquality(int iters, int size, int type_int) {
+ BM_StringPieceEqualityType type =
+ static_cast<BM_StringPieceEqualityType>(type_int);
+ string str_a(size, 'x');
+ string str_b(size, 'x');
+ int result = 0;
+ switch (type) {
+ case STRCMP_EQUAL:
+ for (int i = 0; i < iters; ++i) {
+ result |= std::strcmp(str_a.c_str(), str_b.c_str());
+ }
+ CHECK_EQ(result, 0);
+ return;
+
+ case STRCMP_FIRST_CHAR_DIFFERS:
+ str_b[0] = 'y';
+ for (int i = 0; i < iters; ++i) {
+ result |= std::strcmp(str_a.c_str(), str_b.c_str());
+ }
+ CHECK_LT(result, 0);
+ return;
+
+ case STRING_PIECES_EQUAL_CONSTANT_TIME_EQUAL:
+ for (int i = 0; i < iters; ++i) {
+ result |= StringPiecesEqualConstantTime(str_a, str_b);
+ }
+ CHECK_EQ(result, 1);
+ return;
+
+ case STRING_PIECES_EQUAL_CONSTANT_TIME_FIRST_CHAR_DIFFERS:
+ str_b[0] = 'y';
+ for (int i = 0; i < iters; ++i) {
+ result |= StringPiecesEqualConstantTime(str_a, str_b);
+ }
+ CHECK_EQ(result, 0);
+ return;
+ }
+
+ DCHECK(false);
+}
+
+// Results should resemble the table below, where 0 and 1 are clearly
+// different (STRCMP), but 2 and 3 are roughly the same
+// (STRING_PIECES_EQUAL_CONSTANT_TIME).
+//
+// DEBUG: Benchmark Time(ns) CPU(ns) Iterations
+// -------------------------------------------------------------------
+// DEBUG: BM_StringPieceEquality/1M/0 77796 77141 7778
+// DEBUG: BM_StringPieceEquality/1M/1 10 10 70000000
+// DEBUG: BM_StringPieceEquality/1M/2 7729735 7700000 100
+// DEBUG: BM_StringPieceEquality/1M/3 7803051 7800000 100
+BENCHMARK(BM_StringPieceEquality)
+ ->ArgPair(1<<20, STRCMP_EQUAL)
+ ->ArgPair(1<<20, STRCMP_FIRST_CHAR_DIFFERS)
+ ->ArgPair(1<<20, STRING_PIECES_EQUAL_CONSTANT_TIME_EQUAL)
+ ->ArgPair(1<<20, STRING_PIECES_EQUAL_CONSTANT_TIME_FIRST_CHAR_DIFFERS);
+*/
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/spdy/mock_spdy_framer_visitor.cc b/chromium/net/spdy/mock_spdy_framer_visitor.cc
new file mode 100644
index 00000000000..c4a210fb0a4
--- /dev/null
+++ b/chromium/net/spdy/mock_spdy_framer_visitor.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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/spdy/mock_spdy_framer_visitor.h"
+
+namespace net {
+
+namespace test {
+
+MockSpdyFramerVisitor::MockSpdyFramerVisitor() {}
+
+MockSpdyFramerVisitor::~MockSpdyFramerVisitor() {}
+
+} // namespace test
+
+} // namespace net
diff --git a/chromium/net/spdy/mock_spdy_framer_visitor.h b/chromium/net/spdy/mock_spdy_framer_visitor.h
new file mode 100644
index 00000000000..c351b3ba7d9
--- /dev/null
+++ b/chromium/net/spdy/mock_spdy_framer_visitor.h
@@ -0,0 +1,65 @@
+// Copyright 2014 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_SPDY_MOCK_SPDY_FRAMER_VISITOR_H_
+#define NET_SPDY_MOCK_SPDY_FRAMER_VISITOR_H_
+
+#include "base/strings/string_piece.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+
+namespace test {
+
+class MockSpdyFramerVisitor : public SpdyFramerVisitorInterface {
+ public:
+ MockSpdyFramerVisitor();
+ virtual ~MockSpdyFramerVisitor();
+ MOCK_METHOD1(OnError, void(SpdyFramer* framer));
+ MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
+ size_t length,
+ bool fin));
+ MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin));
+ MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len));
+ MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional));
+ MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
+ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status));
+ MOCK_METHOD1(OnSettings, void(bool clear_persisted));
+ MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+ MOCK_METHOD0(OnSettingsEnd, void());
+ MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status));
+ MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end));
+ MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
+ uint32 delta_window_size));
+ MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end));
+ MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
+ MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ base::StringPiece protocol_id,
+ base::StringPiece host,
+ base::StringPiece origin));
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_SPDY_MOCK_SPDY_FRAMER_VISITOR_H_
diff --git a/chromium/net/spdy/spdy_buffer.cc b/chromium/net/spdy/spdy_buffer.cc
index b93ae0a6428..0a69cd3e3e5 100644
--- a/chromium/net/spdy/spdy_buffer.cc
+++ b/chromium/net/spdy/spdy_buffer.cc
@@ -15,11 +15,15 @@ namespace net {
namespace {
+// Bound on largest frame any SPDY version has allowed.
+const size_t kMaxSpdyFrameSize = 0x00ffffff;
+
// Makes a SpdyFrame with |size| bytes of data copied from
// |data|. |data| must be non-NULL and |size| must be positive.
scoped_ptr<SpdyFrame> MakeSpdyFrame(const char* data, size_t size) {
DCHECK(data);
- DCHECK_GT(size, 0u);
+ CHECK_GT(size, 0u);
+ CHECK_LE(size, kMaxSpdyFrameSize);
scoped_ptr<char[]> frame_data(new char[size]);
std::memcpy(frame_data.get(), data, size);
scoped_ptr<SpdyFrame> frame(
@@ -63,6 +67,8 @@ SpdyBuffer::SpdyBuffer(scoped_ptr<SpdyFrame> frame)
SpdyBuffer::SpdyBuffer(const char* data, size_t size) :
shared_frame_(new SharedFrame()),
offset_(0) {
+ CHECK_GT(size, 0u);
+ CHECK_LE(size, kMaxSpdyFrameSize);
shared_frame_->data = MakeSpdyFrame(data, size);
}
diff --git a/chromium/net/spdy/spdy_frame_builder.cc b/chromium/net/spdy/spdy_frame_builder.cc
index 9e779ff4594..a7eb91d71ce 100644
--- a/chromium/net/spdy/spdy_frame_builder.cc
+++ b/chromium/net/spdy/spdy_frame_builder.cc
@@ -32,10 +32,12 @@ FlagsAndLength CreateFlagsAndLength(uint8 flags, size_t length) {
} // namespace
-SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
+ SpdyFrameBuilder::SpdyFrameBuilder(size_t size, SpdyMajorVersion version)
: buffer_(new char[size]),
capacity_(size),
- length_(0) {
+ length_(0),
+ offset_(0),
+ version_(version) {
}
SpdyFrameBuilder::~SpdyFrameBuilder() {
@@ -45,7 +47,7 @@ char* SpdyFrameBuilder::GetWritableBuffer(size_t length) {
if (!CanWrite(length)) {
return NULL;
}
- return buffer_.get() + length_;
+ return buffer_.get() + offset_ + length_;
}
bool SpdyFrameBuilder::Seek(size_t length) {
@@ -60,14 +62,16 @@ bool SpdyFrameBuilder::Seek(size_t length) {
bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer,
SpdyFrameType type,
uint8 flags) {
- DCHECK_GE(type, FIRST_CONTROL_TYPE);
- DCHECK_LE(type, LAST_CONTROL_TYPE);
- DCHECK_GT(4, framer.protocol_version());
+ DCHECK_GE(SPDY3, version_);
+ DCHECK_NE(-1,
+ SpdyConstants::SerializeFrameType(version_, type));
bool success = true;
FlagsAndLength flags_length = CreateFlagsAndLength(
flags, capacity_ - framer.GetControlFrameHeaderSize());
- success &= WriteUInt16(kControlFlagMask | framer.protocol_version());
- success &= WriteUInt16(type);
+ success &= WriteUInt16(kControlFlagMask |
+ SpdyConstants::SerializeMajorVersion(version_));
+ success &= WriteUInt16(
+ SpdyConstants::SerializeFrameType(framer.protocol_version(), type));
success &= WriteBytes(&flags_length, sizeof(flags_length));
DCHECK_EQ(framer.GetControlFrameHeaderSize(), length());
return success;
@@ -75,9 +79,9 @@ bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer,
bool SpdyFrameBuilder::WriteDataFrameHeader(const SpdyFramer& framer,
SpdyStreamId stream_id,
- SpdyDataFlags flags) {
- if (framer.protocol_version() >= 4) {
- return WriteFramePrefix(framer, DATA, flags, stream_id);
+ uint8 flags) {
+ if (version_ > SPDY3) {
+ return BeginNewFrame(framer, DATA, flags, stream_id);
}
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
bool success = true;
@@ -93,21 +97,36 @@ bool SpdyFrameBuilder::WriteDataFrameHeader(const SpdyFramer& framer,
return success;
}
-bool SpdyFrameBuilder::WriteFramePrefix(const SpdyFramer& framer,
- SpdyFrameType type,
- uint8 flags,
- SpdyStreamId stream_id) {
- DCHECK_LE(DATA, type);
- DCHECK_GE(LAST_CONTROL_TYPE, type);
+bool SpdyFrameBuilder::BeginNewFrame(const SpdyFramer& framer,
+ SpdyFrameType type,
+ uint8 flags,
+ SpdyStreamId stream_id) {
+ DCHECK(SpdyConstants::IsValidFrameType(version_,
+ SpdyConstants::SerializeFrameType(version_, type)));
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
- DCHECK_LE(4, framer.protocol_version());
+ DCHECK_LT(SPDY3, framer.protocol_version());
bool success = true;
- DCHECK_GT(1u<<16, capacity_); // Make sure length fits in 2B.
- success &= WriteUInt16(capacity_);
- success &= WriteUInt8(type);
+ if (length_ > 0) {
+ // Update length field for previous frame.
+ OverwriteLength(framer, length_ - framer.GetPrefixLength(type));
+ DLOG_IF(DFATAL, SpdyConstants::GetFrameMaximumSize(version_) < length_)
+ << "Frame length " << length_
+ << " is longer than the maximum allowed length.";
+ }
+
+ offset_ += length_;
+ length_ = 0;
+
+ // Assume all remaining capacity will be used for this frame. If not,
+ // the length will get overwritten when we begin the next frame.
+ // Don't check for length limits here because this may be larger than the
+ // actual frame length.
+ success &= WriteUInt16(capacity_ - offset_ - framer.GetPrefixLength(type));
+ success &= WriteUInt8(
+ SpdyConstants::SerializeFrameType(version_, type));
success &= WriteUInt8(flags);
success &= WriteUInt32(stream_id);
- DCHECK_EQ(framer.GetDataFrameMinimumSize(), length());
+ DCHECK_EQ(framer.GetDataFrameMinimumSize(), length_);
return success;
}
@@ -143,20 +162,23 @@ bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) {
}
bool SpdyFrameBuilder::RewriteLength(const SpdyFramer& framer) {
- if (framer.protocol_version() < 4) {
- return OverwriteLength(framer,
- length_ - framer.GetControlFrameHeaderSize());
- } else {
- return OverwriteLength(framer, length_);
- }
+ return OverwriteLength(framer,
+ length_ - framer.GetControlFrameHeaderSize());
}
bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer,
size_t length) {
+ if (version_ <= SPDY3) {
+ DCHECK_GE(SpdyConstants::GetFrameMaximumSize(version_) -
+ framer.GetFrameMinimumSize(),
+ length);
+ } else {
+ DCHECK_GE(SpdyConstants::GetFrameMaximumSize(version_), length);
+ }
bool success = false;
const size_t old_length = length_;
- if (framer.protocol_version() < 4) {
+ if (version_ <= SPDY3) {
FlagsAndLength flags_length = CreateFlagsAndLength(
0, // We're not writing over the flags value anyway.
length);
@@ -174,13 +196,25 @@ bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer,
return success;
}
+bool SpdyFrameBuilder::OverwriteFlags(const SpdyFramer& framer,
+ uint8 flags) {
+ DCHECK_LT(SPDY3, framer.protocol_version());
+ bool success = false;
+ const size_t old_length = length_;
+ // Flags are the fourth octet in the frame prefix.
+ length_ = 3;
+ success = WriteUInt8(flags);
+ length_ = old_length;
+ return success;
+}
+
bool SpdyFrameBuilder::CanWrite(size_t length) const {
if (length > kLengthMask) {
DCHECK(false);
return false;
}
- if (length_ + length > capacity_) {
+ if (offset_ + length_ + length > capacity_) {
DCHECK(false);
return false;
}
diff --git a/chromium/net/spdy/spdy_frame_builder.h b/chromium/net/spdy/spdy_frame_builder.h
index 348f105cd70..b4089baf618 100644
--- a/chromium/net/spdy/spdy_frame_builder.h
+++ b/chromium/net/spdy/spdy_frame_builder.h
@@ -28,12 +28,13 @@ class SpdyFramer;
class NET_EXPORT_PRIVATE SpdyFrameBuilder {
public:
// Initializes a SpdyFrameBuilder with a buffer of given size
- explicit SpdyFrameBuilder(size_t size);
+ SpdyFrameBuilder(size_t size, SpdyMajorVersion version);
~SpdyFrameBuilder();
- // Returns the size of the SpdyFrameBuilder's data.
- size_t length() const { return length_; }
+ // Returns the total size of the SpdyFrameBuilder's data, which may include
+ // multiple frames.
+ size_t length() const { return offset_ + length_; }
// Returns a writeable buffer of given size in bytes, to be appended to the
// currently written frame. Does bounds checking on length but does not
@@ -59,22 +60,28 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
// information from the |framer| and length information from capacity_.
bool WriteDataFrameHeader(const SpdyFramer& framer,
SpdyStreamId stream_id,
- SpdyDataFlags flags);
+ uint8 flags);
// Populates this frame with a SPDY4/HTTP2 frame prefix using
// version-specific information from the |framer| and length information from
// capacity_. The given type must be a control frame type.
// Used only for SPDY versions >=4.
- bool WriteFramePrefix(const SpdyFramer& framer,
- SpdyFrameType type,
- uint8 flags,
- SpdyStreamId stream_id);
+ bool BeginNewFrame(const SpdyFramer& framer,
+ SpdyFrameType type,
+ uint8 flags,
+ SpdyStreamId stream_id);
// Takes the buffer from the SpdyFrameBuilder.
SpdyFrame* take() {
- SpdyFrame* rv = new SpdyFrame(buffer_.release(), length_, true);
+ if (version_ > SPDY3) {
+ DLOG_IF(DFATAL, SpdyConstants::GetFrameMaximumSize(version_) < length_)
+ << "Frame length " << length_
+ << " is longer than the maximum allowed length.";
+ }
+ SpdyFrame* rv = new SpdyFrame(buffer_.release(), length(), true);
capacity_ = 0;
length_ = 0;
+ offset_ = 0;
return rv;
}
@@ -92,6 +99,12 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
value = htonl(value);
return WriteBytes(&value, sizeof(value));
}
+ bool WriteUInt64(uint64 value) {
+ uint32 upper = htonl(value >> 32);
+ uint32 lower = htonl(value);
+ return (WriteBytes(&upper, sizeof(upper)) &&
+ WriteBytes(&lower, sizeof(lower)));
+ }
// TODO(hkhalil) Rename to WriteStringPiece16().
bool WriteString(const std::string& value);
bool WriteStringPiece32(const base::StringPiece& value);
@@ -111,6 +124,11 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
// with the correct version for the frame being written.
bool OverwriteLength(const SpdyFramer& framer, size_t length);
+ // Update (in-place) the flags field in the frame being built to reflect the
+ // given flags value.
+ // Used only for SPDY versions >=4.
+ bool OverwriteFlags(const SpdyFramer& framer, uint8 flags);
+
private:
// Checks to make sure that there is an appropriate amount of space for a
// write of given size, in bytes.
@@ -118,7 +136,10 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
scoped_ptr<char[]> buffer_;
size_t capacity_; // Allocation size of payload, set by constructor.
- size_t length_; // Current length of the buffer.
+ size_t length_; // Length of the latest frame in the buffer.
+ size_t offset_; // Position at which the latest frame begins.
+
+ const SpdyMajorVersion version_;
};
} // namespace net
diff --git a/chromium/net/spdy/spdy_frame_builder_test.cc b/chromium/net/spdy/spdy_frame_builder_test.cc
index 014449b2e57..73cad2419a4 100644
--- a/chromium/net/spdy/spdy_frame_builder_test.cc
+++ b/chromium/net/spdy/spdy_frame_builder_test.cc
@@ -10,19 +10,6 @@
namespace net {
-TEST(SpdyFrameBuilderTestVersionAgnostic, GetWritableBuffer) {
- const size_t builder_size = 10;
- SpdyFrameBuilder builder(builder_size);
- char* writable_buffer = builder.GetWritableBuffer(builder_size);
- memset(writable_buffer, ~1, builder_size);
- EXPECT_TRUE(builder.Seek(builder_size));
- scoped_ptr<SpdyFrame> frame(builder.take());
- char expected[builder_size];
- memset(expected, ~1, builder_size);
- EXPECT_EQ(base::StringPiece(expected, builder_size),
- base::StringPiece(frame->data(), builder_size));
-}
-
class SpdyFrameBuilderTest : public ::testing::TestWithParam<SpdyMajorVersion> {
protected:
virtual void SetUp() {
@@ -38,20 +25,33 @@ INSTANTIATE_TEST_CASE_P(SpdyFrameBuilderTests,
SpdyFrameBuilderTest,
::testing::Values(SPDY2, SPDY3, SPDY4));
+TEST_P(SpdyFrameBuilderTest, GetWritableBuffer) {
+ const size_t builder_size = 10;
+ SpdyFrameBuilder builder(builder_size, spdy_version_);
+ char* writable_buffer = builder.GetWritableBuffer(builder_size);
+ memset(writable_buffer, ~1, builder_size);
+ EXPECT_TRUE(builder.Seek(builder_size));
+ scoped_ptr<SpdyFrame> frame(builder.take());
+ char expected[builder_size];
+ memset(expected, ~1, builder_size);
+ EXPECT_EQ(base::StringPiece(expected, builder_size),
+ base::StringPiece(frame->data(), builder_size));
+}
+
TEST_P(SpdyFrameBuilderTest, RewriteLength) {
// Create an empty SETTINGS frame both via framer and manually via builder.
// The one created via builder is initially given the incorrect length, but
// then is corrected via RewriteLength().
SpdyFramer framer(spdy_version_);
- SettingsMap settings;
- scoped_ptr<SpdyFrame> expected(framer.CreateSettings(settings));
- SpdyFrameBuilder builder(expected->size() + 1);
- if (spdy_version_ < 4) {
+ SpdySettingsIR settings_ir;
+ scoped_ptr<SpdyFrame> expected(framer.SerializeSettings(settings_ir));
+ SpdyFrameBuilder builder(expected->size() + 1, spdy_version_);
+ if (spdy_version_ <= SPDY3) {
builder.WriteControlFrameHeader(framer, SETTINGS, 0);
+ builder.WriteUInt32(0); // Write the number of settings.
} else {
- builder.WriteFramePrefix(framer, SETTINGS, 0, 0);
+ builder.BeginNewFrame(framer, SETTINGS, 0, 0);
}
- builder.WriteUInt32(0); // Write the number of settings.
EXPECT_TRUE(builder.GetWritableBuffer(1) != NULL);
builder.RewriteLength(framer);
scoped_ptr<SpdyFrame> built(builder.take());
@@ -59,4 +59,21 @@ TEST_P(SpdyFrameBuilderTest, RewriteLength) {
base::StringPiece(built->data(), expected->size()));
}
+TEST_P(SpdyFrameBuilderTest, OverwriteFlags) {
+ // Create a HEADERS frame both via framer and manually via builder with
+ // different flags set, then make them match using OverwriteFlags().
+ SpdyFramer framer(spdy_version_);
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyHeadersIR headers_ir(1);
+ scoped_ptr<SpdyFrame> expected(framer.SerializeHeaders(headers_ir));
+ SpdyFrameBuilder builder(expected->size(), spdy_version_);
+ builder.BeginNewFrame(framer, HEADERS, 0, 1);
+ builder.OverwriteFlags(framer, HEADERS_FLAG_END_HEADERS);
+ scoped_ptr<SpdyFrame> built(builder.take());
+ EXPECT_EQ(base::StringPiece(expected->data(), expected->size()),
+ base::StringPiece(built->data(), built->size()));
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_frame_reader.cc b/chromium/net/spdy/spdy_frame_reader.cc
index 0ab1f2f55e2..3ecce43e17e 100644
--- a/chromium/net/spdy/spdy_frame_reader.cc
+++ b/chromium/net/spdy/spdy_frame_reader.cc
@@ -64,6 +64,24 @@ bool SpdyFrameReader::ReadUInt32(uint32* result) {
return true;
}
+bool SpdyFrameReader::ReadUInt64(uint64* result) {
+ // Make sure that we have the whole uint64.
+ if (!CanRead(8)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result. Network byte order is big-endian.
+ uint64 upper = ntohl(*(reinterpret_cast<const uint32*>(data_ + ofs_)));
+ uint64 lower = ntohl(*(reinterpret_cast<const uint32*>(data_ + ofs_ + 4)));
+ *result = (upper << 32) + lower;
+
+ // Iterate.
+ ofs_ += 8;
+
+ return true;
+}
+
bool SpdyFrameReader::ReadUInt31(uint32* result) {
bool success = ReadUInt32(result);
diff --git a/chromium/net/spdy/spdy_frame_reader.h b/chromium/net/spdy/spdy_frame_reader.h
index 886f5be7a47..d30ae93c1ba 100644
--- a/chromium/net/spdy/spdy_frame_reader.h
+++ b/chromium/net/spdy/spdy_frame_reader.h
@@ -34,29 +34,34 @@ class NET_EXPORT_PRIVATE SpdyFrameReader {
~SpdyFrameReader() {}
// Reads an 8-bit unsigned integer into the given output parameter.
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadUInt8(uint8* result);
// Reads a 16-bit unsigned integer into the given output parameter.
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadUInt16(uint16* result);
// Reads a 32-bit unsigned integer into the given output parameter.
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadUInt32(uint32* result);
+ // Reads a 64-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt64(uint64* result);
+
// Reads a 31-bit unsigned integer into the given output parameter. This is
// equivalent to ReadUInt32() above except that the highest-order bit is
// discarded.
- // Forwards the internal iterater (by 4B) on success.
+ // Forwards the internal iterator (by 4B) on success.
// Returns true on success, false otherwise.
bool ReadUInt31(uint32* result);
// Reads a 24-bit unsigned integer into the given output parameter.
- // Forwards the internal iterater (by 3B) on success.
+ // Forwards the internal iterator (by 3B) on success.
// Returns true on success, false otherwise.
bool ReadUInt24(uint32* result);
@@ -65,7 +70,7 @@ class NET_EXPORT_PRIVATE SpdyFrameReader {
// NOTE: Does not copy but rather references strings in the underlying buffer.
// This should be kept in mind when handling memory management!
//
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadStringPiece16(base::StringPiece* result);
@@ -74,13 +79,13 @@ class NET_EXPORT_PRIVATE SpdyFrameReader {
// NOTE: Does not copy but rather references strings in the underlying buffer.
// This should be kept in mind when handling memory management!
//
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadStringPiece32(base::StringPiece* result);
// Reads a given number of bytes into the given buffer. The buffer
// must be of adequate size.
- // Forwards the internal iterater on success.
+ // Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadBytes(void* result, size_t size);
diff --git a/chromium/net/spdy/spdy_framer.cc b/chromium/net/spdy/spdy_framer.cc
index 37e6a282dbf..77aa413a78c 100644
--- a/chromium/net/spdy/spdy_framer.cc
+++ b/chromium/net/spdy/spdy_framer.cc
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// TODO(rtenhove) clean up frame buffer size calculations so that we aren't
-// constantly adding and subtracting header sizes; this is ugly and error-
-// prone.
-
#include "net/spdy/spdy_framer.h"
#include "base/lazy_instance.h"
@@ -17,6 +13,8 @@
#include "net/spdy/spdy_bitmasks.h"
#include "third_party/zlib/zlib.h"
+using base::StringPiece;
+using std::string;
using std::vector;
namespace net {
@@ -49,11 +47,14 @@ base::LazyInstance<DictionaryIds>::Leaky g_dictionary_ids;
// Used to indicate no flags in a SPDY flags field.
const uint8 kNoFlags = 0;
+// Wire sizes of priority payloads.
+const size_t kPriorityDependencyPayloadSize = 4;
+const size_t kPriorityWeightPayloadSize = 1;
+
} // namespace
const SpdyStreamId SpdyFramer::kInvalidStream = -1;
const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
-// The size of the control frame buffer. Must be >= the minimum size of the
// largest control frame, which is SYN_STREAM. See GetSynStreamMinimumSize() for
// calculation details.
const size_t SpdyFramer::kControlFrameBufferSize = 18;
@@ -79,9 +80,9 @@ const size_t SpdyFramer::kControlFrameBufferSize = 18;
} while (false)
#endif
-SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(int version,
- uint32 wire) {
- if (version < 3) {
+SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(
+ SpdyMajorVersion version, uint32 wire) {
+ if (version < SPDY3) {
ConvertFlagsAndIdForSpdy2(&wire);
}
return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff);
@@ -89,12 +90,13 @@ SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(int version,
SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id)
: flags_(flags), id_(id & 0x00ffffff) {
- DCHECK_GT(1u << 24, id) << "SPDY setting ID too large.";
+ LOG_IF(DFATAL, id > (1u << 24)) << "SPDY setting ID too large: " << id;
}
-uint32 SettingsFlagsAndId::GetWireFormat(int version) const {
+uint32 SettingsFlagsAndId::GetWireFormat(SpdyMajorVersion version)
+ const {
uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24);
- if (version < 3) {
+ if (version < SPDY3) {
ConvertFlagsAndIdForSpdy2(&wire);
}
return wire;
@@ -111,8 +113,19 @@ void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) {
std::swap(wire_array[1], wire_array[2]);
}
-SpdyCredential::SpdyCredential() : slot(0) {}
-SpdyCredential::~SpdyCredential() {}
+SpdyAltSvcScratch::SpdyAltSvcScratch() { Reset(); }
+SpdyAltSvcScratch::~SpdyAltSvcScratch() {}
+
+bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data,
+ size_t len) {
+ return true;
+}
+
+bool SpdyFramerVisitorInterface::OnRstStreamFrameData(
+ const char* rst_stream_data,
+ size_t len) {
+ return true;
+}
SpdyFramer::SpdyFramer(SpdyMajorVersion version)
: current_frame_buffer_(new char[kControlFrameBufferSize]),
@@ -122,7 +135,9 @@ SpdyFramer::SpdyFramer(SpdyMajorVersion version)
display_protocol_("SPDY"),
spdy_version_(version),
syn_frame_processed_(false),
- probable_http_response_(false) {
+ probable_http_response_(false),
+ expect_continuation_(0),
+ end_stream_when_done_(false) {
DCHECK_GE(spdy_version_, SPDY_MIN_VERSION);
DCHECK_LE(spdy_version_, SPDY_MAX_VERSION);
Reset();
@@ -149,38 +164,32 @@ void SpdyFramer::Reset() {
current_frame_length_ = 0;
current_frame_stream_id_ = kInvalidStream;
settings_scratch_.Reset();
+ altsvc_scratch_.Reset();
+ remaining_padding_payload_length_ = 0;
+ remaining_padding_length_fields_ = 0;
}
size_t SpdyFramer::GetDataFrameMinimumSize() const {
- // Size, in bytes, of the data frame header. Future versions of SPDY
- // will likely vary this, so we allow for the flexibility of a function call
- // for this value as opposed to a constant.
- return 8;
+ return SpdyConstants::GetDataFrameMinimumSize();
}
// Size, in bytes, of the control frame header.
size_t SpdyFramer::GetControlFrameHeaderSize() const {
- switch (protocol_version()) {
- case SPDY2:
- case SPDY3:
- case SPDY4:
- return 8;
- }
- LOG(DFATAL) << "Unhandled SPDY version.";
- return 0;
+ return SpdyConstants::GetControlFrameHeaderSize(protocol_version());
}
size_t SpdyFramer::GetSynStreamMinimumSize() const {
// Size, in bytes, of a SYN_STREAM frame not including the variable-length
// name-value block.
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
// Calculated as:
- // control frame header + 2 * 4 (stream IDs) + 1 (priority) + 1 (slot)
+ // control frame header + 2 * 4 (stream IDs) + 1 (priority)
+ // + 1 (unused, was credential slot)
return GetControlFrameHeaderSize() + 10;
} else {
- // Calculated as:
- // frame prefix + 4 (associated stream ID) + 1 (priority) + 1 (slot)
- return GetControlFrameHeaderSize() + 6;
+ return GetControlFrameHeaderSize() +
+ kPriorityDependencyPayloadSize +
+ kPriorityWeightPayloadSize;
}
}
@@ -188,23 +197,23 @@ size_t SpdyFramer::GetSynReplyMinimumSize() const {
// Size, in bytes, of a SYN_REPLY frame not including the variable-length
// name-value block.
size_t size = GetControlFrameHeaderSize();
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
// Calculated as:
// control frame header + 4 (stream IDs)
size += 4;
}
// In SPDY 2, there were 2 unused bytes before payload.
- if (protocol_version() < 3) {
+ if (protocol_version() < SPDY3) {
size += 2;
}
return size;
}
-size_t SpdyFramer::GetRstStreamSize() const {
+size_t SpdyFramer::GetRstStreamMinimumSize() const {
// Size, in bytes, of a RST_STREAM frame.
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
// Calculated as:
// control frame header + 4 (stream id) + 4 (status code)
return GetControlFrameHeaderSize() + 8;
@@ -219,22 +228,36 @@ size_t SpdyFramer::GetSettingsMinimumSize() const {
// Size, in bytes, of a SETTINGS frame not including the IDs and values
// from the variable-length value block. Calculated as:
// control frame header + 4 (number of ID/value pairs)
- return GetControlFrameHeaderSize() + 4;
+ if (protocol_version() <= SPDY3) {
+ return GetControlFrameHeaderSize() + 4;
+ } else {
+ return GetControlFrameHeaderSize();
+ }
}
size_t SpdyFramer::GetPingSize() const {
- // Size, in bytes, of this PING frame. Calculated as:
- // control frame header + 4 (id)
- return GetControlFrameHeaderSize() + 4;
+ // Size, in bytes, of this PING frame.
+ if (protocol_version() <= SPDY3) {
+ // Calculated as:
+ // control frame header + 4 (id)
+ return GetControlFrameHeaderSize() + 4;
+ } else {
+ // Calculated as:
+ // control frame header + 8 (id)
+ return GetControlFrameHeaderSize() + 8;
+ }
}
-size_t SpdyFramer::GetGoAwaySize() const {
+size_t SpdyFramer::GetGoAwayMinimumSize() const {
// Size, in bytes, of this GOAWAY frame. Calculated as:
- // control frame header + 4 (last good stream id)
- size_t size = GetControlFrameHeaderSize() + 4;
+ // 1. Control frame header size
+ size_t size = GetControlFrameHeaderSize();
- // SPDY 3+ GOAWAY frames also contain a status.
- if (protocol_version() >= 3) {
+ // 2. Last good stream id (4 bytes)
+ size += 4;
+
+ // 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes)
+ if (protocol_version() >= SPDY3) {
size += 4;
}
@@ -245,14 +268,14 @@ size_t SpdyFramer::GetHeadersMinimumSize() const {
// Size, in bytes, of a HEADERS frame not including the variable-length
// name-value block.
size_t size = GetControlFrameHeaderSize();
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
// Calculated as:
// control frame header + 4 (stream IDs)
size += 4;
}
// In SPDY 2, there were 2 unused bytes before payload.
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
size += 2;
}
@@ -261,7 +284,7 @@ size_t SpdyFramer::GetHeadersMinimumSize() const {
size_t SpdyFramer::GetWindowUpdateSize() const {
// Size, in bytes, of a WINDOW_UPDATE frame.
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
// Calculated as:
// control frame header + 4 (stream id) + 4 (delta)
return GetControlFrameHeaderSize() + 8;
@@ -272,39 +295,59 @@ size_t SpdyFramer::GetWindowUpdateSize() const {
}
}
-size_t SpdyFramer::GetCredentialMinimumSize() const {
- // Size, in bytes, of a CREDENTIAL frame sans variable-length certificate list
- // and proof. Calculated as:
- // control frame header + 2 (slot)
- return GetControlFrameHeaderSize() + 2;
-}
-
size_t SpdyFramer::GetBlockedSize() const {
- DCHECK_LE(4, protocol_version());
+ DCHECK_LT(SPDY3, protocol_version());
// Size, in bytes, of a BLOCKED frame.
// The BLOCKED frame has no payload beyond the control frame header.
return GetControlFrameHeaderSize();
}
size_t SpdyFramer::GetPushPromiseMinimumSize() const {
- DCHECK_LE(4, protocol_version());
+ DCHECK_LT(SPDY3, protocol_version());
// Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block.
// Calculated as frame prefix + 4 (promised stream id).
return GetControlFrameHeaderSize() + 4;
}
+size_t SpdyFramer::GetContinuationMinimumSize() const {
+ // Size, in bytes, of a CONTINUATION frame not including the variable-length
+ // headers fragments.
+ return GetControlFrameHeaderSize();
+}
+
+size_t SpdyFramer::GetAltSvcMinimumSize() const {
+ // Size, in bytes, of an ALTSVC frame not including the Protocol-ID, Host, and
+ // (optional) Origin fields, all of which can vary in length.
+ // Note that this gives a lower bound on the frame size rather than a true
+ // minimum; the actual frame should always be larger than this.
+ // Calculated as frame prefix + 4 (max-age) + 2 (port) + 1 (reserved byte)
+ // + 1 (pid_len) + 1 (host_len).
+ return GetControlFrameHeaderSize() + 9;
+}
+
+size_t SpdyFramer::GetPrioritySize() const {
+ // Size, in bytes, of a PRIORITY frame.
+ return GetControlFrameHeaderSize() +
+ kPriorityDependencyPayloadSize +
+ kPriorityWeightPayloadSize;
+}
+
size_t SpdyFramer::GetFrameMinimumSize() const {
return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize());
}
size_t SpdyFramer::GetFrameMaximumSize() const {
- return (protocol_version() < 4) ? 0xffffff : 0xffff;
+ return SpdyConstants::GetFrameMaximumSize(protocol_version());
}
size_t SpdyFramer::GetDataFrameMaximumPayload() const {
return GetFrameMaximumSize() - GetDataFrameMinimumSize();
}
+size_t SpdyFramer::GetPrefixLength(SpdyFrameType type) const {
+ return SpdyConstants::GetPrefixLength(type, protocol_version());
+}
+
const char* SpdyFramer::StateToString(int state) {
switch (state) {
case SPDY_ERROR:
@@ -317,6 +360,10 @@ const char* SpdyFramer::StateToString(int state) {
return "READING_COMMON_HEADER";
case SPDY_CONTROL_FRAME_PAYLOAD:
return "CONTROL_FRAME_PAYLOAD";
+ case SPDY_READ_PADDING_LENGTH:
+ return "SPDY_READ_PADDING_LENGTH";
+ case SPDY_CONSUME_PADDING:
+ return "SPDY_CONSUME_PADDING";
case SPDY_IGNORE_REMAINING_PAYLOAD:
return "IGNORE_REMAINING_PAYLOAD";
case SPDY_FORWARD_STREAM_FRAME:
@@ -325,10 +372,14 @@ const char* SpdyFramer::StateToString(int state) {
return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK";
case SPDY_CONTROL_FRAME_HEADER_BLOCK:
return "SPDY_CONTROL_FRAME_HEADER_BLOCK";
- case SPDY_CREDENTIAL_FRAME_PAYLOAD:
- return "SPDY_CREDENTIAL_FRAME_PAYLOAD";
+ case SPDY_GOAWAY_FRAME_PAYLOAD:
+ return "SPDY_GOAWAY_FRAME_PAYLOAD";
+ case SPDY_RST_STREAM_FRAME_PAYLOAD:
+ return "SPDY_RST_STREAM_FRAME_PAYLOAD";
case SPDY_SETTINGS_FRAME_PAYLOAD:
return "SPDY_SETTINGS_FRAME_PAYLOAD";
+ case SPDY_ALTSVC_FRAME_PAYLOAD:
+ return "SPDY_ALTSVC_FRAME_PAYLOAD";
}
return "UNKNOWN_STATE";
}
@@ -336,6 +387,12 @@ const char* SpdyFramer::StateToString(int state) {
void SpdyFramer::set_error(SpdyError error) {
DCHECK(visitor_);
error_code_ = error;
+ // These values will usually get reset once we come to the end
+ // of a header block, but if we run into an error that
+ // might not happen, so reset them here.
+ expect_continuation_ = 0;
+ end_stream_when_done_ = false;
+
CHANGE_STATE(SPDY_ERROR);
visitor_->OnError(this);
}
@@ -360,6 +417,8 @@ const char* SpdyFramer::ErrorCodeToString(int error_code) {
return "SPDY_INVALID_DATA_FRAME_FLAGS";
case SPDY_INVALID_CONTROL_FRAME_FLAGS:
return "SPDY_INVALID_CONTROL_FRAME_FLAGS";
+ case SPDY_UNEXPECTED_FRAME:
+ return "UNEXPECTED_FRAME";
}
return "UNKNOWN_ERROR";
}
@@ -390,6 +449,10 @@ const char* SpdyFramer::StatusCodeToString(int status_code) {
return "INVALID_CREDENTIALS";
case RST_STREAM_FRAME_TOO_LARGE:
return "FRAME_TOO_LARGE";
+ case RST_STREAM_CONNECT_ERROR:
+ return "CONNECT_ERROR";
+ case RST_STREAM_ENHANCE_YOUR_CALM:
+ return "ENHANCE_YOUR_CALM";
}
return "UNKNOWN_STATUS";
}
@@ -422,6 +485,12 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) {
return "BLOCKED";
case PUSH_PROMISE:
return "PUSH_PROMISE";
+ case CONTINUATION:
+ return "CONTINUATION";
+ case ALTSVC:
+ return "ALTSVC";
+ case PRIORITY:
+ return "PRIORITY";
}
return "UNKNOWN_CONTROL_TYPE";
}
@@ -454,7 +523,7 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: {
// Control frames that contain header blocks
- // (SYN_STREAM, SYN_REPLY, HEADERS, PUSH_PROMISE)
+ // (SYN_STREAM, SYN_REPLY, HEADERS, PUSH_PROMISE, CONTINUATION)
// take a different path through the state machine - they
// will go:
// 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
@@ -480,14 +549,29 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
}
case SPDY_CONTROL_FRAME_HEADER_BLOCK: {
- int bytes_read = ProcessControlFrameHeaderBlock(data, len);
+ int bytes_read = ProcessControlFrameHeaderBlock(
+ data, len, protocol_version() > SPDY3);
len -= bytes_read;
data += bytes_read;
break;
}
- case SPDY_CREDENTIAL_FRAME_PAYLOAD: {
- size_t bytes_read = ProcessCredentialFramePayload(data, len);
+ case SPDY_RST_STREAM_FRAME_PAYLOAD: {
+ size_t bytes_read = ProcessRstStreamFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
+ case SPDY_GOAWAY_FRAME_PAYLOAD: {
+ size_t bytes_read = ProcessGoAwayFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
+ case SPDY_ALTSVC_FRAME_PAYLOAD: {
+ size_t bytes_read = ProcessAltSvcFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
break;
@@ -500,15 +584,34 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
break;
}
- case SPDY_IGNORE_REMAINING_PAYLOAD:
- // control frame has too-large payload
- // intentional fallthrough
+ case SPDY_READ_PADDING_LENGTH: {
+ size_t bytes_read = ProcessFramePaddingLength(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
+ case SPDY_CONSUME_PADDING: {
+ size_t bytes_read = ProcessFramePadding(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
+ case SPDY_IGNORE_REMAINING_PAYLOAD: {
+ size_t bytes_read = ProcessIgnoredControlFramePayload(/*data,*/ len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
case SPDY_FORWARD_STREAM_FRAME: {
size_t bytes_read = ProcessDataFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
break;
}
+
default:
LOG(DFATAL) << "Invalid value for " << display_protocol_
<< " framer state: " << state_;
@@ -562,13 +665,27 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
// ProcessControlFrameHeader() will set current_frame_type_ to the
// correct value if this is a valid control frame.
current_frame_type_ = DATA;
- if (protocol_version() < 4) {
+ if (protocol_version() <= SPDY3) {
bool successful_read = reader->ReadUInt16(&version);
DCHECK(successful_read);
is_control_frame = (version & kControlFlagMask) != 0;
version &= ~kControlFlagMask; // Only valid for control frames.
-
if (is_control_frame) {
+ // We check version before we check validity: version can never be
+ // 'invalid', it can only be unsupported.
+ if (version < SpdyConstants::SerializeMajorVersion(SPDY_MIN_VERSION) ||
+ version > SpdyConstants::SerializeMajorVersion(SPDY_MAX_VERSION) ||
+ SpdyConstants::ParseMajorVersion(version) != protocol_version()) {
+ // Version does not match the version the framer was initialized with.
+ DVLOG(1) << "Unsupported SPDY version "
+ << version
+ << " (expected " << protocol_version() << ")";
+ set_error(SPDY_UNSUPPORTED_VERSION);
+ return 0;
+ } else {
+ // Convert version from wire format to SpdyMajorVersion.
+ version = SpdyConstants::ParseMajorVersion(version);
+ }
// We check control_frame_type_field's validity in
// ProcessControlFrameHeader().
successful_read = reader->ReadUInt16(&control_frame_type_field);
@@ -591,7 +708,6 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
uint16 length_field = 0;
bool successful_read = reader->ReadUInt16(&length_field);
DCHECK(successful_read);
- current_frame_length_ = length_field;
uint8 control_frame_type_field_uint8 = DATA;
successful_read = reader->ReadUInt8(&control_frame_type_field_uint8);
@@ -601,6 +717,12 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
control_frame_type_field = control_frame_type_field_uint8;
is_control_frame = (control_frame_type_field != DATA);
+ if (is_control_frame) {
+ current_frame_length_ = length_field + GetControlFrameHeaderSize();
+ } else {
+ current_frame_length_ = length_field + GetDataFrameMinimumSize();
+ }
+
successful_read = reader->ReadUInt8(&current_frame_flags_);
DCHECK(successful_read);
@@ -608,6 +730,22 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
DCHECK(successful_read);
remaining_data_length_ = current_frame_length_ - reader->GetBytesConsumed();
+
+ // Before we accept a DATA frame, we need to make sure we're not in the
+ // middle of processing a header block.
+ const bool is_continuation_frame = (control_frame_type_field ==
+ SpdyConstants::SerializeFrameType(protocol_version(), CONTINUATION));
+ if ((expect_continuation_ != 0) != is_continuation_frame) {
+ if (expect_continuation_ != 0) {
+ DLOG(ERROR) << "The framer was expecting to receive a CONTINUATION "
+ << "frame, but instead received frame type "
+ << control_frame_type_field;
+ } else {
+ DLOG(ERROR) << "The framer received an unexpected CONTINUATION frame.";
+ }
+ set_error(SPDY_UNEXPECTED_FRAME);
+ return original_len - len;
+ }
}
DCHECK_EQ(is_control_frame ? GetControlFrameHeaderSize()
: GetDataFrameMinimumSize(),
@@ -632,14 +770,28 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
// if we're here, then we have the common header all received.
if (!is_control_frame) {
- if (current_frame_flags_ & ~DATA_FLAG_FIN) {
+ if (protocol_version() > SPDY3) {
+ // Catch bogus tests sending oversized DATA frames.
+ DCHECK_GE(GetFrameMaximumSize(), current_frame_length_)
+ << "DATA frame too large for SPDY >= 4.";
+ }
+
+ uint8 valid_data_flags = 0;
+ if (protocol_version() > SPDY3) {
+ valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT |
+ DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH;
+ } else {
+ valid_data_flags = DATA_FLAG_FIN;
+ }
+
+ if (current_frame_flags_ & ~valid_data_flags) {
set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
} else {
visitor_->OnDataFrameHeader(current_frame_stream_id_,
remaining_data_length_,
current_frame_flags_ & DATA_FLAG_FIN);
if (remaining_data_length_ > 0) {
- CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH);
} else {
// Empty data frame.
if (current_frame_flags_ & DATA_FLAG_FIN) {
@@ -649,12 +801,6 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
CHANGE_STATE(SPDY_AUTO_RESET);
}
}
- } else if (version != spdy_version_) {
- // We check version before we check validity: version can never be
- // 'invalid', it can only be unsupported.
- DVLOG(1) << "Unsupported SPDY version " << version
- << " (expected " << spdy_version_ << ")";
- set_error(SPDY_UNSUPPORTED_VERSION);
} else {
ProcessControlFrameHeader(control_frame_type_field);
}
@@ -666,20 +812,35 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
DCHECK_EQ(SPDY_NO_ERROR, error_code_);
DCHECK_LE(GetControlFrameHeaderSize(), current_frame_buffer_length_);
- if (control_frame_type_field < FIRST_CONTROL_TYPE ||
- control_frame_type_field > LAST_CONTROL_TYPE) {
- set_error(SPDY_INVALID_CONTROL_FRAME);
- return;
- }
+ // Early detection of deprecated frames that we ignore.
+ if (protocol_version() <= SPDY3) {
+ if (control_frame_type_field == NOOP) {
+ current_frame_type_ = NOOP;
+ DVLOG(1) << "NOOP control frame found. Ignoring.";
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ return;
+ }
- current_frame_type_ = static_cast<SpdyFrameType>(control_frame_type_field);
+ if (control_frame_type_field == CREDENTIAL) {
+ current_frame_type_ = CREDENTIAL;
+ DCHECK_EQ(SPDY3, protocol_version());
+ DVLOG(1) << "CREDENTIAL control frame found. Ignoring.";
+ CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD);
+ return;
+ }
+ }
- if (current_frame_type_ == NOOP) {
- DVLOG(1) << "NOOP control frame found. Ignoring.";
- CHANGE_STATE(SPDY_AUTO_RESET);
+ if (!SpdyConstants::IsValidFrameType(protocol_version(),
+ control_frame_type_field)) {
+ DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field
+ << " (protocol version: " << protocol_version() << ")";
+ set_error(SPDY_INVALID_CONTROL_FRAME);
return;
}
+ current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(),
+ control_frame_type_field);
+
// Do some sanity checking on the control frame sizes and flags.
switch (current_frame_type_) {
case SYN_STREAM:
@@ -698,35 +859,63 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
}
break;
case RST_STREAM:
- if (current_frame_length_ != GetRstStreamSize()) {
+ // For SPDY versions < 4, the header has a fixed length.
+ // For SPDY version 4 and up, the RST_STREAM frame may include optional
+ // opaque data, so we only have a lower limit on the frame size.
+ if ((current_frame_length_ != GetRstStreamMinimumSize() &&
+ protocol_version() <= SPDY3) ||
+ (current_frame_length_ < GetRstStreamMinimumSize() &&
+ protocol_version() > SPDY3)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
case SETTINGS:
+ {
// Make sure that we have an integral number of 8-byte key/value pairs,
- // plus a 4-byte length field.
+ // plus a 4-byte length field in SPDY3 and below.
+ size_t values_prefix_size = (protocol_version() <= SPDY3 ? 4 : 0);
+ // Size of each key/value pair in bytes.
+ size_t setting_size = (protocol_version() <= SPDY3 ? 8 : 5);
if (current_frame_length_ < GetSettingsMinimumSize() ||
- (current_frame_length_ - GetControlFrameHeaderSize()) % 8 != 4) {
+ (current_frame_length_ - GetControlFrameHeaderSize())
+ % setting_size != values_prefix_size) {
DLOG(WARNING) << "Invalid length for SETTINGS frame: "
<< current_frame_length_;
set_error(SPDY_INVALID_CONTROL_FRAME);
- } else if (current_frame_flags_ &
+ } else if (protocol_version() <= SPDY3 &&
+ current_frame_flags_ &
~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ } else if (protocol_version() > SPDY3 &&
+ current_frame_flags_ & ~SETTINGS_FLAG_ACK) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ } else if (protocol_version() > SPDY3 &&
+ current_frame_flags_ & SETTINGS_FLAG_ACK &&
+ current_frame_length_ > GetSettingsMinimumSize()) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
}
break;
+ }
case PING:
if (current_frame_length_ != GetPingSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
- } else if (current_frame_flags_ != 0) {
+ } else if ((protocol_version() <= SPDY3 && current_frame_flags_ != 0) ||
+ (current_frame_flags_ & ~PING_FLAG_ACK)) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
case GOAWAY:
{
- if (current_frame_length_ != GetGoAwaySize()) {
+ // For SPDY version < 4, there are only mandatory fields and the header
+ // has a fixed length. For SPDY version >= 4, optional opaque data may
+ // be appended to the GOAWAY frame, thus there is only a minimal length
+ // restriction.
+ if ((current_frame_length_ != GetGoAwayMinimumSize() &&
+ protocol_version() <= SPDY3) ||
+ (current_frame_length_ < GetGoAwayMinimumSize() &&
+ protocol_version() > SPDY3)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
@@ -734,10 +923,24 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
break;
}
case HEADERS:
- if (current_frame_length_ < GetHeadersMinimumSize()) {
- set_error(SPDY_INVALID_CONTROL_FRAME);
- } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) {
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ {
+ size_t min_size = GetHeadersMinimumSize();
+ if (protocol_version() > SPDY3 &&
+ (current_frame_flags_ & HEADERS_FLAG_PRIORITY)) {
+ min_size += 4;
+ }
+ if (current_frame_length_ < min_size) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ } else if (protocol_version() <= SPDY3 &&
+ current_frame_flags_ & ~CONTROL_FLAG_FIN) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ } else if (protocol_version() > SPDY3 &&
+ current_frame_flags_ &
+ ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
+ HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_END_SEGMENT |
+ HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ }
}
break;
case WINDOW_UPDATE:
@@ -747,22 +950,47 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
- case CREDENTIAL:
- if (current_frame_length_ < GetCredentialMinimumSize()) {
+ case BLOCKED:
+ if (current_frame_length_ != GetBlockedSize() ||
+ protocol_version() <= SPDY3) {
+ // TODO(mlavan): BLOCKED frames are no longer part of SPDY4.
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
- case BLOCKED:
- if (current_frame_length_ != GetBlockedSize()) {
+ case PUSH_PROMISE:
+ if (current_frame_length_ < GetPushPromiseMinimumSize()) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ } else if (protocol_version() <= SPDY3 && current_frame_flags_ != 0) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ } else if (protocol_version() > SPDY3 &&
+ current_frame_flags_ &
+ ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE |
+ HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ }
+ break;
+ case CONTINUATION:
+ if (current_frame_length_ < GetContinuationMinimumSize() ||
+ protocol_version() <= SPDY3) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ } else if (current_frame_flags_ &
+ ~(HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
+ }
+ break;
+ case ALTSVC:
+ if (current_frame_length_ <= GetAltSvcMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
- case PUSH_PROMISE:
- if (current_frame_length_ < GetPushPromiseMinimumSize()) {
+ case PRIORITY:
+ if (current_frame_length_ != GetPrioritySize() ||
+ protocol_version() <= SPDY3) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
@@ -792,11 +1020,20 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
return;
}
- if (current_frame_type_ == CREDENTIAL) {
- CHANGE_STATE(SPDY_CREDENTIAL_FRAME_PAYLOAD);
+ if (current_frame_type_ == GOAWAY) {
+ CHANGE_STATE(SPDY_GOAWAY_FRAME_PAYLOAD);
+ return;
+ }
+
+ if (current_frame_type_ == RST_STREAM) {
+ CHANGE_STATE(SPDY_RST_STREAM_FRAME_PAYLOAD);
return;
}
+ if (current_frame_type_ == ALTSVC) {
+ CHANGE_STATE(SPDY_ALTSVC_FRAME_PAYLOAD);
+ return;
+ }
// Determine the frame size without variable-length data.
int32 frame_size_without_variable_data;
switch (current_frame_type_) {
@@ -813,10 +1050,19 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
break;
case HEADERS:
frame_size_without_variable_data = GetHeadersMinimumSize();
+ if (protocol_version() > SPDY3 &&
+ current_frame_flags_ & HEADERS_FLAG_PRIORITY) {
+ frame_size_without_variable_data +=
+ kPriorityDependencyPayloadSize +
+ kPriorityWeightPayloadSize;
+ }
break;
case PUSH_PROMISE:
frame_size_without_variable_data = GetPushPromiseMinimumSize();
break;
+ case CONTINUATION:
+ frame_size_without_variable_data = GetContinuationMinimumSize();
+ break;
default:
frame_size_without_variable_data = -1;
break;
@@ -866,10 +1112,11 @@ size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len,
return bytes_to_read;
}
-size_t SpdyFramer::GetSerializedLength(const int spdy_version,
- const SpdyHeaderBlock* headers) {
+size_t SpdyFramer::GetSerializedLength(
+ const SpdyMajorVersion spdy_version,
+ const SpdyHeaderBlock* headers) {
const size_t num_name_value_pairs_size
- = (spdy_version < 3) ? sizeof(uint16) : sizeof(uint32);
+ = (spdy_version < SPDY3) ? sizeof(uint16) : sizeof(uint32);
const size_t length_of_name_size = num_name_value_pairs_size;
const size_t length_of_value_size = num_name_value_pairs_size;
@@ -886,16 +1133,16 @@ size_t SpdyFramer::GetSerializedLength(const int spdy_version,
}
void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame,
- const int spdy_version,
+ const SpdyMajorVersion spdy_version,
const SpdyHeaderBlock* headers) {
- if (spdy_version < 3) {
+ if (spdy_version < SPDY3) {
frame->WriteUInt16(headers->size()); // Number of headers.
} else {
frame->WriteUInt32(headers->size()); // Number of headers.
}
SpdyHeaderBlock::const_iterator it;
for (it = headers->begin(); it != headers->end(); ++it) {
- if (spdy_version < 3) {
+ if (spdy_version < SPDY3) {
frame->WriteString(it->first);
frame->WriteString(it->second);
} else {
@@ -1075,7 +1322,7 @@ void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
size_t len) {
DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_);
- size_t original_len = len;
+ const size_t original_len = len;
if (remaining_control_header_ > 0) {
size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len,
@@ -1092,11 +1339,10 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
switch (current_frame_type_) {
case SYN_STREAM:
{
+ DCHECK_GE(SPDY3, protocol_version());
bool successful_read = true;
- if (spdy_version_ < 4) {
- successful_read = reader.ReadUInt31(&current_frame_stream_id_);
- DCHECK(successful_read);
- }
+ successful_read = reader.ReadUInt31(&current_frame_stream_id_);
+ DCHECK(successful_read);
if (current_frame_stream_id_ == 0) {
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
@@ -1109,20 +1355,14 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
SpdyPriority priority = 0;
successful_read = reader.ReadUInt8(&priority);
DCHECK(successful_read);
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
priority = priority >> 6;
} else {
priority = priority >> 5;
}
- uint8 slot = 0;
- if (protocol_version() < 3) {
- // SPDY 2 had an unused byte here. Seek past it.
- reader.Seek(1);
- } else {
- successful_read = reader.ReadUInt8(&slot);
- DCHECK(successful_read);
- }
+ // Seek past unused byte; used to be credential slot in SPDY 3.
+ reader.Seek(1);
DCHECK(reader.IsDoneReading());
if (debug_visitor_) {
@@ -1135,23 +1375,31 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
current_frame_stream_id_,
associated_to_stream_id,
priority,
- slot,
(current_frame_flags_ & CONTROL_FLAG_FIN) != 0,
(current_frame_flags_ & CONTROL_FLAG_UNIDIRECTIONAL) != 0);
}
CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
break;
case SETTINGS:
- visitor_->OnSettings(current_frame_flags_ &
- SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS);
- CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD);
+ if (protocol_version() > SPDY3 &&
+ current_frame_flags_ & SETTINGS_FLAG_ACK) {
+ visitor_->OnSettingsAck();
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ } else {
+ visitor_->OnSettings(current_frame_flags_ &
+ SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS);
+ CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD);
+ }
break;
case SYN_REPLY:
case HEADERS:
// SYN_REPLY and HEADERS are the same, save for the visitor call.
{
+ if (protocol_version() > SPDY3) {
+ DCHECK_EQ(HEADERS, current_frame_type_);
+ }
bool successful_read = true;
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
successful_read = reader.ReadUInt31(&current_frame_stream_id_);
DCHECK(successful_read);
}
@@ -1159,32 +1407,66 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
}
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
// SPDY 2 had two unused bytes here. Seek past them.
reader.Seek(2);
}
+ if (protocol_version() > SPDY3 &&
+ !(current_frame_flags_ & HEADERS_FLAG_END_HEADERS) &&
+ current_frame_type_ == HEADERS) {
+ expect_continuation_ = current_frame_stream_id_;
+ end_stream_when_done_ = current_frame_flags_ & CONTROL_FLAG_FIN;
+ }
+ const bool has_priority =
+ (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0;
+ uint32 priority = 0;
+ if (protocol_version() > SPDY3 && has_priority) {
+ // TODO(jgraettinger): Process dependency rather than ignoring it.
+ reader.Seek(kPriorityDependencyPayloadSize);
+ uint8 weight = 0;
+ successful_read = reader.ReadUInt8(&weight);
+ if (successful_read) {
+ priority = MapWeightToPriority(weight);
+ }
+ }
DCHECK(reader.IsDoneReading());
if (debug_visitor_) {
+ // SPDY 4 reports HEADERS with PRIORITY as SYN_STREAM.
+ SpdyFrameType reported_type = current_frame_type_;
+ if (protocol_version() > SPDY3 && has_priority) {
+ reported_type = SYN_STREAM;
+ }
debug_visitor_->OnReceiveCompressedFrame(
current_frame_stream_id_,
- current_frame_type_,
+ reported_type,
current_frame_length_);
}
if (current_frame_type_ == SYN_REPLY) {
visitor_->OnSynReply(
current_frame_stream_id_,
(current_frame_flags_ & CONTROL_FLAG_FIN) != 0);
+ } else if (protocol_version() > SPDY3 &&
+ current_frame_flags_ & HEADERS_FLAG_PRIORITY) {
+ // SPDY 4+ is missing SYN_STREAM. Simulate it so that API changes
+ // can be made independent of wire changes.
+ visitor_->OnSynStream(
+ current_frame_stream_id_,
+ 0, // associated_to_stream_id
+ priority,
+ current_frame_flags_ & CONTROL_FLAG_FIN,
+ false); // unidirectional
} else {
visitor_->OnHeaders(
current_frame_stream_id_,
- (current_frame_flags_ & CONTROL_FLAG_FIN) != 0);
+ (current_frame_flags_ & CONTROL_FLAG_FIN) != 0,
+ expect_continuation_ == 0);
}
}
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH);
break;
case PUSH_PROMISE:
{
- DCHECK_LE(4, protocol_version());
+ DCHECK_LT(SPDY3, protocol_version());
if (current_frame_stream_id_ == 0) {
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
@@ -1197,15 +1479,47 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
}
+ if (!(current_frame_flags_ & PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) {
+ expect_continuation_ = current_frame_stream_id_;
+ }
if (debug_visitor_) {
debug_visitor_->OnReceiveCompressedFrame(
current_frame_stream_id_,
current_frame_type_,
current_frame_length_);
}
- visitor_->OnPushPromise(current_frame_stream_id_, promised_stream_id);
+ visitor_->OnPushPromise(current_frame_stream_id_,
+ promised_stream_id,
+ (current_frame_flags_ &
+ PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0);
}
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH);
+ break;
+ case CONTINUATION:
+ {
+ // Check to make sure the stream id of the current frame is
+ // the same as that of the preceding frame.
+ // If we're at this point we should already know that
+ // expect_continuation_ != 0, so this doubles as a check
+ // that current_frame_stream_id != 0.
+ if (current_frame_stream_id_ != expect_continuation_) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ break;
+ }
+ if (current_frame_flags_ & HEADERS_FLAG_END_HEADERS) {
+ expect_continuation_ = 0;
+ }
+ if (debug_visitor_) {
+ debug_visitor_->OnReceiveCompressedFrame(
+ current_frame_stream_id_,
+ current_frame_type_,
+ current_frame_length_);
+ }
+ visitor_->OnContinuation(current_frame_stream_id_,
+ (current_frame_flags_ &
+ HEADERS_FLAG_END_HEADERS) != 0);
+ }
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH);
break;
default:
DCHECK(false);
@@ -1219,41 +1533,64 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
// IncrementallyDeliverControlFrameHeaderData() or
// IncrementallyDecompressControlFrameHeaderData() respectively.
size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data,
- size_t data_len) {
+ size_t data_len,
+ bool is_hpack_header_block) {
DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_);
bool processed_successfully = true;
if (current_frame_type_ != SYN_STREAM &&
current_frame_type_ != SYN_REPLY &&
current_frame_type_ != HEADERS &&
- current_frame_type_ != PUSH_PROMISE) {
+ current_frame_type_ != PUSH_PROMISE &&
+ current_frame_type_ != CONTINUATION) {
LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock.";
}
- size_t process_bytes = std::min(data_len, remaining_data_length_);
- if (process_bytes > 0) {
- if (enable_compression_) {
+ size_t process_bytes = std::min(
+ data_len, remaining_data_length_ - remaining_padding_payload_length_);
+ if (is_hpack_header_block) {
+ if (!GetHpackDecoder()->HandleControlFrameHeadersData(
+ current_frame_stream_id_, data, process_bytes)) {
+ // TODO(jgraettinger): Finer-grained HPACK error codes.
+ set_error(SPDY_DECOMPRESS_FAILURE);
+ processed_successfully = false;
+ }
+ } else if (process_bytes > 0) {
+ if (enable_compression_ && protocol_version() <= SPDY3) {
processed_successfully = IncrementallyDecompressControlFrameHeaderData(
current_frame_stream_id_, data, process_bytes);
} else {
processed_successfully = IncrementallyDeliverControlFrameHeaderData(
current_frame_stream_id_, data, process_bytes);
}
-
- remaining_data_length_ -= process_bytes;
}
+ remaining_data_length_ -= process_bytes;
// Handle the case that there is no futher data in this frame.
- if (remaining_data_length_ == 0 && processed_successfully) {
- // The complete header block has been delivered. We send a zero-length
- // OnControlFrameHeaderData() to indicate this.
- visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0);
-
- // If this is a FIN, tell the caller.
- if (current_frame_flags_ & CONTROL_FLAG_FIN) {
- visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
+ if (remaining_data_length_ == remaining_padding_payload_length_ &&
+ processed_successfully) {
+ if (expect_continuation_ == 0) {
+ if (is_hpack_header_block) {
+ if (!GetHpackDecoder()->HandleControlFrameHeadersComplete(
+ current_frame_stream_id_)) {
+ set_error(SPDY_DECOMPRESS_FAILURE);
+ processed_successfully = false;
+ } else {
+ // TODO(jgraettinger): To be removed with migration to
+ // SpdyHeadersHandlerInterface. Serializes the HPACK block as a SPDY3
+ // block, delivered via reentrant call to
+ // ProcessControlFrameHeaderBlock().
+ DeliverHpackBlockAsSpdy3Block();
+ return process_bytes;
+ }
+ } else {
+ // The complete header block has been delivered. We send a zero-length
+ // OnControlFrameHeaderData() to indicate this.
+ visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0);
+ }
+ }
+ if (processed_successfully) {
+ CHANGE_STATE(SPDY_CONSUME_PADDING);
}
-
- CHANGE_STATE(SPDY_AUTO_RESET);
}
// Handle error.
@@ -1272,15 +1609,17 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
size_t unprocessed_bytes = std::min(data_len, remaining_data_length_);
size_t processed_bytes = 0;
+ size_t setting_size = protocol_version() <= SPDY3 ? 8 : 5;
+
// Loop over our incoming data.
while (unprocessed_bytes > 0) {
// Process up to one setting at a time.
size_t processing = std::min(
unprocessed_bytes,
- static_cast<size_t>(8 - settings_scratch_.setting_buf_len));
+ static_cast<size_t>(setting_size - settings_scratch_.setting_buf_len));
// Check if we have a complete setting in our input.
- if (processing == 8) {
+ if (processing == setting_size) {
// Parse the setting directly out of the input without buffering.
if (!ProcessSetting(data + processed_bytes)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
@@ -1294,7 +1633,7 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
settings_scratch_.setting_buf_len += processing;
// Check if we have a complete setting buffered.
- if (settings_scratch_.setting_buf_len == 8) {
+ if (settings_scratch_.setting_buf_len == setting_size) {
if (!ProcessSetting(settings_scratch_.setting_buf)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return processed_bytes;
@@ -1312,54 +1651,87 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
// Check if we're done handling this SETTINGS frame.
remaining_data_length_ -= processed_bytes;
if (remaining_data_length_ == 0) {
+ visitor_->OnSettingsEnd();
CHANGE_STATE(SPDY_AUTO_RESET);
}
return processed_bytes;
}
+void SpdyFramer::DeliverHpackBlockAsSpdy3Block() {
+ DCHECK_LT(SPDY3, protocol_version());
+ DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_);
+
+ const SpdyNameValueBlock& block = GetHpackDecoder()->decoded_block();
+ if (block.empty()) {
+ // Special-case this to make tests happy.
+ ProcessControlFrameHeaderBlock(NULL, 0, false);
+ return;
+ }
+ SpdyFrameBuilder builder(
+ GetSerializedLength(protocol_version(), &block),
+ SPDY3);
+
+ SerializeNameValueBlockWithoutCompression(&builder, block);
+ scoped_ptr<SpdyFrame> frame(builder.take());
+
+ // Preserve padding length, and reset it after the re-entrant call.
+ size_t remaining_padding = remaining_padding_payload_length_;
+
+ remaining_padding_payload_length_ = 0;
+ remaining_data_length_ = frame->size();
+
+ ProcessControlFrameHeaderBlock(frame->data(), frame->size(), false);
+
+ remaining_padding_payload_length_ = remaining_padding;
+ remaining_data_length_ = remaining_padding;
+}
+
bool SpdyFramer::ProcessSetting(const char* data) {
+ int id_field;
+ SpdySettingsIds id;
+ uint8 flags = 0;
+ uint32 value;
+
// Extract fields.
// Maintain behavior of old SPDY 2 bug with byte ordering of flags/id.
- const uint32 id_and_flags_wire = *(reinterpret_cast<const uint32*>(data));
- SettingsFlagsAndId id_and_flags =
- SettingsFlagsAndId::FromWireFormat(spdy_version_, id_and_flags_wire);
- uint8 flags = id_and_flags.flags();
- uint32 value = ntohl(*(reinterpret_cast<const uint32*>(data + 4)));
-
- // Validate id.
- switch (id_and_flags.id()) {
- case SETTINGS_UPLOAD_BANDWIDTH:
- case SETTINGS_DOWNLOAD_BANDWIDTH:
- case SETTINGS_ROUND_TRIP_TIME:
- case SETTINGS_MAX_CONCURRENT_STREAMS:
- case SETTINGS_CURRENT_CWND:
- case SETTINGS_DOWNLOAD_RETRANS_RATE:
- case SETTINGS_INITIAL_WINDOW_SIZE:
- // Valid values.
- break;
- default:
- DLOG(WARNING) << "Unknown SETTINGS ID: " << id_and_flags.id();
- return false;
+ if (protocol_version() <= SPDY3) {
+ const uint32 id_and_flags_wire = *(reinterpret_cast<const uint32*>(data));
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(protocol_version(), id_and_flags_wire);
+ id_field = id_and_flags.id();
+ flags = id_and_flags.flags();
+ value = ntohl(*(reinterpret_cast<const uint32*>(data + 4)));
+ } else {
+ id_field = *(reinterpret_cast<const uint8*>(data));
+ value = ntohl(*(reinterpret_cast<const uint32*>(data + 1)));
}
- SpdySettingsIds id = static_cast<SpdySettingsIds>(id_and_flags.id());
- // Detect duplciates.
- if (static_cast<uint32>(id) <= settings_scratch_.last_setting_id) {
- DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id
- << " in " << display_protocol_ << " SETTINGS frame "
- << "(last settikng id was "
- << settings_scratch_.last_setting_id << ").";
+ // Validate id.
+ if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) {
+ DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field;
return false;
}
- settings_scratch_.last_setting_id = id;
+ id = SpdyConstants::ParseSettingId(protocol_version(), id_field);
- // Validate flags.
- uint8 kFlagsMask = SETTINGS_FLAG_PLEASE_PERSIST | SETTINGS_FLAG_PERSISTED;
- if ((flags & ~(kFlagsMask)) != 0) {
- DLOG(WARNING) << "Unknown SETTINGS flags provided for id " << id << ": "
- << flags;
- return false;
+ if (protocol_version() <= SPDY3) {
+ // Detect duplicates.
+ if (id <= settings_scratch_.last_setting_id) {
+ DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id
+ << " in " << display_protocol_ << " SETTINGS frame "
+ << "(last setting id was "
+ << settings_scratch_.last_setting_id << ").";
+ return false;
+ }
+ settings_scratch_.last_setting_id = id;
+
+ // Validate flags.
+ uint8 kFlagsMask = SETTINGS_FLAG_PLEASE_PERSIST | SETTINGS_FLAG_PERSISTED;
+ if ((flags & ~(kFlagsMask)) != 0) {
+ DLOG(WARNING) << "Unknown SETTINGS flags provided for id " << id << ": "
+ << flags;
+ return false;
+ }
}
// Validation succeeded. Pass on to visitor.
@@ -1369,8 +1741,8 @@ bool SpdyFramer::ProcessSetting(const char* data) {
size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
size_t original_len = len;
- size_t bytes_read =
- UpdateCurrentFrameBuffer(&data, &len, remaining_data_length_);
+ size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len,
+ remaining_data_length_);
remaining_data_length_ -= bytes_read;
if (remaining_data_length_ == 0) {
SpdyFrameReader reader(current_frame_buffer_.get(),
@@ -1379,61 +1751,27 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
// Use frame-specific handlers.
switch (current_frame_type_) {
- case RST_STREAM: {
+ case PING: {
+ SpdyPingId id = 0;
+ bool is_ack = protocol_version() > SPDY3 &&
+ (current_frame_flags_ & PING_FLAG_ACK);
bool successful_read = true;
- if (spdy_version_ < 4) {
- successful_read = reader.ReadUInt31(&current_frame_stream_id_);
- DCHECK(successful_read);
- }
- SpdyRstStreamStatus status = RST_STREAM_INVALID;
- uint32 status_raw = status;
- successful_read = reader.ReadUInt32(&status_raw);
- DCHECK(successful_read);
- if (status_raw > RST_STREAM_INVALID &&
- status_raw < RST_STREAM_NUM_STATUS_CODES) {
- status = static_cast<SpdyRstStreamStatus>(status_raw);
+ if (protocol_version() <= SPDY3) {
+ uint32 id32 = 0;
+ successful_read = reader.ReadUInt32(&id32);
+ id = id32;
} else {
- // TODO(hkhalil): Probably best to OnError here, depending on
- // our interpretation of the spec. Keeping with existing liberal
- // behavior for now.
+ successful_read = reader.ReadUInt64(&id);
}
- DCHECK(reader.IsDoneReading());
- visitor_->OnRstStream(current_frame_stream_id_, status);
- }
- break;
- case PING: {
- SpdyPingId id = 0;
- bool successful_read = reader.ReadUInt32(&id);
DCHECK(successful_read);
DCHECK(reader.IsDoneReading());
- visitor_->OnPing(id);
- }
- break;
- case GOAWAY: {
- bool successful_read = reader.ReadUInt31(&current_frame_stream_id_);
- DCHECK(successful_read);
- SpdyGoAwayStatus status = GOAWAY_OK;
- if (spdy_version_ >= 3) {
- uint32 status_raw = GOAWAY_OK;
- successful_read = reader.ReadUInt32(&status_raw);
- DCHECK(successful_read);
- if (status_raw >= GOAWAY_OK &&
- status_raw < static_cast<uint32>(GOAWAY_NUM_STATUS_CODES)) {
- status = static_cast<SpdyGoAwayStatus>(status_raw);
- } else {
- // TODO(hkhalil): Probably best to OnError here, depending on
- // our interpretation of the spec. Keeping with existing liberal
- // behavior for now.
- }
- }
- DCHECK(reader.IsDoneReading());
- visitor_->OnGoAway(current_frame_stream_id_, status);
+ visitor_->OnPing(id, is_ack);
}
break;
case WINDOW_UPDATE: {
uint32 delta_window_size = 0;
bool successful_read = true;
- if (spdy_version_ < 4) {
+ if (protocol_version() <= SPDY3) {
successful_read = reader.ReadUInt31(&current_frame_stream_id_);
DCHECK(successful_read);
}
@@ -1445,11 +1783,18 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
}
break;
case BLOCKED: {
- DCHECK_LE(4, protocol_version());
+ DCHECK_LT(SPDY3, protocol_version());
DCHECK(reader.IsDoneReading());
visitor_->OnBlocked(current_frame_stream_id_);
}
break;
+ case PRIORITY: {
+ DCHECK_LT(SPDY3, protocol_version());
+ // TODO(hkhalil): Process PRIORITY frames rather than ignore them.
+ reader.Seek(5);
+ DCHECK(reader.IsDoneReading());
+ }
+ break;
default:
// Unreachable.
LOG(FATAL) << "Unhandled control frame " << current_frame_type_;
@@ -1460,29 +1805,362 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
return original_len - len;
}
-size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) {
+size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) {
+ if (len == 0) {
+ return 0;
+ }
+ // Clamp to the actual remaining payload.
+ if (len > remaining_data_length_) {
+ len = remaining_data_length_;
+ }
+ size_t original_len = len;
+
+ // Check if we had already read enough bytes to parse the GOAWAY header.
+ const size_t header_size = GetGoAwayMinimumSize();
+ size_t unread_header_bytes = header_size - current_frame_buffer_length_;
+ bool already_parsed_header = (unread_header_bytes == 0);
+ if (!already_parsed_header) {
+ // Buffer the new GOAWAY header bytes we got.
+ UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes);
+
+ // Do we have enough to parse the constant size GOAWAY header?
+ if (current_frame_buffer_length_ == header_size) {
+ // Parse out the last good stream id.
+ SpdyFrameReader reader(current_frame_buffer_.get(),
+ current_frame_buffer_length_);
+ reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header.
+ bool successful_read = reader.ReadUInt31(&current_frame_stream_id_);
+ DCHECK(successful_read);
+
+ // In SPDYv3 and up, frames also specify a status code - parse it out.
+ SpdyGoAwayStatus status = GOAWAY_OK;
+ if (protocol_version() >= SPDY3) {
+ uint32 status_raw = GOAWAY_OK;
+ successful_read = reader.ReadUInt32(&status_raw);
+ DCHECK(successful_read);
+ if (SpdyConstants::IsValidGoAwayStatus(protocol_version(),
+ status_raw)) {
+ status = SpdyConstants::ParseGoAwayStatus(protocol_version(),
+ status_raw);
+ } else {
+ DCHECK(false);
+ // Throw an error for SPDY4+, keep liberal behavior
+ // for earlier versions.
+ if (protocol_version() > SPDY3) {
+ DLOG(WARNING) << "Invalid GO_AWAY status " << status_raw;
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ }
+ }
+ // Finished parsing the GOAWAY header, call frame handler.
+ visitor_->OnGoAway(current_frame_stream_id_, status);
+ }
+ }
+
+ // Handle remaining data as opaque.
+ bool processed_successfully = true;
if (len > 0) {
- // Clamp to the actual remaining payload.
- if (len > remaining_data_length_) {
- len = remaining_data_length_;
- }
- bool processed_succesfully = visitor_->OnCredentialFrameData(data, len);
- remaining_data_length_ -= len;
- if (!processed_succesfully) {
- set_error(SPDY_CREDENTIAL_FRAME_CORRUPT);
- } else if (remaining_data_length_ == 0) {
- visitor_->OnCredentialFrameData(NULL, 0);
- CHANGE_STATE(SPDY_AUTO_RESET);
+ processed_successfully = visitor_->OnGoAwayFrameData(data, len);
+ }
+ remaining_data_length_ -= original_len;
+ if (!processed_successfully) {
+ set_error(SPDY_GOAWAY_FRAME_CORRUPT);
+ } else if (remaining_data_length_ == 0) {
+ // Signal that there is not more opaque data.
+ visitor_->OnGoAwayFrameData(NULL, 0);
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+ return original_len;
+}
+
+size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) {
+ if (len == 0) {
+ return 0;
+ }
+ // Clamp to the actual remaining payload.
+ if (len > remaining_data_length_) {
+ len = remaining_data_length_;
+ }
+ size_t original_len = len;
+
+ // Check if we had already read enough bytes to parse the fixed-length portion
+ // of the RST_STREAM frame.
+ const size_t header_size = GetRstStreamMinimumSize();
+ size_t unread_header_bytes = header_size - current_frame_buffer_length_;
+ bool already_parsed_header = (unread_header_bytes == 0);
+ if (!already_parsed_header) {
+ // Buffer the new RST_STREAM header bytes we got.
+ UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes);
+
+ // Do we have enough to parse the constant size RST_STREAM header?
+ if (current_frame_buffer_length_ == header_size) {
+ // Parse out the last good stream id.
+ SpdyFrameReader reader(current_frame_buffer_.get(),
+ current_frame_buffer_length_);
+ reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header.
+ if (protocol_version() <= SPDY3) {
+ bool successful_read = reader.ReadUInt31(&current_frame_stream_id_);
+ DCHECK(successful_read);
+ }
+
+ SpdyRstStreamStatus status = RST_STREAM_INVALID;
+ uint32 status_raw = status;
+ bool successful_read = reader.ReadUInt32(&status_raw);
+ DCHECK(successful_read);
+ if (SpdyConstants::IsValidRstStreamStatus(protocol_version(),
+ status_raw)) {
+ status = static_cast<SpdyRstStreamStatus>(status_raw);
+ } else {
+ // Throw an error for SPDY4+, keep liberal behavior
+ // for earlier versions.
+ if (protocol_version() > SPDY3) {
+ DLOG(WARNING) << "Invalid RST_STREAM status " << status_raw;
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ }
+ // Finished parsing the RST_STREAM header, call frame handler.
+ visitor_->OnRstStream(current_frame_stream_id_, status);
}
}
- return len;
+
+ // Handle remaining data as opaque.
+ bool processed_successfully = true;
+ if (len > 0) {
+ processed_successfully = visitor_->OnRstStreamFrameData(data, len);
+ }
+ remaining_data_length_ -= original_len;
+ if (!processed_successfully) {
+ set_error(SPDY_RST_STREAM_FRAME_CORRUPT);
+ } else if (remaining_data_length_ == 0) {
+ // Signal that there is not more opaque data.
+ visitor_->OnRstStreamFrameData(NULL, 0);
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+ return original_len;
}
-size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
+size_t SpdyFramer::ProcessAltSvcFramePayload(const char* data, size_t len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ // Clamp to the actual remaining payload.
+ len = std::min(len, remaining_data_length_);
+
+ size_t processed_bytes = 0;
+ size_t processing = 0;
+ size_t bytes_remaining;
+ char* buffer;
+ size_t* buffer_len;
+
+ while (len > 0) {
+ if (altsvc_scratch_.pid_len == 0) {
+ // The size of the frame up to the PID_LEN field.
+ size_t fixed_len_portion = GetAltSvcMinimumSize() - 1;
+ bytes_remaining = fixed_len_portion - current_frame_buffer_length_;
+ processing = std::min(len, bytes_remaining);
+ // Buffer the new ALTSVC bytes we got.
+ UpdateCurrentFrameBuffer(&data, &len, processing);
+
+ // Do we have enough to parse the length of the protocol id?
+ if (current_frame_buffer_length_ == fixed_len_portion) {
+ // Parse out the max age, port, and pid_len.
+ SpdyFrameReader reader(current_frame_buffer_.get(),
+ current_frame_buffer_length_);
+ reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header.
+ bool successful_read = reader.ReadUInt32(&altsvc_scratch_.max_age);
+ reader.ReadUInt16(&altsvc_scratch_.port);
+ reader.Seek(1); // Reserved byte.
+ successful_read = successful_read &&
+ reader.ReadUInt8(&altsvc_scratch_.pid_len);
+ DCHECK(successful_read);
+ // Sanity check length value.
+ if (GetAltSvcMinimumSize() + altsvc_scratch_.pid_len >=
+ current_frame_length_) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ altsvc_scratch_.protocol_id.reset(
+ new char[size_t(altsvc_scratch_.pid_len)]);
+ }
+ processed_bytes += processing;
+ continue;
+ } else if (altsvc_scratch_.pid_buf_len < altsvc_scratch_.pid_len) {
+ // Buffer protocol id field as in comes in.
+ buffer = altsvc_scratch_.protocol_id.get();
+ buffer_len = &altsvc_scratch_.pid_buf_len;
+ bytes_remaining = altsvc_scratch_.pid_len - altsvc_scratch_.pid_buf_len;
+ } else if (altsvc_scratch_.host_len == 0) {
+ // Parse out the host length.
+ processing = 1;
+ altsvc_scratch_.host_len = *reinterpret_cast<const uint8*>(data);
+ // Sanity check length value.
+ if (GetAltSvcMinimumSize() + altsvc_scratch_.pid_len +
+ altsvc_scratch_.host_len > current_frame_length_) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ altsvc_scratch_.host.reset(new char[altsvc_scratch_.host_len]);
+ // Once we have host length, we can also determine the origin length
+ // by process of elimination.
+ altsvc_scratch_.origin_len = current_frame_length_ -
+ GetAltSvcMinimumSize() -
+ altsvc_scratch_.pid_len -
+ altsvc_scratch_.host_len;
+ if (altsvc_scratch_.origin_len > 0) {
+ altsvc_scratch_.origin.reset(new char[altsvc_scratch_.origin_len]);
+ }
+ data += processing;
+ processed_bytes += processing;
+ len -= processing;
+ continue;
+ } else if (altsvc_scratch_.host_buf_len < altsvc_scratch_.host_len) {
+ // Buffer host field as it comes in.
+ // TODO(mlavan): check formatting for host and origin
+ buffer = altsvc_scratch_.host.get();
+ buffer_len = &altsvc_scratch_.host_buf_len;
+ bytes_remaining = altsvc_scratch_.host_len - altsvc_scratch_.host_buf_len;
+ } else {
+ // Buffer (optional) origin field as it comes in.
+ if (altsvc_scratch_.origin_len <= 0) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ buffer = altsvc_scratch_.origin.get();
+ buffer_len = &altsvc_scratch_.origin_buf_len;
+ bytes_remaining = remaining_data_length_ -
+ processed_bytes -
+ altsvc_scratch_.origin_buf_len;
+ if (len > bytes_remaining) {
+ // This is our last field; there shouldn't be any more bytes.
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return 0;
+ }
+ }
+
+ // Copy data bytes into the appropriate field.
+ processing = std::min(len, bytes_remaining);
+ memcpy(buffer + *buffer_len,
+ data,
+ processing);
+ *buffer_len += processing;
+ data += processing;
+ processed_bytes += processing;
+ len -= processing;
+ }
+
+ remaining_data_length_ -= processed_bytes;
+ if (remaining_data_length_ == 0) {
+ visitor_->OnAltSvc(current_frame_stream_id_,
+ altsvc_scratch_.max_age,
+ altsvc_scratch_.port,
+ StringPiece(altsvc_scratch_.protocol_id.get(),
+ altsvc_scratch_.pid_len),
+ StringPiece(altsvc_scratch_.host.get(),
+ altsvc_scratch_.host_len),
+ StringPiece(altsvc_scratch_.origin.get(),
+ altsvc_scratch_.origin_len));
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+
+ return processed_bytes;
+}
+
+size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) {
+ DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_);
+
size_t original_len = len;
+ if (remaining_padding_length_fields_ == 0) {
+ DCHECK_EQ(remaining_padding_payload_length_, 0u);
+ bool pad_low = false;
+ bool pad_high = false;
+ if (current_frame_flags_ & DATA_FLAG_PAD_LOW) {
+ pad_low = true;
+ ++remaining_padding_length_fields_;
+ }
+ if (current_frame_flags_ & DATA_FLAG_PAD_HIGH) {
+ pad_high = true;
+ ++remaining_padding_length_fields_;
+ }
+ if ((pad_high && !pad_low) ||
+ remaining_data_length_ < remaining_padding_length_fields_) {
+ set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
+ return 0;
+ }
+ }
- if (remaining_data_length_ > 0) {
- size_t amount_to_forward = std::min(remaining_data_length_, len);
+ // Parse the padding length.
+ while (len != 0 && remaining_padding_length_fields_ != 0) {
+ remaining_padding_payload_length_ =
+ (remaining_padding_payload_length_ << 8) +
+ *reinterpret_cast<const uint8*>(data);
+ ++data;
+ --len;
+ --remaining_padding_length_fields_;
+ --remaining_data_length_;
+ }
+
+ if (remaining_padding_length_fields_ == 0) {
+ if (remaining_padding_payload_length_ > remaining_data_length_) {
+ set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
+ return 0;
+ }
+ if (current_frame_type_ == DATA) {
+ CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
+ } else {
+ DCHECK(current_frame_type_ == HEADERS ||
+ current_frame_type_ == PUSH_PROMISE ||
+ current_frame_type_ == CONTINUATION ||
+ current_frame_type_ == SYN_STREAM ||
+ current_frame_type_ == SYN_REPLY)
+ << current_frame_type_;
+ CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ }
+ }
+ return original_len - len;
+}
+
+size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) {
+ DCHECK_EQ(SPDY_CONSUME_PADDING, state_);
+
+ size_t original_len = len;
+ if (remaining_padding_payload_length_ > 0) {
+ DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_);
+ size_t amount_to_discard = std::min(remaining_padding_payload_length_, len);
+ if (current_frame_type_ == DATA && amount_to_discard > 0) {
+ // The visitor needs to know about padding so it can send window updates.
+ // Communicate the padding to the visitor through a NULL data pointer,
+ // with a nonzero size.
+ visitor_->OnStreamFrameData(
+ current_frame_stream_id_, NULL, amount_to_discard, false);
+ }
+ data += amount_to_discard;
+ len -= amount_to_discard;
+ remaining_padding_payload_length_ -= amount_to_discard;
+ remaining_data_length_ -= amount_to_discard;
+ }
+
+ if (remaining_data_length_ == 0) {
+ // If the FIN flag is set, or this ends a header block which set FIN,
+ // inform the visitor of EOF via a 0-length data frame.
+ if (expect_continuation_ == 0 &&
+ ((current_frame_flags_ & CONTROL_FLAG_FIN) != 0 ||
+ end_stream_when_done_)) {
+ end_stream_when_done_ = false;
+ visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
+ }
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+ return original_len - len;
+}
+
+size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
+ size_t original_len = len;
+ if (remaining_data_length_ - remaining_padding_payload_length_ > 0) {
+ size_t amount_to_forward = std::min(
+ remaining_data_length_ - remaining_padding_payload_length_, len);
if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) {
// Only inform the visitor if there is data.
if (amount_to_forward) {
@@ -1493,12 +2171,21 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
data += amount_to_forward;
len -= amount_to_forward;
remaining_data_length_ -= amount_to_forward;
+ }
- // If the FIN flag is set, and there is no more data in this data
- // frame, inform the visitor of EOF via a 0-length data frame.
- if (!remaining_data_length_ && current_frame_flags_ & DATA_FLAG_FIN) {
- visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
- }
+ if (remaining_data_length_ == remaining_padding_payload_length_) {
+ CHANGE_STATE(SPDY_CONSUME_PADDING);
+ }
+ return original_len - len;
+}
+
+size_t SpdyFramer::ProcessIgnoredControlFramePayload(/*const char* data,*/
+ size_t len) {
+ size_t original_len = len;
+ if (remaining_data_length_ > 0) {
+ size_t amount_to_ignore = std::min(remaining_data_length_, len);
+ len -= amount_to_ignore;
+ remaining_data_length_ -= amount_to_ignore;
}
if (remaining_data_length_ == 0) {
@@ -1514,7 +2201,7 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
// Read number of headers.
uint32 num_headers;
- if (spdy_version_ < 3) {
+ if (protocol_version() <= SPDY2) {
uint16 temp;
if (!reader.ReadUInt16(&temp)) {
DVLOG(1) << "Unable to read number of headers.";
@@ -1533,7 +2220,7 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
base::StringPiece temp;
// Read header name.
- if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp)
: !reader.ReadStringPiece32(&temp)) {
DVLOG(1) << "Unable to read header name (" << index + 1 << " of "
<< num_headers << ").";
@@ -1542,7 +2229,7 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
std::string name = temp.as_string();
// Read header value.
- if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp)
: !reader.ReadStringPiece32(&temp)) {
DVLOG(1) << "Unable to read header value (" << index + 1 << " of "
<< num_headers << ").";
@@ -1563,97 +2250,91 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
return reader.GetBytesConsumed();
}
-/* static */
-bool SpdyFramer::ParseCredentialData(const char* data, size_t len,
- SpdyCredential* credential) {
- DCHECK(credential);
-
- SpdyFrameReader parser(data, len);
- base::StringPiece temp;
- if (!parser.ReadUInt16(&credential->slot)) {
- return false;
+SpdySerializedFrame* SpdyFramer::SerializeData(
+ const SpdyDataIR& data_ir) const {
+ uint8 flags = DATA_FLAG_NONE;
+ if (data_ir.fin()) {
+ flags = DATA_FLAG_FIN;
}
- if (!parser.ReadStringPiece32(&temp)) {
- return false;
- }
- credential->proof = temp.as_string();
+ if (protocol_version() > SPDY3) {
+ int num_padding_fields = 0;
+ if (data_ir.pad_low()) {
+ flags |= DATA_FLAG_PAD_LOW;
+ ++num_padding_fields;
+ }
+ if (data_ir.pad_high()) {
+ flags |= DATA_FLAG_PAD_HIGH;
+ ++num_padding_fields;
+ }
- while (!parser.IsDoneReading()) {
- if (!parser.ReadStringPiece32(&temp)) {
- return false;
+ const size_t size_with_padding = num_padding_fields +
+ data_ir.data().length() + data_ir.padding_payload_len() +
+ GetDataFrameMinimumSize();
+ SpdyFrameBuilder builder(size_with_padding, protocol_version());
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags);
+ if (data_ir.pad_high()) {
+ builder.WriteUInt8(data_ir.padding_payload_len() >> 8);
+ }
+ if (data_ir.pad_low()) {
+ builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
- credential->certs.push_back(temp.as_string());
+ builder.WriteBytes(data_ir.data().data(), data_ir.data().length());
+ if (data_ir.padding_payload_len() > 0) {
+ string padding = string(data_ir.padding_payload_len(), '0');
+ builder.WriteBytes(padding.data(), padding.length());
+ }
+ DCHECK_EQ(size_with_padding, builder.length());
+ return builder.take();
+ } else {
+ const size_t size = GetDataFrameMinimumSize() + data_ir.data().length();
+ SpdyFrameBuilder builder(size, protocol_version());
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags);
+ builder.WriteBytes(data_ir.data().data(), data_ir.data().length());
+ DCHECK_EQ(size, builder.length());
+ return builder.take();
}
- return true;
-}
-
-SpdyFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
- const char* data,
- uint32 len, SpdyDataFlags flags) const {
- DCHECK_EQ(0, flags & (!DATA_FLAG_FIN));
-
- SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
- data_ir.set_fin(flags & DATA_FLAG_FIN);
- return SerializeData(data_ir);
}
-SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& data) const {
- const size_t kSize = GetDataFrameMinimumSize() + data.data().length();
-
- SpdyDataFlags flags = DATA_FLAG_NONE;
- if (data.fin()) {
+SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
+ const SpdyDataIR& data_ir) const {
+ uint8 flags = DATA_FLAG_NONE;
+ if (data_ir.fin()) {
flags = DATA_FLAG_FIN;
}
- SpdyFrameBuilder builder(kSize);
- builder.WriteDataFrameHeader(*this, data.stream_id(), flags);
- builder.WriteBytes(data.data().data(), data.data().length());
- DCHECK_EQ(kSize, builder.length());
- return builder.take();
-}
-
-SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeader(
- const SpdyDataIR& data) const {
- const size_t kSize = GetDataFrameMinimumSize();
-
- SpdyDataFlags flags = DATA_FLAG_NONE;
- if (data.fin()) {
- flags = DATA_FLAG_FIN;
+ size_t frame_size = GetDataFrameMinimumSize();
+ size_t num_padding_fields = 0;
+ if (protocol_version() > SPDY3) {
+ if (data_ir.pad_low()) {
+ flags |= DATA_FLAG_PAD_LOW;
+ ++num_padding_fields;
+ }
+ if (data_ir.pad_high()) {
+ flags |= DATA_FLAG_PAD_HIGH;
+ ++num_padding_fields;
+ }
+ frame_size += num_padding_fields;
}
- SpdyFrameBuilder builder(kSize);
- builder.WriteDataFrameHeader(*this, data.stream_id(), flags);
- if (protocol_version() < 4) {
- builder.OverwriteLength(*this, data.data().length());
+ SpdyFrameBuilder builder(frame_size, protocol_version());
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags);
+ if (protocol_version() > SPDY3) {
+ if (data_ir.pad_high()) {
+ builder.WriteUInt8(data_ir.padding_payload_len() >> 8);
+ }
+ if (data_ir.pad_low()) {
+ builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
+ }
+ builder.OverwriteLength(*this, num_padding_fields +
+ data_ir.data().length() + data_ir.padding_payload_len());
} else {
- builder.OverwriteLength(*this, data.data().length() + kSize);
+ builder.OverwriteLength(*this, data_ir.data().length());
}
- DCHECK_EQ(kSize, builder.length());
+ DCHECK_EQ(frame_size, builder.length());
return builder.take();
}
-SpdyFrame* SpdyFramer::CreateSynStream(
- SpdyStreamId stream_id,
- SpdyStreamId associated_stream_id,
- SpdyPriority priority,
- uint8 credential_slot,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* headers) {
- DCHECK_EQ(0, flags & ~CONTROL_FLAG_FIN & ~CONTROL_FLAG_UNIDIRECTIONAL);
-
- SpdySynStreamIR syn_stream(stream_id);
- syn_stream.set_associated_to_stream_id(associated_stream_id);
- syn_stream.set_priority(priority);
- syn_stream.set_slot(credential_slot);
- syn_stream.set_fin((flags & CONTROL_FLAG_FIN) != 0);
- syn_stream.set_unidirectional((flags & CONTROL_FLAG_UNIDIRECTIONAL) != 0);
- // TODO(hkhalil): Avoid copy here.
- *(syn_stream.GetMutableNameValueBlock()) = *headers;
-
- return SerializeSynStream(syn_stream);
-}
-
SpdySerializedFrame* SpdyFramer::SerializeSynStream(
const SpdySynStreamIR& syn_stream) {
uint8 flags = 0;
@@ -1661,37 +2342,69 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
flags |= CONTROL_FLAG_FIN;
}
if (syn_stream.unidirectional()) {
+ // TODO(hkhalil): invalid for HTTP2.
flags |= CONTROL_FLAG_UNIDIRECTIONAL;
}
+ // In SPDY >= 4, SYN_STREAM frames are HEADERS frames, but for now
+ // we never expect to have to overflow into a CONTINUATION frame.
+ if (protocol_version() > SPDY3) {
+ flags |= HEADERS_FLAG_PRIORITY;
+ flags |= HEADERS_FLAG_END_HEADERS;
+ }
+
+ // Sanitize priority.
+ uint8 priority = syn_stream.priority();
+ if (priority > GetLowestPriority()) {
+ DLOG(DFATAL) << "Priority out-of-bounds.";
+ priority = GetLowestPriority();
+ }
// The size of this frame, including variable-length name-value block.
- const size_t size = GetSynStreamMinimumSize()
- + GetSerializedLength(syn_stream.name_value_block());
+ size_t size = GetSynStreamMinimumSize();
- SpdyFrameBuilder builder(size);
- if (spdy_version_ < 4) {
+ string hpack_encoding;
+ if (protocol_version() > SPDY3) {
+ if (enable_compression_) {
+ GetHpackEncoder()->EncodeHeaderSet(
+ syn_stream.name_value_block(), &hpack_encoding);
+ } else {
+ GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+ syn_stream.name_value_block(), &hpack_encoding);
+ }
+ size += hpack_encoding.size();
+ } else {
+ size += GetSerializedLength(syn_stream.name_value_block());
+ }
+
+ SpdyFrameBuilder builder(size, protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, SYN_STREAM, flags);
builder.WriteUInt32(syn_stream.stream_id());
+ builder.WriteUInt32(syn_stream.associated_to_stream_id());
+ builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5));
+ builder.WriteUInt8(0); // Unused byte where credential slot used to be.
} else {
- builder.WriteFramePrefix(*this,
- SYN_STREAM,
- flags,
- syn_stream.stream_id());
+ builder.BeginNewFrame(*this,
+ HEADERS,
+ flags,
+ syn_stream.stream_id());
+ // TODO(jgraettinger): Plumb priorities and stream dependencies.
+ builder.WriteUInt32(0); // Non-exclusive bit and root stream ID.
+ builder.WriteUInt8(MapPriorityToWeight(priority));
}
- builder.WriteUInt32(syn_stream.associated_to_stream_id());
- uint8 priority = syn_stream.priority();
- if (priority > GetLowestPriority()) {
- DLOG(DFATAL) << "Priority out-of-bounds.";
- priority = GetLowestPriority();
- }
- builder.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5));
- builder.WriteUInt8(syn_stream.slot());
DCHECK_EQ(GetSynStreamMinimumSize(), builder.length());
- SerializeNameValueBlock(&builder, syn_stream);
+ if (protocol_version() > SPDY3) {
+ builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
+ } else {
+ SerializeNameValueBlock(&builder, syn_stream);
+ }
if (debug_visitor_) {
- const size_t payload_len = GetSerializedLength(
- protocol_version(), &(syn_stream.name_value_block()));
+ const size_t payload_len = protocol_version() > SPDY3 ?
+ hpack_encoding.size() :
+ GetSerializedLength(protocol_version(),
+ &(syn_stream.name_value_block()));
+ // SPDY 4 reports this compression as a SYN_STREAM compression.
debug_visitor_->OnSendCompressedFrame(syn_stream.stream_id(),
SYN_STREAM,
payload_len,
@@ -1701,50 +2414,60 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
return builder.take();
}
-SpdyFrame* SpdyFramer::CreateSynReply(
- SpdyStreamId stream_id,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* headers) {
- DCHECK_EQ(0, flags & ~CONTROL_FLAG_FIN);
-
- SpdySynReplyIR syn_reply(stream_id);
- syn_reply.set_fin(flags & CONTROL_FLAG_FIN);
- // TODO(hkhalil): Avoid copy here.
- *(syn_reply.GetMutableNameValueBlock()) = *headers;
-
- return SerializeSynReply(syn_reply);
-}
-
SpdySerializedFrame* SpdyFramer::SerializeSynReply(
const SpdySynReplyIR& syn_reply) {
uint8 flags = 0;
if (syn_reply.fin()) {
flags |= CONTROL_FLAG_FIN;
}
+ // In SPDY >= 4, SYN_REPLY frames are HEADERS frames, but for now
+ // we never expect to have to overflow into a CONTINUATION frame.
+ if (protocol_version() > SPDY3) {
+ flags |= HEADERS_FLAG_END_HEADERS;
+ }
// The size of this frame, including variable-length name-value block.
- size_t size = GetSynReplyMinimumSize()
- + GetSerializedLength(syn_reply.name_value_block());
+ size_t size = GetSynReplyMinimumSize();
- SpdyFrameBuilder builder(size);
- if (spdy_version_ < 4) {
+ string hpack_encoding;
+ if (protocol_version() > SPDY3) {
+ if (enable_compression_) {
+ GetHpackEncoder()->EncodeHeaderSet(
+ syn_reply.name_value_block(), &hpack_encoding);
+ } else {
+ GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+ syn_reply.name_value_block(), &hpack_encoding);
+ }
+ size += hpack_encoding.size();
+ } else {
+ size += GetSerializedLength(syn_reply.name_value_block());
+ }
+
+ SpdyFrameBuilder builder(size, protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, SYN_REPLY, flags);
builder.WriteUInt32(syn_reply.stream_id());
} else {
- builder.WriteFramePrefix(*this,
- SYN_REPLY,
- flags,
- syn_reply.stream_id());
+ builder.BeginNewFrame(*this,
+ HEADERS,
+ flags,
+ syn_reply.stream_id());
}
- if (protocol_version() < 3) {
+ if (protocol_version() < SPDY3) {
builder.WriteUInt16(0); // Unused.
}
DCHECK_EQ(GetSynReplyMinimumSize(), builder.length());
- SerializeNameValueBlock(&builder, syn_reply);
+ if (protocol_version() > SPDY3) {
+ builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
+ } else {
+ SerializeNameValueBlock(&builder, syn_reply);
+ }
if (debug_visitor_) {
- const size_t payload_len = GetSerializedLength(
- protocol_version(), &(syn_reply.name_value_block()));
+ const size_t payload_len = protocol_version() > SPDY3 ?
+ hpack_encoding.size() :
+ GetSerializedLength(protocol_version(),
+ &(syn_reply.name_value_block()));
debug_visitor_->OnSendCompressedFrame(syn_reply.stream_id(),
SYN_REPLY,
payload_len,
@@ -1754,142 +2477,152 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply(
return builder.take();
}
-SpdyFrame* SpdyFramer::CreateRstStream(
- SpdyStreamId stream_id,
- SpdyRstStreamStatus status) const {
- SpdyRstStreamIR rst_stream(stream_id, status);
- return SerializeRstStream(rst_stream);
-}
-
SpdySerializedFrame* SpdyFramer::SerializeRstStream(
const SpdyRstStreamIR& rst_stream) const {
- SpdyFrameBuilder builder(GetRstStreamSize());
- if (spdy_version_ < 4) {
+ // TODO(jgraettinger): For now, Chromium will support parsing RST_STREAM
+ // payloads, but will not emit them. SPDY4 is used for draft HTTP/2,
+ // which doesn't currently include RST_STREAM payloads. GFE flags have been
+ // commented but left in place to simplify future patching.
+ // Compute the output buffer size, taking opaque data into account.
+ uint16 expected_length = GetRstStreamMinimumSize();
+ if (protocol_version() > SPDY3) {
+ expected_length += rst_stream.description().size();
+ }
+ SpdyFrameBuilder builder(expected_length, protocol_version());
+
+ // Serialize the RST_STREAM frame.
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, RST_STREAM, 0);
builder.WriteUInt32(rst_stream.stream_id());
} else {
- builder.WriteFramePrefix(*this,
- RST_STREAM,
- 0,
- rst_stream.stream_id());
+ builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id());
}
+
builder.WriteUInt32(rst_stream.status());
- DCHECK_EQ(GetRstStreamSize(), builder.length());
- return builder.take();
-}
-SpdyFrame* SpdyFramer::CreateSettings(
- const SettingsMap& values) const {
- SpdySettingsIR settings;
- for (SettingsMap::const_iterator it = values.begin();
- it != values.end();
- ++it) {
- settings.AddSetting(it->first,
- (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
- (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
- it->second.second);
+ // In SPDY4 and up, RST_STREAM frames may also specify opaque data.
+ if (protocol_version() > SPDY3 && rst_stream.description().size() > 0) {
+ builder.WriteBytes(rst_stream.description().data(),
+ rst_stream.description().size());
}
- return SerializeSettings(settings);
+
+ DCHECK_EQ(expected_length, builder.length());
+ return builder.take();
}
SpdySerializedFrame* SpdyFramer::SerializeSettings(
const SpdySettingsIR& settings) const {
uint8 flags = 0;
- if (settings.clear_settings()) {
- flags |= SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS;
+
+ if (protocol_version() <= SPDY3) {
+ if (settings.clear_settings()) {
+ flags |= SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS;
+ }
+ } else {
+ if (settings.is_ack()) {
+ flags |= SETTINGS_FLAG_ACK;
+ }
}
const SpdySettingsIR::ValueMap* values = &(settings.values());
+ size_t setting_size = (protocol_version() <= SPDY3 ? 8 : 5);
// Size, in bytes, of this SETTINGS frame.
- const size_t size = GetSettingsMinimumSize() + (values->size() * 8);
-
- SpdyFrameBuilder builder(size);
- if (spdy_version_ < 4) {
+ const size_t size = GetSettingsMinimumSize() +
+ (values->size() * setting_size);
+ SpdyFrameBuilder builder(size, protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, SETTINGS, flags);
} else {
- builder.WriteFramePrefix(*this, SETTINGS, flags, 0);
+ builder.BeginNewFrame(*this, SETTINGS, flags, 0);
+ }
+
+ // If this is an ACK, payload should be empty.
+ if (protocol_version() > SPDY3 && settings.is_ack()) {
+ return builder.take();
+ }
+
+ if (protocol_version() <= SPDY3) {
+ builder.WriteUInt32(values->size());
}
- builder.WriteUInt32(values->size());
DCHECK_EQ(GetSettingsMinimumSize(), builder.length());
for (SpdySettingsIR::ValueMap::const_iterator it = values->begin();
it != values->end();
++it) {
- uint8 setting_flags = 0;
- if (it->second.persist_value) {
- setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST;
- }
- if (it->second.persisted) {
- setting_flags |= SETTINGS_FLAG_PERSISTED;
+ if (protocol_version() <= SPDY3) {
+ uint8 setting_flags = 0;
+ if (it->second.persist_value) {
+ setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST;
+ }
+ if (it->second.persisted) {
+ setting_flags |= SETTINGS_FLAG_PERSISTED;
+ }
+ SettingsFlagsAndId flags_and_id(
+ setting_flags,
+ SpdyConstants::SerializeSettingId(protocol_version(), it->first));
+ uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version());
+ builder.WriteBytes(&id_and_flags_wire, 4);
+ } else {
+ builder.WriteUInt8(SpdyConstants::SerializeSettingId(protocol_version(),
+ it->first));
}
- SettingsFlagsAndId flags_and_id(setting_flags, it->first);
- uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version());
- builder.WriteBytes(&id_and_flags_wire, 4);
builder.WriteUInt32(it->second.value);
}
DCHECK_EQ(size, builder.length());
return builder.take();
}
-SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const {
- DCHECK_LE(4, protocol_version());
- SpdyFrameBuilder builder(GetBlockedSize());
- builder.WriteFramePrefix(*this, BLOCKED, kNoFlags, blocked.stream_id());
- return builder.take();
-}
-
-SpdyFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const {
- SpdyPingIR ping(unique_id);
- return SerializePing(ping);
-}
-
SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const {
- SpdyFrameBuilder builder(GetPingSize());
- if (spdy_version_ < 4) {
+ SpdyFrameBuilder builder(GetPingSize(), protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, PING, kNoFlags);
+ builder.WriteUInt32(static_cast<uint32>(ping.id()));
} else {
- builder.WriteFramePrefix(*this, PING, 0, 0);
+ uint8 flags = 0;
+ if (ping.is_ack()) {
+ flags |= PING_FLAG_ACK;
+ }
+ builder.BeginNewFrame(*this, PING, flags, 0);
+ builder.WriteUInt64(ping.id());
}
- builder.WriteUInt32(ping.id());
DCHECK_EQ(GetPingSize(), builder.length());
return builder.take();
}
-SpdyFrame* SpdyFramer::CreateGoAway(
- SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) const {
- SpdyGoAwayIR goaway(last_accepted_stream_id, status);
- return SerializeGoAway(goaway);
-}
-
SpdySerializedFrame* SpdyFramer::SerializeGoAway(
const SpdyGoAwayIR& goaway) const {
- SpdyFrameBuilder builder(GetGoAwaySize());
- if (spdy_version_ < 4) {
+
+ // Compute the output buffer size, take opaque data into account.
+ uint16 expected_length = GetGoAwayMinimumSize();
+ if (protocol_version() > SPDY3) {
+ expected_length += goaway.description().size();
+ }
+ SpdyFrameBuilder builder(expected_length, protocol_version());
+
+ // Serialize the GOAWAY frame.
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags);
} else {
- builder.WriteFramePrefix(*this, GOAWAY, 0, 0);
+ builder.BeginNewFrame(*this, GOAWAY, 0, 0);
}
+
+ // GOAWAY frames specify the last good stream id for all SPDY versions.
builder.WriteUInt32(goaway.last_good_stream_id());
- if (protocol_version() >= 3) {
- builder.WriteUInt32(goaway.status());
- }
- DCHECK_EQ(GetGoAwaySize(), builder.length());
- return builder.take();
-}
-SpdyFrame* SpdyFramer::CreateHeaders(
- SpdyStreamId stream_id,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* header_block) {
- // Basically the same as CreateSynReply().
- DCHECK_EQ(0, flags & (!CONTROL_FLAG_FIN));
+ // In SPDY3 and up, GOAWAY frames also specify the error status code.
+ if (protocol_version() >= SPDY3) {
+ // TODO(jgraettinger): Merge back to server-side.
+ builder.WriteUInt32(SpdyConstants::SerializeGoAwayStatus(protocol_version(),
+ goaway.status()));
+ }
- SpdyHeadersIR headers(stream_id);
- headers.set_fin(flags & CONTROL_FLAG_FIN);
- // TODO(hkhalil): Avoid copy here.
- *(headers.GetMutableNameValueBlock()) = *header_block;
+ // In SPDY4 and up, GOAWAY frames may also specify opaque data.
+ if ((protocol_version() > SPDY3) && (goaway.description().size() > 0)) {
+ builder.WriteBytes(goaway.description().data(),
+ goaway.description().size());
+ }
- return SerializeHeaders(headers);
+ DCHECK_EQ(expected_length, builder.length());
+ return builder.take();
}
SpdySerializedFrame* SpdyFramer::SerializeHeaders(
@@ -1898,31 +2631,79 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
if (headers.fin()) {
flags |= CONTROL_FLAG_FIN;
}
+ if (protocol_version() > SPDY3) {
+ // This will get overwritten if we overflow into a CONTINUATION frame.
+ flags |= HEADERS_FLAG_END_HEADERS;
+ if (headers.has_priority()) {
+ flags |= HEADERS_FLAG_PRIORITY;
+ }
+ }
// The size of this frame, including variable-length name-value block.
- size_t size = GetHeadersMinimumSize()
- + GetSerializedLength(headers.name_value_block());
+ size_t size = GetHeadersMinimumSize();
- SpdyFrameBuilder builder(size);
- if (spdy_version_ < 4) {
+ uint32 priority = headers.priority();
+ if (headers.has_priority()) {
+ if (priority > GetLowestPriority()) {
+ DLOG(DFATAL) << "Priority out-of-bounds.";
+ priority = GetLowestPriority();
+ }
+ size += 4;
+ }
+
+ string hpack_encoding;
+ if (protocol_version() > SPDY3) {
+ if (enable_compression_) {
+ GetHpackEncoder()->EncodeHeaderSet(
+ headers.name_value_block(), &hpack_encoding);
+ } else {
+ GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+ headers.name_value_block(), &hpack_encoding);
+ }
+ size += hpack_encoding.size();
+ if (size > GetControlFrameBufferMaxSize()) {
+ size += GetNumberRequiredContinuationFrames(size) *
+ GetContinuationMinimumSize();
+ flags &= ~HEADERS_FLAG_END_HEADERS;
+ }
+ } else {
+ size += GetSerializedLength(headers.name_value_block());
+ }
+
+ SpdyFrameBuilder builder(size, protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, HEADERS, flags);
builder.WriteUInt32(headers.stream_id());
} else {
- builder.WriteFramePrefix(*this,
- HEADERS,
- flags,
- headers.stream_id());
+ builder.BeginNewFrame(*this,
+ HEADERS,
+ flags,
+ headers.stream_id());
+ if (headers.has_priority()) {
+ // TODO(jgraettinger): Plumb priorities and stream dependencies.
+ builder.WriteUInt32(0); // Non-exclusive bit and root stream ID.
+ builder.WriteUInt8(MapPriorityToWeight(priority));
+ }
}
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
builder.WriteUInt16(0); // Unused.
}
DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
- SerializeNameValueBlock(&builder, headers);
+ if (protocol_version() > SPDY3) {
+ WritePayloadWithContinuation(&builder,
+ hpack_encoding,
+ headers.stream_id(),
+ HEADERS);
+ } else {
+ SerializeNameValueBlock(&builder, headers);
+ }
if (debug_visitor_) {
- const size_t payload_len = GetSerializedLength(
- protocol_version(), &(headers.name_value_block()));
+ const size_t payload_len = protocol_version() > SPDY3 ?
+ hpack_encoding.size() :
+ GetSerializedLength(protocol_version(),
+ &(headers.name_value_block()));
debug_visitor_->OnSendCompressedFrame(headers.stream_id(),
HEADERS,
payload_len,
@@ -1932,109 +2713,149 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
return builder.take();
}
-SpdyFrame* SpdyFramer::CreateWindowUpdate(
- SpdyStreamId stream_id,
- uint32 delta_window_size) const {
- SpdyWindowUpdateIR window_update(stream_id, delta_window_size);
- return SerializeWindowUpdate(window_update);
-}
-
SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate(
const SpdyWindowUpdateIR& window_update) const {
- SpdyFrameBuilder builder(GetWindowUpdateSize());
- if (spdy_version_ < 4) {
+ SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version());
+ if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags);
builder.WriteUInt32(window_update.stream_id());
} else {
- builder.WriteFramePrefix(*this,
- WINDOW_UPDATE,
- kNoFlags,
- window_update.stream_id());
+ builder.BeginNewFrame(*this,
+ WINDOW_UPDATE,
+ kNoFlags,
+ window_update.stream_id());
}
builder.WriteUInt32(window_update.delta());
DCHECK_EQ(GetWindowUpdateSize(), builder.length());
return builder.take();
}
-// TODO(hkhalil): Gut with SpdyCredential removal.
-SpdyFrame* SpdyFramer::CreateCredentialFrame(
- const SpdyCredential& credential) const {
- SpdyCredentialIR credential_ir(credential.slot);
- credential_ir.set_proof(credential.proof);
- for (std::vector<std::string>::const_iterator cert = credential.certs.begin();
- cert != credential.certs.end();
- ++cert) {
- credential_ir.AddCertificate(*cert);
- }
- return SerializeCredential(credential_ir);
+SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const {
+ DCHECK_LT(SPDY3, protocol_version());
+ SpdyFrameBuilder builder(GetBlockedSize(), protocol_version());
+ builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id());
+ return builder.take();
}
-SpdySerializedFrame* SpdyFramer::SerializeCredential(
- const SpdyCredentialIR& credential) const {
- size_t size = GetCredentialMinimumSize();
- size += 4 + credential.proof().length(); // Room for proof.
- for (SpdyCredentialIR::CertificateList::const_iterator it =
- credential.certificates()->begin();
- it != credential.certificates()->end();
- ++it) {
- size += 4 + it->length(); // Room for certificate.
+SpdyFrame* SpdyFramer::SerializePushPromise(
+ const SpdyPushPromiseIR& push_promise) {
+ DCHECK_LT(SPDY3, protocol_version());
+ uint8 flags = 0;
+ // This will get overwritten if we overflow into a CONTINUATION frame.
+ flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
+ // The size of this frame, including variable-length name-value block.
+ size_t size = GetPushPromiseMinimumSize();
+
+ string hpack_encoding;
+ if (protocol_version() > SPDY3) {
+ if (enable_compression_) {
+ GetHpackEncoder()->EncodeHeaderSet(
+ push_promise.name_value_block(), &hpack_encoding);
+ } else {
+ GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+ push_promise.name_value_block(), &hpack_encoding);
+ }
+ size += hpack_encoding.size();
+ if (size > GetControlFrameBufferMaxSize()) {
+ size += GetNumberRequiredContinuationFrames(size) *
+ GetContinuationMinimumSize();
+ flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
+ }
+ } else {
+ size += GetSerializedLength(push_promise.name_value_block());
}
- SpdyFrameBuilder builder(size);
- if (spdy_version_ < 4) {
- builder.WriteControlFrameHeader(*this, CREDENTIAL, kNoFlags);
+ SpdyFrameBuilder builder(size, protocol_version());
+ builder.BeginNewFrame(*this,
+ PUSH_PROMISE,
+ flags,
+ push_promise.stream_id());
+ builder.WriteUInt32(push_promise.promised_stream_id());
+ DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
+
+ if (protocol_version() > SPDY3) {
+ WritePayloadWithContinuation(&builder,
+ hpack_encoding,
+ push_promise.stream_id(),
+ PUSH_PROMISE);
} else {
- builder.WriteFramePrefix(*this, CREDENTIAL, kNoFlags, 0);
- }
- builder.WriteUInt16(credential.slot());
- DCHECK_EQ(GetCredentialMinimumSize(), builder.length());
- builder.WriteStringPiece32(credential.proof());
- for (SpdyCredentialIR::CertificateList::const_iterator it =
- credential.certificates()->begin();
- it != credential.certificates()->end();
- ++it) {
- builder.WriteStringPiece32(*it);
+ SerializeNameValueBlock(&builder, push_promise);
}
- DCHECK_EQ(size, builder.length());
- return builder.take();
-}
-SpdyFrame* SpdyFramer::CreatePushPromise(
- SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id,
- const SpdyHeaderBlock* header_block) {
- SpdyPushPromiseIR push_promise(stream_id, promised_stream_id);
- // TODO(hkhalil): Avoid copy here.
- *(push_promise.GetMutableNameValueBlock()) = *header_block;
+ if (debug_visitor_) {
+ const size_t payload_len = protocol_version() > SPDY3 ?
+ hpack_encoding.size() :
+ GetSerializedLength(protocol_version(),
+ &(push_promise.name_value_block()));
+ debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(),
+ PUSH_PROMISE, payload_len, builder.length());
+ }
- return SerializePushPromise(push_promise);
+ return builder.take();
}
-SpdyFrame* SpdyFramer::SerializePushPromise(
- const SpdyPushPromiseIR& push_promise) {
- DCHECK_LE(4, protocol_version());
+// TODO(jgraettinger): This implementation is incorrect. The continuation
+// frame continues a previously-begun HPACK encoding; it doesn't begin a
+// new one. Figure out whether it makes sense to keep SerializeContinuation().
+SpdyFrame* SpdyFramer::SerializeContinuation(
+ const SpdyContinuationIR& continuation) {
+ CHECK_LT(SPDY3, protocol_version());
+ uint8 flags = 0;
+ if (continuation.end_headers()) {
+ flags |= HEADERS_FLAG_END_HEADERS;
+ }
+
// The size of this frame, including variable-length name-value block.
- size_t size = GetPushPromiseMinimumSize()
- + GetSerializedLength(push_promise.name_value_block());
+ size_t size = GetContinuationMinimumSize();
+ string hpack_encoding;
+ if (enable_compression_) {
+ GetHpackEncoder()->EncodeHeaderSet(
+ continuation.name_value_block(), &hpack_encoding);
+ } else {
+ GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+ continuation.name_value_block(), &hpack_encoding);
+ }
+ size += hpack_encoding.size();
- SpdyFrameBuilder builder(size);
- builder.WriteFramePrefix(*this, PUSH_PROMISE, kNoFlags,
- push_promise.stream_id());
- builder.WriteUInt32(push_promise.promised_stream_id());
- DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
+ SpdyFrameBuilder builder(size, protocol_version());
+ builder.BeginNewFrame(*this, CONTINUATION, flags,
+ continuation.stream_id());
+ DCHECK_EQ(GetContinuationMinimumSize(), builder.length());
- SerializeNameValueBlock(&builder, push_promise);
+ builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
if (debug_visitor_) {
- const size_t payload_len = GetSerializedLength(
- protocol_version(), &(push_promise.name_value_block()));
- debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(),
- PUSH_PROMISE, payload_len, builder.length());
+ const size_t payload_len = hpack_encoding.size();
+ debug_visitor_->OnSendCompressedFrame(continuation.stream_id(),
+ CONTINUATION, payload_len, builder.length());
}
return builder.take();
}
+SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc) {
+ DCHECK_LT(SPDY3, protocol_version());
+ size_t size = GetAltSvcMinimumSize();
+ size += altsvc.protocol_id().length();
+ size += altsvc.host().length();
+ size += altsvc.origin().length();
+
+ SpdyFrameBuilder builder(size, protocol_version());
+ builder.BeginNewFrame(*this, ALTSVC, kNoFlags, altsvc.stream_id());
+
+ builder.WriteUInt32(altsvc.max_age());
+ builder.WriteUInt16(altsvc.port());
+ builder.WriteUInt8(0); // Reserved.
+ builder.WriteUInt8(altsvc.protocol_id().length());
+ builder.WriteBytes(altsvc.protocol_id().data(),
+ altsvc.protocol_id().length());
+ builder.WriteUInt8(altsvc.host().length());
+ builder.WriteBytes(altsvc.host().data(), altsvc.host().length());
+ builder.WriteBytes(altsvc.origin().data(), altsvc.origin().length());
+ DCHECK_LT(GetAltSvcMinimumSize(), builder.length());
+ return builder.take();
+}
+
namespace {
class FrameSerializationVisitor : public SpdyFrameVisitor {
@@ -2072,9 +2893,6 @@ class FrameSerializationVisitor : public SpdyFrameVisitor {
const SpdyWindowUpdateIR& window_update) OVERRIDE {
frame_.reset(framer_->SerializeWindowUpdate(window_update));
}
- virtual void VisitCredential(const SpdyCredentialIR& credential) OVERRIDE {
- frame_.reset(framer_->SerializeCredential(credential));
- }
virtual void VisitBlocked(const SpdyBlockedIR& blocked) OVERRIDE {
frame_.reset(framer_->SerializeBlocked(blocked));
}
@@ -2082,6 +2900,13 @@ class FrameSerializationVisitor : public SpdyFrameVisitor {
const SpdyPushPromiseIR& push_promise) OVERRIDE {
frame_.reset(framer_->SerializePushPromise(push_promise));
}
+ virtual void VisitContinuation(
+ const SpdyContinuationIR& continuation) OVERRIDE {
+ frame_.reset(framer_->SerializeContinuation(continuation));
+ }
+ virtual void VisitAltSvc(const SpdyAltSvcIR& altsvc) OVERRIDE {
+ frame_.reset(framer_->SerializeAltSvc(altsvc));
+ }
private:
SpdyFramer* framer_;
@@ -2097,8 +2922,9 @@ SpdySerializedFrame* SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) {
}
size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) {
+ CHECK_GE(SPDY3, protocol_version());
const size_t uncompressed_length =
- GetSerializedLength(protocol_version(), &headers);
+ GetSerializedLength(protocol_version(), &headers);
if (!enable_compression_) {
return uncompressed_length;
}
@@ -2108,6 +2934,69 @@ size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) {
return 2 * deflateBound(compressor, uncompressed_length);
}
+size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) {
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize();
+ DCHECK_GT(protocol_version(), SPDY3);
+ DCHECK_GT(size, kMaxControlFrameSize);
+ size_t overflow = size - kMaxControlFrameSize;
+ return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1;
+}
+
+void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder,
+ const string& hpack_encoding,
+ SpdyStreamId stream_id,
+ SpdyFrameType type) {
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize();
+
+ // In addition to the prefix, fixed_field_size includes the size of
+ // any fields that come before the variable-length name/value block.
+ size_t fixed_field_size = 0;
+ uint8 end_flag = 0;
+ uint8 flags = 0;
+ if (type == HEADERS) {
+ fixed_field_size = GetHeadersMinimumSize();
+ end_flag = HEADERS_FLAG_END_HEADERS;
+ } else if (type == PUSH_PROMISE) {
+ fixed_field_size = GetPushPromiseMinimumSize();
+ end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
+ } else {
+ DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type "
+ << FrameTypeToString(type);
+ }
+
+ // Write as much of the payload as possible into the initial frame.
+ size_t bytes_remaining = hpack_encoding.size() -
+ std::min(hpack_encoding.size(),
+ kMaxControlFrameSize - fixed_field_size);
+ builder->WriteBytes(&hpack_encoding[0],
+ hpack_encoding.size() - bytes_remaining);
+
+ if (bytes_remaining > 0) {
+ builder->OverwriteLength(*this,
+ kMaxControlFrameSize - GetControlFrameHeaderSize());
+ }
+
+ // Tack on CONTINUATION frames for the overflow.
+ while (bytes_remaining > 0) {
+ size_t bytes_to_write = std::min(bytes_remaining,
+ kMaxControlFrameSize -
+ GetContinuationMinimumSize());
+ // Write CONTINUATION frame prefix.
+ if (bytes_remaining == bytes_to_write) {
+ flags |= end_flag;
+ }
+ builder->BeginNewFrame(*this,
+ CONTINUATION,
+ flags,
+ stream_id);
+ // Write payload fragment.
+ builder->WriteBytes(&hpack_encoding[hpack_encoding.size() -
+ bytes_remaining],
+ bytes_to_write);
+ bytes_remaining -= bytes_to_write;
+ }
+}
+
// The following compression setting are based on Brian Olson's analysis. See
// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792
// for more details.
@@ -2136,10 +3025,10 @@ z_stream* SpdyFramer::GetHeaderCompressor() {
kCompressorMemLevel,
Z_DEFAULT_STRATEGY);
if (success == Z_OK) {
- const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
- : kV3Dictionary;
- const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
- : kV3DictionarySize;
+ const char* dictionary = (protocol_version() <= SPDY2) ?
+ kV2Dictionary : kV3Dictionary;
+ const int dictionary_size = (protocol_version() <= SPDY2) ?
+ kV2DictionarySize : kV3DictionarySize;
success = deflateSetDictionary(header_compressor_.get(),
reinterpret_cast<const Bytef*>(dictionary),
dictionary_size);
@@ -2168,6 +3057,32 @@ z_stream* SpdyFramer::GetHeaderDecompressor() {
return header_decompressor_.get();
}
+HpackEncoder* SpdyFramer::GetHpackEncoder() {
+ DCHECK_LT(SPDY3, spdy_version_);
+ if (hpack_encoder_.get() == NULL) {
+ hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable()));
+ }
+ return hpack_encoder_.get();
+}
+
+HpackDecoder* SpdyFramer::GetHpackDecoder() {
+ DCHECK_LT(SPDY3, spdy_version_);
+ if (hpack_decoder_.get() == NULL) {
+ hpack_decoder_.reset(new HpackDecoder(ObtainHpackHuffmanTable()));
+ }
+ return hpack_decoder_.get();
+}
+
+uint8 SpdyFramer::MapPriorityToWeight(SpdyPriority priority) {
+ const float kSteps = 255.9f / 7.f;
+ return static_cast<uint8>(kSteps * (7.f - priority));
+}
+
+SpdyPriority SpdyFramer::MapWeightToPriority(uint8 weight) {
+ const float kSteps = 255.9f / 7.f;
+ return static_cast<SpdyPriority>(7.f - weight / kSteps);
+}
+
// Incrementally decompress the control frame's header block, feeding the
// result to the visitor in chunks. Continue this until the visitor
// indicates that it cannot process any more data, or (more commonly) we
@@ -2199,13 +3114,13 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData(
int rv = inflate(decomp, Z_SYNC_FLUSH);
if (rv == Z_NEED_DICT) {
- const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
- : kV3Dictionary;
- const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
- : kV3DictionarySize;
+ const char* dictionary = (protocol_version() <= SPDY2) ? kV2Dictionary
+ : kV3Dictionary;
+ const int dictionary_size = (protocol_version() <= SPDY2) ?
+ kV2DictionarySize : kV3DictionarySize;
const DictionaryIds& ids = g_dictionary_ids.Get();
- const uLong dictionary_id = (spdy_version_ < 3) ? ids.v2_dictionary_id
- : ids.v3_dictionary_id;
+ const uLong dictionary_id = (protocol_version() <= SPDY2) ?
+ ids.v2_dictionary_id : ids.v3_dictionary_id;
// Need to try again with the right dictionary.
if (decomp->adler == dictionary_id) {
rv = inflateSetDictionary(decomp,
@@ -2263,7 +3178,7 @@ void SpdyFramer::SerializeNameValueBlockWithoutCompression(
SpdyFrameBuilder* builder,
const SpdyNameValueBlock& name_value_block) const {
// Serialize number of headers.
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
builder->WriteUInt16(name_value_block.size());
} else {
builder->WriteUInt32(name_value_block.size());
@@ -2273,7 +3188,7 @@ void SpdyFramer::SerializeNameValueBlockWithoutCompression(
for (SpdyHeaderBlock::const_iterator it = name_value_block.begin();
it != name_value_block.end();
++it) {
- if (protocol_version() < 3) {
+ if (protocol_version() <= SPDY2) {
builder->WriteString(it->first);
builder->WriteString(it->second);
} else {
@@ -2286,6 +3201,7 @@ void SpdyFramer::SerializeNameValueBlockWithoutCompression(
void SpdyFramer::SerializeNameValueBlock(
SpdyFrameBuilder* builder,
const SpdyFrameWithNameValueBlockIR& frame) {
+ CHECK_GE(SPDY3, protocol_version());
if (!enable_compression_) {
return SerializeNameValueBlockWithoutCompression(builder,
frame.name_value_block());
@@ -2294,7 +3210,7 @@ void SpdyFramer::SerializeNameValueBlock(
// First build an uncompressed version to be fed into the compressor.
const size_t uncompressed_len = GetSerializedLength(
protocol_version(), &(frame.name_value_block()));
- SpdyFrameBuilder uncompressed_builder(uncompressed_len);
+ SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version());
SerializeNameValueBlockWithoutCompression(&uncompressed_builder,
frame.name_value_block());
scoped_ptr<SpdyFrame> uncompressed_payload(uncompressed_builder.take());
diff --git a/chromium/net/spdy/spdy_framer.h b/chromium/net/spdy/spdy_framer.h
index 0447af07515..218423424fb 100644
--- a/chromium/net/spdy/spdy_framer.h
+++ b/chromium/net/spdy/spdy_framer.h
@@ -7,6 +7,7 @@
#include <list>
#include <map>
+#include <memory>
#include <string>
#include <utility>
#include <vector>
@@ -14,8 +15,11 @@
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
#include "base/sys_byteorder.h"
#include "net/base/net_export.h"
+#include "net/spdy/hpack_decoder.h"
+#include "net/spdy/hpack_encoder.h"
#include "net/spdy/spdy_header_block.h"
#include "net/spdy/spdy_protocol.h"
@@ -54,14 +58,15 @@ typedef std::map<std::string, std::string> SpdyHeaderBlock;
// Conveniently handles converstion to/from wire format.
class NET_EXPORT_PRIVATE SettingsFlagsAndId {
public:
- static SettingsFlagsAndId FromWireFormat(int version, uint32 wire);
+ static SettingsFlagsAndId FromWireFormat(SpdyMajorVersion version,
+ uint32 wire);
SettingsFlagsAndId() : flags_(0), id_(0) {}
// TODO(hkhalil): restrict to enums instead of free-form ints.
SettingsFlagsAndId(uint8 flags, uint32 id);
- uint32 GetWireFormat(int version) const;
+ uint32 GetWireFormat(SpdyMajorVersion version) const;
uint32 id() const { return id_; }
uint8 flags() const { return flags_; }
@@ -77,24 +82,13 @@ class NET_EXPORT_PRIVATE SettingsFlagsAndId {
typedef std::pair<SpdySettingsFlags, uint32> SettingsFlagsAndValue;
typedef std::map<SpdySettingsIds, SettingsFlagsAndValue> SettingsMap;
-// A datastrcture for holding the contents of a CREDENTIAL frame.
-// TODO(hkhalil): Remove, use SpdyCredentialIR instead.
-struct NET_EXPORT_PRIVATE SpdyCredential {
- SpdyCredential();
- ~SpdyCredential();
-
- uint16 slot;
- std::vector<std::string> certs;
- std::string proof;
-};
-
// Scratch space necessary for processing SETTINGS frames.
struct NET_EXPORT_PRIVATE SpdySettingsScratch {
SpdySettingsScratch() { Reset(); }
void Reset() {
setting_buf_len = 0;
- last_setting_id = 0;
+ last_setting_id = -1;
}
// Buffer contains up to one complete key/value pair.
@@ -105,8 +99,40 @@ struct NET_EXPORT_PRIVATE SpdySettingsScratch {
// The ID of the last setting that was processed in the current SETTINGS
// frame. Used for detecting out-of-order or duplicate keys within a settings
- // frame. Set to 0 before first key/value pair is processed.
- uint32 last_setting_id;
+ // frame. Set to -1 before first key/value pair is processed.
+ int last_setting_id;
+};
+
+// Scratch space necessary for processing ALTSVC frames.
+struct NET_EXPORT_PRIVATE SpdyAltSvcScratch {
+ SpdyAltSvcScratch();
+ ~SpdyAltSvcScratch();
+
+ void Reset() {
+ max_age = 0;
+ port = 0;
+ pid_len = 0;
+ host_len = 0;
+ origin_len = 0;
+ pid_buf_len = 0;
+ host_buf_len = 0;
+ origin_buf_len = 0;
+ protocol_id.reset();
+ host.reset();
+ origin.reset();
+ }
+
+ uint32 max_age;
+ uint16 port;
+ uint8 pid_len;
+ uint8 host_len;
+ size_t origin_len;
+ size_t pid_buf_len;
+ size_t host_buf_len;
+ size_t origin_buf_len;
+ scoped_ptr<char[]> protocol_id;
+ scoped_ptr<char[]> host;
+ scoped_ptr<char[]> origin;
};
// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
@@ -175,7 +201,6 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional) = 0;
@@ -196,8 +221,14 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
// validated.
virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) = 0;
+ // Called when a SETTINGS frame is received with the ACK flag set.
+ virtual void OnSettingsAck() {}
+
+ // Called before and after parsing SETTINGS id and value tuples.
+ virtual void OnSettingsEnd() = 0;
+
// Called when a PING frame has been parsed.
- virtual void OnPing(uint32 unique_id) = 0;
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) = 0;
// Called when a GOAWAY frame has been parsed.
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
@@ -206,21 +237,30 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
// Called when a HEADERS frame is received.
// Note that header block data is not included. See
// OnControlFrameHeaderData().
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) = 0;
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) = 0;
// Called when a WINDOW_UPDATE frame has been parsed.
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) = 0;
- // Called when a chunk of payload data for a credential frame is available.
- // |header_data| A buffer containing the header data chunk received.
+ // Called when a goaway frame opaque data is available.
+ // |goaway_data| A buffer containing the opaque GOAWAY data chunk received.
// |len| The length of the header data buffer. A length of zero indicates
// that the header data block has been completely sent.
// When this function returns true the visitor indicates that it accepted
- // all of the data. Returning false indicates that that an unrecoverable
- // error has occurred, such as bad header data or resource exhaustion.
- virtual bool OnCredentialFrameData(const char* credential_data,
- size_t len) = 0;
+ // all of the data. Returning false indicates that that an error has
+ // occurred while processing the data. Default implementation returns true.
+ virtual bool OnGoAwayFrameData(const char* goaway_data, size_t len);
+
+ // Called when rst_stream frame opaque data is available.
+ // |rst_stream_data| A buffer containing the opaque RST_STREAM
+ // data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the opaque data has been completely sent.
+ // When this function returns true the visitor indicates that it accepted
+ // all of the data. Returning false indicates that that an error has
+ // occurred while processing the data. Default implementation returns true.
+ virtual bool OnRstStreamFrameData(const char* rst_stream_data, size_t len);
// Called when a BLOCKED frame has been parsed.
virtual void OnBlocked(SpdyStreamId stream_id) {}
@@ -229,7 +269,21 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
// Note that header block data is not included. See
// OnControlFrameHeaderData().
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) = 0;
+ SpdyStreamId promised_stream_id,
+ bool end) = 0;
+
+ // Called when a CONTINUATION frame is received.
+ // Note that header block data is not included. See
+ // OnControlFrameHeaderData().
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) = 0;
+
+ // Called when an ALTSVC frame has been parsed.
+ virtual void OnAltSvc(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ base::StringPiece protocol_id,
+ base::StringPiece host,
+ base::StringPiece origin) {}
};
// Optionally, and in addition to SpdyFramerVisitorInterface, a class supporting
@@ -270,12 +324,16 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_AUTO_RESET,
SPDY_READING_COMMON_HEADER,
SPDY_CONTROL_FRAME_PAYLOAD,
+ SPDY_READ_PADDING_LENGTH,
+ SPDY_CONSUME_PADDING,
SPDY_IGNORE_REMAINING_PAYLOAD,
SPDY_FORWARD_STREAM_FRAME,
SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK,
SPDY_CONTROL_FRAME_HEADER_BLOCK,
- SPDY_CREDENTIAL_FRAME_PAYLOAD,
+ SPDY_GOAWAY_FRAME_PAYLOAD,
+ SPDY_RST_STREAM_FRAME_PAYLOAD,
SPDY_SETTINGS_FRAME_PAYLOAD,
+ SPDY_ALTSVC_FRAME_PAYLOAD,
};
// SPDY error codes.
@@ -287,9 +345,11 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_UNSUPPORTED_VERSION, // Control frame has unsupported version.
SPDY_DECOMPRESS_FAILURE, // There was an error decompressing.
SPDY_COMPRESS_FAILURE, // There was an error compressing.
- SPDY_CREDENTIAL_FRAME_CORRUPT, // CREDENTIAL frame could not be parsed.
+ SPDY_GOAWAY_FRAME_CORRUPT, // GOAWAY frame could not be parsed.
+ SPDY_RST_STREAM_FRAME_CORRUPT, // RST_STREAM frame could not be parsed.
SPDY_INVALID_DATA_FRAME_FLAGS, // Data frame has invalid flags.
SPDY_INVALID_CONTROL_FRAME_FLAGS, // Control frame has invalid flags.
+ SPDY_UNEXPECTED_FRAME, // Frame received out of order.
LAST_ERROR, // Must be the last entry in the enum.
};
@@ -304,13 +364,14 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// Serializes a SpdyHeaderBlock.
static void WriteHeaderBlock(SpdyFrameBuilder* frame,
- const int spdy_version,
+ const SpdyMajorVersion spdy_version,
const SpdyHeaderBlock* headers);
// Retrieve serialized length of SpdyHeaderBlock.
// TODO(hkhalil): Remove, or move to quic code.
- static size_t GetSerializedLength(const int spdy_version,
- const SpdyHeaderBlock* headers);
+ static size_t GetSerializedLength(
+ const SpdyMajorVersion spdy_version,
+ const SpdyHeaderBlock* headers);
// Create a new Framer, provided a SPDY version.
explicit SpdyFramer(SpdyMajorVersion version);
@@ -353,115 +414,69 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t header_length,
SpdyHeaderBlock* block) const;
- // Create a data frame.
- // |stream_id| is the stream for this frame
- // |data| is the data to be included in the frame.
- // |len| is the length of the data
- // |flags| is the flags to use with the data.
- // To mark this frame as the last data frame, enable DATA_FLAG_FIN.
- SpdyFrame* CreateDataFrame(SpdyStreamId stream_id, const char* data,
- uint32 len, SpdyDataFlags flags) const;
+ // Serialize a data frame.
SpdySerializedFrame* SerializeData(const SpdyDataIR& data) const;
- // Serializes just the data frame header, excluding actual data payload.
- SpdySerializedFrame* SerializeDataFrameHeader(const SpdyDataIR& data) const;
-
- // Creates and serializes a SYN_STREAM frame.
- // |stream_id| is the id for this stream.
- // |associated_stream_id| is the associated stream id for this stream.
- // |priority| is the priority (GetHighestPriority()-GetLowestPriority) for
- // this stream.
- // |credential_slot| is the CREDENTIAL slot to be used for this request.
- // |flags| is the flags to use with the data.
- // To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
- // |compressed| specifies whether the frame should be compressed.
- // |headers| is the header block to include in the frame.
- SpdyFrame* CreateSynStream(SpdyStreamId stream_id,
- SpdyStreamId associated_stream_id,
- SpdyPriority priority,
- uint8 credential_slot,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* headers);
+ // Serializes the data frame header and optionally padding length fields,
+ // excluding actual data payload and padding.
+ SpdySerializedFrame* SerializeDataFrameHeaderWithPaddingLengthField(
+ const SpdyDataIR& data) const;
+
+ // Serializes a SYN_STREAM frame.
SpdySerializedFrame* SerializeSynStream(const SpdySynStreamIR& syn_stream);
- // Create a SYN_REPLY SpdyFrame.
- // |stream_id| is the stream for this frame.
- // |flags| is the flags to use with the data.
- // To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
- // |compressed| specifies whether the frame should be compressed.
- // |headers| is the header block to include in the frame.
- SpdyFrame* CreateSynReply(SpdyStreamId stream_id,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* headers);
+ // Serialize a SYN_REPLY SpdyFrame.
SpdySerializedFrame* SerializeSynReply(const SpdySynReplyIR& syn_reply);
- SpdyFrame* CreateRstStream(SpdyStreamId stream_id,
- SpdyRstStreamStatus status) const;
SpdySerializedFrame* SerializeRstStream(
const SpdyRstStreamIR& rst_stream) const;
- // Creates and serializes a SETTINGS frame. The SETTINGS frame is
+ // Serializes a SETTINGS frame. The SETTINGS frame is
// used to communicate name/value pairs relevant to the communication channel.
- SpdyFrame* CreateSettings(const SettingsMap& values) const;
SpdySerializedFrame* SerializeSettings(const SpdySettingsIR& settings) const;
- // Creates and serializes a PING frame. The unique_id is used to
+ // Serializes a PING frame. The unique_id is used to
// identify the ping request/response.
- SpdyFrame* CreatePingFrame(uint32 unique_id) const;
SpdySerializedFrame* SerializePing(const SpdyPingIR& ping) const;
- // Creates and serializes a GOAWAY frame. The GOAWAY frame is used
+ // Serializes a GOAWAY frame. The GOAWAY frame is used
// prior to the shutting down of the TCP connection, and includes the
// stream_id of the last stream the sender of the frame is willing to process
// to completion.
- SpdyFrame* CreateGoAway(SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) const;
SpdySerializedFrame* SerializeGoAway(const SpdyGoAwayIR& goaway) const;
- // Creates and serializes a HEADERS frame. The HEADERS frame is used
- // for sending additional headers outside of a SYN_STREAM/SYN_REPLY. The
- // arguments are the same as for CreateSynReply.
- SpdyFrame* CreateHeaders(SpdyStreamId stream_id,
- SpdyControlFlags flags,
- const SpdyHeaderBlock* headers);
+ // Serializes a HEADERS frame. The HEADERS frame is used
+ // for sending additional headers outside of a SYN_STREAM/SYN_REPLY.
SpdySerializedFrame* SerializeHeaders(const SpdyHeadersIR& headers);
- // Creates and serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE
+ // Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE
// frame is used to implement per stream flow control in SPDY.
- SpdyFrame* CreateWindowUpdate(
- SpdyStreamId stream_id,
- uint32 delta_window_size) const;
SpdySerializedFrame* SerializeWindowUpdate(
const SpdyWindowUpdateIR& window_update) const;
- // Creates and serializes a CREDENTIAL frame. The CREDENTIAL
- // frame is used to send a client certificate to the server when
- // request more than one origin are sent over the same SPDY session.
- SpdyFrame* CreateCredentialFrame(const SpdyCredential& credential) const;
- SpdySerializedFrame* SerializeCredential(
- const SpdyCredentialIR& credential) const;
-
- // Serializes a BLOCKED frame. The BLOCKED frame is used to indicate to the
- // remote endpoint that this endpoint believes itself to be flow-control
- // blocked but otherwise ready to send data. The BLOCKED frame is purely
- // advisory and optional.
+ // Serializes a BLOCKED frame. The BLOCKED frame is used to
+ // indicate to the remote endpoint that this endpoint believes itself to be
+ // flow-control blocked but otherwise ready to send data. The BLOCKED frame
+ // is purely advisory and optional.
SpdySerializedFrame* SerializeBlocked(const SpdyBlockedIR& blocked) const;
- // Creates and serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used
+ // Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used
// to inform the client that it will be receiving an additional stream
// in response to the original request. The frame includes synthesized
// headers to explain the upcoming data.
- SpdyFrame* CreatePushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id,
- const SpdyHeaderBlock* headers);
SpdySerializedFrame* SerializePushPromise(
const SpdyPushPromiseIR& push_promise);
- // Given a CREDENTIAL frame's payload, extract the credential.
- // Returns true on successful parse, false otherwise.
- // TODO(hkhalil): Implement CREDENTIAL frame parsing in SpdyFramer
- // and eliminate this method.
- static bool ParseCredentialData(const char* data, size_t len,
- SpdyCredential* credential);
+ // Serializes a CONTINUATION frame. The CONTINUATION frame is used
+ // to continue a sequence of header block fragments.
+ // TODO(jgraettinger): This implementation is incorrect. The continuation
+ // frame continues a previously-begun HPACK encoding; it doesn't begin a
+ // new one. Figure out whether it makes sense to keep SerializeContinuation().
+ SpdySerializedFrame* SerializeContinuation(
+ const SpdyContinuationIR& continuation);
+
+ // Serializes an ALTSVC frame. The ALTSVC frame advertises the
+ // availability of an alternative service to the client.
+ SpdySerializedFrame* SerializeAltSvc(const SpdyAltSvcIR& altsvc);
// Serialize a frame of unknown type.
SpdySerializedFrame* SerializeFrame(const SpdyFrameIR& frame);
@@ -499,15 +514,17 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t GetControlFrameHeaderSize() const;
size_t GetSynStreamMinimumSize() const;
size_t GetSynReplyMinimumSize() const;
- size_t GetRstStreamSize() const;
+ size_t GetRstStreamMinimumSize() const;
size_t GetSettingsMinimumSize() const;
size_t GetPingSize() const;
- size_t GetGoAwaySize() const;
+ size_t GetGoAwayMinimumSize() const;
size_t GetHeadersMinimumSize() const;
size_t GetWindowUpdateSize() const;
- size_t GetCredentialMinimumSize() const;
size_t GetBlockedSize() const;
size_t GetPushPromiseMinimumSize() const;
+ size_t GetContinuationMinimumSize() const;
+ size_t GetAltSvcMinimumSize() const;
+ size_t GetPrioritySize() const;
// Returns the minimum size a frame can be (data or control).
size_t GetFrameMinimumSize() const;
@@ -515,9 +532,15 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// Returns the maximum size a frame can be (data or control).
size_t GetFrameMaximumSize() const;
+ // Returns the maximum size that a control frame can be.
+ size_t GetControlFrameMaximumSize() const;
+
// Returns the maximum payload size of a DATA frame.
size_t GetDataFrameMaximumPayload() const;
+ // Returns the prefix length for the given frame type.
+ size_t GetPrefixLength(SpdyFrameType type) const;
+
// For debugging.
static const char* StateToString(int state);
static const char* ErrorCodeToString(int error_code);
@@ -528,9 +551,19 @@ class NET_EXPORT_PRIVATE SpdyFramer {
bool probable_http_response() const { return probable_http_response_; }
- SpdyPriority GetLowestPriority() const { return spdy_version_ < 3 ? 3 : 7; }
+ SpdyStreamId expect_continuation() const { return expect_continuation_; }
+
+ SpdyPriority GetLowestPriority() const {
+ return spdy_version_ < SPDY3 ? 3 : 7;
+ }
+
SpdyPriority GetHighestPriority() const { return 0; }
+ // Interpolates SpdyPriority values into SPDY4/HTTP2 priority weights,
+ // and vice versa.
+ uint8 MapPriorityToWeight(SpdyPriority priority);
+ SpdyPriority MapWeightToPriority(uint8 weight);
+
// Deliver the given control frame's compressed headers block to the visitor
// in decompressed form, in chunks. Returns true if the visitor has
// accepted all of the chunks.
@@ -540,6 +573,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t len);
protected:
+ // TODO(jgraettinger): Switch to test peer pattern.
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, BasicCompression);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ControlFrameSizesAreValidated);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HeaderCompression);
@@ -556,6 +590,10 @@ class NET_EXPORT_PRIVATE SpdyFramer {
ReadLargeSettingsFrameInSmallChunks);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ControlFrameAtMaxSizeLimit);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ControlFrameTooLarge);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
+ TooLargeHeadersFrameUsesContinuation);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
+ TooLargePushPromiseFrameUsesContinuation);
friend class net::HttpNetworkLayer; // This is temporary for the server.
friend class net::HttpNetworkTransactionTest;
friend class net::HttpProxyClientSocketPoolTest;
@@ -573,15 +611,33 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// consumed from the data.
size_t ProcessCommonHeader(const char* data, size_t len);
size_t ProcessControlFramePayload(const char* data, size_t len);
- size_t ProcessCredentialFramePayload(const char* data, size_t len);
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
- size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
- size_t ProcessSettingsFramePayload(const char* data, size_t len);
+ // HPACK data is re-encoded as SPDY3 and re-entrantly delivered through
+ // |ProcessControlFrameHeaderBlock()|. |is_hpack_header_block| controls
+ // whether data is treated as HPACK- vs SPDY3-encoded.
+ size_t ProcessControlFrameHeaderBlock(const char* data,
+ size_t len,
+ bool is_hpack_header_block);
+ size_t ProcessFramePaddingLength(const char* data, size_t len);
+ size_t ProcessFramePadding(const char* data, size_t len);
size_t ProcessDataFramePayload(const char* data, size_t len);
+ size_t ProcessGoAwayFramePayload(const char* data, size_t len);
+ size_t ProcessRstStreamFramePayload(const char* data, size_t len);
+ size_t ProcessSettingsFramePayload(const char* data, size_t len);
+ size_t ProcessAltSvcFramePayload(const char* data, size_t len);
+ size_t ProcessIgnoredControlFramePayload(/*const char* data,*/ size_t len);
+
+ // TODO(jgraettinger): To be removed with migration to
+ // SpdyHeadersHandlerInterface.
+ // Serializes the last-processed header block of |hpack_decoder_| as
+ // a SPDY3 format block, and delivers it to the visitor via reentrant
+ // call to ProcessControlFrameHeaderBlock().
+ void DeliverHpackBlockAsSpdy3Block();
// Helpers for above internal breakouts from ProcessInput.
void ProcessControlFrameHeader(uint16 control_frame_type_field);
- bool ProcessSetting(const char* data); // Always passed exactly 8 bytes.
+ // Always passed exactly 1 setting's worth of data.
+ bool ProcessSetting(const char* data);
// Retrieve serialized length of SpdyHeaderBlock. If compression is enabled, a
// maximum estimate is returned.
@@ -591,6 +647,17 @@ class NET_EXPORT_PRIVATE SpdyFramer {
z_stream* GetHeaderCompressor();
z_stream* GetHeaderDecompressor();
+ // Get (and lazily initialize) the HPACK state.
+ HpackEncoder* GetHpackEncoder();
+ HpackDecoder* GetHpackDecoder();
+
+ size_t GetNumberRequiredContinuationFrames(size_t size);
+
+ void WritePayloadWithContinuation(SpdyFrameBuilder* builder,
+ const std::string& hpack_encoding,
+ SpdyStreamId stream_id,
+ SpdyFrameType type);
+
private:
// Deliver the given control frame's uncompressed headers block to the
// visitor in chunks. Returns true if the visitor has accepted all of the
@@ -637,9 +704,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
if (spdy_version_ == SPDY3) {
return 16 * 1024 * 1024;
}
- // The theoretical maximum for SPDY4 is 2^16 - 1, as the length
- // field does count the size of the header.
- return 16 * 1024;
+ // Absolute maximum size of HTTP2 frame payload (section 4.2 "Frame size").
+ return (1<<14) - 1;
}
// The size of the control frame buffer.
@@ -651,8 +717,18 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SpdyState state_;
SpdyState previous_state_;
SpdyError error_code_;
+
+ // Note that for DATA frame, remaining_data_length_ is sum of lengths of
+ // frame header, padding length field (optional), data payload (optional) and
+ // padding payload (optional).
size_t remaining_data_length_;
+ // The length (in bytes) of the padding payload to be processed.
+ size_t remaining_padding_payload_length_;
+
+ // The length (in bytes) of the padding length field to be processed.
+ size_t remaining_padding_length_fields_;
+
// The number of bytes remaining to read from the current control frame's
// headers. Note that header data blocks (for control types that have them)
// are part of the frame's payload, and not the frame's headers.
@@ -679,11 +755,16 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// current_frame_buffer_.
SpdySettingsScratch settings_scratch_;
+ SpdyAltSvcScratch altsvc_scratch_;
+
bool enable_compression_; // Controls all compression
// SPDY header compressors.
scoped_ptr<z_stream> header_compressor_;
scoped_ptr<z_stream> header_decompressor_;
+ scoped_ptr<HpackEncoder> hpack_encoder_;
+ scoped_ptr<HpackDecoder> hpack_decoder_;
+
SpdyFramerVisitorInterface* visitor_;
SpdyFramerDebugVisitorInterface* debug_visitor_;
@@ -696,8 +777,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// type SYN_STREAM or SYN_REPLY.
//
// If we ever get something which looks like a data frame before we've had a
- // SYN, we explicitly check to see if it looks like we got an HTTP response to
- // a SPDY request. This boolean lets us do that.
+ // SYN, we explicitly check to see if it looks like we got an HTTP response
+ // to a SPDY request. This boolean lets us do that.
bool syn_frame_processed_;
// If we ever get a data frame before a SYN frame, we check to see if it
@@ -706,6 +787,18 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// corrupt data that just looks like HTTP, but deterministic checking requires
// a lot more state.
bool probable_http_response_;
+
+ // Set this to the current stream when we receive a HEADERS, PUSH_PROMISE, or
+ // CONTINUATION frame without the END_HEADERS(0x4) bit set. These frames must
+ // be followed by a CONTINUATION frame, or else we throw a PROTOCOL_ERROR.
+ // A value of 0 indicates that we are not expecting a CONTINUATION frame.
+ SpdyStreamId expect_continuation_;
+
+ // If a HEADERS frame is followed by a CONTINUATION frame, the FIN/END_STREAM
+ // flag is still carried in the HEADERS frame. If it's set, flip this so that
+ // we know to terminate the stream when the entire header block has been
+ // processed.
+ bool end_stream_when_done_;
};
} // namespace net
diff --git a/chromium/net/spdy/spdy_framer_test.cc b/chromium/net/spdy/spdy_framer_test.cc
index f9c25036097..b6c5041934d 100644
--- a/chromium/net/spdy/spdy_framer_test.cc
+++ b/chromium/net/spdy/spdy_framer_test.cc
@@ -8,17 +8,23 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
+#include "net/spdy/hpack_output_stream.h"
+#include "net/spdy/mock_spdy_framer_visitor.h"
#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_frame_reader.h"
#include "net/spdy/spdy_framer.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
+using base::StringPiece;
using std::string;
using std::max;
using std::min;
using std::numeric_limits;
+using testing::ElementsAre;
+using testing::Pair;
using testing::_;
namespace net {
@@ -27,46 +33,6 @@ namespace test {
static const size_t kMaxDecompressedSize = 1024;
-// TODO(akalin): Make sure expectations on mocks are set before mock
-// functions are called, as interleaving expectations and calls is
-// undefined.
-class MockVisitor : public SpdyFramerVisitorInterface {
- public:
- MOCK_METHOD1(OnError, void(SpdyFramer* framer));
- MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
- size_t length,
- bool fin));
- MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
- const char* data,
- size_t len,
- bool fin));
- MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
- const char* header_data,
- size_t len));
- MOCK_METHOD6(OnSynStream, void(SpdyStreamId stream_id,
- SpdyStreamId associated_stream_id,
- SpdyPriority priority,
- uint8 slot,
- bool fin,
- bool unidirectional));
- MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
- MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
- SpdyRstStreamStatus status));
- MOCK_METHOD1(OnSettings, void(bool clear_persisted));
- MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
- MOCK_METHOD1(OnPing, void(uint32 unique_id));
- MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status));
- MOCK_METHOD2(OnHeaders, void(SpdyStreamId stream_id, bool fin));
- MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
- uint32 delta_window_size));
- MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
- size_t len));
- MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
- MOCK_METHOD2(OnPushPromise, void(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id));
-};
-
class MockDebugVisitor : public SpdyFramerDebugVisitorInterface {
public:
MOCK_METHOD4(OnSendCompressedFrame, void(SpdyStreamId stream_id,
@@ -98,15 +64,9 @@ class SpdyFramerTestUtil {
char* buffer = visitor.ReleaseBuffer();
CHECK(buffer != NULL);
SpdyFrame* decompressed_frame = new SpdyFrame(buffer, visitor.size(), true);
- if (framer->protocol_version() == 4) {
- SetFrameLength(decompressed_frame,
- visitor.size(),
- framer->protocol_version());
- } else {
- SetFrameLength(decompressed_frame,
- visitor.size() - framer->GetControlFrameHeaderSize(),
- framer->protocol_version());
- }
+ SetFrameLength(decompressed_frame,
+ visitor.size() - framer->GetControlFrameHeaderSize(),
+ framer->protocol_version());
return decompressed_frame;
}
@@ -154,26 +114,16 @@ class SpdyFramerTestUtil {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 slot,
bool fin,
bool unidirectional) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
- const SpdyHeaderBlock null_headers;
- int flags = CONTROL_FLAG_NONE;
- if (fin) {
- flags &= CONTROL_FLAG_FIN;
- }
- if (unidirectional) {
- flags &= CONTROL_FLAG_UNIDIRECTIONAL;
- }
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(stream_id,
- associated_stream_id,
- priority,
- slot,
- static_cast<SpdyControlFlags>(flags),
- &null_headers));
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_associated_to_stream_id(associated_stream_id);
+ syn_stream.set_priority(priority);
+ syn_stream.set_fin(fin);
+ syn_stream.set_unidirectional(unidirectional);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetSynStreamMinimumSize());
size_ += framer.GetSynStreamMinimumSize();
@@ -182,15 +132,9 @@ class SpdyFramerTestUtil {
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
- const SpdyHeaderBlock null_headers;
- int flags = CONTROL_FLAG_NONE;
- if (fin) {
- flags &= CONTROL_FLAG_FIN;
- }
- scoped_ptr<SpdyFrame> frame(
- framer.CreateHeaders(stream_id,
- static_cast<SpdyControlFlags>(flags),
- &null_headers));
+ SpdyHeadersIR headers(stream_id);
+ headers.set_fin(fin);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetHeadersMinimumSize());
size_ += framer.GetSynStreamMinimumSize();
@@ -205,26 +149,23 @@ class SpdyFramerTestUtil {
uint32 value) OVERRIDE {
LOG(FATAL);
}
- virtual void OnPing(uint32 unique_id) OVERRIDE {
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
LOG(FATAL);
}
+ virtual void OnSettingsEnd() OVERRIDE { LOG(FATAL); }
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) OVERRIDE {
LOG(FATAL);
}
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {
+ virtual void OnHeaders(SpdyStreamId stream_id,
+ bool fin,
+ bool end) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
- const SpdyHeaderBlock null_headers;
- int flags = CONTROL_FLAG_NONE;
- if (fin) {
- flags &= CONTROL_FLAG_FIN;
- }
- scoped_ptr<SpdyFrame> frame(
- framer.CreateHeaders(stream_id,
- static_cast<SpdyControlFlags>(flags),
- &null_headers));
+ SpdyHeadersIR headers(stream_id);
+ headers.set_fin(fin);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetHeadersMinimumSize());
size_ += framer.GetHeadersMinimumSize();
@@ -233,25 +174,23 @@ class SpdyFramerTestUtil {
virtual void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) {
LOG(FATAL);
}
- virtual bool OnCredentialFrameData(const char* /*credential_data*/,
- size_t /*len*/) OVERRIDE {
- LOG(FATAL) << "Unexpected CREDENTIAL Frame";
- return false;
- }
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
- const SpdyHeaderBlock null_headers;
- scoped_ptr<SpdyFrame> frame(
- framer.CreatePushPromise(stream_id, promised_stream_id,
- &null_headers));
+ SpdyPushPromiseIR push_promise(stream_id, promised_stream_id);
+ scoped_ptr<SpdyFrame> frame(framer.SerializePushPromise(push_promise));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetPushPromiseMinimumSize());
size_ += framer.GetPushPromiseMinimumSize();
}
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+ LOG(FATAL);
+ }
+
char* ReleaseBuffer() {
CHECK(finished_);
return buffer_.release();
@@ -283,8 +222,9 @@ class SpdyFramerTestUtil {
class TestSpdyVisitor : public SpdyFramerVisitorInterface,
public SpdyFramerDebugVisitorInterface {
public:
+ // This is larger than our max frame size because header blocks that
+ // are too long can spill over into CONTINUATION frames.
static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024;
- static const size_t kDefaultCredentialBufferSize = 16 * 1024;
explicit TestSpdyVisitor(SpdyMajorVersion version)
: framer_(version),
@@ -293,14 +233,21 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
+ push_promise_frame_count_(0),
goaway_count_(0),
setting_count_(0),
+ settings_ack_sent_(0),
+ settings_ack_received_(0),
+ continuation_count_(0),
+ altsvc_count_(0),
+ test_altsvc_ir_(0),
last_window_update_stream_(0),
last_window_update_delta_(0),
last_push_promise_stream_(0),
last_push_promise_promised_stream_(0),
data_bytes_(0),
fin_frame_count_(0),
+ fin_opaque_data_(),
fin_flag_count_(0),
zero_length_data_frame_count_(0),
control_frame_header_data_count_(0),
@@ -313,22 +260,19 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
header_buffer_size_(kDefaultHeaderBufferSize),
header_stream_id_(-1),
header_control_type_(DATA),
- header_buffer_valid_(false),
- credential_buffer_(new char[kDefaultCredentialBufferSize]),
- credential_buffer_length_(0),
- credential_buffer_size_(kDefaultCredentialBufferSize) {
+ header_buffer_valid_(false) {
}
virtual void OnError(SpdyFramer* f) OVERRIDE {
LOG(INFO) << "SpdyFramer Error: "
<< SpdyFramer::ErrorCodeToString(f->error_code());
- error_count_++;
+ ++error_count_;
}
virtual void OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) OVERRIDE {
- data_frame_count_++;
+ ++data_frame_count_;
header_stream_id_ = stream_id;
}
@@ -358,10 +302,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
if (len == 0) {
++zero_length_control_frame_header_data_count_;
// Indicates end-of-header-block.
+ headers_.clear();
CHECK(header_buffer_valid_);
size_t parsed_length = framer_.ParseHeaderBlockInBuffer(
header_buffer_.get(), header_buffer_length_, &headers_);
- DCHECK_EQ(header_buffer_length_, parsed_length);
+ LOG_IF(DFATAL, header_buffer_length_ != parsed_length)
+ << "Check failed: header_buffer_length_ == parsed_length "
+ << "(" << header_buffer_length_ << " vs. " << parsed_length << ")";
return true;
}
const size_t available = header_buffer_size_ - header_buffer_length_;
@@ -377,49 +324,66 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional) OVERRIDE {
- syn_frame_count_++;
+ ++syn_frame_count_;
InitHeaderStreaming(SYN_STREAM, stream_id);
if (fin) {
- fin_flag_count_++;
+ ++fin_flag_count_;
}
}
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
- syn_reply_frame_count_++;
+ ++syn_reply_frame_count_;
InitHeaderStreaming(SYN_REPLY, stream_id);
if (fin) {
- fin_flag_count_++;
+ ++fin_flag_count_;
}
}
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE {
- fin_frame_count_++;
+ ++fin_frame_count_;
+ }
+
+ virtual bool OnRstStreamFrameData(const char* rst_stream_data,
+ size_t len) OVERRIDE {
+ if ((rst_stream_data != NULL) && (len > 0)) {
+ fin_opaque_data_ += std::string(rst_stream_data, len);
+ }
+ return true;
}
virtual void OnSetting(SpdySettingsIds id,
uint8 flags,
uint32 value) OVERRIDE {
- setting_count_++;
+ ++setting_count_;
+ }
+
+ virtual void OnSettingsAck() OVERRIDE {
+ DCHECK_LT(SPDY3, framer_.protocol_version());
+ ++settings_ack_received_;
+ }
+
+ virtual void OnSettingsEnd() OVERRIDE {
+ if (framer_.protocol_version() <= SPDY3) { return; }
+ ++settings_ack_sent_;
}
- virtual void OnPing(uint32 unique_id) OVERRIDE {
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
DLOG(FATAL);
}
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) OVERRIDE {
- goaway_count_++;
+ ++goaway_count_;
}
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {
- headers_frame_count_++;
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
+ ++headers_frame_count_;
InitHeaderStreaming(HEADERS, stream_id);
if (fin) {
- fin_flag_count_++;
+ ++fin_flag_count_;
}
}
@@ -429,35 +393,36 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
last_window_update_delta_ = delta_window_size;
}
- virtual bool OnCredentialFrameData(const char* credential_data,
- size_t len) OVERRIDE {
- if (len == 0) {
- if (!framer_.ParseCredentialData(credential_buffer_.get(),
- credential_buffer_length_,
- &credential_)) {
- LOG(INFO) << "Error parsing credential data.";
- ++error_count_;
- }
- return true;
- }
- const size_t available =
- credential_buffer_size_ - credential_buffer_length_;
- if (len > available) {
- return false;
- }
- memcpy(credential_buffer_.get() + credential_buffer_length_,
- credential_data, len);
- credential_buffer_length_ += len;
- return true;
- }
-
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE {
+ ++push_promise_frame_count_;
InitHeaderStreaming(PUSH_PROMISE, stream_id);
last_push_promise_stream_ = stream_id;
last_push_promise_promised_stream_ = promised_stream_id;
}
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+ ++continuation_count_;
+ }
+
+ virtual void OnAltSvc(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ StringPiece protocol_id,
+ StringPiece host,
+ StringPiece origin) OVERRIDE {
+ test_altsvc_ir_.set_stream_id(stream_id);
+ test_altsvc_ir_.set_max_age(max_age);
+ test_altsvc_ir_.set_port(port);
+ test_altsvc_ir_.set_protocol_id(protocol_id.as_string());
+ test_altsvc_ir_.set_host(host.as_string());
+ if (origin.length() > 0) {
+ test_altsvc_ir_.set_origin(origin.as_string());
+ }
+ ++altsvc_count_;
+ }
+
virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
SpdyFrameType type,
size_t payload_len,
@@ -522,14 +487,21 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
+ int push_promise_frame_count_;
int goaway_count_;
int setting_count_;
+ int settings_ack_sent_;
+ int settings_ack_received_;
+ int continuation_count_;
+ int altsvc_count_;
+ SpdyAltSvcIR test_altsvc_ir_;
SpdyStreamId last_window_update_stream_;
uint32 last_window_update_delta_;
SpdyStreamId last_push_promise_stream_;
SpdyStreamId last_push_promise_promised_stream_;
int data_bytes_;
int fin_frame_count_; // The count of RST_STREAM type frames received.
+ std::string fin_opaque_data_;
int fin_flag_count_; // The count of frames with the FIN flag set.
int zero_length_data_frame_count_; // The count of zero-length data frames.
int control_frame_header_data_count_; // The count of chunks received.
@@ -547,19 +519,41 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
SpdyFrameType header_control_type_;
bool header_buffer_valid_;
SpdyHeaderBlock headers_;
-
- scoped_ptr<char[]> credential_buffer_;
- size_t credential_buffer_length_;
- size_t credential_buffer_size_;
- SpdyCredential credential_;
};
-// Retrieves serialized headers from SYN_STREAM frame.
-// Does not check that the given frame is a SYN_STREAM.
+// Retrieves serialized headers from a HEADERS or SYN_STREAM frame.
base::StringPiece GetSerializedHeaders(const SpdyFrame* frame,
const SpdyFramer& framer) {
- return base::StringPiece(frame->data() + framer.GetSynStreamMinimumSize(),
- frame->size() - framer.GetSynStreamMinimumSize());
+ SpdyFrameReader reader(frame->data(), frame->size());
+ reader.Seek(2); // Seek past the frame length.
+ SpdyFrameType frame_type;
+ if (framer.protocol_version() > SPDY3) {
+ uint8 serialized_type;
+ reader.ReadUInt8(&serialized_type);
+ frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(),
+ serialized_type);
+ DCHECK_EQ(HEADERS, frame_type);
+ uint8 flags;
+ reader.ReadUInt8(&flags);
+ if (flags & HEADERS_FLAG_PRIORITY) {
+ frame_type = SYN_STREAM;
+ }
+ } else {
+ uint16 serialized_type;
+ reader.ReadUInt16(&serialized_type);
+ frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(),
+ serialized_type);
+ DCHECK(frame_type == HEADERS ||
+ frame_type == SYN_STREAM) << frame_type;
+ }
+
+ if (frame_type == SYN_STREAM) {
+ return StringPiece(frame->data() + framer.GetSynStreamMinimumSize(),
+ frame->size() - framer.GetSynStreamMinimumSize());
+ } else {
+ return StringPiece(frame->data() + framer.GetHeadersMinimumSize(),
+ frame->size() - framer.GetHeadersMinimumSize());
+ }
}
} // namespace test
@@ -579,7 +573,8 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> {
protected:
virtual void SetUp() {
spdy_version_ = GetParam();
- spdy_version_ch_ = static_cast<unsigned char>(spdy_version_);
+ spdy_version_ch_ = static_cast<unsigned char>(
+ SpdyConstants::SerializeMajorVersion(spdy_version_));
}
void CompareFrame(const string& description,
@@ -630,21 +625,10 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> {
return true;
}
- void AddSpdySettingFromWireFormat(SettingsMap* settings,
- uint32 key,
- uint32 value) {
- SettingsFlagsAndId flags_and_id =
- SettingsFlagsAndId::FromWireFormat(spdy_version_, key);
- SpdySettingsIds id = static_cast<SpdySettingsIds>(flags_and_id.id());
- SpdySettingsFlags flags =
- static_cast<SpdySettingsFlags>(flags_and_id.flags());
- CHECK(settings->find(id) == settings->end());
- settings->insert(std::make_pair(id, SettingsFlagsAndValue(flags, value)));
- }
-
bool IsSpdy2() { return spdy_version_ == SPDY2; }
bool IsSpdy3() { return spdy_version_ == SPDY3; }
bool IsSpdy4() { return spdy_version_ == SPDY4; }
+ bool IsSpdy5() { return spdy_version_ == SPDY5; }
// Version of SPDY protocol to be used.
SpdyMajorVersion spdy_version_;
@@ -658,139 +642,62 @@ INSTANTIATE_TEST_CASE_P(SpdyFramerTests,
// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- headers["gamma"] = "charlie";
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
// Encode the header block into a SynStream frame.
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("alpha", "beta");
+ syn_stream.SetHeader("gamma", "charlie");
+ syn_stream.SetHeader("cookie", "key1=value1; key2=value2");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(frame.get() != NULL);
- base::StringPiece serialized_headers =
- GetSerializedHeaders(frame.get(), framer);
- SpdyHeaderBlock new_headers;
- EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
- serialized_headers.size(),
- &new_headers));
- EXPECT_EQ(headers.size(), new_headers.size());
- EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
- EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(frame->data()),
+ frame->size());
+
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+ EXPECT_TRUE(CompareHeaderBlocks(&syn_stream.name_value_block(),
+ &visitor.headers_));
}
// Test that if there's not a full frame, we fail to parse it.
TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- headers["gamma"] = "charlie";
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
// Encode the header block into a SynStream frame.
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("alpha", "beta");
+ syn_stream.SetHeader("gamma", "charlie");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(frame.get() != NULL);
- base::StringPiece serialized_headers =
- GetSerializedHeaders(frame.get(), framer);
- SpdyHeaderBlock new_headers;
- EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
- serialized_headers.size() - 2,
- &new_headers));
-}
-
-TEST_P(SpdyFramerTest, OutOfOrderHeaders) {
- SpdyFramer framer(spdy_version_);
- framer.set_enable_compression(false);
-
- // Frame builder with plentiful buffer size.
- SpdyFrameBuilder frame(1024);
- if (spdy_version_ < 4) {
- frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
- frame.WriteUInt32(3); // stream_id
- } else {
- frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3);
- }
-
- frame.WriteUInt32(0); // Associated stream id
- frame.WriteUInt16(0); // Priority.
-
- if (IsSpdy2()) {
- frame.WriteUInt16(2); // Number of headers.
- frame.WriteString("gamma");
- frame.WriteString("gamma");
- frame.WriteString("alpha");
- frame.WriteString("alpha");
- } else {
- frame.WriteUInt32(2); // Number of headers.
- frame.WriteStringPiece32("gamma");
- frame.WriteStringPiece32("gamma");
- frame.WriteStringPiece32("alpha");
- frame.WriteStringPiece32("alpha");
- }
- // write the length
- frame.RewriteLength(framer);
-
- SpdyHeaderBlock new_headers;
- scoped_ptr<SpdyFrame> control_frame(frame.take());
- base::StringPiece serialized_headers =
- GetSerializedHeaders(control_frame.get(), framer);
- EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
- serialized_headers.size(),
- &new_headers));
-}
-
-// Test that if we receive a SYN_STREAM with stream ID zero, we signal an error
-// (but don't crash).
-TEST_P(SpdyFramerTest, SynStreamWithStreamIdZero) {
- testing::StrictMock<net::test::MockVisitor> visitor;
- SpdyFramer framer(spdy_version_);
- framer.set_visitor(&visitor);
-
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- scoped_ptr<SpdySerializedFrame> frame(
- framer.CreateSynStream(0, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
- ASSERT_TRUE(frame.get() != NULL);
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(frame->data()),
+ frame->size() - 2);
- // We shouldn't have to read the whole frame before we signal an error.
- EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
- EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
- EXPECT_TRUE(framer.HasError());
- EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
- << SpdyFramer::ErrorCodeToString(framer.error_code());
+ EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_);
+ EXPECT_EQ(0u, visitor.headers_.size());
}
// Test that if we receive a SYN_REPLY with stream ID zero, we signal an error
// (but don't crash).
TEST_P(SpdyFramerTest, SynReplyWithStreamIdZero) {
- testing::StrictMock<net::test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- scoped_ptr<SpdySerializedFrame> frame(
- framer.CreateSynReply(0, // stream id
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynReplyIR syn_reply(0);
+ syn_reply.SetHeader("alpha", "beta");
+ scoped_ptr<SpdySerializedFrame> frame(framer.SerializeSynReply(syn_reply));
ASSERT_TRUE(frame.get() != NULL);
// We shouldn't have to read the whole frame before we signal an error.
@@ -804,16 +711,13 @@ TEST_P(SpdyFramerTest, SynReplyWithStreamIdZero) {
// Test that if we receive a HEADERS with stream ID zero, we signal an error
// (but don't crash).
TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
- testing::StrictMock<net::test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- scoped_ptr<SpdySerializedFrame> frame(
- framer.CreateHeaders(0, // stream id
- CONTROL_FLAG_NONE,
- &headers));
+ SpdyHeadersIR headers_ir(0);
+ headers_ir.SetHeader("alpha", "beta");
+ scoped_ptr<SpdySerializedFrame> frame(framer.SerializeHeaders(headers_ir));
ASSERT_TRUE(frame.get() != NULL);
// We shouldn't have to read the whole frame before we signal an error.
@@ -827,20 +731,18 @@ TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
// Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an
// error (but don't crash).
TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
- testing::StrictMock<net::test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
+ SpdyPushPromiseIR push_promise(0, 4);
+ push_promise.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(
- framer.CreatePushPromise(0, // stream id
- 4, // promised stream id
- &headers));
+ framer.SerializePushPromise(push_promise));
ASSERT_TRUE(frame.get() != NULL);
// We shouldn't have to read the whole frame before we signal an error.
@@ -854,20 +756,18 @@ TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
// Test that if we receive a PUSH_PROMISE with promised stream ID zero, we
// signal an error (but don't crash).
TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
- testing::StrictMock<net::test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
+ SpdyPushPromiseIR push_promise(3, 0);
+ push_promise.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(
- framer.CreatePushPromise(3, // stream id
- 0, // promised stream id
- &headers));
+ framer.SerializePushPromise(push_promise));
ASSERT_TRUE(frame.get() != NULL);
// We shouldn't have to read the whole frame before we signal an error.
@@ -878,139 +778,26 @@ TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
-TEST_P(SpdyFramerTest, CreateCredential) {
- SpdyFramer framer(spdy_version_);
-
- {
- const char kDescription[] = "CREDENTIAL frame";
- const unsigned char kV3FrameData[] = { // Also applies for V2.
- 0x80, spdy_version_ch_, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x33,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
- const unsigned char kV4FrameData[] = {
- 0x00, 0x3b, 0x0A, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
- scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
- if (IsSpdy4()) {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
- } else {
- CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
- }
- }
-}
-
-TEST_P(SpdyFramerTest, ParseCredentialFrameData) {
- SpdyFramer framer(spdy_version_);
-
- {
- const unsigned char kV3FrameData[] = { // Also applies for V2.
- 0x80, spdy_version_ch_, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x33,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
- const unsigned char kV4FrameData[] = {
- 0x00, 0x37, 0x0A, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
-
- SpdyCredential credential;
- if (IsSpdy4()) {
- EXPECT_TRUE(SpdyFramer::ParseCredentialData(
- reinterpret_cast<const char*>(kV4FrameData) +
- framer.GetControlFrameHeaderSize(),
- arraysize(kV4FrameData) - framer.GetControlFrameHeaderSize(),
- &credential));
- } else {
- EXPECT_TRUE(SpdyFramer::ParseCredentialData(
- reinterpret_cast<const char*>(kV3FrameData) +
- framer.GetControlFrameHeaderSize(),
- arraysize(kV3FrameData) - framer.GetControlFrameHeaderSize(),
- &credential));
- }
- EXPECT_EQ(3u, credential.slot);
- EXPECT_EQ("proof", credential.proof);
- EXPECT_EQ("a cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_EQ("another cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_EQ("final cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_TRUE(credential.certs.empty());
- }
-}
-
TEST_P(SpdyFramerTest, DuplicateHeader) {
+ if (spdy_version_ > SPDY3) {
+ // TODO(jgraettinger): Punting on this because we haven't determined
+ // whether duplicate HPACK headers other than Cookie are an error.
+ // If they are, this will need to be updated to use HpackOutputStream.
+ return;
+ }
SpdyFramer framer(spdy_version_);
// Frame builder with plentiful buffer size.
- SpdyFrameBuilder frame(1024);
- if (spdy_version_ < 4) {
+ SpdyFrameBuilder frame(1024, spdy_version_);
+ if (spdy_version_ <= SPDY3) {
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt32(0); // associated stream id
+ frame.WriteUInt16(0); // Priority.
} else {
- frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3);
+ frame.BeginNewFrame(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3);
+ frame.WriteUInt32(framer.GetHighestPriority());
}
- frame.WriteUInt32(0); // associated stream id
- frame.WriteUInt16(0); // Priority.
-
if (IsSpdy2()) {
frame.WriteUInt16(2); // Number of headers.
frame.WriteString("name");
@@ -1041,22 +828,34 @@ TEST_P(SpdyFramerTest, DuplicateHeader) {
TEST_P(SpdyFramerTest, MultiValueHeader) {
SpdyFramer framer(spdy_version_);
// Frame builder with plentiful buffer size.
- SpdyFrameBuilder frame(1024);
- if (spdy_version_ < 4) {
+ SpdyFrameBuilder frame(1024, spdy_version_);
+ if (spdy_version_ <= SPDY3) {
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt32(0); // associated stream id
+ frame.WriteUInt16(0); // Priority.
} else {
- frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3);
+ frame.BeginNewFrame(framer,
+ HEADERS,
+ HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS,
+ 3);
+ frame.WriteUInt32(0); // Priority exclusivity and dependent stream.
+ frame.WriteUInt8(255); // Priority weight.
}
- frame.WriteUInt32(0); // associated stream id
- frame.WriteUInt16(0); // Priority.
-
- string value("value1\0value2");
+ string value("value1\0value2", 13);
if (IsSpdy2()) {
frame.WriteUInt16(1); // Number of headers.
frame.WriteString("name");
frame.WriteString(value);
+ } else if (spdy_version_ > SPDY3) {
+ // TODO(jgraettinger): If this pattern appears again, move to test class.
+ std::map<string, string> header_set;
+ header_set["name"] = value;
+ string buffer;
+ HpackEncoder encoder(ObtainHpackHuffmanTable());
+ encoder.EncodeHeaderSetWithoutCompression(header_set, &buffer);
+ frame.WriteBytes(&buffer[0], buffer.size());
} else {
frame.WriteUInt32(1); // Number of headers.
frame.WriteStringPiece32("name");
@@ -1065,37 +864,36 @@ TEST_P(SpdyFramerTest, MultiValueHeader) {
// write the length
frame.RewriteLength(framer);
- SpdyHeaderBlock new_headers;
framer.set_enable_compression(false);
scoped_ptr<SpdyFrame> control_frame(frame.take());
- base::StringPiece serialized_headers =
- GetSerializedHeaders(control_frame.get(), framer);
- EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
- serialized_headers.size(),
- &new_headers));
- EXPECT_TRUE(new_headers.find("name") != new_headers.end());
- EXPECT_EQ(value, new_headers.find("name")->second);
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->size());
+
+ EXPECT_THAT(visitor.headers_, ElementsAre(
+ Pair("name", value)));
}
TEST_P(SpdyFramerTest, BasicCompression) {
- SpdyHeaderBlock headers;
- headers["server"] = "SpdyServer 1.0";
- headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
- headers["status"] = "200";
- headers["version"] = "HTTP/1.1";
- headers["content-type"] = "text/html";
- headers["content-length"] = "12";
-
+ if (spdy_version_ > SPDY3) {
+ // Deflate compression doesn't apply to HPACK.
+ return;
+ }
scoped_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor(spdy_version_));
SpdyFramer framer(spdy_version_);
framer.set_debug_visitor(visitor.get());
- scoped_ptr<SpdyFrame> frame1(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("server", "SpdyServer 1.0");
+ syn_stream.SetHeader("date", "Mon 12 Jan 2009 12:12:12 PST");
+ syn_stream.SetHeader("status", "200");
+ syn_stream.SetHeader("version", "HTTP/1.1");
+ syn_stream.SetHeader("content-type", "text/html");
+ syn_stream.SetHeader("content-length", "12");
+ scoped_ptr<SpdyFrame> frame1(framer.SerializeSynStream(syn_stream));
size_t uncompressed_size1 = visitor->last_payload_len_;
size_t compressed_size1 =
visitor->last_frame_len_ - framer.GetSynStreamMinimumSize();
@@ -1114,13 +912,7 @@ TEST_P(SpdyFramerTest, BasicCompression) {
EXPECT_EQ(117u, compressed_size1);
#endif // !defined(USE_SYSTEM_ZLIB)
}
- scoped_ptr<SpdyFrame> frame2(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ scoped_ptr<SpdyFrame> frame2(framer.SerializeSynStream(syn_stream));
size_t uncompressed_size2 = visitor->last_payload_len_;
size_t compressed_size2 =
visitor->last_frame_len_ - framer.GetSynStreamMinimumSize();
@@ -1168,35 +960,24 @@ TEST_P(SpdyFramerTest, BasicCompression) {
// from scratch.
framer.set_enable_compression(false);
scoped_ptr<SpdyFrame> uncompressed_frame(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ framer.SerializeSynStream(syn_stream));
CompareFrames("Uncompressed SYN_STREAM", *frame3, *uncompressed_frame);
}
TEST_P(SpdyFramerTest, CompressEmptyHeaders) {
// See crbug.com/172383
- SpdyHeaderBlock headers;
- headers["server"] = "SpdyServer 1.0";
- headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
- headers["status"] = "200";
- headers["version"] = "HTTP/1.1";
- headers["content-type"] = "text/html";
- headers["content-length"] = "12";
- headers["x-empty-header"] = "";
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.SetHeader("server", "SpdyServer 1.0");
+ syn_stream.SetHeader("date", "Mon 12 Jan 2009 12:12:12 PST");
+ syn_stream.SetHeader("status", "200");
+ syn_stream.SetHeader("version", "HTTP/1.1");
+ syn_stream.SetHeader("content-type", "text/html");
+ syn_stream.SetHeader("content-length", "12");
+ syn_stream.SetHeader("x-empty-header", "");
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(true);
- scoped_ptr<SpdyFrame> frame1(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ scoped_ptr<SpdyFrame> frame1(framer.SerializeSynStream(syn_stream));
}
TEST_P(SpdyFramerTest, Basic) {
@@ -1242,7 +1023,7 @@ TEST_P(SpdyFramerTest, Basic) {
0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #1
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL
0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
0x00, 0x00, 0x00, 0x00,
@@ -1250,7 +1031,7 @@ TEST_P(SpdyFramerTest, Basic) {
0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #3
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL
};
const unsigned char kV3Input[] = {
@@ -1301,7 +1082,7 @@ TEST_P(SpdyFramerTest, Basic) {
0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #1
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL
0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
0x00, 0x00, 0x00, 0x00,
@@ -1309,60 +1090,54 @@ TEST_P(SpdyFramerTest, Basic) {
0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #3
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL
};
+ // SYN_STREAM doesn't exist in SPDY4, so instead we send
+ // HEADERS frames with PRIORITY and END_HEADERS set.
const unsigned char kV4Input[] = {
- 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x02, 'h', 'h',
- 0x00, 0x00, 0x00, 0x02,
- 'v', 'v',
+ 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x00, 0x00, 0x00, // Priority 0
+ 0x82, // :method: GET
- 0x00, 0x24, 0x08, 0x00, // HEADERS on Stream #1
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x02,
- 'h', '2', 0x00, 0x00,
- 0x00, 0x02, 'v', '2',
- 0x00, 0x00, 0x00, 0x02,
- 'h', '3', 0x00, 0x00,
- 0x00, 0x02, 'v', '3',
+ 0x00, 0x01, 0x01, 0x04, // HEADERS: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x8c, // :status: 200
- 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1
+ 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x12, 0x01, 0x00, // SYN Stream #3
- 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00,
+ 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS
+ 0x00, 0x00, 0x00, 0x03, // Stream 3
+ 0x00, 0x00, 0x00, 0x00, // Priority 0
+ 0x82, // :method: GET
- 0x00, 0x10, 0x00, 0x00, // DATA on Stream #3
+ 0x00, 0x08, 0x00, 0x00, // DATA on Stream #3
0x00, 0x00, 0x00, 0x03,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1
+ 0x00, 0x04, 0x00, 0x00, // DATA on Stream #1
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #1
+ 0x00, 0x04, 0x03, 0x00, // RST_STREAM on Stream #1
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, // RST_STREAM_CANCEL
- 0x00, 0x08, 0x00, 0x00, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x00, // DATA on Stream #3
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #3
+ 0x00, 0x0f, 0x03, 0x00, // RST_STREAM on Stream #3
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, // RST_STREAM_CANCEL
+ 0x52, 0x45, 0x53, 0x45, // opaque data
+ 0x54, 0x53, 0x54, 0x52,
+ 0x45, 0x41, 0x4d,
};
TestSpdyVisitor visitor(spdy_version_);
@@ -1374,15 +1149,25 @@ TEST_P(SpdyFramerTest, Basic) {
visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
}
- EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(2, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(24, visitor.data_bytes_);
+
+ EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(2, visitor.fin_frame_count_);
+
+ if (IsSpdy4()) {
+ base::StringPiece reset_stream = "RESETSTREAM";
+ EXPECT_EQ(reset_stream, visitor.fin_opaque_data_);
+ } else {
+ EXPECT_TRUE(visitor.fin_opaque_data_.empty());
+ }
+
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_EQ(4, visitor.data_frame_count_);
+ visitor.fin_opaque_data_.clear();
}
// Test that the FIN flag on a data frame signifies EOF.
@@ -1442,30 +1227,26 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) {
0x01, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
};
+
+ // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+ // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
const unsigned char kV4Input[] = {
- 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x02, 'h', 'h',
- 0x00, 0x00, 0x00, 0x02,
- 'v', 'v',
+ 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x00, 0x00, 0x00, // Priority 0
+ 0x82, // :method: GET
- 0x00, 0x18, 0x02, 0x00, // SYN REPLY Stream #1
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x02,
- 'a', 'a', 0x00, 0x00,
- 0x00, 0x02, 'b', 'b',
+ 0x00, 0x01, 0x01, 0x04, // HEADERS: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x8c, // :status: 200
- 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1
+ 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x0c, 0x00, 0x01, // DATA on Stream #1, with FIN
+ 0x00, 0x04, 0x00, 0x01, // DATA on Stream #1, with FIN
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
};
@@ -1481,8 +1262,13 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) {
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
- EXPECT_EQ(1, visitor.syn_reply_frame_count_);
- EXPECT_EQ(0, visitor.headers_frame_count_);
+ if (IsSpdy4()) {
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ } else {
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ }
EXPECT_EQ(16, visitor.data_bytes_);
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
@@ -1527,22 +1313,18 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
'a', 'a', 0x00, 0x00,
0x00, 0x02, 'b', 'b',
};
- const unsigned char kV4Input[] = {
- 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x02, 'h', 'h',
- 0x00, 0x00, 0x00, 0x02,
- 'v', 'v',
- 0x00, 0x18, 0x02, 0x01, // SYN_REPLY #1, with FIN
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x02,
- 'a', 'a', 0x00, 0x00,
- 0x00, 0x02, 'b', 'b',
+ // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+ // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
+ const unsigned char kV4Input[] = {
+ 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x00, 0x00, 0x00, // Priority 0
+ 0x82, // :method: GET
+
+ 0x00, 0x01, 0x01, 0x05, // HEADERS: FIN | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x8c, // :status: 200
};
TestSpdyVisitor visitor(spdy_version_);
@@ -1556,8 +1338,13 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
- EXPECT_EQ(1, visitor.syn_reply_frame_count_);
- EXPECT_EQ(0, visitor.headers_frame_count_);
+ if (IsSpdy4()) {
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ } else {
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ }
EXPECT_EQ(0, visitor.data_bytes_);
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(1, visitor.fin_flag_count_);
@@ -1566,6 +1353,10 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
}
TEST_P(SpdyFramerTest, HeaderCompression) {
+ if (spdy_version_ > SPDY3) {
+ // Deflate compression doesn't apply to HPACK.
+ return;
+ }
SpdyFramer send_framer(spdy_version_);
SpdyFramer recv_framer(spdy_version_);
@@ -1583,22 +1374,16 @@ TEST_P(SpdyFramerTest, HeaderCompression) {
SpdyHeaderBlock block;
block[kHeader1] = kValue1;
block[kHeader2] = kValue2;
- SpdyControlFlags flags(CONTROL_FLAG_NONE);
SpdySynStreamIR syn_ir_1(1);
- syn_ir_1.SetHeader(kHeader1, kValue1);
- syn_ir_1.SetHeader(kHeader2, kValue2);
+ syn_ir_1.set_name_value_block(block);
scoped_ptr<SpdyFrame> syn_frame_1(send_framer.SerializeFrame(syn_ir_1));
EXPECT_TRUE(syn_frame_1.get() != NULL);
// SYN_STREAM #2
block[kHeader3] = kValue3;
- scoped_ptr<SpdyFrame> syn_frame_2(
- send_framer.CreateSynStream(3, // stream id
- 0, // associated stream id
- 0, // priority
- 0, // credential slot
- flags,
- &block));
+ SpdySynStreamIR syn_stream(3);
+ syn_stream.set_name_value_block(block);
+ scoped_ptr<SpdyFrame> syn_frame_2(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame_2.get() != NULL);
// Now start decompressing
@@ -1645,24 +1430,16 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) {
const char kValue1[] = "value1";
const char kValue2[] = "value2";
- SpdyHeaderBlock block;
- block[kHeader1] = kValue1;
- block[kHeader2] = kValue2;
- SpdyControlFlags flags(CONTROL_FLAG_NONE);
- scoped_ptr<SpdyFrame> syn_frame(
- send_framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 0, // priority
- 0, // credential slot
- flags,
- &block));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.SetHeader(kHeader1, kValue1);
+ syn_stream.SetHeader(kHeader2, kValue2);
+ scoped_ptr<SpdyFrame> syn_frame(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame.get() != NULL);
- const char bytes[] = "this is a test test test test test!";
- scoped_ptr<SpdyFrame> send_frame(
- send_framer.CreateDataFrame(
- 1, bytes, arraysize(bytes),
- static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
+ StringPiece bytes = "this is a test test test test test!";
+ SpdyDataIR data_ir(1, bytes);
+ data_ir.set_fin(true);
+ scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
EXPECT_TRUE(send_frame.get() != NULL);
// Run the inputs through the framer.
@@ -1678,7 +1455,7 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) {
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
- EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
+ EXPECT_EQ(bytes.size(), static_cast<unsigned>(visitor.data_bytes_));
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
@@ -1697,24 +1474,16 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
const char kValue1[] = "value1";
const char kValue2[] = "value2";
- SpdyHeaderBlock block;
- block[kHeader1] = kValue1;
- block[kHeader2] = kValue2;
- SpdyControlFlags flags(CONTROL_FLAG_NONE);
- scoped_ptr<SpdyFrame> syn_frame(
- send_framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- 0, // priority
- 0, // credential slot
- flags,
- &block));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.SetHeader(kHeader1, kValue1);
+ syn_stream.SetHeader(kHeader2, kValue2);
+ scoped_ptr<SpdyFrame> syn_frame(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame.get() != NULL);
const char bytes[] = "this is a test test test test test!";
- scoped_ptr<SpdyFrame> send_frame(
- send_framer.CreateDataFrame(
- 1, bytes, arraysize(bytes),
- static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
+ SpdyDataIR data_ir(1, StringPiece(bytes, arraysize(bytes)));
+ data_ir.set_fin(true);
+ scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
EXPECT_TRUE(send_frame.get() != NULL);
// Run the inputs through the framer.
@@ -1745,7 +1514,8 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
TEST_P(SpdyFramerTest, WindowUpdateFrame) {
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x12345678));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
+ SpdyWindowUpdateIR(1, 0x12345678)));
const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678";
const unsigned char kV3FrameData[] = { // Also applies for V2.
@@ -1755,15 +1525,15 @@ TEST_P(SpdyFramerTest, WindowUpdateFrame) {
0x12, 0x34, 0x56, 0x78
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x09, 0x00,
+ 0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x12, 0x34, 0x56, 0x78
};
if (IsSpdy4()) {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
- CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+ CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
@@ -1779,15 +1549,120 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
'o'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0d, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
+ const char bytes[] = "hello";
+
+ SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+ if (IsSpdy4()) {
+ CompareFrame(
+ kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ } else {
+ CompareFrame(
+ kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+ }
+
+ SpdyDataIR data_header_ir(1);
+ data_header_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes)));
+ frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(
+ data_header_ir));
+ CompareCharArraysWithHexError(
+ kDescription,
+ reinterpret_cast<const unsigned char*>(frame->data()),
+ framer.GetDataFrameMinimumSize(),
+ IsSpdy4() ? kV4FrameData : kV3FrameData,
+ framer.GetDataFrameMinimumSize());
+ }
+
+ {
+ const char kDescription[] = "'hello' data frame with more padding, no FIN";
+ const unsigned char kV3FrameData[] = { // Also applies for V2.
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x05,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
+
+ const unsigned char kV4FrameData[] = {
+ 0x01, 0x0b, 0x00, 0x18, // Length = 267. PAD_HIGH and PAD_LOW set.
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x04, // Pad Low and Pad High fields.
+ 'h', 'e', 'l', 'l', // Data
+ 'o',
+ // Padding of 260 zeros (so both PAD_HIGH and PAD_LOW fields are used).
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ };
+ const char bytes[] = "hello";
+
+ SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+ // 260 zeros and the pad low/high fields make the overall padding to be 262
+ // bytes.
+ data_ir.set_padding_len(262);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+ if (IsSpdy4()) {
+ CompareFrame(
+ kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ } else {
+ CompareFrame(
+ kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+ }
+
+ frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
+ CompareCharArraysWithHexError(
+ kDescription,
+ reinterpret_cast<const unsigned char*>(frame->data()),
+ framer.GetDataFrameMinimumSize(),
+ IsSpdy4() ? kV4FrameData : kV3FrameData,
+ framer.GetDataFrameMinimumSize());
+ }
+
+ {
+ const char kDescription[] = "'hello' data frame with few padding, no FIN";
+ const unsigned char kV3FrameData[] = { // Also applies for V2.
0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x05,
'h', 'e', 'l', 'l',
'o'
};
+
+ const unsigned char kV4FrameData[] = {
+ 0x00, 0x0d, 0x00, 0x08, // Length = 13. PAD_LOW set.
+ 0x00, 0x00, 0x00, 0x01,
+ 0x07, // Pad Low field.
+ 'h', 'e', 'l', 'l', // Data
+ 'o',
+ '0', '0', '0', '0', // Padding
+ '0', '0', '0'
+ };
const char bytes[] = "hello";
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 1, bytes, strlen(bytes), DATA_FLAG_NONE));
+ SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+ // 7 zeros and the pad low field make the overall padding to be 8 bytes.
+ data_ir.set_padding_len(8);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -1795,10 +1670,41 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
+ }
+
+ {
+ const char kDescription[] =
+ "'hello' data frame with 1 byte padding, no FIN";
+ const unsigned char kV3FrameData[] = { // Also applies for V2.
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x05,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
- SpdyDataIR data_ir(1);
- data_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes)));
- frame.reset(framer.SerializeDataFrameHeader(data_ir));
+ const unsigned char kV4FrameData[] = {
+ 0x00, 0x06, 0x00, 0x08, // Length = 6. PAD_LOW set.
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, // Pad Low field.
+ 'h', 'e', 'l', 'l', // Data
+ 'o',
+ };
+ const char bytes[] = "hello";
+
+ SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+ // The pad low field itself is used for the 1-byte padding and no padding
+ // payload is needed.
+ data_ir.set_padding_len(1);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+ if (IsSpdy4()) {
+ CompareFrame(
+ kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ } else {
+ CompareFrame(
+ kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+ }
+
+ frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
CompareCharArraysWithHexError(
kDescription,
reinterpret_cast<const unsigned char*>(frame->data()),
@@ -1815,12 +1721,12 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
0xff
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x09, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff
};
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 1, "\xff", 1, DATA_FLAG_NONE));
+ SpdyDataIR data_ir(1, StringPiece("\xff", 1));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -1839,13 +1745,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
'o'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0d, 0x00, 0x01,
+ 0x00, 0x05, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
'h', 'e', 'l', 'l',
'o'
};
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 1, "hello", 5, DATA_FLAG_FIN));
+ SpdyDataIR data_ir(1, StringPiece("hello", 5));
+ data_ir.set_fin(true);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -1862,11 +1769,11 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 1, "", 0, DATA_FLAG_NONE));
+ SpdyDataIR data_ir(1, StringPiece());
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -1874,6 +1781,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
+
+ frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
+ CompareCharArraysWithHexError(
+ kDescription,
+ reinterpret_cast<const unsigned char*>(frame->data()),
+ framer.GetDataFrameMinimumSize(),
+ IsSpdy4() ? kV4FrameData : kV3FrameData,
+ framer.GetDataFrameMinimumSize());
}
{
@@ -1885,13 +1800,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
'o'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0d, 0x00, 0x01,
+ 0x00, 0x05, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
'h', 'e', 'l', 'l',
'o'
};
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 0x7fffffff, "hello", 5, DATA_FLAG_FIN));
+ SpdyDataIR data_ir(0x7fffffff, "hello");
+ data_ir.set_fin(true);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -1918,8 +1834,9 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
- scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
- 1, kData.data(), kData.size(), DATA_FLAG_FIN));
+ SpdyDataIR data_ir(1, StringPiece(kData.data(), kData.size()));
+ data_ir.set_fin(true);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
}
}
@@ -1929,14 +1846,9 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
framer.set_enable_compression(false);
{
- const char kDescription[] = "SYN_STREAM frame, lowest pri, slot 2, no FIN";
-
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
+ const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
const unsigned char kPri = IsSpdy2() ? 0xC0 : 0xE0;
- const unsigned char kCre = IsSpdy2() ? 0 : 2;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x20,
@@ -1954,7 +1866,7 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
0x00, 0x00, 0x00, 0x2a,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
- kPri, kCre, 0x00, 0x00,
+ kPri, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x00, 0x00,
@@ -1965,26 +1877,20 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
'a', 'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x2e, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- kPri, kCre, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x00,
- 0x00, 0x03, 'b', 'a',
- 'r', 0x00, 0x00, 0x00,
- 0x03, 'f', 'o', 'o',
- 0x00, 0x00, 0x00, 0x03,
- 'f', 'o', 'o', 0x00,
- 0x00, 0x00, 0x03, 'b',
- 'a', 'r'
+ 0x00, 0x17, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 0.
+ 0x00, 0x00, 0x03, 0x62,
+ 0x61, 0x72, 0x03, 0x66,
+ 0x6f, 0x6f, 0x00, 0x03,
+ 0x66, 0x6f, 0x6f, 0x03,
+ 0x62, 0x61, 0x72,
};
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- framer.GetLowestPriority(),
- kCre, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(framer.GetLowestPriority());
+ syn_stream.SetHeader("bar", "foo");
+ syn_stream.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -1999,10 +1905,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
"SYN_STREAM frame with a 0-length header name, highest pri, FIN, "
"max stream ID";
- SpdyHeaderBlock headers;
- headers[std::string()] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x01, 0x00, 0x00, 0x1D,
@@ -2030,25 +1932,21 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x2b, 0x01, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x03, 'f', 'o', 'o',
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r'
+ 0x00, 0x14, 0x01, 0x25, // HEADERS: PRIORITY | FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 255.
+ 0xff, 0x00, 0x00, 0x03,
+ 0x66, 0x6f, 0x6f, 0x00,
+ 0x03, 0x66, 0x6f, 0x6f,
+ 0x03, 0x62, 0x61, 0x72,
};
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(0x7fffffff, // stream id
- 0x7fffffff, // associated stream id
- framer.GetHighestPriority(),
- 0, // credential slot
- CONTROL_FLAG_FIN,
- &headers));
+ SpdySynStreamIR syn_stream(0x7fffffff);
+ syn_stream.set_associated_to_stream_id(0x7fffffff);
+ syn_stream.set_priority(framer.GetHighestPriority());
+ syn_stream.set_fin(true);
+ syn_stream.SetHeader("", "foo");
+ syn_stream.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2063,10 +1961,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
"SYN_STREAM frame with a 0-length header val, high pri, FIN, "
"max stream ID";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "";
-
const unsigned char kPri = IsSpdy2() ? 0x40 : 0x20;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
@@ -2095,25 +1989,21 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
0x00, 0x00, 0x00
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x2b, 0x01, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x7f, 0xff, 0xff, 0xff,
- kPri, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x00,
- 0x00, 0x03, 'b', 'a',
- 'r', 0x00, 0x00, 0x00,
- 0x03, 'f', 'o', 'o',
- 0x00, 0x00, 0x00, 0x03,
- 'f', 'o', 'o', 0x00,
- 0x00, 0x00, 0x00
+ 0x00, 0x14, 0x01, 0x25, // HEADERS: PRIORITY | FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 219.
+ 0xdb, 0x00, 0x03, 0x62,
+ 0x61, 0x72, 0x03, 0x66,
+ 0x6f, 0x6f, 0x00, 0x03,
+ 0x66, 0x6f, 0x6f, 0x00,
};
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(0x7fffffff, // stream id
- 0x7fffffff, // associated stream id
- 1, // priority
- 0, // credential slot
- CONTROL_FLAG_FIN,
- &headers));
+ SpdySynStreamIR syn_stream(0x7fffffff);
+ syn_stream.set_associated_to_stream_id(0x7fffffff);
+ syn_stream.set_priority(1);
+ syn_stream.set_fin(true);
+ syn_stream.SetHeader("bar", "foo");
+ syn_stream.SetHeader("foo", "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2135,10 +2025,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) {
const char kDescription[] =
"SYN_STREAM frame, low pri, no FIN";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
-
const SpdyPriority priority = IsSpdy2() ? 2 : 4;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
@@ -2176,36 +2062,17 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) {
0x80, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF,
};
- const unsigned char kV4FrameData[] = {
- 0x00, 0x3b, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x80, 0x00, 0x38, 0xea,
- 0xe3, 0xc6, 0xa7, 0xc2,
- 0x02, 0xe5, 0x0e, 0x50,
- 0xc2, 0x4b, 0x4a, 0x04,
- 0xe5, 0x0b, 0x66, 0x80,
- 0x00, 0x4a, 0xcb, 0xcf,
- 0x07, 0x08, 0x20, 0x10,
- 0x95, 0x96, 0x9f, 0x0f,
- 0xa2, 0x00, 0x02, 0x28,
- 0x29, 0xb1, 0x08, 0x20,
- 0x80, 0x00, 0x00, 0x00,
- 0x00, 0xff, 0xff,
- };
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(1, // stream id
- 0, // associated stream id
- priority,
- 0, // credential slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(priority);
+ syn_stream.SetHeader("bar", "foo");
+ syn_stream.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ // Deflate compression doesn't apply to HPACK.
}
}
}
@@ -2218,10 +2085,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x1C,
@@ -2247,19 +2110,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
0x03, 'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x28, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x03, 'b', 'a', 'r'
+ 0x00, 0x12, 0x01, 0x04, // HEADER: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x03, 0x62, // oo.b
+ 0x61, 0x72, // ar
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
- 1, CONTROL_FLAG_NONE, &headers));
+ SpdySynReplyIR syn_reply(1);
+ syn_reply.SetHeader("bar", "foo");
+ syn_reply.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2273,10 +2135,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
const char kDescription[] =
"SYN_REPLY frame with a 0-length header name, FIN, max stream ID";
- SpdyHeaderBlock headers;
- headers[std::string()] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
@@ -2302,19 +2160,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x25, 0x02, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03,
- 'f', 'o', 'o', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'b', 'a',
- 'r'
+ 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x00, 0x03, 0x66, // @..f
+ 0x6f, 0x6f, 0x00, 0x03, // oo@.
+ 0x66, 0x6f, 0x6f, 0x03, // foo.
+ 0x62, 0x61, 0x72, // bar
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
- 0x7fffffff, CONTROL_FLAG_FIN, &headers));
+ SpdySynReplyIR syn_reply(0x7fffffff);
+ syn_reply.set_fin(true);
+ syn_reply.SetHeader("", "foo");
+ syn_reply.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2328,10 +2185,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
const char kDescription[] =
"SYN_REPLY frame with a 0-length header val, FIN, max stream ID";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
@@ -2357,19 +2210,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
0x00
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x25, 0x02, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x00
+ 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x00, // oo.
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
- 0x7fffffff, CONTROL_FLAG_FIN, &headers));
+ SpdySynReplyIR syn_reply(0x7fffffff);
+ syn_reply.set_fin(true);
+ syn_reply.SetHeader("bar", "foo");
+ syn_reply.SetHeader("foo", "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2390,10 +2242,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) {
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x32,
@@ -2428,30 +2276,16 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) {
0x00, 0x00, 0x00, 0xff,
0xff,
};
- const unsigned char kV4FrameData[] = {
- 0x00, 0x35, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x38, 0xea, 0xe3, 0xc6,
- 0xa7, 0xc2, 0x02, 0xe5,
- 0x0e, 0x50, 0xc2, 0x4b,
- 0x4a, 0x04, 0xe5, 0x0b,
- 0x66, 0x80, 0x00, 0x4a,
- 0xcb, 0xcf, 0x07, 0x08,
- 0x20, 0x10, 0x95, 0x96,
- 0x9f, 0x0f, 0xa2, 0x00,
- 0x02, 0x28, 0x29, 0xb1,
- 0x08, 0x20, 0x80, 0x00,
- 0x00, 0x00, 0x00, 0xff,
- 0xff,
- };
- scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
- 1, CONTROL_FLAG_NONE, &headers));
+ SpdySynReplyIR syn_reply(1);
+ syn_reply.SetHeader("bar", "foo");
+ syn_reply.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ // Deflate compression doesn't apply to HPACK.
}
}
}
@@ -2469,12 +2303,13 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x03, 0x00,
+ 0x00, 0x07, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
+ 0x52, 0x53, 0x54
};
- scoped_ptr<SpdyFrame> frame(
- framer.CreateRstStream(1, RST_STREAM_PROTOCOL_ERROR));
+ SpdyRstStreamIR rst_stream(1, RST_STREAM_PROTOCOL_ERROR, "RST");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -2491,12 +2326,14 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x03, 0x00,
+ 0x00, 0x04, 0x03, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(
- 0x7FFFFFFF, RST_STREAM_PROTOCOL_ERROR));
+ SpdyRstStreamIR rst_stream(0x7FFFFFFF,
+ RST_STREAM_PROTOCOL_ERROR,
+ "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -2513,12 +2350,14 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
0x00, 0x00, 0x00, 0x06,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x03, 0x00,
+ 0x00, 0x04, 0x03, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x06,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(
- 0x7FFFFFFF, RST_STREAM_INTERNAL_ERROR));
+ SpdyRstStreamIR rst_stream(0x7FFFFFFF,
+ RST_STREAM_INTERNAL_ERROR,
+ "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -2533,39 +2372,42 @@ TEST_P(SpdyFramerTest, CreateSettings) {
{
const char kDescription[] = "Network byte order SETTINGS frame";
- uint32 kValue = 0x0a0b0c0d;
- SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
- SpdySettingsIds kId = static_cast<SpdySettingsIds>(0x020304);
-
- SettingsMap settings;
- settings[kId] = SettingsFlagsAndValue(kFlags, kValue);
-
- EXPECT_EQ(kFlags, settings[kId].first);
- EXPECT_EQ(kValue, settings[kId].second);
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x01,
- 0x04, 0x03, 0x02, 0x01,
+ 0x07, 0x00, 0x00, 0x01,
0x0a, 0x0b, 0x0c, 0x0d,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x01,
- 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x00, 0x00, 0x07,
0x0a, 0x0b, 0x0c, 0x0d,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x14, 0x04, 0x00,
+ 0x00, 0x05, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x01, 0x02, 0x03, 0x04,
- 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x04, 0x0a, 0x0b, 0x0c,
+ 0x0d,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+ uint32 kValue = 0x0a0b0c0d;
+ SpdySettingsIR settings_ir;
+
+ SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
+ SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE;
+ SettingsMap settings;
+ settings[kId] = SettingsFlagsAndValue(kFlags, kValue);
+ EXPECT_EQ(kFlags, settings[kId].first);
+ EXPECT_EQ(kValue, settings[kId].second);
+ settings_ir.AddSetting(kId,
+ kFlags & SETTINGS_FLAG_PLEASE_PERSIST,
+ kFlags & SETTINGS_FLAG_PERSISTED,
+ kValue);
+
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2578,66 +2420,90 @@ TEST_P(SpdyFramerTest, CreateSettings) {
{
const char kDescription[] = "Basic SETTINGS frame";
- SettingsMap settings;
- AddSpdySettingFromWireFormat(
- &settings, 0x00000000, 0x00000001); // 1st Setting
- AddSpdySettingFromWireFormat(
- &settings, 0x01000001, 0x00000002); // 2nd Setting
- AddSpdySettingFromWireFormat(
- &settings, 0x02000002, 0x00000003); // 3rd Setting
- AddSpdySettingFromWireFormat(
- &settings, 0x03000003, 0xff000004); // 4th Setting
-
- const unsigned char kV3FrameData[] = { // Also applies for V2.
+ const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x24,
0x00, 0x00, 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x00, // 1st Setting
- 0x00, 0x00, 0x00, 0x01,
- 0x01, 0x00, 0x00, 0x01, // 2nd Setting
- 0x00, 0x00, 0x00, 0x02,
- 0x02, 0x00, 0x00, 0x02, // 3rd Setting
- 0x00, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x03, // 4th Setting
- 0xff, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x00, 0x00, // 1st Setting
+ 0x00, 0x00, 0x00, 0x05,
+ 0x02, 0x00, 0x00, 0x00, // 2nd Setting
+ 0x00, 0x00, 0x00, 0x06,
+ 0x03, 0x00, 0x00, 0x00, // 3rd Setting
+ 0x00, 0x00, 0x00, 0x07,
+ 0x04, 0x00, 0x00, 0x00, // 4th Setting
+ 0x00, 0x00, 0x00, 0x08,
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, spdy_version_ch_, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x01, // 1st Setting
+ 0x00, 0x00, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x02, // 2nd Setting
+ 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x03, // 3rd Setting
+ 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x04, // 4th Setting
+ 0x00, 0x00, 0x00, 0x08,
};
+ // These end up seemingly out of order because of the way that our internal
+ // ordering for settings_ir works. HTTP2 has no requirement on ordering on
+ // the wire.
const unsigned char kV4FrameData[] = {
- 0x00, 0x2c, 0x04, 0x00,
+ 0x00, 0x14, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x00, // 1st Setting
- 0x00, 0x00, 0x00, 0x01,
- 0x01, 0x00, 0x00, 0x01, // 2nd Setting
- 0x00, 0x00, 0x00, 0x02,
- 0x02, 0x00, 0x00, 0x02, // 3rd Setting
- 0x00, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x03, // 4th Setting
- 0xff, 0x00, 0x00, 0x04,
+ 0x03, // 3rd Setting
+ 0x00, 0x00, 0x00, 0x07,
+ 0x04, // 4th Setting
+ 0x00, 0x00, 0x00, 0x08,
+ 0x01, // 1st Setting
+ 0x00, 0x00, 0x00, 0x05,
+ 0x02, // 2nd Setting
+ 0x00, 0x00, 0x00, 0x06,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
- if (IsSpdy4()) {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
- } else {
+
+ SpdySettingsIR settings_ir;
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
+ false, // persist
+ false, // persisted
+ 5);
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
+ false, // persist
+ false, // persisted
+ 6);
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
+ false, // persist
+ false, // persisted
+ 7);
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 4),
+ false, // persist
+ false, // persisted
+ 8);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
+
+ if (IsSpdy2()) {
+ CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
+ } else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+ } else {
+ CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] = "Empty SETTINGS frame";
- SettingsMap settings;
-
const unsigned char kV3FrameData[] = { // Also applies for V2.
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x04, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+ SpdySettingsIR settings_ir;
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -2657,14 +2523,34 @@ TEST_P(SpdyFramerTest, CreatePingFrame) {
0x12, 0x34, 0x56, 0x78,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x06, 0x00,
+ 0x00, 0x08, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9a, 0xbc, 0xde, 0xff,
+ };
+ const unsigned char kV4FrameDataWithAck[] = {
+ 0x00, 0x08, 0x06, 0x01,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x56, 0x78,
+ 0x9a, 0xbc, 0xde, 0xff,
};
- scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u));
+ scoped_ptr<SpdyFrame> frame;
if (IsSpdy4()) {
+ const SpdyPingId kPingId = 0x123456789abcdeffULL;
+ SpdyPingIR ping_ir(kPingId);
+ // Tests SpdyPingIR when the ping is not an ack.
+ ASSERT_FALSE(ping_ir.is_ack());
+ frame.reset(framer.SerializePing(ping_ir));
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+
+ // Tests SpdyPingIR when the ping is an ack.
+ ping_ir.set_is_ack(true);
+ frame.reset(framer.SerializePing(ping_ir));
+ CompareFrame(kDescription, *frame,
+ kV4FrameDataWithAck, arraysize(kV4FrameDataWithAck));
+
} else {
+ frame.reset(framer.SerializePing(SpdyPingIR(0x12345678ull)));
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
@@ -2678,21 +2564,23 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
+ 0x00, 0x00, 0x00, 0x00, // Status
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x10, 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0a, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream id
+ 0x00, 0x00, 0x00, 0x00, // Status
+ 0x47, 0x41, // Opaque Description
};
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0, GOAWAY_OK));
+ SpdyGoAwayIR goaway_ir(0, GOAWAY_OK, "GA");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2707,22 +2595,23 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
- 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
+ 0x00, 0x00, 0x00, 0x01, // Status: PROTOCOL_ERROR.
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x10, 0x07, 0x00,
+ 0x00, 0x0a, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
+ 0x00, 0x00, 0x00, 0x02, // Status: INTERNAL_ERROR.
+ 0x47, 0x41, // Opaque Description
};
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF,
- GOAWAY_INTERNAL_ERROR));
+ SpdyGoAwayIR goaway_ir(0x7FFFFFFF, GOAWAY_INTERNAL_ERROR, "GA");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2740,10 +2629,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
{
const char kDescription[] = "HEADERS frame, no FIN";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x1C,
@@ -2769,19 +2654,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
0x03, 'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x28, 0x08, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x03, 'b', 'a', 'r'
+ 0x00, 0x12, 0x01, 0x04, // Headers: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x03, 0x62, // oo.b
+ 0x61, 0x72, // ar
};
- scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
- 1, CONTROL_FLAG_NONE, &headers));
+ SpdyHeadersIR headers_ir(1);
+ headers_ir.SetHeader("bar", "foo");
+ headers_ir.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2795,10 +2679,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
const char kDescription[] =
"HEADERS frame with a 0-length header name, FIN, max stream ID";
- SpdyHeaderBlock headers;
- headers[std::string()] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
@@ -2824,19 +2704,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
'r'
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x25, 0x08, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03,
- 'f', 'o', 'o', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'b', 'a',
- 'r'
+ 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x00, 0x03, 0x66, // @..f
+ 0x6f, 0x6f, 0x00, 0x03, // oo@.
+ 0x66, 0x6f, 0x6f, 0x03, // foo.
+ 0x62, 0x61, 0x72, // bar
};
- scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
- 0x7fffffff, CONTROL_FLAG_FIN, &headers));
+ SpdyHeadersIR headers_ir(0x7fffffff);
+ headers_ir.set_fin(true);
+ headers_ir.SetHeader("", "foo");
+ headers_ir.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2850,10 +2729,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
const char kDescription[] =
"HEADERS frame with a 0-length header val, FIN, max stream ID";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
@@ -2879,19 +2754,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
0x00
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x25, 0x08, 0x01,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x00
+ 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS
+ 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x00, // oo.
};
- scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
- 0x7fffffff, CONTROL_FLAG_FIN, &headers));
+ SpdyHeadersIR headers_ir(0x7fffffff);
+ headers_ir.set_fin(true);
+ headers_ir.SetHeader("bar", "foo");
+ headers_ir.SetHeader("foo", "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2912,10 +2786,6 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
{
const char kDescription[] = "HEADERS frame, no FIN";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
-
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x32,
@@ -2950,30 +2820,16 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
0x00, 0x00, 0x00, 0xff,
0xff,
};
- const unsigned char kV4FrameData[] = {
- 0x00, 0x35, 0x08, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- 0x38, 0xea, 0xe3, 0xc6,
- 0xa7, 0xc2, 0x02, 0xe5,
- 0x0e, 0x50, 0xc2, 0x4b,
- 0x4a, 0x04, 0xe5, 0x0b,
- 0x66, 0x80, 0x00, 0x4a,
- 0xcb, 0xcf, 0x07, 0x08,
- 0x20, 0x10, 0x95, 0x96,
- 0x9f, 0x0f, 0xa2, 0x00,
- 0x02, 0x28, 0x29, 0xb1,
- 0x08, 0x20, 0x80, 0x00,
- 0x00, 0x00, 0x00, 0xff,
- 0xff
- };
- scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
- 1, CONTROL_FLAG_NONE, &headers));
+ SpdyHeadersIR headers_ir(1);
+ headers_ir.SetHeader("bar", "foo");
+ headers_ir.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
- CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+ // Deflate compression doesn't apply to HPACK.
}
}
}
@@ -2991,12 +2847,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x09, 0x00,
+ 0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
scoped_ptr<SpdyFrame> frame(
- framer.CreateWindowUpdate(1, 1));
+ framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -3013,11 +2869,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x09, 0x00,
+ 0x00, 0x04, 0x08, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
+ SpdyWindowUpdateIR(0x7FFFFFFF, 1)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -3034,11 +2891,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
0x7f, 0xff, 0xff, 0xff,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x0c, 0x09, 0x00,
+ 0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
+ SpdyWindowUpdateIR(1, 0x7FFFFFFF)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
@@ -3048,103 +2906,109 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
}
TEST_P(SpdyFramerTest, SerializeBlocked) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
SpdyFramer framer(spdy_version_);
const char kDescription[] = "BLOCKED frame";
+ const unsigned char kType = static_cast<unsigned char>(
+ SpdyConstants::SerializeFrameType(spdy_version_, BLOCKED));
const unsigned char kFrameData[] = {
- 0x00, 0x08, 0x0b, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
};
SpdyBlockedIR blocked_ir(0);
scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(blocked_ir));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+}
+
+TEST_P(SpdyFramerTest, CreateBlocked) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ SpdyFramer framer(spdy_version_);
+
+ const char kDescription[] = "BLOCKED frame";
+ const SpdyStreamId kStreamId = 3;
+ scoped_ptr<SpdySerializedFrame> frame_serialized(
+ framer.SerializeBlocked(SpdyBlockedIR(kStreamId)));
+ SpdyBlockedIR blocked_ir(kStreamId);
+ scoped_ptr<SpdySerializedFrame> frame_created(
+ framer.SerializeFrame(blocked_ir));
+
+ CompareFrames(kDescription, *frame_serialized, *frame_created);
}
TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
-
const char kDescription[] = "PUSH_PROMISE frame";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
const unsigned char kFrameData[] = {
- 0x00, 0x2C, 0x0C, 0x00, // length = 44, type = 12, flags = 0
- 0x00, 0x00, 0x00, 0x2A, // stream id = 42
- 0x00, 0x00, 0x00, 0x39, // promised stream id = 57
- 0x00, 0x00, 0x00, 0x02, // start of uncompressed header block
- 0x00, 0x00, 0x00, 0x03,
- 'b', 'a', 'r', 0x00,
- 0x00, 0x00, 0x03, 'f',
- 'o', 'o', 0x00, 0x00,
- 0x00, 0x03, 'f', 'o',
- 'o', 0x00, 0x00, 0x00,
- 0x03, 'b', 'a', 'r' // end of uncompressed header block
+ 0x00, 0x16, 0x05, 0x04, // PUSH_PROMISE: END_HEADERS
+ 0x00, 0x00, 0x00, 0x2a, // Stream 42
+ 0x00, 0x00, 0x00, 0x39, // Promised stream 57
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x03, 0x62, // oo.b
+ 0x61, 0x72, // ar
};
- scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise(
- 42, 57, &headers));
+ SpdyPushPromiseIR push_promise(42, 57);
+ push_promise.SetHeader("bar", "foo");
+ push_promise.SetHeader("foo", "bar");
+ scoped_ptr<SpdySerializedFrame> frame(
+ framer.SerializePushPromise(push_promise));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
-TEST_P(SpdyFramerTest, CreatePushPromiseCompressed) {
- if (spdy_version_ < SPDY4) {
+TEST_P(SpdyFramerTest, CreateAltSvc) {
+ if (spdy_version_ <= SPDY3) {
return;
}
SpdyFramer framer(spdy_version_);
- framer.set_enable_compression(true);
-
- const char kDescription[] = "PUSH_PROMISE frame";
- SpdyHeaderBlock headers;
- headers["bar"] = "foo";
- headers["foo"] = "bar";
+ const char kDescription[] = "ALTSVC frame";
+ const unsigned char kType = static_cast<unsigned char>(
+ SpdyConstants::SerializeFrameType(spdy_version_, ALTSVC));
const unsigned char kFrameData[] = {
- 0x00, 0x39, 0x0C, 0x00, // length = 57, type = 12, flags = 0
- 0x00, 0x00, 0x00, 0x2A, // stream id = 42
- 0x00, 0x00, 0x00, 0x39, // promised stream id = 57
- 0x38, 0xea, 0xe3, 0xc6, // start of compressed header block
- 0xa7, 0xc2, 0x02, 0xe5,
- 0x0e, 0x50, 0xc2, 0x4b,
- 0x4a, 0x04, 0xe5, 0x0b,
- 0x66, 0x80, 0x00, 0x4a,
- 0xcb, 0xcf, 0x07, 0x08,
- 0x20, 0x10, 0x95, 0x96,
- 0x9f, 0x0f, 0xa2, 0x00,
- 0x02, 0x28, 0x29, 0xb1,
- 0x08, 0x20, 0x80, 0x00,
- 0x00, 0x00, 0x00, 0xff,
- 0xff // end of compressed header block
+ 0x00, 0x17, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05,
+ 0x01, 0xbb, 0x00, 0x04, // Port = 443
+ 'p', 'i', 'd', '1', // Protocol-ID
+ 0x04, 'h', 'o', 's',
+ 't', 'o', 'r', 'i',
+ 'g', 'i', 'n',
};
-
- scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise(
- 42, 57, &headers));
+ SpdyAltSvcIR altsvc_ir(3);
+ altsvc_ir.set_max_age(5);
+ altsvc_ir.set_port(443);
+ altsvc_ir.set_protocol_id("pid1");
+ altsvc_ir.set_host("host");
+ altsvc_ir.set_origin("origin");
+ scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) {
- SpdyHeaderBlock headers;
- headers["aa"] = "vv";
- headers["bb"] = "ww";
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("aa", "vv");
+ syn_stream.SetHeader("bb", "ww");
+ SpdyHeaderBlock headers = syn_stream.name_value_block();
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
@@ -3156,33 +3020,35 @@ TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) {
}
TEST_P(SpdyFramerTest, ReadCompressedSynReplyHeaderBlock) {
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- headers["gamma"] = "delta";
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynReply(1, // stream_id
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynReplyIR syn_reply(1);
+ syn_reply.SetHeader("alpha", "beta");
+ syn_reply.SetHeader("gamma", "delta");
+ SpdyHeaderBlock headers = syn_reply.name_value_block();
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynReply(syn_reply));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
- EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ if (IsSpdy4()) {
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ } else {
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ }
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- headers["gamma"] = "delta";
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateHeaders(1, // stream_id
- CONTROL_FLAG_NONE,
- &headers));
+ SpdyHeadersIR headers_ir(1);
+ headers_ir.SetHeader("alpha", "beta");
+ headers_ir.SetHeader("gamma", "delta");
+ SpdyHeaderBlock headers = headers_ir.name_value_block();
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers_ir));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
@@ -3201,14 +3067,13 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
}
TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
- SpdyHeaderBlock headers;
- headers["alpha"] = "beta";
- headers["gamma"] = "delta";
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateHeaders(1, // stream_id
- CONTROL_FLAG_FIN,
- &headers));
+ SpdyHeadersIR headers_ir(1);
+ headers_ir.set_fin(true);
+ headers_ir.SetHeader("alpha", "beta");
+ headers_ir.SetHeader("gamma", "delta");
+ SpdyHeaderBlock headers = headers_ir.name_value_block();
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers_ir));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
@@ -3227,32 +3092,25 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
}
TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
+ if (spdy_version_ > SPDY3) {
+ // TODO(jgraettinger): This test setup doesn't work with HPACK.
+ return;
+ }
// First find the size of the header value in order to just reach the control
// frame max size.
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
- SpdyHeaderBlock headers;
- headers["aa"] = "";
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("aa", "");
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
const size_t kBigValueSize =
framer.GetControlFrameBufferMaxSize() - control_frame->size();
- // Create a frame at exatly that size.
+ // Create a frame at exactly that size.
string big_value(kBigValueSize, 'x');
- headers["aa"] = big_value.c_str();
- control_frame.reset(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ syn_stream.SetHeader("aa", big_value);
+ control_frame.reset(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
EXPECT_EQ(framer.GetControlFrameBufferMaxSize(), control_frame->size());
@@ -3269,32 +3127,29 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
}
TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
+ if (spdy_version_ > SPDY3) {
+ // TODO(jgraettinger): This test setup doesn't work with HPACK.
+ return;
+ }
// First find the size of the header value in order to just reach the control
// frame max size.
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
- SpdyHeaderBlock headers;
- headers["aa"] = "";
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.SetHeader("aa", "");
+ syn_stream.set_priority(1);
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
const size_t kBigValueSize =
framer.GetControlFrameBufferMaxSize() - control_frame->size() + 1;
// Create a frame at exatly that size.
string big_value(kBigValueSize, 'x');
- headers["aa"] = big_value.c_str();
- control_frame.reset(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ syn_stream.SetHeader("aa", big_value);
+ // Upstream branches here and wraps SPDY4 with EXPECT_DEBUG_DFATAL. We
+ // neither support that in Chromium, nor do we use the same DFATAL (see
+ // SpdyFrameBuilder::WriteFramePrefix()).
+ control_frame.reset(framer.SerializeSynStream(syn_stream));
+
EXPECT_TRUE(control_frame.get() != NULL);
EXPECT_EQ(framer.GetControlFrameBufferMaxSize() + 1,
control_frame->size());
@@ -3312,25 +3167,78 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
EXPECT_EQ(0u, visitor.header_buffer_length_);
}
+TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+ framer.set_enable_compression(false);
+ SpdyHeadersIR headers(1);
+
+ // Exact payload length will change with HPACK, but this should be long
+ // enough to cause an overflow.
+ const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize();
+ string big_value(kBigValueSize, 'x');
+ headers.SetHeader("aa", big_value);
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize());
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->size());
+ EXPECT_TRUE(visitor.header_buffer_valid_);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(1, visitor.continuation_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+}
+
+TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+ framer.set_enable_compression(false);
+ SpdyPushPromiseIR push_promise(1, 2);
+
+ // Exact payload length will change with HPACK, but this should be long
+ // enough to cause an overflow.
+ const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize();
+ string big_value(kBigValueSize, 'x');
+ push_promise.SetHeader("aa", big_value);
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.SerializePushPromise(push_promise));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize());
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->size());
+ EXPECT_TRUE(visitor.header_buffer_valid_);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.push_promise_frame_count_);
+ EXPECT_EQ(1, visitor.continuation_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+}
+
// Check that the framer stops delivering header data chunks once the visitor
// declares it doesn't want any more. This is important to guard against
// "zip bomb" types of attacks.
TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
- SpdyHeaderBlock headers;
const size_t kHeaderBufferChunks = 4;
const size_t kHeaderBufferSize =
TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
const size_t kBigValueSize = kHeaderBufferSize * 2;
string big_value(kBigValueSize, 'x');
- headers["aa"] = big_value.c_str();
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_FIN, // half close
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.set_fin(true);
+ syn_stream.SetHeader("aa", big_value);
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.set_header_buffer_size(kHeaderBufferSize);
@@ -3360,20 +3268,19 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
}
TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) {
- SpdyHeaderBlock headers;
- headers["aa"] = "alpha beta gamma delta";
+ if (spdy_version_ > SPDY3) {
+ // Deflate compression doesn't apply to HPACK.
+ return;
+ }
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
// Construct a SYN_STREAM control frame without compressing the header block,
// and have the framer try to decompress it. This will cause the framer to
// deal with a decompression error.
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateSynStream(1, // stream_id
- 0, // associated_stream_id
- 1, // priority
- 0, // credential_slot
- CONTROL_FLAG_NONE,
- &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("aa", "alpha beta gamma delta");
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
@@ -3386,6 +3293,7 @@ TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) {
}
TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
+ SpdyFramer framer(spdy_version_);
// Create a GoAway frame that has a few extra bytes at the end.
// We create enough overhead to overflow the framer's control frame buffer.
ASSERT_GE(250u, SpdyFramer::kControlFrameBufferSize);
@@ -3393,15 +3301,20 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
const unsigned char kV3FrameData[] = { // Also applies for V2.
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, length,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream ID
+ 0x00, 0x00, 0x00, 0x00, // Status
};
+
+ // SPDY version 4 and up GOAWAY frames are only bound to a minimal length,
+ // since it may carry opaque data. Verify that minimal length is tested.
+ const unsigned char less_than_min_length =
+ framer.GetGoAwayMinimumSize() - framer.GetControlFrameHeaderSize() - 1;
const unsigned char kV4FrameData[] = {
- 0x00, static_cast<uint8>(length + 4), 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, static_cast<uint8>(less_than_min_length), 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
+ 0x00, 0x00, 0x00, 0x00, // Status
};
- SpdyFramer framer(spdy_version_);
const size_t pad_length =
length + framer.GetControlFrameHeaderSize() -
(IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData));
@@ -3426,49 +3339,65 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) {
SpdyFramer framer(spdy_version_);
- SettingsMap settings;
- scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ SpdySettingsIR settings_ir;
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
SetFrameLength(control_frame.get(), 0, spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
framer.GetControlFrameHeaderSize());
- // Should generate an error, since zero-len settings frames are unsupported.
- EXPECT_EQ(1, visitor.error_count_);
+ if (spdy_version_ <= SPDY3) {
+ // Should generate an error, since zero-len settings frames are unsupported.
+ EXPECT_EQ(1, visitor.error_count_);
+ } else {
+ // Zero-len settings frames are permitted as of SPDY 4.
+ EXPECT_EQ(0, visitor.error_count_);
+ }
}
// Tests handling of SETTINGS frames with invalid length.
TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) {
SpdyFramer framer(spdy_version_);
- SettingsMap settings;
+ SpdySettingsIR settings_ir;
+
// Add a setting to pad the frame so that we don't get a buffer overflow when
// calling SimulateInFramer() below.
- settings[SETTINGS_UPLOAD_BANDWIDTH] =
- SettingsFlagsAndValue(SETTINGS_FLAG_PLEASE_PERSIST, 0x00000002);
- scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
- const size_t kNewLength = 5;
+ settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE,
+ false,
+ false,
+ 0x00000002);
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
+ const size_t kNewLength = 14;
SetFrameLength(control_frame.get(), kNewLength, spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
framer.GetControlFrameHeaderSize() + kNewLength);
- // Should generate an error, since zero-len settings frames are unsupported.
+ // Should generate an error, since its not possible to have a
+ // settings frame of length kNewLength.
EXPECT_EQ(1, visitor.error_count_);
}
// Tests handling of SETTINGS frames larger than the frame buffer size.
TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
SpdyFramer framer(spdy_version_);
- SettingsMap settings;
- SpdySettingsFlags flags = SETTINGS_FLAG_PLEASE_PERSIST;
- settings[SETTINGS_UPLOAD_BANDWIDTH] =
- SettingsFlagsAndValue(flags, 0x00000002);
- settings[SETTINGS_DOWNLOAD_BANDWIDTH] =
- SettingsFlagsAndValue(flags, 0x00000003);
- settings[SETTINGS_ROUND_TRIP_TIME] = SettingsFlagsAndValue(flags, 0x00000004);
- scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ SpdySettingsIR settings_ir;
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
+ false, // persist
+ false, // persisted
+ 5);
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
+ false, // persist
+ false, // persisted
+ 6);
+ settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
+ false, // persist
+ false, // persisted
+ 7);
+
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
EXPECT_LT(SpdyFramer::kControlFrameBufferSize,
control_frame->size());
TestSpdyVisitor visitor(spdy_version_);
@@ -3479,7 +3408,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(0, visitor.error_count_);
- EXPECT_EQ(settings.size(), static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(3, visitor.setting_count_);
+ if (spdy_version_ > SPDY3) {
+ EXPECT_EQ(1, visitor.settings_ack_sent_);
+ }
// Read data in small chunks.
size_t framed_data = 0;
@@ -3494,7 +3426,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
framed_data += to_read;
}
EXPECT_EQ(0, visitor.error_count_);
- EXPECT_EQ(settings.size() * 2, static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(3 * 2, visitor.setting_count_);
+ if (spdy_version_ > SPDY3) {
+ EXPECT_EQ(2, visitor.settings_ack_sent_);
+ }
}
// Tests handling of SETTINGS frame with duplicate entries.
@@ -3524,14 +3459,13 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x24, 0x04, 0x00,
+ 0x00, 0x0f, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x01, // 1st Setting
+ 0x01, // 1st Setting
0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x01, // 2nd (duplicate) Setting
+ 0x01, // 2nd (duplicate) Setting
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x03, // 3rd (unprocessed) Setting
+ 0x03, // 3rd (unprocessed) Setting
0x00, 0x00, 0x00, 0x03,
};
@@ -3544,8 +3478,36 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
} else {
visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
}
- EXPECT_EQ(1, visitor.error_count_);
+
+ if (!IsSpdy4()) {
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.error_count_);
+ } else {
+ // In SPDY 4+, duplicate settings are allowed;
+ // each setting replaces the previous value for that setting.
+ EXPECT_EQ(3, visitor.setting_count_);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.settings_ack_sent_);
+ }
+}
+
+// Tests handling of SETTINGS_COMPRESS_DATA.
+TEST_P(SpdyFramerTest, AcceptSettingsCompressData) {
+ if (!IsSpdy4()) { return; }
+ SpdyFramer framer(spdy_version_);
+
+ const unsigned char kFrameData[] = {
+ 0x00, 0x05, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00,
+ 0x01,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(0, visitor.error_count_);
}
// Tests handling of SETTINGS frame with entries out of order.
@@ -3575,14 +3537,13 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x24, 0x04, 0x00,
+ 0x00, 0x0f, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x02, // 1st Setting
+ 0x02, // 1st Setting
0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x01, // 2nd (out of order) Setting
+ 0x01, // 2nd (out of order) Setting
0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x01, 0x03, // 3rd (unprocessed) Setting
+ 0x03, // 3rd (unprocessed) Setting
0x00, 0x00, 0x00, 0x03,
};
@@ -3595,14 +3556,111 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
} else {
visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
}
- EXPECT_EQ(1, visitor.error_count_);
- EXPECT_EQ(1, visitor.setting_count_);
+
+ if (!IsSpdy4()) {
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.error_count_);
+ } else {
+ // In SPDY 4+, settings are allowed in any order.
+ EXPECT_EQ(3, visitor.setting_count_);
+ EXPECT_EQ(0, visitor.error_count_);
+ // EXPECT_EQ(1, visitor.settings_ack_count_);
+ }
+}
+
+TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x04, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(0, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.settings_ack_received_);
+}
+
+
+TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const int kPaddingLen = 512; // So we get two bytes for padding length field.
+ const char data_payload[] = "hello";
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ SpdyDataIR data_ir(1, StringPiece(data_payload, strlen(data_payload)));
+ data_ir.set_padding_len(kPaddingLen);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+ ASSERT_TRUE(frame.get() != NULL);
+
+ int bytes_consumed = 0;
+
+ // Send the frame header.
+ EXPECT_CALL(visitor, OnDataFrameHeader(1,
+ kPaddingLen + strlen(data_payload),
+ false));
+ CHECK_EQ(8u, framer.ProcessInput(frame->data(), 8));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 8;
+
+ // Send the first byte of the padding length field.
+ CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 1;
+
+ // Send the second byte of the padding length field.
+ CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 1;
+
+ // Send the first two bytes of the data payload.
+ EXPECT_CALL(visitor, OnStreamFrameData(1, _, 2, false));
+ CHECK_EQ(2u, framer.ProcessInput(frame->data() + bytes_consumed, 2));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 2;
+
+ // Send the rest three bytes of the data payload.
+ EXPECT_CALL(visitor, OnStreamFrameData(1, _, 3, false));
+ CHECK_EQ(3u, framer.ProcessInput(frame->data() + bytes_consumed, 3));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 3;
+
+ // Send the first 100 bytes of the padding payload.
+ EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 100, false));
+ CHECK_EQ(100u, framer.ProcessInput(frame->data() + bytes_consumed, 100));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+ bytes_consumed += 100;
+
+ // Send rest of the padding payload.
+ EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 410, false));
+ CHECK_EQ(410u, framer.ProcessInput(frame->data() + bytes_consumed, 410));
+ CHECK_EQ(framer.state(), SpdyFramer::SPDY_RESET);
+ CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
}
TEST_P(SpdyFramerTest, ReadWindowUpdate) {
SpdyFramer framer(spdy_version_);
scoped_ptr<SpdyFrame> control_frame(
- framer.CreateWindowUpdate(1, 2));
+ framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
@@ -3611,180 +3669,446 @@ TEST_P(SpdyFramerTest, ReadWindowUpdate) {
EXPECT_EQ(2u, visitor.last_window_update_delta_);
}
-TEST_P(SpdyFramerTest, ReadCredentialFrame) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ReceiveCredentialFrame) {
+ if (!IsSpdy3()) {
+ return;
+ }
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(control_frame.get() != NULL);
+ const unsigned char kV3FrameData[] = { // Also applies for V2.
+ 0x80, spdy_version_ch_, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
+ visitor.SimulateInFramer(kV3FrameData, arraysize(kV3FrameData));
+ EXPECT_EQ(0, visitor.error_count_);
+}
+
+TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
+ if (!IsSpdy3()) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+ const unsigned char kV3FrameData[] = { // Also applies for V2.
+ 0x80, spdy_version_ch_, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ string multiple_frame_data(reinterpret_cast<const char*>(kV3FrameData),
+ arraysize(kV3FrameData));
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
+ multiple_frame_data.append(string(control_frame->data(),
+ control_frame->size()));
visitor.SimulateInFramer(
- reinterpret_cast<unsigned char*>(control_frame->data()),
- control_frame->size());
+ reinterpret_cast<unsigned const char*>(multiple_frame_data.data()),
+ multiple_frame_data.length());
EXPECT_EQ(0, visitor.error_count_);
- EXPECT_EQ(control_frame->size() - framer.GetControlFrameHeaderSize(),
- visitor.credential_buffer_length_);
- EXPECT_EQ(credential.slot, visitor.credential_.slot);
- EXPECT_EQ(credential.proof, visitor.credential_.proof);
- EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size());
- for (size_t i = 0; i < credential.certs.size(); i++) {
- EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]);
+ EXPECT_EQ(1u, visitor.last_window_update_stream_);
+ EXPECT_EQ(2u, visitor.last_window_update_delta_);
+}
+
+TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
+ if (spdy_version_ <= SPDY3) {
+ return;
}
+
+ SpdyFramer framer(spdy_version_);
+ framer.set_enable_compression(false);
+ const char kDescription[] = "CONTINUATION frame";
+
+ const unsigned char kFrameData[] = {
+ 0x00, 0x12, 0x09, 0x00, // CONTINUATION
+ 0x00, 0x00, 0x00, 0x2a, // Stream 42
+ 0x00, 0x03, 0x62, 0x61, // @.ba
+ 0x72, 0x03, 0x66, 0x6f, // r.fo
+ 0x6f, 0x00, 0x03, 0x66, // o@.f
+ 0x6f, 0x6f, 0x03, 0x62, // oo.b
+ 0x61, 0x72, // ar
+ };
+
+ SpdyContinuationIR continuation(42);
+ continuation.SetHeader("bar", "foo");
+ continuation.SetHeader("foo", "bar");
+ scoped_ptr<SpdySerializedFrame> frame(
+ framer.SerializeContinuation(continuation));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
-TEST_P(SpdyFramerTest, ReadCredentialFrameOneByteAtATime) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(control_frame.get() != NULL);
+ SpdyPushPromiseIR push_promise(42, 57);
+ push_promise.SetHeader("foo", "bar");
+ push_promise.SetHeader("bar", "foofoo");
+ SpdyHeaderBlock headers = push_promise.name_value_block();
+ scoped_ptr<SpdySerializedFrame> frame(
+ framer.SerializePushPromise(push_promise));
+ EXPECT_TRUE(frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = false;
- // Read one byte at a time to make sure we handle edge cases
- unsigned char* data =
- reinterpret_cast<unsigned char*>(control_frame->data());
- for (size_t idx = 0; idx < control_frame->size(); ++idx) {
- visitor.SimulateInFramer(data + idx, 1);
- ASSERT_EQ(0, visitor.error_count_);
+ visitor.use_compression_ = true;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(frame->data()),
+ frame->size());
+ EXPECT_EQ(42u, visitor.last_push_promise_stream_);
+ EXPECT_EQ(57u, visitor.last_push_promise_promised_stream_);
+ EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
+}
+
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndPadding) {
+ if (spdy_version_ <= SPDY3) {
+ return;
}
+
+ const unsigned char kInput[] = {
+ 0x00, 0x14, 0x01, 0x08, // HEADERS: PAD_LOW
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x03, // Padding of 3.
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+ 0x00, 0x00, 0x00,
+
+ 0x00, 0x1a, 0x09, 0x18, // CONTINUATION: PAD_LOW & PAD_HIGH
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x04, // Padding of 4.
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x08, 0x62, 0x61, 0x7a,
+ 0x3d, 0x62, 0x69, 0x6e,
+ 0x67, 0x00, 0x06, 0x63,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x13, 0x09, 0x0c, // CONTINUATION: PAD_LOW & END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, // Padding of 0.
+ 0x6f, 0x6f, 0x6b, 0x69,
+ 0x65, 0x00, 0x00, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65,
+ 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
EXPECT_EQ(0, visitor.error_count_);
- EXPECT_EQ(control_frame->size() - framer.GetControlFrameHeaderSize(),
- visitor.credential_buffer_length_);
- EXPECT_EQ(credential.slot, visitor.credential_.slot);
- EXPECT_EQ(credential.proof, visitor.credential_.proof);
- EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size());
- for (size_t i = 0; i < credential.certs.size(); i++) {
- EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(2, visitor.continuation_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+ EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+
+ EXPECT_THAT(visitor.headers_, ElementsAre(
+ Pair("cookie", "foo=bar; baz=bing; "),
+ Pair("name", "value")));
+}
+
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) {
+ if (spdy_version_ <= SPDY3) {
+ return;
}
+
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x01, 0x01, // HEADERS: FIN
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+
+ 0x00, 0x14, 0x09, 0x00, // CONTINUATION
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x08, 0x62, 0x61, 0x7a,
+ 0x3d, 0x62, 0x69, 0x6e,
+ 0x67, 0x00, 0x06, 0x63,
+
+ 0x00, 0x12, 0x09, 0x04, // CONTINUATION: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x6f, 0x6f, 0x6b, 0x69,
+ 0x65, 0x00, 0x00, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65,
+ 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65,
+ };
+
+ SpdyFramer framer(spdy_version_);
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(2, visitor.continuation_count_);
+ EXPECT_EQ(1, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+
+ EXPECT_THAT(visitor.headers_, ElementsAre(
+ Pair("cookie", "foo=bar; baz=bing; "),
+ Pair("name", "value")));
}
-TEST_P(SpdyFramerTest, ReadCredentialFrameWithNoPayload) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuationAndPadding) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kInput[] = {
+ 0x00, 0x18, 0x05, 0x18, // PUSH_PROMISE: PAD_LOW & PAD_HIGH
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x00, 0x00, 0x2A, // Promised stream 42
+ 0x00, 0x02, // Padding of 2.
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+ 0x00, 0x00,
+
+ 0x00, 0x14, 0x09, 0x00, // CONTINUATION:
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x08, 0x62, 0x61, 0x7a,
+ 0x3d, 0x62, 0x69, 0x6e,
+ 0x67, 0x00, 0x06, 0x63,
+
+ 0x00, 0x17, 0x09, 0x0c, // CONTINUATION: PAD_LOW & END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x04, // Padding of 4.
+ 0x6f, 0x6f, 0x6b, 0x69,
+ 0x65, 0x00, 0x00, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65,
+ 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = false;
- SetFrameLength(control_frame.get(), 0, spdy_version_);
- unsigned char* data =
- reinterpret_cast<unsigned char*>(control_frame->data());
- visitor.SimulateInFramer(data, framer.GetControlFrameHeaderSize());
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1u, visitor.last_push_promise_stream_);
+ EXPECT_EQ(42u, visitor.last_push_promise_promised_stream_);
+ EXPECT_EQ(2, visitor.continuation_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+ EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+
+ EXPECT_THAT(visitor.headers_, ElementsAre(
+ Pair("cookie", "foo=bar; baz=bing; "),
+ Pair("name", "value")));
+}
+
+TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x01, 0x00, // HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+
+ 0x00, 0x14, 0x09, 0x00, // CONTINUATION
+ 0x00, 0x00, 0x00, 0x02, // Stream 2
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x08, 0x62, 0x61, 0x7a,
+ 0x3d, 0x62, 0x69, 0x6e,
+ 0x67, 0x00, 0x06, 0x63,
+ };
+
+ SpdyFramer framer(spdy_version_);
+ TestSpdyVisitor visitor(spdy_version_);
+ framer.set_visitor(&visitor);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.continuation_count_);
+ EXPECT_EQ(0u, visitor.header_buffer_length_);
}
-TEST_P(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x09, 0x00, // CONTINUATION
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+ };
+
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = false;
- unsigned char* data =
- reinterpret_cast<unsigned char*>(control_frame->data());
- size_t offset = framer.GetControlFrameHeaderSize() + 4;
- data[offset] = 0xFF; // Proof length is past the end of the frame
- visitor.SimulateInFramer(
- data, control_frame->size());
+ framer.set_visitor(&visitor);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+ visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ EXPECT_EQ(0, visitor.continuation_count_);
+ EXPECT_EQ(0u, visitor.header_buffer_length_);
}
-TEST_P(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x01, 0x00, // HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+ };
+
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> control_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = false;
- unsigned char* data =
- reinterpret_cast<unsigned char*>(control_frame->data());
- size_t offset = framer.GetCredentialMinimumSize() + 1;
- data[offset] = 0xFF; // Proof length is past the end of the frame
- visitor.SimulateInFramer(
- data, control_frame->size());
+ framer.set_visitor(&visitor);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+ visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.continuation_count_);
+ EXPECT_EQ(0u, visitor.header_buffer_length_);
+ EXPECT_EQ(0, visitor.data_frame_count_);
}
-// Regression test for parsing issue found in b/8278897.
-TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x01, 0x00, // HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+
+ 0x00, 0x14, 0x08, 0x00, // HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f, // (Note this is a valid continued encoding).
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x08, 0x62, 0x61, 0x7a,
+ 0x3d, 0x62, 0x69, 0x6e,
+ 0x67, 0x00, 0x06, 0x63,
+ };
+
SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> credential_frame(
- framer.CreateCredentialFrame(credential));
- EXPECT_TRUE(credential_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = false;
- string multiple_frame_data(credential_frame->data(),
- credential_frame->size());
- scoped_ptr<SpdyFrame> goaway_frame(framer.CreateGoAway(0, GOAWAY_OK));
- multiple_frame_data.append(string(goaway_frame->data(),
- goaway_frame->size()));
- visitor.SimulateInFramer(
- reinterpret_cast<unsigned const char*>(multiple_frame_data.data()),
- multiple_frame_data.length());
- EXPECT_EQ(0, visitor.error_count_);
- EXPECT_EQ(credential_frame->size() - framer.GetControlFrameHeaderSize(),
- visitor.credential_buffer_length_);
- EXPECT_EQ(credential.slot, visitor.credential_.slot);
- EXPECT_EQ(credential.proof, visitor.credential_.proof);
- EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size());
- for (size_t i = 0; i < credential.certs.size(); i++) {
- EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]);
+ framer.set_visitor(&visitor);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+ visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.continuation_count_);
+ EXPECT_EQ(0u, visitor.header_buffer_length_);
+ EXPECT_EQ(0, visitor.data_frame_count_);
+}
+
+TEST_P(SpdyFramerTest, EndSegmentOnDataFrame) {
+ if (spdy_version_ <= SPDY3) {
+ return;
}
+ const unsigned char kInput[] = {
+ 0x00, 0x0c, 0x00, 0x02, // DATA: END_SEGMENT
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+ // TODO(jgraettinger): Verify END_SEGMENT when support is added.
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(12, visitor.data_bytes_);
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
}
-TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
- if (spdy_version_ < 4) {
+TEST_P(SpdyFramerTest, EndSegmentOnHeadersFrame) {
+ if (spdy_version_ <= SPDY3) {
return;
}
+ const unsigned char kInput[] = {
+ 0x00, 0x10, 0x01, 0x06, // HEADERS: END_SEGMENT | END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0x00, 0x06, 0x63, 0x6f,
+ 0x6f, 0x6b, 0x69, 0x65,
+ 0x07, 0x66, 0x6f, 0x6f,
+ 0x3d, 0x62, 0x61, 0x72,
+ };
- SpdyHeaderBlock headers;
- headers["foo"] = "bar";
- headers["bar"] = "foofoo";
- SpdyFramer framer(spdy_version_);
- scoped_ptr<SpdyFrame> frame(framer.CreatePushPromise(42, 57, &headers));
- EXPECT_TRUE(frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
- visitor.use_compression_ = true;
- visitor.SimulateInFramer(
- reinterpret_cast<unsigned char*>(frame->data()),
- frame->size());
- EXPECT_EQ(42u, visitor.last_push_promise_stream_);
- EXPECT_EQ(57u, visitor.last_push_promise_promised_stream_);
- EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
+ visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+ // TODO(jgraettinger): Verify END_SEGMENT when support is added.
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+
+ EXPECT_THAT(visitor.headers_, ElementsAre(
+ Pair("cookie", "foo=bar")));
}
TEST_P(SpdyFramerTest, ReadGarbage) {
@@ -3797,6 +4121,23 @@ TEST_P(SpdyFramerTest, ReadGarbage) {
EXPECT_EQ(1, visitor.error_count_);
}
+TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
+ if (!IsSpdy4()) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+ const unsigned char kFrameData[] = {
+ 0x00, 0x10, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ };
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) {
if (IsSpdy4()) {
// Not valid for SPDY 4 since there is no version field.
@@ -3813,37 +4154,55 @@ TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) {
EXPECT_EQ(1, visitor.error_count_);
}
+TEST_P(SpdyFramerTest, ReadGarbageHPACKEncoding) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ const unsigned char kInput[] = {
+ 0x00, 0x12, 0x01, 0x04, // HEADER: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream 1
+ 0xef, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kInput, arraysize(kInput));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
TEST_P(SpdyFramerTest, SizesTest) {
SpdyFramer framer(spdy_version_);
EXPECT_EQ(8u, framer.GetDataFrameMinimumSize());
- if (IsSpdy4()) {
+ if (IsSpdy4() || IsSpdy5()) {
EXPECT_EQ(8u, framer.GetSynReplyMinimumSize());
- EXPECT_EQ(12u, framer.GetRstStreamSize());
- EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
- EXPECT_EQ(12u, framer.GetPingSize());
- EXPECT_EQ(16u, framer.GetGoAwaySize());
+ EXPECT_EQ(12u, framer.GetRstStreamMinimumSize());
+ EXPECT_EQ(8u, framer.GetSettingsMinimumSize());
+ EXPECT_EQ(16u, framer.GetPingSize());
+ EXPECT_EQ(16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(8u, framer.GetHeadersMinimumSize());
EXPECT_EQ(12u, framer.GetWindowUpdateSize());
- EXPECT_EQ(10u, framer.GetCredentialMinimumSize());
EXPECT_EQ(8u, framer.GetBlockedSize());
EXPECT_EQ(12u, framer.GetPushPromiseMinimumSize());
+ EXPECT_EQ(17u, framer.GetAltSvcMinimumSize());
EXPECT_EQ(8u, framer.GetFrameMinimumSize());
- EXPECT_EQ(65535u, framer.GetFrameMaximumSize());
- EXPECT_EQ(65527u, framer.GetDataFrameMaximumPayload());
+ EXPECT_EQ(16383u, framer.GetFrameMaximumSize());
+ EXPECT_EQ(16375u, framer.GetDataFrameMaximumPayload());
} else {
EXPECT_EQ(8u, framer.GetControlFrameHeaderSize());
EXPECT_EQ(18u, framer.GetSynStreamMinimumSize());
EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetSynReplyMinimumSize());
- EXPECT_EQ(16u, framer.GetRstStreamSize());
+ EXPECT_EQ(16u, framer.GetRstStreamMinimumSize());
EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
EXPECT_EQ(12u, framer.GetPingSize());
- EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwaySize());
+ EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize());
EXPECT_EQ(16u, framer.GetWindowUpdateSize());
- EXPECT_EQ(10u, framer.GetCredentialMinimumSize());
EXPECT_EQ(8u, framer.GetFrameMinimumSize());
- EXPECT_EQ(16777215u, framer.GetFrameMaximumSize());
- EXPECT_EQ(16777207u, framer.GetDataFrameMaximumPayload());
+ EXPECT_EQ(16777223u, framer.GetFrameMaximumSize());
+ EXPECT_EQ(16777215u, framer.GetDataFrameMaximumPayload());
}
}
@@ -3872,15 +4231,15 @@ TEST_P(SpdyFramerTest, StateToStringTest) {
EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
- EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD",
- SpdyFramer::StateToString(
- SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD));
EXPECT_STREQ("SPDY_SETTINGS_FRAME_PAYLOAD",
SpdyFramer::StateToString(
SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD));
+ EXPECT_STREQ("SPDY_ALTSVC_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD));
EXPECT_STREQ("UNKNOWN_STATE",
SpdyFramer::StateToString(
- SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD + 1));
+ SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD + 1));
}
TEST_P(SpdyFramerTest, ErrorCodeToStringTest) {
@@ -3932,7 +4291,7 @@ TEST_P(SpdyFramerTest, StatusCodeToStringTest) {
EXPECT_STREQ("FLOW_CONTROL_ERROR",
SpdyFramer::StatusCodeToString(RST_STREAM_FLOW_CONTROL_ERROR));
EXPECT_STREQ("UNKNOWN_STATUS",
- SpdyFramer::StatusCodeToString(RST_STREAM_NUM_STATUS_CODES));
+ SpdyFramer::StatusCodeToString(-1));
}
TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
@@ -3960,6 +4319,8 @@ TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
SpdyFramer::FrameTypeToString(PUSH_PROMISE));
EXPECT_STREQ("CREDENTIAL",
SpdyFramer::FrameTypeToString(CREDENTIAL));
+ EXPECT_STREQ("CONTINUATION",
+ SpdyFramer::FrameTypeToString(CONTINUATION));
}
TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
@@ -3968,7 +4329,7 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
return;
}
{
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -3980,7 +4341,7 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
{
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -3993,16 +4354,20 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
}
}
-TEST_P(SpdyFramerTest, DataFrameFlags) {
+TEST_P(SpdyFramerTest, DataFrameFlagsV2V3) {
+ if (spdy_version_ > SPDY3) {
+ return;
+ }
+
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(
- framer.CreateDataFrame(1, "hello", 5, DATA_FLAG_NONE));
+ SpdyDataIR data_ir(1, StringPiece("hello", 5));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~DATA_FLAG_FIN) {
@@ -4029,11 +4394,60 @@ TEST_P(SpdyFramerTest, DataFrameFlags) {
}
}
+TEST_P(SpdyFramerTest, DataFrameFlagsV4) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ uint8 valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT |
+ DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH;
+
+ for (int flags = 0; flags < 256; ++flags) {
+ SCOPED_TRACE(testing::Message() << "Flags " << flags);
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ SpdyDataIR data_ir(1, StringPiece("hello", 5));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+ SetFrameFlags(frame.get(), flags, spdy_version_);
+
+ if (flags & ~valid_data_flags) {
+ EXPECT_CALL(visitor, OnError(_));
+ } else {
+ EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN));
+ if ((flags & DATA_FLAG_PAD_LOW) || (flags & DATA_FLAG_PAD_HIGH)) {
+ // Expect Error since we don't set pad_high and pad_low in payload.
+ EXPECT_CALL(visitor, OnError(_));
+ } else {
+ EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5, false));
+ if (flags & DATA_FLAG_FIN) {
+ EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+ }
+ }
+ }
+
+ framer.ProcessInput(frame->data(), frame->size());
+ if ((flags & ~valid_data_flags) || (flags & DATA_FLAG_PAD_LOW) ||
+ (flags & DATA_FLAG_PAD_HIGH)) {
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS,
+ framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else {
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ }
+ }
+}
+
TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -4041,27 +4455,67 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(8, SYN_STREAM, _, _));
- SpdyHeaderBlock headers;
- headers["foo"] = "bar";
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynStream(8, 3, 1, 0, CONTROL_FLAG_NONE, &headers));
- SetFrameFlags(frame.get(), flags, spdy_version_);
+ SpdySynStreamIR syn_stream(8);
+ syn_stream.set_associated_to_stream_id(3);
+ syn_stream.set_priority(1);
+ syn_stream.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
+ int set_flags = flags;
+ if (IsSpdy4()) {
+ // PRIORITY required for SYN_STREAM simulation.
+ set_flags |= HEADERS_FLAG_PRIORITY;
- if (flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
+ // TODO(jgraettinger): Add padding to SynStreamIR, and implement framing.
+ set_flags &= ~HEADERS_FLAG_PAD_LOW;
+ set_flags &= ~HEADERS_FLAG_PAD_HIGH;
+ }
+ SetFrameFlags(frame.get(), set_flags, spdy_version_);
+
+ if (!IsSpdy4() &&
+ flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
+ EXPECT_CALL(visitor, OnError(_));
+ } else if (IsSpdy4() &&
+ flags & ~(CONTROL_FLAG_FIN |
+ HEADERS_FLAG_PRIORITY |
+ HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_END_SEGMENT |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(8, SYN_STREAM, _));
- EXPECT_CALL(visitor, OnSynStream(8, 3, 1, 0, flags & CONTROL_FLAG_FIN,
- flags & CONTROL_FLAG_UNIDIRECTIONAL));
+ if (IsSpdy4()) {
+ EXPECT_CALL(visitor, OnSynStream(8, 0, 1, flags & CONTROL_FLAG_FIN,
+ false));
+ } else {
+ EXPECT_CALL(visitor, OnSynStream(8, 3, 1, flags & CONTROL_FLAG_FIN,
+ flags & CONTROL_FLAG_UNIDIRECTIONAL));
+ }
EXPECT_CALL(visitor, OnControlFrameHeaderData(8, _, _))
.WillRepeatedly(testing::Return(true));
- if (flags & DATA_FLAG_FIN) {
+ if (flags & DATA_FLAG_FIN && (!IsSpdy4() ||
+ flags & HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+ } else {
+ // Do not close the stream if we are expecting a CONTINUATION frame.
+ EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
}
}
framer.ProcessInput(frame->data(), frame->size());
- if (flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
+ if (!IsSpdy4() &&
+ flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+ framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else if (IsSpdy4() &&
+ flags & ~(CONTROL_FLAG_FIN |
+ HEADERS_FLAG_PRIORITY |
+ HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_END_SEGMENT |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
@@ -4075,17 +4529,20 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
}
TEST_P(SpdyFramerTest, SynReplyFrameFlags) {
+ if (IsSpdy4()) {
+ // Covered by HEADERS case.
+ return;
+ }
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["foo"] = "bar";
- scoped_ptr<SpdyFrame> frame(
- framer.CreateSynReply(37, CONTROL_FLAG_NONE, &headers));
+ SpdySynReplyIR syn_reply(37);
+ syn_reply.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~CONTROL_FLAG_FIN) {
@@ -4117,11 +4574,12 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(13, RST_STREAM_CANCEL));
+ SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL, "");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
@@ -4144,18 +4602,21 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
}
}
-TEST_P(SpdyFramerTest, SettingsFrameFlags) {
+TEST_P(SpdyFramerTest, SettingsFrameFlagsOldFormat) {
+ if (spdy_version_ > SPDY3) { return; }
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SettingsMap settings;
- settings[SETTINGS_UPLOAD_BANDWIDTH] =
- std::make_pair(SETTINGS_FLAG_NONE, 54321);
- scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+ SpdySettingsIR settings_ir;
+ settings_ir.AddSetting(SETTINGS_UPLOAD_BANDWIDTH,
+ false,
+ false,
+ 54321);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) {
@@ -4165,6 +4626,7 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) {
flags & SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS));
EXPECT_CALL(visitor, OnSetting(SETTINGS_UPLOAD_BANDWIDTH,
SETTINGS_FLAG_NONE, 54321));
+ EXPECT_CALL(visitor, OnSettingsEnd());
}
framer.ProcessInput(frame->data(), frame->size());
@@ -4181,15 +4643,58 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) {
}
}
+TEST_P(SpdyFramerTest, SettingsFrameFlags) {
+ if (spdy_version_ <= SPDY3) { return; }
+ for (int flags = 0; flags < 256; ++flags) {
+ SCOPED_TRACE(testing::Message() << "Flags " << flags);
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ SpdySettingsIR settings_ir;
+ settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 0, 16);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
+ SetFrameFlags(frame.get(), flags, spdy_version_);
+
+ if (flags != 0) {
+ EXPECT_CALL(visitor, OnError(_));
+ } else {
+ EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK));
+ EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 16));
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ }
+
+ framer.ProcessInput(frame->data(), frame->size());
+ if (flags & ~SETTINGS_FLAG_ACK) {
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+ framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else if (flags & SETTINGS_FLAG_ACK) {
+ // The frame is invalid because ACK frames should have no payload.
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else {
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ }
+ }
+}
+
TEST_P(SpdyFramerTest, GoawayFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(97, GOAWAY_OK));
+ SpdyGoAwayIR goaway_ir(97, GOAWAY_OK, "test");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
@@ -4214,35 +4719,71 @@ TEST_P(SpdyFramerTest, GoawayFrameFlags) {
TEST_P(SpdyFramerTest, HeadersFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
+ if (IsSpdy4() && flags & HEADERS_FLAG_PRIORITY) {
+ // Covered by SYN_STREAM case.
+ continue;
+ }
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- SpdyHeaderBlock headers;
- headers["foo"] = "bar";
- scoped_ptr<SpdyFrame> frame(
- framer.CreateHeaders(57, CONTROL_FLAG_NONE, &headers));
- SetFrameFlags(frame.get(), flags, spdy_version_);
+ SpdyHeadersIR headers_ir(57);
+ headers_ir.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
+ int set_flags = flags;
+ if (IsSpdy4()) {
+ // TODO(jgraettinger): Add padding to SpdyHeadersIR,
+ // and implement framing.
+ set_flags &= ~HEADERS_FLAG_PAD_LOW;
+ set_flags &= ~HEADERS_FLAG_PAD_HIGH;
+ }
+ SetFrameFlags(frame.get(), set_flags, spdy_version_);
- if (flags & ~CONTROL_FLAG_FIN) {
+ if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
+ EXPECT_CALL(visitor, OnError(_));
+ } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
+ HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_END_SEGMENT |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_CALL(visitor, OnError(_));
} else {
- EXPECT_CALL(visitor, OnHeaders(57, flags & CONTROL_FLAG_FIN));
+ EXPECT_CALL(visitor, OnHeaders(57,
+ flags & CONTROL_FLAG_FIN,
+ (flags & HEADERS_FLAG_END_HEADERS) ||
+ !IsSpdy4()));
EXPECT_CALL(visitor, OnControlFrameHeaderData(57, _, _))
.WillRepeatedly(testing::Return(true));
- if (flags & DATA_FLAG_FIN) {
+ if (flags & DATA_FLAG_FIN && (!IsSpdy4() ||
+ flags & HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+ } else {
+ // Do not close the stream if we are expecting a CONTINUATION frame.
+ EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
}
}
framer.ProcessInput(frame->data(), frame->size());
- if (flags & ~CONTROL_FLAG_FIN) {
+ if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
+ HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_END_SEGMENT |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+ framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else if (IsSpdy4() && ~(flags & HEADERS_FLAG_END_HEADERS)) {
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
@@ -4255,29 +4796,33 @@ TEST_P(SpdyFramerTest, PingFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(42));
+ scoped_ptr<SpdyFrame> frame(framer.SerializePing(SpdyPingIR(42)));
SetFrameFlags(frame.get(), flags, spdy_version_);
- if (flags != 0) {
- EXPECT_CALL(visitor, OnError(_));
+ if (spdy_version_ > SPDY3 &&
+ flags == PING_FLAG_ACK) {
+ EXPECT_CALL(visitor, OnPing(42, true));
+ } else if (flags == 0) {
+ EXPECT_CALL(visitor, OnPing(42, false));
} else {
- EXPECT_CALL(visitor, OnPing(42));
+ EXPECT_CALL(visitor, OnError(_));
}
framer.ProcessInput(frame->data(), frame->size());
- if (flags != 0) {
+ if ((spdy_version_ > SPDY3 && flags == PING_FLAG_ACK) ||
+ flags == 0) {
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+ } else {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
- } else {
- EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
- EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
- << SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
@@ -4286,11 +4831,12 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(4, 1024));
+ scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
+ SpdyWindowUpdateIR(4, 1024)));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
@@ -4314,14 +4860,14 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) {
}
TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<net::test::MockVisitor> visitor;
+ testing::StrictMock<net::test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<net::test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -4329,22 +4875,31 @@ TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, PUSH_PROMISE, _, _));
- SpdyHeaderBlock headers;
- headers["foo"] = "bar";
- scoped_ptr<SpdyFrame> frame(framer.CreatePushPromise(42, 57, &headers));
- SetFrameFlags(frame.get(), flags, spdy_version_);
-
- if (flags != 0) {
+ SpdyPushPromiseIR push_promise(42, 57);
+ push_promise.SetHeader("foo", "bar");
+ scoped_ptr<SpdySerializedFrame> frame(
+ framer.SerializePushPromise(push_promise));
+ // TODO(jgraettinger): Add padding to SpdyPushPromiseIR,
+ // and implement framing.
+ int set_flags = flags & ~HEADERS_FLAG_PAD_LOW & ~HEADERS_FLAG_PAD_HIGH;
+ SetFrameFlags(frame.get(), set_flags, spdy_version_);
+
+ if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, PUSH_PROMISE, _));
- EXPECT_CALL(visitor, OnPushPromise(42, 57));
+ EXPECT_CALL(visitor, OnPushPromise(42, 57,
+ flags & PUSH_PROMISE_FLAG_END_PUSH_PROMISE));
EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
.WillRepeatedly(testing::Return(true));
}
framer.ProcessInput(frame->data(), frame->size());
- if (flags != 0) {
+ if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
@@ -4357,27 +4912,58 @@ TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
}
}
-TEST_P(SpdyFramerTest, CredentialFrameFlags) {
+TEST_P(SpdyFramerTest, ContinuationFrameFlags) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ testing::StrictMock<net::test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
+ framer.set_debug_visitor(&debug_visitor);
- SpdyCredential credential;
- scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
- SetFrameFlags(frame.get(), flags, spdy_version_);
-
- if (flags != 0) {
+ EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, HEADERS, _, _));
+ EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, HEADERS, _));
+ EXPECT_CALL(visitor, OnHeaders(42, 0, false));
+ EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, CONTINUATION, _, _));
+
+ SpdyHeadersIR headers_ir(42);
+ headers_ir.SetHeader("foo", "bar");
+ scoped_ptr<SpdyFrame> frame0(framer.SerializeHeaders(headers_ir));
+ SetFrameFlags(frame0.get(), 0, spdy_version_);
+
+ SpdyContinuationIR continuation(42);
+ continuation.SetHeader("foo", "bar");
+ scoped_ptr<SpdySerializedFrame> frame(
+ framer.SerializeContinuation(continuation));
+ // TODO(jgraettinger): Add padding to the eventual continuation
+ // serialization implementation.
+ int set_flags = flags & ~HEADERS_FLAG_PAD_LOW & ~HEADERS_FLAG_PAD_HIGH;
+ SetFrameFlags(frame.get(), set_flags, spdy_version_);
+
+ if (flags & ~(HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_CALL(visitor, OnError(_));
} else {
- EXPECT_CALL(visitor, OnCredentialFrameData(_, _))
+ EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, CONTINUATION, _));
+ EXPECT_CALL(visitor, OnContinuation(42,
+ flags & HEADERS_FLAG_END_HEADERS));
+ EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
.WillRepeatedly(testing::Return(true));
}
+ framer.ProcessInput(frame0->data(), frame0->size());
framer.ProcessInput(frame->data(), frame->size());
- if (flags != 0) {
+ if (flags & ~(HEADERS_FLAG_END_HEADERS |
+ HEADERS_FLAG_PAD_LOW |
+ HEADERS_FLAG_PAD_HIGH)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
@@ -4390,10 +4976,12 @@ TEST_P(SpdyFramerTest, CredentialFrameFlags) {
}
}
-TEST_P(SpdyFramerTest, EmptySynStream) {
- SpdyHeaderBlock headers;
+// TODO(mlavan): Add TEST_P(SpdyFramerTest, AltSvcFrameFlags)
- testing::StrictMock<test::MockVisitor> visitor;
+// TODO(hkhalil): Add TEST_P(SpdyFramerTest, BlockedFrameFlags)
+
+TEST_P(SpdyFramerTest, EmptySynStream) {
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -4401,23 +4989,17 @@ TEST_P(SpdyFramerTest, EmptySynStream) {
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(1, SYN_STREAM, _, _));
- scoped_ptr<SpdyFrame>
- frame(framer.CreateSynStream(1, 0, 1, 0, CONTROL_FLAG_NONE, &headers));
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.set_priority(1);
+ scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
// Adjust size to remove the name/value block.
- if (IsSpdy4()) {
- SetFrameLength(
- frame.get(),
- framer.GetSynStreamMinimumSize(),
- spdy_version_);
- } else {
- SetFrameLength(
- frame.get(),
- framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(),
- spdy_version_);
- }
+ SetFrameLength(
+ frame.get(),
+ framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(),
+ spdy_version_);
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(1, SYN_STREAM, _));
- EXPECT_CALL(visitor, OnSynStream(1, 0, 1, 0, false, false));
+ EXPECT_CALL(visitor, OnSynStream(1, 0, 1, false, false));
EXPECT_CALL(visitor, OnControlFrameHeaderData(1, NULL, 0));
framer.ProcessInput(frame->data(), framer.GetSynStreamMinimumSize());
@@ -4440,61 +5022,72 @@ TEST_P(SpdyFramerTest, SettingsFlagsAndId) {
// Test handling of a RST_STREAM with out-of-bounds status codes.
TEST_P(SpdyFramerTest, RstStreamStatusBounds) {
- DCHECK_GE(0xff, RST_STREAM_NUM_STATUS_CODES);
-
+ const unsigned char kRstStreamStatusTooLow = 0x00;
+ const unsigned char kRstStreamStatusTooHigh = 0xff;
const unsigned char kV3RstStreamInvalid[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, RST_STREAM_INVALID
+ 0x00, 0x00, 0x00, kRstStreamStatusTooLow
};
const unsigned char kV4RstStreamInvalid[] = {
- 0x00, 0x0c, 0x03, 0x00,
+ 0x00, 0x04, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, RST_STREAM_INVALID
+ 0x00, 0x00, 0x00, kRstStreamStatusTooLow
};
const unsigned char kV3RstStreamNumStatusCodes[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
+ 0x00, 0x00, 0x00, kRstStreamStatusTooHigh
};
const unsigned char kV4RstStreamNumStatusCodes[] = {
- 0x00, 0x0c, 0x03, 0x00,
+ 0x00, 0x04, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
+ 0x00, 0x00, 0x00, kRstStreamStatusTooHigh
};
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
if (IsSpdy4()) {
+ EXPECT_CALL(visitor, OnError(_));
framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid),
arraysize(kV4RstStreamInvalid));
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
+ EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
framer.ProcessInput(reinterpret_cast<const char*>(kV3RstStreamInvalid),
arraysize(kV3RstStreamInvalid));
- }
- EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
- EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
+ }
+
+
+ framer.Reset();
- EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
if (IsSpdy4()) {
+ EXPECT_CALL(visitor, OnError(_));
framer.ProcessInput(
reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes),
arraysize(kV4RstStreamNumStatusCodes));
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
+ EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
framer.ProcessInput(
reinterpret_cast<const char*>(kV3RstStreamNumStatusCodes),
arraysize(kV3RstStreamNumStatusCodes));
- }
- EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
- EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
+ }
}
// Tests handling of a GOAWAY frame with out-of-bounds stream ID.
@@ -4511,13 +5104,13 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) {
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x10, 0x07, 0x00,
+ 0x00, 0x08, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
};
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -4538,13 +5131,13 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) {
}
TEST_P(SpdyFramerTest, OnBlocked) {
- if (spdy_version_ < SPDY4) {
+ if (spdy_version_ <= SPDY3) {
return;
}
const SpdyStreamId kStreamId = 0;
- testing::StrictMock<test::MockVisitor> visitor;
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
@@ -4559,4 +5152,281 @@ TEST_P(SpdyFramerTest, OnBlocked) {
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
+TEST_P(SpdyFramerTest, OnAltSvc) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const SpdyStreamId kStreamId = 1;
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ EXPECT_CALL(visitor, OnAltSvc(kStreamId,
+ 10,
+ 443,
+ StringPiece("pid"),
+ StringPiece("h1"),
+ StringPiece("o1")));
+
+ SpdyAltSvcIR altsvc_ir(1);
+ altsvc_ir.set_max_age(10);
+ altsvc_ir.set_port(443);
+ altsvc_ir.set_protocol_id("pid");
+ altsvc_ir.set_host("h1");
+ altsvc_ir.set_origin("o1");
+ scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir));
+ framer.ProcessInput(frame->data(), framer.GetAltSvcMinimumSize() +
+ altsvc_ir.protocol_id().length() +
+ altsvc_ir.host().length() +
+ altsvc_ir.origin().length());
+
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+}
+
+TEST_P(SpdyFramerTest, OnAltSvcNoOrigin) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const SpdyStreamId kStreamId = 1;
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ EXPECT_CALL(visitor, OnAltSvc(kStreamId,
+ 10,
+ 443,
+ StringPiece("pid"),
+ StringPiece("h1"),
+ StringPiece("")));
+
+ SpdyAltSvcIR altsvc_ir(1);
+ altsvc_ir.set_max_age(10);
+ altsvc_ir.set_port(443);
+ altsvc_ir.set_protocol_id("pid");
+ altsvc_ir.set_host("h1");
+ scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir));
+ framer.ProcessInput(frame->data(), framer.GetAltSvcMinimumSize() +
+ altsvc_ir.protocol_id().length() +
+ altsvc_ir.host().length());
+
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+ << SpdyFramer::ErrorCodeToString(framer.error_code());
+}
+
+TEST_P(SpdyFramerTest, OnAltSvcBadLengths) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kType = static_cast<unsigned char>(
+ SpdyConstants::SerializeFrameType(spdy_version_, ALTSVC));
+ {
+ TestSpdyVisitor visitor(spdy_version_);
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ const unsigned char kFrameDataLargePIDLen[] = {
+ 0x00, 0x17, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05,
+ 0x01, 0xbb, 0x00, 0x05, // Port = 443
+ 'p', 'i', 'd', '1', // Protocol-ID
+ 0x04, 'h', 'o', 's',
+ 't', 'o', 'r', 'i',
+ 'g', 'i', 'n',
+ };
+
+ visitor.SimulateInFramer(kFrameDataLargePIDLen,
+ sizeof(kFrameDataLargePIDLen));
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code());
+ }
+
+ {
+ TestSpdyVisitor visitor(spdy_version_);
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+ const unsigned char kFrameDataPIDLenLargerThanFrame[] = {
+ 0x00, 0x17, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05,
+ 0x01, 0xbb, 0x00, 0x99, // Port = 443
+ 'p', 'i', 'd', '1', // Protocol-ID
+ 0x04, 'h', 'o', 's',
+ 't', 'o', 'r', 'i',
+ 'g', 'i', 'n',
+ };
+
+ visitor.SimulateInFramer(kFrameDataPIDLenLargerThanFrame,
+ sizeof(kFrameDataPIDLenLargerThanFrame));
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code());
+ }
+
+ {
+ TestSpdyVisitor visitor(spdy_version_);
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+
+ const unsigned char kFrameDataLargeHostLen[] = {
+ 0x00, 0x17, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05,
+ 0x01, 0xbb, 0x00, 0x04, // Port = 443
+ 'p', 'i', 'd', '1', // Protocol-ID
+ 0x0f, 'h', 'o', 's',
+ 't', 'o', 'r', 'i',
+ 'g', 'i', 'n',
+ };
+
+ visitor.SimulateInFramer(kFrameDataLargeHostLen,
+ sizeof(kFrameDataLargeHostLen));
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code());
+ }
+
+ {
+ TestSpdyVisitor visitor(spdy_version_);
+ SpdyFramer framer(spdy_version_);
+ framer.set_visitor(&visitor);
+ const unsigned char kFrameDataSmallPIDLen[] = {
+ 0x00, 0x17, kType, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05,
+ 0x01, 0xbb, 0x00, 0x01, // Port = 443
+ 'p', 'i', 'd', '1', // Protocol-ID
+ 0x04, 'h', 'o', 's',
+ 't', 'o', 'r', 'i',
+ 'g', 'i', 'n',
+ };
+
+ visitor.SimulateInFramer(kFrameDataSmallPIDLen,
+ sizeof(kFrameDataSmallPIDLen));
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code());
+ }
+}
+
+// Tests handling of ALTSVC frames delivered in small chunks.
+TEST_P(SpdyFramerTest, ReadChunkedAltSvcFrame) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+ SpdyAltSvcIR altsvc_ir(1);
+ altsvc_ir.set_max_age(20);
+ altsvc_ir.set_port(443);
+ altsvc_ir.set_protocol_id("protocolid");
+ altsvc_ir.set_host("hostname");
+
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeAltSvc(altsvc_ir));
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.use_compression_ = false;
+
+ // Read data in small chunks.
+ size_t framed_data = 0;
+ size_t unframed_data = control_frame->size();
+ size_t kReadChunkSize = 5; // Read five bytes at a time.
+ while (unframed_data > 0) {
+ size_t to_read = min(kReadChunkSize, unframed_data);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data() + framed_data),
+ to_read);
+ unframed_data -= to_read;
+ framed_data += to_read;
+ }
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.altsvc_count_);
+ EXPECT_EQ(20u, visitor.test_altsvc_ir_.max_age());
+ EXPECT_EQ(443u, visitor.test_altsvc_ir_.port());
+ EXPECT_EQ("protocolid", visitor.test_altsvc_ir_.protocol_id());
+ EXPECT_EQ("hostname", visitor.test_altsvc_ir_.host());
+}
+
+// Tests handling of PRIORITY frames.
+TEST_P(SpdyFramerTest, ReadPriority) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ const unsigned char kFrameData[] = {
+ 0x00, 0x05, 0x02, 0x00, // PRIORITY frame
+ 0x00, 0x00, 0x00, 0x03, // stream ID 3
+ 0x00, 0x00, 0x00, 0x01, // dependent on stream id 1
+ 0x00 // weight 0
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
+
+ EXPECT_EQ(SpdyFramer::SPDY_RESET, visitor.framer_.state());
+ EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code());
+}
+
+// Tests handling of PRIORITY frame with incorrect size.
+TEST_P(SpdyFramerTest, ReadIncorrectlySizedPriority) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+
+ // PRIORITY frame of size 4, which isn't correct.
+ const unsigned char kFrameData[] = {
+ 0x00, 0x04, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01,
+ };
+
+ TestSpdyVisitor visitor(spdy_version_);
+ visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
+
+ EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state());
+ EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+ visitor.framer_.error_code())
+ << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code());
+}
+
+TEST_P(SpdyFramerTest, PriorityWeightMapping) {
+ if (spdy_version_ <= SPDY3) {
+ return;
+ }
+ SpdyFramer framer(spdy_version_);
+
+ EXPECT_EQ(255u, framer.MapPriorityToWeight(0));
+ EXPECT_EQ(219u, framer.MapPriorityToWeight(1));
+ EXPECT_EQ(182u, framer.MapPriorityToWeight(2));
+ EXPECT_EQ(146u, framer.MapPriorityToWeight(3));
+ EXPECT_EQ(109u, framer.MapPriorityToWeight(4));
+ EXPECT_EQ(73u, framer.MapPriorityToWeight(5));
+ EXPECT_EQ(36u, framer.MapPriorityToWeight(6));
+ EXPECT_EQ(0u, framer.MapPriorityToWeight(7));
+
+ EXPECT_EQ(0u, framer.MapWeightToPriority(255));
+ EXPECT_EQ(0u, framer.MapWeightToPriority(220));
+ EXPECT_EQ(1u, framer.MapWeightToPriority(219));
+ EXPECT_EQ(1u, framer.MapWeightToPriority(183));
+ EXPECT_EQ(2u, framer.MapWeightToPriority(182));
+ EXPECT_EQ(2u, framer.MapWeightToPriority(147));
+ EXPECT_EQ(3u, framer.MapWeightToPriority(146));
+ EXPECT_EQ(3u, framer.MapWeightToPriority(110));
+ EXPECT_EQ(4u, framer.MapWeightToPriority(109));
+ EXPECT_EQ(4u, framer.MapWeightToPriority(74));
+ EXPECT_EQ(5u, framer.MapWeightToPriority(73));
+ EXPECT_EQ(5u, framer.MapWeightToPriority(37));
+ EXPECT_EQ(6u, framer.MapWeightToPriority(36));
+ EXPECT_EQ(6u, framer.MapWeightToPriority(1));
+ EXPECT_EQ(7u, framer.MapWeightToPriority(0));
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_header_block.cc b/chromium/net/spdy/spdy_header_block.cc
index ecc22a45324..bbe9c716737 100644
--- a/chromium/net/spdy/spdy_header_block.cc
+++ b/chromium/net/spdy/spdy_header_block.cc
@@ -5,13 +5,13 @@
#include "net/spdy/spdy_header_block.h"
#include "base/values.h"
-#include "net/spdy/spdy_http_utils.h"
+#include "net/http/http_log_util.h"
namespace net {
base::Value* SpdyHeaderBlockNetLogCallback(
const SpdyHeaderBlock* headers,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
base::DictionaryValue* headers_dict = new base::DictionaryValue();
for (SpdyHeaderBlock::const_iterator it = headers->begin();
@@ -19,7 +19,7 @@ base::Value* SpdyHeaderBlockNetLogCallback(
headers_dict->SetWithoutPathExpansion(
it->first,
new base::StringValue(
- ShouldShowHttpHeaderValue(it->first) ? it->second : "[elided]"));
+ ElideHeaderValueForNetLog(log_level, it->first, it->second)));
}
dict->Set("headers", headers_dict);
return dict;
diff --git a/chromium/net/spdy/spdy_header_block_unittest.cc b/chromium/net/spdy/spdy_header_block_unittest.cc
index 3cfef16e999..1b6b449dc94 100644
--- a/chromium/net/spdy/spdy_header_block_unittest.cc
+++ b/chromium/net/spdy/spdy_header_block_unittest.cc
@@ -18,7 +18,7 @@ TEST(SpdyHeaderBlockTest, ToNetLogParamAndBackAgain) {
headers["A"] = "a";
headers["B"] = "b";
- scoped_ptr<Value> event_param(
+ scoped_ptr<base::Value> event_param(
SpdyHeaderBlockNetLogCallback(&headers, NetLog::LOG_ALL_BUT_BYTES));
SpdyHeaderBlock headers2;
diff --git a/chromium/net/spdy/spdy_headers_block_parser.cc b/chromium/net/spdy/spdy_headers_block_parser.cc
new file mode 100644
index 00000000000..2a302c18762
--- /dev/null
+++ b/chromium/net/spdy/spdy_headers_block_parser.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 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/spdy/spdy_headers_block_parser.h"
+
+#include "base/sys_byteorder.h"
+
+namespace net {
+
+const size_t SpdyHeadersBlockParser::kMaximumFieldLength = 16 * 1024;
+
+SpdyHeadersBlockParser::SpdyHeadersBlockParser(
+ SpdyMajorVersion spdy_version,
+ SpdyHeadersHandlerInterface* handler) :
+ state_(READING_HEADER_BLOCK_LEN),
+ length_field_size_(LengthFieldSizeForVersion(spdy_version)),
+ max_headers_in_block_(MaxNumberOfHeadersForVersion(spdy_version)),
+ total_bytes_received_(0),
+ remaining_key_value_pairs_for_frame_(0),
+ handler_(handler),
+ error_(OK) {
+ // The handler that we set must not be NULL.
+ DCHECK(handler_ != NULL);
+}
+
+SpdyHeadersBlockParser::~SpdyHeadersBlockParser() {}
+
+bool SpdyHeadersBlockParser::HandleControlFrameHeadersData(
+ SpdyStreamId stream_id,
+ const char* headers_data,
+ size_t headers_data_length) {
+ if (error_ == NEED_MORE_DATA) {
+ error_ = OK;
+ }
+ CHECK_EQ(error_, OK);
+
+ // If this is the first call with the current header block,
+ // save its stream id.
+ if (state_ == READING_HEADER_BLOCK_LEN) {
+ stream_id_ = stream_id;
+ }
+ CHECK_EQ(stream_id_, stream_id);
+
+ total_bytes_received_ += headers_data_length;
+
+ SpdyPinnableBufferPiece prefix, key, value;
+ // Simultaneously tie lifetimes to the stack, and clear member variables.
+ prefix.Swap(&headers_block_prefix_);
+ key.Swap(&key_);
+
+ // Apply the parsing state machine to the remaining prefix
+ // from last invocation, plus newly-available headers data.
+ Reader reader(prefix.buffer(), prefix.length(),
+ headers_data, headers_data_length);
+ while (error_ == OK) {
+ ParserState next_state(FINISHED_HEADER);
+
+ switch (state_) {
+ case READING_HEADER_BLOCK_LEN:
+ next_state = READING_KEY_LEN;
+ ParseBlockLength(&reader);
+ break;
+ case READING_KEY_LEN:
+ next_state = READING_KEY;
+ ParseFieldLength(&reader);
+ break;
+ case READING_KEY:
+ next_state = READING_VALUE_LEN;
+ if (!reader.ReadN(next_field_length_, &key)) {
+ error_ = NEED_MORE_DATA;
+ }
+ break;
+ case READING_VALUE_LEN:
+ next_state = READING_VALUE;
+ ParseFieldLength(&reader);
+ break;
+ case READING_VALUE:
+ next_state = FINISHED_HEADER;
+ if (!reader.ReadN(next_field_length_, &value)) {
+ error_ = NEED_MORE_DATA;
+ } else {
+ handler_->OnHeader(stream_id, key, value);
+ }
+ break;
+ case FINISHED_HEADER:
+ // Prepare for next header or block.
+ if (--remaining_key_value_pairs_for_frame_ > 0) {
+ next_state = READING_KEY_LEN;
+ } else {
+ next_state = READING_HEADER_BLOCK_LEN;
+ handler_->OnHeaderBlockEnd(stream_id, total_bytes_received_);
+ // Expect to have consumed all buffer.
+ if (reader.Available() != 0) {
+ error_ = TOO_MUCH_DATA;
+ }
+ }
+ break;
+ default:
+ CHECK(false) << "Not reached.";
+ }
+
+ if (error_ == OK) {
+ state_ = next_state;
+
+ if (next_state == READING_HEADER_BLOCK_LEN) {
+ // We completed reading a full header block. Return to caller.
+ total_bytes_received_ = 0;
+ break;
+ }
+ } else if (error_ == NEED_MORE_DATA) {
+ // We can't continue parsing until more data is available. Make copies of
+ // the key and buffer remainder, in preperation for the next invocation.
+ if (state_ > READING_KEY) {
+ key_.Swap(&key);
+ key_.Pin();
+ }
+ reader.ReadN(reader.Available(), &headers_block_prefix_);
+ headers_block_prefix_.Pin();
+ }
+ }
+ return error_ == OK;
+}
+
+void SpdyHeadersBlockParser::ParseBlockLength(Reader* reader) {
+ ParseLength(reader, &remaining_key_value_pairs_for_frame_);
+ if (error_ == OK &&
+ remaining_key_value_pairs_for_frame_ > max_headers_in_block_) {
+ error_ = HEADER_BLOCK_TOO_LARGE;
+ }
+ if (error_ == OK) {
+ handler_->OnHeaderBlock(stream_id_, remaining_key_value_pairs_for_frame_);
+ }
+}
+
+void SpdyHeadersBlockParser::ParseFieldLength(Reader* reader) {
+ ParseLength(reader, &next_field_length_);
+ if (error_ == OK &&
+ next_field_length_ > kMaximumFieldLength) {
+ error_ = HEADER_FIELD_TOO_LARGE;
+ }
+}
+
+void SpdyHeadersBlockParser::ParseLength(Reader* reader,
+ uint32_t* parsed_length) {
+ char buffer[] = {0, 0, 0, 0};
+ if (!reader->ReadN(length_field_size_, buffer)) {
+ error_ = NEED_MORE_DATA;
+ return;
+ }
+ // Convert from network to host order and return the parsed out integer.
+ if (length_field_size_ == sizeof(uint32_t)) {
+ *parsed_length = ntohl(*reinterpret_cast<const uint32_t *>(buffer));
+ } else {
+ *parsed_length = ntohs(*reinterpret_cast<const uint16_t *>(buffer));
+ }
+}
+
+void SpdyHeadersBlockParser::Reset() {
+ {
+ SpdyPinnableBufferPiece empty;
+ headers_block_prefix_.Swap(&empty);
+ }
+ {
+ SpdyPinnableBufferPiece empty;
+ key_.Swap(&empty);
+ }
+ error_ = OK;
+ state_ = READING_HEADER_BLOCK_LEN;
+ stream_id_ = 0;
+ total_bytes_received_ = 0;
+}
+
+size_t SpdyHeadersBlockParser::LengthFieldSizeForVersion(
+ SpdyMajorVersion spdy_version) {
+ if (spdy_version < SPDY3) {
+ return sizeof(uint16_t);
+ }
+ return sizeof(uint32_t);
+}
+
+size_t SpdyHeadersBlockParser::MaxNumberOfHeadersForVersion(
+ SpdyMajorVersion spdy_version) {
+ // Account for the length of the header block field.
+ size_t max_bytes_for_headers =
+ kMaximumFieldLength - LengthFieldSizeForVersion(spdy_version);
+
+ // A minimal size header is twice the length field size (and has a
+ // zero-lengthed key and a zero-lengthed value).
+ return max_bytes_for_headers / (2 * LengthFieldSizeForVersion(spdy_version));
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_headers_block_parser.h b/chromium/net/spdy/spdy_headers_block_parser.h
new file mode 100644
index 00000000000..2c9207cc5bc
--- /dev/null
+++ b/chromium/net/spdy/spdy_headers_block_parser.h
@@ -0,0 +1,149 @@
+// Copyright 2014 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_SPDY_SPDY_HEADERS_BLOCK_PARSER_H_
+#define NET_SPDY_SPDY_HEADERS_BLOCK_PARSER_H_
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/spdy/spdy_prefixed_buffer_reader.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net {
+
+// A handler class for SPDY headers.
+class SpdyHeadersHandlerInterface {
+ public:
+ virtual ~SpdyHeadersHandlerInterface() {}
+
+ // A callback method which notifies when the parser starts handling a new
+ // SPDY headers block, this method also notifies on the number of headers in
+ // the block.
+ virtual void OnHeaderBlock(SpdyStreamId stream_id,
+ uint32_t num_of_headers) = 0;
+
+ // A callback method which notifies on a SPDY header key value pair.
+ virtual void OnHeader(SpdyStreamId stream_id,
+ base::StringPiece key,
+ base::StringPiece value) = 0;
+
+ // A callback method which notifies when the parser finishes handling a SPDY
+ // headers block. Also notifies on the total number of bytes in this block.
+ virtual void OnHeaderBlockEnd(SpdyStreamId stream_id,
+ size_t header_bytes_parsed) = 0;
+};
+
+namespace test {
+
+class SpdyHeadersBlockParserPeer;
+
+} // namespace test
+
+// This class handles SPDY headers block bytes and parses out key-value pairs
+// as they arrive. This class is not thread-safe, and assumes that all headers
+// block bytes are processed in a single thread.
+class NET_EXPORT_PRIVATE SpdyHeadersBlockParser {
+ public:
+ // Bound on acceptable header name or value length.
+ static const size_t kMaximumFieldLength; // = 16 * 1024
+
+ // Constructor. The handler's OnHeader will be called for every key
+ // value pair that we parsed from the headers block.
+ SpdyHeadersBlockParser(SpdyMajorVersion spdy_version,
+ SpdyHeadersHandlerInterface* handler);
+
+ virtual ~SpdyHeadersBlockParser();
+
+ // Handles headers block data as it arrives. Returns false if an error has
+ // been set, which can include the recoverable error NEED_MORE_DATA. Returns
+ // true if the invocation completes the parse of the entire headers block,
+ // in which case the parser is ready for a new headers block.
+ bool HandleControlFrameHeadersData(SpdyStreamId stream_id,
+ const char* headers_data,
+ size_t len);
+ enum ParserError {
+ OK,
+ // Set when parsing failed due to insufficient data.
+ // This error is recoverable, by passing in new data.
+ NEED_MORE_DATA,
+ // Set when a complete block has been read, but unprocessed data remains.
+ TOO_MUCH_DATA,
+ // Set when a block exceeds |MaxNumberOfHeadersForVersion| headers.
+ HEADER_BLOCK_TOO_LARGE,
+ // Set when a header key or value exceeds |kMaximumFieldLength|.
+ HEADER_FIELD_TOO_LARGE,
+ };
+ ParserError get_error() const { return error_; }
+
+ // Resets the state of the parser to prepare it for a headers block of a
+ // new frame.
+ void Reset();
+
+ // Returns the size in bytes of a length field in a SPDY header.
+ static size_t LengthFieldSizeForVersion(SpdyMajorVersion spdy_version);
+
+ // Returns the maximal number of headers in a SPDY headers block.
+ static size_t MaxNumberOfHeadersForVersion(SpdyMajorVersion spdy_version);
+
+ private:
+ typedef SpdyPrefixedBufferReader Reader;
+
+ // Parses and sanity-checks header block length.
+ void ParseBlockLength(Reader* reader);
+
+ // Parses and sanity-checks header field length.
+ void ParseFieldLength(Reader* reader);
+
+ // Parses and decodes network-order lengths into |parsed_length|.
+ void ParseLength(Reader* reader, uint32_t* parsed_length);
+
+ // The state of the parser.
+ enum ParserState {
+ READING_HEADER_BLOCK_LEN,
+ READING_KEY_LEN,
+ READING_KEY,
+ READING_VALUE_LEN,
+ READING_VALUE,
+ FINISHED_HEADER
+ };
+ ParserState state_;
+
+ // Size in bytes of a length field in the spdy header.
+ const size_t length_field_size_;
+
+ // The maximal number of headers in a SPDY headers block.
+ const size_t max_headers_in_block_;
+
+ // A running total of the bytes parsed since the last call to Reset().
+ size_t total_bytes_received_;
+
+ // Number of key-value pairs until we complete handling the current
+ // headers block.
+ uint32_t remaining_key_value_pairs_for_frame_;
+
+ // The length of the next header field to be read (either key or value).
+ uint32_t next_field_length_;
+
+ // Handles key-value pairs as we parse them.
+ SpdyHeadersHandlerInterface* handler_;
+
+ // Holds unprocessed buffer remainders between calls to
+ // |HandleControlFrameHeadersData|.
+ SpdyPinnableBufferPiece headers_block_prefix_;
+
+ // Holds the key of a partially processed header between calls to
+ // |HandleControlFrameHeadersData|.
+ SpdyPinnableBufferPiece key_;
+
+ // The current header block stream identifier.
+ SpdyStreamId stream_id_;
+
+ ParserError error_;
+};
+
+} // namespace net
+
+#endif // NET_SPDY_SPDY_HEADERS_BLOCK_PARSER_H_
diff --git a/chromium/net/spdy/spdy_headers_block_parser_test.cc b/chromium/net/spdy/spdy_headers_block_parser_test.cc
new file mode 100644
index 00000000000..7764bc9c3aa
--- /dev/null
+++ b/chromium/net/spdy/spdy_headers_block_parser_test.cc
@@ -0,0 +1,259 @@
+// Copyright 2014 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/spdy/spdy_headers_block_parser.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_byteorder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+using base::IntToString;
+using base::StringPiece;
+using std::string;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+
+// A mock the handler class to check that we parse out the correct headers
+// and call the callback methods when we should.
+class MockSpdyHeadersHandler : public SpdyHeadersHandlerInterface {
+ public:
+ MOCK_METHOD2(OnHeaderBlock, void(SpdyStreamId stream_id,
+ uint32_t num_of_headers));
+ MOCK_METHOD2(OnHeaderBlockEnd, void(SpdyStreamId stream_id, size_t bytes));
+ MOCK_METHOD3(OnHeader, void(SpdyStreamId stream_id,
+ StringPiece,
+ StringPiece));
+};
+
+class SpdyHeadersBlockParserTest :
+ public ::testing::TestWithParam<SpdyMajorVersion> {
+ public:
+ virtual ~SpdyHeadersBlockParserTest() {}
+
+ protected:
+ virtual void SetUp() {
+ // Create a parser using the mock handler.
+ spdy_version_ = GetParam();
+ parser_.reset(new SpdyHeadersBlockParser(spdy_version_, &handler_));
+ length_field_size_ =
+ SpdyHeadersBlockParser::LengthFieldSizeForVersion(spdy_version_);
+ }
+
+ // Create a header block with a specified number of headers.
+ string CreateHeaders(uint32_t num_headers, bool insert_nulls) {
+ string headers;
+
+ // First, write the number of headers in the header block.
+ headers += EncodeLength(num_headers);
+
+ // Second, write the key-value pairs.
+ for (uint32_t i = 0; i < num_headers; i++) {
+ // Build the key.
+ string key;
+ if (insert_nulls) {
+ key = string(base_key) + string("\0", 1) + IntToString(i);
+ } else {
+ key = string(base_key) + IntToString(i);
+ }
+ // Encode the key as SPDY header.
+ headers += EncodeLength(key.length());
+ headers += key;
+
+ // Build the value.
+ string value;
+ if (insert_nulls) {
+ value = string(base_value) + string("\0", 1) + IntToString(i);
+ } else {
+ value = string(base_value) + IntToString(i);
+ }
+ // Encode the value as SPDY header.
+ headers += EncodeLength(value.length());
+ headers += value;
+ }
+ return headers;
+ }
+
+ string EncodeLength(uint32_t len) {
+ char buffer[4];
+ if (length_field_size_ == sizeof(uint32_t)) {
+ uint32_t net_order_len = htonl(len);
+ memcpy(buffer, &net_order_len, length_field_size_);
+ } else if (length_field_size_ == sizeof(uint16_t)) {
+ uint16_t net_order_len = htons(len);
+ memcpy(buffer, &net_order_len, length_field_size_);
+ } else {
+ CHECK(false) << "Invalid length field size";
+ }
+ return string(buffer, length_field_size_);
+ }
+
+ size_t length_field_size_;
+ SpdyMajorVersion spdy_version_;
+
+ MockSpdyHeadersHandler handler_;
+ scoped_ptr<SpdyHeadersBlockParser> parser_;
+
+ static const char* base_key;
+ static const char* base_value;
+
+ // Number of headers and header blocks used in the tests.
+ static const int kNumHeadersInBlock = 10;
+ static const int kNumHeaderBlocks = 10;
+};
+
+const char* SpdyHeadersBlockParserTest::base_key = "test_key";
+const char* SpdyHeadersBlockParserTest::base_value = "test_value";
+
+// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, SPDY/4.
+INSTANTIATE_TEST_CASE_P(SpdyHeadersBlockParserTests,
+ SpdyHeadersBlockParserTest,
+ ::testing::Values(SPDY2, SPDY3, SPDY4));
+
+TEST_P(SpdyHeadersBlockParserTest, BasicTest) {
+ // Sanity test, verify that we parse out correctly a block with
+ // a single key-value pair and that we notify when we start and finish
+ // handling a headers block.
+ string headers(CreateHeaders(1, false));
+
+ EXPECT_CALL(handler_, OnHeaderBlock(1, 1)).Times(1);
+
+ std::string expect_key = base_key + IntToString(0);
+ std::string expect_value = base_value + IntToString(0);
+ EXPECT_CALL(handler_, OnHeader(1, StringPiece(expect_key),
+ StringPiece(expect_value))).Times(1);
+ EXPECT_CALL(handler_, OnHeaderBlockEnd(1, headers.length())).Times(1);
+
+ EXPECT_TRUE(parser_->
+ HandleControlFrameHeadersData(1, headers.c_str(), headers.length()));
+ EXPECT_EQ(SpdyHeadersBlockParser::OK, parser_->get_error());
+}
+
+TEST_P(SpdyHeadersBlockParserTest, NullsSupportedTest) {
+ // Sanity test, verify that we parse out correctly a block with
+ // a single key-value pair when the key and value contain null charecters.
+ string headers(CreateHeaders(1, true));
+
+ EXPECT_CALL(handler_, OnHeaderBlock(1, 1)).Times(1);
+
+ std::string expect_key = base_key + string("\0", 1) + IntToString(0);
+ std::string expect_value = base_value + string("\0", 1) + IntToString(0);
+ EXPECT_CALL(handler_, OnHeader(1, StringPiece(expect_key),
+ StringPiece(expect_value))).Times(1);
+ EXPECT_CALL(handler_, OnHeaderBlockEnd(1, headers.length())).Times(1);
+
+ EXPECT_TRUE(parser_->
+ HandleControlFrameHeadersData(1, headers.c_str(), headers.length()));
+ EXPECT_EQ(SpdyHeadersBlockParser::OK, parser_->get_error());
+}
+
+TEST_P(SpdyHeadersBlockParserTest, MultipleBlocksAndHeadersWithPartialData) {
+ testing::InSequence s;
+
+ // CreateHeaders is deterministic; we can call it once for the whole test.
+ string headers(CreateHeaders(kNumHeadersInBlock, false));
+
+ // The mock doesn't retain storage of arguments, so keep them in scope.
+ std::vector<string> retained_arguments;
+ for (int i = 0; i < kNumHeadersInBlock; i++) {
+ retained_arguments.push_back(base_key + IntToString(i));
+ retained_arguments.push_back(base_value + IntToString(i));
+ }
+ // For each block we expect to parse out the headers in order.
+ for (int i = 0; i < kNumHeaderBlocks; i++) {
+ EXPECT_CALL(handler_, OnHeaderBlock(i, kNumHeadersInBlock)).Times(1);
+ for (int j = 0; j < kNumHeadersInBlock; j++) {
+ EXPECT_CALL(handler_, OnHeader(
+ i,
+ StringPiece(retained_arguments[2 * j]),
+ StringPiece(retained_arguments[2 * j + 1]))).Times(1);
+ }
+ EXPECT_CALL(handler_, OnHeaderBlockEnd(i, headers.length())).Times(1);
+ }
+ // Parse the header blocks, feeding the parser one byte at a time.
+ for (int i = 0; i < kNumHeaderBlocks; i++) {
+ for (string::iterator it = headers.begin(); it != headers.end(); ++it) {
+ if ((it + 1) == headers.end()) {
+ // Last byte completes the block.
+ EXPECT_TRUE(parser_->HandleControlFrameHeadersData(i, &(*it), 1));
+ EXPECT_EQ(SpdyHeadersBlockParser::OK, parser_->get_error());
+ } else {
+ EXPECT_FALSE(parser_->HandleControlFrameHeadersData(i, &(*it), 1));
+ EXPECT_EQ(SpdyHeadersBlockParser::NEED_MORE_DATA, parser_->get_error());
+ }
+ }
+ }
+}
+
+TEST_P(SpdyHeadersBlockParserTest, HandlesEmptyCallsTest) {
+ EXPECT_CALL(handler_, OnHeaderBlock(1, 1)).Times(1);
+
+ string headers(CreateHeaders(1, false));
+
+ string expect_key = base_key + IntToString(0);
+ string expect_value = base_value + IntToString(0);
+ EXPECT_CALL(handler_, OnHeader(1, StringPiece(expect_key),
+ StringPiece(expect_value))).Times(1);
+ EXPECT_CALL(handler_, OnHeaderBlockEnd(1, headers.length())).Times(1);
+
+ // Send a header in pieces with intermediate empty calls.
+ for (string::iterator it = headers.begin(); it != headers.end(); ++it) {
+ if ((it + 1) == headers.end()) {
+ // Last byte completes the block.
+ EXPECT_TRUE(parser_->HandleControlFrameHeadersData(1, &(*it), 1));
+ EXPECT_EQ(SpdyHeadersBlockParser::OK, parser_->get_error());
+ } else {
+ EXPECT_FALSE(parser_->HandleControlFrameHeadersData(1, &(*it), 1));
+ EXPECT_EQ(SpdyHeadersBlockParser::NEED_MORE_DATA, parser_->get_error());
+ EXPECT_FALSE(parser_->HandleControlFrameHeadersData(1, NULL, 0));
+ }
+ }
+}
+
+TEST_P(SpdyHeadersBlockParserTest, LargeBlocksDiscardedTest) {
+ // Header block with too many headers.
+ {
+ string headers = EncodeLength(
+ parser_->MaxNumberOfHeadersForVersion(spdy_version_) + 1);
+ EXPECT_FALSE(parser_->
+ HandleControlFrameHeadersData(1, headers.c_str(), headers.length()));
+ EXPECT_EQ(SpdyHeadersBlockParser::HEADER_BLOCK_TOO_LARGE,
+ parser_->get_error());
+ }
+ parser_->Reset();
+ // Header block with one header, which has a too-long key.
+ {
+ EXPECT_CALL(handler_, OnHeaderBlock(1, 1)).Times(1);
+
+ string headers = EncodeLength(1) + EncodeLength(
+ SpdyHeadersBlockParser::kMaximumFieldLength + 1);
+ EXPECT_FALSE(parser_->
+ HandleControlFrameHeadersData(1, headers.c_str(), headers.length()));
+ EXPECT_EQ(SpdyHeadersBlockParser::HEADER_FIELD_TOO_LARGE,
+ parser_->get_error());
+ }
+}
+
+TEST_P(SpdyHeadersBlockParserTest, ExtraDataTest) {
+ string headers = CreateHeaders(1, false) + "foobar";
+
+ EXPECT_CALL(handler_, OnHeaderBlock(1, 1)).Times(1);
+ EXPECT_CALL(handler_, OnHeaderBlockEnd(1, headers.length())).Times(1);
+
+ string expect_key = base_key + IntToString(0);
+ string expect_value = base_value + IntToString(0);
+ EXPECT_CALL(handler_, OnHeader(1, StringPiece(expect_key),
+ StringPiece(expect_value))).Times(1);
+
+ EXPECT_FALSE(parser_->
+ HandleControlFrameHeadersData(1, headers.c_str(), headers.length()));
+ EXPECT_EQ(SpdyHeadersBlockParser::TOO_MUCH_DATA, parser_->get_error());
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_http_stream.cc b/chromium/net/spdy/spdy_http_stream.cc
index 878ccb00aa7..055d46fee9f 100644
--- a/chromium/net/spdy/spdy_http_stream.cc
+++ b/chromium/net/spdy/spdy_http_stream.cc
@@ -27,8 +27,7 @@ namespace net {
SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
bool direct)
- : weak_factory_(this),
- spdy_session_(spdy_session),
+ : spdy_session_(spdy_session),
is_reused_(spdy_session_->IsReused()),
stream_closed_(false),
closed_stream_status_(ERR_FAILED),
@@ -41,7 +40,8 @@ SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
request_body_buf_size_(0),
buffered_read_callback_pending_(false),
more_read_data_pending_(false),
- direct_(direct) {
+ direct_(direct),
+ weak_factory_(this) {
DCHECK(spdy_session_.get());
}
@@ -89,10 +89,6 @@ int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info,
return rv;
}
-const HttpResponseInfo* SpdyHttpStream::GetResponseInfo() const {
- return response_info_;
-}
-
UploadProgress SpdyHttpStream::GetUploadProgress() const {
if (!request_info_ || !HasUploadData())
return UploadProgress();
@@ -110,7 +106,7 @@ int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
// Check if we already have the response headers. If so, return synchronously.
if (response_headers_status_ == RESPONSE_HEADERS_ARE_COMPLETE) {
- CHECK(stream_->IsIdle());
+ CHECK(!stream_->IsIdle());
return OK;
}
@@ -123,7 +119,7 @@ int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
int SpdyHttpStream::ReadResponseBody(
IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
if (stream_.get())
- CHECK(stream_->IsIdle());
+ CHECK(!stream_->IsIdle());
CHECK(buf);
CHECK(buf_len);
@@ -210,10 +206,7 @@ int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
HttpResponseInfo* response,
const CompletionCallback& callback) {
if (stream_closed_) {
- if (stream_->type() == SPDY_PUSH_STREAM)
- return closed_stream_status_;
-
- return (closed_stream_status_ == OK) ? ERR_FAILED : closed_stream_status_;
+ return closed_stream_status_;
}
base::Time request_time = base::Time::Now();
diff --git a/chromium/net/spdy/spdy_http_stream.h b/chromium/net/spdy/spdy_http_stream.h
index 5a9377c4f1d..0f37c6600a3 100644
--- a/chromium/net/spdy/spdy_http_stream.h
+++ b/chromium/net/spdy/spdy_http_stream.h
@@ -51,7 +51,6 @@ class NET_EXPORT_PRIVATE SpdyHttpStream : public SpdyStream::Delegate,
const CompletionCallback& callback) OVERRIDE;
virtual UploadProgress GetUploadProgress() const OVERRIDE;
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
@@ -109,8 +108,6 @@ class NET_EXPORT_PRIVATE SpdyHttpStream : public SpdyStream::Delegate,
bool DoBufferedReadCallback();
bool ShouldWaitForMoreBufferedData() const;
- base::WeakPtrFactory<SpdyHttpStream> weak_factory_;
-
const base::WeakPtr<SpdySession> spdy_session_;
bool is_reused_;
SpdyStreamRequest stream_request_;
@@ -161,6 +158,8 @@ class NET_EXPORT_PRIVATE SpdyHttpStream : public SpdyStream::Delegate,
// Is this spdy stream direct to the origin server (or to a proxy).
bool direct_;
+ base::WeakPtrFactory<SpdyHttpStream> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SpdyHttpStream);
};
diff --git a/chromium/net/spdy/spdy_http_stream_unittest.cc b/chromium/net/spdy/spdy_http_stream_unittest.cc
index 5a5870365b5..d20d822493a 100644
--- a/chromium/net/spdy/spdy_http_stream_unittest.cc
+++ b/chromium/net/spdy/spdy_http_stream_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
#include "base/stl_util.h"
#include "crypto/ec_private_key.h"
#include "crypto/ec_signature_creator.h"
@@ -130,8 +131,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdyHttpStreamTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
// SpdyHttpStream::GetUploadProgress() should still work even before the
// stream is initialized.
@@ -142,13 +142,16 @@ TEST_P(SpdyHttpStreamTest, GetUploadProgressBeforeInitialization) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSession(reads, arraysize(reads), NULL, 0, key);
SpdyHttpStream stream(session_, false);
UploadProgress progress = stream.GetUploadProgress();
EXPECT_EQ(0u, progress.size());
EXPECT_EQ(0u, progress.position());
+
+ // Pump the event loop so |reads| is consumed before the function returns.
+ base::RunLoop().RunUntilIdle();
}
TEST_P(SpdyHttpStreamTest, SendRequest) {
@@ -165,7 +168,7 @@ TEST_P(SpdyHttpStreamTest, SendRequest) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
HttpRequestInfo request;
@@ -240,7 +243,7 @@ TEST_P(SpdyHttpStreamTest, LoadTimingTwoRequests) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSessionDeterministic(reads, arraysize(reads),
writes, arraysize(writes),
key);
@@ -329,7 +332,7 @@ TEST_P(SpdyHttpStreamTest, SendChunkedPost) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSession(vector_as_array(&reads), reads.size(),
vector_as_array(&writes), writes.size(),
key);
@@ -405,7 +408,7 @@ TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPost) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSessionDeterministic(reads, arraysize(reads),
writes, arraysize(writes),
key);
@@ -499,7 +502,7 @@ TEST_P(SpdyHttpStreamTest, SpdyURLTest) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
HttpRequestInfo request;
@@ -558,7 +561,7 @@ TEST_P(SpdyHttpStreamTest, DelayedSendChunkedPostWithWindowUpdate) {
HostPortPair host_port_pair("www.google.com", 80);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
InitSessionDeterministic(reads, arraysize(reads),
writes, arraysize(writes),
diff --git a/chromium/net/spdy/spdy_http_utils.cc b/chromium/net/spdy/spdy_http_utils.cc
index da95b51fad0..448c82ffd3c 100644
--- a/chromium/net/spdy/spdy_http_utils.cc
+++ b/chromium/net/spdy/spdy_http_utils.cc
@@ -29,18 +29,21 @@ bool SpdyHeadersToHttpResponse(const SpdyHeaderBlock& headers,
std::string version;
std::string status;
- // The "status" and "version" headers are required.
+ // The "status" header is required. "version" is required below SPDY4.
SpdyHeaderBlock::const_iterator it;
it = headers.find(status_key);
if (it == headers.end())
return false;
status = it->second;
- it = headers.find(version_key);
- if (it == headers.end())
- return false;
- version = it->second;
-
+ if (protocol_version >= SPDY4) {
+ version = "HTTP/1.1";
+ } else {
+ it = headers.find(version_key);
+ if (it == headers.end())
+ return false;
+ version = it->second;
+ }
std::string raw_headers(version);
raw_headers.push_back(' ');
raw_headers.append(status);
@@ -90,7 +93,7 @@ void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
while (it.GetNext()) {
std::string name = StringToLowerASCII(it.name());
if (name == "connection" || name == "proxy-connection" ||
- name == "transfer-encoding") {
+ name == "transfer-encoding" || name == "host") {
continue;
}
if (headers->find(name) == headers->end()) {
@@ -114,14 +117,16 @@ void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info,
else
(*headers)["url"] = HttpUtil::SpecForRequest(info.url);
} else {
- (*headers)[":version"] = kHttpProtocolVersion;
+ if (protocol_version < SPDY4) {
+ (*headers)[":version"] = kHttpProtocolVersion;
+ (*headers)[":host"] = GetHostAndOptionalPort(info.url);
+ } else {
+ (*headers)[":authority"] = GetHostAndOptionalPort(info.url);
+ }
(*headers)[":method"] = info.method;
- (*headers)[":host"] = GetHostAndOptionalPort(info.url);
(*headers)[":scheme"] = info.url.scheme();
(*headers)[":path"] = HttpUtil::PathForRequest(info.url);
- headers->erase("host"); // this is kinda insane, spdy 3 spec.
}
-
}
COMPILE_ASSERT(HIGHEST - LOWEST < 4 &&
@@ -170,7 +175,8 @@ GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
}
const char* scheme_header = protocol_version >= SPDY3 ? ":scheme" : "scheme";
- const char* host_header = protocol_version >= SPDY3 ? ":host" : "host";
+ const char* host_header = protocol_version >= SPDY4 ? ":authority" :
+ (protocol_version >= SPDY3 ? ":host" : "host");
const char* path_header = protocol_version >= SPDY3 ? ":path" : "url";
std::string scheme;
@@ -193,12 +199,4 @@ GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
return GURL(url);
}
-bool ShouldShowHttpHeaderValue(const std::string& header_name) {
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- if (header_name == "proxy-authorization")
- return false;
-#endif
- return true;
-}
-
} // namespace net
diff --git a/chromium/net/spdy/spdy_http_utils.h b/chromium/net/spdy/spdy_http_utils.h
index 6a91c888c9a..d15b6c7a873 100644
--- a/chromium/net/spdy/spdy_http_utils.h
+++ b/chromium/net/spdy/spdy_http_utils.h
@@ -38,13 +38,9 @@ void NET_EXPORT_PRIVATE CreateSpdyHeadersFromHttpRequest(
// Returns the URL associated with the |headers| by assembling the
// scheme, host and path from the protocol specific keys.
-GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
- SpdyMajorVersion protocol_version,
- bool pushed);
-
-// Returns true if the value of this header should be displayed.
-NET_EXPORT_PRIVATE bool ShouldShowHttpHeaderValue(
- const std::string& header_name);
+NET_EXPORT_PRIVATE GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
+ SpdyMajorVersion protocol_version,
+ bool pushed);
NET_EXPORT_PRIVATE SpdyPriority ConvertRequestPriorityToSpdyPriority(
RequestPriority priority,
diff --git a/chromium/net/spdy/spdy_http_utils_unittest.cc b/chromium/net/spdy/spdy_http_utils_unittest.cc
index daac323bcfe..d8111646bb8 100644
--- a/chromium/net/spdy/spdy_http_utils_unittest.cc
+++ b/chromium/net/spdy/spdy_http_utils_unittest.cc
@@ -52,16 +52,6 @@ TEST(SpdyHttpUtilsTest, ConvertSpdy3PriorityToRequestPriority) {
}
}
-TEST(SpdyHttpUtilsTest, ShowHttpHeaderValue){
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- EXPECT_FALSE(ShouldShowHttpHeaderValue("proxy-authorization"));
- EXPECT_TRUE(ShouldShowHttpHeaderValue("accept-encoding"));
-#else
- EXPECT_TRUE(ShouldShowHttpHeaderValue("proxy-authorization"));
- EXPECT_TRUE(ShouldShowHttpHeaderValue("accept-encoding"));
-#endif
-}
-
} // namespace
} // namespace net
diff --git a/chromium/net/spdy/spdy_network_transaction_unittest.cc b/chromium/net/spdy/spdy_network_transaction_unittest.cc
index bf9fbce45b6..f11cb656a9c 100644
--- a/chromium/net/spdy/spdy_network_transaction_unittest.cc
+++ b/chromium/net/spdy/spdy_network_transaction_unittest.cc
@@ -12,6 +12,7 @@
#include "base/memory/scoped_vector.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
#include "base/test/test_file_util.h"
#include "net/base/auth.h"
#include "net/base/net_log_unittest.h"
@@ -22,7 +23,7 @@
#include "net/http/http_network_session_peer.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_server_properties.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/next_proto.h"
#include "net/spdy/buffered_spdy_framer.h"
@@ -32,7 +33,9 @@
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/spdy/spdy_test_utils.h"
+#include "net/ssl/ssl_connection_status_flags.h"
#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
//-----------------------------------------------------------------------------
@@ -40,6 +43,10 @@
namespace net {
namespace {
+
+using testing::Each;
+using testing::Eq;
+
const char kRequestUrl[] = "http://www.google.com/";
enum SpdyNetworkTransactionTestSSLType {
@@ -63,15 +70,45 @@ struct SpdyNetworkTransactionTestParams {
SpdyNetworkTransactionTestSSLType ssl_type;
};
+void UpdateSpdySessionDependencies(
+ SpdyNetworkTransactionTestParams test_params,
+ SpdySessionDependencies* session_deps) {
+ switch (test_params.ssl_type) {
+ case SPDYNPN:
+ session_deps->http_server_properties.SetAlternateProtocol(
+ HostPortPair("www.google.com", 80), 443,
+ AlternateProtocolFromNextProto(test_params.protocol));
+ session_deps->use_alternate_protocols = true;
+ session_deps->next_protos = SpdyNextProtos();
+ break;
+ case SPDYNOSSL:
+ session_deps->force_spdy_over_ssl = false;
+ session_deps->force_spdy_always = true;
+ break;
+ case SPDYSSL:
+ session_deps->force_spdy_over_ssl = true;
+ session_deps->force_spdy_always = true;
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
SpdySessionDependencies* CreateSpdySessionDependencies(
SpdyNetworkTransactionTestParams test_params) {
- return new SpdySessionDependencies(test_params.protocol);
+ SpdySessionDependencies* session_deps =
+ new SpdySessionDependencies(test_params.protocol);
+ UpdateSpdySessionDependencies(test_params, session_deps);
+ return session_deps;
}
SpdySessionDependencies* CreateSpdySessionDependencies(
SpdyNetworkTransactionTestParams test_params,
ProxyService* proxy_service) {
- return new SpdySessionDependencies(test_params.protocol, proxy_service);
+ SpdySessionDependencies* session_deps =
+ new SpdySessionDependencies(test_params.protocol, proxy_service);
+ UpdateSpdySessionDependencies(test_params, session_deps);
+ return session_deps;
}
} // namespace
@@ -80,20 +117,25 @@ class SpdyNetworkTransactionTest
: public ::testing::TestWithParam<SpdyNetworkTransactionTestParams> {
protected:
SpdyNetworkTransactionTest() : spdy_util_(GetParam().protocol) {
+ LOG(INFO) << __FUNCTION__;
}
virtual ~SpdyNetworkTransactionTest() {
+ LOG(INFO) << __FUNCTION__;
// UploadDataStream posts deletion tasks back to the message loop on
// destruction.
upload_data_stream_.reset();
base::RunLoop().RunUntilIdle();
+ LOG(INFO) << __FUNCTION__;
}
virtual void SetUp() {
+ LOG(INFO) << __FUNCTION__;
google_get_request_initialized_ = false;
google_post_request_initialized_ = false;
google_chunked_post_request_initialized_ = false;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ LOG(INFO) << __FUNCTION__;
}
struct TransactionHelperResult {
@@ -159,50 +201,30 @@ class SpdyNetworkTransactionTest
}
void RunPreTestSetup() {
+ LOG(INFO) << __FUNCTION__;
if (!session_deps_.get())
session_deps_.reset(CreateSpdySessionDependencies(test_params_));
- if (!session_.get())
+ if (!session_.get()) {
session_ = SpdySessionDependencies::SpdyCreateSession(
session_deps_.get());
- HttpStreamFactory::set_use_alternate_protocols(false);
- HttpStreamFactory::set_force_spdy_over_ssl(false);
- HttpStreamFactory::set_force_spdy_always(false);
-
- std::vector<NextProto> next_protos = SpdyNextProtos();
-
- switch (test_params_.ssl_type) {
- case SPDYNPN:
- session_->http_server_properties()->SetAlternateProtocol(
- HostPortPair("www.google.com", 80), 443,
- AlternateProtocolFromNextProto(test_params_.protocol));
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(next_protos);
- break;
- case SPDYNOSSL:
- HttpStreamFactory::set_force_spdy_over_ssl(false);
- HttpStreamFactory::set_force_spdy_always(true);
- break;
- case SPDYSSL:
- HttpStreamFactory::set_force_spdy_over_ssl(true);
- HttpStreamFactory::set_force_spdy_always(true);
- break;
- default:
- NOTREACHED();
}
// We're now ready to use SSL-npn SPDY.
trans_.reset(new HttpNetworkTransaction(priority_, session_.get()));
+ LOG(INFO) << __FUNCTION__;
}
// Start the transaction, read some data, finish.
void RunDefaultTest() {
+ LOG(INFO) << __FUNCTION__;
if (!StartDefaultTest())
return;
FinishDefaultTest();
+ LOG(INFO) << __FUNCTION__;
}
bool StartDefaultTest() {
- output_.rv = trans_->Start(&request_, callback.callback(), log_);
+ output_.rv = trans_->Start(&request_, callback_.callback(), log_);
// We expect an IO Pending or some sort of error.
EXPECT_LT(output_.rv, 0);
@@ -210,7 +232,7 @@ class SpdyNetworkTransactionTest
}
void FinishDefaultTest() {
- output_.rv = callback.WaitForResult();
+ output_.rv = callback_.WaitForResult();
if (output_.rv != OK) {
session_->spdy_session_pool()->CloseCurrentSessions(net::ERR_ABORTED);
return;
@@ -290,17 +312,35 @@ class SpdyNetworkTransactionTest
VerifyDataConsumed();
}
+ void RunToCompletionWithSSLData(
+ StaticSocketDataProvider* data,
+ scoped_ptr<SSLSocketDataProvider> ssl_provider) {
+ RunPreTestSetup();
+ AddDataWithSSLSocketDataProvider(data, ssl_provider.Pass());
+ RunDefaultTest();
+ VerifyDataConsumed();
+ }
+
void AddData(StaticSocketDataProvider* data) {
+ scoped_ptr<SSLSocketDataProvider> ssl_provider(
+ new SSLSocketDataProvider(ASYNC, OK));
+ AddDataWithSSLSocketDataProvider(data, ssl_provider.Pass());
+ }
+
+ void AddDataWithSSLSocketDataProvider(
+ StaticSocketDataProvider* data,
+ scoped_ptr<SSLSocketDataProvider> ssl_provider) {
DCHECK(!deterministic_);
data_vector_.push_back(data);
- SSLSocketDataProvider* ssl_provider =
- new SSLSocketDataProvider(ASYNC, OK);
if (test_params_.ssl_type == SPDYNPN)
ssl_provider->SetNextProto(test_params_.protocol);
- ssl_vector_.push_back(ssl_provider);
- if (test_params_.ssl_type == SPDYNPN || test_params_.ssl_type == SPDYSSL)
- session_deps_->socket_factory->AddSSLSocketDataProvider(ssl_provider);
+ if (test_params_.ssl_type == SPDYNPN ||
+ test_params_.ssl_type == SPDYSSL) {
+ session_deps_->socket_factory->AddSSLSocketDataProvider(
+ ssl_provider.get());
+ }
+ ssl_vector_.push_back(ssl_provider.release());
session_deps_->socket_factory->AddSocketDataProvider(data);
if (test_params_.ssl_type == SPDYNPN) {
@@ -373,7 +413,7 @@ class SpdyNetworkTransactionTest
TransactionHelperResult output_;
scoped_ptr<StaticSocketDataProvider> first_transaction_;
SSLVector ssl_vector_;
- TestCompletionCallback callback;
+ TestCompletionCallback callback_;
scoped_ptr<HttpNetworkTransaction> trans_;
scoped_ptr<HttpNetworkTransaction> trans_http_;
DataVector data_vector_;
@@ -440,7 +480,7 @@ class SpdyNetworkTransactionTest
base::FilePath file_path;
CHECK(base::CreateTemporaryFileInDir(temp_dir_.path(), &file_path));
CHECK_EQ(static_cast<int>(kUploadDataSize),
- file_util::WriteFile(file_path, kUploadData, kUploadDataSize));
+ base::WriteFile(file_path, kUploadData, kUploadDataSize));
ScopedVector<UploadElementReader> element_readers;
element_readers.push_back(
@@ -467,7 +507,7 @@ class SpdyNetworkTransactionTest
base::FilePath file_path;
CHECK(base::CreateTemporaryFileInDir(temp_dir_.path(), &file_path));
CHECK_EQ(static_cast<int>(kUploadDataSize),
- file_util::WriteFile(file_path, kUploadData, kUploadDataSize));
+ base::WriteFile(file_path, kUploadData, kUploadDataSize));
CHECK(file_util::MakeFileUnreadable(file_path));
ScopedVector<UploadElementReader> element_readers;
@@ -496,7 +536,7 @@ class SpdyNetworkTransactionTest
base::FilePath file_path;
CHECK(base::CreateTemporaryFileInDir(temp_dir_.path(), &file_path));
CHECK_EQ(static_cast<int>(kUploadDataSize),
- file_util::WriteFile(file_path, kUploadData, kUploadDataSize));
+ base::WriteFile(file_path, kUploadData, kUploadDataSize));
ScopedVector<UploadElementReader> element_readers;
element_readers.push_back(
@@ -573,7 +613,7 @@ class SpdyNetworkTransactionTest
int port = helper.test_params().ssl_type == SPDYNPN ? 443 : 80;
HostPortPair host_port_pair(url.host(), port);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
BoundNetLog log;
const scoped_refptr<HttpNetworkSession>& session = helper.session();
base::WeakPtr<SpdySession> spdy_session =
@@ -690,24 +730,26 @@ INSTANTIATE_TEST_CASE_P(
SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYNOSSL),
SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYSSL),
SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYNPN),
- SpdyNetworkTransactionTestParams(kProtoSPDY4a2, SPDYNOSSL),
- SpdyNetworkTransactionTestParams(kProtoSPDY4a2, SPDYSSL),
- SpdyNetworkTransactionTestParams(kProtoSPDY4a2, SPDYNPN),
- SpdyNetworkTransactionTestParams(kProtoHTTP2Draft04, SPDYNOSSL),
- SpdyNetworkTransactionTestParams(kProtoHTTP2Draft04, SPDYSSL),
- SpdyNetworkTransactionTestParams(kProtoHTTP2Draft04, SPDYNPN)));
+ SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNOSSL),
+ SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYSSL),
+ SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNPN)));
// Verify HttpNetworkTransaction constructor.
TEST_P(SpdyNetworkTransactionTest, Constructor) {
+ LOG(INFO) << __FUNCTION__;
scoped_ptr<SpdySessionDependencies> session_deps(
CreateSpdySessionDependencies(GetParam()));
+ LOG(INFO) << __FUNCTION__;
scoped_refptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(session_deps.get()));
+ LOG(INFO) << __FUNCTION__;
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+ LOG(INFO) << __FUNCTION__;
}
TEST_P(SpdyNetworkTransactionTest, Get) {
+ LOG(INFO) << __FUNCTION__;
// Construct the request.
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
@@ -1060,9 +1102,7 @@ TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) {
helper.session()->ssl_config_service()->GetSSLConfig(&preconnect_ssl_config);
HttpStreamFactory* http_stream_factory =
helper.session()->http_stream_factory();
- if (http_stream_factory->has_next_protos()) {
- preconnect_ssl_config.next_protos = http_stream_factory->next_protos();
- }
+ helper.session()->GetNextProtos(&preconnect_ssl_config.next_protos);
http_stream_factory->PreconnectStreams(
1, httpreq, DEFAULT_PRIORITY,
@@ -1132,9 +1172,11 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) {
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
scoped_ptr<SpdyFrame> settings_frame(
spdy_util_.ConstructSpdySettings(settings));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
MockWrite writes[] = {
CreateMockWrite(*req),
+ CreateMockWrite(*settings_ack, 2),
CreateMockWrite(*req2),
CreateMockWrite(*req3),
};
@@ -1144,10 +1186,10 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) {
CreateMockRead(*resp),
CreateMockRead(*body),
CreateMockRead(*fbody),
- CreateMockRead(*resp2, 7),
+ CreateMockRead(*resp2, 8),
CreateMockRead(*body2),
CreateMockRead(*fbody2),
- CreateMockRead(*resp3, 12),
+ CreateMockRead(*resp3, 13),
CreateMockRead(*body3),
CreateMockRead(*fbody3),
@@ -1270,8 +1312,10 @@ TEST_P(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) {
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
scoped_ptr<SpdyFrame> settings_frame(
spdy_util_.ConstructSpdySettings(settings));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
MockWrite writes[] = { CreateMockWrite(*req),
+ CreateMockWrite(*settings_ack, 2),
CreateMockWrite(*req2),
CreateMockWrite(*req4),
CreateMockWrite(*req3),
@@ -1281,12 +1325,12 @@ TEST_P(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) {
CreateMockRead(*resp),
CreateMockRead(*body),
CreateMockRead(*fbody),
- CreateMockRead(*resp2, 7),
+ CreateMockRead(*resp2, 8),
CreateMockRead(*body2),
CreateMockRead(*fbody2),
- CreateMockRead(*resp4, 13),
+ CreateMockRead(*resp4, 14),
CreateMockRead(*fbody4),
- CreateMockRead(*resp3, 16),
+ CreateMockRead(*resp3, 17),
CreateMockRead(*body3),
CreateMockRead(*fbody3),
@@ -1412,8 +1456,11 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) {
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
scoped_ptr<SpdyFrame> settings_frame(
spdy_util_.ConstructSpdySettings(settings));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = { CreateMockWrite(*req),
+ MockWrite writes[] = {
+ CreateMockWrite(*req),
+ CreateMockWrite(*settings_ack, 2),
CreateMockWrite(*req2),
};
MockRead reads[] = {
@@ -1421,7 +1468,7 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) {
CreateMockRead(*resp),
CreateMockRead(*body),
CreateMockRead(*fbody),
- CreateMockRead(*resp2, 7),
+ CreateMockRead(*resp2, 8),
CreateMockRead(*body2),
CreateMockRead(*fbody2),
MockRead(ASYNC, 0, 0), // EOF
@@ -1546,8 +1593,11 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) {
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
scoped_ptr<SpdyFrame> settings_frame(
spdy_util_.ConstructSpdySettings(settings));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = { CreateMockWrite(*req),
+ MockWrite writes[] = {
+ CreateMockWrite(*req),
+ CreateMockWrite(*settings_ack, 2),
CreateMockWrite(*req2),
};
MockRead reads[] = {
@@ -1555,7 +1605,7 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) {
CreateMockRead(*resp),
CreateMockRead(*body),
CreateMockRead(*fin_body),
- CreateMockRead(*resp2, 7),
+ CreateMockRead(*resp2, 8),
MockRead(ASYNC, ERR_CONNECTION_RESET, 0), // Abort!
};
@@ -1959,6 +2009,7 @@ TEST_P(SpdyNetworkTransactionTest, DelayedChunkedPost) {
// Test that a POST without any post data works.
TEST_P(SpdyNetworkTransactionTest, NullPost) {
+ BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
// Setup the request
HttpRequestInfo request;
request.method = "POST";
@@ -1968,10 +2019,14 @@ TEST_P(SpdyNetworkTransactionTest, NullPost) {
// When request.upload_data_stream is NULL for post, content-length is
// expected to be 0.
- scoped_ptr<SpdyFrame> req(
- spdy_util_.ConstructSpdyPost(kRequestUrl, 1, 0, LOWEST, NULL, 0));
- // Set the FIN bit since there will be no body.
- test::SetFrameFlags(req.get(), CONTROL_FLAG_FIN, spdy_util_.spdy_version());
+ SpdySynStreamIR syn_ir(1);
+ syn_ir.set_name_value_block(
+ *spdy_util_.ConstructPostHeaderBlock(kRequestUrl, 0));
+ syn_ir.set_fin(true); // No body.
+ syn_ir.set_priority(ConvertRequestPriorityToSpdyPriority(
+ LOWEST, spdy_util_.spdy_version()));
+ scoped_ptr<SpdyFrame> req(framer.SerializeFrame(syn_ir));
+
MockWrite writes[] = {
CreateMockWrite(*req),
};
@@ -1998,6 +2053,7 @@ TEST_P(SpdyNetworkTransactionTest, NullPost) {
// Test that a simple POST works.
TEST_P(SpdyNetworkTransactionTest, EmptyPost) {
+ BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
// Create an empty UploadDataStream.
ScopedVector<UploadElementReader> element_readers;
UploadDataStream stream(element_readers.Pass(), 0);
@@ -2009,11 +2065,15 @@ TEST_P(SpdyNetworkTransactionTest, EmptyPost) {
request.upload_data_stream = &stream;
const uint64 kContentLength = 0;
- scoped_ptr<SpdyFrame> req(
- spdy_util_.ConstructSpdyPost(
- kRequestUrl, 1, kContentLength, LOWEST, NULL, 0));
- // Set the FIN bit since there will be no body.
- test::SetFrameFlags(req.get(), CONTROL_FLAG_FIN, spdy_util_.spdy_version());
+
+ SpdySynStreamIR syn_ir(1);
+ syn_ir.set_name_value_block(
+ *spdy_util_.ConstructPostHeaderBlock(kRequestUrl, kContentLength));
+ syn_ir.set_fin(true); // No body.
+ syn_ir.set_priority(ConvertRequestPriorityToSpdyPriority(
+ LOWEST, spdy_util_.spdy_version()));
+ scoped_ptr<SpdyFrame> req(framer.SerializeFrame(syn_ir));
+
MockWrite writes[] = {
CreateMockWrite(*req),
};
@@ -2037,57 +2097,50 @@ TEST_P(SpdyNetworkTransactionTest, EmptyPost) {
EXPECT_EQ("hello!", out.response_data);
}
-// While we're doing a post, the server sends back a SYN_REPLY.
-TEST_P(SpdyNetworkTransactionTest, PostWithEarlySynReply) {
- static const char upload[] = { "hello!" };
- ScopedVector<UploadElementReader> element_readers;
- element_readers.push_back(
- new UploadBytesElementReader(upload, sizeof(upload)));
- UploadDataStream stream(element_readers.Pass(), 0);
-
- // Setup the request
- HttpRequestInfo request;
- request.method = "POST";
- request.url = GURL(kRequestUrl);
- request.upload_data_stream = &stream;
-
- scoped_ptr<SpdyFrame> stream_reply(
- spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
- MockRead reads[] = {
- CreateMockRead(*stream_reply, 1),
- MockRead(ASYNC, 0, 4) // EOF
- };
-
- scoped_ptr<SpdyFrame> req(
- spdy_util_.ConstructSpdyPost(
- kRequestUrl, 1, kUploadDataSize, LOWEST, NULL, 0));
+// While we're doing a post, the server sends the reply before upload completes.
+TEST_P(SpdyNetworkTransactionTest, ResponseBeforePostCompletes) {
+ scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(NULL, 0));
scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
- scoped_ptr<SpdyFrame> rst(
- spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR));
MockWrite writes[] = {
CreateMockWrite(*req, 0),
- CreateMockWrite(*body, 2),
- CreateMockWrite(*rst, 3)
+ CreateMockWrite(*body, 3),
+ };
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1),
+ CreateMockRead(*body, 2),
+ MockRead(ASYNC, 0, 4) // EOF
};
+ // Write the request headers, and read the complete response
+ // while still waiting for chunked request data.
DeterministicSocketData data(reads, arraysize(reads),
writes, arraysize(writes));
- NormalSpdyTransactionHelper helper(CreatePostRequest(), DEFAULT_PRIORITY,
+ NormalSpdyTransactionHelper helper(CreateChunkedPostRequest(),
+ DEFAULT_PRIORITY,
BoundNetLog(), GetParam(), NULL);
helper.SetDeterministic();
helper.RunPreTestSetup();
helper.AddDeterministicData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(
- &CreatePostRequest(), callback.callback(), BoundNetLog());
- EXPECT_EQ(ERR_IO_PENDING, rv);
+ ASSERT_TRUE(helper.StartDefaultTest());
- data.RunFor(4);
- rv = callback.WaitForResult();
- EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv);
- data.RunFor(1);
+ // Process the request headers, SYN_REPLY, and response body.
+ // The request body is still in flight.
+ data.RunFor(3);
+
+ const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ // Finish sending the request body.
+ helper.request().upload_data_stream->AppendChunk(
+ kUploadData, kUploadDataSize, true);
+ data.RunFor(2);
+
+ std::string response_body;
+ EXPECT_EQ(OK, ReadTransaction(helper.trans(), &response_body));
+ EXPECT_EQ(kUploadData, response_body);
+ helper.VerifyDataConsumed();
}
// The client upon cancellation tries to send a RST_STREAM frame. The mock
@@ -2155,7 +2208,7 @@ TEST_P(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) {
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
scoped_ptr<SpdyFrame> rst(
- spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_STREAM_IN_USE));
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR));
MockWrite writes[] = {
CreateMockWrite(*req),
CreateMockWrite(*rst),
@@ -2511,11 +2564,12 @@ TEST_P(SpdyNetworkTransactionTest, RedirectGetRequest) {
writes2, arraysize(writes2));
// TODO(erikchen): Make test support SPDYSSL, SPDYNPN
- HttpStreamFactory::set_force_spdy_over_ssl(false);
- HttpStreamFactory::set_force_spdy_always(true);
TestDelegate d;
{
- SpdyURLRequestContext spdy_url_request_context(GetParam().protocol);
+ SpdyURLRequestContext spdy_url_request_context(
+ GetParam().protocol,
+ false /* force_spdy_over_ssl*/,
+ true /* force_spdy_always */);
net::URLRequest r(GURL("http://www.google.com/"),
DEFAULT_PRIORITY,
&d,
@@ -2605,11 +2659,12 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
writes2, arraysize(writes2));
// TODO(erikchen): Make test support SPDYSSL, SPDYNPN
- HttpStreamFactory::set_force_spdy_over_ssl(false);
- HttpStreamFactory::set_force_spdy_always(true);
TestDelegate d;
TestDelegate d2;
- SpdyURLRequestContext spdy_url_request_context(GetParam().protocol);
+ SpdyURLRequestContext spdy_url_request_context(
+ GetParam().protocol,
+ false /* force_spdy_over_ssl*/,
+ true /* force_spdy_always */);
{
net::URLRequest r(GURL("http://www.google.com/"),
DEFAULT_PRIORITY,
@@ -3035,6 +3090,12 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) {
}
TEST_P(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) {
+ if (spdy_util_.spdy_version() == SPDY4) {
+ // PUSH_PROMISE with stream id 0 is connection-level error.
+ // TODO(baranovich): Test session going away.
+ return;
+ }
+
scoped_ptr<SpdyFrame> stream1_syn(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
scoped_ptr<SpdyFrame> stream1_body(
@@ -3174,15 +3235,8 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushNoURL) {
(*incomplete_headers)["hello"] = "bye";
(*incomplete_headers)[spdy_util_.GetStatusKey()] = "200 OK";
(*incomplete_headers)[spdy_util_.GetVersionKey()] = "HTTP/1.1";
- scoped_ptr<SpdyFrame> stream2_syn(
- spdy_util_.ConstructSpdyControlFrame(incomplete_headers.Pass(),
- false,
- 2, // Stream ID
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- // Associated stream ID
- 1));
+ scoped_ptr<SpdyFrame> stream2_syn(spdy_util_.ConstructInitialSpdyPushFrame(
+ incomplete_headers.Pass(), 2, 1));
MockRead reads[] = {
CreateMockRead(*stream1_reply, 2),
CreateMockRead(*stream2_syn, 3),
@@ -3255,16 +3309,20 @@ TEST_P(SpdyNetworkTransactionTest, SynReplyHeaders) {
test_cases[0].expected_headers["cookie"] += "val2";
test_cases[0].expected_headers["hello"] = "bye";
test_cases[0].expected_headers["status"] = "200";
- test_cases[0].expected_headers["version"] = "HTTP/1.1";
test_cases[1].expected_headers["hello"] = "bye";
test_cases[1].expected_headers["status"] = "200";
- test_cases[1].expected_headers["version"] = "HTTP/1.1";
test_cases[2].expected_headers["cookie"] = "val1,val2";
test_cases[2].expected_headers["hello"] = "bye";
test_cases[2].expected_headers["status"] = "200";
- test_cases[2].expected_headers["version"] = "HTTP/1.1";
+
+ if (spdy_util_.spdy_version() < SPDY4) {
+ // SPDY4/HTTP2 eliminates use of the :version header.
+ test_cases[0].expected_headers["version"] = "HTTP/1.1";
+ test_cases[1].expected_headers["version"] = "HTTP/1.1";
+ test_cases[2].expected_headers["version"] = "HTTP/1.1";
+ }
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
scoped_ptr<SpdyFrame> req(
@@ -3553,7 +3611,12 @@ TEST_P(SpdyNetworkTransactionTest, InvalidSynReply) {
}
// Verify that we don't crash on some corrupt frames.
+// TODO(jgraettinger): SPDY4 and up treats a header decompression failure as a
+// connection error. I'd like to backport this behavior to SPDY3 as well.
TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) {
+ if (spdy_util_.spdy_version() >= SPDY4) {
+ return;
+ }
// This is the length field that's too short.
scoped_ptr<SpdyFrame> syn_reply_wrong_length(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
@@ -3600,19 +3663,107 @@ TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) {
}
}
+// SPDY4 treats a header decompression failure as a connection-level error.
+TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionErrorSpdy4) {
+ if (spdy_util_.spdy_version() < SPDY4) {
+ return;
+ }
+ // This is the length field that's too short.
+ scoped_ptr<SpdyFrame> syn_reply_wrong_length(
+ spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
+ size_t right_size =
+ syn_reply_wrong_length->size() - framer.GetControlFrameHeaderSize();
+ size_t wrong_size = right_size - 4;
+ test::SetFrameLength(syn_reply_wrong_length.get(),
+ wrong_size,
+ spdy_util_.spdy_version());
+
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(
+ 0, GOAWAY_COMPRESSION_ERROR, "Framer error: 5 (DECOMPRESS_FAILURE)."));
+ MockWrite writes[] = {CreateMockWrite(*req), CreateMockWrite(*goaway)};
+
+ scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ MockRead reads[] = {
+ MockRead(ASYNC, syn_reply_wrong_length->data(),
+ syn_reply_wrong_length->size() - 4),
+ };
+
+ DelayedSocketData data(1, reads, arraysize(reads),
+ writes, arraysize(writes));
+ NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
+ BoundNetLog(), GetParam(), NULL);
+ helper.RunToCompletion(&data);
+ TransactionHelperResult out = helper.output();
+ EXPECT_EQ(ERR_SPDY_COMPRESSION_ERROR, out.rv);
+}
+
+TEST_P(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) {
+ if (GetParam().protocol < kProtoSPDY4) {
+ // Decompression failures are a stream error in SPDY3 and above.
+ return;
+ }
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(
+ 0, GOAWAY_COMPRESSION_ERROR, "Framer error: 5 (DECOMPRESS_FAILURE)."));
+ MockWrite writes[] = {CreateMockWrite(*req), CreateMockWrite(*goaway)};
+
+ // Read HEADERS with corrupted payload.
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ memset(resp->data() + 12, 0xff, resp->size() - 12);
+ MockRead reads[] = {CreateMockRead(*resp)};
+
+ DelayedSocketData data(1, reads, arraysize(reads), writes, arraysize(writes));
+ NormalSpdyTransactionHelper helper(
+ CreateGetRequest(), DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL);
+ helper.RunToCompletion(&data);
+ TransactionHelperResult out = helper.output();
+ EXPECT_EQ(ERR_SPDY_COMPRESSION_ERROR, out.rv);
+}
+
+TEST_P(SpdyNetworkTransactionTest, GoAwayOnFrameSizeError) {
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(
+ 0, GOAWAY_PROTOCOL_ERROR, "Framer error: 1 (INVALID_CONTROL_FRAME)."));
+ MockWrite writes[] = {CreateMockWrite(*req), CreateMockWrite(*goaway)};
+
+ // Read WINDOW_UPDATE with incorrectly-sized payload.
+ // TODO(jgraettinger): SpdyFramer signals this as an INVALID_CONTROL_FRAME,
+ // which is mapped to a protocol error, and not a frame size error.
+ scoped_ptr<SpdyFrame> bad_window_update(
+ spdy_util_.ConstructSpdyWindowUpdate(1, 1));
+ test::SetFrameLength(bad_window_update.get(),
+ bad_window_update->size() - 1,
+ spdy_util_.spdy_version());
+ MockRead reads[] = {CreateMockRead(*bad_window_update)};
+
+ DelayedSocketData data(1, reads, arraysize(reads), writes, arraysize(writes));
+ NormalSpdyTransactionHelper helper(
+ CreateGetRequest(), DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL);
+ helper.RunToCompletion(&data);
+ TransactionHelperResult out = helper.output();
+ EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv);
+}
+
// Test that we shutdown correctly on write errors.
TEST_P(SpdyNetworkTransactionTest, WriteError) {
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
MockWrite writes[] = {
- // We'll write 10 bytes successfully
- MockWrite(ASYNC, req->data(), 10, 0),
- // Followed by ERROR!
- MockWrite(ASYNC, ERR_FAILED, 1),
+ // We'll write 10 bytes successfully
+ MockWrite(ASYNC, req->data(), 10, 0),
+ // Followed by ERROR!
+ MockWrite(ASYNC, ERR_FAILED, 1),
+ // Session drains and attempts to write a GOAWAY: Another ERROR!
+ MockWrite(ASYNC, ERR_FAILED, 2),
};
MockRead reads[] = {
- MockRead(ASYNC, 0, 2) // EOF
+ MockRead(ASYNC, 0, 3) // EOF
};
DeterministicSocketData data(reads, arraysize(reads),
@@ -3662,13 +3813,15 @@ TEST_P(SpdyNetworkTransactionTest, PartialWrite) {
// In this test, we enable compression, but get a uncompressed SynReply from
// the server. Verify that teardown is all clean.
TEST_P(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) {
+ if (spdy_util_.spdy_version() >= SPDY4) {
+ // HPACK doesn't use deflate compression.
+ return;
+ }
scoped_ptr<SpdyFrame> compressed(
spdy_util_.ConstructSpdyGet(NULL, 0, true, 1, LOWEST, true));
- scoped_ptr<SpdyFrame> rst(
- spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(*compressed),
- };
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(
+ 0, GOAWAY_COMPRESSION_ERROR, "Framer error: 5 (DECOMPRESS_FAILURE)."));
+ MockWrite writes[] = {CreateMockWrite(*compressed), CreateMockWrite(*goaway)};
scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
@@ -3685,7 +3838,7 @@ TEST_P(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) {
BoundNetLog(), GetParam(), session_deps);
helper.RunToCompletion(&data);
TransactionHelperResult out = helper.output();
- EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv);
+ EXPECT_EQ(ERR_SPDY_COMPRESSION_ERROR, out.rv);
data.Reset();
}
@@ -3761,9 +3914,12 @@ TEST_P(SpdyNetworkTransactionTest, NetLog) {
expected.push_back(std::string(spdy_util_.GetHostKey()) + ": www.google.com");
expected.push_back(std::string(spdy_util_.GetPathKey()) + ": /");
expected.push_back(std::string(spdy_util_.GetSchemeKey()) + ": http");
- expected.push_back(std::string(spdy_util_.GetVersionKey()) + ": HTTP/1.1");
expected.push_back(std::string(spdy_util_.GetMethodKey()) + ": GET");
expected.push_back("user-agent: Chrome");
+ if (spdy_util_.spdy_version() < SPDY4) {
+ // SPDY4/HTTP2 eliminates use of the :version header.
+ expected.push_back(std::string(spdy_util_.GetVersionKey()) + ": HTTP/1.1");
+ }
EXPECT_EQ(expected.size(), header_list->GetSize());
for (std::vector<std::string>::const_iterator it = expected.begin();
it != expected.end();
@@ -3975,11 +4131,11 @@ TEST_P(SpdyNetworkTransactionTest, BufferedAll) {
MockWrite writes[] = { CreateMockWrite(*req) };
// 5 data frames in a single read.
- scoped_ptr<SpdyFrame> syn_reply(
- spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
- // turn off FIN bit
- test::SetFrameFlags(
- syn_reply.get(), CONTROL_FLAG_NONE, spdy_util_.spdy_version());
+ SpdySynReplyIR reply_ir(1);
+ reply_ir.SetHeader(spdy_util_.GetStatusKey(), "200");
+ reply_ir.SetHeader(spdy_util_.GetVersionKey(), "HTTP/1.1");
+
+ scoped_ptr<SpdyFrame> syn_reply(framer.SerializeFrame(reply_ir));
scoped_ptr<SpdyFrame> data_frame(
framer.CreateDataFrame(1, "message", 7, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> data_frame_fin(
@@ -4157,7 +4313,9 @@ TEST_P(SpdyNetworkTransactionTest, BufferedCancelled) {
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
- MockWrite writes[] = { CreateMockWrite(*req) };
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
+ MockWrite writes[] = {CreateMockWrite(*req), CreateMockWrite(*rst)};
// NOTE: We don't FIN the stream.
scoped_ptr<SpdyFrame> data_frame(
@@ -4198,21 +4356,16 @@ TEST_P(SpdyNetworkTransactionTest, BufferedCancelled) {
// Read Data
TestCompletionCallback read_callback;
- do {
- const int kReadSize = 256;
- scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kReadSize));
- rv = trans->Read(buf.get(), kReadSize, read_callback.callback());
- if (rv == net::ERR_IO_PENDING) {
- // Complete the read now, which causes buffering to start.
- data.CompleteRead();
- // Destroy the transaction, causing the stream to get cancelled
- // and orphaning the buffered IO task.
- helper.ResetTrans();
- break;
- }
- // We shouldn't get here in this test.
- FAIL() << "Unexpected read: " << rv;
- } while (rv > 0);
+ const int kReadSize = 256;
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kReadSize));
+ rv = trans->Read(buf.get(), kReadSize, read_callback.callback());
+ ASSERT_EQ(net::ERR_IO_PENDING, rv) << "Unexpected read: " << rv;
+
+ // Complete the read now, which causes buffering to start.
+ data.CompleteRead();
+ // Destroy the transaction, causing the stream to get cancelled
+ // and orphaning the buffered IO task.
+ helper.ResetTrans();
// Flush the MessageLoop; this will cause the buffered IO task
// to run for the final time.
@@ -4225,6 +4378,11 @@ TEST_P(SpdyNetworkTransactionTest, BufferedCancelled) {
// Test that if the server requests persistence of settings, that we save
// the settings in the HttpServerProperties.
TEST_P(SpdyNetworkTransactionTest, SettingsSaved) {
+ if (spdy_util_.spdy_version() >= SPDY4) {
+ // SPDY4 doesn't support flags on individual settings, and
+ // has no concept of settings persistence.
+ return;
+ }
static const SpdyHeaderInfo kSynReplyInfo = {
SYN_REPLY, // Syn Reply
1, // Stream ID
@@ -4329,6 +4487,7 @@ TEST_P(SpdyNetworkTransactionTest, SettingsSaved) {
// Test that when there are settings saved that they are sent back to the
// server upon session establishment.
TEST_P(SpdyNetworkTransactionTest, SettingsPlayback) {
+ // TODO(jgraettinger): Remove settings persistence mechanisms altogether.
static const SpdyHeaderInfo kSynReplyInfo = {
SYN_REPLY, // Syn Reply
1, // Stream ID
@@ -4359,9 +4518,9 @@ TEST_P(SpdyNetworkTransactionTest, SettingsPlayback) {
EXPECT_TRUE(spdy_session_pool->http_server_properties()->GetSpdySettings(
host_port_pair).empty());
- const SpdySettingsIds kSampleId1 = SETTINGS_UPLOAD_BANDWIDTH;
+ const SpdySettingsIds kSampleId1 = SETTINGS_MAX_CONCURRENT_STREAMS;
unsigned int kSampleValue1 = 0x0a0a0a0a;
- const SpdySettingsIds kSampleId2 = SETTINGS_ROUND_TRIP_TIME;
+ const SpdySettingsIds kSampleId2 = SETTINGS_INITIAL_WINDOW_SIZE;
unsigned int kSampleValue2 = 0x0c0c0c0c;
// First add a persisted setting.
@@ -4406,7 +4565,7 @@ TEST_P(SpdyNetworkTransactionTest, SettingsPlayback) {
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
std::vector<MockWrite> writes;
- if (GetParam().protocol == kProtoHTTP2Draft04) {
+ if (GetParam().protocol == kProtoSPDY4) {
writes.push_back(
MockWrite(ASYNC,
kHttp2ConnectionHeaderPrefix,
@@ -4688,12 +4847,12 @@ TEST_P(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) {
// Check that the SpdySession is still in the SpdySessionPool.
HostPortPair host_port_pair("www.google.com", helper.port());
SpdySessionKey session_pool_key_direct(
- host_port_pair, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_TRUE(HasSpdySession(spdy_session_pool, session_pool_key_direct));
SpdySessionKey session_pool_key_proxy(
host_port_pair,
ProxyServer::FromURI("www.foo.com", ProxyServer::SCHEME_HTTP),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
EXPECT_FALSE(HasSpdySession(spdy_session_pool, session_pool_key_proxy));
// Set up data for the proxy connection.
@@ -4888,7 +5047,7 @@ TEST_P(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) {
// Test that turning SPDY on and off works properly.
TEST_P(SpdyNetworkTransactionTest, SpdyOnOffToggle) {
- net::HttpStreamFactory::set_spdy_enabled(true);
+ HttpStreamFactory::set_spdy_enabled(true);
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
MockWrite spdy_writes[] = { CreateMockWrite(*req) };
@@ -5005,7 +5164,8 @@ TEST_P(SpdyNetworkTransactionTest, SpdyBasicAuth) {
EXPECT_EQ("MyRealm", auth_challenge->realm);
// Restart with a username/password.
- AuthCredentials credentials(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"));
+ AuthCredentials credentials(base::ASCIIToUTF16("foo"),
+ base::ASCIIToUTF16("bar"));
TestCompletionCallback callback_restart;
const int rv_restart = trans->RestartWithAuth(
credentials, callback_restart.callback());
@@ -5034,13 +5194,7 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushWithHeaders) {
spdy_util_.AddUrlToHeaderBlock(
"http://www.google.com/foo.dat", initial_headers.get());
scoped_ptr<SpdyFrame> stream2_syn(
- spdy_util_.ConstructSpdyControlFrame(initial_headers.Pass(),
- false,
- 2,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- 1));
+ spdy_util_.ConstructInitialSpdyPushFrame(initial_headers.Pass(), 2, 1));
scoped_ptr<SpdyHeaderBlock> late_headers(new SpdyHeaderBlock());
(*late_headers)["hello"] = "bye";
@@ -5103,13 +5257,7 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushClaimBeforeHeaders) {
spdy_util_.AddUrlToHeaderBlock(
"http://www.google.com/foo.dat", initial_headers.get());
scoped_ptr<SpdyFrame> stream2_syn(
- spdy_util_.ConstructSpdyControlFrame(initial_headers.Pass(),
- false,
- 2,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- 1));
+ spdy_util_.ConstructInitialSpdyPushFrame(initial_headers.Pass(), 2, 1));
scoped_ptr<SpdyHeaderBlock> late_headers(new SpdyHeaderBlock());
(*late_headers)["hello"] = "bye";
@@ -5214,6 +5362,7 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushClaimBeforeHeaders) {
EXPECT_TRUE(data.at_write_eof());
}
+// TODO(baranovich): HTTP 2 does not allow multiple HEADERS frames
TEST_P(SpdyNetworkTransactionTest, ServerPushWithTwoHeaderFrames) {
// We push a stream and attempt to claim it before the headers come down.
scoped_ptr<SpdyFrame> stream1_syn(
@@ -5225,16 +5374,14 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushWithTwoHeaderFrames) {
};
scoped_ptr<SpdyHeaderBlock> initial_headers(new SpdyHeaderBlock());
+ if (spdy_util_.spdy_version() < SPDY4) {
+ // In SPDY4 PUSH_PROMISE headers won't show up in the response headers.
+ (*initial_headers)["alpha"] = "beta";
+ }
spdy_util_.AddUrlToHeaderBlock(
"http://www.google.com/foo.dat", initial_headers.get());
scoped_ptr<SpdyFrame> stream2_syn(
- spdy_util_.ConstructSpdyControlFrame(initial_headers.Pass(),
- false,
- 2,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- 1));
+ spdy_util_.ConstructInitialSpdyPushFrame(initial_headers.Pass(), 2, 1));
scoped_ptr<SpdyHeaderBlock> middle_headers(new SpdyHeaderBlock());
(*middle_headers)["hello"] = "bye";
@@ -5249,7 +5396,10 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushWithTwoHeaderFrames) {
scoped_ptr<SpdyHeaderBlock> late_headers(new SpdyHeaderBlock());
(*late_headers)[spdy_util_.GetStatusKey()] = "200";
- (*late_headers)[spdy_util_.GetVersionKey()] = "HTTP/1.1";
+ if (spdy_util_.spdy_version() < SPDY4) {
+ // SPDY4/HTTP2 eliminates use of the :version header.
+ (*late_headers)[spdy_util_.GetVersionKey()] = "HTTP/1.1";
+ }
scoped_ptr<SpdyFrame> stream2_headers2(
spdy_util_.ConstructSpdyControlFrame(late_headers.Pass(),
false,
@@ -5338,22 +5488,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushWithTwoHeaderFrames) {
EXPECT_TRUE(response2.headers.get() != NULL);
EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine());
- // Verify we got all the headers
- if (spdy_util_.spdy_version() < SPDY3) {
- EXPECT_TRUE(response2.headers->HasHeaderValue(
- "url",
- "http://www.google.com/foo.dat"));
- } else {
- EXPECT_TRUE(response2.headers->HasHeaderValue(
- "scheme", "http"));
- EXPECT_TRUE(response2.headers->HasHeaderValue(
- "host", "www.google.com"));
- EXPECT_TRUE(response2.headers->HasHeaderValue(
- "path", "/foo.dat"));
- }
+ // Verify we got all the headers from all header blocks.
+ if (spdy_util_.spdy_version() < SPDY4)
+ EXPECT_TRUE(response2.headers->HasHeaderValue("alpha", "beta"));
EXPECT_TRUE(response2.headers->HasHeaderValue("hello", "bye"));
EXPECT_TRUE(response2.headers->HasHeaderValue("status", "200"));
- EXPECT_TRUE(response2.headers->HasHeaderValue("version", "HTTP/1.1"));
// Read the final EOF (which will close the session)
data.RunFor(1);
@@ -5377,13 +5516,7 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushWithNoStatusHeaderFrames) {
spdy_util_.AddUrlToHeaderBlock(
"http://www.google.com/foo.dat", initial_headers.get());
scoped_ptr<SpdyFrame> stream2_syn(
- spdy_util_.ConstructSpdyControlFrame(initial_headers.Pass(),
- false,
- 2,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- 1));
+ spdy_util_.ConstructInitialSpdyPushFrame(initial_headers.Pass(), 2, 1));
scoped_ptr<SpdyHeaderBlock> middle_headers(new SpdyHeaderBlock());
(*middle_headers)["hello"] = "bye";
@@ -5951,16 +6084,15 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) {
if (GetParam().protocol < kProtoSPDY3)
return;
- // Set the data in the body frame large enough to trigger sending a
- // WINDOW_UPDATE by the stream.
- const std::string body_data(kSpdyStreamInitialWindowSize / 2 + 1, 'x');
+ // Amount of body required to trigger a sent window update.
+ const size_t kTargetSize = kSpdyStreamInitialWindowSize / 2 + 1;
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
scoped_ptr<SpdyFrame> session_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(0, body_data.size()));
+ spdy_util_.ConstructSpdyWindowUpdate(0, kTargetSize));
scoped_ptr<SpdyFrame> window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, body_data.size()));
+ spdy_util_.ConstructSpdyWindowUpdate(1, kTargetSize));
std::vector<MockWrite> writes;
writes.push_back(CreateMockWrite(*req));
@@ -5968,23 +6100,23 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) {
writes.push_back(CreateMockWrite(*session_window_update));
writes.push_back(CreateMockWrite(*window_update));
+ std::vector<MockRead> reads;
scoped_ptr<SpdyFrame> resp(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
- scoped_ptr<SpdyFrame> body_no_fin(
- spdy_util_.ConstructSpdyBodyFrame(
- 1, body_data.data(), body_data.size(), false));
- scoped_ptr<SpdyFrame> body_fin(
- spdy_util_.ConstructSpdyBodyFrame(1, NULL, 0, true));
- MockRead reads[] = {
- CreateMockRead(*resp),
- CreateMockRead(*body_no_fin),
- MockRead(ASYNC, ERR_IO_PENDING, 0), // Force a pause
- CreateMockRead(*body_fin),
- MockRead(ASYNC, ERR_IO_PENDING, 0), // Force a pause
- MockRead(ASYNC, 0, 0) // EOF
- };
+ reads.push_back(CreateMockRead(*resp));
+
+ ScopedVector<SpdyFrame> body_frames;
+ const std::string body_data(4096, 'x');
+ for (size_t remaining = kTargetSize; remaining != 0;) {
+ size_t frame_size = std::min(remaining, body_data.size());
+ body_frames.push_back(spdy_util_.ConstructSpdyBodyFrame(
+ 1, body_data.data(), frame_size, false));
+ reads.push_back(CreateMockRead(*body_frames.back()));
+ remaining -= frame_size;
+ }
+ reads.push_back(MockRead(ASYNC, ERR_IO_PENDING, 0)); // Yield.
- DelayedSocketData data(1, reads, arraysize(reads),
+ DelayedSocketData data(1, vector_as_array(&reads), reads.size(),
vector_as_array(&writes), writes.size());
NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
@@ -6005,9 +6137,9 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) {
ASSERT_TRUE(stream != NULL);
ASSERT_TRUE(stream->stream() != NULL);
- EXPECT_EQ(
- static_cast<int>(kSpdyStreamInitialWindowSize - body_data.size()),
- stream->stream()->recv_window_size());
+ // All data has been read, but not consumed. The window reflects this.
+ EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize - kTargetSize),
+ stream->stream()->recv_window_size());
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
@@ -6017,22 +6149,15 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) {
// Issue a read which will cause a WINDOW_UPDATE to be sent and window
// size increased to default.
- scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(body_data.size()));
- rv = trans->Read(buf.get(), body_data.size(), CompletionCallback());
- EXPECT_EQ(static_cast<int>(body_data.size()), rv);
- std::string content(buf->data(), buf->data() + body_data.size());
- EXPECT_EQ(body_data, content);
-
- // Schedule the reading of empty data frame with FIN
- data.CompleteRead();
-
- // Force write of WINDOW_UPDATE which was scheduled during the above
- // read.
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kTargetSize));
+ EXPECT_EQ(static_cast<int>(kTargetSize),
+ trans->Read(buf.get(), kTargetSize, CompletionCallback()));
+ EXPECT_EQ(static_cast<int>(kSpdyStreamInitialWindowSize),
+ stream->stream()->recv_window_size());
+ EXPECT_THAT(base::StringPiece(buf->data(), kTargetSize), Each(Eq('x')));
+
+ // Allow scheduled WINDOW_UPDATE frames to write.
base::RunLoop().RunUntilIdle();
-
- // Read EOF.
- data.CompleteRead();
-
helper.VerifyDataConsumed();
}
@@ -6291,6 +6416,9 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) {
if (GetParam().protocol >= kProtoSPDY31)
reads.push_back(CreateMockRead(*session_window_update, i++));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
+ writes.push_back(CreateMockWrite(*settings_ack, i++));
+
writes.push_back(CreateMockWrite(*body3, i++));
scoped_ptr<SpdyFrame> reply(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
@@ -6342,7 +6470,7 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) {
// since we're send-stalled.
EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control());
- data.RunFor(6); // Read in SETTINGS frame to unstall.
+ data.RunFor(7); // Read in SETTINGS frame to unstall.
rv = callback.WaitForResult();
helper.VerifyDataConsumed();
// If stream is NULL, that means it was unstalled and closed.
@@ -6415,6 +6543,9 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) {
reads.push_back(CreateMockRead(*window_update_init_size, i++));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
+ writes.push_back(CreateMockWrite(*settings_ack, i++));
+
writes.push_back(CreateMockWrite(*body3, i++));
scoped_ptr<SpdyFrame> reply(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
@@ -6467,9 +6598,115 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) {
EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control());
// Read in WINDOW_UPDATE or SETTINGS frame.
- data.RunFor((GetParam().protocol >= kProtoSPDY31) ? 8 : 7);
+ data.RunFor((GetParam().protocol >= kProtoSPDY31) ? 9 : 8);
rv = callback.WaitForResult();
helper.VerifyDataConsumed();
}
+class SpdyNetworkTransactionNoTLSUsageCheckTest
+ : public SpdyNetworkTransactionTest {
+ protected:
+ void RunNoTLSUsageCheckTest(scoped_ptr<SSLSocketDataProvider> ssl_provider) {
+ // Construct the request.
+ scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyGet(
+ "https://www.google.com/", false, 1, LOWEST));
+ MockWrite writes[] = {CreateMockWrite(*req)};
+
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ MockRead reads[] = {
+ CreateMockRead(*resp), CreateMockRead(*body),
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+
+ DelayedSocketData data(
+ 1, reads, arraysize(reads), writes, arraysize(writes));
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ NormalSpdyTransactionHelper helper(
+ request, DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL);
+ helper.RunToCompletionWithSSLData(&data, ssl_provider.Pass());
+ TransactionHelperResult out = helper.output();
+ EXPECT_EQ(OK, out.rv);
+ EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+ EXPECT_EQ("hello!", out.response_data);
+ }
+};
+
+//-----------------------------------------------------------------------------
+// All tests are run with three different connection types: SPDY after NPN
+// negotiation, SPDY without SSL, and SPDY with SSL.
+//
+// TODO(akalin): Use ::testing::Combine() when we are able to use
+// <tr1/tuple>.
+INSTANTIATE_TEST_CASE_P(
+ Spdy,
+ SpdyNetworkTransactionNoTLSUsageCheckTest,
+ ::testing::Values(SpdyNetworkTransactionTestParams(kProtoDeprecatedSPDY2,
+ SPDYNPN),
+ SpdyNetworkTransactionTestParams(kProtoSPDY3, SPDYNPN),
+ SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYNPN)));
+
+TEST_P(SpdyNetworkTransactionNoTLSUsageCheckTest, TLSVersionTooOld) {
+ scoped_ptr<SSLSocketDataProvider> ssl_provider(
+ new SSLSocketDataProvider(ASYNC, OK));
+ SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3,
+ &ssl_provider->connection_status);
+
+ RunNoTLSUsageCheckTest(ssl_provider.Pass());
+}
+
+TEST_P(SpdyNetworkTransactionNoTLSUsageCheckTest, TLSCipherSuiteSucky) {
+ scoped_ptr<SSLSocketDataProvider> ssl_provider(
+ new SSLSocketDataProvider(ASYNC, OK));
+ // Set to TLS_RSA_WITH_NULL_MD5
+ SSLConnectionStatusSetCipherSuite(0x1, &ssl_provider->connection_status);
+
+ RunNoTLSUsageCheckTest(ssl_provider.Pass());
+}
+
+class SpdyNetworkTransactionTLSUsageCheckTest
+ : public SpdyNetworkTransactionTest {
+ protected:
+ void RunTLSUsageCheckTest(scoped_ptr<SSLSocketDataProvider> ssl_provider) {
+ scoped_ptr<SpdyFrame> goaway(
+ spdy_util_.ConstructSpdyGoAway(0, GOAWAY_INADEQUATE_SECURITY, ""));
+ MockWrite writes[] = {CreateMockWrite(*goaway)};
+
+ DelayedSocketData data(1, NULL, 0, writes, arraysize(writes));
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ NormalSpdyTransactionHelper helper(
+ request, DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL);
+ helper.RunToCompletionWithSSLData(&data, ssl_provider.Pass());
+ TransactionHelperResult out = helper.output();
+ EXPECT_EQ(ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, out.rv);
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ Spdy,
+ SpdyNetworkTransactionTLSUsageCheckTest,
+ ::testing::Values(SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNPN)));
+
+TEST_P(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) {
+ scoped_ptr<SSLSocketDataProvider> ssl_provider(
+ new SSLSocketDataProvider(ASYNC, OK));
+ SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3,
+ &ssl_provider->connection_status);
+
+ RunTLSUsageCheckTest(ssl_provider.Pass());
+}
+
+TEST_P(SpdyNetworkTransactionTLSUsageCheckTest, TLSCipherSuiteSucky) {
+ scoped_ptr<SSLSocketDataProvider> ssl_provider(
+ new SSLSocketDataProvider(ASYNC, OK));
+ // Set to TLS_RSA_WITH_NULL_MD5
+ SSLConnectionStatusSetCipherSuite(0x1, &ssl_provider->connection_status);
+
+ RunTLSUsageCheckTest(ssl_provider.Pass());
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_pinnable_buffer_piece.cc b/chromium/net/spdy/spdy_pinnable_buffer_piece.cc
new file mode 100644
index 00000000000..01f8d1db143
--- /dev/null
+++ b/chromium/net/spdy/spdy_pinnable_buffer_piece.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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/spdy/spdy_pinnable_buffer_piece.h"
+
+namespace net {
+
+SpdyPinnableBufferPiece::SpdyPinnableBufferPiece()
+ : buffer_(0), length_(0) {}
+
+SpdyPinnableBufferPiece::~SpdyPinnableBufferPiece() {}
+
+void SpdyPinnableBufferPiece::Pin() {
+ if (!storage_ && buffer_ != NULL && length_ != 0) {
+ storage_.reset(new char[length_]);
+ std::copy(buffer_, buffer_ + length_, storage_.get());
+ buffer_ = storage_.get();
+ }
+}
+
+void SpdyPinnableBufferPiece::Swap(SpdyPinnableBufferPiece* other) {
+ size_t length = length_;
+ length_ = other->length_;
+ other->length_ = length;
+
+ const char* buffer = buffer_;
+ buffer_ = other->buffer_;
+ other->buffer_ = buffer;
+
+ storage_.swap(other->storage_);
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_pinnable_buffer_piece.h b/chromium/net/spdy/spdy_pinnable_buffer_piece.h
new file mode 100644
index 00000000000..6ffc2639f71
--- /dev/null
+++ b/chromium/net/spdy/spdy_pinnable_buffer_piece.h
@@ -0,0 +1,60 @@
+// Copyright 2014 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_SPDY_SPDY_PINNABLE_BUFFER_PIECE_H_
+#define NET_SPDY_SPDY_PINNABLE_BUFFER_PIECE_H_
+
+#include <memory>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class SpdyPrefixedBufferReader;
+
+// Helper class of SpdyPrefixedBufferReader.
+// Represents a piece of consumed buffer which may (or may not) own its
+// underlying storage. Users may "pin" the buffer at a later time to ensure
+// a SpdyPinnableBufferPiece owns and retains storage of the buffer.
+struct NET_EXPORT_PRIVATE SpdyPinnableBufferPiece {
+ public:
+ SpdyPinnableBufferPiece();
+ ~SpdyPinnableBufferPiece();
+
+ const char * buffer() const {
+ return buffer_;
+ }
+
+ size_t length() const {
+ return length_;
+ }
+
+ operator base::StringPiece() const {
+ return base::StringPiece(buffer_, length_);
+ }
+
+ // Allocates and copies the buffer to internal storage.
+ void Pin();
+
+ bool IsPinned() const {
+ return storage_.get() != NULL;
+ }
+
+ // Swaps buffers, including internal storage, with |other|.
+ void Swap(SpdyPinnableBufferPiece* other);
+
+ private:
+ friend class SpdyPrefixedBufferReader;
+
+ const char * buffer_;
+ size_t length_;
+ // Null iff |buffer_| isn't pinned.
+ scoped_ptr<char[]> storage_;
+};
+
+} // namespace net
+
+#endif // NET_SPDY_SPDY_PINNABLE_BUFFER_PIECE_H_
diff --git a/chromium/net/spdy/spdy_pinnable_buffer_piece_test.cc b/chromium/net/spdy/spdy_pinnable_buffer_piece_test.cc
new file mode 100644
index 00000000000..456e4a86def
--- /dev/null
+++ b/chromium/net/spdy/spdy_pinnable_buffer_piece_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 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/spdy/spdy_pinnable_buffer_piece.h"
+
+#include "net/spdy/spdy_prefixed_buffer_reader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace test {
+
+using base::StringPiece;
+
+class SpdyPinnableBufferPieceTest : public ::testing::Test {
+ protected:
+ SpdyPrefixedBufferReader Build(std::string prefix, std::string suffix) {
+ prefix_ = prefix;
+ suffix_ = suffix;
+ return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(),
+ suffix_.data(), suffix_.length());
+ }
+ std::string prefix_, suffix_;
+};
+
+TEST_F(SpdyPinnableBufferPieceTest, Pin) {
+ SpdyPrefixedBufferReader reader = Build("foobar", "");
+ SpdyPinnableBufferPiece piece;
+ EXPECT_TRUE(reader.ReadN(6, &piece));
+
+ // Piece points to underlying prefix storage.
+ EXPECT_EQ(StringPiece("foobar"), piece);
+ EXPECT_FALSE(piece.IsPinned());
+ EXPECT_EQ(prefix_.data(), piece.buffer());
+
+ piece.Pin();
+
+ // Piece now points to allocated storage.
+ EXPECT_EQ(StringPiece("foobar"), piece);
+ EXPECT_TRUE(piece.IsPinned());
+ EXPECT_NE(prefix_.data(), piece.buffer());
+
+ // Pinning again has no effect.
+ const char* buffer = piece.buffer();
+ piece.Pin();
+ EXPECT_EQ(buffer, piece.buffer());
+}
+
+TEST_F(SpdyPinnableBufferPieceTest, Swap) {
+ SpdyPrefixedBufferReader reader = Build("foobar", "");
+ SpdyPinnableBufferPiece piece1, piece2;
+ EXPECT_TRUE(reader.ReadN(4, &piece1));
+ EXPECT_TRUE(reader.ReadN(2, &piece2));
+
+ piece1.Pin();
+
+ EXPECT_EQ(StringPiece("foob"), piece1);
+ EXPECT_TRUE(piece1.IsPinned());
+ EXPECT_EQ(StringPiece("ar"), piece2);
+ EXPECT_FALSE(piece2.IsPinned());
+
+ piece1.Swap(&piece2);
+
+ EXPECT_EQ(StringPiece("ar"), piece1);
+ EXPECT_FALSE(piece1.IsPinned());
+ EXPECT_EQ(StringPiece("foob"), piece2);
+ EXPECT_TRUE(piece2.IsPinned());
+
+ SpdyPinnableBufferPiece empty;
+ piece2.Swap(&empty);
+
+ EXPECT_EQ(StringPiece(""), piece2);
+ EXPECT_FALSE(piece2.IsPinned());
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_prefixed_buffer_reader.cc b/chromium/net/spdy/spdy_prefixed_buffer_reader.cc
new file mode 100644
index 00000000000..8a4638c3140
--- /dev/null
+++ b/chromium/net/spdy/spdy_prefixed_buffer_reader.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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/spdy/spdy_prefixed_buffer_reader.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+SpdyPrefixedBufferReader::SpdyPrefixedBufferReader(
+ const char* prefix,
+ size_t prefix_length,
+ const char* suffix,
+ size_t suffix_length)
+ : prefix_(prefix),
+ suffix_(suffix),
+ prefix_length_(prefix_length),
+ suffix_length_(suffix_length) {}
+
+size_t SpdyPrefixedBufferReader::Available() {
+ return prefix_length_ + suffix_length_;
+}
+
+bool SpdyPrefixedBufferReader::ReadN(size_t count, char* out) {
+ if (Available() < count)
+ return false;
+
+ if (prefix_length_ >= count) {
+ // Read is fully satisfied by the prefix.
+ std::copy(prefix_, prefix_ + count, out);
+ prefix_ += count;
+ prefix_length_ -= count;
+ return true;
+ } else if (prefix_length_ != 0) {
+ // Read is partially satisfied by the prefix.
+ out = std::copy(prefix_, prefix_ + prefix_length_, out);
+ count -= prefix_length_;
+ prefix_length_ = 0;
+ // Fallthrough to suffix read.
+ }
+ DCHECK(suffix_length_ >= count);
+ // Read is satisfied by the suffix.
+ std::copy(suffix_, suffix_ + count, out);
+ suffix_ += count;
+ suffix_length_ -= count;
+ return true;
+}
+
+bool SpdyPrefixedBufferReader::ReadN(size_t count,
+ SpdyPinnableBufferPiece* out) {
+ if (Available() < count)
+ return false;
+
+ out->storage_.reset();
+ out->length_ = count;
+
+ if (prefix_length_ >= count) {
+ // Read is fully satisfied by the prefix.
+ out->buffer_ = prefix_;
+ prefix_ += count;
+ prefix_length_ -= count;
+ return true;
+ } else if (prefix_length_ != 0) {
+ // Read is only partially satisfied by the prefix. We need to allocate
+ // contiguous storage as the read spans the prefix & suffix.
+ out->storage_.reset(new char[count]);
+ out->buffer_ = out->storage_.get();
+ ReadN(count, out->storage_.get());
+ return true;
+ } else {
+ DCHECK(suffix_length_ >= count);
+ // Read is fully satisfied by the suffix.
+ out->buffer_ = suffix_;
+ suffix_ += count;
+ suffix_length_ -= count;
+ return true;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_prefixed_buffer_reader.h b/chromium/net/spdy/spdy_prefixed_buffer_reader.h
new file mode 100644
index 00000000000..6d277179712
--- /dev/null
+++ b/chromium/net/spdy/spdy_prefixed_buffer_reader.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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_SPDY_SPDY_PREFIXED_BUFFER_READER_H_
+#define NET_SPDY_SPDY_PREFIXED_BUFFER_READER_H_
+
+#include "net/base/net_export.h"
+#include "net/spdy/spdy_pinnable_buffer_piece.h"
+
+namespace net {
+
+// Reader class which simplifies reading contiguously from
+// from a disjoint buffer prefix & suffix.
+class NET_EXPORT_PRIVATE SpdyPrefixedBufferReader {
+ public:
+ SpdyPrefixedBufferReader(const char* prefix, size_t prefix_length,
+ const char* suffix, size_t suffix_length);
+
+ // Returns number of bytes available to be read.
+ size_t Available();
+
+ // Reads |count| bytes, copying into |*out|. Returns true on success,
+ // false if not enough bytes were available.
+ bool ReadN(size_t count, char* out);
+
+ // Reads |count| bytes, returned in |*out|. Returns true on success,
+ // false if not enough bytes were available.
+ bool ReadN(size_t count, SpdyPinnableBufferPiece* out);
+
+ private:
+ const char* prefix_;
+ const char* suffix_;
+
+ size_t prefix_length_;
+ size_t suffix_length_;
+};
+
+} // namespace net
+
+#endif // NET_SPDY_SPDY_PREFIXED_BUFFER_READER_H_
diff --git a/chromium/net/spdy/spdy_prefixed_buffer_reader_test.cc b/chromium/net/spdy/spdy_prefixed_buffer_reader_test.cc
new file mode 100644
index 00000000000..e3a6a6552dc
--- /dev/null
+++ b/chromium/net/spdy/spdy_prefixed_buffer_reader_test.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 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/spdy/spdy_prefixed_buffer_reader.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace test {
+
+using base::StringPiece;
+using testing::ElementsAreArray;
+
+class SpdyPrefixedBufferReaderTest : public ::testing::Test {
+ protected:
+ SpdyPrefixedBufferReader Build(std::string prefix, std::string suffix) {
+ prefix_ = prefix;
+ suffix_ = suffix;
+ return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(),
+ suffix_.data(), suffix_.length());
+ }
+ std::string prefix_, suffix_;
+};
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadRawFromPrefix) {
+ SpdyPrefixedBufferReader reader = Build("foobar", "");
+ EXPECT_EQ(6u, reader.Available());
+
+ char buffer[] = "123456";
+ EXPECT_FALSE(reader.ReadN(10, buffer)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("foobar"));
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadPieceFromPrefix) {
+ SpdyPrefixedBufferReader reader = Build("foobar", "");
+ EXPECT_EQ(6u, reader.Available());
+
+ SpdyPinnableBufferPiece piece;
+ EXPECT_FALSE(reader.ReadN(10, &piece)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, &piece));
+ EXPECT_FALSE(piece.IsPinned());
+ EXPECT_EQ(StringPiece("foobar"), piece);
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadRawFromSuffix) {
+ SpdyPrefixedBufferReader reader = Build("", "foobar");
+ EXPECT_EQ(6u, reader.Available());
+
+ char buffer[] = "123456";
+ EXPECT_FALSE(reader.ReadN(10, buffer)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("foobar"));
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadPieceFromSuffix) {
+ SpdyPrefixedBufferReader reader = Build("", "foobar");
+ EXPECT_EQ(6u, reader.Available());
+
+ SpdyPinnableBufferPiece piece;
+ EXPECT_FALSE(reader.ReadN(10, &piece)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, &piece));
+ EXPECT_FALSE(piece.IsPinned());
+ EXPECT_EQ(StringPiece("foobar"), piece);
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadRawSpanning) {
+ SpdyPrefixedBufferReader reader = Build("foob", "ar");
+ EXPECT_EQ(6u, reader.Available());
+
+ char buffer[] = "123456";
+ EXPECT_FALSE(reader.ReadN(10, buffer)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("foobar"));
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadPieceSpanning) {
+ SpdyPrefixedBufferReader reader = Build("foob", "ar");
+ EXPECT_EQ(6u, reader.Available());
+
+ SpdyPinnableBufferPiece piece;
+ EXPECT_FALSE(reader.ReadN(10, &piece)); // Not enough buffer.
+ EXPECT_TRUE(reader.ReadN(6, &piece));
+ EXPECT_TRUE(piece.IsPinned());
+ EXPECT_EQ(StringPiece("foobar"), piece);
+ EXPECT_EQ(0u, reader.Available());
+}
+
+TEST_F(SpdyPrefixedBufferReaderTest, ReadMixed) {
+ SpdyPrefixedBufferReader reader = Build("abcdef", "hijkl");
+ EXPECT_EQ(11u, reader.Available());
+
+ char buffer[] = "1234";
+ SpdyPinnableBufferPiece piece;
+
+ EXPECT_TRUE(reader.ReadN(3, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("abc4"));
+ EXPECT_EQ(8u, reader.Available());
+
+ EXPECT_TRUE(reader.ReadN(2, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("dec4"));
+ EXPECT_EQ(6u, reader.Available());
+
+ EXPECT_TRUE(reader.ReadN(3, &piece));
+ EXPECT_EQ(StringPiece("fhi"), piece);
+ EXPECT_TRUE(piece.IsPinned());
+ EXPECT_EQ(3u, reader.Available());
+
+ EXPECT_TRUE(reader.ReadN(2, &piece));
+ EXPECT_EQ(StringPiece("jk"), piece);
+ EXPECT_FALSE(piece.IsPinned());
+ EXPECT_EQ(1u, reader.Available());
+
+ EXPECT_TRUE(reader.ReadN(1, buffer));
+ EXPECT_THAT(buffer, ElementsAreArray("lec4"));
+ EXPECT_EQ(0u, reader.Available());
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/chromium/net/spdy/spdy_priority_forest.h b/chromium/net/spdy/spdy_priority_forest.h
index 4f00f566caf..8624bbfe8ee 100644
--- a/chromium/net/spdy/spdy_priority_forest.h
+++ b/chromium/net/spdy/spdy_priority_forest.h
@@ -7,6 +7,7 @@
#include <map>
#include <set>
+#include <vector>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
diff --git a/chromium/net/spdy/spdy_protocol.cc b/chromium/net/spdy/spdy_protocol.cc
index 39031ebd9f5..37c1a1b7d13 100644
--- a/chromium/net/spdy/spdy_protocol.cc
+++ b/chromium/net/spdy/spdy_protocol.cc
@@ -12,15 +12,724 @@ SpdyFrameWithNameValueBlockIR::SpdyFrameWithNameValueBlockIR(
SpdyFrameWithNameValueBlockIR::~SpdyFrameWithNameValueBlockIR() {}
SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, const base::StringPiece& data)
- : SpdyFrameWithFinIR(stream_id) {
+ : SpdyFrameWithFinIR(stream_id),
+ pad_low_(false),
+ pad_high_(false),
+ padding_payload_len_(0) {
SetDataDeep(data);
}
SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id)
- : SpdyFrameWithFinIR(stream_id) {}
+ : SpdyFrameWithFinIR(stream_id),
+ pad_low_(false),
+ pad_high_(false),
+ padding_payload_len_(0) {}
SpdyDataIR::~SpdyDataIR() {}
+bool SpdyConstants::IsValidFrameType(SpdyMajorVersion version,
+ int frame_type_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ // SYN_STREAM is the first valid frame.
+ if (frame_type_field < SerializeFrameType(version, SYN_STREAM)) {
+ return false;
+ }
+
+ // WINDOW_UPDATE is the last valid frame.
+ if (frame_type_field > SerializeFrameType(version, WINDOW_UPDATE)) {
+ return false;
+ }
+
+ // The valid range is non-contiguous.
+ if (frame_type_field == NOOP) {
+ return false;
+ }
+
+ return true;
+ case SPDY4:
+ case SPDY5:
+ // DATA is the first valid frame.
+ if (frame_type_field < SerializeFrameType(version, DATA)) {
+ return false;
+ }
+
+ // BLOCKED is the last valid frame.
+ if (frame_type_field > SerializeFrameType(version, BLOCKED)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return false;
+}
+
+SpdyFrameType SpdyConstants::ParseFrameType(SpdyMajorVersion version,
+ int frame_type_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (frame_type_field) {
+ case 1:
+ return SYN_STREAM;
+ case 2:
+ return SYN_REPLY;
+ case 3:
+ return RST_STREAM;
+ case 4:
+ return SETTINGS;
+ case 6:
+ return PING;
+ case 7:
+ return GOAWAY;
+ case 8:
+ return HEADERS;
+ case 9:
+ return WINDOW_UPDATE;
+ }
+ break;
+ case SPDY4:
+ case SPDY5:
+ switch (frame_type_field) {
+ case 0:
+ return DATA;
+ case 1:
+ return HEADERS;
+ case 2:
+ return PRIORITY;
+ case 3:
+ return RST_STREAM;
+ case 4:
+ return SETTINGS;
+ case 5:
+ return PUSH_PROMISE;
+ case 6:
+ return PING;
+ case 7:
+ return GOAWAY;
+ case 8:
+ return WINDOW_UPDATE;
+ case 9:
+ return CONTINUATION;
+ case 10:
+ return ALTSVC;
+ case 11:
+ return BLOCKED;
+ }
+ break;
+ }
+
+ LOG(DFATAL) << "Unhandled frame type " << frame_type_field;
+ return DATA;
+}
+
+int SpdyConstants::SerializeFrameType(SpdyMajorVersion version,
+ SpdyFrameType frame_type) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (frame_type) {
+ case SYN_STREAM:
+ return 1;
+ case SYN_REPLY:
+ return 2;
+ case RST_STREAM:
+ return 3;
+ case SETTINGS:
+ return 4;
+ case PING:
+ return 6;
+ case GOAWAY:
+ return 7;
+ case HEADERS:
+ return 8;
+ case WINDOW_UPDATE:
+ return 9;
+ default:
+ LOG(DFATAL) << "Serializing unhandled frame type " << frame_type;
+ return -1;
+ }
+ case SPDY4:
+ case SPDY5:
+ switch (frame_type) {
+ case DATA:
+ return 0;
+ case HEADERS:
+ return 1;
+ case PRIORITY:
+ return 2;
+ case RST_STREAM:
+ return 3;
+ case SETTINGS:
+ return 4;
+ case PUSH_PROMISE:
+ return 5;
+ case PING:
+ return 6;
+ case GOAWAY:
+ return 7;
+ case WINDOW_UPDATE:
+ return 8;
+ case CONTINUATION:
+ return 9;
+ case ALTSVC:
+ return 10;
+ case BLOCKED:
+ return 11;
+ default:
+ LOG(DFATAL) << "Serializing unhandled frame type " << frame_type;
+ return -1;
+ }
+ }
+
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return -1;
+}
+
+bool SpdyConstants::IsValidSettingId(SpdyMajorVersion version,
+ int setting_id_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ // UPLOAD_BANDWIDTH is the first valid setting id.
+ if (setting_id_field <
+ SerializeSettingId(version, SETTINGS_UPLOAD_BANDWIDTH)) {
+ return false;
+ }
+
+ // INITIAL_WINDOW_SIZE is the last valid setting id.
+ if (setting_id_field >
+ SerializeSettingId(version, SETTINGS_INITIAL_WINDOW_SIZE)) {
+ return false;
+ }
+
+ return true;
+ case SPDY4:
+ case SPDY5:
+ // HEADER_TABLE_SIZE is the first valid setting id.
+ if (setting_id_field <
+ SerializeSettingId(version, SETTINGS_HEADER_TABLE_SIZE)) {
+ return false;
+ }
+
+ // COMPRESS_DATA is the last valid setting id.
+ if (setting_id_field >
+ SerializeSettingId(version, SETTINGS_COMPRESS_DATA)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return false;
+}
+
+SpdySettingsIds SpdyConstants::ParseSettingId(SpdyMajorVersion version,
+ int setting_id_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (setting_id_field) {
+ case 1:
+ return SETTINGS_UPLOAD_BANDWIDTH;
+ case 2:
+ return SETTINGS_DOWNLOAD_BANDWIDTH;
+ case 3:
+ return SETTINGS_ROUND_TRIP_TIME;
+ case 4:
+ return SETTINGS_MAX_CONCURRENT_STREAMS;
+ case 5:
+ return SETTINGS_CURRENT_CWND;
+ case 6:
+ return SETTINGS_DOWNLOAD_RETRANS_RATE;
+ case 7:
+ return SETTINGS_INITIAL_WINDOW_SIZE;
+ }
+ break;
+ case SPDY4:
+ case SPDY5:
+ switch (setting_id_field) {
+ case 1:
+ return SETTINGS_HEADER_TABLE_SIZE;
+ case 2:
+ return SETTINGS_ENABLE_PUSH;
+ case 3:
+ return SETTINGS_MAX_CONCURRENT_STREAMS;
+ case 4:
+ return SETTINGS_INITIAL_WINDOW_SIZE;
+ case 5:
+ return SETTINGS_COMPRESS_DATA;
+ }
+ break;
+ }
+
+ LOG(DFATAL) << "Unhandled setting ID " << setting_id_field;
+ return SETTINGS_UPLOAD_BANDWIDTH;
+}
+
+int SpdyConstants::SerializeSettingId(SpdyMajorVersion version,
+ SpdySettingsIds id) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (id) {
+ case SETTINGS_UPLOAD_BANDWIDTH:
+ return 1;
+ case SETTINGS_DOWNLOAD_BANDWIDTH:
+ return 2;
+ case SETTINGS_ROUND_TRIP_TIME:
+ return 3;
+ case SETTINGS_MAX_CONCURRENT_STREAMS:
+ return 4;
+ case SETTINGS_CURRENT_CWND:
+ return 5;
+ case SETTINGS_DOWNLOAD_RETRANS_RATE:
+ return 6;
+ case SETTINGS_INITIAL_WINDOW_SIZE:
+ return 7;
+ default:
+ LOG(DFATAL) << "Serializing unhandled setting id " << id;
+ return -1;
+ }
+ case SPDY4:
+ case SPDY5:
+ switch (id) {
+ case SETTINGS_HEADER_TABLE_SIZE:
+ return 1;
+ case SETTINGS_ENABLE_PUSH:
+ return 2;
+ case SETTINGS_MAX_CONCURRENT_STREAMS:
+ return 3;
+ case SETTINGS_INITIAL_WINDOW_SIZE:
+ return 4;
+ case SETTINGS_COMPRESS_DATA:
+ return 5;
+ default:
+ LOG(DFATAL) << "Serializing unhandled setting id " << id;
+ return -1;
+ }
+ }
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return -1;
+}
+
+bool SpdyConstants::IsValidRstStreamStatus(SpdyMajorVersion version,
+ int rst_stream_status_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ // PROTOCOL_ERROR is the valid first status code.
+ if (rst_stream_status_field <
+ SerializeRstStreamStatus(version, RST_STREAM_PROTOCOL_ERROR)) {
+ return false;
+ }
+
+ // FRAME_TOO_LARGE is the valid last status code.
+ if (rst_stream_status_field >
+ SerializeRstStreamStatus(version, RST_STREAM_FRAME_TOO_LARGE)) {
+ return false;
+ }
+
+ return true;
+ case SPDY4:
+ case SPDY5:
+ // NO_ERROR is the first valid status code.
+ if (rst_stream_status_field <
+ SerializeRstStreamStatus(version, RST_STREAM_PROTOCOL_ERROR)) {
+ return false;
+ }
+
+ // TODO(hkhalil): Omit COMPRESSION_ERROR and SETTINGS_TIMEOUT
+ /*
+ // This works because GOAWAY and RST_STREAM share a namespace.
+ if (rst_stream_status_field ==
+ SerializeGoAwayStatus(version, GOAWAY_COMPRESSION_ERROR) ||
+ rst_stream_status_field ==
+ SerializeGoAwayStatus(version, GOAWAY_SETTINGS_TIMEOUT)) {
+ return false;
+ }
+ */
+
+ // ENHANCE_YOUR_CALM is the last valid status code.
+ if (rst_stream_status_field >
+ SerializeRstStreamStatus(version, RST_STREAM_ENHANCE_YOUR_CALM)) {
+ return false;
+ }
+
+ return true;
+ }
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return false;
+}
+
+SpdyRstStreamStatus SpdyConstants::ParseRstStreamStatus(
+ SpdyMajorVersion version,
+ int rst_stream_status_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (rst_stream_status_field) {
+ case 1:
+ return RST_STREAM_PROTOCOL_ERROR;
+ case 2:
+ return RST_STREAM_INVALID_STREAM;
+ case 3:
+ return RST_STREAM_REFUSED_STREAM;
+ case 4:
+ return RST_STREAM_UNSUPPORTED_VERSION;
+ case 5:
+ return RST_STREAM_CANCEL;
+ case 6:
+ return RST_STREAM_INTERNAL_ERROR;
+ case 7:
+ return RST_STREAM_FLOW_CONTROL_ERROR;
+ case 8:
+ return RST_STREAM_STREAM_IN_USE;
+ case 9:
+ return RST_STREAM_STREAM_ALREADY_CLOSED;
+ case 10:
+ return RST_STREAM_INVALID_CREDENTIALS;
+ case 11:
+ return RST_STREAM_FRAME_TOO_LARGE;
+ }
+ break;
+ case SPDY4:
+ case SPDY5:
+ switch (rst_stream_status_field) {
+ case 1:
+ return RST_STREAM_PROTOCOL_ERROR;
+ case 2:
+ return RST_STREAM_INTERNAL_ERROR;
+ case 3:
+ return RST_STREAM_FLOW_CONTROL_ERROR;
+ case 5:
+ return RST_STREAM_STREAM_CLOSED;
+ case 6:
+ return RST_STREAM_FRAME_SIZE_ERROR;
+ case 7:
+ return RST_STREAM_REFUSED_STREAM;
+ case 8:
+ return RST_STREAM_CANCEL;
+ case 10:
+ return RST_STREAM_CONNECT_ERROR;
+ case 11:
+ return RST_STREAM_ENHANCE_YOUR_CALM;
+ }
+ break;
+ }
+
+ LOG(DFATAL) << "Invalid RST_STREAM status " << rst_stream_status_field;
+ return RST_STREAM_PROTOCOL_ERROR;
+}
+
+int SpdyConstants::SerializeRstStreamStatus(
+ SpdyMajorVersion version,
+ SpdyRstStreamStatus rst_stream_status) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (rst_stream_status) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ return 1;
+ case RST_STREAM_INVALID_STREAM:
+ return 2;
+ case RST_STREAM_REFUSED_STREAM:
+ return 3;
+ case RST_STREAM_UNSUPPORTED_VERSION:
+ return 4;
+ case RST_STREAM_CANCEL:
+ return 5;
+ case RST_STREAM_INTERNAL_ERROR:
+ return 6;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ return 7;
+ case RST_STREAM_STREAM_IN_USE:
+ return 8;
+ case RST_STREAM_STREAM_ALREADY_CLOSED:
+ return 9;
+ case RST_STREAM_INVALID_CREDENTIALS:
+ return 10;
+ case RST_STREAM_FRAME_TOO_LARGE:
+ return 11;
+ default:
+ LOG(DFATAL) << "Unhandled RST_STREAM status "
+ << rst_stream_status;
+ return -1;
+ }
+ case SPDY4:
+ case SPDY5:
+ switch (rst_stream_status) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ return 1;
+ case RST_STREAM_INTERNAL_ERROR:
+ return 2;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ return 3;
+ case RST_STREAM_STREAM_CLOSED:
+ return 5;
+ case RST_STREAM_FRAME_SIZE_ERROR:
+ return 6;
+ case RST_STREAM_REFUSED_STREAM:
+ return 7;
+ case RST_STREAM_CANCEL:
+ return 8;
+ case RST_STREAM_CONNECT_ERROR:
+ return 10;
+ case RST_STREAM_ENHANCE_YOUR_CALM:
+ return 11;
+ default:
+ LOG(DFATAL) << "Unhandled RST_STREAM status "
+ << rst_stream_status;
+ return -1;
+ }
+ }
+ LOG(DFATAL) << "Unhandled SPDY version " << version;
+ return -1;
+}
+
+bool SpdyConstants::IsValidGoAwayStatus(SpdyMajorVersion version,
+ int goaway_status_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ // GOAWAY_OK is the first valid status.
+ if (goaway_status_field < SerializeGoAwayStatus(version, GOAWAY_OK)) {
+ return false;
+ }
+
+ // GOAWAY_INTERNAL_ERROR is the last valid status.
+ if (goaway_status_field > SerializeGoAwayStatus(version,
+ GOAWAY_INTERNAL_ERROR)) {
+ return false;
+ }
+
+ return true;
+ case SPDY4:
+ case SPDY5:
+ // GOAWAY_NO_ERROR is the first valid status.
+ if (goaway_status_field < SerializeGoAwayStatus(version,
+ GOAWAY_NO_ERROR)) {
+ return false;
+ }
+
+ // GOAWAY_INADEQUATE_SECURITY is the last valid status.
+ if (goaway_status_field >
+ SerializeGoAwayStatus(version, GOAWAY_INADEQUATE_SECURITY)) {
+ return false;
+ }
+
+ return true;
+ }
+ LOG(DFATAL) << "Unknown SpdyMajorVersion " << version;
+ return false;
+}
+
+SpdyGoAwayStatus SpdyConstants::ParseGoAwayStatus(SpdyMajorVersion version,
+ int goaway_status_field) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ switch (goaway_status_field) {
+ case 0:
+ return GOAWAY_OK;
+ case 1:
+ return GOAWAY_PROTOCOL_ERROR;
+ case 2:
+ return GOAWAY_INTERNAL_ERROR;
+ }
+ break;
+ case SPDY4:
+ case SPDY5:
+ switch (goaway_status_field) {
+ case 0:
+ return GOAWAY_NO_ERROR;
+ case 1:
+ return GOAWAY_PROTOCOL_ERROR;
+ case 2:
+ return GOAWAY_INTERNAL_ERROR;
+ case 3:
+ return GOAWAY_FLOW_CONTROL_ERROR;
+ case 4:
+ return GOAWAY_SETTINGS_TIMEOUT;
+ case 5:
+ return GOAWAY_STREAM_CLOSED;
+ case 6:
+ return GOAWAY_FRAME_SIZE_ERROR;
+ case 7:
+ return GOAWAY_REFUSED_STREAM;
+ case 8:
+ return GOAWAY_CANCEL;
+ case 9:
+ return GOAWAY_COMPRESSION_ERROR;
+ case 10:
+ return GOAWAY_CONNECT_ERROR;
+ case 11:
+ return GOAWAY_ENHANCE_YOUR_CALM;
+ case 12:
+ return GOAWAY_INADEQUATE_SECURITY;
+ }
+ break;
+ }
+
+ LOG(DFATAL) << "Unhandled GOAWAY status " << goaway_status_field;
+ return GOAWAY_PROTOCOL_ERROR;
+}
+
+SpdyMajorVersion SpdyConstants::ParseMajorVersion(int version_number) {
+ switch (version_number) {
+ case 2:
+ return SPDY2;
+ case 3:
+ return SPDY3;
+ case 4:
+ return SPDY4;
+ case 5:
+ return SPDY5;
+ default:
+ LOG(DFATAL) << "Unsupported SPDY version number: " << version_number;
+ return SPDY3;
+ }
+}
+
+int SpdyConstants::SerializeMajorVersion(SpdyMajorVersion version) {
+ switch (version) {
+ case SPDY2:
+ return 2;
+ case SPDY3:
+ return 3;
+ case SPDY4:
+ return 4;
+ case SPDY5:
+ return 5;
+ default:
+ LOG(DFATAL) << "Unsupported SPDY major version: " << version;
+ return -1;
+ }
+}
+
+std::string SpdyConstants::GetVersionString(SpdyMajorVersion version) {
+ switch (version) {
+ case SPDY2:
+ return "spdy/2";
+ case SPDY3:
+ return "spdy/3";
+ case SPDY4:
+ return "spdy/4";
+ case SPDY5:
+ return "spdy/5";
+ default:
+ LOG(DFATAL) << "Unsupported SPDY major version: " << version;
+ return "spdy/3";
+ }
+}
+
+int SpdyConstants::SerializeGoAwayStatus(SpdyMajorVersion version,
+ SpdyGoAwayStatus status) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ // TODO(jgraettinger): Merge this back to server-side.
+ switch (status) {
+ case GOAWAY_NO_ERROR:
+ return 0;
+ case GOAWAY_PROTOCOL_ERROR:
+ case GOAWAY_INTERNAL_ERROR:
+ case GOAWAY_FLOW_CONTROL_ERROR:
+ case GOAWAY_SETTINGS_TIMEOUT:
+ case GOAWAY_STREAM_CLOSED:
+ case GOAWAY_FRAME_SIZE_ERROR:
+ case GOAWAY_REFUSED_STREAM:
+ case GOAWAY_CANCEL:
+ case GOAWAY_COMPRESSION_ERROR:
+ case GOAWAY_CONNECT_ERROR:
+ case GOAWAY_ENHANCE_YOUR_CALM:
+ case GOAWAY_INADEQUATE_SECURITY:
+ return 1; // PROTOCOL_ERROR.
+ default:
+ LOG(DFATAL) << "Serializing unhandled GOAWAY status " << status;
+ return -1;
+ }
+ case SPDY4:
+ case SPDY5:
+ switch (status) {
+ case GOAWAY_NO_ERROR:
+ return 0;
+ case GOAWAY_PROTOCOL_ERROR:
+ return 1;
+ case GOAWAY_INTERNAL_ERROR:
+ return 2;
+ case GOAWAY_FLOW_CONTROL_ERROR:
+ return 3;
+ case GOAWAY_SETTINGS_TIMEOUT:
+ return 4;
+ case GOAWAY_STREAM_CLOSED:
+ return 5;
+ case GOAWAY_FRAME_SIZE_ERROR:
+ return 6;
+ case GOAWAY_REFUSED_STREAM:
+ return 7;
+ case GOAWAY_CANCEL:
+ return 8;
+ case GOAWAY_COMPRESSION_ERROR:
+ return 9;
+ case GOAWAY_CONNECT_ERROR:
+ return 10;
+ case GOAWAY_ENHANCE_YOUR_CALM:
+ return 11;
+ case GOAWAY_INADEQUATE_SECURITY:
+ return 12;
+ default:
+ LOG(DFATAL) << "Serializing unhandled GOAWAY status " << status;
+ return -1;
+ }
+ }
+ LOG(DFATAL) << "Unknown SpdyMajorVersion " << version;
+ return -1;
+}
+
+size_t SpdyConstants::GetDataFrameMinimumSize() {
+ return 8;
+}
+
+size_t SpdyConstants::GetControlFrameHeaderSize(SpdyMajorVersion version) {
+ switch (version) {
+ case SPDY2:
+ case SPDY3:
+ case SPDY4:
+ case SPDY5:
+ return 8;
+ }
+ LOG(DFATAL) << "Unhandled SPDY version.";
+ return 0;
+}
+
+size_t SpdyConstants::GetPrefixLength(SpdyFrameType type,
+ SpdyMajorVersion version) {
+ if (type != DATA) {
+ return GetControlFrameHeaderSize(version);
+ } else {
+ return GetDataFrameMinimumSize();
+ }
+}
+
+size_t SpdyConstants::GetFrameMaximumSize(SpdyMajorVersion version) {
+ if (version < SPDY4) {
+ // 24-bit length field plus eight-byte frame header.
+ return ((1<<24) - 1) + 8;
+ } else {
+ // 14-bit length field.
+ return (1<<14) - 1;
+ }
+}
+
+size_t SpdyConstants::GetSizeOfSizeField(SpdyMajorVersion version) {
+ return (version < SPDY3) ? sizeof(uint16) : sizeof(uint32);
+}
+
void SpdyDataIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitData(*this);
}
@@ -33,11 +742,23 @@ void SpdySynReplyIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitSynReply(*this);
}
+SpdyRstStreamIR::SpdyRstStreamIR(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status,
+ base::StringPiece description)
+ : SpdyFrameWithStreamIdIR(stream_id),
+ description_(description) {
+ set_status(status);
+}
+
+SpdyRstStreamIR::~SpdyRstStreamIR() {}
+
void SpdyRstStreamIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitRstStream(*this);
}
-SpdySettingsIR::SpdySettingsIR() : clear_settings_(false) {}
+SpdySettingsIR::SpdySettingsIR()
+ : clear_settings_(false),
+ is_ack_(false) {}
SpdySettingsIR::~SpdySettingsIR() {}
@@ -49,6 +770,20 @@ void SpdyPingIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitPing(*this);
}
+SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
+ SpdyGoAwayStatus status,
+ const base::StringPiece& description)
+ : description_(description) {
+ set_last_good_stream_id(last_good_stream_id);
+ set_status(status);
+}
+
+SpdyGoAwayIR::~SpdyGoAwayIR() {}
+
+const base::StringPiece& SpdyGoAwayIR::description() const {
+ return description_;
+}
+
void SpdyGoAwayIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitGoAway(*this);
}
@@ -61,16 +796,6 @@ void SpdyWindowUpdateIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitWindowUpdate(*this);
}
-SpdyCredentialIR::SpdyCredentialIR(int16 slot) {
- set_slot(slot);
-}
-
-SpdyCredentialIR::~SpdyCredentialIR() {}
-
-void SpdyCredentialIR::Visit(SpdyFrameVisitor* visitor) const {
- return visitor->VisitCredential(*this);
-}
-
void SpdyBlockedIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitBlocked(*this);
}
@@ -79,4 +804,17 @@ void SpdyPushPromiseIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitPushPromise(*this);
}
+void SpdyContinuationIR::Visit(SpdyFrameVisitor* visitor) const {
+ return visitor->VisitContinuation(*this);
+}
+
+SpdyAltSvcIR::SpdyAltSvcIR(SpdyStreamId stream_id)
+ : SpdyFrameWithStreamIdIR(stream_id),
+ max_age_(0),
+ port_(0) {}
+
+void SpdyAltSvcIR::Visit(SpdyFrameVisitor* visitor) const {
+ return visitor->VisitAltSvc(*this);
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_protocol.h b/chromium/net/spdy/spdy_protocol.h
index 3c891a5e88a..9d28b97224e 100644
--- a/chromium/net/spdy/spdy_protocol.h
+++ b/chromium/net/spdy/spdy_protocol.h
@@ -28,14 +28,15 @@ namespace net {
// The major versions of SPDY. Major version differences indicate
// framer-layer incompatibility, as opposed to minor version numbers
-// which indicate application-layer incompatibility. It is guaranteed
-// that the enum value SPDYn maps to the integer n.
+// which indicate application-layer incompatibility. Do not rely on
+// the mapping from enum value SPDYn to the integer n.
enum SpdyMajorVersion {
SPDY2 = 2,
SPDY_MIN_VERSION = SPDY2,
SPDY3 = 3,
SPDY4 = 4,
- SPDY_MAX_VERSION = SPDY4
+ SPDY5 = 5,
+ SPDY_MAX_VERSION = SPDY5
};
// A SPDY stream id is a 31 bit entity.
@@ -282,50 +283,86 @@ enum SpdyFrameType {
GOAWAY,
HEADERS,
WINDOW_UPDATE,
- CREDENTIAL,
+ CREDENTIAL, // No longer valid. Kept for identifiability/enum order.
BLOCKED,
PUSH_PROMISE,
- LAST_CONTROL_TYPE = PUSH_PROMISE
+ CONTINUATION,
+ ALTSVC,
+ PRIORITY,
+ LAST_CONTROL_TYPE = PRIORITY
};
// Flags on data packets.
enum SpdyDataFlags {
- DATA_FLAG_NONE = 0,
- DATA_FLAG_FIN = 1,
+ DATA_FLAG_NONE = 0x00,
+ DATA_FLAG_FIN = 0x01,
+ DATA_FLAG_END_SEGMENT = 0x02,
+ DATA_FLAG_PAD_LOW = 0x08,
+ DATA_FLAG_PAD_HIGH = 0x10,
+ DATA_FLAG_COMPRESSED = 0x20,
};
// Flags on control packets
enum SpdyControlFlags {
- CONTROL_FLAG_NONE = 0,
- CONTROL_FLAG_FIN = 1,
- CONTROL_FLAG_UNIDIRECTIONAL = 2
+ CONTROL_FLAG_NONE = 0x00,
+ CONTROL_FLAG_FIN = 0x01,
+ CONTROL_FLAG_UNIDIRECTIONAL = 0x02,
+};
+
+enum SpdyPingFlags {
+ PING_FLAG_ACK = 0x01,
+};
+
+// Used by HEADERS, PUSH_PROMISE, and CONTINUATION.
+enum SpdyHeadersFlags {
+ HEADERS_FLAG_END_SEGMENT = 0x02,
+ HEADERS_FLAG_END_HEADERS = 0x04,
+ HEADERS_FLAG_PAD_LOW = 0x08,
+ HEADERS_FLAG_PAD_HIGH = 0x10,
+ HEADERS_FLAG_PRIORITY = 0x20,
+};
+
+enum SpdyPushPromiseFlags {
+ PUSH_PROMISE_FLAG_END_PUSH_PROMISE = 0x04,
};
// Flags on the SETTINGS control frame.
enum SpdySettingsControlFlags {
- SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS = 0x1
+ SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS = 0x01,
+};
+
+enum Http2SettingsControlFlags {
+ SETTINGS_FLAG_ACK = 0x01,
};
// Flags for settings within a SETTINGS frame.
enum SpdySettingsFlags {
- SETTINGS_FLAG_NONE = 0x0,
- SETTINGS_FLAG_PLEASE_PERSIST = 0x1,
- SETTINGS_FLAG_PERSISTED = 0x2
+ SETTINGS_FLAG_NONE = 0x00,
+ SETTINGS_FLAG_PLEASE_PERSIST = 0x01,
+ SETTINGS_FLAG_PERSISTED = 0x02,
};
-// List of known settings.
+// List of known settings. Avoid changing these enum values, as persisted
+// settings are keyed on them, and they are also exposed in net-internals.
enum SpdySettingsIds {
SETTINGS_UPLOAD_BANDWIDTH = 0x1,
SETTINGS_DOWNLOAD_BANDWIDTH = 0x2,
// Network round trip time in milliseconds.
SETTINGS_ROUND_TRIP_TIME = 0x3,
+ // The maximum number of simultaneous live streams in each direction.
SETTINGS_MAX_CONCURRENT_STREAMS = 0x4,
// TCP congestion window in packets.
SETTINGS_CURRENT_CWND = 0x5,
// Downstream byte retransmission rate in percentage.
SETTINGS_DOWNLOAD_RETRANS_RATE = 0x6,
// Initial window size in bytes
- SETTINGS_INITIAL_WINDOW_SIZE = 0x7
+ SETTINGS_INITIAL_WINDOW_SIZE = 0x7,
+ // HPACK header table maximum size.
+ SETTINGS_HEADER_TABLE_SIZE = 0x8,
+ // Whether or not server push (PUSH_PROMISE) is enabled.
+ SETTINGS_ENABLE_PUSH = 0x9,
+ // Whether or not to enable GZip compression of DATA frames.
+ SETTINGS_COMPRESS_DATA = 0xa,
};
// Status codes for RST_STREAM frames.
@@ -333,6 +370,7 @@ enum SpdyRstStreamStatus {
RST_STREAM_INVALID = 0,
RST_STREAM_PROTOCOL_ERROR = 1,
RST_STREAM_INVALID_STREAM = 2,
+ RST_STREAM_STREAM_CLOSED = 2, // Equivalent to INVALID_STREAM
RST_STREAM_REFUSED_STREAM = 3,
RST_STREAM_UNSUPPORTED_VERSION = 4,
RST_STREAM_CANCEL = 5,
@@ -341,29 +379,143 @@ enum SpdyRstStreamStatus {
RST_STREAM_STREAM_IN_USE = 8,
RST_STREAM_STREAM_ALREADY_CLOSED = 9,
RST_STREAM_INVALID_CREDENTIALS = 10,
+ // FRAME_TOO_LARGE (defined by SPDY versions 3.1 and below), and
+ // FRAME_SIZE_ERROR (defined by HTTP/2) are mapped to the same internal
+ // reset status.
RST_STREAM_FRAME_TOO_LARGE = 11,
- RST_STREAM_NUM_STATUS_CODES = 12
+ RST_STREAM_FRAME_SIZE_ERROR = 11,
+ RST_STREAM_SETTINGS_TIMEOUT = 12,
+ RST_STREAM_CONNECT_ERROR = 13,
+ RST_STREAM_ENHANCE_YOUR_CALM = 14,
+ RST_STREAM_NUM_STATUS_CODES = 15
};
// Status codes for GOAWAY frames.
enum SpdyGoAwayStatus {
- GOAWAY_INVALID = -1,
GOAWAY_OK = 0,
+ GOAWAY_NO_ERROR = GOAWAY_OK,
GOAWAY_PROTOCOL_ERROR = 1,
GOAWAY_INTERNAL_ERROR = 2,
- GOAWAY_NUM_STATUS_CODES = 3
+ GOAWAY_FLOW_CONTROL_ERROR = 3,
+ GOAWAY_SETTINGS_TIMEOUT = 4,
+ GOAWAY_STREAM_CLOSED = 5,
+ GOAWAY_FRAME_SIZE_ERROR = 6,
+ GOAWAY_REFUSED_STREAM = 7,
+ GOAWAY_CANCEL = 8,
+ GOAWAY_COMPRESSION_ERROR = 9,
+ GOAWAY_CONNECT_ERROR = 10,
+ GOAWAY_ENHANCE_YOUR_CALM = 11,
+ GOAWAY_INADEQUATE_SECURITY = 12
};
// A SPDY priority is a number between 0 and 7 (inclusive).
-// SPDY priority range is version-dependant. For SPDY 2 and below, priority is a
+// SPDY priority range is version-dependent. For SPDY 2 and below, priority is a
// number between 0 and 3.
typedef uint8 SpdyPriority;
-typedef uint8 SpdyCredentialSlot;
-
typedef std::map<std::string, std::string> SpdyNameValueBlock;
-typedef uint32 SpdyPingId;
+typedef uint64 SpdyPingId;
+
+typedef std::string SpdyProtocolId;
+
+// TODO(hkhalil): Add direct testing for this? It won't increase coverage any,
+// but is good to do anyway.
+class NET_EXPORT_PRIVATE SpdyConstants {
+ public:
+ // Returns true if a given on-the-wire enumeration of a frame type is valid
+ // for a given protocol version, false otherwise.
+ static bool IsValidFrameType(SpdyMajorVersion version, int frame_type_field);
+
+ // Parses a frame type from an on-the-wire enumeration of a given protocol
+ // version.
+ // Behavior is undefined for invalid frame type fields; consumers should first
+ // use IsValidFrameType() to verify validity of frame type fields.
+ static SpdyFrameType ParseFrameType(SpdyMajorVersion version,
+ int frame_type_field);
+
+ // Serializes a given frame type to the on-the-wire enumeration value for the
+ // given protocol version.
+ // Returns -1 on failure (I.E. Invalid frame type for the given version).
+ static int SerializeFrameType(SpdyMajorVersion version,
+ SpdyFrameType frame_type);
+
+ // Returns true if a given on-the-wire enumeration of a setting id is valid
+ // for a given protocol version, false otherwise.
+ static bool IsValidSettingId(SpdyMajorVersion version, int setting_id_field);
+
+ // Parses a setting id from an on-the-wire enumeration of a given protocol
+ // version.
+ // Behavior is undefined for invalid setting id fields; consumers should first
+ // use IsValidSettingId() to verify validity of setting id fields.
+ static SpdySettingsIds ParseSettingId(SpdyMajorVersion version,
+ int setting_id_field);
+
+ // Serializes a given setting id to the on-the-wire enumeration value for the
+ // given protocol version.
+ // Returns -1 on failure (I.E. Invalid setting id for the given version).
+ static int SerializeSettingId(SpdyMajorVersion version, SpdySettingsIds id);
+
+ // Returns true if a given on-the-wire enumeration of a RST_STREAM status code
+ // is valid for a given protocol version, false otherwise.
+ static bool IsValidRstStreamStatus(SpdyMajorVersion version,
+ int rst_stream_status_field);
+
+ // Parses a RST_STREAM status code from an on-the-wire enumeration of a given
+ // protocol version.
+ // Behavior is undefined for invalid RST_STREAM status code fields; consumers
+ // should first use IsValidRstStreamStatus() to verify validity of RST_STREAM
+ // status code fields..
+ static SpdyRstStreamStatus ParseRstStreamStatus(SpdyMajorVersion version,
+ int rst_stream_status_field);
+
+ // Serializes a given RST_STREAM status code to the on-the-wire enumeration
+ // value for the given protocol version.
+ // Returns -1 on failure (I.E. Invalid RST_STREAM status code for the given
+ // version).
+ static int SerializeRstStreamStatus(SpdyMajorVersion version,
+ SpdyRstStreamStatus rst_stream_status);
+
+ // Returns true if a given on-the-wire enumeration of a GOAWAY status code is
+ // valid for the given protocol version, false otherwise.
+ static bool IsValidGoAwayStatus(SpdyMajorVersion version,
+ int goaway_status_field);
+
+ // Parses a GOAWAY status from an on-the-wire enumeration of a given protocol
+ // version.
+ // Behavior is undefined for invalid GOAWAY status fields; consumers should
+ // first use IsValidGoAwayStatus() to verify validity of GOAWAY status fields.
+ static SpdyGoAwayStatus ParseGoAwayStatus(SpdyMajorVersion version,
+ int goaway_status_field);
+
+ // Serializes a given GOAWAY status to the on-the-wire enumeration value for
+ // the given protocol version.
+ // Returns -1 on failure (I.E. Invalid GOAWAY status for the given version).
+ static int SerializeGoAwayStatus(SpdyMajorVersion version,
+ SpdyGoAwayStatus status);
+
+ // Size, in bytes, of the data frame header. Future versions of SPDY
+ // will likely vary this, so we allow for the flexibility of a function call
+ // for this value as opposed to a constant.
+ static size_t GetDataFrameMinimumSize();
+
+ // Size, in bytes, of the control frame header.
+ static size_t GetControlFrameHeaderSize(SpdyMajorVersion version);
+
+ static size_t GetPrefixLength(SpdyFrameType type, SpdyMajorVersion version);
+
+ static size_t GetFrameMaximumSize(SpdyMajorVersion version);
+
+ // Returns the size of a header block size field. Valid only for SPDY
+ // versions <= 3.
+ static size_t GetSizeOfSizeField(SpdyMajorVersion version);
+
+ static SpdyMajorVersion ParseMajorVersion(int version_number);
+
+ static int SerializeMajorVersion(SpdyMajorVersion version);
+
+ static std::string GetVersionString(SpdyMajorVersion version);
+};
class SpdyFrame;
typedef SpdyFrame SpdySerializedFrame;
@@ -373,7 +525,7 @@ class SpdyFrameVisitor;
// Intermediate representation for SPDY frames.
// TODO(hkhalil): Rename this class to SpdyFrame when the existing SpdyFrame is
// gone.
-class SpdyFrameIR {
+class NET_EXPORT_PRIVATE SpdyFrameIR {
public:
virtual ~SpdyFrameIR() {}
@@ -388,7 +540,7 @@ class SpdyFrameIR {
// Abstract class intended to be inherited by IRs that have a stream associated
// to them.
-class SpdyFrameWithStreamIdIR : public SpdyFrameIR {
+class NET_EXPORT_PRIVATE SpdyFrameWithStreamIdIR : public SpdyFrameIR {
public:
virtual ~SpdyFrameWithStreamIdIR() {}
SpdyStreamId stream_id() const { return stream_id_; }
@@ -410,7 +562,7 @@ class SpdyFrameWithStreamIdIR : public SpdyFrameIR {
// Abstract class intended to be inherited by IRs that have the option of a FIN
// flag. Implies SpdyFrameWithStreamIdIR.
-class SpdyFrameWithFinIR : public SpdyFrameWithStreamIdIR {
+class NET_EXPORT_PRIVATE SpdyFrameWithFinIR : public SpdyFrameWithStreamIdIR {
public:
virtual ~SpdyFrameWithFinIR() {}
bool fin() const { return fin_; }
@@ -435,11 +587,17 @@ class NET_EXPORT_PRIVATE SpdyFrameWithNameValueBlockIR
const SpdyNameValueBlock& name_value_block() const {
return name_value_block_;
}
- SpdyNameValueBlock* GetMutableNameValueBlock() { return &name_value_block_; }
+ void set_name_value_block(const SpdyNameValueBlock& name_value_block) {
+ // Deep copy.
+ name_value_block_ = name_value_block;
+ }
void SetHeader(const base::StringPiece& name,
const base::StringPiece& value) {
name_value_block_[name.as_string()] = value.as_string();
}
+ SpdyNameValueBlock* mutable_name_value_block() {
+ return &name_value_block_;
+ }
protected:
explicit SpdyFrameWithNameValueBlockIR(SpdyStreamId stream_id);
@@ -464,6 +622,29 @@ class NET_EXPORT_PRIVATE SpdyDataIR
base::StringPiece data() const { return data_; }
+ bool pad_low() const { return pad_low_; }
+
+ bool pad_high() const { return pad_high_; }
+
+ int padding_payload_len() const { return padding_payload_len_; }
+
+ void set_padding_len(int padding_len) {
+ // The padding_len should be in (0, 65535 + 2].
+ // Note that SpdyFramer::GetDataFrameMaximumPayload() enforces the overall
+ // payload size later so we actually can't pad more than 16375 bytes.
+ DCHECK_GT(padding_len, 0);
+ DCHECK_LT(padding_len, 65537);
+
+ if (padding_len <= 256) {
+ pad_low_ = true;
+ --padding_len;
+ } else {
+ pad_low_ = pad_high_ = true;
+ padding_len -= 2;
+ }
+ padding_payload_len_ = padding_len;
+ }
+
// Deep-copy of data (keep private copy).
void SetDataDeep(const base::StringPiece& data) {
data_store_.reset(new std::string(data.data(), data.length()));
@@ -483,6 +664,11 @@ class NET_EXPORT_PRIVATE SpdyDataIR
scoped_ptr<std::string> data_store_;
base::StringPiece data_;
+ bool pad_low_;
+ bool pad_high_;
+ // padding_payload_len_ = desired padding length - len(padding length field).
+ int padding_payload_len_;
+
DISALLOW_COPY_AND_ASSIGN(SpdyDataIR);
};
@@ -493,7 +679,6 @@ class NET_EXPORT_PRIVATE SpdySynStreamIR
: SpdyFrameWithNameValueBlockIR(stream_id),
associated_to_stream_id_(0),
priority_(0),
- slot_(0),
unidirectional_(false) {}
SpdyStreamId associated_to_stream_id() const {
return associated_to_stream_id_;
@@ -503,8 +688,6 @@ class NET_EXPORT_PRIVATE SpdySynStreamIR
}
SpdyPriority priority() const { return priority_; }
void set_priority(SpdyPriority priority) { priority_ = priority; }
- SpdyCredentialSlot slot() const { return slot_; }
- void set_slot(SpdyCredentialSlot slot) { slot_ = slot; }
bool unidirectional() const { return unidirectional_; }
void set_unidirectional(bool unidirectional) {
unidirectional_ = unidirectional;
@@ -515,13 +698,12 @@ class NET_EXPORT_PRIVATE SpdySynStreamIR
private:
SpdyStreamId associated_to_stream_id_;
SpdyPriority priority_;
- SpdyCredentialSlot slot_;
bool unidirectional_;
DISALLOW_COPY_AND_ASSIGN(SpdySynStreamIR);
};
-class SpdySynReplyIR : public SpdyFrameWithNameValueBlockIR {
+class NET_EXPORT_PRIVATE SpdySynReplyIR : public SpdyFrameWithNameValueBlockIR {
public:
explicit SpdySynReplyIR(SpdyStreamId stream_id)
: SpdyFrameWithNameValueBlockIR(stream_id) {}
@@ -532,30 +714,36 @@ class SpdySynReplyIR : public SpdyFrameWithNameValueBlockIR {
DISALLOW_COPY_AND_ASSIGN(SpdySynReplyIR);
};
-class SpdyRstStreamIR : public SpdyFrameWithStreamIdIR {
+class NET_EXPORT_PRIVATE SpdyRstStreamIR : public SpdyFrameWithStreamIdIR {
public:
- SpdyRstStreamIR(SpdyStreamId stream_id, SpdyRstStreamStatus status)
- : SpdyFrameWithStreamIdIR(stream_id) {
- set_status(status);
- }
+ SpdyRstStreamIR(SpdyStreamId stream_id, SpdyRstStreamStatus status,
+ base::StringPiece description);
+
+ virtual ~SpdyRstStreamIR();
+
SpdyRstStreamStatus status() const {
return status_;
}
void set_status(SpdyRstStreamStatus status) {
- DCHECK_NE(status, RST_STREAM_INVALID);
- DCHECK_LT(status, RST_STREAM_NUM_STATUS_CODES);
status_ = status;
}
+ base::StringPiece description() const { return description_; }
+
+ void set_description(base::StringPiece description) {
+ description_ = description;
+ }
+
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
private:
SpdyRstStreamStatus status_;
+ base::StringPiece description_;
DISALLOW_COPY_AND_ASSIGN(SpdyRstStreamIR);
};
-class SpdySettingsIR : public SpdyFrameIR {
+class NET_EXPORT_PRIVATE SpdySettingsIR : public SpdyFrameIR {
public:
// Associates flags with a value.
struct Value {
@@ -578,45 +766,53 @@ class SpdySettingsIR : public SpdyFrameIR {
bool persist_value,
bool persisted,
int32 value) {
- // TODO(hkhalil): DCHECK_LE(SETTINGS_UPLOAD_BANDWIDTH, id);
- // TODO(hkhalil): DCHECK_GE(SETTINGS_INITIAL_WINDOW_SIZE, id);
values_[id].persist_value = persist_value;
values_[id].persisted = persisted;
values_[id].value = value;
}
+
bool clear_settings() const { return clear_settings_; }
void set_clear_settings(bool clear_settings) {
clear_settings_ = clear_settings;
}
+ bool is_ack() const { return is_ack_; }
+ void set_is_ack(bool is_ack) {
+ is_ack_ = is_ack;
+ }
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
private:
ValueMap values_;
bool clear_settings_;
+ bool is_ack_;
DISALLOW_COPY_AND_ASSIGN(SpdySettingsIR);
};
-class SpdyPingIR : public SpdyFrameIR {
+class NET_EXPORT_PRIVATE SpdyPingIR : public SpdyFrameIR {
public:
- explicit SpdyPingIR(SpdyPingId id) : id_(id) {}
+ explicit SpdyPingIR(SpdyPingId id) : id_(id), is_ack_(false) {}
SpdyPingId id() const { return id_; }
+ // ACK logic is valid only for SPDY versions 4 and above.
+ bool is_ack() const { return is_ack_; }
+ void set_is_ack(bool is_ack) { is_ack_ = is_ack; }
+
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
private:
SpdyPingId id_;
+ bool is_ack_;
DISALLOW_COPY_AND_ASSIGN(SpdyPingIR);
};
-class SpdyGoAwayIR : public SpdyFrameIR {
+class NET_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR {
public:
- SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayStatus status) {
- set_last_good_stream_id(last_good_stream_id);
- set_status(status);
- }
+ SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayStatus status,
+ const base::StringPiece& description);
+ virtual ~SpdyGoAwayIR();
SpdyStreamId last_good_stream_id() const { return last_good_stream_id_; }
void set_last_good_stream_id(SpdyStreamId last_good_stream_id) {
DCHECK_LE(0u, last_good_stream_id);
@@ -629,27 +825,40 @@ class SpdyGoAwayIR : public SpdyFrameIR {
status_ = status;
}
+ const base::StringPiece& description() const;
+
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
private:
SpdyStreamId last_good_stream_id_;
SpdyGoAwayStatus status_;
+ const base::StringPiece description_;
DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayIR);
};
-class SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR {
+class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR {
public:
explicit SpdyHeadersIR(SpdyStreamId stream_id)
- : SpdyFrameWithNameValueBlockIR(stream_id) {}
+ : SpdyFrameWithNameValueBlockIR(stream_id),
+ has_priority_(false),
+ priority_(0) {}
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
+ bool has_priority() const { return has_priority_; }
+ void set_has_priority(bool has_priority) { has_priority_ = has_priority; }
+ uint32 priority() const { return priority_; }
+ void set_priority(SpdyPriority priority) { priority_ = priority; }
+
private:
+ bool has_priority_;
+ // 31-bit priority.
+ uint32 priority_;
DISALLOW_COPY_AND_ASSIGN(SpdyHeadersIR);
};
-class SpdyWindowUpdateIR : public SpdyFrameWithStreamIdIR {
+class NET_EXPORT_PRIVATE SpdyWindowUpdateIR : public SpdyFrameWithStreamIdIR {
public:
SpdyWindowUpdateIR(SpdyStreamId stream_id, int32 delta)
: SpdyFrameWithStreamIdIR(stream_id) {
@@ -670,50 +879,20 @@ class SpdyWindowUpdateIR : public SpdyFrameWithStreamIdIR {
DISALLOW_COPY_AND_ASSIGN(SpdyWindowUpdateIR);
};
-class SpdyCredentialIR : public SpdyFrameIR {
- public:
- typedef std::vector<std::string> CertificateList;
-
- explicit SpdyCredentialIR(int16 slot);
- virtual ~SpdyCredentialIR();
-
- int16 slot() const { return slot_; }
- void set_slot(int16 slot) {
- // TODO(hkhalil): Verify valid slot range?
- slot_ = slot;
- }
- base::StringPiece proof() const { return proof_; }
- void set_proof(const base::StringPiece& proof) {
- proof.CopyToString(&proof_);
- }
- const CertificateList* certificates() const { return &certificates_; }
- void AddCertificate(const base::StringPiece& certificate) {
- certificates_.push_back(certificate.as_string());
- }
-
- virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
-
- private:
- int16 slot_;
- std::string proof_;
- CertificateList certificates_;
-
- DISALLOW_COPY_AND_ASSIGN(SpdyCredentialIR);
-};
-
class NET_EXPORT_PRIVATE SpdyBlockedIR
: public NON_EXPORTED_BASE(SpdyFrameWithStreamIdIR) {
- public:
- explicit SpdyBlockedIR(SpdyStreamId stream_id)
- : SpdyFrameWithStreamIdIR(stream_id) {}
+ public:
+ explicit SpdyBlockedIR(SpdyStreamId stream_id)
+ : SpdyFrameWithStreamIdIR(stream_id) {}
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
- private:
- DISALLOW_COPY_AND_ASSIGN(SpdyBlockedIR);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SpdyBlockedIR);
};
-class SpdyPushPromiseIR : public SpdyFrameWithNameValueBlockIR {
+class NET_EXPORT_PRIVATE SpdyPushPromiseIR
+ : public SpdyFrameWithNameValueBlockIR {
public:
SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id)
: SpdyFrameWithNameValueBlockIR(stream_id),
@@ -728,6 +907,59 @@ class SpdyPushPromiseIR : public SpdyFrameWithNameValueBlockIR {
DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR);
};
+// TODO(jgraettinger): This representation needs review. SpdyContinuationIR
+// needs to frame a portion of a single, arbitrarily-broken encoded buffer.
+class NET_EXPORT_PRIVATE SpdyContinuationIR
+ : public SpdyFrameWithNameValueBlockIR {
+ public:
+ explicit SpdyContinuationIR(SpdyStreamId stream_id)
+ : SpdyFrameWithNameValueBlockIR(stream_id),
+ end_headers_(false) {}
+
+ virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
+
+ bool end_headers() const { return end_headers_; }
+ void set_end_headers(bool end_headers) {end_headers_ = end_headers;}
+
+ private:
+ bool end_headers_;
+ DISALLOW_COPY_AND_ASSIGN(SpdyContinuationIR);
+};
+
+class NET_EXPORT_PRIVATE SpdyAltSvcIR : public SpdyFrameWithStreamIdIR {
+ public:
+ explicit SpdyAltSvcIR(SpdyStreamId stream_id);
+
+ uint32 max_age() const { return max_age_; }
+ uint16 port() const { return port_; }
+ SpdyProtocolId protocol_id() const {
+ return protocol_id_;
+ }
+ std::string host() const { return host_; }
+ std::string origin() const { return origin_; }
+
+ void set_max_age(uint32 max_age) { max_age_ = max_age; }
+ void set_port(uint16 port) { port_ = port; }
+ void set_protocol_id(SpdyProtocolId protocol_id) {
+ protocol_id_ = protocol_id;
+ }
+ void set_host(std::string host) {
+ host_ = host;
+ }
+ void set_origin(std::string origin) {
+ origin_ = origin;
+ }
+
+ virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
+
+ private:
+ uint32 max_age_;
+ uint16 port_;
+ SpdyProtocolId protocol_id_;
+ std::string host_;
+ std::string origin_;
+ DISALLOW_COPY_AND_ASSIGN(SpdyAltSvcIR);
+};
// -------------------------------------------------------------------------
// Wrapper classes for various SPDY frames.
@@ -786,9 +1018,10 @@ class SpdyFrameVisitor {
virtual void VisitGoAway(const SpdyGoAwayIR& goaway) = 0;
virtual void VisitHeaders(const SpdyHeadersIR& headers) = 0;
virtual void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) = 0;
- virtual void VisitCredential(const SpdyCredentialIR& credential) = 0;
virtual void VisitBlocked(const SpdyBlockedIR& blocked) = 0;
virtual void VisitPushPromise(const SpdyPushPromiseIR& push_promise) = 0;
+ virtual void VisitContinuation(const SpdyContinuationIR& continuation) = 0;
+ virtual void VisitAltSvc(const SpdyAltSvcIR& altsvc) = 0;
virtual void VisitData(const SpdyDataIR& data) = 0;
protected:
diff --git a/chromium/net/spdy/spdy_protocol_test.cc b/chromium/net/spdy/spdy_protocol_test.cc
index 98661f01de5..e098281678c 100644
--- a/chromium/net/spdy/spdy_protocol_test.cc
+++ b/chromium/net/spdy/spdy_protocol_test.cc
@@ -10,13 +10,14 @@
#include "base/memory/scoped_ptr.h"
#include "net/spdy/spdy_bitmasks.h"
#include "net/spdy/spdy_framer.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
enum SpdyProtocolTestTypes {
- SPDY2 = 2,
- SPDY3 = 3,
+ SPDY2 = net::SPDY2,
+ SPDY3 = net::SPDY3,
};
} // namespace
@@ -27,11 +28,11 @@ class SpdyProtocolTest
: public ::testing::TestWithParam<SpdyProtocolTestTypes> {
protected:
virtual void SetUp() {
- spdy_version_ = GetParam();
+ spdy_version_ = static_cast<SpdyMajorVersion>(GetParam());
}
// Version of SPDY protocol to be used.
- int spdy_version_;
+ SpdyMajorVersion spdy_version_;
};
// All tests are run with two different SPDY versions: SPDY/2 and SPDY/3.
@@ -40,6 +41,7 @@ INSTANTIATE_TEST_CASE_P(SpdyProtocolTests,
::testing::Values(SPDY2, SPDY3));
// Test our protocol constants
+// TODO(hkhalil): Remove this test once we no longer rely on exact values.
TEST_P(SpdyProtocolTest, ProtocolConstants) {
EXPECT_EQ(1, SYN_STREAM);
EXPECT_EQ(2, SYN_REPLY);
@@ -53,7 +55,10 @@ TEST_P(SpdyProtocolTest, ProtocolConstants) {
EXPECT_EQ(10, CREDENTIAL);
EXPECT_EQ(11, BLOCKED);
EXPECT_EQ(12, PUSH_PROMISE);
- EXPECT_EQ(12, LAST_CONTROL_TYPE);
+ EXPECT_EQ(13, CONTINUATION);
+ EXPECT_EQ(14, ALTSVC);
+ EXPECT_EQ(15, PRIORITY);
+ EXPECT_EQ(15, LAST_CONTROL_TYPE);
EXPECT_EQ(std::numeric_limits<int32>::max(), kSpdyMaximumWindowSize);
}
@@ -64,21 +69,16 @@ INSTANTIATE_TEST_CASE_P(SpdyProtocolDeathTests,
SpdyProtocolDeathTest,
::testing::Values(SPDY2, SPDY3));
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
TEST_P(SpdyProtocolDeathTest, TestSpdySettingsAndIdOutOfBounds) {
scoped_ptr<SettingsFlagsAndId> flags_and_id;
- EXPECT_DEBUG_DEATH(
- {
- flags_and_id.reset(new SettingsFlagsAndId(1, ~0));
- },
- "SPDY setting ID too large.");
+ EXPECT_DFATAL(flags_and_id.reset(new SettingsFlagsAndId(1, ~0)),
+ "SPDY setting ID too large.");
// Make sure that we get expected values in opt mode.
if (flags_and_id.get() != NULL) {
EXPECT_EQ(1, flags_and_id->flags());
EXPECT_EQ(static_cast<SpdyPingId>(0xffffff), flags_and_id->id());
}
}
-#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
} // namespace net
diff --git a/chromium/net/spdy/spdy_proxy_client_socket.cc b/chromium/net/spdy/spdy_proxy_client_socket.cc
index fa3300f3d49..d268a4d8451 100644
--- a/chromium/net/spdy/spdy_proxy_client_socket.cc
+++ b/chromium/net/spdy/spdy_proxy_client_socket.cc
@@ -36,18 +36,18 @@ SpdyProxyClientSocket::SpdyProxyClientSocket(
: next_state_(STATE_DISCONNECTED),
spdy_stream_(spdy_stream),
endpoint_(endpoint),
- auth_(
- new HttpAuthController(HttpAuth::AUTH_PROXY,
- GURL("https://" + proxy_server.ToString()),
- auth_cache,
- auth_handler_factory)),
+ auth_(new HttpAuthController(HttpAuth::AUTH_PROXY,
+ GURL("https://" + proxy_server.ToString()),
+ auth_cache,
+ auth_handler_factory)),
user_buffer_len_(0),
write_buffer_len_(0),
was_ever_used_(false),
redirect_has_load_timing_info_(false),
- weak_factory_(this),
net_log_(BoundNetLog::Make(spdy_stream->net_log().net_log(),
- NetLog::SOURCE_PROXY_CLIENT_SOCKET)) {
+ NetLog::SOURCE_PROXY_CLIENT_SOCKET)),
+ weak_factory_(this),
+ write_callback_weak_factory_(this) {
request_.method = "CONNECT";
request_.url = url;
if (!user_agent.empty())
@@ -137,6 +137,7 @@ void SpdyProxyClientSocket::Disconnect() {
write_buffer_len_ = 0;
write_callback_.Reset();
+ write_callback_weak_factory_.InvalidateWeakPtrs();
next_state_ = STATE_DISCONNECTED;
@@ -154,7 +155,7 @@ bool SpdyProxyClientSocket::IsConnected() const {
bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
return IsConnected() && read_buffer_queue_.IsEmpty() &&
- spdy_stream_->IsIdle();
+ spdy_stream_->IsOpen();
}
const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
@@ -237,16 +238,16 @@ int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
return ERR_IO_PENDING;
}
-bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
+int SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
// Since this StreamSocket sits on top of a shared SpdySession, it
- // is not safe for callers to set change this underlying socket.
- return false;
+ // is not safe for callers to change this underlying socket.
+ return ERR_NOT_IMPLEMENTED;
}
-bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
+int SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
// Since this StreamSocket sits on top of a shared SpdySession, it
- // is not safe for callers to set change this underlying socket.
- return false;
+ // is not safe for callers to change this underlying socket.
+ return ERR_NOT_IMPLEMENTED;
}
int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
@@ -268,6 +269,11 @@ void SpdyProxyClientSocket::LogBlockedTunnelResponse() const {
/* is_https_proxy = */ true);
}
+void SpdyProxyClientSocket::RunCallback(const CompletionCallback& callback,
+ int result) const {
+ callback.Run(result);
+}
+
void SpdyProxyClientSocket::OnIOComplete(int result) {
DCHECK_NE(STATE_DISCONNECTED, next_state_);
int rv = DoLoop(result);
@@ -410,6 +416,7 @@ int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
if (SanitizeProxyRedirect(&response_, request_.url)) {
redirect_has_load_timing_info_ =
spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_);
+ // Note that this triggers a RST_STREAM_CANCEL.
spdy_stream_->DetachDelegate();
next_state_ = STATE_DISCONNECTED;
return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
@@ -482,7 +489,15 @@ void SpdyProxyClientSocket::OnDataSent() {
int rv = write_buffer_len_;
write_buffer_len_ = 0;
- ResetAndReturn(&write_callback_).Run(rv);
+
+ // Proxy write callbacks result in deep callback chains. Post to allow the
+ // stream's write callback chain to unwind (see crbug.com/355511).
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SpdyProxyClientSocket::RunCallback,
+ write_callback_weak_factory_.GetWeakPtr(),
+ ResetAndReturn(&write_callback_),
+ rv));
}
void SpdyProxyClientSocket::OnClose(int status) {
diff --git a/chromium/net/spdy/spdy_proxy_client_socket.h b/chromium/net/spdy/spdy_proxy_client_socket.h
index 3d7933110b4..7a293a0482e 100644
--- a/chromium/net/spdy/spdy_proxy_client_socket.h
+++ b/chromium/net/spdy/spdy_proxy_client_socket.h
@@ -86,8 +86,8 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket,
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 int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
@@ -113,6 +113,10 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket,
void LogBlockedTunnelResponse() const;
+ // Calls |callback.Run(result)|. Used to run a callback posted to the
+ // message loop.
+ void RunCallback(const CompletionCallback& callback, int result) const;
+
void OnIOComplete(int result);
int DoLoop(int last_io_result);
@@ -163,9 +167,14 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket,
bool redirect_has_load_timing_info_;
LoadTimingInfo redirect_load_timing_info_;
+ const BoundNetLog net_log_;
+
+ // The default weak pointer factory.
base::WeakPtrFactory<SpdyProxyClientSocket> weak_factory_;
- const BoundNetLog net_log_;
+ // Only used for posting write callbacks. Weak pointers created by this
+ // factory are invalidated in Disconnect().
+ base::WeakPtrFactory<SpdyProxyClientSocket> write_callback_weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocket);
};
diff --git a/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc b/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc
index c2b1ac010f1..1f9747d2f55 100644
--- a/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -71,6 +71,8 @@ class SpdyProxyClientSocketTest
protected:
void Initialize(MockRead* reads, size_t reads_count, MockWrite* writes,
size_t writes_count);
+ void PopulateConnectRequestIR(SpdySynStreamIR* syn_ir);
+ void PopulateConnectReplyIR(SpdySynReplyIR* reply_ir, const char* status);
SpdyFrame* ConstructConnectRequestFrame();
SpdyFrame* ConstructConnectAuthRequestFrame();
SpdyFrame* ConstructConnectReplyFrame();
@@ -93,8 +95,8 @@ class SpdyProxyClientSocketTest
int num_reads);
void AddAuthToCache() {
- const base::string16 kFoo(ASCIIToUTF16("foo"));
- const base::string16 kBar(ASCIIToUTF16("bar"));
+ const base::string16 kFoo(base::ASCIIToUTF16("foo"));
+ const base::string16 kBar(base::ASCIIToUTF16("bar"));
session_->http_auth_cache()->Add(GURL(kProxyUrl),
"MyRealm1",
HttpAuth::AUTH_SCHEME_BASIC,
@@ -141,8 +143,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdyProxyClientSocketTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
SpdyProxyClientSocketTest::SpdyProxyClientSocketTest()
: spdy_util_(GetParam()),
@@ -158,12 +159,11 @@ SpdyProxyClientSocketTest::SpdyProxyClientSocketTest()
proxy_(ProxyServer::SCHEME_HTTPS, proxy_host_port_),
endpoint_spdy_session_key_(endpoint_host_port_pair_,
proxy_,
- kPrivacyModeDisabled) {
+ PRIVACY_MODE_DISABLED) {
session_deps_.net_log = net_log_.bound().net_log();
}
void SpdyProxyClientSocketTest::TearDown() {
- sock_.reset(NULL);
if (session_.get() != NULL)
session_->spdy_session_pool()->CloseAllSessions();
@@ -230,7 +230,6 @@ void SpdyProxyClientSocketTest::AssertConnectionEstablished() {
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != NULL);
ASSERT_EQ(200, response->headers->response_code());
- ASSERT_EQ("Connection Established", response->headers->GetStatusText());
}
void SpdyProxyClientSocketTest::AssertSyncReadEquals(const char* data,
@@ -311,152 +310,69 @@ void SpdyProxyClientSocketTest::AssertAsyncWriteWithReadsSucceeds(
write_callback_.WaitForResult();
}
+void SpdyProxyClientSocketTest::PopulateConnectRequestIR(
+ SpdySynStreamIR* syn_ir) {
+ spdy_util_.SetPriority(LOWEST, syn_ir);
+ syn_ir->SetHeader(spdy_util_.GetMethodKey(), "CONNECT");
+ syn_ir->SetHeader(spdy_util_.GetPathKey(), kOriginHostPort);
+ syn_ir->SetHeader(spdy_util_.GetHostKey(), kOriginHost);
+ syn_ir->SetHeader("user-agent", kUserAgent);
+ spdy_util_.MaybeAddVersionHeader(syn_ir);
+}
+
+void SpdyProxyClientSocketTest::PopulateConnectReplyIR(SpdySynReplyIR* reply_ir,
+ const char* status) {
+ reply_ir->SetHeader(spdy_util_.GetStatusKey(), status);
+ spdy_util_.MaybeAddVersionHeader(reply_ir);
+}
+
// Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
SpdyFrame*
SpdyProxyClientSocketTest::ConstructConnectRequestFrame() {
- const SpdyHeaderInfo kSynStartHeader = {
- SYN_STREAM,
- kStreamId,
- 0,
- net::ConvertRequestPriorityToSpdyPriority(
- LOWEST, spdy_util_.spdy_version()),
- 0,
- CONTROL_FLAG_NONE,
- false,
- RST_STREAM_INVALID,
- NULL,
- 0,
- DATA_FLAG_NONE
- };
- bool spdy2 = spdy_util_.is_spdy2();
- const char* const kConnectHeaders[] = {
- spdy2 ? "method" : ":method", "CONNECT",
- spdy2 ? "url" : ":path", kOriginHostPort,
- spdy2 ? "host" : ":host", kOriginHost,
- "user-agent", kUserAgent,
- spdy2 ? "version" : ":version", "HTTP/1.1",
- };
- return spdy_util_.ConstructSpdyFrame(
- kSynStartHeader, NULL, 0, kConnectHeaders, arraysize(kConnectHeaders)/2);
+ SpdySynStreamIR syn_ir(kStreamId);
+ PopulateConnectRequestIR(&syn_ir);
+ return spdy_util_.CreateFramer(false)->SerializeFrame(syn_ir);
}
// Constructs a SPDY SYN_STREAM frame for a CONNECT request which includes
// Proxy-Authorization headers.
-SpdyFrame*
-SpdyProxyClientSocketTest::ConstructConnectAuthRequestFrame() {
- const SpdyHeaderInfo kSynStartHeader = {
- SYN_STREAM,
- kStreamId,
- 0,
- net::ConvertRequestPriorityToSpdyPriority(
- LOWEST, spdy_util_.spdy_version()),
- 0,
- CONTROL_FLAG_NONE,
- false,
- RST_STREAM_INVALID,
- NULL,
- 0,
- DATA_FLAG_NONE
- };
- bool spdy2 = spdy_util_.is_spdy2();
- const char* const kConnectHeaders[] = {
- spdy2 ? "method" : ":method", "CONNECT",
- spdy2 ? "url" : ":path", kOriginHostPort,
- spdy2 ? "host" : ":host", kOriginHost,
- "user-agent", kUserAgent,
- spdy2 ? "version" : ":version", "HTTP/1.1",
- "proxy-authorization", "Basic Zm9vOmJhcg==",
- };
- return spdy_util_.ConstructSpdyFrame(
- kSynStartHeader, NULL, 0, kConnectHeaders, arraysize(kConnectHeaders)/2);
+SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectAuthRequestFrame() {
+ SpdySynStreamIR syn_ir(kStreamId);
+ PopulateConnectRequestIR(&syn_ir);
+ syn_ir.SetHeader("proxy-authorization", "Basic Zm9vOmJhcg==");
+ return spdy_util_.CreateFramer(false)->SerializeFrame(syn_ir);
}
// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT.
SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectReplyFrame() {
- bool spdy2 = spdy_util_.is_spdy2();
- const char* const kStandardReplyHeaders[] = {
- spdy2 ? "status" : ":status", "200 Connection Established",
- spdy2 ? "version" : ":version", "HTTP/1.1"
- };
- return spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- false,
- kStreamId,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardReplyHeaders,
- arraysize(kStandardReplyHeaders),
- 0);
+ SpdySynReplyIR reply_ir(kStreamId);
+ PopulateConnectReplyIR(&reply_ir, "200");
+ return spdy_util_.CreateFramer(false)->SerializeFrame(reply_ir);
}
-// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT.
-SpdyFrame*
-SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() {
- bool spdy2 = spdy_util_.is_spdy2();
-
- const char* const kStandardReplyHeaders[] = {
- spdy2 ? "status" : ":status", "407 Proxy Authentication Required",
- spdy2 ? "version" : ":version", "HTTP/1.1",
- "proxy-authenticate", "Basic realm=\"MyRealm1\"",
- };
-
- return spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- false,
- kStreamId,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardReplyHeaders,
- arraysize(kStandardReplyHeaders),
- 0);
+// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT,
+// including Proxy-Authenticate headers.
+SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() {
+ SpdySynReplyIR reply_ir(kStreamId);
+ PopulateConnectReplyIR(&reply_ir, "407");
+ reply_ir.SetHeader("proxy-authenticate", "Basic realm=\"MyRealm1\"");
+ return framer_.SerializeFrame(reply_ir);
}
// Constructs a SPDY SYN_REPLY frame with an HTTP 302 redirect.
-SpdyFrame*
-SpdyProxyClientSocketTest::ConstructConnectRedirectReplyFrame() {
- bool spdy2 = spdy_util_.is_spdy2();
-
- const char* const kStandardReplyHeaders[] = {
- spdy2 ? "status" : ":status", "302 Found",
- spdy2 ? "version" : ":version", "HTTP/1.1",
- "location", kRedirectUrl,
- "set-cookie", "foo=bar"
- };
-
- return spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- false,
- kStreamId,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardReplyHeaders,
- arraysize(kStandardReplyHeaders),
- 0);
+SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectRedirectReplyFrame() {
+ SpdySynReplyIR reply_ir(kStreamId);
+ PopulateConnectReplyIR(&reply_ir, "302");
+ reply_ir.SetHeader("location", kRedirectUrl);
+ reply_ir.SetHeader("set-cookie", "foo=bar");
+ return framer_.SerializeFrame(reply_ir);
}
// Constructs a SPDY SYN_REPLY frame with an HTTP 500 error.
-SpdyFrame*
-SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() {
- bool spdy2 = spdy_util_.is_spdy2();
-
- const char* const kStandardReplyHeaders[] = {
- spdy2 ? "status" : ":status", "500 Internal Server Error",
- spdy2 ? "version" : ":version", "HTTP/1.1",
- };
-
- return spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- false,
- kStreamId,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardReplyHeaders,
- arraysize(kStandardReplyHeaders),
- 0);
+SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() {
+ SpdySynReplyIR reply_ir(kStreamId);
+ PopulateConnectReplyIR(&reply_ir, "500");
+ return framer_.SerializeFrame(reply_ir);
}
SpdyFrame* SpdyProxyClientSocketTest::ConstructBodyFrame(
@@ -507,8 +423,6 @@ TEST_P(SpdyProxyClientSocketTest, ConnectWithAuthRequested) {
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != NULL);
ASSERT_EQ(407, response->headers->response_code());
- ASSERT_EQ("Proxy Authentication Required",
- response->headers->GetStatusText());
}
TEST_P(SpdyProxyClientSocketTest, ConnectWithAuthCredentials) {
@@ -533,14 +447,15 @@ TEST_P(SpdyProxyClientSocketTest, ConnectWithAuthCredentials) {
TEST_P(SpdyProxyClientSocketTest, ConnectRedirects) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectRedirectReplyFrame());
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 2), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 3), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -558,6 +473,9 @@ TEST_P(SpdyProxyClientSocketTest, ConnectRedirects) {
std::string location;
ASSERT_TRUE(headers->IsRedirect(&location));
ASSERT_EQ(location, kRedirectUrl);
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
TEST_P(SpdyProxyClientSocketTest, ConnectFails) {
@@ -584,14 +502,15 @@ TEST_P(SpdyProxyClientSocketTest, ConnectFails) {
TEST_P(SpdyProxyClientSocketTest, WasEverUsedReturnsCorrectValues) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 2), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 3), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -601,6 +520,9 @@ TEST_P(SpdyProxyClientSocketTest, WasEverUsedReturnsCorrectValues) {
EXPECT_TRUE(sock_->WasEverUsed());
sock_->Disconnect();
EXPECT_TRUE(sock_->WasEverUsed());
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// ----------- GetPeerAddress
@@ -1072,14 +994,15 @@ TEST_P(SpdyProxyClientSocketTest, PendingReadOnCloseReturnsZero) {
TEST_P(SpdyProxyClientSocketTest,
ReadOnDisconnectSocketReturnsNotConnected) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 2), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 3), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -1090,6 +1013,9 @@ TEST_P(SpdyProxyClientSocketTest,
ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED,
sock_->Read(NULL, 1, CompletionCallback()));
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// Reading buffered data from an already closed socket should return
@@ -1153,15 +1079,16 @@ TEST_P(SpdyProxyClientSocketTest, WriteOnClosedStream) {
// Calling Write() on a disconnected socket is an error
TEST_P(SpdyProxyClientSocketTest, WriteOnDisconnectedSocket) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
scoped_ptr<SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 2), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 3), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -1173,6 +1100,9 @@ TEST_P(SpdyProxyClientSocketTest, WriteOnDisconnectedSocket) {
scoped_refptr<IOBufferWithSize> buf(CreateBuffer(kMsg1, kLen1));
EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED,
sock_->Write(buf.get(), buf->size(), CompletionCallback()));
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// If the socket is closed with a pending Write(), the callback
@@ -1209,15 +1139,16 @@ TEST_P(SpdyProxyClientSocketTest, WritePendingOnClose) {
// should not be called.
TEST_P(SpdyProxyClientSocketTest, DisconnectWithWritePending) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
- MockWrite(SYNCHRONOUS, 0, 2), // EOF
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
+ MockWrite(SYNCHRONOUS, 0, 3), // EOF
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 3), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 4), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -1234,20 +1165,24 @@ TEST_P(SpdyProxyClientSocketTest, DisconnectWithWritePending) {
EXPECT_FALSE(sock_->IsConnected());
EXPECT_FALSE(write_callback_.have_result());
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// If the socket is Disconnected with a pending Read(), the callback
// should not be called.
TEST_P(SpdyProxyClientSocketTest, DisconnectWithReadPending) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 2),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- MockRead(ASYNC, 0, 2), // EOF
+ CreateMockRead(*resp, 1, ASYNC), MockRead(ASYNC, 0, 3), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -1264,6 +1199,9 @@ TEST_P(SpdyProxyClientSocketTest, DisconnectWithReadPending) {
EXPECT_FALSE(sock_->IsConnected());
EXPECT_FALSE(read_callback_.have_result());
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// If the socket is Reset when both a read and write are pending,
@@ -1305,22 +1243,26 @@ TEST_P(SpdyProxyClientSocketTest, RstWithReadAndWritePending) {
EXPECT_TRUE(sock_.get());
EXPECT_TRUE(read_callback_.have_result());
EXPECT_TRUE(write_callback_.have_result());
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// Makes sure the proxy client socket's source gets the expected NetLog events
// and only the expected NetLog events (No SpdySession events).
TEST_P(SpdyProxyClientSocketTest, NetLog) {
scoped_ptr<SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
MockWrite writes[] = {
- CreateMockWrite(*conn, 0, SYNCHRONOUS),
+ CreateMockWrite(*conn, 0, SYNCHRONOUS), CreateMockWrite(*rst, 3),
};
scoped_ptr<SpdyFrame> resp(ConstructConnectReplyFrame());
scoped_ptr<SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
MockRead reads[] = {
- CreateMockRead(*resp, 1, ASYNC),
- CreateMockRead(*msg1, 2, ASYNC),
- MockRead(ASYNC, 0, 3), // EOF
+ CreateMockRead(*resp, 1, ASYNC), CreateMockRead(*msg1, 2, ASYNC),
+ MockRead(ASYNC, 0, 4), // EOF
};
Initialize(reads, arraysize(reads), writes, arraysize(writes));
@@ -1360,6 +1302,9 @@ TEST_P(SpdyProxyClientSocketTest, NetLog) {
NetLog::TYPE_SOCKET_BYTES_RECEIVED,
NetLog::PHASE_NONE));
EXPECT_TRUE(LogContainsEndEvent(entry_list, 9, NetLog::TYPE_SOCKET_ALIVE));
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
// CompletionCallback that causes the SpdyProxyClientSocket to be
@@ -1431,6 +1376,9 @@ TEST_P(SpdyProxyClientSocketTest, RstWithReadAndWritePendingDelete) {
EXPECT_FALSE(sock_.get());
EXPECT_TRUE(read_callback.have_result());
EXPECT_FALSE(write_callback_.have_result());
+
+ // Let the RST_STREAM write while |rst| is in-scope.
+ base::MessageLoop::current()->RunUntilIdle();
}
} // namespace net
diff --git a/chromium/net/spdy/spdy_session.cc b/chromium/net/spdy/spdy_session.cc
index 9508f0e6af4..0310564a7e9 100644
--- a/chromium/net/spdy/spdy_session.cc
+++ b/chromium/net/spdy/spdy_session.cc
@@ -29,8 +29,10 @@
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/cert/asn1_util.h"
+#include "net/http/http_log_util.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties.h"
+#include "net/http/http_util.h"
#include "net/spdy/spdy_buffer_producer.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_http_utils.h"
@@ -38,6 +40,8 @@
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "net/ssl/server_bound_cert_service.h"
+#include "net/ssl/ssl_cipher_suite_names.h"
+#include "net/ssl/ssl_connection_status_flags.h"
namespace net {
@@ -47,20 +51,18 @@ const int kReadBufferSize = 8 * 1024;
const int kDefaultConnectionAtRiskOfLossSeconds = 10;
const int kHungIntervalSeconds = 10;
-// Always start at 1 for the first stream id.
-const SpdyStreamId kFirstStreamId = 1;
-
// Minimum seconds that unclaimed pushed streams will be kept in memory.
const int kMinPushedStreamLifetimeSeconds = 300;
scoped_ptr<base::ListValue> SpdyHeaderBlockToListValue(
- const SpdyHeaderBlock& headers) {
+ const SpdyHeaderBlock& headers,
+ net::NetLog::LogLevel log_level) {
scoped_ptr<base::ListValue> headers_list(new base::ListValue());
for (SpdyHeaderBlock::const_iterator it = headers.begin();
it != headers.end(); ++it) {
headers_list->AppendString(
it->first + ": " +
- (ShouldShowHttpHeaderValue(it->first) ? it->second : "[elided]"));
+ ElideHeaderValueForNetLog(log_level, it->first, it->second));
}
return headers_list.Pass();
}
@@ -70,9 +72,10 @@ base::Value* NetLogSpdySynStreamSentCallback(const SpdyHeaderBlock* headers,
bool unidirectional,
SpdyPriority spdy_priority,
SpdyStreamId stream_id,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetBoolean("unidirectional", unidirectional);
dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority));
@@ -87,9 +90,10 @@ base::Value* NetLogSpdySynStreamReceivedCallback(
SpdyPriority spdy_priority,
SpdyStreamId stream_id,
SpdyStreamId associated_stream,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetBoolean("unidirectional", unidirectional);
dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority));
@@ -102,9 +106,10 @@ base::Value* NetLogSpdySynReplyOrHeadersReceivedCallback(
const SpdyHeaderBlock* headers,
bool fin,
SpdyStreamId stream_id,
- NetLog::LogLevel /* log_level */) {
+ NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release());
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
dict->SetBoolean("fin", fin);
dict->SetInteger("stream_id", stream_id);
return dict;
@@ -205,12 +210,14 @@ base::Value* NetLogSpdyRstCallback(SpdyStreamId stream_id,
return dict;
}
-base::Value* NetLogSpdyPingCallback(uint32 unique_id,
+base::Value* NetLogSpdyPingCallback(SpdyPingId unique_id,
+ bool is_ack,
const char* type,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("unique_id", unique_id);
dict->SetString("type", type);
+ dict->SetBoolean("is_ack", is_ack);
return dict;
}
@@ -228,6 +235,19 @@ base::Value* NetLogSpdyGoAwayCallback(SpdyStreamId last_stream_id,
return dict;
}
+base::Value* NetLogSpdyPushPromiseReceivedCallback(
+ const SpdyHeaderBlock* headers,
+ SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
+ dict->SetInteger("id", stream_id);
+ dict->SetInteger("promised_stream_id", promised_stream_id);
+ return dict;
+}
+
// Helper function to return the total size of an array of objects
// with .size() member functions.
template <typename T, size_t N> size_t GetTotalSize(const T (&arr)[N]) {
@@ -259,6 +279,151 @@ const size_t kMaxConcurrentStreamLimit = 256;
} // namespace
+SpdyProtocolErrorDetails MapFramerErrorToProtocolError(
+ SpdyFramer::SpdyError err) {
+ switch(err) {
+ case SpdyFramer::SPDY_NO_ERROR:
+ return SPDY_ERROR_NO_ERROR;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME;
+ case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+ return SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE;
+ case SpdyFramer::SPDY_ZLIB_INIT_FAILURE:
+ return SPDY_ERROR_ZLIB_INIT_FAILURE;
+ case SpdyFramer::SPDY_UNSUPPORTED_VERSION:
+ return SPDY_ERROR_UNSUPPORTED_VERSION;
+ case SpdyFramer::SPDY_DECOMPRESS_FAILURE:
+ return SPDY_ERROR_DECOMPRESS_FAILURE;
+ case SpdyFramer::SPDY_COMPRESS_FAILURE:
+ return SPDY_ERROR_COMPRESS_FAILURE;
+ case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT:
+ return SPDY_ERROR_GOAWAY_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT:
+ return SPDY_ERROR_RST_STREAM_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_DATA_FRAME_FLAGS;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS;
+ case SpdyFramer::SPDY_UNEXPECTED_FRAME:
+ return SPDY_ERROR_UNEXPECTED_FRAME;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
+Error MapFramerErrorToNetError(SpdyFramer::SpdyError err) {
+ switch (err) {
+ case SpdyFramer::SPDY_NO_ERROR:
+ return OK;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+ return ERR_SPDY_FRAME_SIZE_ERROR;
+ case SpdyFramer::SPDY_ZLIB_INIT_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_UNSUPPORTED_VERSION:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_DECOMPRESS_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_COMPRESS_FAILURE:
+ return ERR_SPDY_COMPRESSION_ERROR;
+ case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ case SpdyFramer::SPDY_UNEXPECTED_FRAME:
+ return ERR_SPDY_PROTOCOL_ERROR;
+ default:
+ NOTREACHED();
+ return ERR_SPDY_PROTOCOL_ERROR;
+ }
+}
+
+SpdyProtocolErrorDetails MapRstStreamStatusToProtocolError(
+ SpdyRstStreamStatus status) {
+ switch(status) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ return STATUS_CODE_PROTOCOL_ERROR;
+ case RST_STREAM_INVALID_STREAM:
+ return STATUS_CODE_INVALID_STREAM;
+ case RST_STREAM_REFUSED_STREAM:
+ return STATUS_CODE_REFUSED_STREAM;
+ case RST_STREAM_UNSUPPORTED_VERSION:
+ return STATUS_CODE_UNSUPPORTED_VERSION;
+ case RST_STREAM_CANCEL:
+ return STATUS_CODE_CANCEL;
+ case RST_STREAM_INTERNAL_ERROR:
+ return STATUS_CODE_INTERNAL_ERROR;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ return STATUS_CODE_FLOW_CONTROL_ERROR;
+ case RST_STREAM_STREAM_IN_USE:
+ return STATUS_CODE_STREAM_IN_USE;
+ case RST_STREAM_STREAM_ALREADY_CLOSED:
+ return STATUS_CODE_STREAM_ALREADY_CLOSED;
+ case RST_STREAM_INVALID_CREDENTIALS:
+ return STATUS_CODE_INVALID_CREDENTIALS;
+ case RST_STREAM_FRAME_SIZE_ERROR:
+ return STATUS_CODE_FRAME_SIZE_ERROR;
+ case RST_STREAM_SETTINGS_TIMEOUT:
+ return STATUS_CODE_SETTINGS_TIMEOUT;
+ case RST_STREAM_CONNECT_ERROR:
+ return STATUS_CODE_CONNECT_ERROR;
+ case RST_STREAM_ENHANCE_YOUR_CALM:
+ return STATUS_CODE_ENHANCE_YOUR_CALM;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
+SpdyGoAwayStatus MapNetErrorToGoAwayStatus(Error err) {
+ switch (err) {
+ case OK:
+ return GOAWAY_NO_ERROR;
+ case ERR_SPDY_PROTOCOL_ERROR:
+ return GOAWAY_PROTOCOL_ERROR;
+ case ERR_SPDY_FLOW_CONTROL_ERROR:
+ return GOAWAY_FLOW_CONTROL_ERROR;
+ case ERR_SPDY_FRAME_SIZE_ERROR:
+ return GOAWAY_FRAME_SIZE_ERROR;
+ case ERR_SPDY_COMPRESSION_ERROR:
+ return GOAWAY_COMPRESSION_ERROR;
+ case ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY:
+ return GOAWAY_INADEQUATE_SECURITY;
+ default:
+ return GOAWAY_PROTOCOL_ERROR;
+ }
+}
+
+void SplitPushedHeadersToRequestAndResponse(const SpdyHeaderBlock& headers,
+ SpdyMajorVersion protocol_version,
+ SpdyHeaderBlock* request_headers,
+ SpdyHeaderBlock* response_headers) {
+ DCHECK(response_headers);
+ DCHECK(request_headers);
+ for (SpdyHeaderBlock::const_iterator it = headers.begin();
+ it != headers.end();
+ ++it) {
+ SpdyHeaderBlock* to_insert = response_headers;
+ if (protocol_version == SPDY2) {
+ if (it->first == "url")
+ to_insert = request_headers;
+ } else {
+ const char* host = protocol_version >= SPDY4 ? ":authority" : ":host";
+ static const char* scheme = ":scheme";
+ static const char* path = ":path";
+ if (it->first == host || it->first == scheme || it->first == path)
+ to_insert = request_headers;
+ }
+ to_insert->insert(*it);
+ }
+}
+
SpdyStreamRequest::SpdyStreamRequest() : weak_ptr_factory_(this) {
Reset();
}
@@ -349,7 +514,8 @@ SpdySession::ActiveStreamInfo::ActiveStreamInfo()
SpdySession::ActiveStreamInfo::ActiveStreamInfo(SpdyStream* stream)
: stream(stream),
- waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {}
+ waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {
+}
SpdySession::ActiveStreamInfo::~ActiveStreamInfo() {}
@@ -377,8 +543,7 @@ SpdySession::SpdySession(
TimeFunc time_func,
const HostPortPair& trusted_spdy_proxy,
NetLog* net_log)
- : weak_factory_(this),
- in_io_loop_(false),
+ : in_io_loop_(false),
spdy_session_key_(spdy_session_key),
pool_(NULL),
http_server_properties_(http_server_properties),
@@ -392,12 +557,12 @@ SpdySession::SpdySession(
read_state_(READ_STATE_DO_READ),
write_state_(WRITE_STATE_IDLE),
error_on_close_(OK),
- max_concurrent_streams_(initial_max_concurrent_streams == 0 ?
- kInitialMaxConcurrentStreams :
- initial_max_concurrent_streams),
- max_concurrent_streams_limit_(max_concurrent_streams_limit == 0 ?
- kMaxConcurrentStreamLimit :
- max_concurrent_streams_limit),
+ max_concurrent_streams_(initial_max_concurrent_streams == 0
+ ? kInitialMaxConcurrentStreams
+ : initial_max_concurrent_streams),
+ max_concurrent_streams_limit_(max_concurrent_streams_limit == 0
+ ? kMaxConcurrentStreamLimit
+ : max_concurrent_streams_limit),
streams_initiated_count_(0),
streams_pushed_count_(0),
streams_pushed_and_claimed_count_(0),
@@ -414,9 +579,9 @@ SpdySession::SpdySession(
send_connection_header_prefix_(false),
flow_control_state_(FLOW_CONTROL_NONE),
stream_initial_send_window_size_(kSpdyStreamInitialWindowSize),
- stream_initial_recv_window_size_(stream_initial_recv_window_size == 0 ?
- kDefaultInitialRecvWindowSize :
- stream_initial_recv_window_size),
+ stream_initial_recv_window_size_(stream_initial_recv_window_size == 0
+ ? kDefaultInitialRecvWindowSize
+ : stream_initial_recv_window_size),
session_send_window_size_(0),
session_recv_window_size_(0),
session_unacked_recv_window_bytes_(0),
@@ -429,10 +594,10 @@ SpdySession::SpdySession(
protocol_(default_protocol),
connection_at_risk_of_loss_time_(
base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds)),
- hung_interval_(
- base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
+ hung_interval_(base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
trusted_spdy_proxy_(trusted_spdy_proxy),
- time_func_(time_func) {
+ time_func_(time_func),
+ weak_factory_(this) {
DCHECK_GE(protocol_, kProtoSPDYMinimumVersion);
DCHECK_LE(protocol_, kProtoSPDYMaximumVersion);
DCHECK(HttpStreamFactory::spdy_enabled());
@@ -446,8 +611,7 @@ SpdySession::SpdySession(
SpdySession::~SpdySession() {
CHECK(!in_io_loop_);
- DCHECK(!pool_);
- DcheckClosed();
+ DcheckDraining();
// TODO(akalin): Check connection->is_initialized() instead. This
// requires re-working CreateFakeSpdySession(), though.
@@ -460,7 +624,7 @@ SpdySession::~SpdySession() {
net_log_.EndEvent(NetLog::TYPE_SPDY_SESSION);
}
-Error SpdySession::InitializeWithSocket(
+void SpdySession::InitializeWithSocket(
scoped_ptr<ClientSocketHandle> connection,
SpdySessionPool* pool,
bool is_secure,
@@ -492,7 +656,7 @@ Error SpdySession::InitializeWithSocket(
DCHECK_GE(protocol_, kProtoSPDYMinimumVersion);
DCHECK_LE(protocol_, kProtoSPDYMaximumVersion);
- if (protocol_ == kProtoHTTP2Draft04)
+ if (protocol_ == kProtoSPDY4)
send_connection_header_prefix_ = true;
if (protocol_ >= kProtoSPDY31) {
@@ -521,26 +685,24 @@ Error SpdySession::InitializeWithSocket(
NetLog::TYPE_SPDY_SESSION_INITIALIZED,
connection_->socket()->NetLog().source().ToEventParametersCallback());
- int error = DoReadLoop(READ_STATE_DO_READ, OK);
- if (error == ERR_IO_PENDING)
- error = OK;
- if (error == OK) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
- connection_->AddHigherLayeredPool(this);
- if (enable_sending_initial_data_)
- SendInitialData();
- pool_ = pool;
- } else {
- DcheckClosed();
- }
- return static_cast<Error>(error);
+ DCHECK_EQ(availability_state_, STATE_AVAILABLE);
+ connection_->AddHigherLayeredPool(this);
+ if (enable_sending_initial_data_)
+ SendInitialData();
+ pool_ = pool;
+
+ // Bootstrap the read loop.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SpdySession::PumpReadLoop,
+ weak_factory_.GetWeakPtr(), READ_STATE_DO_READ, OK));
}
bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
if (!verify_domain_authentication_)
return true;
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return false;
SSLInfo ssl_info;
@@ -549,6 +711,10 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
if (!GetSSLInfo(&ssl_info, &was_npn_negotiated, &protocol_negotiated))
return true; // This is not a secure session, so all domains are okay.
+ // Disable pooling for secure sessions.
+ // TODO(rch): re-enable this.
+ return false;
+#if 0
bool unused = false;
return
!ssl_info.client_cert_sent &&
@@ -556,6 +722,7 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
(ServerBoundCertService::GetDomainForHost(domain) ==
ServerBoundCertService::GetDomainForHost(host_port_pair().host()))) &&
ssl_info.cert->VerifyNameMatch(domain, &unused);
+#endif
}
int SpdySession::GetPushStream(
@@ -566,8 +733,7 @@ int SpdySession::GetPushStream(
stream->reset();
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(url);
@@ -587,17 +753,14 @@ int SpdySession::GetPushStream(
// another being closed due to received data.
Error SpdySession::TryAccessStream(const GURL& url) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
-
if (is_secure_ && certificate_error_code_ != OK &&
(url.SchemeIs("https") || url.SchemeIs("wss"))) {
RecordProtocolErrorHistogram(
PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
static_cast<Error>(certificate_error_code_),
"Tried to get SPDY stream for secure content over an unauthenticated "
"session.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
return ERR_SPDY_PROTOCOL_ERROR;
}
return OK;
@@ -611,8 +774,7 @@ int SpdySession::TryCreateStream(
if (availability_state_ == STATE_GOING_AWAY)
return ERR_FAILED;
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(request->url());
@@ -642,8 +804,7 @@ int SpdySession::CreateStream(const SpdyStreamRequest& request,
if (availability_state_ == STATE_GOING_AWAY)
return ERR_FAILED;
- // TODO(akalin): Add unit test exercising this code path.
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return ERR_CONNECTION_CLOSED;
Error err = TryAccessStream(request.url());
@@ -659,10 +820,9 @@ int SpdySession::CreateStream(const SpdyStreamRequest& request,
UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected",
connection_->socket()->IsConnected());
if (!connection_->socket()->IsConnected()) {
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_CONNECTION_CLOSED,
"Tried to create SPDY stream for a closed socket connection.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
return ERR_CONNECTION_CLOSED;
}
}
@@ -690,17 +850,17 @@ void SpdySession::CancelStreamRequest(
CHECK_GE(priority, MINIMUM_PRIORITY);
CHECK_LE(priority, MAXIMUM_PRIORITY);
- if (DCHECK_IS_ON()) {
- // |request| should not be in a queue not matching its priority.
- for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
- if (priority == i)
- continue;
- PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i];
- DCHECK(std::find_if(queue->begin(),
- queue->end(),
- RequestEquals(request)) == queue->end());
- }
+#if DCHECK_IS_ON
+ // |request| should not be in a queue not matching its priority.
+ for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
+ if (priority == i)
+ continue;
+ PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i];
+ DCHECK(std::find_if(queue->begin(),
+ queue->end(),
+ RequestEquals(request)) == queue->end());
}
+#endif
PendingStreamRequestQueue* queue =
&pending_create_stream_queues_[priority];
@@ -749,6 +909,9 @@ void SpdySession::ProcessPendingStreamRequests() {
if (!pending_request)
break;
+ // Note that this post can race with other stream creations, and it's
+ // possible that the un-stalled stream will be stalled again if it loses.
+ // TODO(jgraettinger): Provide stronger ordering guarantees.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SpdySession::CompleteStreamRequest,
@@ -766,23 +929,46 @@ SpdyMajorVersion SpdySession::GetProtocolVersion() const {
return buffered_spdy_framer_->protocol_version();
}
+bool SpdySession::HasAcceptableTransportSecurity() const {
+ // If we're not even using TLS, we have no standards to meet.
+ if (!is_secure_) {
+ return true;
+ }
+
+ // We don't enforce transport security standards for older SPDY versions.
+ if (GetProtocolVersion() < SPDY4) {
+ return true;
+ }
+
+ SSLInfo ssl_info;
+ CHECK(connection_->socket()->GetSSLInfo(&ssl_info));
+
+ // HTTP/2 requires TLS 1.2+
+ if (SSLConnectionStatusToVersion(ssl_info.connection_status) <
+ SSL_CONNECTION_VERSION_TLS1_2) {
+ return false;
+ }
+
+ if (!IsSecureTLSCipherSuite(
+ SSLConnectionStatusToCipherSuite(ssl_info.connection_status))) {
+ return false;
+ }
+
+ return true;
+}
+
base::WeakPtr<SpdySession> SpdySession::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
bool SpdySession::CloseOneIdleConnection() {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK(pool_);
- if (!active_streams_.empty())
- return false;
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Closing one idle connection.");
- if (result != SESSION_CLOSED_AND_REMOVED) {
- NOTREACHED();
- return false;
+ if (active_streams_.empty()) {
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
}
- return true;
+ // Return false as the socket wasn't immediately closed.
+ return false;
}
void SpdySession::EnqueueStreamWrite(
@@ -799,7 +985,6 @@ void SpdySession::EnqueueStreamWrite(
scoped_ptr<SpdyFrame> SpdySession::CreateSynStream(
SpdyStreamId stream_id,
RequestPriority priority,
- uint8 credential_slot,
SpdyControlFlags flags,
const SpdyHeaderBlock& headers) {
ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
@@ -812,15 +997,14 @@ scoped_ptr<SpdyFrame> SpdySession::CreateSynStream(
SpdyPriority spdy_priority =
ConvertRequestPriorityToSpdyPriority(priority, GetProtocolVersion());
scoped_ptr<SpdyFrame> syn_frame(
- buffered_spdy_framer_->CreateSynStream(
- stream_id, 0, spdy_priority,
- credential_slot, flags, &headers));
+ buffered_spdy_framer_->CreateSynStream(stream_id, 0, spdy_priority, flags,
+ &headers));
base::StatsCounter spdy_requests("spdy.requests");
spdy_requests.Increment();
streams_initiated_count_++;
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SYN_STREAM,
base::Bind(&NetLogSpdySynStreamSentCallback, &headers,
@@ -837,8 +1021,7 @@ scoped_ptr<SpdyBuffer> SpdySession::CreateDataBuffer(SpdyStreamId stream_id,
IOBuffer* data,
int len,
SpdyDataFlags flags) {
- if (availability_state_ == STATE_CLOSED) {
- NOTREACHED();
+ if (availability_state_ == STATE_DRAINING) {
return scoped_ptr<SpdyBuffer>();
}
@@ -929,7 +1112,7 @@ scoped_ptr<SpdyBuffer> SpdySession::CreateDataBuffer(SpdyStreamId stream_id,
if (effective_len < len)
flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_FIN);
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SEND_DATA,
base::Bind(&NetLogSpdyDataCallback, stream_id, effective_len,
@@ -1027,22 +1210,13 @@ void SpdySession::CloseActiveStreamIterator(ActiveStreamMap::iterator it,
if (owned_stream->type() == SPDY_PUSH_STREAM)
unclaimed_pushed_streams_.erase(owned_stream->url());
- base::WeakPtr<SpdySession> weak_this = GetWeakPtr();
-
DeleteStream(owned_stream.Pass(), status);
-
- if (!weak_this)
- return;
-
- if (availability_state_ == STATE_CLOSED)
- return;
+ MaybeFinishGoingAway();
// If there are no active streams and the socket pool is stalled, close the
// session to free up a socket slot.
if (active_streams_.empty() && connection_->IsPoolStalled()) {
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
}
}
@@ -1082,42 +1256,31 @@ void SpdySession::EnqueueResetStreamFrame(SpdyStreamId stream_id,
buffered_spdy_framer_->CreateRstStream(stream_id, status));
EnqueueSessionWrite(priority, RST_STREAM, rst_frame.Pass());
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(status + STATUS_CODE_INVALID));
+ RecordProtocolErrorHistogram(MapRstStreamStatusToProtocolError(status));
}
void SpdySession::PumpReadLoop(ReadState expected_read_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
- DCHECK_EQ(read_state_, expected_read_state);
-
- result = DoReadLoop(expected_read_state, result);
-
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- RemoveFromPool();
+ if (availability_state_ == STATE_DRAINING) {
return;
}
-
- DCHECK(result == OK || result == ERR_IO_PENDING);
+ ignore_result(DoReadLoop(expected_read_state, result));
}
int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
- DCHECK_EQ(read_state_, expected_read_state);
+ CHECK_EQ(read_state_, expected_read_state);
in_io_loop_ = true;
int bytes_read_without_yielding = 0;
- // Loop until the session is closed, the read becomes blocked, or
+ // Loop until the session is draining, the read becomes blocked, or
// the read limit is exceeded.
while (true) {
switch (read_state_) {
case READ_STATE_DO_READ:
- DCHECK_EQ(result, OK);
+ CHECK_EQ(result, OK);
result = DoRead();
break;
case READ_STATE_DO_READ_COMPLETE:
@@ -1130,11 +1293,8 @@ int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
break;
}
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(result, ERR_IO_PENDING);
+ if (availability_state_ == STATE_DRAINING)
break;
- }
if (result == ERR_IO_PENDING)
break;
@@ -1158,7 +1318,6 @@ int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
int SpdySession::DoRead() {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
CHECK(connection_);
CHECK(connection_->socket());
@@ -1172,7 +1331,6 @@ int SpdySession::DoRead() {
int SpdySession::DoReadComplete(int result) {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
// Parse a frame. For now this code requires that the frame fit into our
// buffer (kReadBufferSize).
@@ -1181,23 +1339,16 @@ int SpdySession::DoReadComplete(int result) {
if (result == 0) {
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySession.BytesRead.EOF",
total_bytes_received_, 1, 100000000, 50);
- CloseSessionResult close_session_result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Connection closed");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, ERR_CONNECTION_CLOSED);
+ DoDrainSession(ERR_CONNECTION_CLOSED, "Connection closed");
+
return ERR_CONNECTION_CLOSED;
}
if (result < 0) {
- CloseSessionResult close_session_result =
- DoCloseSession(static_cast<Error>(result), "result is < 0.");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, result);
+ DoDrainSession(static_cast<Error>(result), "result is < 0.");
return result;
}
-
+ CHECK_LE(result, kReadBufferSize);
total_bytes_received_ += result;
last_activity_time_ = time_func_();
@@ -1209,9 +1360,8 @@ int SpdySession::DoReadComplete(int result) {
result -= bytes_processed;
data += bytes_processed;
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- return error_on_close_;
+ if (availability_state_ == STATE_DRAINING) {
+ return ERR_CONNECTION_CLOSED;
}
DCHECK_EQ(buffered_spdy_framer_->error_code(), SpdyFramer::SPDY_NO_ERROR);
@@ -1223,24 +1373,19 @@ int SpdySession::DoReadComplete(int result) {
void SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(write_state_, expected_write_state);
- result = DoWriteLoop(expected_write_state, result);
+ DoWriteLoop(expected_write_state, result);
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
- RemoveFromPool();
+ if (availability_state_ == STATE_DRAINING && !in_flight_write_ &&
+ write_queue_.IsEmpty()) {
+ pool_->RemoveUnavailableSession(GetWeakPtr()); // Destroys |this|.
return;
}
-
- DCHECK(result == OK || result == ERR_IO_PENDING);
}
int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_NE(write_state_, WRITE_STATE_IDLE);
DCHECK_EQ(write_state_, expected_write_state);
@@ -1262,12 +1407,6 @@ int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
break;
}
- if (availability_state_ == STATE_CLOSED) {
- DCHECK_EQ(result, error_on_close_);
- DCHECK_LT(result, ERR_IO_PENDING);
- break;
- }
-
if (write_state_ == WRITE_STATE_IDLE) {
DCHECK_EQ(result, ERR_IO_PENDING);
break;
@@ -1285,7 +1424,6 @@ int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
int SpdySession::DoWrite() {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK(buffered_spdy_framer_);
if (in_flight_write_) {
@@ -1301,18 +1439,23 @@ int SpdySession::DoWrite() {
}
if (stream.get())
- DCHECK(!stream->IsClosed());
+ CHECK(!stream->IsClosed());
// Activate the stream only when sending the SYN_STREAM frame to
// guarantee monotonically-increasing stream IDs.
if (frame_type == SYN_STREAM) {
- if (stream.get() && stream->stream_id() == 0) {
- scoped_ptr<SpdyStream> owned_stream =
- ActivateCreatedStream(stream.get());
- InsertActivatedStream(owned_stream.Pass());
- } else {
- NOTREACHED();
- return ERR_UNEXPECTED;
+ CHECK(stream.get());
+ CHECK_EQ(stream->stream_id(), 0u);
+ scoped_ptr<SpdyStream> owned_stream =
+ ActivateCreatedStream(stream.get());
+ InsertActivatedStream(owned_stream.Pass());
+
+ if (stream_hi_water_mark_ > kLastStreamId) {
+ CHECK_EQ(stream->stream_id(), kLastStreamId);
+ // We've exhausted the stream ID space, and no new streams may be
+ // created after this one.
+ MakeUnavailable();
+ StartGoingAway(kLastStreamId, ERR_ABORTED);
}
}
@@ -1344,7 +1487,6 @@ int SpdySession::DoWrite() {
int SpdySession::DoWriteComplete(int result) {
CHECK(in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_NE(result, ERR_IO_PENDING);
DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
@@ -1356,12 +1498,9 @@ int SpdySession::DoWriteComplete(int result) {
in_flight_write_frame_type_ = DATA;
in_flight_write_frame_size_ = 0;
in_flight_write_stream_.reset();
- CloseSessionResult close_session_result =
- DoCloseSession(static_cast<Error>(result), "Write error");
- DCHECK_EQ(close_session_result, SESSION_CLOSED_BUT_NOT_REMOVED);
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_EQ(error_on_close_, result);
- return result;
+ write_state_ = WRITE_STATE_DO_WRITE;
+ DoDrainSession(static_cast<Error>(result), "Write error");
+ return OK;
}
// It should not be possible to have written more bytes than our
@@ -1396,22 +1535,20 @@ int SpdySession::DoWriteComplete(int result) {
}
void SpdySession::DcheckGoingAway() const {
+#if DCHECK_IS_ON
DCHECK_GE(availability_state_, STATE_GOING_AWAY);
- if (DCHECK_IS_ON()) {
- for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
- DCHECK(pending_create_stream_queues_[i].empty());
- }
+ for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
+ DCHECK(pending_create_stream_queues_[i].empty());
}
DCHECK(created_streams_.empty());
+#endif
}
-void SpdySession::DcheckClosed() const {
+void SpdySession::DcheckDraining() const {
DcheckGoingAway();
- DCHECK_EQ(availability_state_, STATE_CLOSED);
- DCHECK_LT(error_on_close_, ERR_IO_PENDING);
+ DCHECK_EQ(availability_state_, STATE_DRAINING);
DCHECK(active_streams_.empty());
DCHECK(unclaimed_pushed_streams_.empty());
- DCHECK(write_queue_.IsEmpty());
}
void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id,
@@ -1461,21 +1598,39 @@ void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id,
}
void SpdySession::MaybeFinishGoingAway() {
- DcheckGoingAway();
- if (active_streams_.empty() && availability_state_ != STATE_CLOSED) {
- CloseSessionResult result =
- DoCloseSession(ERR_CONNECTION_CLOSED, "Finished going away");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ if (active_streams_.empty() && availability_state_ == STATE_GOING_AWAY) {
+ DoDrainSession(OK, "Finished going away");
}
}
-SpdySession::CloseSessionResult SpdySession::DoCloseSession(
- Error err,
- const std::string& description) {
- DCHECK_LT(err, ERR_IO_PENDING);
-
- if (availability_state_ == STATE_CLOSED)
- return SESSION_ALREADY_CLOSED;
+void SpdySession::DoDrainSession(Error err, const std::string& description) {
+ if (availability_state_ == STATE_DRAINING) {
+ return;
+ }
+ MakeUnavailable();
+
+ // If |err| indicates an error occurred, inform the peer that we're closing
+ // and why. Don't GOAWAY on a graceful or idle close, as that may
+ // unnecessarily wake the radio. We could technically GOAWAY on network errors
+ // (we'll probably fail to actually write it, but that's okay), however many
+ // unit-tests would need to be updated.
+ if (err != OK &&
+ err != ERR_ABORTED && // Used by SpdySessionPool to close idle sessions.
+ err != ERR_NETWORK_CHANGED && // Used to deprecate sessions on IP change.
+ err != ERR_SOCKET_NOT_CONNECTED &&
+ err != ERR_CONNECTION_CLOSED && err != ERR_CONNECTION_RESET) {
+ // Enqueue a GOAWAY to inform the peer of why we're closing the connection.
+ SpdyGoAwayIR goaway_ir(0, // Last accepted stream ID.
+ MapNetErrorToGoAwayStatus(err),
+ description);
+ EnqueueSessionWrite(HIGHEST,
+ GOAWAY,
+ scoped_ptr<SpdyFrame>(
+ buffered_spdy_framer_->SerializeFrame(goaway_ir)));
+ }
+
+ availability_state_ = STATE_DRAINING;
+ error_on_close_ = err;
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_CLOSE,
@@ -1485,33 +1640,14 @@ SpdySession::CloseSessionResult SpdySession::DoCloseSession(
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySession.BytesRead.OtherErrors",
total_bytes_received_, 1, 100000000, 50);
- // |pool_| will be NULL when |InitializeWithSocket()| is in the
- // call stack.
- if (pool_ && availability_state_ != STATE_GOING_AWAY)
- pool_->MakeSessionUnavailable(GetWeakPtr());
-
- availability_state_ = STATE_CLOSED;
- error_on_close_ = err;
-
- StartGoingAway(0, err);
- write_queue_.Clear();
-
- DcheckClosed();
-
- if (in_io_loop_)
- return SESSION_CLOSED_BUT_NOT_REMOVED;
-
- RemoveFromPool();
- return SESSION_CLOSED_AND_REMOVED;
-}
-
-void SpdySession::RemoveFromPool() {
- DcheckClosed();
- CHECK(pool_);
-
- SpdySessionPool* pool = pool_;
- pool_ = NULL;
- pool->RemoveUnavailableSession(GetWeakPtr());
+ if (err == OK) {
+ // We ought to be going away already, as this is a graceful close.
+ DcheckGoingAway();
+ } else {
+ StartGoingAway(0, err);
+ }
+ DcheckDraining();
+ MaybePostWriteLoop();
}
void SpdySession::LogAbandonedStream(SpdyStream* stream, Error status) {
@@ -1541,19 +1677,24 @@ void SpdySession::LogAbandonedActiveStream(ActiveStreamMap::const_iterator it,
}
}
-int SpdySession::GetNewStreamId() {
- int id = stream_hi_water_mark_;
+SpdyStreamId SpdySession::GetNewStreamId() {
+ CHECK_LE(stream_hi_water_mark_, kLastStreamId);
+ SpdyStreamId id = stream_hi_water_mark_;
stream_hi_water_mark_ += 2;
- if (stream_hi_water_mark_ > 0x7fff)
- stream_hi_water_mark_ = 1;
return id;
}
void SpdySession::CloseSessionOnError(Error err,
const std::string& description) {
- // We may be called from anywhere, so we can't expect a particular
- // return value.
- ignore_result(DoCloseSession(err, description));
+ DCHECK_LT(err, ERR_IO_PENDING);
+ DoDrainSession(err, description);
+}
+
+void SpdySession::MakeUnavailable() {
+ if (availability_state_ == STATE_AVAILABLE) {
+ availability_state_ = STATE_GOING_AWAY;
+ pool_->MakeSessionUnavailable(GetWeakPtr());
+ }
}
base::Value* SpdySession::GetInfoAsValue() const {
@@ -1607,7 +1748,8 @@ base::Value* SpdySession::GetInfoAsValue() const {
}
bool SpdySession::IsReused() const {
- return buffered_spdy_framer_->frames_received() > 0;
+ return buffered_spdy_framer_->frames_received() > 0 ||
+ connection_->reuse_type() == ClientSocketHandle::UNUSED_IDLE;
}
bool SpdySession::GetLoadTimingInfo(SpdyStreamId stream_id,
@@ -1643,10 +1785,9 @@ int SpdySession::GetLocalAddress(IPEndPoint* address) const {
void SpdySession::EnqueueSessionWrite(RequestPriority priority,
SpdyFrameType frame_type,
scoped_ptr<SpdyFrame> frame) {
- DCHECK(frame_type == RST_STREAM ||
- frame_type == SETTINGS ||
- frame_type == WINDOW_UPDATE ||
- frame_type == PING);
+ DCHECK(frame_type == RST_STREAM || frame_type == SETTINGS ||
+ frame_type == WINDOW_UPDATE || frame_type == PING ||
+ frame_type == GOAWAY);
EnqueueWrite(
priority, frame_type,
scoped_ptr<SpdyBufferProducer>(
@@ -1659,14 +1800,16 @@ void SpdySession::EnqueueWrite(RequestPriority priority,
SpdyFrameType frame_type,
scoped_ptr<SpdyBufferProducer> producer,
const base::WeakPtr<SpdyStream>& stream) {
- if (availability_state_ == STATE_CLOSED)
+ if (availability_state_ == STATE_DRAINING)
return;
- bool was_idle = write_queue_.IsEmpty();
write_queue_.Enqueue(priority, frame_type, producer.Pass(), stream);
+ MaybePostWriteLoop();
+}
+
+void SpdySession::MaybePostWriteLoop() {
if (write_state_ == WRITE_STATE_IDLE) {
- DCHECK(was_idle);
- DCHECK(!in_flight_write_);
+ CHECK(!in_flight_write_);
write_state_ = WRITE_STATE_DO_WRITE;
base::MessageLoop::current()->PostTask(
FROM_HERE,
@@ -1676,14 +1819,14 @@ void SpdySession::EnqueueWrite(RequestPriority priority,
}
void SpdySession::InsertCreatedStream(scoped_ptr<SpdyStream> stream) {
- DCHECK_EQ(stream->stream_id(), 0u);
- DCHECK(created_streams_.find(stream.get()) == created_streams_.end());
+ CHECK_EQ(stream->stream_id(), 0u);
+ CHECK(created_streams_.find(stream.get()) == created_streams_.end());
created_streams_.insert(stream.release());
}
scoped_ptr<SpdyStream> SpdySession::ActivateCreatedStream(SpdyStream* stream) {
- DCHECK_EQ(stream->stream_id(), 0u);
- DCHECK(created_streams_.find(stream) != created_streams_.end());
+ CHECK_EQ(stream->stream_id(), 0u);
+ CHECK(created_streams_.find(stream) != created_streams_.end());
stream->set_stream_id(GetNewStreamId());
scoped_ptr<SpdyStream> owned_stream(stream);
created_streams_.erase(stream);
@@ -1692,15 +1835,12 @@ scoped_ptr<SpdyStream> SpdySession::ActivateCreatedStream(SpdyStream* stream) {
void SpdySession::InsertActivatedStream(scoped_ptr<SpdyStream> stream) {
SpdyStreamId stream_id = stream->stream_id();
- DCHECK_NE(stream_id, 0u);
+ CHECK_NE(stream_id, 0u);
std::pair<ActiveStreamMap::iterator, bool> result =
active_streams_.insert(
std::make_pair(stream_id, ActiveStreamInfo(stream.get())));
- if (result.second) {
- ignore_result(stream.release());
- } else {
- NOTREACHED();
- }
+ CHECK(result.second);
+ ignore_result(stream.release());
}
void SpdySession::DeleteStream(scoped_ptr<SpdyStream> stream, int status) {
@@ -1713,26 +1853,10 @@ void SpdySession::DeleteStream(scoped_ptr<SpdyStream> stream, int status) {
}
write_queue_.RemovePendingWritesForStream(stream->GetWeakPtr());
-
- // |stream->OnClose()| may end up closing |this|, so detect that.
- base::WeakPtr<SpdySession> weak_this = GetWeakPtr();
-
stream->OnClose(status);
- if (!weak_this)
- return;
-
- switch (availability_state_) {
- case STATE_AVAILABLE:
- ProcessPendingStreamRequests();
- break;
- case STATE_GOING_AWAY:
- DcheckGoingAway();
- MaybeFinishGoingAway();
- break;
- case STATE_CLOSED:
- // Do nothing.
- break;
+ if (availability_state_ == STATE_AVAILABLE) {
+ ProcessPendingStreamRequests();
}
}
@@ -1776,25 +1900,18 @@ bool SpdySession::GetSSLCertRequestInfo(
void SpdySession::OnError(SpdyFramer::SpdyError error_code) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(error_code));
- std::string description = base::StringPrintf(
- "SPDY_ERROR error_code: %d.", error_code);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PROTOCOL_ERROR, description);
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ RecordProtocolErrorHistogram(MapFramerErrorToProtocolError(error_code));
+ std::string description =
+ base::StringPrintf("Framer error: %d (%s).",
+ error_code,
+ SpdyFramer::ErrorCodeToString(error_code));
+ DoDrainSession(MapFramerErrorToNetError(error_code), description);
}
void SpdySession::OnStreamError(SpdyStreamId stream_id,
const std::string& description) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
ActiveStreamMap::iterator it = active_streams_.find(stream_id);
if (it == active_streams_.end()) {
// We still want to send a frame to reset the stream even if we
@@ -1812,9 +1929,6 @@ void SpdySession::OnDataFrameHeader(SpdyStreamId stream_id,
bool fin) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
ActiveStreamMap::iterator it = active_streams_.find(stream_id);
// By the time data comes in, the stream may already be inactive.
@@ -1835,11 +1949,15 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
bool fin) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
+ if (data == NULL && len != 0) {
+ // This is notification of consumed data padding.
+ // TODO(jgraettinger): Properly flow padding into WINDOW_UPDATE frames.
+ // See crbug.com/353012.
return;
+ }
DCHECK_LT(len, 1u << 24);
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_DATA,
base::Bind(&NetLogSpdyDataCallback, stream_id, len, fin));
@@ -1853,6 +1971,7 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
scoped_ptr<SpdyBuffer> buffer;
if (data) {
DCHECK_GT(len, 0u);
+ CHECK_LE(len, static_cast<size_t>(kReadBufferSize));
buffer.reset(new SpdyBuffer(data, len));
if (flow_control_state_ == FLOW_CONTROL_STREAM_AND_SESSION) {
@@ -1889,18 +2008,26 @@ void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
void SpdySession::OnSettings(bool clear_persisted) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
if (clear_persisted)
http_server_properties_->ClearSpdySettings(host_port_pair());
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS,
base::Bind(&NetLogSpdySettingsCallback, host_port_pair(),
clear_persisted));
}
+
+ if (GetProtocolVersion() >= SPDY4) {
+ // Send an acknowledgment of the setting.
+ SpdySettingsIR settings_ir;
+ settings_ir.set_is_ack(true);
+ EnqueueSessionWrite(
+ HIGHEST,
+ SETTINGS,
+ scoped_ptr<SpdyFrame>(
+ buffered_spdy_framer_->SerializeFrame(settings_ir)));
+ }
}
void SpdySession::OnSetting(SpdySettingsIds id,
@@ -1908,9 +2035,6 @@ void SpdySession::OnSetting(SpdySettingsIds id,
uint32 value) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
HandleSetting(id, value);
http_server_properties_->SetSpdySetting(
host_port_pair(),
@@ -1973,19 +2097,21 @@ int SpdySession::OnInitialResponseHeadersReceived(
void SpdySession::OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
+ if (GetProtocolVersion() >= SPDY4) {
+ DCHECK_EQ(0u, associated_stream_id);
+ OnHeaders(stream_id, fin, headers);
return;
+ }
base::Time response_time = base::Time::Now();
base::TimeTicks recv_first_byte_time = time_func_();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_PUSHED_SYN_STREAM,
base::Bind(&NetLogSpdySynStreamReceivedCallback,
@@ -1993,117 +2119,15 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
stream_id, associated_stream_id));
}
- // Server-initiated streams should have even sequence numbers.
- if ((stream_id & 0x1) != 0) {
- LOG(WARNING) << "Received invalid OnSyn stream id " << stream_id;
- return;
- }
-
- if (IsStreamActive(stream_id)) {
- LOG(WARNING) << "Received OnSyn for active stream " << stream_id;
- return;
- }
-
- RequestPriority request_priority =
- ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
-
- if (availability_state_ == STATE_GOING_AWAY) {
- // TODO(akalin): This behavior isn't in the SPDY spec, although it
- // probably should be.
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM,
- "OnSyn received when going away");
- return;
- }
-
- if (associated_stream_id == 0) {
- std::string description = base::StringPrintf(
- "Received invalid OnSyn associated stream id %d for stream %d",
- associated_stream_id, stream_id);
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM, description);
- return;
- }
-
- streams_pushed_count_++;
-
- // TODO(mbelshe): DCHECK that this is a GET method?
-
- // Verify that the response had a URL for us.
- GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
- if (!gurl.is_valid()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Pushed stream url was invalid: " + gurl.spec());
- return;
- }
-
- // Verify we have a valid stream association.
- ActiveStreamMap::iterator associated_it =
- active_streams_.find(associated_stream_id);
- if (associated_it == active_streams_.end()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_INVALID_STREAM,
- base::StringPrintf(
- "Received OnSyn with inactive associated stream %d",
- associated_stream_id));
- return;
- }
-
- // Check that the SYN advertises the same origin as its associated stream.
- // Bypass this check if and only if this session is with a SPDY proxy that
- // is trusted explicitly via the --trusted-spdy-proxy switch.
- if (trusted_spdy_proxy_.Equals(host_port_pair())) {
- // Disallow pushing of HTTPS content.
- if (gurl.SchemeIs("https")) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected push of Cross Origin HTTPS content %d",
- associated_stream_id));
- }
- } else {
- GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
- if (associated_url.GetOrigin() != gurl.GetOrigin()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected Cross Origin Push Stream %d",
- associated_stream_id));
- return;
- }
- }
+ // Split headers to simulate push promise and response.
+ SpdyHeaderBlock request_headers;
+ SpdyHeaderBlock response_headers;
+ SplitPushedHeadersToRequestAndResponse(
+ headers, GetProtocolVersion(), &request_headers, &response_headers);
- // There should not be an existing pushed stream with the same path.
- PushedStreamMap::iterator pushed_it =
- unclaimed_pushed_streams_.lower_bound(gurl);
- if (pushed_it != unclaimed_pushed_streams_.end() &&
- pushed_it->first == gurl) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Received duplicate pushed stream with url: " +
- gurl.spec());
+ if (!TryCreatePushStream(
+ stream_id, associated_stream_id, priority, request_headers))
return;
- }
-
- scoped_ptr<SpdyStream> stream(
- new SpdyStream(SPDY_PUSH_STREAM, GetWeakPtr(), gurl,
- request_priority,
- stream_initial_send_window_size_,
- stream_initial_recv_window_size_,
- net_log_));
- stream->set_stream_id(stream_id);
- stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
- last_compressed_frame_len_ = 0;
-
- DeleteExpiredPushedStreams();
- PushedStreamMap::iterator inserted_pushed_it =
- unclaimed_pushed_streams_.insert(
- pushed_it,
- std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
- DCHECK(inserted_pushed_it != pushed_it);
-
- InsertActivatedStream(stream.Pass());
ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
if (active_it == active_streams_.end()) {
@@ -2111,10 +2135,10 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
return;
}
- // Parse the headers.
- if (OnInitialResponseHeadersReceived(
- headers, response_time,
- recv_first_byte_time, active_it->second.stream) != OK)
+ if (OnInitialResponseHeadersReceived(response_headers,
+ response_time,
+ recv_first_byte_time,
+ active_it->second.stream) != OK)
return;
base::StatsCounter push_requests("spdy.pushed_streams");
@@ -2149,7 +2173,8 @@ void SpdySession::DeleteExpiredPushedStreams() {
LogAbandonedActiveStream(active_it, ERR_INVALID_SPDY_STREAM);
// CloseActiveStreamIterator() will remove the stream from
// |unclaimed_pushed_streams_|.
- CloseActiveStreamIterator(active_it, ERR_INVALID_SPDY_STREAM);
+ ResetStreamIterator(
+ active_it, RST_STREAM_REFUSED_STREAM, "Stream not claimed.");
}
next_unclaimed_push_stream_sweep_time_ = time_func_() +
@@ -2161,13 +2186,10 @@ void SpdySession::OnSynReply(SpdyStreamId stream_id,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
base::Time response_time = base::Time::Now();
base::TimeTicks recv_first_byte_time = time_func_();
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_SYN_REPLY,
base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback,
@@ -2186,11 +2208,18 @@ void SpdySession::OnSynReply(SpdyStreamId stream_id,
stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
last_compressed_frame_len_ = 0;
+ if (GetProtocolVersion() >= SPDY4) {
+ const std::string& error =
+ "SPDY4 wasn't expecting SYN_REPLY.";
+ stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
+ return;
+ }
if (!it->second.waiting_for_syn_reply) {
const std::string& error =
"Received duplicate SYN_REPLY for stream.";
stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
- ResetStreamIterator(it, RST_STREAM_STREAM_IN_USE, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
return;
}
it->second.waiting_for_syn_reply = false;
@@ -2204,10 +2233,7 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RECV_HEADERS,
base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback,
@@ -2227,10 +2253,30 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
last_compressed_frame_len_ = 0;
- int rv = stream->OnAdditionalResponseHeadersReceived(headers);
- if (rv < 0) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(active_streams_.find(stream_id) == active_streams_.end());
+ base::Time response_time = base::Time::Now();
+ base::TimeTicks recv_first_byte_time = time_func_();
+
+ if (it->second.waiting_for_syn_reply) {
+ if (GetProtocolVersion() < SPDY4) {
+ const std::string& error =
+ "Was expecting SYN_REPLY, not HEADERS.";
+ stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+ ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
+ return;
+ }
+
+ it->second.waiting_for_syn_reply = false;
+ ignore_result(OnInitialResponseHeadersReceived(
+ headers, response_time, recv_first_byte_time, stream));
+ } else if (it->second.stream->IsReservedRemote()) {
+ ignore_result(OnInitialResponseHeadersReceived(
+ headers, response_time, recv_first_byte_time, stream));
+ } else {
+ int rv = stream->OnAdditionalResponseHeadersReceived(headers);
+ if (rv < 0) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ DCHECK(active_streams_.find(stream_id) == active_streams_.end());
+ }
}
}
@@ -2238,9 +2284,6 @@ void SpdySession::OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
std::string description;
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_RST_STREAM,
@@ -2276,8 +2319,7 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
+ // TODO(jgraettinger): UMA histogram on |status|.
net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_GOAWAY,
base::Bind(&NetLogSpdyGoAwayCallback,
@@ -2285,13 +2327,7 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
active_streams_.size(),
unclaimed_pushed_streams_.size(),
status));
- if (availability_state_ < STATE_GOING_AWAY) {
- availability_state_ = STATE_GOING_AWAY;
- // |pool_| will be NULL when |InitializeWithSocket()| is in the
- // call stack.
- if (pool_)
- pool_->MakeSessionUnavailable(GetWeakPtr());
- }
+ MakeUnavailable();
StartGoingAway(last_accepted_stream_id, ERR_ABORTED);
// This is to handle the case when we already don't have any active
// streams (i.e., StartGoingAway() did nothing). Otherwise, we have
@@ -2300,28 +2336,24 @@ void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id,
MaybeFinishGoingAway();
}
-void SpdySession::OnPing(uint32 unique_id) {
+void SpdySession::OnPing(SpdyPingId unique_id, bool is_ack) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_PING,
- base::Bind(&NetLogSpdyPingCallback, unique_id, "received"));
+ base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "received"));
// Send response to a PING from server.
- if (unique_id % 2 == 0) {
- WritePingFrame(unique_id);
+ if ((protocol_ >= kProtoSPDY4 && !is_ack) ||
+ (protocol_ < kProtoSPDY4 && unique_id % 2 == 0)) {
+ WritePingFrame(unique_id, true);
return;
}
--pings_in_flight_;
if (pings_in_flight_ < 0) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_UNEXPECTED_PING);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PROTOCOL_ERROR, "pings_in_flight_ is < 0.");
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ DoDrainSession(ERR_SPDY_PROTOCOL_ERROR, "pings_in_flight_ is < 0.");
pings_in_flight_ = 0;
return;
}
@@ -2338,9 +2370,6 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) {
CHECK(in_io_loop_);
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_LE(delta_window_size, static_cast<uint32>(kint32max));
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_RECEIVED_WINDOW_UPDATE_FRAME,
@@ -2358,11 +2387,10 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
if (delta_window_size < 1u) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_SPDY_PROTOCOL_ERROR,
"Received WINDOW_UPDATE with an invalid delta_window_size " +
- base::UintToString(delta_window_size));
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
+ base::UintToString(delta_window_size));
return;
}
@@ -2402,9 +2430,172 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
}
}
+bool SpdySession::TryCreatePushStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ const SpdyHeaderBlock& headers) {
+ // Server-initiated streams should have even sequence numbers.
+ if ((stream_id & 0x1) != 0) {
+ LOG(WARNING) << "Received invalid push stream id " << stream_id;
+ return false;
+ }
+
+ if (IsStreamActive(stream_id)) {
+ LOG(WARNING) << "Received push for active stream " << stream_id;
+ return false;
+ }
+
+ RequestPriority request_priority =
+ ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
+
+ if (availability_state_ == STATE_GOING_AWAY) {
+ // TODO(akalin): This behavior isn't in the SPDY spec, although it
+ // probably should be.
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ "push stream request received when going away");
+ return false;
+ }
+
+ if (associated_stream_id == 0) {
+ // In SPDY4 0 stream id in PUSH_PROMISE frame leads to framer error and
+ // session going away. We should never get here.
+ CHECK_GT(SPDY4, GetProtocolVersion());
+ std::string description = base::StringPrintf(
+ "Received invalid associated stream id %d for pushed stream %d",
+ associated_stream_id,
+ stream_id);
+ EnqueueResetStreamFrame(
+ stream_id, request_priority, RST_STREAM_REFUSED_STREAM, description);
+ return false;
+ }
+
+ streams_pushed_count_++;
+
+ // TODO(mbelshe): DCHECK that this is a GET method?
+
+ // Verify that the response had a URL for us.
+ GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
+ if (!gurl.is_valid()) {
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Pushed stream url was invalid: " + gurl.spec());
+ return false;
+ }
+
+ // Verify we have a valid stream association.
+ ActiveStreamMap::iterator associated_it =
+ active_streams_.find(associated_stream_id);
+ if (associated_it == active_streams_.end()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_INVALID_STREAM,
+ base::StringPrintf("Received push for inactive associated stream %d",
+ associated_stream_id));
+ return false;
+ }
+
+ // Check that the pushed stream advertises the same origin as its associated
+ // stream. Bypass this check if and only if this session is with a SPDY proxy
+ // that is trusted explicitly via the --trusted-spdy-proxy switch.
+ if (trusted_spdy_proxy_.Equals(host_port_pair())) {
+ // Disallow pushing of HTTPS content.
+ if (gurl.SchemeIs("https")) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected push of Cross Origin HTTPS content %d",
+ associated_stream_id));
+ }
+ } else {
+ GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
+ if (associated_url.GetOrigin() != gurl.GetOrigin()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected Cross Origin Push Stream %d",
+ associated_stream_id));
+ return false;
+ }
+ }
+
+ // There should not be an existing pushed stream with the same path.
+ PushedStreamMap::iterator pushed_it =
+ unclaimed_pushed_streams_.lower_bound(gurl);
+ if (pushed_it != unclaimed_pushed_streams_.end() &&
+ pushed_it->first == gurl) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Received duplicate pushed stream with url: " + gurl.spec());
+ return false;
+ }
+
+ scoped_ptr<SpdyStream> stream(new SpdyStream(SPDY_PUSH_STREAM,
+ GetWeakPtr(),
+ gurl,
+ request_priority,
+ stream_initial_send_window_size_,
+ stream_initial_recv_window_size_,
+ net_log_));
+ stream->set_stream_id(stream_id);
+
+ // In spdy4/http2 PUSH_PROMISE arrives on associated stream.
+ if (associated_it != active_streams_.end() && GetProtocolVersion() >= SPDY4) {
+ associated_it->second.stream->IncrementRawReceivedBytes(
+ last_compressed_frame_len_);
+ } else {
+ stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
+ }
+
+ last_compressed_frame_len_ = 0;
+
+ DeleteExpiredPushedStreams();
+ PushedStreamMap::iterator inserted_pushed_it =
+ unclaimed_pushed_streams_.insert(
+ pushed_it,
+ std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
+ DCHECK(inserted_pushed_it != pushed_it);
+
+ InsertActivatedStream(stream.Pass());
+
+ ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
+ if (active_it == active_streams_.end()) {
+ NOTREACHED();
+ return false;
+ }
+
+ active_it->second.stream->OnPushPromiseHeadersReceived(headers);
+ DCHECK(active_it->second.stream->IsReservedRemote());
+ return true;
+}
+
void SpdySession::OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) {
- // TODO(akalin): Handle PUSH_PROMISE frames.
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) {
+ CHECK(in_io_loop_);
+
+ if (net_log_.IsLogging()) {
+ net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_RECV_PUSH_PROMISE,
+ base::Bind(&NetLogSpdyPushPromiseReceivedCallback,
+ &headers,
+ stream_id,
+ promised_stream_id));
+ }
+
+ // Any priority will do.
+ // TODO(baranovich): pass parent stream id priority?
+ if (!TryCreatePushStream(promised_stream_id, stream_id, 0, headers))
+ return;
+
+ base::StatsCounter push_requests("spdy.pushed_streams");
+ push_requests.Increment();
}
void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id,
@@ -2419,10 +2610,9 @@ void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id,
void SpdySession::SendInitialData() {
DCHECK(enable_sending_initial_data_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
if (send_connection_header_prefix_) {
- DCHECK_EQ(protocol_, kProtoHTTP2Draft04);
+ DCHECK_EQ(protocol_, kProtoSPDY4);
scoped_ptr<SpdyFrame> connection_header_prefix_frame(
new SpdyFrame(const_cast<char*>(kHttp2ConnectionHeaderPrefix),
kHttp2ConnectionHeaderPrefixSize,
@@ -2485,8 +2675,6 @@ void SpdySession::SendInitialData() {
void SpdySession::SendSettings(const SettingsMap& settings) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
-
net_log_.AddEvent(
NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS,
base::Bind(&NetLogSpdySendSettingsCallback, &settings));
@@ -2557,7 +2745,7 @@ void SpdySession::SendPrefacePingIfNoneInFlight() {
}
void SpdySession::SendPrefacePing() {
- WritePingFrame(next_ping_id_);
+ WritePingFrame(next_ping_id_, false);
}
void SpdySession::SendWindowUpdateFrame(SpdyStreamId stream_id,
@@ -2583,18 +2771,18 @@ void SpdySession::SendWindowUpdateFrame(SpdyStreamId stream_id,
EnqueueSessionWrite(priority, WINDOW_UPDATE, window_update_frame.Pass());
}
-void SpdySession::WritePingFrame(uint32 unique_id) {
+void SpdySession::WritePingFrame(uint32 unique_id, bool is_ack) {
DCHECK(buffered_spdy_framer_.get());
scoped_ptr<SpdyFrame> ping_frame(
- buffered_spdy_framer_->CreatePingFrame(unique_id));
+ buffered_spdy_framer_->CreatePingFrame(unique_id, is_ack));
EnqueueSessionWrite(HIGHEST, PING, ping_frame.Pass());
- if (net_log().IsLoggingAllEvents()) {
+ if (net_log().IsLogging()) {
net_log().AddEvent(
NetLog::TYPE_SPDY_SESSION_PING,
- base::Bind(&NetLogSpdyPingCallback, unique_id, "sent"));
+ base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "sent"));
}
- if (unique_id % 2 != 0) {
+ if (!is_ack) {
next_ping_id_ += 2;
++pings_in_flight_;
PlanToCheckPingStatus();
@@ -2615,7 +2803,6 @@ void SpdySession::PlanToCheckPingStatus() {
void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
CHECK(!in_io_loop_);
- DCHECK_NE(availability_state_, STATE_CLOSED);
// Check if we got a response back for all PINGs we had sent.
if (pings_in_flight_ == 0) {
@@ -2630,12 +2817,8 @@ void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
if (delay.InMilliseconds() < 0 || last_activity_time_ < last_check_time) {
// Track all failed PING messages in a separate bucket.
- const base::TimeDelta kFailedPing =
- base::TimeDelta::FromInternalValue(INT_MAX);
- RecordPingRTTHistogram(kFailedPing);
- CloseSessionResult result =
- DoCloseSession(ERR_SPDY_PING_FAILED, "Failed ping.");
- DCHECK_EQ(result, SESSION_CLOSED_AND_REMOVED);
+ RecordPingRTTHistogram(base::TimeDelta::Max());
+ DoDrainSession(ERR_SPDY_PING_FAILED, "Failed ping.");
return;
}
@@ -2738,13 +2921,16 @@ void SpdySession::CompleteStreamRequest(
return;
base::WeakPtr<SpdyStream> stream;
- int rv = CreateStream(*pending_request, &stream);
+ int rv = TryCreateStream(pending_request, &stream);
if (rv == OK) {
DCHECK(stream);
pending_request->OnRequestCompleteSuccess(stream);
- } else {
- DCHECK(!stream);
+ return;
+ }
+ DCHECK(!stream);
+
+ if (rv != ERR_IO_PENDING) {
pending_request->OnRequestCompleteFailure(rv);
}
}
@@ -2765,9 +2951,6 @@ void SpdySession::OnWriteBufferConsumed(
// We can be called with |in_io_loop_| set if a write SpdyBuffer is
// deleted (e.g., a stream is closed due to incoming data).
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
if (consume_source == SpdyBuffer::DISCARD) {
@@ -2787,7 +2970,6 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
// We can be called with |in_io_loop_| set if a SpdyBuffer is
// deleted (e.g., a stream is closed due to incoming data).
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(delta_window_size, 1);
@@ -2795,13 +2977,12 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
int32 max_delta_window_size = kint32max - session_send_window_size_;
if (delta_window_size > max_delta_window_size) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
- CloseSessionResult result = DoCloseSession(
+ DoDrainSession(
ERR_SPDY_PROTOCOL_ERROR,
"Received WINDOW_UPDATE [delta: " +
- base::IntToString(delta_window_size) +
- "] for session overflows session_send_window_size_ [current: " +
- base::IntToString(session_send_window_size_) + "]");
- DCHECK_NE(result, SESSION_ALREADY_CLOSED);
+ base::IntToString(delta_window_size) +
+ "] for session overflows session_send_window_size_ [current: " +
+ base::IntToString(session_send_window_size_) + "]");
return;
}
@@ -2817,7 +2998,6 @@ void SpdySession::IncreaseSendWindowSize(int32 delta_window_size) {
}
void SpdySession::DecreaseSendWindowSize(int32 delta_window_size) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
// We only call this method when sending a frame. Therefore,
@@ -2843,9 +3023,6 @@ void SpdySession::OnReadBufferConsumed(
// We can be called with |in_io_loop_| set if a read SpdyBuffer is
// deleted (e.g., discarded by a SpdyReadQueue).
- if (availability_state_ == STATE_CLOSED)
- return;
-
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(consume_size, 1u);
DCHECK_LE(consume_size, static_cast<size_t>(kint32max));
@@ -2854,7 +3031,6 @@ void SpdySession::OnReadBufferConsumed(
}
void SpdySession::IncreaseRecvWindowSize(int32 delta_window_size) {
- DCHECK_NE(availability_state_, STATE_CLOSED);
DCHECK_EQ(flow_control_state_, FLOW_CONTROL_STREAM_AND_SESSION);
DCHECK_GE(session_unacked_recv_window_bytes_, 0);
DCHECK_GE(session_recv_window_size_, session_unacked_recv_window_bytes_);
@@ -2887,12 +3063,11 @@ void SpdySession::DecreaseRecvWindowSize(int32 delta_window_size) {
// negative. If we do, the receive window isn't being respected.
if (delta_window_size > session_recv_window_size_) {
RecordProtocolErrorHistogram(PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION);
- CloseSessionResult result = DoCloseSession(
- ERR_SPDY_PROTOCOL_ERROR,
+ DoDrainSession(
+ ERR_SPDY_FLOW_CONTROL_ERROR,
"delta_window_size is " + base::IntToString(delta_window_size) +
" in DecreaseRecvWindowSize, which is larger than the receive " +
"window size of " + base::IntToString(session_recv_window_size_));
- DCHECK_EQ(result, SESSION_CLOSED_BUT_NOT_REMOVED);
return;
}
@@ -2919,10 +3094,11 @@ void SpdySession::ResumeSendStalledStreams() {
// have to worry about streams being closed, as well as ourselves
// being closed.
- while (availability_state_ != STATE_CLOSED && !IsSendStalled()) {
+ while (!IsSendStalled()) {
size_t old_size = 0;
- if (DCHECK_IS_ON())
- old_size = GetTotalSize(stream_send_unstall_queue_);
+#if DCHECK_IS_ON
+ old_size = GetTotalSize(stream_send_unstall_queue_);
+#endif
SpdyStreamId stream_id = PopStreamToPossiblyResume();
if (stream_id == 0)
diff --git a/chromium/net/spdy/spdy_session.h b/chromium/net/spdy/spdy_session.h
index fde42024be8..b74b1ee8b0a 100644
--- a/chromium/net/spdy/spdy_session.h
+++ b/chromium/net/spdy/spdy_session.h
@@ -61,59 +61,83 @@ const int kMaxReadBytesWithoutYielding = 32 * 1024;
// The initial receive window size for both streams and sessions.
const int32 kDefaultInitialRecvWindowSize = 10 * 1024 * 1024; // 10MB
+// First and last valid stream IDs. As we always act as the client,
+// start at 1 for the first stream id.
+const SpdyStreamId kFirstStreamId = 1;
+const SpdyStreamId kLastStreamId = 0x7fffffff;
+
class BoundNetLog;
struct LoadTimingInfo;
class SpdyStream;
class SSLInfo;
// NOTE: There's an enum of the same name (also with numeric suffixes)
-// in histograms.xml.
-//
-// WARNING: DO NOT INSERT ENUMS INTO THIS LIST! Add only to the end.
+// in histograms.xml. Be sure to add new values there also.
enum SpdyProtocolErrorDetails {
- // SpdyFramer::SpdyErrors
- SPDY_ERROR_NO_ERROR,
- SPDY_ERROR_INVALID_CONTROL_FRAME,
- SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE,
- SPDY_ERROR_ZLIB_INIT_FAILURE,
- SPDY_ERROR_UNSUPPORTED_VERSION,
- SPDY_ERROR_DECOMPRESS_FAILURE,
- SPDY_ERROR_COMPRESS_FAILURE,
- SPDY_ERROR_CREDENTIAL_FRAME_CORRUPT,
- SPDY_ERROR_INVALID_DATA_FRAME_FLAGS,
- SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS,
- // SpdyRstStreamStatus
- STATUS_CODE_INVALID,
- STATUS_CODE_PROTOCOL_ERROR,
- STATUS_CODE_INVALID_STREAM,
- STATUS_CODE_REFUSED_STREAM,
- STATUS_CODE_UNSUPPORTED_VERSION,
- STATUS_CODE_CANCEL,
- STATUS_CODE_INTERNAL_ERROR,
- STATUS_CODE_FLOW_CONTROL_ERROR,
- STATUS_CODE_STREAM_IN_USE,
- STATUS_CODE_STREAM_ALREADY_CLOSED,
- STATUS_CODE_INVALID_CREDENTIALS,
- STATUS_CODE_FRAME_TOO_LARGE,
+ // SpdyFramer::SpdyError mappings.
+ SPDY_ERROR_NO_ERROR = 0,
+ SPDY_ERROR_INVALID_CONTROL_FRAME = 1,
+ SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE = 2,
+ SPDY_ERROR_ZLIB_INIT_FAILURE = 3,
+ SPDY_ERROR_UNSUPPORTED_VERSION = 4,
+ SPDY_ERROR_DECOMPRESS_FAILURE = 5,
+ SPDY_ERROR_COMPRESS_FAILURE = 6,
+ // SPDY_ERROR_CREDENTIAL_FRAME_CORRUPT = 7, (removed).
+ SPDY_ERROR_GOAWAY_FRAME_CORRUPT = 29,
+ SPDY_ERROR_RST_STREAM_FRAME_CORRUPT = 30,
+ SPDY_ERROR_INVALID_DATA_FRAME_FLAGS = 8,
+ SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS = 9,
+ SPDY_ERROR_UNEXPECTED_FRAME = 31,
+ // SpdyRstStreamStatus mappings.
+ // RST_STREAM_INVALID not mapped.
+ STATUS_CODE_PROTOCOL_ERROR = 11,
+ STATUS_CODE_INVALID_STREAM = 12,
+ STATUS_CODE_REFUSED_STREAM = 13,
+ STATUS_CODE_UNSUPPORTED_VERSION = 14,
+ STATUS_CODE_CANCEL = 15,
+ STATUS_CODE_INTERNAL_ERROR = 16,
+ STATUS_CODE_FLOW_CONTROL_ERROR = 17,
+ STATUS_CODE_STREAM_IN_USE = 18,
+ STATUS_CODE_STREAM_ALREADY_CLOSED = 19,
+ STATUS_CODE_INVALID_CREDENTIALS = 20,
+ STATUS_CODE_FRAME_SIZE_ERROR = 21,
+ STATUS_CODE_SETTINGS_TIMEOUT = 32,
+ STATUS_CODE_CONNECT_ERROR = 33,
+ STATUS_CODE_ENHANCE_YOUR_CALM = 34,
+
// SpdySession errors
- PROTOCOL_ERROR_UNEXPECTED_PING,
- PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM,
- PROTOCOL_ERROR_SPDY_COMPRESSION_FAILURE,
- PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION,
- PROTOCOL_ERROR_SYN_REPLY_NOT_RECEIVED,
- PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE,
- PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION,
- NUM_SPDY_PROTOCOL_ERROR_DETAILS
+ PROTOCOL_ERROR_UNEXPECTED_PING = 22,
+ PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM = 23,
+ PROTOCOL_ERROR_SPDY_COMPRESSION_FAILURE = 24,
+ PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION = 25,
+ PROTOCOL_ERROR_SYN_REPLY_NOT_RECEIVED = 26,
+ PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE = 27,
+ PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION = 28,
+
+ // Next free value.
+ NUM_SPDY_PROTOCOL_ERROR_DETAILS = 35,
};
-
-COMPILE_ASSERT(STATUS_CODE_INVALID ==
- static_cast<SpdyProtocolErrorDetails>(SpdyFramer::LAST_ERROR),
+SpdyProtocolErrorDetails NET_EXPORT_PRIVATE
+ MapFramerErrorToProtocolError(SpdyFramer::SpdyError error);
+Error NET_EXPORT_PRIVATE MapFramerErrorToNetError(SpdyFramer::SpdyError error);
+SpdyProtocolErrorDetails NET_EXPORT_PRIVATE
+ MapRstStreamStatusToProtocolError(SpdyRstStreamStatus status);
+SpdyGoAwayStatus NET_EXPORT_PRIVATE MapNetErrorToGoAwayStatus(Error err);
+
+// If these compile asserts fail then SpdyProtocolErrorDetails needs
+// to be updated with new values, as do the mapping functions above.
+COMPILE_ASSERT(12 == SpdyFramer::LAST_ERROR,
SpdyProtocolErrorDetails_SpdyErrors_mismatch);
+COMPILE_ASSERT(15 == RST_STREAM_NUM_STATUS_CODES,
+ SpdyProtocolErrorDetails_RstStreamStatus_mismatch);
-COMPILE_ASSERT(PROTOCOL_ERROR_UNEXPECTED_PING ==
- static_cast<SpdyProtocolErrorDetails>(
- RST_STREAM_NUM_STATUS_CODES + STATUS_CODE_INVALID),
- SpdyProtocolErrorDetails_SpdyErrors_mismatch);
+// Splits pushed |headers| into request and response parts. Request headers are
+// the headers specifying resource URL.
+void NET_EXPORT_PRIVATE
+ SplitPushedHeadersToRequestAndResponse(const SpdyHeaderBlock& headers,
+ SpdyMajorVersion protocol_version,
+ SpdyHeaderBlock* request_headers,
+ SpdyHeaderBlock* response_headers);
// A helper class used to manage a request to create a stream.
class NET_EXPORT_PRIVATE SpdyStreamRequest {
@@ -171,7 +195,6 @@ class NET_EXPORT_PRIVATE SpdyStreamRequest {
void Reset();
- base::WeakPtrFactory<SpdyStreamRequest> weak_ptr_factory_;
SpdyStreamType type_;
base::WeakPtr<SpdySession> session_;
base::WeakPtr<SpdyStream> stream_;
@@ -180,6 +203,8 @@ class NET_EXPORT_PRIVATE SpdyStreamRequest {
BoundNetLog net_log_;
CompletionCallback callback_;
+ base::WeakPtrFactory<SpdyStreamRequest> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SpdyStreamRequest);
};
@@ -251,13 +276,13 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// |certificate_error_code| must either be OK or less than
// ERR_IO_PENDING.
//
- // Returns OK on success, or an error on failure. Never returns
- // ERR_IO_PENDING. If an error is returned, the session must be
- // destroyed immediately.
- Error InitializeWithSocket(scoped_ptr<ClientSocketHandle> connection,
- SpdySessionPool* pool,
- bool is_secure,
- int certificate_error_code);
+ // The session begins reading from |connection| on a subsequent event loop
+ // iteration, so the SpdySession may close immediately afterwards if the first
+ // read of |connection| fails.
+ void InitializeWithSocket(scoped_ptr<ClientSocketHandle> connection,
+ SpdySessionPool* pool,
+ bool is_secure,
+ int certificate_error_code);
// Returns the protocol used by this session. Always between
// kProtoSPDYMinimumVersion and kProtoSPDYMaximumVersion.
@@ -286,7 +311,6 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
scoped_ptr<SpdyFrame> CreateSynStream(
SpdyStreamId stream_id,
RequestPriority priority,
- uint8 credential_slot,
SpdyControlFlags flags,
const SpdyHeaderBlock& headers);
@@ -335,12 +359,10 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
void SendStreamWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size);
- // Whether the stream is closed, i.e. it has stopped processing data
- // and is about to be destroyed.
- //
- // TODO(akalin): This is only used in tests. Remove this function
- // and have tests test the WeakPtr instead.
- bool IsClosed() const { return availability_state_ == STATE_CLOSED; }
+ // Accessors for the session's availability state.
+ bool IsAvailable() const { return availability_state_ == STATE_AVAILABLE; }
+ bool IsGoingAway() const { return availability_state_ == STATE_GOING_AWAY; }
+ bool IsDraining() const { return availability_state_ == STATE_DRAINING; }
// Closes this session. This will close all active streams and mark
// the session as permanently closed. Callers must assume that the
@@ -353,12 +375,30 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// |description| indicates the reason for the error.
void CloseSessionOnError(Error err, const std::string& description);
+ // Mark this session as unavailable, meaning that it will not be used to
+ // service new streams. Unlike when a GOAWAY frame is received, this function
+ // will not close any streams.
+ void MakeUnavailable();
+
+ // Closes all active streams with stream id's greater than
+ // |last_good_stream_id|, as well as any created or pending
+ // streams. Must be called only when |availability_state_| >=
+ // STATE_GOING_AWAY. After this function, DcheckGoingAway() will
+ // pass. May be called multiple times.
+ void StartGoingAway(SpdyStreamId last_good_stream_id, Error status);
+
+ // Must be called only when going away (i.e., DcheckGoingAway()
+ // passes). If there are no more active streams and the session
+ // isn't closed yet, close it.
+ void MaybeFinishGoingAway();
+
// Retrieves information on the current state of the SPDY session as a
// Value. Caller takes possession of the returned value.
base::Value* GetInfoAsValue() const;
// Indicates whether the session is being reused after having successfully
- // used to send/receive data in the past.
+ // used to send/receive data in the past or if the underlying socket was idle
+ // before being used for a SPDY session.
bool IsReused() const;
// Returns true if the underlying transport socket ever had any reads or
@@ -455,6 +495,10 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
return buffered_spdy_framer_->GetDataFrameMaximumPayload();
}
+ // https://http2.github.io/http2-spec/#TLSUsage mandates minimum security
+ // standards for TLS.
+ bool HasAcceptableTransportSecurity() const;
+
// Must be used only by |pool_|.
base::WeakPtr<SpdySession> GetWeakPtr();
@@ -479,6 +523,9 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoReceiveLeaks);
FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoSendLeaks);
FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlEndToEnd);
+ FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, StreamIdSpaceExhausted);
+ FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, UnstallRacesWithStreamCreation);
+ FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, GoAwayOnSessionFlowControlError);
typedef std::deque<base::WeakPtr<SpdyStreamRequest> >
PendingStreamRequestQueue;
@@ -512,9 +559,11 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// The session can process data on existing streams but will
// refuse to create new ones.
STATE_GOING_AWAY,
- // The session has been closed, is waiting to be deleted, and will
- // refuse to process any more data.
- STATE_CLOSED
+ // The session is draining its write queue in preparation of closing.
+ // Further writes will not be queued, and further reads will not be issued
+ // (though the remainder of a current read may be processed). The session
+ // will be destroyed by its write loop once the write queue is drained.
+ STATE_DRAINING,
};
enum ReadState {
@@ -529,18 +578,6 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
WRITE_STATE_DO_WRITE_COMPLETE,
};
- // The return value of DoCloseSession() describing what was done.
- enum CloseSessionResult {
- // The session was already closed so nothing was done.
- SESSION_ALREADY_CLOSED,
- // The session was moved into the closed state but was not removed
- // from |pool_| (because we're in an IO loop).
- SESSION_CLOSED_BUT_NOT_REMOVED,
- // The session was moved into the closed state and removed from
- // |pool_|.
- SESSION_CLOSED_AND_REMOVED,
- };
-
// Checks whether a stream for the given |url| can be created or
// retrieved from the set of unclaimed push streams. Returns OK if
// so. Otherwise, the session is closed and an error <
@@ -574,6 +611,11 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// possible.
void ProcessPendingStreamRequests();
+ bool TryCreatePushStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ const SpdyHeaderBlock& headers);
+
// Close the stream pointed to by the given iterator. Note that that
// stream may hold the last reference to the session.
void CloseActiveStreamIterator(ActiveStreamMap::iterator it, int status);
@@ -598,30 +640,31 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
SpdyRstStreamStatus status,
const std::string& description);
- // Calls DoReadLoop and then if |availability_state_| is
- // STATE_CLOSED, calls RemoveFromPool().
- //
- // Use this function instead of DoReadLoop when posting a task to
- // pump the read loop.
+ // Calls DoReadLoop. Use this function instead of DoReadLoop when
+ // posting a task to pump the read loop.
void PumpReadLoop(ReadState expected_read_state, int result);
// Advance the ReadState state machine. |expected_read_state| is the
// expected starting read state.
//
- // This function must always be called via PumpReadLoop() except for
- // from InitializeWithSocket().
+ // This function must always be called via PumpReadLoop().
int DoReadLoop(ReadState expected_read_state, int result);
// The implementations of the states of the ReadState state machine.
int DoRead();
int DoReadComplete(int result);
- // Calls DoWriteLoop and then if |availability_state_| is
- // STATE_CLOSED, calls RemoveFromPool().
+ // Calls DoWriteLoop. If |availability_state_| is STATE_DRAINING and no
+ // writes remain, the session is removed from the session pool and
+ // destroyed.
//
// Use this function instead of DoWriteLoop when posting a task to
// pump the write loop.
void PumpWriteLoop(WriteState expected_write_state, int result);
+ // Iff the write loop is not currently active, posts a callback into
+ // PumpWriteLoop().
+ void MaybePostWriteLoop();
+
// Advance the WriteState state machine. |expected_write_state| is
// the expected starting write state.
//
@@ -660,7 +703,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
RequestPriority priority);
// Send the PING frame.
- void WritePingFrame(uint32 unique_id);
+ void WritePingFrame(uint32 unique_id, bool is_ack);
// Post a CheckPingStatus call after delay. Don't post if there is already
// CheckPingStatus running.
@@ -671,7 +714,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
void CheckPingStatus(base::TimeTicks last_check_time);
// Get a new stream id.
- int GetNewStreamId();
+ SpdyStreamId GetNewStreamId();
// Pushes the given frame with the given priority into the write
// queue for the session.
@@ -724,36 +767,13 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
void DcheckGoingAway() const;
// Calls DcheckGoingAway(), then DCHECKs that |availability_state_|
- // == STATE_CLOSED, |error_on_close_| has a valid value, that there
- // are no active streams or unclaimed pushed streams, and that the
- // write queue is empty.
- void DcheckClosed() const;
-
- // Closes all active streams with stream id's greater than
- // |last_good_stream_id|, as well as any created or pending
- // streams. Must be called only when |availability_state_| >=
- // STATE_GOING_AWAY. After this function, DcheckGoingAway() will
- // pass. May be called multiple times.
- void StartGoingAway(SpdyStreamId last_good_stream_id, Error status);
+ // == STATE_DRAINING, |error_on_close_| has a valid value, and that there
+ // are no active streams or unclaimed pushed streams.
+ void DcheckDraining() const;
- // Must be called only when going away (i.e., DcheckGoingAway()
- // passes). If there are no more active streams and the session
- // isn't closed yet, close it.
- void MaybeFinishGoingAway();
-
- // If the stream is already closed, does nothing. Otherwise, moves
- // the session to a closed state. Then, if we're in an IO loop,
- // returns (as the IO loop will do the pool removal itself when its
- // done). Otherwise, also removes |this| from |pool_|. The returned
- // result describes what was done.
- CloseSessionResult DoCloseSession(Error err, const std::string& description);
-
- // Remove this session from its pool, which must exist. Must be
- // called only when the session is closed.
- //
- // Must be called only via Pump{Read,Write}Loop() or
- // DoCloseSession().
- void RemoveFromPool();
+ // If the session is already draining, does nothing. Otherwise, moves
+ // the session to the draining state.
+ void DoDrainSession(Error err, const std::string& description);
// Called right before closing a (possibly-inactive) stream for a
// reason other than being requested to by the stream.
@@ -776,7 +796,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE;
virtual void OnStreamError(SpdyStreamId stream_id,
const std::string& description) OVERRIDE;
- virtual void OnPing(uint32 unique_id) OVERRIDE;
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE;
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE;
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
@@ -794,11 +814,11 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE;
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE;
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) OVERRIDE;
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) OVERRIDE;
@@ -913,12 +933,6 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// or NULL, if the transport is not SSL.
SSLClientSocket* GetSSLClientSocket() const;
- // Used for posting asynchronous IO tasks. We use this even though
- // SpdySession is refcounted because we don't need to keep the SpdySession
- // alive if the last reference is within a RunnableMethod. Just revoke the
- // method.
- base::WeakPtrFactory<SpdySession> weak_factory_;
-
// Whether Do{Read,Write}Loop() is in the call stack. Useful for
// making sure we don't destroy ourselves prematurely in that case.
bool in_io_loop_;
@@ -941,7 +955,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
// The read buffer used to read data from the socket.
scoped_refptr<IOBuffer> read_buffer_;
- int stream_hi_water_mark_; // The next stream id to use.
+ SpdyStreamId stream_hi_water_mark_; // The next stream id to use.
// Queue, for each priority, of pending stream requests that have
// not yet been satisfied.
@@ -999,10 +1013,10 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
ReadState read_state_;
WriteState write_state_;
- // If the session was closed (i.e., |availability_state_| is
- // STATE_CLOSED), then |error_on_close_| holds the error with which
- // it was closed, which is < ERR_IO_PENDING. Otherwise, it is set to
- // OK.
+ // If the session is closing (i.e., |availability_state_| is STATE_DRAINING),
+ // then |error_on_close_| holds the error with which it was closed, which
+ // may be OK (upon a polite GOAWAY) or an error < ERR_IO_PENDING otherwise.
+ // Initialized to OK.
Error error_on_close_;
// Limits
@@ -1114,6 +1128,12 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
HostPortPair trusted_spdy_proxy_;
TimeFunc time_func_;
+
+ // Used for posting asynchronous IO tasks. We use this even though
+ // SpdySession is refcounted because we don't need to keep the SpdySession
+ // alive if the last reference is within a RunnableMethod. Just revoke the
+ // method.
+ base::WeakPtrFactory<SpdySession> weak_factory_;
};
} // namespace net
diff --git a/chromium/net/spdy/spdy_session_key.cc b/chromium/net/spdy/spdy_session_key.cc
index 3d6cdeafad1..3ef95de5c3a 100644
--- a/chromium/net/spdy/spdy_session_key.cc
+++ b/chromium/net/spdy/spdy_session_key.cc
@@ -8,7 +8,7 @@
namespace net {
-SpdySessionKey::SpdySessionKey() : privacy_mode_(kPrivacyModeDisabled) {
+SpdySessionKey::SpdySessionKey() : privacy_mode_(PRIVACY_MODE_DISABLED) {
}
SpdySessionKey::SpdySessionKey(const HostPortPair& host_port_pair,
diff --git a/chromium/net/spdy/spdy_session_pool.cc b/chromium/net/spdy/spdy_session_pool.cc
index 72beec465db..189c9453cad 100644
--- a/chromium/net/spdy/spdy_session_pool.cc
+++ b/chromium/net/spdy/spdy_session_pool.cc
@@ -32,7 +32,6 @@ SpdySessionPool::SpdySessionPool(
SSLConfigService* ssl_config_service,
const base::WeakPtr<HttpServerProperties>& http_server_properties,
bool force_single_domain,
- bool enable_ip_pooling,
bool enable_compression,
bool enable_ping_based_connection_checking,
NextProto default_protocol,
@@ -47,7 +46,6 @@ SpdySessionPool::SpdySessionPool(
verify_domain_authentication_(true),
enable_sending_initial_data_(true),
force_single_domain_(force_single_domain),
- enable_ip_pooling_(enable_ip_pooling),
enable_compression_(enable_compression),
enable_ping_based_connection_checking_(
enable_ping_based_connection_checking),
@@ -73,18 +71,23 @@ SpdySessionPool::SpdySessionPool(
SpdySessionPool::~SpdySessionPool() {
CloseAllSessions();
+ while (!sessions_.empty()) {
+ // Destroy sessions to enforce that lifetime is scoped to SpdySessionPool.
+ // Write callbacks queued upon session drain are not invoked.
+ RemoveUnavailableSession((*sessions_.begin())->GetWeakPtr());
+ }
+
if (ssl_config_service_.get())
ssl_config_service_->RemoveObserver(this);
NetworkChangeNotifier::RemoveIPAddressObserver(this);
CertDatabase::GetInstance()->RemoveObserver(this);
}
-net::Error SpdySessionPool::CreateAvailableSessionFromSocket(
+base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket(
const SpdySessionKey& key,
scoped_ptr<ClientSocketHandle> connection,
const BoundNetLog& net_log,
int certificate_error_code,
- base::WeakPtr<SpdySession>* available_session,
bool is_secure) {
DCHECK_GE(default_protocol_, kProtoSPDYMinimumVersion);
DCHECK_LE(default_protocol_, kProtoSPDYMaximumVersion);
@@ -107,35 +110,29 @@ net::Error SpdySessionPool::CreateAvailableSessionFromSocket(
trusted_spdy_proxy_,
net_log.net_log()));
- Error error = new_session->InitializeWithSocket(
+ new_session->InitializeWithSocket(
connection.Pass(), this, is_secure, certificate_error_code);
- DCHECK_NE(error, ERR_IO_PENDING);
-
- if (error != OK) {
- available_session->reset();
- return error;
- }
- *available_session = new_session->GetWeakPtr();
+ base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr();
sessions_.insert(new_session.release());
- MapKeyToAvailableSession(key, *available_session);
+ MapKeyToAvailableSession(key, available_session);
net_log.AddEvent(
NetLog::TYPE_SPDY_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET,
- (*available_session)->net_log().source().ToEventParametersCallback());
+ available_session->net_log().source().ToEventParametersCallback());
// Look up the IP address for this session so that we can match
// future sessions (potentially to different domains) which can
// potentially be pooled with this one. Because GetPeerAddress()
// reports the proxy's address instead of the origin server, check
// to see if this is a direct connection.
- if (enable_ip_pooling_ && key.proxy_server().is_direct()) {
+ if (key.proxy_server().is_direct()) {
IPEndPoint address;
- if ((*available_session)->GetPeerAddress(&address) == OK)
+ if (available_session->GetPeerAddress(&address) == OK)
aliases_[address] = key;
}
- return error;
+ return available_session;
}
base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession(
@@ -151,9 +148,6 @@ base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession(
return it->second;
}
- if (!enable_ip_pooling_)
- return base::WeakPtr<SpdySession>();
-
// Look up the key's from the resolver's cache.
net::HostResolver::RequestInfo resolve_info(key.host_port_pair());
AddressList addresses;
@@ -256,7 +250,7 @@ void SpdySessionPool::CloseCurrentIdleSessions() {
}
void SpdySessionPool::CloseAllSessions() {
- while (!sessions_.empty()) {
+ while (!available_sessions_.empty()) {
CloseCurrentSessionsHelper(ERR_ABORTED, "Closing all sessions.",
false /* idle_only */);
}
@@ -278,7 +272,27 @@ base::Value* SpdySessionPool::SpdySessionPoolInfoToValue() const {
}
void SpdySessionPool::OnIPAddressChanged() {
- CloseCurrentSessions(ERR_NETWORK_CHANGED);
+ WeakSessionList current_sessions = GetCurrentSessions();
+ for (WeakSessionList::const_iterator it = current_sessions.begin();
+ it != current_sessions.end(); ++it) {
+ if (!*it)
+ continue;
+
+// For OSs that terminate TCP connections upon relevant network changes,
+// attempt to preserve active streams by marking all sessions as going
+// away, rather than explicitly closing them. Streams may still fail due
+// to a generated TCP reset.
+#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
+ (*it)->MakeUnavailable();
+ (*it)->StartGoingAway(kLastStreamId, ERR_NETWORK_CHANGED);
+ (*it)->MaybeFinishGoingAway();
+#else
+ (*it)->CloseSessionOnError(ERR_NETWORK_CHANGED,
+ "Closing current sessions.");
+ DCHECK((*it)->IsDraining());
+#endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
+ DCHECK(!IsSessionAvailable(*it));
+ }
http_server_properties_->ClearAllSpdySettings();
}
@@ -318,7 +332,7 @@ const SpdySessionKey& SpdySessionPool::NormalizeListKey(
HostPortPair single_domain = HostPortPair("singledomain.com", 80);
single_domain_key = new SpdySessionKey(single_domain,
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
}
return *single_domain_key;
}
@@ -384,7 +398,6 @@ void SpdySessionPool::CloseCurrentSessionsHelper(
(*it)->CloseSessionOnError(error, description);
DCHECK(!IsSessionAvailable(*it));
- DCHECK(!*it);
}
}
diff --git a/chromium/net/spdy/spdy_session_pool.h b/chromium/net/spdy/spdy_session_pool.h
index e68d0c4f6d5..0fad5d2d46b 100644
--- a/chromium/net/spdy/spdy_session_pool.h
+++ b/chromium/net/spdy/spdy_session_pool.h
@@ -51,7 +51,6 @@ class NET_EXPORT SpdySessionPool
SSLConfigService* ssl_config_service,
const base::WeakPtr<HttpServerProperties>& http_server_properties,
bool force_single_domain,
- bool enable_ip_pooling,
bool enable_compression,
bool enable_ping_based_connection_checking,
NextProto default_protocol,
@@ -80,15 +79,14 @@ class NET_EXPORT SpdySessionPool
// encountered when connecting the SSL socket, with OK meaning there
// was no error.
//
- // If successful, OK is returned and |available_session| will be
- // non-NULL and available. Otherwise, an error is returned and
- // |available_session| will be NULL.
- net::Error CreateAvailableSessionFromSocket(
+ // Returns the new SpdySession. Note that the SpdySession begins reading from
+ // |connection| on a subsequent event loop iteration, so it may be closed
+ // immediately afterwards if the first read of |connection| fails.
+ base::WeakPtr<SpdySession> CreateAvailableSessionFromSocket(
const SpdySessionKey& key,
scoped_ptr<ClientSocketHandle> connection,
const BoundNetLog& net_log,
int certificate_error_code,
- base::WeakPtr<SpdySession>* available_session,
bool is_secure);
// Find an available session for the given key, or NULL if there isn't one.
@@ -215,7 +213,6 @@ class NET_EXPORT SpdySessionPool
bool verify_domain_authentication_;
bool enable_sending_initial_data_;
bool force_single_domain_;
- bool enable_ip_pooling_;
bool enable_compression_;
bool enable_ping_based_connection_checking_;
const NextProto default_protocol_;
diff --git a/chromium/net/spdy/spdy_session_pool_unittest.cc b/chromium/net/spdy/spdy_session_pool_unittest.cc
index c9873fc8141..16a624b1ad0 100644
--- a/chromium/net/spdy/spdy_session_pool_unittest.cc
+++ b/chromium/net/spdy/spdy_session_pool_unittest.cc
@@ -14,6 +14,7 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/transport_client_socket_pool.h"
#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_stream_test_util.h"
#include "net/spdy/spdy_test_util_common.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -51,8 +52,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdySessionPoolTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
// A delegate that opens a new session when it is closed.
class SessionOpeningDelegate : public SpdyStream::Delegate {
@@ -96,7 +96,7 @@ TEST_P(SpdySessionPoolTest, CloseCurrentSessions) {
SpdySessionKey test_key =
SpdySessionKey(
test_host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
MockConnect connect_data(SYNCHRONOUS, OK);
MockRead reads[] = {
@@ -139,7 +139,7 @@ TEST_P(SpdySessionPoolTest, CloseCurrentSessions) {
TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
MockConnect connect_data(SYNCHRONOUS, OK);
MockRead reads[] = {
- MockRead(ASYNC, 0, 0) // EOF
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
};
session_deps_.host_resolver->set_synchronous_mode(true);
@@ -157,7 +157,7 @@ TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
const std::string kTestHost1("http://www.a.com");
HostPortPair test_host_port_pair1(kTestHost1, 80);
SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session1 =
CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
GURL url1(kTestHost1);
@@ -171,7 +171,7 @@ TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
const std::string kTestHost2("http://www.b.com");
HostPortPair test_host_port_pair2(kTestHost2, 80);
SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session2 =
CreateInsecureSpdySession(http_session_, key2, BoundNetLog());
GURL url2(kTestHost2);
@@ -185,7 +185,7 @@ TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
const std::string kTestHost3("http://www.c.com");
HostPortPair test_host_port_pair3(kTestHost3, 80);
SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session3 =
CreateInsecureSpdySession(http_session_, key3, BoundNetLog());
GURL url3(kTestHost3);
@@ -196,20 +196,20 @@ TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
// All sessions are active and not closed
EXPECT_TRUE(session1->is_active());
- EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session1->IsAvailable());
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
EXPECT_TRUE(session3->is_active());
- EXPECT_FALSE(session3->IsClosed());
+ EXPECT_TRUE(session3->IsAvailable());
// Should not do anything, all are active
spdy_session_pool_->CloseCurrentIdleSessions();
EXPECT_TRUE(session1->is_active());
- EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session1->IsAvailable());
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
EXPECT_TRUE(session3->is_active());
- EXPECT_FALSE(session3->IsClosed());
+ EXPECT_TRUE(session3->IsAvailable());
// Make sessions 1 and 3 inactive, but keep them open.
// Session 2 still open and active
@@ -218,32 +218,40 @@ TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
session3->CloseCreatedStream(spdy_stream3, OK);
EXPECT_EQ(NULL, spdy_stream3.get());
EXPECT_FALSE(session1->is_active());
- EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session1->IsAvailable());
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
EXPECT_FALSE(session3->is_active());
- EXPECT_FALSE(session3->IsClosed());
+ EXPECT_TRUE(session3->IsAvailable());
// Should close session 1 and 3, 2 should be left open
spdy_session_pool_->CloseCurrentIdleSessions();
+ base::MessageLoop::current()->RunUntilIdle();
+
EXPECT_TRUE(session1 == NULL);
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
EXPECT_TRUE(session3 == NULL);
// Should not do anything
spdy_session_pool_->CloseCurrentIdleSessions();
+ base::MessageLoop::current()->RunUntilIdle();
+
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
// Make 2 not active
session2->CloseCreatedStream(spdy_stream2, OK);
+ base::MessageLoop::current()->RunUntilIdle();
+
EXPECT_EQ(NULL, spdy_stream2.get());
EXPECT_FALSE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
// This should close session 2
spdy_session_pool_->CloseCurrentIdleSessions();
+ base::MessageLoop::current()->RunUntilIdle();
+
EXPECT_TRUE(session2 == NULL);
}
@@ -259,7 +267,7 @@ TEST_P(SpdySessionPoolTest, CloseAllSessions) {
SpdySessionKey test_key =
SpdySessionKey(
test_host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
MockConnect connect_data(SYNCHRONOUS, OK);
MockRead reads[] = {
@@ -348,7 +356,7 @@ void SpdySessionPoolTest::RunIPPoolingTest(
// Setup a SpdySessionKey
test_hosts[i].key = SpdySessionKey(
HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
}
MockConnect connect_data(SYNCHRONOUS, OK);
@@ -382,7 +390,7 @@ void SpdySessionPoolTest::RunIPPoolingTest(
// Verify that the second host, through a proxy, won't share the IP.
SpdySessionKey proxy_key(test_hosts[1].key.host_port_pair(),
ProxyServer::FromPacString("HTTP http://proxy.foo.com/"),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, proxy_key));
// Overlap between 2 and 3 does is not transitive to 1.
@@ -421,8 +429,9 @@ void SpdySessionPoolTest::RunIPPoolingTest(
switch (close_sessions_type) {
case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
session->CloseSessionOnError(ERR_ABORTED, std::string());
- EXPECT_TRUE(session == NULL);
session2->CloseSessionOnError(ERR_ABORTED, std::string());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(session == NULL);
EXPECT_TRUE(session2 == NULL);
break;
case SPDY_POOL_CLOSE_CURRENT_SESSIONS:
@@ -450,27 +459,30 @@ void SpdySessionPoolTest::RunIPPoolingTest(
// Check spdy_session and spdy_session1 are not closed.
EXPECT_FALSE(session->is_active());
- EXPECT_FALSE(session->IsClosed());
+ EXPECT_TRUE(session->IsAvailable());
EXPECT_FALSE(session1->is_active());
- EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session1->IsAvailable());
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
// Test that calling CloseIdleSessions, does not cause a crash.
// http://crbug.com/181400
spdy_session_pool_->CloseCurrentIdleSessions();
+ base::MessageLoop::current()->RunUntilIdle();
// Verify spdy_session and spdy_session1 are closed.
EXPECT_TRUE(session == NULL);
EXPECT_TRUE(session1 == NULL);
EXPECT_TRUE(session2->is_active());
- EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session2->IsAvailable());
spdy_stream2->Cancel();
EXPECT_EQ(NULL, spdy_stream.get());
EXPECT_EQ(NULL, spdy_stream1.get());
EXPECT_EQ(NULL, spdy_stream2.get());
+
session2->CloseSessionOnError(ERR_ABORTED, std::string());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session2 == NULL);
break;
}
@@ -493,6 +505,116 @@ TEST_P(SpdySessionPoolTest, IPPoolingCloseIdleSessions) {
RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS);
}
+// Construct a Pool with SpdySessions in various availability states. Simulate
+// an IP address change. Ensure sessions gracefully shut down. Regression test
+// for crbug.com/379469.
+TEST_P(SpdySessionPoolTest, IPAddressChanged) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ SpdyTestUtil spdy_util(GetParam());
+
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
+ };
+ scoped_ptr<SpdyFrame> req(
+ spdy_util.ConstructSpdyGet("http://www.a.com", false, 1, MEDIUM));
+ MockWrite writes[] = {CreateMockWrite(*req, 1)};
+
+ DelayedSocketData data(1, reads, arraysize(reads), writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateNetworkSession();
+
+ // Set up session A: Going away, but with an active stream.
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ const std::string kTestHostA("http://www.a.com");
+ HostPortPair test_host_port_pairA(kTestHostA, 80);
+ SpdySessionKey keyA(
+ test_host_port_pairA, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+ base::WeakPtr<SpdySession> sessionA =
+ CreateInsecureSpdySession(http_session_, keyA, BoundNetLog());
+
+ GURL urlA(kTestHostA);
+ base::WeakPtr<SpdyStream> spdy_streamA = CreateStreamSynchronously(
+ SPDY_BIDIRECTIONAL_STREAM, sessionA, urlA, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegateA(spdy_streamA);
+ spdy_streamA->SetDelegate(&delegateA);
+
+ scoped_ptr<SpdyHeaderBlock> headers(
+ spdy_util.ConstructGetHeaderBlock(urlA.spec()));
+ spdy_streamA->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND);
+ EXPECT_TRUE(spdy_streamA->HasUrlFromHeaders());
+
+ base::MessageLoop::current()->RunUntilIdle(); // Allow headers to write.
+ EXPECT_TRUE(delegateA.send_headers_completed());
+
+ sessionA->MakeUnavailable();
+ EXPECT_TRUE(sessionA->IsGoingAway());
+ EXPECT_FALSE(delegateA.StreamIsClosed());
+
+ // Set up session B: Available, with a created stream.
+ const std::string kTestHostB("http://www.b.com");
+ HostPortPair test_host_port_pairB(kTestHostB, 80);
+ SpdySessionKey keyB(
+ test_host_port_pairB, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+ base::WeakPtr<SpdySession> sessionB =
+ CreateInsecureSpdySession(http_session_, keyB, BoundNetLog());
+ EXPECT_TRUE(sessionB->IsAvailable());
+
+ GURL urlB(kTestHostB);
+ base::WeakPtr<SpdyStream> spdy_streamB = CreateStreamSynchronously(
+ SPDY_BIDIRECTIONAL_STREAM, sessionB, urlB, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegateB(spdy_streamB);
+ spdy_streamB->SetDelegate(&delegateB);
+
+ // Set up session C: Draining.
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ const std::string kTestHostC("http://www.c.com");
+ HostPortPair test_host_port_pairC(kTestHostC, 80);
+ SpdySessionKey keyC(
+ test_host_port_pairC, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+ base::WeakPtr<SpdySession> sessionC =
+ CreateInsecureSpdySession(http_session_, keyC, BoundNetLog());
+
+ sessionC->CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Error!");
+ EXPECT_TRUE(sessionC->IsDraining());
+
+ spdy_session_pool_->OnIPAddressChanged();
+
+#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
+ EXPECT_TRUE(sessionA->IsGoingAway());
+ EXPECT_TRUE(sessionB->IsDraining());
+ EXPECT_TRUE(sessionC->IsDraining());
+
+ EXPECT_EQ(1u,
+ sessionA->num_active_streams()); // Active stream is still active.
+ EXPECT_FALSE(delegateA.StreamIsClosed());
+
+ EXPECT_TRUE(delegateB.StreamIsClosed()); // Created stream was closed.
+ EXPECT_EQ(ERR_NETWORK_CHANGED, delegateB.WaitForClose());
+
+ sessionA->CloseSessionOnError(ERR_ABORTED, "Closing");
+ sessionB->CloseSessionOnError(ERR_ABORTED, "Closing");
+
+ EXPECT_TRUE(delegateA.StreamIsClosed());
+ EXPECT_EQ(ERR_ABORTED, delegateA.WaitForClose());
+#else
+ EXPECT_TRUE(sessionA->IsDraining());
+ EXPECT_TRUE(sessionB->IsDraining());
+ EXPECT_TRUE(sessionC->IsDraining());
+
+ // Both streams were closed with an error.
+ EXPECT_TRUE(delegateA.StreamIsClosed());
+ EXPECT_EQ(ERR_NETWORK_CHANGED, delegateA.WaitForClose());
+ EXPECT_TRUE(delegateB.StreamIsClosed());
+ EXPECT_EQ(ERR_NETWORK_CHANGED, delegateB.WaitForClose());
+#endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/spdy/spdy_session_unittest.cc b/chromium/net/spdy/spdy_session_unittest.cc
index 2f185c7c01a..7145fc81027 100644
--- a/chromium/net/spdy/spdy_session_unittest.cc
+++ b/chromium/net/spdy/spdy_session_unittest.cc
@@ -107,7 +107,7 @@ class SpdySessionTest : public PlatformTest,
test_url_(kTestUrl),
test_host_port_pair_(kTestHost, kTestPort),
key_(test_host_port_pair_, ProxyServer::Direct(),
- kPrivacyModeDisabled) {
+ PRIVACY_MODE_DISABLED) {
}
virtual ~SpdySessionTest() {
@@ -182,16 +182,19 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdySessionTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
// Try to create a SPDY session that will fail during
// initialization. Nothing should blow up.
TEST_P(SpdySessionTest, InitialReadError) {
CreateDeterministicNetworkSession();
- TryCreateFakeSpdySessionExpectingFailure(
- spdy_session_pool_, key_, ERR_FAILED);
+ base::WeakPtr<SpdySession> session = TryCreateFakeSpdySessionExpectingFailure(
+ spdy_session_pool_, key_, ERR_CONNECTION_CLOSED);
+ EXPECT_TRUE(session);
+ // Flush the read.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(session);
}
namespace {
@@ -238,9 +241,6 @@ TEST_P(SpdySessionTest, PendingStreamCancellingAnother) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -280,12 +280,9 @@ TEST_P(SpdySessionTest, PendingStreamCancellingAnother) {
session->CloseSessionOnError(ERR_ABORTED, "Aborting session");
EXPECT_EQ(ERR_ABORTED, callback1.WaitForResult());
-
- data.RunFor(1);
}
-// A session receiving a GOAWAY frame with no active streams should
-// immediately close.
+// A session receiving a GOAWAY frame with no active streams should close.
TEST_P(SpdySessionTest, GoAwayWithNoActiveStreams) {
session_deps_.host_resolver->set_synchronous_mode(true);
@@ -298,9 +295,6 @@ TEST_P(SpdySessionTest, GoAwayWithNoActiveStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -312,9 +306,8 @@ TEST_P(SpdySessionTest, GoAwayWithNoActiveStreams) {
// Read and process the GOAWAY frame.
data.RunFor(1);
-
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
-
+ base::RunLoop().RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -332,16 +325,16 @@ TEST_P(SpdySessionTest, GoAwayImmediatelyWithNoActiveStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
data.StopAfter(1);
- TryCreateInsecureSpdySessionExpectingFailure(
- http_session_, key_, ERR_CONNECTION_CLOSED, BoundNetLog());
+ base::WeakPtr<SpdySession> session =
+ TryCreateInsecureSpdySessionExpectingFailure(
+ http_session_, key_, ERR_CONNECTION_CLOSED, BoundNetLog());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(session);
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
}
@@ -369,9 +362,6 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -379,7 +369,7 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreams) {
EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -417,12 +407,13 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreams) {
EXPECT_EQ(NULL, spdy_stream2.get());
EXPECT_TRUE(session->IsStreamActive(1));
- EXPECT_FALSE(session->IsClosed());
+ EXPECT_TRUE(session->IsGoingAway());
// Should close the session.
spdy_stream1->Close();
EXPECT_EQ(NULL, spdy_stream1.get());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -453,9 +444,6 @@ TEST_P(SpdySessionTest, GoAwayTwice) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -463,7 +451,7 @@ TEST_P(SpdySessionTest, GoAwayTwice) {
EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -500,13 +488,12 @@ TEST_P(SpdySessionTest, GoAwayTwice) {
EXPECT_FALSE(session->IsStreamActive(3));
EXPECT_EQ(NULL, spdy_stream2.get());
EXPECT_TRUE(session->IsStreamActive(1));
-
- EXPECT_FALSE(session->IsClosed());
+ EXPECT_TRUE(session->IsGoingAway());
// Read and process the second GOAWAY frame, which should close the
// session.
data.RunFor(1);
-
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -535,9 +522,6 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreamsThenClose) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -545,7 +529,7 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreamsThenClose) {
EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -582,19 +566,85 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreamsThenClose) {
EXPECT_FALSE(session->IsStreamActive(3));
EXPECT_EQ(NULL, spdy_stream2.get());
EXPECT_TRUE(session->IsStreamActive(1));
-
- EXPECT_FALSE(session->IsClosed());
+ EXPECT_TRUE(session->IsGoingAway());
session->CloseSessionOnError(ERR_ABORTED, "Aborting session");
-
EXPECT_EQ(NULL, spdy_stream1.get());
+
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(session == NULL);
+}
+
+// Process a joint read buffer which causes the session to begin draining, and
+// then processes a GOAWAY. The session should gracefully drain. Regression test
+// for crbug.com/379469
+TEST_P(SpdySessionTest, GoAwayWhileDraining) {
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true));
+ MockWrite writes[] = {
+ CreateMockWrite(*req, 0),
+ };
+
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(1));
+ scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ size_t joint_size = goaway->size() * 2 + body->size();
+
+ // Compose interleaved |goaway| and |body| frames into a single read.
+ scoped_ptr<char[]> buffer(new char[joint_size]);
+ {
+ size_t out = 0;
+ memcpy(&buffer[out], goaway->data(), goaway->size());
+ out += goaway->size();
+ memcpy(&buffer[out], body->data(), body->size());
+ out += body->size();
+ memcpy(&buffer[out], goaway->data(), goaway->size());
+ out += goaway->size();
+ ASSERT_EQ(out, joint_size);
+ }
+ SpdyFrame joint_frames(buffer.get(), joint_size, false);
+
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1), CreateMockRead(joint_frames, 2),
+ MockRead(ASYNC, 0, 3) // EOF
+ };
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ DeterministicSocketData data(
+ reads, arraysize(reads), writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ CreateDeterministicNetworkSession();
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+
+ GURL url(kDefaultURL);
+ base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate(spdy_stream);
+ spdy_stream->SetDelegate(&delegate);
+
+ scoped_ptr<SpdyHeaderBlock> headers(
+ spdy_util_.ConstructGetHeaderBlock(url.spec()));
+ spdy_stream->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND);
+ EXPECT_TRUE(spdy_stream->HasUrlFromHeaders());
+
+ data.RunFor(3);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Stream and session closed gracefully.
+ EXPECT_TRUE(delegate.StreamIsClosed());
+ EXPECT_EQ(OK, delegate.WaitForClose());
+ EXPECT_EQ(kUploadData, delegate.TakeReceivedData());
EXPECT_TRUE(session == NULL);
}
// Try to create a stream after receiving a GOAWAY frame. It should
// fail.
TEST_P(SpdySessionTest, CreateStreamAfterGoAway) {
- const char kStreamUrl[] = "http://www.google.com";
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
@@ -613,9 +663,6 @@ TEST_P(SpdySessionTest, CreateStreamAfterGoAway) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -623,7 +670,7 @@ TEST_P(SpdySessionTest, CreateStreamAfterGoAway) {
EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
- GURL url(kStreamUrl);
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -662,13 +709,12 @@ TEST_P(SpdySessionTest, CreateStreamAfterGoAway) {
// Receiving a SYN_STREAM frame after a GOAWAY frame should result in
// the stream being refused.
TEST_P(SpdySessionTest, SynStreamAfterGoAway) {
- const char kStreamUrl[] = "http://www.google.com";
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(1));
scoped_ptr<SpdyFrame>
- push(spdy_util_.ConstructSpdyPush(NULL, 0, 2, 1, kStreamUrl));
+ push(spdy_util_.ConstructSpdyPush(NULL, 0, 2, 1, kDefaultURL));
MockRead reads[] = {
CreateMockRead(*goaway, 1),
CreateMockRead(*push, 2),
@@ -687,9 +733,6 @@ TEST_P(SpdySessionTest, SynStreamAfterGoAway) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -697,7 +740,7 @@ TEST_P(SpdySessionTest, SynStreamAfterGoAway) {
EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
- GURL url(kStreamUrl);
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -724,7 +767,75 @@ TEST_P(SpdySessionTest, SynStreamAfterGoAway) {
// Read and process the SYN_STREAM frame, the subsequent RST_STREAM,
// and EOF.
data.RunFor(3);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(session == NULL);
+}
+
+// A session observing a network change with active streams should close
+// when the last active stream is closed.
+TEST_P(SpdySessionTest, NetworkChangeWithActiveStreams) {
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 1) // EOF
+ };
+ scoped_ptr<SpdyFrame> req1(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true));
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0),
+ };
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ CreateDeterministicNetworkSession();
+
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+
+ EXPECT_EQ(spdy_util_.spdy_version(), session->GetProtocolVersion());
+
+ base::WeakPtr<SpdyStream> spdy_stream =
+ CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session,
+ GURL(kDefaultURL), MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate(spdy_stream);
+ spdy_stream->SetDelegate(&delegate);
+
+ scoped_ptr<SpdyHeaderBlock> headers(
+ spdy_util_.ConstructGetHeaderBlock(kDefaultURL));
+
+ spdy_stream->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND);
+ EXPECT_TRUE(spdy_stream->HasUrlFromHeaders());
+
+ data.RunFor(1);
+
+ EXPECT_EQ(1u, spdy_stream->stream_id());
+
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_));
+ spdy_session_pool_->OnIPAddressChanged();
+
+ // The SpdySessionPool behavior differs based on how the OSs reacts to
+ // network changes; see comment in SpdySessionPool::OnIPAddressChanged().
+#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_IOS)
+ // For OSs where the TCP connections will close upon relevant network
+ // changes, SpdySessionPool doesn't need to force them to close, so in these
+ // cases verify the session has become unavailable but remains open and the
+ // pre-existing stream is still active.
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
+
+ EXPECT_TRUE(session->IsGoingAway());
+
+ EXPECT_TRUE(session->IsStreamActive(1));
+
+ // Should close the session.
+ spdy_stream->Close();
+#endif
+ EXPECT_EQ(NULL, spdy_stream.get());
+
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -733,12 +844,12 @@ TEST_P(SpdySessionTest, ClientPing) {
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
- scoped_ptr<SpdyFrame> read_ping(spdy_util_.ConstructSpdyPing(1));
+ scoped_ptr<SpdyFrame> read_ping(spdy_util_.ConstructSpdyPing(1, true));
MockRead reads[] = {
CreateMockRead(*read_ping, 1),
MockRead(ASYNC, 0, 0, 2) // EOF
};
- scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1));
+ scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1, false));
MockWrite writes[] = {
CreateMockWrite(*write_ping, 0),
};
@@ -747,9 +858,6 @@ TEST_P(SpdySessionTest, ClientPing) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -791,12 +899,12 @@ TEST_P(SpdySessionTest, ServerPing) {
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
- scoped_ptr<SpdyFrame> read_ping(spdy_util_.ConstructSpdyPing(2));
+ scoped_ptr<SpdyFrame> read_ping(spdy_util_.ConstructSpdyPing(2, false));
MockRead reads[] = {
CreateMockRead(*read_ping),
MockRead(SYNCHRONOUS, 0, 0) // EOF
};
- scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(2));
+ scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(2, true));
MockWrite writes[] = {
CreateMockWrite(*write_ping),
};
@@ -805,9 +913,6 @@ TEST_P(SpdySessionTest, ServerPing) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -838,7 +943,7 @@ TEST_P(SpdySessionTest, PingAndWriteLoop) {
session_deps_.time_func = TheNearFuture;
MockConnect connect_data(SYNCHRONOUS, OK);
- scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1));
+ scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1, false));
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
MockWrite writes[] = {
@@ -857,15 +962,12 @@ TEST_P(SpdySessionTest, PingAndWriteLoop) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, LOWEST, BoundNetLog());
@@ -884,45 +986,245 @@ TEST_P(SpdySessionTest, PingAndWriteLoop) {
session->CloseSessionOnError(ERR_ABORTED, "Aborting");
}
-TEST_P(SpdySessionTest, DeleteExpiredPushStreams) {
+TEST_P(SpdySessionTest, StreamIdSpaceExhausted) {
+ const SpdyStreamId kLastStreamId = 0x7fffffff;
session_deps_.host_resolver->set_synchronous_mode(true);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
- session_deps_.time_func = TheNearFuture;
+ // Test setup: |stream_hi_water_mark_| and |max_concurrent_streams_| are
+ // fixed to allow for two stream ID assignments, and three concurrent
+ // streams. Four streams are started, and two are activated. Verify the
+ // session goes away, and that the created (but not activated) and
+ // stalled streams are aborted. Also verify the activated streams complete,
+ // at which point the session closes.
+
+ scoped_ptr<SpdyFrame> req1(spdy_util_.ConstructSpdyGet(
+ NULL, 0, false, kLastStreamId - 2, MEDIUM, true));
+ scoped_ptr<SpdyFrame> req2(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, kLastStreamId, MEDIUM, true));
+
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0), CreateMockWrite(*req2, 1),
+ };
+
+ scoped_ptr<SpdyFrame> resp1(
+ spdy_util_.ConstructSpdyGetSynReply(NULL, 0, kLastStreamId - 2));
+ scoped_ptr<SpdyFrame> resp2(
+ spdy_util_.ConstructSpdyGetSynReply(NULL, 0, kLastStreamId));
+
+ scoped_ptr<SpdyFrame> body1(
+ spdy_util_.ConstructSpdyBodyFrame(kLastStreamId - 2, true));
+ scoped_ptr<SpdyFrame> body2(
+ spdy_util_.ConstructSpdyBodyFrame(kLastStreamId, true));
+
+ MockRead reads[] = {
+ CreateMockRead(*resp1, 2), CreateMockRead(*resp2, 3),
+ CreateMockRead(*body1, 4), CreateMockRead(*body2, 5),
+ MockRead(ASYNC, 0, 6) // EOF
+ };
+
+ DeterministicSocketData data(
+ reads, arraysize(reads), writes, arraysize(writes));
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ CreateDeterministicNetworkSession();
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+
+ // Fix stream_hi_water_mark_ to allow for two stream activations.
+ session->stream_hi_water_mark_ = kLastStreamId - 2;
+ // Fix max_concurrent_streams to allow for three stream creations.
+ session->max_concurrent_streams_ = 3;
+
+ // Create three streams synchronously, and begin a fourth (which is stalled).
+ GURL url(kDefaultURL);
+ base::WeakPtr<SpdyStream> stream1 = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate1(stream1);
+ stream1->SetDelegate(&delegate1);
+
+ base::WeakPtr<SpdyStream> stream2 = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate2(stream2);
+ stream2->SetDelegate(&delegate2);
+
+ base::WeakPtr<SpdyStream> stream3 = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate3(stream3);
+ stream3->SetDelegate(&delegate3);
+
+ SpdyStreamRequest request4;
+ TestCompletionCallback callback4;
+ EXPECT_EQ(ERR_IO_PENDING,
+ request4.StartRequest(SPDY_REQUEST_RESPONSE_STREAM,
+ session,
+ url,
+ MEDIUM,
+ BoundNetLog(),
+ callback4.callback()));
+
+ // Streams 1-3 were created. 4th is stalled. No streams are active yet.
+ EXPECT_EQ(0u, session->num_active_streams());
+ EXPECT_EQ(3u, session->num_created_streams());
+ EXPECT_EQ(1u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Activate stream 1. One ID remains available.
+ stream1->SendRequestHeaders(
+ scoped_ptr<SpdyHeaderBlock>(
+ spdy_util_.ConstructGetHeaderBlock(url.spec())),
+ NO_MORE_DATA_TO_SEND);
+ data.RunFor(1);
+
+ EXPECT_EQ(kLastStreamId - 2u, stream1->stream_id());
+ EXPECT_EQ(1u, session->num_active_streams());
+ EXPECT_EQ(2u, session->num_created_streams());
+ EXPECT_EQ(1u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Activate stream 2. ID space is exhausted.
+ stream2->SendRequestHeaders(
+ scoped_ptr<SpdyHeaderBlock>(
+ spdy_util_.ConstructGetHeaderBlock(url.spec())),
+ NO_MORE_DATA_TO_SEND);
+ data.RunFor(1);
+
+ // Active streams remain active.
+ EXPECT_EQ(kLastStreamId, stream2->stream_id());
+ EXPECT_EQ(2u, session->num_active_streams());
+
+ // Session is going away. Created and stalled streams were aborted.
+ EXPECT_EQ(SpdySession::STATE_GOING_AWAY, session->availability_state_);
+ EXPECT_EQ(ERR_ABORTED, delegate3.WaitForClose());
+ EXPECT_EQ(ERR_ABORTED, callback4.WaitForResult());
+ EXPECT_EQ(0u, session->num_created_streams());
+ EXPECT_EQ(0u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Read responses on remaining active streams.
+ data.RunFor(4);
+ EXPECT_EQ(OK, delegate1.WaitForClose());
+ EXPECT_EQ(kUploadData, delegate1.TakeReceivedData());
+ EXPECT_EQ(OK, delegate2.WaitForClose());
+ EXPECT_EQ(kUploadData, delegate2.TakeReceivedData());
+
+ // Session was destroyed.
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(session.get());
+}
+
+// Verifies that an unstalled pending stream creation racing with a new stream
+// creation doesn't violate the maximum stream concurrency. Regression test for
+// crbug.com/373858.
+TEST_P(SpdySessionTest, UnstallRacesWithStreamCreation) {
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
+ };
+
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ data.set_connect_data(connect_data);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
CreateNetworkSession();
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+ // Fix max_concurrent_streams to allow for one open stream.
+ session->max_concurrent_streams_ = 1;
+
+ // Create two streams: one synchronously, and one which stalls.
+ GURL url(kDefaultURL);
+ base::WeakPtr<SpdyStream> stream1 = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+
+ SpdyStreamRequest request2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM,
+ session,
+ url,
+ MEDIUM,
+ BoundNetLog(),
+ callback2.callback()));
+
+ EXPECT_EQ(1u, session->num_created_streams());
+ EXPECT_EQ(1u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Cancel the first stream. A callback to unstall the second stream was
+ // posted. Don't run it yet.
+ stream1->Cancel();
+
+ EXPECT_EQ(0u, session->num_created_streams());
+ EXPECT_EQ(0u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Create a third stream prior to the second stream's callback.
+ base::WeakPtr<SpdyStream> stream3 = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+
+ EXPECT_EQ(1u, session->num_created_streams());
+ EXPECT_EQ(0u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // NOW run the message loop. The unstalled stream will re-stall itself.
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(1u, session->num_created_streams());
+ EXPECT_EQ(1u, session->pending_create_stream_queue_size(MEDIUM));
+
+ // Cancel the third stream and run the message loop. Verify that the second
+ // stream creation now completes.
+ stream3->Cancel();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(1u, session->num_created_streams());
+ EXPECT_EQ(0u, session->pending_create_stream_queue_size(MEDIUM));
+ EXPECT_EQ(OK, callback2.WaitForResult());
+}
+
+TEST_P(SpdySessionTest, DeleteExpiredPushStreams) {
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ session_deps_.time_func = TheNearFuture;
+
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true));
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(2, RST_STREAM_REFUSED_STREAM));
+
+ scoped_ptr<SpdyFrame> push_a(spdy_util_.ConstructSpdyPush(
+ NULL, 0, 2, 1, "http://www.google.com/a.dat"));
+ scoped_ptr<SpdyFrame> push_a_body(
+ spdy_util_.ConstructSpdyBodyFrame(2, false));
+ scoped_ptr<SpdyFrame> push_b(spdy_util_.ConstructSpdyPush(
+ NULL, 0, 4, 1, "http://www.google.com/b.dat"));
+ MockWrite writes[] = {CreateMockWrite(*req, 0), CreateMockWrite(*rst, 4)};
+ MockRead reads[] = {
+ CreateMockRead(*push_a, 1), CreateMockRead(*push_a_body, 2),
+ CreateMockRead(*push_b, 3), MockRead(ASYNC, 0, 5), // EOF
+ };
+ DeterministicSocketData data(
+ reads, arraysize(reads), writes, arraysize(writes));
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
- CreateFakeSpdySession(spdy_session_pool_, key_);
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- session->buffered_spdy_framer_.reset(
- new BufferedSpdyFramer(spdy_util_.spdy_version(), false));
-
- // Create the associated stream and add to active streams.
- scoped_ptr<SpdyHeaderBlock> request_headers(
- spdy_util_.ConstructGetHeaderBlock("http://www.google.com/"));
-
- scoped_ptr<SpdyStream> stream(new SpdyStream(SPDY_REQUEST_RESPONSE_STREAM,
- session,
- GURL(),
- DEFAULT_PRIORITY,
- kSpdyStreamInitialWindowSize,
- kSpdyStreamInitialWindowSize,
- session->net_log_));
- stream->SendRequestHeaders(request_headers.Pass(), NO_MORE_DATA_TO_SEND);
- SpdyStream* stream_ptr = stream.get();
- session->InsertCreatedStream(stream.Pass());
- stream = session->ActivateCreatedStream(stream_ptr);
- session->InsertActivatedStream(stream.Pass());
+ // Process the principal request, and the first push stream request & body.
+ GURL url(kDefaultURL);
+ base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog());
+ test::StreamDelegateDoNothing delegate(spdy_stream);
+ spdy_stream->SetDelegate(&delegate);
- SpdyHeaderBlock headers;
- spdy_util_.AddUrlToHeaderBlock("http://www.google.com/a.dat", &headers);
+ scoped_ptr<SpdyHeaderBlock> headers(
+ spdy_util_.ConstructGetHeaderBlock(url.spec()));
+ spdy_stream->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND);
- // OnSynStream() expects |in_io_loop_| to be true.
- session->in_io_loop_ = true;
- session->OnSynStream(2, 1, 0, 0, true, false, headers);
- session->in_io_loop_ = false;
+ data.RunFor(3);
// Verify that there is one unclaimed push stream.
EXPECT_EQ(1u, session->num_unclaimed_pushed_streams());
@@ -931,19 +1233,37 @@ TEST_P(SpdySessionTest, DeleteExpiredPushStreams) {
GURL("http://www.google.com/a.dat"));
EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter);
- // Shift time to expire the push stream.
- g_time_delta = base::TimeDelta::FromSeconds(301);
+ if (session->flow_control_state_ ==
+ SpdySession::FLOW_CONTROL_STREAM_AND_SESSION) {
+ // Unclaimed push body consumed bytes from the session window.
+ EXPECT_EQ(kSpdySessionInitialWindowSize - kUploadDataSize,
+ session->session_recv_window_size_);
+ EXPECT_EQ(0, session->session_unacked_recv_window_bytes_);
+ }
- spdy_util_.AddUrlToHeaderBlock("http://www.google.com/b.dat", &headers);
- session->in_io_loop_ = true;
- session->OnSynStream(4, 1, 0, 0, true, false, headers);
- session->in_io_loop_ = false;
+ // Shift time to expire the push stream. Read the second SYN_STREAM,
+ // and verify a RST_STREAM was written.
+ g_time_delta = base::TimeDelta::FromSeconds(301);
+ data.RunFor(2);
// Verify that the second pushed stream evicted the first pushed stream.
EXPECT_EQ(1u, session->num_unclaimed_pushed_streams());
iter = session->unclaimed_pushed_streams_.find(
GURL("http://www.google.com/b.dat"));
EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter);
+
+ if (session->flow_control_state_ ==
+ SpdySession::FLOW_CONTROL_STREAM_AND_SESSION) {
+ // Verify that the session window reclaimed the evicted stream body.
+ EXPECT_EQ(kSpdySessionInitialWindowSize,
+ session->session_recv_window_size_);
+ EXPECT_EQ(kUploadDataSize, session->session_unacked_recv_window_bytes_);
+ }
+
+ // Read and process EOF.
+ data.RunFor(1);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(session == NULL);
}
TEST_P(SpdySessionTest, FailedPing) {
@@ -951,17 +1271,18 @@ TEST_P(SpdySessionTest, FailedPing) {
MockConnect connect_data(SYNCHRONOUS, OK);
MockRead reads[] = {
- MockRead(ASYNC, 0, 0, 0) // EOF
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
};
- scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1));
- DeterministicSocketData data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1, false));
+ scoped_ptr<SpdyFrame> goaway(
+ spdy_util_.ConstructSpdyGoAway(0, GOAWAY_PROTOCOL_ERROR, "Failed ping."));
+ MockWrite writes[] = {CreateMockWrite(*write_ping), CreateMockWrite(*goaway)};
+ StaticSocketDataProvider data(
+ reads, arraysize(reads), writes, arraysize(writes));
data.set_connect_data(connect_data);
- session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
-
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
- CreateDeterministicNetworkSession();
+ CreateNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
@@ -977,13 +1298,13 @@ TEST_P(SpdySessionTest, FailedPing) {
session->set_hung_interval(base::TimeDelta::FromSeconds(0));
// Send a PING frame.
- session->WritePingFrame(1);
+ session->WritePingFrame(1, false);
EXPECT_LT(0, session->pings_in_flight());
EXPECT_GE(session->next_ping_id(), static_cast<uint32>(1));
EXPECT_TRUE(session->check_ping_status_pending());
// Assert session is not closed.
- EXPECT_FALSE(session->IsClosed());
+ EXPECT_TRUE(session->IsAvailable());
EXPECT_LT(0u, session->num_active_streams() + session->num_created_streams());
EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_));
@@ -992,11 +1313,10 @@ TEST_P(SpdySessionTest, FailedPing) {
base::TimeTicks now = base::TimeTicks::Now();
session->last_activity_time_ = now - base::TimeDelta::FromSeconds(1);
session->CheckPingStatus(now);
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
-
- data.RunFor(1);
EXPECT_EQ(NULL, spdy_stream1.get());
}
@@ -1020,14 +1340,17 @@ TEST_P(SpdySessionTest, OnSettings) {
MockRead(ASYNC, 0, 1),
};
- DeterministicSocketData data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
+ MockWrite writes[] = {
+ CreateMockWrite(*settings_ack, 2),
+ };
+
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
MockConnect connect_data(SYNCHRONOUS, OK);
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -1054,6 +1377,11 @@ TEST_P(SpdySessionTest, OnSettings) {
EXPECT_EQ(OK, stream_releaser.WaitForResult());
data.RunFor(1);
+ if (spdy_util_.spdy_version() >= SPDY4) {
+ // Allow the SETTINGS+ACK to write, so the session finishes draining.
+ data.RunFor(1);
+ }
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -1062,6 +1390,11 @@ TEST_P(SpdySessionTest, OnSettings) {
// also clears the persisted data. Verify that persisted data is
// correct.
TEST_P(SpdySessionTest, ClearSettings) {
+ if (spdy_util_.spdy_version() >= SPDY4) {
+ // SPDY4 doesn't include settings persistence, or a CLEAR_SETTINGS flag.
+ // Flag 0x1, CLEAR_SETTINGS in SPDY3, is instead settings ACK in SPDY4.
+ return;
+ }
session_deps_.host_resolver->set_synchronous_mode(true);
SettingsMap new_settings;
@@ -1082,9 +1415,6 @@ TEST_P(SpdySessionTest, ClearSettings) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
// Initialize the SpdySetting with the default.
@@ -1153,9 +1483,6 @@ TEST_P(SpdySessionTest, CancelPendingCreateStream) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
// Initialize the SpdySetting with 1 max concurrent streams.
@@ -1229,7 +1556,7 @@ TEST_P(SpdySessionTest, SendInitialDataOnNewSession) {
kSessionFlowControlStreamId,
kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize));
std::vector<MockWrite> writes;
- if (GetParam() == kProtoHTTP2Draft04) {
+ if (GetParam() == kProtoSPDY4) {
writes.push_back(
MockWrite(ASYNC,
kHttp2ConnectionHeaderPrefix,
@@ -1256,9 +1583,6 @@ TEST_P(SpdySessionTest, SendInitialDataOnNewSession) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
spdy_session_pool_->http_server_properties()->SetSpdySetting(
@@ -1309,9 +1633,6 @@ TEST_P(SpdySessionTest, Initialize) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -1340,7 +1661,7 @@ TEST_P(SpdySessionTest, Initialize) {
EXPECT_NE(log.bound().source().id, socket_source.id);
}
-TEST_P(SpdySessionTest, CloseSessionOnError) {
+TEST_P(SpdySessionTest, NetLogOnSessionGoaway) {
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
@@ -1354,9 +1675,6 @@ TEST_P(SpdySessionTest, CloseSessionOnError) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
CapturingBoundNetLog log;
@@ -1385,6 +1703,53 @@ TEST_P(SpdySessionTest, CloseSessionOnError) {
CapturingNetLog::CapturedEntry entry = entries[pos];
int error_code = 0;
ASSERT_TRUE(entry.GetNetErrorCode(&error_code));
+ EXPECT_EQ(OK, error_code);
+ } else {
+ ADD_FAILURE();
+ }
+}
+
+TEST_P(SpdySessionTest, NetLogOnSessionEOF) {
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 0, 0) // EOF
+ };
+
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+ data.set_connect_data(connect_data);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ CreateNetworkSession();
+
+ CapturingBoundNetLog log;
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, log.bound());
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_));
+
+ // Flush the read completion task.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
+ EXPECT_TRUE(session == NULL);
+
+ // Check that the NetLog was filled reasonably.
+ net::CapturingNetLog::CapturedEntryList entries;
+ log.GetEntries(&entries);
+ EXPECT_LT(0u, entries.size());
+
+ // Check that we logged SPDY_SESSION_CLOSE correctly.
+ int pos =
+ net::ExpectLogContainsSomewhere(entries,
+ 0,
+ net::NetLog::TYPE_SPDY_SESSION_CLOSE,
+ net::NetLog::PHASE_NONE);
+
+ if (pos < static_cast<int>(entries.size())) {
+ CapturingNetLog::CapturedEntry entry = entries[pos];
+ int error_code = 0;
+ ASSERT_TRUE(entry.GetNetErrorCode(&error_code));
EXPECT_EQ(ERR_CONNECTION_CLOSED, error_code);
} else {
ADD_FAILURE();
@@ -1429,15 +1794,12 @@ TEST_P(SpdySessionTest, OutOfOrderSynStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream_lowest =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
@@ -1502,15 +1864,12 @@ TEST_P(SpdySessionTest, CancelStream) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, HIGHEST, BoundNetLog());
@@ -1519,7 +1878,7 @@ TEST_P(SpdySessionTest, CancelStream) {
test::StreamDelegateDoNothing delegate1(spdy_stream1);
spdy_stream1->SetDelegate(&delegate1);
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream2 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url2, LOWEST, BoundNetLog());
@@ -1575,22 +1934,19 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
session, url1, HIGHEST, BoundNetLog());
ASSERT_TRUE(spdy_stream1.get() != NULL);
EXPECT_EQ(0u, spdy_stream1->stream_id());
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream2 =
CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
session, url2, LOWEST, BoundNetLog());
@@ -1626,6 +1982,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) {
EXPECT_TRUE(delegate1.StreamIsClosed());
EXPECT_TRUE(delegate2.StreamIsClosed());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -1649,22 +2006,19 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
session, url1, HIGHEST, BoundNetLog());
ASSERT_TRUE(spdy_stream1.get() != NULL);
EXPECT_EQ(0u, spdy_stream1->stream_id());
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream2 =
CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
session, url2, LOWEST, BoundNetLog());
@@ -1702,6 +2056,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) {
EXPECT_TRUE(delegate1.StreamIsClosed());
EXPECT_TRUE(delegate2.StreamIsClosed());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -1730,22 +2085,19 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
ASSERT_TRUE(spdy_stream1.get() != NULL);
EXPECT_EQ(0u, spdy_stream1->stream_id());
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream2 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url2, MEDIUM, BoundNetLog());
@@ -1786,6 +2138,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) {
EXPECT_TRUE(delegate1.StreamIsClosed());
EXPECT_TRUE(delegate2.StreamIsClosed());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -1814,22 +2167,19 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
ASSERT_TRUE(spdy_stream1.get() != NULL);
EXPECT_EQ(0u, spdy_stream1->stream_id());
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream2 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url2, MEDIUM, BoundNetLog());
@@ -1872,6 +2222,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) {
EXPECT_TRUE(delegate1.StreamIsClosed());
EXPECT_TRUE(delegate2.StreamIsClosed());
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
@@ -1886,7 +2237,7 @@ class SessionClosingDelegate : public test::StreamDelegateDoNothing {
virtual ~SessionClosingDelegate() {}
virtual void OnClose(int status) OVERRIDE {
- session_to_close_->CloseSessionOnError(ERR_ABORTED, "Aborted");
+ session_to_close_->CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Error");
}
private:
@@ -1902,27 +2253,31 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) {
scoped_ptr<SpdyFrame> req(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true));
+ scoped_ptr<SpdyFrame> rst(
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL));
+ scoped_ptr<SpdyFrame> goaway(
+ spdy_util_.ConstructSpdyGoAway(0, GOAWAY_PROTOCOL_ERROR, "Error"));
+ // The GOAWAY has higher-priority than the RST_STREAM, and is written first
+ // despite being queued second.
MockWrite writes[] = {
- CreateMockWrite(*req, 0),
+ CreateMockWrite(*req, 0), CreateMockWrite(*goaway, 1),
+ CreateMockWrite(*rst, 2),
};
MockRead reads[] = {
- MockRead(ASYNC, 0, 1) // EOF
+ MockRead(ASYNC, 0, 3) // EOF
};
DeterministicSocketData data(reads, arraysize(reads),
writes, arraysize(writes));
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -1949,10 +2304,13 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) {
EXPECT_EQ(NULL, spdy_stream.get());
EXPECT_TRUE(delegate.StreamIsClosed());
+
+ data.RunFor(2); // Write the RST_STREAM & GOAWAY.
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(session == NULL);
}
-TEST_P(SpdySessionTest, VerifyDomainAuthentication) {
+TEST_P(SpdySessionTest, DISABLED_VerifyDomainAuthentication) {
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
@@ -1994,7 +2352,8 @@ TEST_P(SpdySessionTest, VerifyDomainAuthentication) {
EXPECT_FALSE(session->VerifyDomainAuthentication("mail.google.com"));
}
-TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) {
+// TODO(rch): re-enable this.
+TEST_P(SpdySessionTest, DISABLED_ConnectionPooledWithTlsChannelId) {
session_deps_.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(SYNCHRONOUS, OK);
@@ -2048,6 +2407,7 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
new_settings[kSpdySettingsIds1] =
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
scoped_ptr<SpdyFrame> req1(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
scoped_ptr<SpdyFrame> req2(
@@ -2055,9 +2415,10 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
scoped_ptr<SpdyFrame> req3(
spdy_util_.ConstructSpdyGet(NULL, 0, false, 5, LOWEST, true));
MockWrite writes[] = {
- CreateMockWrite(*req1, 1),
- CreateMockWrite(*req2, 4),
- CreateMockWrite(*req3, 7),
+ CreateMockWrite(*settings_ack, 1),
+ CreateMockWrite(*req1, 2),
+ CreateMockWrite(*req2, 5),
+ CreateMockWrite(*req3, 8),
};
// Set up the socket so we read a SETTINGS frame that sets max concurrent
@@ -2076,13 +2437,13 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
MockRead reads[] = {
CreateMockRead(*settings_frame),
- CreateMockRead(*resp1, 2),
- CreateMockRead(*body1, 3),
- CreateMockRead(*resp2, 5),
- CreateMockRead(*body2, 6),
- CreateMockRead(*resp3, 8),
- CreateMockRead(*body3, 9),
- MockRead(ASYNC, 0, 10) // EOF
+ CreateMockRead(*resp1, 3),
+ CreateMockRead(*body1, 4),
+ CreateMockRead(*resp2, 6),
+ CreateMockRead(*body2, 7),
+ CreateMockRead(*resp3, 9),
+ CreateMockRead(*body3, 10),
+ MockRead(ASYNC, 0, 11) // EOF
};
DeterministicSocketData data(reads, arraysize(reads),
@@ -2090,9 +2451,6 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -2101,7 +2459,7 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
// Read the settings frame.
data.RunFor(1);
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, LOWEST, BoundNetLog());
@@ -2111,7 +2469,7 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
spdy_stream1->SetDelegate(&delegate1);
TestCompletionCallback callback2;
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
SpdyStreamRequest request2;
ASSERT_EQ(ERR_IO_PENDING,
request2.StartRequest(
@@ -2119,7 +2477,7 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
session, url2, LOWEST, BoundNetLog(), callback2.callback()));
TestCompletionCallback callback3;
- GURL url3("http://www.google.com");
+ GURL url3(kDefaultURL);
SpdyStreamRequest request3;
ASSERT_EQ(ERR_IO_PENDING,
request3.StartRequest(
@@ -2137,7 +2495,7 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) {
// Run until 1st stream is activated and then closed.
EXPECT_EQ(0u, delegate1.stream_id());
- data.RunFor(3);
+ data.RunFor(4);
EXPECT_EQ(NULL, spdy_stream1.get());
EXPECT_EQ(1u, delegate1.stream_id());
@@ -2213,9 +2571,6 @@ TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) {
data.set_connect_data(connect_data);
session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -2229,7 +2584,7 @@ TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) {
ASSERT_TRUE(spdy_stream != NULL);
}
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
session, url1, LOWEST, BoundNetLog());
@@ -2237,7 +2592,7 @@ TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) {
EXPECT_EQ(0u, spdy_stream1->stream_id());
TestCompletionCallback callback2;
- GURL url2("http://www.google.com");
+ GURL url2(kDefaultURL);
SpdyStreamRequest request2;
ASSERT_EQ(ERR_IO_PENDING,
request2.StartRequest(
@@ -2245,7 +2600,7 @@ TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) {
callback2.callback()));
TestCompletionCallback callback3;
- GURL url3("http://www.google.com");
+ GURL url3(kDefaultURL);
SpdyStreamRequest request3;
ASSERT_EQ(ERR_IO_PENDING,
request3.StartRequest(
@@ -2334,15 +2689,12 @@ TEST_P(SpdySessionTest, ReadDataWithoutYielding) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
@@ -2428,15 +2780,12 @@ TEST_P(SpdySessionTest, TestYieldingDuringReadData) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
@@ -2544,15 +2893,12 @@ TEST_P(SpdySessionTest, TestYieldingDuringAsyncReadData) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
@@ -2618,15 +2964,12 @@ TEST_P(SpdySessionTest, GoAwayWhileInDoReadLoop) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream1 =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url1, MEDIUM, BoundNetLog());
@@ -2718,7 +3061,7 @@ TEST_P(SpdySessionTest, CloseOneIdleConnection) {
// Create an idle SPDY session.
SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session1 =
CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
EXPECT_FALSE(pool->IsStalled());
@@ -2778,14 +3121,14 @@ TEST_P(SpdySessionTest, CloseOneIdleConnectionWithAlias) {
// Create an idle SPDY session.
SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session1 =
CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
EXPECT_FALSE(pool->IsStalled());
// Set up an alias for the idle SPDY session, increasing its ref count to 2.
SpdySessionKey key2(HostPortPair("2.com", 80), ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
HostResolver::RequestInfo info(key2.host_port_pair());
AddressList addresses;
// Pre-populate the DNS cache, since a synchronous resolution is required in
@@ -2864,9 +3207,9 @@ TEST_P(SpdySessionTest, CloseSessionOnIdleWhenPoolStalled) {
HttpNetworkSession::NORMAL_SOCKET_POOL);
// Create a SPDY session.
- GURL url1("http://www.google.com");
+ GURL url1(kDefaultURL);
SpdySessionKey key1(HostPortPair(url1.host(), 80),
- ProxyServer::Direct(), kPrivacyModeDisabled);
+ ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> session1 =
CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
EXPECT_FALSE(pool->IsStalled());
@@ -2927,9 +3270,9 @@ TEST_P(SpdySessionTest, SpdySessionKeyPrivacyMode) {
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key_privacy_enabled(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeEnabled);
+ PRIVACY_MODE_ENABLED);
SpdySessionKey key_privacy_disabled(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_enabled));
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_disabled));
@@ -2968,7 +3311,7 @@ class StreamCreatingDelegate : public test::StreamDelegateDoNothing {
virtual ~StreamCreatingDelegate() {}
virtual void OnClose(int status) OVERRIDE {
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
ignore_result(
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session_, url, MEDIUM, BoundNetLog()));
@@ -3003,15 +3346,12 @@ TEST_P(SpdySessionTest, CreateStreamOnStreamReset) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
- GURL url("http://www.google.com");
+ GURL url(kDefaultURL);
base::WeakPtr<SpdyStream> spdy_stream =
CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM,
session, url, MEDIUM, BoundNetLog());
@@ -3065,15 +3405,17 @@ TEST_P(SpdySessionTest, UpdateStreamsSendWindowSize) {
MockRead(ASYNC, 0, 1) // EOF
};
- session_deps_.host_resolver->set_synchronous_mode(true);
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
+ MockWrite writes[] = {
+ CreateMockWrite(*settings_ack, 2),
+ };
- scoped_ptr<DeterministicSocketData> data(
- new DeterministicSocketData(reads, arraysize(reads), NULL, 0));
- data->set_connect_data(connect_data);
- session_deps_.deterministic_socket_factory->AddSocketDataProvider(data.get());
+ session_deps_.host_resolver->set_synchronous_mode(true);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
CreateDeterministicNetworkSession();
@@ -3086,7 +3428,7 @@ TEST_P(SpdySessionTest, UpdateStreamsSendWindowSize) {
TestCompletionCallback callback1;
EXPECT_NE(spdy_stream1->send_window_size(), window_size);
- data->RunFor(1); // Process the SETTINGS frame, but not the EOF
+ data.RunFor(1); // Process the SETTINGS frame, but not the EOF
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(session->stream_initial_send_window_size(), window_size);
EXPECT_EQ(spdy_stream1->send_window_size(), window_size);
@@ -3134,9 +3476,6 @@ TEST_P(SpdySessionTest, AdjustRecvWindowSize) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
@@ -3224,9 +3563,6 @@ TEST_P(SpdySessionTest, SessionFlowControlInactiveStream) {
data.set_connect_data(connect_data);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
@@ -3302,9 +3638,6 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -3380,9 +3713,6 @@ TEST_P(SpdySessionTest, SessionFlowControlNoSendLeaks) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -3472,9 +3802,6 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) {
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
-
CreateDeterministicNetworkSession();
base::WeakPtr<SpdySession> session =
@@ -3633,7 +3960,6 @@ void SpdySessionTest::RunResumeAfterUnstallTest(
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(), delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
}
@@ -3830,12 +4156,10 @@ TEST_P(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) {
EXPECT_TRUE(delegate1.send_headers_completed());
EXPECT_EQ("200", delegate1.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate1.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(), delegate1.TakeReceivedData());
EXPECT_TRUE(delegate2.send_headers_completed());
EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate2.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(), delegate2.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
@@ -4019,7 +4343,6 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedStreams) {
EXPECT_TRUE(delegate2.send_headers_completed());
EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate2.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(), delegate2.TakeReceivedData());
EXPECT_TRUE(delegate3.send_headers_completed());
@@ -4125,6 +4448,7 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) {
// Close the session (since we can't do it from within the delegate
// method, since it's in the stream's loop).
session->CloseSessionOnError(ERR_CONNECTION_CLOSED, "Closing session");
+ base::RunLoop().RunUntilIdle();
EXPECT_TRUE(session == NULL);
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
@@ -4141,4 +4465,133 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) {
EXPECT_TRUE(data.at_write_eof());
}
+TEST_P(SpdySessionTest, GoAwayOnSessionFlowControlError) {
+ if (GetParam() < kProtoSPDY31)
+ return;
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true));
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(
+ 0,
+ GOAWAY_FLOW_CONTROL_ERROR,
+ "delta_window_size is 6 in DecreaseRecvWindowSize, which is larger than "
+ "the receive window size of 1"));
+ MockWrite writes[] = {
+ CreateMockWrite(*req, 0), CreateMockWrite(*goaway, 3),
+ };
+
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1), CreateMockRead(*body, 2),
+ };
+
+ DeterministicSocketData data(
+ reads, arraysize(reads), writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ CreateDeterministicNetworkSession();
+
+ base::WeakPtr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+
+ GURL url(kDefaultURL);
+ base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, LOWEST, BoundNetLog());
+ ASSERT_TRUE(spdy_stream.get() != NULL);
+ test::StreamDelegateDoNothing delegate(spdy_stream);
+ spdy_stream->SetDelegate(&delegate);
+
+ scoped_ptr<SpdyHeaderBlock> headers(
+ spdy_util_.ConstructGetHeaderBlock(url.spec()));
+ spdy_stream->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND);
+
+ data.RunFor(1); // Write request.
+
+ // Put session on the edge of overflowing it's recv window.
+ session->session_recv_window_size_ = 1;
+
+ // Read response headers & body. Body overflows the session window, and a
+ // goaway is written.
+ data.RunFor(3);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(ERR_SPDY_FLOW_CONTROL_ERROR, delegate.WaitForClose());
+ EXPECT_TRUE(session == NULL);
+}
+
+TEST_P(SpdySessionTest, SplitHeaders) {
+ GURL kStreamUrl("http://www.google.com/foo.dat");
+ SpdyHeaderBlock headers;
+ spdy_util_.AddUrlToHeaderBlock(kStreamUrl.spec(), &headers);
+ headers["alpha"] = "beta";
+
+ SpdyHeaderBlock request_headers;
+ SpdyHeaderBlock response_headers;
+
+ SplitPushedHeadersToRequestAndResponse(
+ headers, spdy_util_.spdy_version(), &request_headers, &response_headers);
+
+ SpdyHeaderBlock::const_iterator it = response_headers.find("alpha");
+ std::string alpha_val =
+ (it == response_headers.end()) ? std::string() : it->second;
+ EXPECT_EQ("beta", alpha_val);
+
+ GURL request_url =
+ GetUrlFromHeaderBlock(request_headers, spdy_util_.spdy_version(), true);
+ EXPECT_EQ(kStreamUrl, request_url);
+}
+
+TEST(MapFramerErrorToProtocolError, MapsValues) {
+ CHECK_EQ(
+ SPDY_ERROR_INVALID_CONTROL_FRAME,
+ MapFramerErrorToProtocolError(SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+ CHECK_EQ(
+ SPDY_ERROR_INVALID_DATA_FRAME_FLAGS,
+ MapFramerErrorToProtocolError(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS));
+ CHECK_EQ(
+ SPDY_ERROR_GOAWAY_FRAME_CORRUPT,
+ MapFramerErrorToProtocolError(SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT));
+ CHECK_EQ(SPDY_ERROR_UNEXPECTED_FRAME,
+ MapFramerErrorToProtocolError(SpdyFramer::SPDY_UNEXPECTED_FRAME));
+}
+
+TEST(MapFramerErrorToNetError, MapsValue) {
+ CHECK_EQ(ERR_SPDY_PROTOCOL_ERROR,
+ MapFramerErrorToNetError(SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+ CHECK_EQ(ERR_SPDY_COMPRESSION_ERROR,
+ MapFramerErrorToNetError(SpdyFramer::SPDY_COMPRESS_FAILURE));
+ CHECK_EQ(ERR_SPDY_COMPRESSION_ERROR,
+ MapFramerErrorToNetError(SpdyFramer::SPDY_DECOMPRESS_FAILURE));
+ CHECK_EQ(
+ ERR_SPDY_FRAME_SIZE_ERROR,
+ MapFramerErrorToNetError(SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE));
+}
+
+TEST(MapRstStreamStatusToProtocolError, MapsValues) {
+ CHECK_EQ(STATUS_CODE_PROTOCOL_ERROR,
+ MapRstStreamStatusToProtocolError(RST_STREAM_PROTOCOL_ERROR));
+ CHECK_EQ(STATUS_CODE_FRAME_SIZE_ERROR,
+ MapRstStreamStatusToProtocolError(RST_STREAM_FRAME_SIZE_ERROR));
+ CHECK_EQ(STATUS_CODE_ENHANCE_YOUR_CALM,
+ MapRstStreamStatusToProtocolError(RST_STREAM_ENHANCE_YOUR_CALM));
+}
+
+TEST(MapNetErrorToGoAwayStatus, MapsValue) {
+ CHECK_EQ(GOAWAY_INADEQUATE_SECURITY,
+ MapNetErrorToGoAwayStatus(ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY));
+ CHECK_EQ(GOAWAY_FLOW_CONTROL_ERROR,
+ MapNetErrorToGoAwayStatus(ERR_SPDY_FLOW_CONTROL_ERROR));
+ CHECK_EQ(GOAWAY_PROTOCOL_ERROR,
+ MapNetErrorToGoAwayStatus(ERR_SPDY_PROTOCOL_ERROR));
+ CHECK_EQ(GOAWAY_COMPRESSION_ERROR,
+ MapNetErrorToGoAwayStatus(ERR_SPDY_COMPRESSION_ERROR));
+ CHECK_EQ(GOAWAY_FRAME_SIZE_ERROR,
+ MapNetErrorToGoAwayStatus(ERR_SPDY_FRAME_SIZE_ERROR));
+ CHECK_EQ(GOAWAY_PROTOCOL_ERROR, MapNetErrorToGoAwayStatus(ERR_UNEXPECTED));
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_stream.cc b/chromium/net/spdy/spdy_stream.cc
index ccf48ecd7d2..40ec654bba4 100644
--- a/chromium/net/spdy/spdy_stream.cc
+++ b/chromium/net/spdy/spdy_stream.cc
@@ -85,32 +85,26 @@ SpdyStream::SpdyStream(SpdyStreamType type,
int32 initial_recv_window_size,
const BoundNetLog& net_log)
: type_(type),
- weak_ptr_factory_(this),
- in_do_loop_(false),
- continue_buffering_data_(type_ == SPDY_PUSH_STREAM),
stream_id_(0),
url_(url),
priority_(priority),
- slot_(0),
send_stalled_by_flow_control_(false),
send_window_size_(initial_send_window_size),
recv_window_size_(initial_recv_window_size),
unacked_recv_window_bytes_(0),
session_(session),
delegate_(NULL),
- send_status_(
- (type_ == SPDY_PUSH_STREAM) ?
- NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND),
+ pending_send_status_(MORE_DATA_TO_SEND),
request_time_(base::Time::Now()),
response_headers_status_(RESPONSE_HEADERS_ARE_INCOMPLETE),
- io_state_((type_ == SPDY_PUSH_STREAM) ? STATE_IDLE : STATE_NONE),
+ io_state_(STATE_IDLE),
response_status_(OK),
net_log_(net_log),
raw_received_bytes_(0),
send_bytes_(0),
recv_bytes_(0),
- just_completed_frame_type_(DATA),
- just_completed_frame_size_(0) {
+ write_handler_guard_(false),
+ weak_ptr_factory_(this) {
CHECK(type_ == SPDY_BIDIRECTIONAL_STREAM ||
type_ == SPDY_REQUEST_RESPONSE_STREAM ||
type_ == SPDY_PUSH_STREAM);
@@ -119,7 +113,7 @@ SpdyStream::SpdyStream(SpdyStreamType type,
}
SpdyStream::~SpdyStream() {
- CHECK(!in_do_loop_);
+ CHECK(!write_handler_guard_);
UpdateHistograms();
}
@@ -128,20 +122,25 @@ void SpdyStream::SetDelegate(Delegate* delegate) {
CHECK(delegate);
delegate_ = delegate;
- if (type_ == SPDY_PUSH_STREAM) {
- DCHECK(continue_buffering_data_);
+ CHECK(io_state_ == STATE_IDLE ||
+ io_state_ == STATE_HALF_CLOSED_LOCAL_UNCLAIMED ||
+ io_state_ == STATE_RESERVED_REMOTE);
+
+ if (io_state_ == STATE_HALF_CLOSED_LOCAL_UNCLAIMED) {
+ DCHECK_EQ(type_, SPDY_PUSH_STREAM);
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&SpdyStream::PushedStreamReplayData, GetWeakPtr()));
+ base::Bind(&SpdyStream::PushedStreamReplay, GetWeakPtr()));
}
}
-void SpdyStream::PushedStreamReplayData() {
+void SpdyStream::PushedStreamReplay() {
DCHECK_EQ(type_, SPDY_PUSH_STREAM);
DCHECK_NE(stream_id_, 0u);
- DCHECK(continue_buffering_data_);
+ CHECK_EQ(stream_id_ % 2, 0u);
- continue_buffering_data_ = false;
+ CHECK_EQ(io_state_, STATE_HALF_CLOSED_LOCAL_UNCLAIMED);
+ io_state_ = STATE_HALF_CLOSED_LOCAL;
// The delegate methods called below may delete |this|, so use
// |weak_this| to detect that.
@@ -156,7 +155,7 @@ void SpdyStream::PushedStreamReplayData() {
// we're waiting for another HEADERS frame, and we had better not
// have any pending data frames.
CHECK(weak_this);
- if (!pending_buffers_.empty()) {
+ if (!pending_recv_data_.empty()) {
LogStreamError(ERR_SPDY_PROTOCOL_ERROR,
"Data received with incomplete headers.");
session_->CloseActiveStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR);
@@ -170,10 +169,10 @@ void SpdyStream::PushedStreamReplayData() {
response_headers_status_ = RESPONSE_HEADERS_ARE_COMPLETE;
- while (!pending_buffers_.empty()) {
- // Take ownership of the first element of |pending_buffers_|.
- scoped_ptr<SpdyBuffer> buffer(pending_buffers_.front());
- pending_buffers_.weak_erase(pending_buffers_.begin());
+ while (!pending_recv_data_.empty()) {
+ // Take ownership of the first element of |pending_recv_data_|.
+ scoped_ptr<SpdyBuffer> buffer(pending_recv_data_.front());
+ pending_recv_data_.weak_erase(pending_recv_data_.begin());
bool eof = (buffer == NULL);
@@ -185,31 +184,30 @@ void SpdyStream::PushedStreamReplayData() {
return;
if (eof) {
- DCHECK(pending_buffers_.empty());
+ DCHECK(pending_recv_data_.empty());
session_->CloseActiveStream(stream_id_, OK);
DCHECK(!weak_this);
- // |pending_buffers_| is invalid at this point.
+ // |pending_recv_data_| is invalid at this point.
break;
}
}
}
scoped_ptr<SpdyFrame> SpdyStream::ProduceSynStreamFrame() {
- CHECK_EQ(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE);
+ CHECK_EQ(io_state_, STATE_IDLE);
CHECK(request_headers_);
CHECK_GT(stream_id_, 0u);
SpdyControlFlags flags =
- (send_status_ == NO_MORE_DATA_TO_SEND) ?
+ (pending_send_status_ == NO_MORE_DATA_TO_SEND) ?
CONTROL_FLAG_FIN : CONTROL_FLAG_NONE;
scoped_ptr<SpdyFrame> frame(session_->CreateSynStream(
- stream_id_, priority_, slot_, flags, *request_headers_));
+ stream_id_, priority_, flags, *request_headers_));
send_time_ = base::TimeTicks::Now();
return frame.Pass();
}
void SpdyStream::DetachDelegate() {
- CHECK(!in_do_loop_);
DCHECK(!IsClosed());
delegate_ = NULL;
Cancel();
@@ -399,7 +397,7 @@ int SpdyStream::OnInitialResponseHeadersReceived(
case SPDY_BIDIRECTIONAL_STREAM:
// For a bidirectional stream, we're ready for the response
// headers once we've finished sending the request headers.
- if (io_state_ < STATE_IDLE) {
+ if (io_state_ == STATE_IDLE) {
session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR,
"Response received before request sent");
return ERR_SPDY_PROTOCOL_ERROR;
@@ -408,10 +406,8 @@ int SpdyStream::OnInitialResponseHeadersReceived(
case SPDY_REQUEST_RESPONSE_STREAM:
// For a request/response stream, we're ready for the response
- // headers once we've finished sending the request headers and
- // the request body (if we have one).
- if ((io_state_ < STATE_IDLE) || (send_status_ == MORE_DATA_TO_SEND) ||
- pending_send_data_.get()) {
+ // headers once we've finished sending the request headers.
+ if (io_state_ == STATE_IDLE) {
session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR,
"Response received before request sent");
return ERR_SPDY_PROTOCOL_ERROR;
@@ -419,15 +415,21 @@ int SpdyStream::OnInitialResponseHeadersReceived(
break;
case SPDY_PUSH_STREAM:
- // For a push stream, we're ready immediately.
- DCHECK_EQ(send_status_, NO_MORE_DATA_TO_SEND);
- DCHECK_EQ(io_state_, STATE_IDLE);
+ // Push streams transition to a locally half-closed state upon headers.
+ // We must continue to buffer data while waiting for a call to
+ // SetDelegate() (which may not ever happen).
+ CHECK_EQ(io_state_, STATE_RESERVED_REMOTE);
+ if (!delegate_) {
+ io_state_ = STATE_HALF_CLOSED_LOCAL_UNCLAIMED;
+ } else {
+ io_state_ = STATE_HALF_CLOSED_LOCAL;
+ }
break;
}
metrics_.StartStream();
- DCHECK_EQ(io_state_, STATE_IDLE);
+ DCHECK_NE(io_state_, STATE_IDLE);
response_time_ = response_time;
recv_first_byte_time_ = recv_first_byte_time;
@@ -451,20 +453,30 @@ int SpdyStream::OnAdditionalResponseHeadersReceived(
return MergeWithResponseHeaders(additional_response_headers);
}
+void SpdyStream::OnPushPromiseHeadersReceived(const SpdyHeaderBlock& headers) {
+ CHECK(!request_headers_.get());
+ CHECK_EQ(io_state_, STATE_IDLE);
+ CHECK_EQ(type_, SPDY_PUSH_STREAM);
+ DCHECK(!delegate_);
+
+ io_state_ = STATE_RESERVED_REMOTE;
+ request_headers_.reset(new SpdyHeaderBlock(headers));
+}
+
void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
DCHECK(session_->IsStreamActive(stream_id_));
// If we're still buffering data for a push stream, we will do the
// check for data received with incomplete headers in
// PushedStreamReplayData().
- if (!delegate_ || continue_buffering_data_) {
+ if (io_state_ == STATE_HALF_CLOSED_LOCAL_UNCLAIMED) {
DCHECK_EQ(type_, SPDY_PUSH_STREAM);
// It should be valid for this to happen in the server push case.
// We'll return received data when delegate gets attached to the stream.
if (buffer) {
- pending_buffers_.push_back(buffer.release());
+ pending_recv_data_.push_back(buffer.release());
} else {
- pending_buffers_.push_back(NULL);
+ pending_recv_data_.push_back(NULL);
metrics_.StopStream();
// Note: we leave the stream open in the session until the stream
// is claimed.
@@ -485,8 +497,15 @@ void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
if (!buffer) {
metrics_.StopStream();
- // Deletes |this|.
- session_->CloseActiveStream(stream_id_, OK);
+ if (io_state_ == STATE_OPEN) {
+ io_state_ = STATE_HALF_CLOSED_REMOTE;
+ } else if (io_state_ == STATE_HALF_CLOSED_LOCAL) {
+ io_state_ = STATE_CLOSED;
+ // Deletes |this|.
+ session_->CloseActiveStream(stream_id_, OK);
+ } else {
+ NOTREACHED() << io_state_;
+ }
return;
}
@@ -509,16 +528,81 @@ void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type,
size_t frame_size) {
+ DCHECK_NE(type_, SPDY_PUSH_STREAM);
+
if (frame_size < session_->GetFrameMinimumSize() ||
frame_size > session_->GetFrameMaximumSize()) {
NOTREACHED();
return;
}
- if (IsClosed())
+ CHECK(frame_type == SYN_STREAM ||
+ frame_type == DATA) << frame_type;
+
+ int result = (frame_type == SYN_STREAM) ?
+ OnRequestHeadersSent() : OnDataSent(frame_size);
+ if (result == ERR_IO_PENDING) {
+ // The write operation hasn't completed yet.
return;
- just_completed_frame_type_ = frame_type;
- just_completed_frame_size_ = frame_size;
- DoLoop(OK);
+ }
+
+ if (pending_send_status_ == NO_MORE_DATA_TO_SEND) {
+ if(io_state_ == STATE_OPEN) {
+ io_state_ = STATE_HALF_CLOSED_LOCAL;
+ } else if(io_state_ == STATE_HALF_CLOSED_REMOTE) {
+ io_state_ = STATE_CLOSED;
+ } else {
+ NOTREACHED() << io_state_;
+ }
+ }
+ // Notify delegate of write completion. Must not destroy |this|.
+ CHECK(delegate_);
+ {
+ base::WeakPtr<SpdyStream> weak_this = GetWeakPtr();
+ write_handler_guard_ = true;
+ if (frame_type == SYN_STREAM) {
+ delegate_->OnRequestHeadersSent();
+ } else {
+ delegate_->OnDataSent();
+ }
+ CHECK(weak_this);
+ write_handler_guard_ = false;
+ }
+
+ if (io_state_ == STATE_CLOSED) {
+ // Deletes |this|.
+ session_->CloseActiveStream(stream_id_, OK);
+ }
+}
+
+int SpdyStream::OnRequestHeadersSent() {
+ CHECK_EQ(io_state_, STATE_IDLE);
+ CHECK_NE(stream_id_, 0u);
+
+ io_state_ = STATE_OPEN;
+ return OK;
+}
+
+int SpdyStream::OnDataSent(size_t frame_size) {
+ CHECK(io_state_ == STATE_OPEN ||
+ io_state_ == STATE_HALF_CLOSED_REMOTE) << io_state_;
+
+ size_t frame_payload_size = frame_size - session_->GetDataFrameMinimumSize();
+
+ CHECK_GE(frame_size, session_->GetDataFrameMinimumSize());
+ CHECK_LE(frame_payload_size, session_->GetDataFrameMaximumPayload());
+
+ send_bytes_ += frame_payload_size;
+
+ // If more data is available to send, dispatch it and
+ // return that the write operation is still ongoing.
+ pending_send_data_->DidConsume(frame_payload_size);
+ if (pending_send_data_->BytesRemaining() > 0) {
+ QueueNextDataFrame();
+ return ERR_IO_PENDING;
+ } else {
+ pending_send_data_ = NULL;
+ return OK;
+ }
}
SpdyMajorVersion SpdyStream::GetProtocolVersion() const {
@@ -532,7 +616,8 @@ void SpdyStream::LogStreamError(int status, const std::string& description) {
}
void SpdyStream::OnClose(int status) {
- CHECK(!in_do_loop_);
+ // In most cases, the stream should already be CLOSED. The exception is when a
+ // SpdySession is shutting down while the stream is in an intermediate state.
io_state_ = STATE_CLOSED;
response_status_ = status;
Delegate* delegate = delegate_;
@@ -544,7 +629,6 @@ void SpdyStream::OnClose(int status) {
}
void SpdyStream::Cancel() {
- CHECK(!in_do_loop_);
// We may be called again from a delegate's OnClose().
if (io_state_ == STATE_CLOSED)
return;
@@ -558,7 +642,6 @@ void SpdyStream::Cancel() {
}
void SpdyStream::Close() {
- CHECK(!in_do_loop_);
// We may be called again from a delegate's OnClose().
if (io_state_ == STATE_CLOSED)
return;
@@ -578,25 +661,29 @@ base::WeakPtr<SpdyStream> SpdyStream::GetWeakPtr() {
int SpdyStream::SendRequestHeaders(scoped_ptr<SpdyHeaderBlock> request_headers,
SpdySendStatus send_status) {
CHECK_NE(type_, SPDY_PUSH_STREAM);
- CHECK_EQ(send_status_, MORE_DATA_TO_SEND);
+ CHECK_EQ(pending_send_status_, MORE_DATA_TO_SEND);
CHECK(!request_headers_);
CHECK(!pending_send_data_.get());
- CHECK_EQ(io_state_, STATE_NONE);
+ CHECK_EQ(io_state_, STATE_IDLE);
request_headers_ = request_headers.Pass();
- send_status_ = send_status;
- io_state_ = STATE_SEND_REQUEST_HEADERS;
- return DoLoop(OK);
+ pending_send_status_ = send_status;
+ session_->EnqueueStreamWrite(
+ GetWeakPtr(), SYN_STREAM,
+ scoped_ptr<SpdyBufferProducer>(
+ new SynStreamBufferProducer(GetWeakPtr())));
+ return ERR_IO_PENDING;
}
void SpdyStream::SendData(IOBuffer* data,
int length,
SpdySendStatus send_status) {
CHECK_NE(type_, SPDY_PUSH_STREAM);
- CHECK_EQ(send_status_, MORE_DATA_TO_SEND);
- CHECK_GE(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE);
+ CHECK_EQ(pending_send_status_, MORE_DATA_TO_SEND);
+ CHECK(io_state_ == STATE_OPEN ||
+ io_state_ == STATE_HALF_CLOSED_REMOTE) << io_state_;
CHECK(!pending_send_data_.get());
pending_send_data_ = new DrainableIOBuffer(data, length);
- send_status_ = send_status;
+ pending_send_status_ = send_status;
QueueNextDataFrame();
}
@@ -612,8 +699,9 @@ bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
}
void SpdyStream::PossiblyResumeIfSendStalled() {
- DCHECK(!IsClosed());
-
+ if (IsLocallyClosed()) {
+ return;
+ }
if (send_stalled_by_flow_control_ && !session_->IsSendStalled() &&
send_window_size_ > 0) {
net_log_.AddEvent(
@@ -628,10 +716,24 @@ bool SpdyStream::IsClosed() const {
return io_state_ == STATE_CLOSED;
}
+bool SpdyStream::IsLocallyClosed() const {
+ return io_state_ == STATE_HALF_CLOSED_LOCAL_UNCLAIMED ||
+ io_state_ == STATE_HALF_CLOSED_LOCAL ||
+ io_state_ == STATE_CLOSED;
+}
+
bool SpdyStream::IsIdle() const {
return io_state_ == STATE_IDLE;
}
+bool SpdyStream::IsOpen() const {
+ return io_state_ == STATE_OPEN;
+}
+
+bool SpdyStream::IsReservedRemote() const {
+ return io_state_ == STATE_RESERVED_REMOTE;
+}
+
NextProto SpdyStream::GetProtocol() const {
return session_->protocol();
}
@@ -644,165 +746,17 @@ bool SpdyStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
}
GURL SpdyStream::GetUrlFromHeaders() const {
- if (type_ != SPDY_PUSH_STREAM && !request_headers_)
+ if (!request_headers_)
return GURL();
- const SpdyHeaderBlock& headers =
- (type_ == SPDY_PUSH_STREAM) ? response_headers_ : *request_headers_;
- return GetUrlFromHeaderBlock(headers, GetProtocolVersion(),
- type_ == SPDY_PUSH_STREAM);
+ return GetUrlFromHeaderBlock(
+ *request_headers_, GetProtocolVersion(), type_ == SPDY_PUSH_STREAM);
}
bool SpdyStream::HasUrlFromHeaders() const {
return !GetUrlFromHeaders().is_empty();
}
-int SpdyStream::DoLoop(int result) {
- CHECK(!in_do_loop_);
- in_do_loop_ = true;
-
- do {
- State state = io_state_;
- io_state_ = STATE_NONE;
- switch (state) {
- case STATE_SEND_REQUEST_HEADERS:
- CHECK_EQ(result, OK);
- result = DoSendRequestHeaders();
- break;
- case STATE_SEND_REQUEST_HEADERS_COMPLETE:
- CHECK_EQ(result, OK);
- result = DoSendRequestHeadersComplete();
- break;
-
- // For request/response streams, no data is sent from the client
- // while in the OPEN state, so OnFrameWriteComplete is never
- // called here. The HTTP body is handled in the OnDataReceived
- // callback, which does not call into DoLoop.
- //
- // For bidirectional streams, we'll send and receive data once
- // the connection is established. Received data is handled in
- // OnDataReceived. Sent data is handled in
- // OnFrameWriteComplete, which calls DoOpen().
- case STATE_IDLE:
- CHECK_EQ(result, OK);
- result = DoOpen();
- break;
-
- case STATE_CLOSED:
- DCHECK_NE(result, ERR_IO_PENDING);
- break;
- default:
- NOTREACHED() << io_state_;
- break;
- }
- } while (result != ERR_IO_PENDING && io_state_ != STATE_NONE &&
- io_state_ != STATE_IDLE);
-
- CHECK(in_do_loop_);
- in_do_loop_ = false;
-
- return result;
-}
-
-int SpdyStream::DoSendRequestHeaders() {
- DCHECK_NE(type_, SPDY_PUSH_STREAM);
- io_state_ = STATE_SEND_REQUEST_HEADERS_COMPLETE;
-
- session_->EnqueueStreamWrite(
- GetWeakPtr(), SYN_STREAM,
- scoped_ptr<SpdyBufferProducer>(
- new SynStreamBufferProducer(GetWeakPtr())));
- return ERR_IO_PENDING;
-}
-
-namespace {
-
-// Assuming we're in STATE_IDLE, maps the given type (which must not
-// be SPDY_PUSH_STREAM) and send status to a result to return from
-// DoSendRequestHeadersComplete() or DoOpen().
-int GetOpenStateResult(SpdyStreamType type, SpdySendStatus send_status) {
- switch (type) {
- case SPDY_BIDIRECTIONAL_STREAM:
- // For bidirectional streams, there's nothing else to do.
- DCHECK_EQ(send_status, MORE_DATA_TO_SEND);
- return OK;
-
- case SPDY_REQUEST_RESPONSE_STREAM:
- // For request/response streams, wait for the delegate to send
- // data if there's request data to send; we'll get called back
- // when the send finishes.
- if (send_status == MORE_DATA_TO_SEND)
- return ERR_IO_PENDING;
-
- return OK;
-
- case SPDY_PUSH_STREAM:
- // This should never be called for push streams.
- break;
- }
-
- CHECK(false);
- return ERR_UNEXPECTED;
-}
-
-} // namespace
-
-int SpdyStream::DoSendRequestHeadersComplete() {
- DCHECK_NE(type_, SPDY_PUSH_STREAM);
- DCHECK_EQ(just_completed_frame_type_, SYN_STREAM);
- DCHECK_NE(stream_id_, 0u);
-
- io_state_ = STATE_IDLE;
-
- CHECK(delegate_);
- // Must not close |this|; if it does, it will trigger the |in_do_loop_|
- // check in the destructor.
- delegate_->OnRequestHeadersSent();
-
- return GetOpenStateResult(type_, send_status_);
-}
-
-int SpdyStream::DoOpen() {
- DCHECK_NE(type_, SPDY_PUSH_STREAM);
-
- if (just_completed_frame_type_ != DATA) {
- NOTREACHED();
- return ERR_UNEXPECTED;
- }
-
- if (just_completed_frame_size_ < session_->GetDataFrameMinimumSize()) {
- NOTREACHED();
- return ERR_UNEXPECTED;
- }
-
- size_t frame_payload_size =
- just_completed_frame_size_ - session_->GetDataFrameMinimumSize();
- if (frame_payload_size > session_->GetDataFrameMaximumPayload()) {
- NOTREACHED();
- return ERR_UNEXPECTED;
- }
-
- // Set |io_state_| first as |delegate_| may check it.
- io_state_ = STATE_IDLE;
-
- send_bytes_ += frame_payload_size;
-
- pending_send_data_->DidConsume(frame_payload_size);
- if (pending_send_data_->BytesRemaining() > 0) {
- QueueNextDataFrame();
- return ERR_IO_PENDING;
- }
-
- pending_send_data_ = NULL;
-
- CHECK(delegate_);
- // Must not close |this|; if it does, it will trigger the
- // |in_do_loop_| check in the destructor.
- delegate_->OnDataSent();
-
- return GetOpenStateResult(type_, send_status_);
-}
-
void SpdyStream::UpdateHistograms() {
// We need at least the receive timers to be filled in, as otherwise
// metrics can be bogus.
@@ -836,13 +790,14 @@ void SpdyStream::UpdateHistograms() {
void SpdyStream::QueueNextDataFrame() {
// Until the request has been completely sent, we cannot be sure
// that our stream_id is correct.
- DCHECK_GT(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE);
+ CHECK(io_state_ == STATE_OPEN ||
+ io_state_ == STATE_HALF_CLOSED_REMOTE) << io_state_;
CHECK_GT(stream_id_, 0u);
CHECK(pending_send_data_.get());
CHECK_GT(pending_send_data_->BytesRemaining(), 0);
SpdyDataFlags flags =
- (send_status_ == NO_MORE_DATA_TO_SEND) ?
+ (pending_send_status_ == NO_MORE_DATA_TO_SEND) ?
DATA_FLAG_FIN : DATA_FLAG_NONE;
scoped_ptr<SpdyBuffer> data_buffer(
session_->CreateDataBuffer(stream_id_,
@@ -931,4 +886,27 @@ int SpdyStream::MergeWithResponseHeaders(
return OK;
}
+#define STATE_CASE(s) \
+ case s: \
+ description = base::StringPrintf("%s (0x%08X)", #s, s); \
+ break
+
+std::string SpdyStream::DescribeState(State state) {
+ std::string description;
+ switch (state) {
+ STATE_CASE(STATE_IDLE);
+ STATE_CASE(STATE_OPEN);
+ STATE_CASE(STATE_HALF_CLOSED_LOCAL_UNCLAIMED);
+ STATE_CASE(STATE_HALF_CLOSED_LOCAL);
+ STATE_CASE(STATE_CLOSED);
+ default:
+ description = base::StringPrintf("Unknown state 0x%08X (%u)", state,
+ state);
+ break;
+ }
+ return description;
+}
+
+#undef STATE_CASE
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_stream.h b/chromium/net/spdy/spdy_stream.h
index 6f5c62e4139..3a4a283fd97 100644
--- a/chromium/net/spdy/spdy_stream.h
+++ b/chromium/net/spdy/spdy_stream.h
@@ -124,6 +124,9 @@ class NET_EXPORT_PRIVATE SpdyStream {
//
// TODO(akalin): Treat headers received after data has been
// received as a protocol error for non-bidirectional streams.
+ // TODO(jgraettinger): This should be at the semantic (HTTP) rather
+ // than stream layer. Streams shouldn't have a notion of header
+ // completeness. Move to SpdyHttpStream/SpdyWebsocketStream.
virtual SpdyResponseHeadersStatus OnResponseHeadersUpdated(
const SpdyHeaderBlock& response_headers) = 0;
@@ -283,9 +286,8 @@ class NET_EXPORT_PRIVATE SpdyStream {
// Called at most once by the SpdySession when the initial response
// headers have been received for this stream, i.e., a SYN_REPLY (or
- // SYN_STREAM for push streams) frame has been received. This is the
- // entry point for a push stream. Returns a status code; if it is
- // an error, the stream was closed by this function.
+ // SYN_STREAM for push streams) frame has been received. Returns a status
+ // code; if it is an error, the stream was closed by this function.
int OnInitialResponseHeadersReceived(const SpdyHeaderBlock& response_headers,
base::Time response_time,
base::TimeTicks recv_first_byte_time);
@@ -297,6 +299,10 @@ class NET_EXPORT_PRIVATE SpdyStream {
int OnAdditionalResponseHeadersReceived(
const SpdyHeaderBlock& additional_response_headers);
+ // Called by the SpdySession when a frame carrying request headers opening a
+ // push stream is received. Stream transits to STATE_RESERVED_REMOTE state.
+ void OnPushPromiseHeadersReceived(const SpdyHeaderBlock& headers);
+
// Called by the SpdySession when response data has been received
// for this stream. This callback may be called multiple times as
// data arrives from the network, and will never be called prior to
@@ -315,6 +321,14 @@ class NET_EXPORT_PRIVATE SpdyStream {
// in bytes, including framing overhead.
void OnFrameWriteComplete(SpdyFrameType frame_type, size_t frame_size);
+ // SYN_STREAM-specific write handler invoked by OnFrameWriteComplete().
+ int OnRequestHeadersSent();
+
+ // DATA-specific write handler invoked by OnFrameWriteComplete().
+ // If more data is already available to be written, the next write is
+ // queued and ERR_IO_PENDING is returned. Returns OK otherwise.
+ int OnDataSent(size_t frame_size);
+
// Called by the SpdySession when the request is finished. This callback
// will always be called at the end of the request and signals to the
// stream that the stream has no more network events. No further callbacks
@@ -378,10 +392,23 @@ class NET_EXPORT_PRIVATE SpdyStream {
// OnClose() method.
bool IsClosed() const;
- // Returns whether or not this stream has finished sending its
- // request headers and is ready to send/receive more data.
+ // Returns whether the streams local endpoint is closed.
+ // The remote endpoint may still be active.
+ bool IsLocallyClosed() const;
+
+ // Returns whether this stream is IDLE: request and response headers
+ // have neither been sent nor receieved.
bool IsIdle() const;
+ // Returns whether or not this stream is fully open: that request and
+ // response headers are complete, and it is not in a half-closed state.
+ bool IsOpen() const;
+
+ // Returns whether the stream is reserved by remote endpoint: server has sent
+ // intended request headers for a pushed stream, but haven't started response
+ // yet.
+ bool IsReservedRemote() const;
+
// Returns the protocol used by this stream. Always between
// kProtoSPDYMinimumVersion and kProtoSPDYMaximumVersion.
NextProto GetProtocol() const;
@@ -416,32 +443,33 @@ class NET_EXPORT_PRIVATE SpdyStream {
class SynStreamBufferProducer;
class HeaderBufferProducer;
+ // SpdyStream states and transitions are modeled
+ // on the HTTP/2 stream state machine. All states and transitions
+ // are modeled, with the exceptions of RESERVED_LOCAL (the client
+ // cannot initate push streams), and the transition to OPEN due to
+ // a remote SYN_STREAM (the client can only initate streams).
enum State {
- STATE_NONE,
- STATE_SEND_REQUEST_HEADERS,
- STATE_SEND_REQUEST_HEADERS_COMPLETE,
STATE_IDLE,
- STATE_CLOSED
+ STATE_OPEN,
+ STATE_HALF_CLOSED_LOCAL_UNCLAIMED,
+ STATE_HALF_CLOSED_LOCAL,
+ STATE_HALF_CLOSED_REMOTE,
+ STATE_RESERVED_REMOTE,
+ STATE_CLOSED,
};
- // Try to make progress sending/receiving the request/response.
- int DoLoop(int result);
-
- // The implementations of each state of the state machine.
- int DoSendRequestHeaders();
- int DoSendRequestHeadersComplete();
- int DoReadHeaders();
- int DoReadHeadersComplete(int result);
- int DoOpen();
-
// Update the histograms. Can safely be called repeatedly, but should only
// be called after the stream has completed.
void UpdateHistograms();
- // When a server-pushed stream is first created, this function is
- // posted on the current MessageLoop to replay the data that the
- // server has already sent.
- void PushedStreamReplayData();
+ // When a server-push stream is claimed by SetDelegate(), this function is
+ // posted on the current MessageLoop to replay everything the server has sent.
+ // From the perspective of SpdyStream's state machine, headers, data, and
+ // FIN states received prior to the delegate being attached have not yet been
+ // read. While buffered by |pending_recv_data_| it's not until
+ // PushedStreamReplay() is invoked that reads are considered
+ // to have occurred, driving the state machine forward.
+ void PushedStreamReplay();
// Produces the SYN_STREAM frame for the stream. The stream must
// already be activated.
@@ -463,23 +491,13 @@ class NET_EXPORT_PRIVATE SpdyStream {
// by this function.
int MergeWithResponseHeaders(const SpdyHeaderBlock& new_response_headers);
- const SpdyStreamType type_;
-
- base::WeakPtrFactory<SpdyStream> weak_ptr_factory_;
+ static std::string DescribeState(State state);
- // Sentinel variable used to make sure we don't get destroyed by a
- // function called from DoLoop().
- bool in_do_loop_;
-
- // There is a small period of time between when a server pushed stream is
- // first created, and the pushed data is replayed. Any data received during
- // this time should continue to be buffered.
- bool continue_buffering_data_;
+ const SpdyStreamType type_;
SpdyStreamId stream_id_;
const GURL url_;
const RequestPriority priority_;
- size_t slot_;
// Flow control variables.
bool send_stalled_by_flow_control_;
@@ -494,17 +512,22 @@ class NET_EXPORT_PRIVATE SpdyStream {
// The transaction should own the delegate.
SpdyStream::Delegate* delegate_;
- // Whether or not we have more data to send on this stream.
- SpdySendStatus send_status_;
-
// The headers for the request to send.
//
// TODO(akalin): Hang onto this only until we send it. This
// necessitates stashing the URL separately.
scoped_ptr<SpdyHeaderBlock> request_headers_;
- // The data waiting to be sent.
+ // Data waiting to be sent, and the close state of the local endpoint
+ // after the data is fully written.
scoped_refptr<DrainableIOBuffer> pending_send_data_;
+ SpdySendStatus pending_send_status_;
+
+ // Data waiting to be received, and the close state of the remote endpoint
+ // after the data is fully read. Specifically, data received before the
+ // delegate is attached must be buffered and later replayed. A remote FIN
+ // is represented by a final, zero-length buffer.
+ ScopedVector<SpdyBuffer> pending_recv_data_;
// The time at which the request was made that resulted in this response.
// For cached responses, this time could be "far" in the past.
@@ -535,16 +558,16 @@ class NET_EXPORT_PRIVATE SpdyStream {
int send_bytes_;
int recv_bytes_;
- // Data received before delegate is attached.
- ScopedVector<SpdyBuffer> pending_buffers_;
-
std::string domain_bound_private_key_;
std::string domain_bound_cert_;
ServerBoundCertService::RequestHandle domain_bound_cert_request_handle_;
- // When OnFrameWriteComplete() is called, these variables are set.
- SpdyFrameType just_completed_frame_type_;
- size_t just_completed_frame_size_;
+ // Guards calls of delegate write handlers ensuring |this| is not destroyed.
+ // TODO(jgraettinger): Consider removing after crbug.com/35511 is tracked
+ // down.
+ bool write_handler_guard_;
+
+ base::WeakPtrFactory<SpdyStream> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SpdyStream);
};
diff --git a/chromium/net/spdy/spdy_stream_unittest.cc b/chromium/net/spdy/spdy_stream_unittest.cc
index cc1386dab43..f754be662c7 100644
--- a/chromium/net/spdy/spdy_stream_unittest.cc
+++ b/chromium/net/spdy/spdy_stream_unittest.cc
@@ -53,7 +53,7 @@ class SpdyStreamTest : public ::testing::Test,
base::WeakPtr<SpdySession> CreateDefaultSpdySession() {
SpdySessionKey key(HostPortPair("www.google.com", 80),
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
return CreateInsecureSpdySession(session_, key, BoundNetLog());
}
@@ -113,8 +113,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdyStreamTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(SpdyStreamTest, SendDataAfterOpen) {
GURL url(kStreamUrl);
@@ -170,8 +169,6 @@ TEST_P(SpdyStreamTest, SendDataAfterOpen) {
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
- EXPECT_EQ("HTTP/1.1",
- delegate.GetResponseHeaderValue(spdy_util_.GetVersionKey()));
EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
@@ -202,16 +199,22 @@ TEST_P(SpdyStreamTest, PushedStream) {
stream.set_stream_id(2);
EXPECT_FALSE(stream.HasUrlFromHeaders());
- // Set a couple of headers.
+ // Set required request headers.
+ SpdyHeaderBlock request_headers;
+ spdy_util_.AddUrlToHeaderBlock(kStreamUrl, &request_headers);
+ stream.OnPushPromiseHeadersReceived(request_headers);
+
+ // Send some basic response headers.
SpdyHeaderBlock response;
- spdy_util_.AddUrlToHeaderBlock(kStreamUrl, &response);
+ response[spdy_util_.GetStatusKey()] = "200";
+ response[spdy_util_.GetVersionKey()] = "OK";
stream.OnInitialResponseHeadersReceived(
response, base::Time::Now(), base::TimeTicks::Now());
- // Send some basic headers.
+ // And some more headers.
+ // TODO(baranovich): not valid for HTTP 2.
SpdyHeaderBlock headers;
- headers[spdy_util_.GetStatusKey()] = "200";
- headers[spdy_util_.GetVersionKey()] = "OK";
+ headers["alpha"] = "beta";
stream.OnAdditionalResponseHeadersReceived(headers);
EXPECT_TRUE(stream.HasUrlFromHeaders());
@@ -223,6 +226,7 @@ TEST_P(SpdyStreamTest, PushedStream) {
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
+ EXPECT_EQ("beta", delegate.GetResponseHeaderValue("alpha"));
EXPECT_TRUE(spdy_session == NULL);
}
@@ -285,8 +289,6 @@ TEST_P(SpdyStreamTest, StreamError) {
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
- EXPECT_EQ("HTTP/1.1",
- delegate.GetResponseHeaderValue(spdy_util_.GetVersionKey()));
EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
@@ -368,8 +370,6 @@ TEST_P(SpdyStreamTest, SendLargeDataAfterOpenRequestResponse) {
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
- EXPECT_EQ("HTTP/1.1",
- delegate.GetResponseHeaderValue(spdy_util_.GetVersionKey()));
EXPECT_EQ(std::string(), delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
}
@@ -431,8 +431,6 @@ TEST_P(SpdyStreamTest, SendLargeDataAfterOpenBidirectional) {
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
- EXPECT_EQ("HTTP/1.1",
- delegate.GetResponseHeaderValue(spdy_util_.GetVersionKey()));
EXPECT_EQ(std::string(), delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
}
@@ -876,7 +874,6 @@ void SpdyStreamTest::RunResumeAfterUnstallRequestResponseTest(
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(), delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
}
@@ -971,7 +968,6 @@ void SpdyStreamTest::RunResumeAfterUnstallBidirectionalTest(
EXPECT_TRUE(delegate.send_headers_completed());
EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
- EXPECT_EQ("HTTP/1.1", delegate.GetResponseHeaderValue(":version"));
EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
delegate.TakeReceivedData());
EXPECT_TRUE(data.at_write_eof());
@@ -1041,7 +1037,8 @@ TEST_P(SpdyStreamTest, ReceivedBytes) {
EXPECT_EQ(kStreamUrl, stream->GetUrlFromHeaders().spec());
int64 reply_frame_len = reply->size();
- int64 data_header_len = spdy_util_.CreateFramer()->GetDataFrameMinimumSize();
+ int64 data_header_len = spdy_util_.CreateFramer(false)
+ ->GetDataFrameMinimumSize();
int64 data_frame_len = data_header_len + kPostBodyLength;
int64 response_len = reply_frame_len + data_frame_len;
diff --git a/chromium/net/spdy/spdy_test_util_common.cc b/chromium/net/spdy/spdy_test_util_common.cc
index 89d18821b28..3d73c7f0c59 100644
--- a/chromium/net/spdy/spdy_test_util_common.cc
+++ b/chromium/net/spdy/spdy_test_util_common.cc
@@ -24,6 +24,7 @@
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
+#include "net/url_request/url_request_job_factory_impl.h"
namespace net {
@@ -50,8 +51,8 @@ void ParseUrl(base::StringPiece url, std::string* scheme, std::string* host,
} // namespace
-std::vector<NextProto> SpdyNextProtos() {
- std::vector<NextProto> next_protos;
+NextProtoVector SpdyNextProtos() {
+ NextProtoVector next_protos;
for (int i = kProtoMinimumVersion; i <= kProtoMaximumVersion; ++i) {
next_protos.push_back(static_cast<NextProto>(i));
}
@@ -216,7 +217,6 @@ class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) OVERRIDE {
@@ -238,7 +238,7 @@ class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
virtual void OnSettings(bool clear_persisted) OVERRIDE {}
virtual void OnSetting(
SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {}
- virtual void OnPing(uint32 unique_id) OVERRIDE {}
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE {}
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
@@ -246,7 +246,8 @@ class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE {}
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {}
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) OVERRIDE {}
private:
SpdyPriority priority_;
@@ -358,6 +359,10 @@ SpdySessionDependencies::SpdySessionDependencies(NextProto protocol)
protocol(protocol),
stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
time_func(&base::TimeTicks::Now),
+ force_spdy_over_ssl(false),
+ force_spdy_always(false),
+ use_alternate_protocols(false),
+ enable_websocket_over_spdy(false),
net_log(NULL) {
DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
@@ -388,6 +393,10 @@ SpdySessionDependencies::SpdySessionDependencies(
protocol(protocol),
stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
time_func(&base::TimeTicks::Now),
+ force_spdy_over_ssl(false),
+ force_spdy_always(false),
+ use_alternate_protocols(false),
+ enable_websocket_over_spdy(false),
net_log(NULL) {
DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
}
@@ -442,12 +451,19 @@ net::HttpNetworkSession::Params SpdySessionDependencies::CreateSessionParams(
params.spdy_stream_initial_recv_window_size =
session_deps->stream_initial_recv_window_size;
params.time_func = session_deps->time_func;
+ params.next_protos = session_deps->next_protos;
params.trusted_spdy_proxy = session_deps->trusted_spdy_proxy;
+ params.force_spdy_over_ssl = session_deps->force_spdy_over_ssl;
+ params.force_spdy_always = session_deps->force_spdy_always;
+ params.use_alternate_protocols = session_deps->use_alternate_protocols;
+ params.enable_websocket_over_spdy = session_deps->enable_websocket_over_spdy;
params.net_log = session_deps->net_log;
return params;
}
-SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol)
+SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol,
+ bool force_spdy_over_ssl,
+ bool force_spdy_always)
: storage_(this) {
DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
@@ -460,6 +476,7 @@ SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol)
host_resolver()));
storage_.set_http_server_properties(
scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl()));
+ storage_.set_job_factory(new URLRequestJobFactoryImpl());
net::HttpNetworkSession::Params params;
params.client_socket_factory = &socket_factory_;
params.host_resolver = host_resolver();
@@ -472,6 +489,8 @@ SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol)
params.enable_spdy_compression = false;
params.enable_spdy_ping_based_connection_checking = false;
params.spdy_default_protocol = protocol;
+ params.force_spdy_over_ssl = force_spdy_over_ssl;
+ params.force_spdy_always = force_spdy_always;
params.http_server_properties = http_server_properties();
scoped_refptr<HttpNetworkSession> network_session(
new HttpNetworkSession(params));
@@ -541,15 +560,12 @@ base::WeakPtr<SpdySession> CreateSpdySessionHelper(
EXPECT_EQ(OK, rv);
- base::WeakPtr<SpdySession> spdy_session;
- EXPECT_EQ(
- expected_status,
+ base::WeakPtr<SpdySession> spdy_session =
http_session->spdy_session_pool()->CreateAvailableSessionFromSocket(
- key, connection.Pass(), net_log, OK, &spdy_session,
- is_secure));
- EXPECT_EQ(expected_status == OK, spdy_session != NULL);
- EXPECT_EQ(expected_status == OK,
- HasSpdySession(http_session->spdy_session_pool(), key));
+ key, connection.Pass(), net_log, OK, is_secure);
+ // Failure is reported asynchronously.
+ EXPECT_TRUE(spdy_session != NULL);
+ EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key));
return spdy_session;
}
@@ -563,14 +579,14 @@ base::WeakPtr<SpdySession> CreateInsecureSpdySession(
OK, false /* is_secure */);
}
-void TryCreateInsecureSpdySessionExpectingFailure(
+base::WeakPtr<SpdySession> TryCreateInsecureSpdySessionExpectingFailure(
const scoped_refptr<HttpNetworkSession>& http_session,
const SpdySessionKey& key,
Error expected_error,
const BoundNetLog& net_log) {
DCHECK_LT(expected_error, ERR_IO_PENDING);
- CreateSpdySessionHelper(http_session, key, net_log,
- expected_error, false /* is_secure */);
+ return CreateSpdySessionHelper(http_session, key, net_log,
+ expected_error, false /* is_secure */);
}
base::WeakPtr<SpdySession> CreateSecureSpdySession(
@@ -644,17 +660,15 @@ base::WeakPtr<SpdySession> CreateFakeSpdySessionHelper(
Error expected_status) {
EXPECT_NE(expected_status, ERR_IO_PENDING);
EXPECT_FALSE(HasSpdySession(pool, key));
- base::WeakPtr<SpdySession> spdy_session;
scoped_ptr<ClientSocketHandle> handle(new ClientSocketHandle());
handle->SetSocket(scoped_ptr<StreamSocket>(new FakeSpdySessionClientSocket(
expected_status == OK ? ERR_IO_PENDING : expected_status)));
- EXPECT_EQ(
- expected_status,
+ base::WeakPtr<SpdySession> spdy_session =
pool->CreateAvailableSessionFromSocket(
- key, handle.Pass(), BoundNetLog(), OK, &spdy_session,
- true /* is_secure */));
- EXPECT_EQ(expected_status == OK, spdy_session != NULL);
- EXPECT_EQ(expected_status == OK, HasSpdySession(pool, key));
+ key, handle.Pass(), BoundNetLog(), OK, true /* is_secure */);
+ // Failure is reported asynchronously.
+ EXPECT_TRUE(spdy_session != NULL);
+ EXPECT_TRUE(HasSpdySession(pool, key));
return spdy_session;
}
@@ -665,11 +679,12 @@ base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool,
return CreateFakeSpdySessionHelper(pool, key, OK);
}
-void TryCreateFakeSpdySessionExpectingFailure(SpdySessionPool* pool,
- const SpdySessionKey& key,
- Error expected_error) {
+base::WeakPtr<SpdySession> TryCreateFakeSpdySessionExpectingFailure(
+ SpdySessionPool* pool,
+ const SpdySessionKey& key,
+ Error expected_error) {
DCHECK_LT(expected_error, ERR_IO_PENDING);
- CreateFakeSpdySessionHelper(pool, key, expected_error);
+ return CreateFakeSpdySessionHelper(pool, key, expected_error);
}
SpdySessionPoolPeer::SpdySessionPoolPeer(SpdySessionPool* pool) : pool_(pool) {
@@ -750,10 +765,8 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(
break;
case SYN_STREAM:
{
- size_t credential_slot = is_spdy2() ? 0 : header_info.credential_slot;
frame = framer.CreateSynStream(header_info.id, header_info.assoc_id,
header_info.priority,
- credential_slot,
header_info.control_flags,
headers.get());
}
@@ -854,18 +867,39 @@ std::string SpdyTestUtil::ConstructSpdyReplyString(
return reply_string;
}
+// TODO(jgraettinger): Eliminate uses of this method in tests (prefer
+// SpdySettingsIR).
SpdyFrame* SpdyTestUtil::ConstructSpdySettings(
const SettingsMap& settings) const {
- return CreateFramer()->CreateSettings(settings);
+ SpdySettingsIR settings_ir;
+ for (SettingsMap::const_iterator it = settings.begin();
+ it != settings.end();
+ ++it) {
+ settings_ir.AddSetting(
+ it->first,
+ (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
+ (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
+ it->second.second);
+ }
+ return CreateFramer(false)->SerializeFrame(settings_ir);
}
-SpdyFrame* SpdyTestUtil::ConstructSpdyCredential(
- const SpdyCredential& credential) const {
- return CreateFramer()->CreateCredentialFrame(credential);
+SpdyFrame* SpdyTestUtil::ConstructSpdySettingsAck() const {
+ char kEmptyWrite[] = "";
+
+ if (spdy_version() > SPDY3) {
+ SpdySettingsIR settings_ir;
+ settings_ir.set_is_ack(true);
+ return CreateFramer(false)->SerializeFrame(settings_ir);
+ }
+ // No settings ACK write occurs. Create an empty placeholder write.
+ return new SpdyFrame(kEmptyWrite, 0, false);
}
-SpdyFrame* SpdyTestUtil::ConstructSpdyPing(uint32 ping_id) const {
- return CreateFramer()->CreatePingFrame(ping_id);
+SpdyFrame* SpdyTestUtil::ConstructSpdyPing(uint32 ping_id, bool is_ack) const {
+ SpdyPingIR ping_ir(ping_id);
+ ping_ir.set_is_ack(is_ack);
+ return CreateFramer(false)->SerializeFrame(ping_ir);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const {
@@ -874,18 +908,30 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const {
SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(
SpdyStreamId last_good_stream_id) const {
- return CreateFramer()->CreateGoAway(last_good_stream_id, GOAWAY_OK);
+ SpdyGoAwayIR go_ir(last_good_stream_id, GOAWAY_OK, "go away");
+ return CreateFramer(false)->SerializeFrame(go_ir);
+}
+
+SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(SpdyStreamId last_good_stream_id,
+ SpdyGoAwayStatus status,
+ const std::string& desc) const {
+ SpdyGoAwayIR go_ir(last_good_stream_id, status, desc);
+ return CreateFramer(false)->SerializeFrame(go_ir);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyWindowUpdate(
const SpdyStreamId stream_id, uint32 delta_window_size) const {
- return CreateFramer()->CreateWindowUpdate(stream_id, delta_window_size);
+ SpdyWindowUpdateIR update_ir(stream_id, delta_window_size);
+ return CreateFramer(false)->SerializeFrame(update_ir);
}
+// TODO(jgraettinger): Eliminate uses of this method in tests (prefer
+// SpdyRstStreamIR).
SpdyFrame* SpdyTestUtil::ConstructSpdyRstStream(
SpdyStreamId stream_id,
SpdyRstStreamStatus status) const {
- return CreateFramer()->CreateRstStream(stream_id, status);
+ SpdyRstStreamIR rst_ir(stream_id, status, "");
+ return CreateFramer(false)->SerializeRstStream(rst_ir);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyGet(
@@ -915,25 +961,19 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyGet(const char* const extra_headers[],
int stream_id,
RequestPriority request_priority,
bool direct) const {
- const bool spdy2 = is_spdy2();
- const char* url = (spdy2 && !direct) ? "http://www.google.com/" : "/";
- const char* const kStandardGetHeaders[] = {
- GetMethodKey(), "GET",
- GetHostKey(), "www.google.com",
- GetSchemeKey(), "http",
- GetVersionKey(), "HTTP/1.1",
- GetPathKey(), url
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- compressed,
- stream_id,
- request_priority,
- SYN_STREAM,
- CONTROL_FLAG_FIN,
- kStandardGetHeaders,
- arraysize(kStandardGetHeaders),
- 0);
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_priority(
+ ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_));
+ syn_stream.set_fin(true);
+ syn_stream.SetHeader(GetMethodKey(), "GET");
+ syn_stream.SetHeader(GetHostKey(), "www.google.com");
+ syn_stream.SetHeader(GetSchemeKey(), "http");
+ syn_stream.SetHeader(GetPathKey(), (is_spdy2() && !direct) ?
+ "http://www.google.com/" : "/");
+ MaybeAddVersionHeader(&syn_stream);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_stream.mutable_name_value_block());
+ return CreateFramer(compressed)->SerializeFrame(syn_stream);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyConnect(
@@ -941,22 +981,16 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyConnect(
int extra_header_count,
int stream_id,
RequestPriority priority) const {
- const char* const kConnectHeaders[] = {
- GetMethodKey(), "CONNECT",
- GetPathKey(), "www.google.com:443",
- GetHostKey(), "www.google.com",
- GetVersionKey(), "HTTP/1.1",
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- /*compressed*/ false,
- stream_id,
- priority,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- kConnectHeaders,
- arraysize(kConnectHeaders),
- 0);
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_priority(
+ ConvertRequestPriorityToSpdyPriority(priority, spdy_version_));
+ syn_stream.SetHeader(GetMethodKey(), "CONNECT");
+ syn_stream.SetHeader(GetPathKey(), "www.google.com:443");
+ syn_stream.SetHeader(GetHostKey(), "www.google.com");
+ MaybeAddVersionHeader(&syn_stream);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_stream.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_stream);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
@@ -964,19 +998,44 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
int stream_id,
int associated_stream_id,
const char* url) {
- scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
- (*headers)["hello"] = "bye";
- (*headers)[GetStatusKey()] = "200 OK";
- (*headers)[GetVersionKey()] = "HTTP/1.1";
- AddUrlToHeaderBlock(url, headers.get());
- AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
- return ConstructSpdyControlFrame(headers.Pass(),
- false,
- stream_id,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- associated_stream_id);
+ if (spdy_version() < SPDY4) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_associated_to_stream_id(associated_stream_id);
+ syn_stream.SetHeader("hello", "bye");
+ syn_stream.SetHeader(GetStatusKey(), "200 OK");
+ syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1");
+ AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block());
+ AppendToHeaderBlock(extra_headers,
+ extra_header_count,
+ syn_stream.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_stream);
+ } else {
+ SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
+ AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block());
+ scoped_ptr<SpdyFrame> push_promise_frame(
+ CreateFramer(false)->SerializeFrame(push_promise));
+
+ // Use SynStreamIR to create HEADERS+PRIORITY. Direct creation breaks
+ // framer.
+ SpdySynStreamIR headers(stream_id);
+ SetPriority(LOWEST, &headers);
+ headers.SetHeader("hello", "bye");
+ headers.SetHeader(GetStatusKey(), "200 OK");
+ AppendToHeaderBlock(
+ extra_headers, extra_header_count, headers.mutable_name_value_block());
+ scoped_ptr<SpdyFrame> headers_frame(
+ CreateFramer(false)->SerializeFrame(headers));
+
+ int joint_data_size = push_promise_frame->size() + headers_frame->size();
+ scoped_ptr<char[]> data(new char[joint_data_size]);
+ const SpdyFrame* frames[2] = {
+ push_promise_frame.get(), headers_frame.get(),
+ };
+ int combined_size =
+ CombineFrames(frames, arraysize(frames), data.get(), joint_data_size);
+ DCHECK_EQ(combined_size, joint_data_size);
+ return new SpdyFrame(data.release(), joint_data_size, true);
+ }
}
SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
@@ -986,40 +1045,75 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
const char* url,
const char* status,
const char* location) {
- scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
- (*headers)["hello"] = "bye";
- (*headers)[GetStatusKey()] = status;
- (*headers)[GetVersionKey()] = "HTTP/1.1";
- (*headers)["location"] = location;
- AddUrlToHeaderBlock(url, headers.get());
- AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
- return ConstructSpdyControlFrame(headers.Pass(),
- false,
- stream_id,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- associated_stream_id);
+ if (spdy_version() < SPDY4) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_associated_to_stream_id(associated_stream_id);
+ syn_stream.SetHeader("hello", "bye");
+ syn_stream.SetHeader(GetStatusKey(), status);
+ syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1");
+ syn_stream.SetHeader("location", location);
+ AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block());
+ AppendToHeaderBlock(extra_headers,
+ extra_header_count,
+ syn_stream.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_stream);
+ } else {
+ SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
+ AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block());
+ scoped_ptr<SpdyFrame> push_promise_frame(
+ CreateFramer(false)->SerializeFrame(push_promise));
+
+ // Use SynStreamIR to create HEADERS+PRIORITY. Direct creation breaks
+ // framer.
+ SpdySynStreamIR headers(stream_id);
+ SetPriority(LOWEST, &headers);
+ headers.SetHeader("hello", "bye");
+ headers.SetHeader(GetStatusKey(), status);
+ headers.SetHeader("location", location);
+ AppendToHeaderBlock(
+ extra_headers, extra_header_count, headers.mutable_name_value_block());
+ scoped_ptr<SpdyFrame> headers_frame(
+ CreateFramer(false)->SerializeFrame(headers));
+
+ int joint_data_size = push_promise_frame->size() + headers_frame->size();
+ scoped_ptr<char[]> data(new char[joint_data_size]);
+ const SpdyFrame* frames[2] = {
+ push_promise_frame.get(), headers_frame.get(),
+ };
+ int combined_size =
+ CombineFrames(frames, arraysize(frames), data.get(), joint_data_size);
+ DCHECK_EQ(combined_size, joint_data_size);
+ return new SpdyFrame(data.release(), joint_data_size, true);
+ }
+}
+
+SpdyFrame* SpdyTestUtil::ConstructInitialSpdyPushFrame(
+ scoped_ptr<SpdyHeaderBlock> headers,
+ int stream_id,
+ int associated_stream_id) {
+ if (spdy_version() < SPDY4) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_associated_to_stream_id(associated_stream_id);
+ SetPriority(LOWEST, &syn_stream);
+ syn_stream.set_name_value_block(*headers);
+ return CreateFramer(false)->SerializeFrame(syn_stream);
+ } else {
+ SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
+ push_promise.set_name_value_block(*headers);
+ return CreateFramer(false)->SerializeFrame(push_promise);
+ }
}
SpdyFrame* SpdyTestUtil::ConstructSpdyPushHeaders(
int stream_id,
const char* const extra_headers[],
int extra_header_count) {
- const char* const kStandardGetHeaders[] = {
- GetStatusKey(), "200 OK",
- GetVersionKey(), "HTTP/1.1"
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- false,
- stream_id,
- LOWEST,
- HEADERS,
- CONTROL_FLAG_NONE,
- kStandardGetHeaders,
- arraysize(kStandardGetHeaders),
- 0);
+ SpdyHeadersIR headers(stream_id);
+ headers.SetHeader(GetStatusKey(), "200 OK");
+ MaybeAddVersionHeader(&headers);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ headers.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(headers);
}
SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(
@@ -1027,21 +1121,13 @@ SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(
const char* const* const extra_headers,
int extra_header_count,
int stream_id) {
- const char* const kStandardGetHeaders[] = {
- "hello", "bye",
- GetStatusKey(), status,
- GetVersionKey(), "HTTP/1.1"
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- false,
- stream_id,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardGetHeaders,
- arraysize(kStandardGetHeaders),
- 0);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.SetHeader("hello", "bye");
+ syn_reply.SetHeader(GetStatusKey(), status);
+ MaybeAddVersionHeader(&syn_reply);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_reply.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_reply);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReplyRedirect(int stream_id) {
@@ -1060,21 +1146,13 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReply(
const char* const extra_headers[],
int extra_header_count,
int stream_id) {
- const char* const kStandardGetHeaders[] = {
- "hello", "bye",
- GetStatusKey(), "200",
- GetVersionKey(), "HTTP/1.1"
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- false,
- stream_id,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardGetHeaders,
- arraysize(kStandardGetHeaders),
- 0);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.SetHeader("hello", "bye");
+ syn_reply.SetHeader(GetStatusKey(), "200");
+ MaybeAddVersionHeader(&syn_reply);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_reply.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_reply);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyPost(const char* url,
@@ -1103,51 +1181,37 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyPost(const char* url,
SpdyFrame* SpdyTestUtil::ConstructChunkedSpdyPost(
const char* const extra_headers[],
int extra_header_count) {
- const char* post_headers[] = {
- GetMethodKey(), "POST",
- GetPathKey(), "/",
- GetHostKey(), "www.google.com",
- GetSchemeKey(), "http",
- GetVersionKey(), "HTTP/1.1"
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- false,
- 1,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- post_headers,
- arraysize(post_headers),
- 0);
+ SpdySynStreamIR syn_stream(1);
+ syn_stream.SetHeader(GetMethodKey(), "POST");
+ syn_stream.SetHeader(GetPathKey(), "/");
+ syn_stream.SetHeader(GetHostKey(), "www.google.com");
+ syn_stream.SetHeader(GetSchemeKey(), "http");
+ MaybeAddVersionHeader(&syn_stream);
+ SetPriority(LOWEST, &syn_stream);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_stream.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_stream);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyPostSynReply(
const char* const extra_headers[],
int extra_header_count) {
- const char* const kStandardGetHeaders[] = {
- "hello", "bye",
- GetStatusKey(), "200",
- GetPathKey(), "/index.php",
- GetVersionKey(), "HTTP/1.1"
- };
- return ConstructSpdyControlFrame(extra_headers,
- extra_header_count,
- false,
- 1,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kStandardGetHeaders,
- arraysize(kStandardGetHeaders),
- 0);
+ SpdySynReplyIR syn_reply(1);
+ syn_reply.SetHeader("hello", "bye");
+ syn_reply.SetHeader(GetStatusKey(), "200");
+ syn_reply.SetHeader(GetPathKey(), "/index.php");
+ MaybeAddVersionHeader(&syn_reply);
+ AppendToHeaderBlock(extra_headers, extra_header_count,
+ syn_reply.mutable_name_value_block());
+ return CreateFramer(false)->SerializeFrame(syn_reply);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id, bool fin) {
SpdyFramer framer(spdy_version_);
- return framer.CreateDataFrame(
- stream_id, kUploadData, kUploadDataSize,
- fin ? DATA_FLAG_FIN : DATA_FLAG_NONE);
+ SpdyDataIR data_ir(stream_id,
+ base::StringPiece(kUploadData, kUploadDataSize));
+ data_ir.set_fin(fin);
+ return framer.SerializeData(data_ir);
}
SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id,
@@ -1155,8 +1219,9 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id,
uint32 len,
bool fin) {
SpdyFramer framer(spdy_version_);
- return framer.CreateDataFrame(
- stream_id, data, len, fin ? DATA_FLAG_FIN : DATA_FLAG_NONE);
+ SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
+ data_ir.set_fin(fin);
+ return framer.SerializeData(data_ir);
}
SpdyFrame* SpdyTestUtil::ConstructWrappedSpdyFrame(
@@ -1183,8 +1248,10 @@ const SpdyHeaderInfo SpdyTestUtil::MakeSpdyHeader(SpdyFrameType type) {
return kHeader;
}
-scoped_ptr<SpdyFramer> SpdyTestUtil::CreateFramer() const {
- return scoped_ptr<SpdyFramer>(new SpdyFramer(spdy_version_));
+scoped_ptr<SpdyFramer> SpdyTestUtil::CreateFramer(bool compressed) const {
+ scoped_ptr<SpdyFramer> framer(new SpdyFramer(spdy_version_));
+ framer->set_enable_compression(compressed);
+ return framer.Pass();
}
const char* SpdyTestUtil::GetMethodKey() const {
@@ -1196,7 +1263,12 @@ const char* SpdyTestUtil::GetStatusKey() const {
}
const char* SpdyTestUtil::GetHostKey() const {
- return is_spdy2() ? "host" : ":host";
+ if (protocol_ < kProtoSPDY3)
+ return "host";
+ if (protocol_ < kProtoSPDY4)
+ return ":host";
+ else
+ return ":authority";
}
const char* SpdyTestUtil::GetSchemeKey() const {
@@ -1222,7 +1294,9 @@ scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeaderBlock(
(*headers)[GetPathKey()] = path.c_str();
(*headers)[GetHostKey()] = host.c_str();
(*headers)[GetSchemeKey()] = scheme.c_str();
- (*headers)[GetVersionKey()] = "HTTP/1.1";
+ if (include_version_header()) {
+ (*headers)[GetVersionKey()] = "HTTP/1.1";
+ }
if (content_length) {
std::string length_str = base::Int64ToString(*content_length);
(*headers)["content-length"] = length_str;
@@ -1230,4 +1304,17 @@ scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeaderBlock(
return headers.Pass();
}
+void SpdyTestUtil::MaybeAddVersionHeader(
+ SpdyFrameWithNameValueBlockIR* frame_ir) const {
+ if (include_version_header()) {
+ frame_ir->SetHeader(GetVersionKey(), "HTTP/1.1");
+ }
+}
+
+void SpdyTestUtil::SetPriority(RequestPriority priority,
+ SpdySynStreamIR* ir) const {
+ ir->set_priority(ConvertRequestPriorityToSpdyPriority(
+ priority, spdy_version()));
+}
+
} // namespace net
diff --git a/chromium/net/spdy/spdy_test_util_common.h b/chromium/net/spdy/spdy_test_util_common.h
index e9f453c13c5..f04ad4205c3 100644
--- a/chromium/net/spdy/spdy_test_util_common.h
+++ b/chromium/net/spdy/spdy_test_util_common.h
@@ -50,7 +50,7 @@ const int kUploadDataSize = arraysize(kUploadData)-1;
// SpdyNextProtos returns a vector of next protocols for negotiating
// SPDY.
-std::vector<NextProto> SpdyNextProtos();
+NextProtoVector SpdyNextProtos();
// Chop a frame into an array of MockWrites.
// |data| is the frame to chop.
@@ -218,13 +218,20 @@ struct SpdySessionDependencies {
NextProto protocol;
size_t stream_initial_recv_window_size;
SpdySession::TimeFunc time_func;
+ NextProtoVector next_protos;
std::string trusted_spdy_proxy;
+ bool force_spdy_over_ssl;
+ bool force_spdy_always;
+ bool use_alternate_protocols;
+ bool enable_websocket_over_spdy;
NetLog* net_log;
};
class SpdyURLRequestContext : public URLRequestContext {
public:
- explicit SpdyURLRequestContext(NextProto protocol);
+ SpdyURLRequestContext(NextProto protocol,
+ bool force_spdy_over_ssl,
+ bool force_spdy_always);
virtual ~SpdyURLRequestContext();
MockClientSocketFactory& socket_factory() { return socket_factory_; }
@@ -247,8 +254,9 @@ base::WeakPtr<SpdySession> CreateInsecureSpdySession(
// Tries to create a SPDY session for the given key but expects the
// attempt to fail with the given error. A SPDY session for |key| must
-// not already exist.
-void TryCreateInsecureSpdySessionExpectingFailure(
+// not already exist. The session will be created but close in the
+// next event loop iteration.
+base::WeakPtr<SpdySession> TryCreateInsecureSpdySessionExpectingFailure(
const scoped_refptr<HttpNetworkSession>& http_session,
const SpdySessionKey& key,
Error expected_error,
@@ -269,10 +277,12 @@ base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool,
// Tries to create an insecure SPDY session for the given key but
// expects the attempt to fail with the given error. The session will
// neither receive nor send any data. A SPDY session for |key| must
-// not already exist.
-void TryCreateFakeSpdySessionExpectingFailure(SpdySessionPool* pool,
- const SpdySessionKey& key,
- Error expected_error);
+// not already exist. The session will be created but close in the
+// next event loop iteration.
+base::WeakPtr<SpdySession> TryCreateFakeSpdySessionExpectingFailure(
+ SpdySessionPool* pool,
+ const SpdySessionKey& key,
+ Error expected_error);
class SpdySessionPoolPeer {
public:
@@ -363,14 +373,13 @@ class SpdyTestUtil {
// Returns the constructed frame. The caller takes ownership of the frame.
SpdyFrame* ConstructSpdySettings(const SettingsMap& settings) const;
- // Construct an expected SPDY CREDENTIAL frame.
- // |credential| is the credential to send.
- // Returns the constructed frame. The caller takes ownership of the frame.
- SpdyFrame* ConstructSpdyCredential(const SpdyCredential& credential) const;
+ // Constructs an expected SPDY SETTINGS acknowledgement frame, if the protocol
+ // version is SPDY4 or higher, or an empty placeholder frame otherwise.
+ SpdyFrame* ConstructSpdySettingsAck() const;
// Construct a SPDY PING frame.
// Returns the constructed frame. The caller takes ownership of the frame.
- SpdyFrame* ConstructSpdyPing(uint32 ping_id) const;
+ SpdyFrame* ConstructSpdyPing(uint32 ping_id, bool is_ack) const;
// Construct a SPDY GOAWAY frame with last_good_stream_id = 0.
// Returns the constructed frame. The caller takes ownership of the frame.
@@ -380,6 +389,13 @@ class SpdyTestUtil {
// Returns the constructed frame. The caller takes ownership of the frame.
SpdyFrame* ConstructSpdyGoAway(SpdyStreamId last_good_stream_id) const;
+ // Construct a SPDY GOAWAY frame with the specified last_good_stream_id,
+ // status, and description. Returns the constructed frame. The caller takes
+ // ownership of the frame.
+ SpdyFrame* ConstructSpdyGoAway(SpdyStreamId last_good_stream_id,
+ SpdyGoAwayStatus status,
+ const std::string& desc) const;
+
// Construct a SPDY WINDOW_UPDATE frame.
// Returns the constructed frame. The caller takes ownership of the frame.
SpdyFrame* ConstructSpdyWindowUpdate(
@@ -392,7 +408,7 @@ class SpdyTestUtil {
SpdyRstStreamStatus status) const;
// Constructs a standard SPDY GET SYN frame, optionally compressed
- // for the url |url|.
+ // for |url|.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
// Returns a SpdyFrame.
@@ -441,6 +457,10 @@ class SpdyTestUtil {
const char* status,
const char* location);
+ SpdyFrame* ConstructInitialSpdyPushFrame(scoped_ptr<SpdyHeaderBlock> headers,
+ int stream_id,
+ int associated_stream_id);
+
SpdyFrame* ConstructSpdyPushHeaders(int stream_id,
const char* const extra_headers[],
int extra_header_count);
@@ -510,10 +530,17 @@ class SpdyTestUtil {
const SpdyHeaderInfo MakeSpdyHeader(SpdyFrameType type);
+ // For versions below SPDY4, adds the version HTTP/1.1 header.
+ void MaybeAddVersionHeader(SpdyFrameWithNameValueBlockIR* frame_ir) const;
+
+ // Maps |priority| to SPDY version priority, and sets it on |frame_ir|.
+ void SetPriority(RequestPriority priority, SpdySynStreamIR* frame_ir) const;
+
NextProto protocol() const { return protocol_; }
SpdyMajorVersion spdy_version() const { return spdy_version_; }
bool is_spdy2() const { return protocol_ < kProtoSPDY3; }
- scoped_ptr<SpdyFramer> CreateFramer() const;
+ bool include_version_header() const { return protocol_ < kProtoSPDY4; }
+ scoped_ptr<SpdyFramer> CreateFramer(bool compressed) const;
const char* GetMethodKey() const;
const char* GetStatusKey() const;
diff --git a/chromium/net/spdy/spdy_test_utils.cc b/chromium/net/spdy/spdy_test_utils.cc
index e7eb8aca91b..e33a08bc1c3 100644
--- a/chromium/net/spdy/spdy_test_utils.cc
+++ b/chromium/net/spdy/spdy_test_utils.cc
@@ -5,9 +5,11 @@
#include "net/spdy/spdy_test_utils.h"
#include <cstring>
+#include <vector>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
#include "base/sys_byteorder.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -83,13 +85,16 @@ void CompareCharArraysWithHexError(
<< HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
}
-void SetFrameFlags(SpdyFrame* frame, uint8 flags, int spdy_version) {
+void SetFrameFlags(SpdyFrame* frame,
+ uint8 flags,
+ SpdyMajorVersion spdy_version) {
switch (spdy_version) {
- case 2:
- case 3:
+ case SPDY2:
+ case SPDY3:
frame->data()[4] = flags;
break;
- case 4:
+ case SPDY4:
+ case SPDY5:
frame->data()[3] = flags;
break;
default:
@@ -97,10 +102,12 @@ void SetFrameFlags(SpdyFrame* frame, uint8 flags, int spdy_version) {
}
}
-void SetFrameLength(SpdyFrame* frame, size_t length, int spdy_version) {
+void SetFrameLength(SpdyFrame* frame,
+ size_t length,
+ SpdyMajorVersion spdy_version) {
switch (spdy_version) {
- case 2:
- case 3:
+ case SPDY2:
+ case SPDY3:
CHECK_EQ(0u, length & ~kLengthMask);
{
int32 wire_length = base::HostToNet32(length);
@@ -109,8 +116,9 @@ void SetFrameLength(SpdyFrame* frame, size_t length, int spdy_version) {
memcpy(frame->data() + 5, reinterpret_cast<char*>(&wire_length) + 1, 3);
}
break;
- case 4:
- CHECK_GT(1u<<16, length);
+ case SPDY4:
+ case SPDY5:
+ CHECK_GT(1u<<14, length);
{
int32 wire_length = base::HostToNet16(static_cast<uint16>(length));
memcpy(frame->data(),
@@ -123,6 +131,13 @@ void SetFrameLength(SpdyFrame* frame, size_t length, int spdy_version) {
}
}
+std::string a2b_hex(const char* hex_data) {
+ std::vector<uint8> output;
+ std::string result;
+ if (base::HexStringToBytes(hex_data, &output))
+ result.assign(reinterpret_cast<const char*>(&output[0]), output.size());
+ return result;
+}
} // namespace test
diff --git a/chromium/net/spdy/spdy_test_utils.h b/chromium/net/spdy/spdy_test_utils.h
index b6e03936fae..439311e2c16 100644
--- a/chromium/net/spdy/spdy_test_utils.h
+++ b/chromium/net/spdy/spdy_test_utils.h
@@ -23,9 +23,15 @@ void CompareCharArraysWithHexError(
const unsigned char* expected,
const int expected_len);
-void SetFrameFlags(SpdyFrame* frame, uint8 flags, int spdy_version);
+void SetFrameFlags(SpdyFrame* frame,
+ uint8 flags,
+ SpdyMajorVersion spdy_version);
-void SetFrameLength(SpdyFrame* frame, size_t length, int spdy_version);
+void SetFrameLength(SpdyFrame* frame,
+ size_t length,
+ SpdyMajorVersion spdy_version);
+
+std::string a2b_hex(const char* hex_data);
} // namespace test
diff --git a/chromium/net/spdy/spdy_websocket_stream.cc b/chromium/net/spdy/spdy_websocket_stream.cc
index c0eac28b6fb..54e668c1649 100644
--- a/chromium/net/spdy/spdy_websocket_stream.cc
+++ b/chromium/net/spdy/spdy_websocket_stream.cc
@@ -19,10 +19,10 @@ namespace net {
SpdyWebSocketStream::SpdyWebSocketStream(
const base::WeakPtr<SpdySession>& spdy_session, Delegate* delegate)
- : weak_ptr_factory_(this),
- spdy_session_(spdy_session),
+ : spdy_session_(spdy_session),
pending_send_data_length_(0),
- delegate_(delegate) {
+ delegate_(delegate),
+ weak_ptr_factory_(this) {
DCHECK(spdy_session_.get());
DCHECK(delegate_);
}
diff --git a/chromium/net/spdy/spdy_websocket_stream.h b/chromium/net/spdy/spdy_websocket_stream.h
index 169d1e481a8..69981348020 100644
--- a/chromium/net/spdy/spdy_websocket_stream.h
+++ b/chromium/net/spdy/spdy_websocket_stream.h
@@ -88,13 +88,14 @@ class NET_EXPORT_PRIVATE SpdyWebSocketStream
void OnSpdyStreamCreated(int status);
- base::WeakPtrFactory<SpdyWebSocketStream> weak_ptr_factory_;
SpdyStreamRequest stream_request_;
base::WeakPtr<SpdyStream> stream_;
const base::WeakPtr<SpdySession> spdy_session_;
size_t pending_send_data_length_;
Delegate* delegate_;
+ base::WeakPtrFactory<SpdyWebSocketStream> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStream);
};
diff --git a/chromium/net/spdy/spdy_websocket_stream_unittest.cc b/chromium/net/spdy/spdy_websocket_stream_unittest.cc
index 3c1b3cb7626..5720675e72b 100644
--- a/chromium/net/spdy/spdy_websocket_stream_unittest.cc
+++ b/chromium/net/spdy/spdy_websocket_stream_unittest.cc
@@ -201,7 +201,7 @@ class SpdyWebSocketStreamTest
host_port_pair_.set_port(80);
spdy_session_key_ = SpdySessionKey(host_port_pair_,
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
spdy_settings_to_send_[spdy_settings_id_to_set_] =
SettingsFlagsAndValue(
@@ -235,6 +235,12 @@ class SpdyWebSocketStreamTest
kClosingFrameLength,
stream_id_,
false));
+
+ closing_frame_fin_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame(
+ kClosingFrame,
+ kClosingFrameLength,
+ stream_id_,
+ true));
}
void InitSession(MockRead* reads, size_t reads_count,
@@ -273,6 +279,7 @@ class SpdyWebSocketStreamTest
scoped_ptr<SpdyFrame> response_frame_;
scoped_ptr<SpdyFrame> message_frame_;
scoped_ptr<SpdyFrame> closing_frame_;
+ scoped_ptr<SpdyFrame> closing_frame_fin_;
HostPortPair host_port_pair_;
SpdySessionKey spdy_session_key_;
TestCompletionCallback completion_callback_;
@@ -288,8 +295,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
SpdyWebSocketStreamTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
// TODO(toyoshim): Replace old framing data to new one, then use HEADERS and
// data frames.
@@ -375,6 +381,62 @@ TEST_P(SpdyWebSocketStreamTest, Basic) {
EXPECT_TRUE(data()->at_write_eof());
}
+// A SPDY websocket may still send it's close frame after
+// recieving a close with SPDY stream FIN.
+TEST_P(SpdyWebSocketStreamTest, RemoteCloseWithFin) {
+ Prepare(1);
+ MockWrite writes[] = {
+ CreateMockWrite(*request_frame_.get(), 1),
+ CreateMockWrite(*closing_frame_.get(), 4),
+ };
+ MockRead reads[] = {
+ CreateMockRead(*response_frame_.get(), 2),
+ CreateMockRead(*closing_frame_fin_.get(), 3),
+ MockRead(SYNCHRONOUS, 0, 5) // EOF cause OnCloseSpdyStream event.
+ };
+ InitSession(reads, arraysize(reads), writes, arraysize(writes));
+
+ SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
+ delegate.SetOnReceivedData(
+ base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame,
+ base::Unretained(this)));
+
+ websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
+ BoundNetLog net_log;
+ GURL url("ws://example.com/echo");
+ ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
+
+ SendRequest();
+ completion_callback_.WaitForResult();
+ websocket_stream_.reset();
+
+ const std::vector<SpdyWebSocketStreamEvent>& events =
+ delegate.GetSeenEvents();
+ EXPECT_EQ(5U, events.size());
+
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
+ events[0].event_type);
+ EXPECT_EQ(OK, events[0].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
+ events[1].event_type);
+ EXPECT_EQ(OK, events[1].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
+ events[2].event_type);
+ EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[2].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
+ events[3].event_type);
+ EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[3].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
+ events[4].event_type);
+ EXPECT_EQ(OK, events[4].result);
+
+ // EOF closes SPDY session.
+ EXPECT_FALSE(
+ HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
+ EXPECT_TRUE(data()->at_read_eof());
+ EXPECT_TRUE(data()->at_write_eof());
+}
+
TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) {
Prepare(1);
MockWrite writes[] = {
@@ -500,18 +562,20 @@ TEST_P(SpdyWebSocketStreamTest, IOPending) {
Prepare(1);
scoped_ptr<SpdyFrame> settings_frame(
spdy_util_.ConstructSpdySettings(spdy_settings_to_send_));
+ scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck());
MockWrite writes[] = {
- CreateMockWrite(*request_frame_.get(), 1),
- CreateMockWrite(*message_frame_.get(), 3),
- CreateMockWrite(*closing_frame_.get(), 5)
+ CreateMockWrite(*settings_ack, 1),
+ CreateMockWrite(*request_frame_.get(), 2),
+ CreateMockWrite(*message_frame_.get(), 4),
+ CreateMockWrite(*closing_frame_.get(), 6)
};
MockRead reads[] = {
CreateMockRead(*settings_frame.get(), 0),
- CreateMockRead(*response_frame_.get(), 2),
- CreateMockRead(*message_frame_.get(), 4),
- CreateMockRead(*closing_frame_.get(), 6),
- MockRead(SYNCHRONOUS, 0, 7) // EOF cause OnCloseSpdyStream event.
+ CreateMockRead(*response_frame_.get(), 3),
+ CreateMockRead(*message_frame_.get(), 5),
+ CreateMockRead(*closing_frame_.get(), 7),
+ MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event.
};
DeterministicSocketData data(reads, arraysize(reads),
@@ -560,7 +624,7 @@ TEST_P(SpdyWebSocketStreamTest, IOPending) {
SendRequest();
- data.RunFor(7);
+ data.RunFor(8);
completion_callback_.WaitForResult();
websocket_stream_.reset();
diff --git a/chromium/net/spdy/spdy_websocket_test_util.cc b/chromium/net/spdy/spdy_websocket_test_util.cc
index 0e09453b347..5e79020c21a 100644
--- a/chromium/net/spdy/spdy_websocket_test_util.cc
+++ b/chromium/net/spdy/spdy_websocket_test_util.cc
@@ -150,6 +150,10 @@ SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdySettings(
return spdy_util_.ConstructSpdySettings(settings);
}
+SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdySettingsAck() const {
+ return spdy_util_.ConstructSpdySettingsAck();
+}
+
SpdyMajorVersion SpdyWebSocketTestUtil::spdy_version() const {
return spdy_util_.spdy_version();
}
diff --git a/chromium/net/spdy/spdy_websocket_test_util.h b/chromium/net/spdy/spdy_websocket_test_util.h
index 7a9a59e96e8..14c8c0279bc 100644
--- a/chromium/net/spdy/spdy_websocket_test_util.h
+++ b/chromium/net/spdy/spdy_websocket_test_util.h
@@ -63,6 +63,7 @@ class SpdyWebSocketTestUtil {
// Forwards to |spdy_util_|.
SpdyFrame* ConstructSpdySettings(const SettingsMap& settings) const;
+ SpdyFrame* ConstructSpdySettingsAck() const;
SpdyMajorVersion spdy_version() const;
private:
diff --git a/chromium/net/spdy/spdy_write_queue.cc b/chromium/net/spdy/spdy_write_queue.cc
index ea6eb1f2922..05b965d957d 100644
--- a/chromium/net/spdy/spdy_write_queue.cc
+++ b/chromium/net/spdy/spdy_write_queue.cc
@@ -5,8 +5,10 @@
#include "net/spdy/spdy_write_queue.h"
#include <cstddef>
+#include <vector>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "net/spdy/spdy_buffer.h"
#include "net/spdy/spdy_buffer_producer.h"
#include "net/spdy/spdy_stream.h"
@@ -26,7 +28,7 @@ SpdyWriteQueue::PendingWrite::PendingWrite(
SpdyWriteQueue::PendingWrite::~PendingWrite() {}
-SpdyWriteQueue::SpdyWriteQueue() {}
+SpdyWriteQueue::SpdyWriteQueue() : removing_writes_(false) {}
SpdyWriteQueue::~SpdyWriteQueue() {
Clear();
@@ -44,6 +46,7 @@ void SpdyWriteQueue::Enqueue(RequestPriority priority,
SpdyFrameType frame_type,
scoped_ptr<SpdyBufferProducer> frame_producer,
const base::WeakPtr<SpdyStream>& stream) {
+ CHECK(!removing_writes_);
CHECK_GE(priority, MINIMUM_PRIORITY);
CHECK_LE(priority, MAXIMUM_PRIORITY);
if (stream.get())
@@ -55,6 +58,7 @@ void SpdyWriteQueue::Enqueue(RequestPriority priority,
bool SpdyWriteQueue::Dequeue(SpdyFrameType* frame_type,
scoped_ptr<SpdyBufferProducer>* frame_producer,
base::WeakPtr<SpdyStream>* stream) {
+ CHECK(!removing_writes_);
for (int i = MAXIMUM_PRIORITY; i >= MINIMUM_PRIORITY; --i) {
if (!queue_[i].empty()) {
PendingWrite pending_write = queue_[i].front();
@@ -72,23 +76,29 @@ bool SpdyWriteQueue::Dequeue(SpdyFrameType* frame_type,
void SpdyWriteQueue::RemovePendingWritesForStream(
const base::WeakPtr<SpdyStream>& stream) {
+ CHECK(!removing_writes_);
+ removing_writes_ = true;
RequestPriority priority = stream->priority();
CHECK_GE(priority, MINIMUM_PRIORITY);
CHECK_LE(priority, MAXIMUM_PRIORITY);
DCHECK(stream.get());
- if (DCHECK_IS_ON()) {
- // |stream| should not have pending writes in a queue not matching
- // its priority.
- for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
- if (priority == i)
- continue;
- for (std::deque<PendingWrite>::const_iterator it = queue_[i].begin();
- it != queue_[i].end(); ++it) {
- DCHECK_NE(it->stream.get(), stream.get());
- }
+#if DCHECK_IS_ON
+ // |stream| should not have pending writes in a queue not matching
+ // its priority.
+ for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
+ if (priority == i)
+ continue;
+ for (std::deque<PendingWrite>::const_iterator it = queue_[i].begin();
+ it != queue_[i].end(); ++it) {
+ DCHECK_NE(it->stream.get(), stream.get());
}
}
+#endif
+
+ // Defer deletion until queue iteration is complete, as
+ // SpdyBuffer::~SpdyBuffer() can result in callbacks into SpdyWriteQueue.
+ std::vector<SpdyBufferProducer*> erased_buffer_producers;
// Do the actual deletion and removal, preserving FIFO-ness.
std::deque<PendingWrite>* queue = &queue_[priority];
@@ -96,17 +106,23 @@ void SpdyWriteQueue::RemovePendingWritesForStream(
for (std::deque<PendingWrite>::const_iterator it = queue->begin();
it != queue->end(); ++it) {
if (it->stream.get() == stream.get()) {
- delete it->frame_producer;
+ erased_buffer_producers.push_back(it->frame_producer);
} else {
*out_it = *it;
++out_it;
}
}
queue->erase(out_it, queue->end());
+ removing_writes_ = false;
+ STLDeleteElements(&erased_buffer_producers); // Invokes callbacks.
}
void SpdyWriteQueue::RemovePendingWritesForStreamsAfter(
SpdyStreamId last_good_stream_id) {
+ CHECK(!removing_writes_);
+ removing_writes_ = true;
+ std::vector<SpdyBufferProducer*> erased_buffer_producers;
+
for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
// Do the actual deletion and removal, preserving FIFO-ness.
std::deque<PendingWrite>* queue = &queue_[i];
@@ -115,7 +131,7 @@ void SpdyWriteQueue::RemovePendingWritesForStreamsAfter(
it != queue->end(); ++it) {
if (it->stream.get() && (it->stream->stream_id() > last_good_stream_id ||
it->stream->stream_id() == 0)) {
- delete it->frame_producer;
+ erased_buffer_producers.push_back(it->frame_producer);
} else {
*out_it = *it;
++out_it;
@@ -123,16 +139,24 @@ void SpdyWriteQueue::RemovePendingWritesForStreamsAfter(
}
queue->erase(out_it, queue->end());
}
+ removing_writes_ = false;
+ STLDeleteElements(&erased_buffer_producers); // Invokes callbacks.
}
void SpdyWriteQueue::Clear() {
+ CHECK(!removing_writes_);
+ removing_writes_ = true;
+ std::vector<SpdyBufferProducer*> erased_buffer_producers;
+
for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
for (std::deque<PendingWrite>::iterator it = queue_[i].begin();
it != queue_[i].end(); ++it) {
- delete it->frame_producer;
+ erased_buffer_producers.push_back(it->frame_producer);
}
queue_[i].clear();
}
+ removing_writes_ = false;
+ STLDeleteElements(&erased_buffer_producers); // Invokes callbacks.
}
} // namespace net
diff --git a/chromium/net/spdy/spdy_write_queue.h b/chromium/net/spdy/spdy_write_queue.h
index 3bceb298c01..bd67f6ba38b 100644
--- a/chromium/net/spdy/spdy_write_queue.h
+++ b/chromium/net/spdy/spdy_write_queue.h
@@ -78,6 +78,8 @@ class NET_EXPORT_PRIVATE SpdyWriteQueue {
~PendingWrite();
};
+ bool removing_writes_;
+
// The actual write queue, binned by priority.
std::deque<PendingWrite> queue_[NUM_PRIORITIES];
diff --git a/chromium/net/spdy/spdy_write_queue_unittest.cc b/chromium/net/spdy/spdy_write_queue_unittest.cc
index 6d6cb3cd3b5..2ab415acde5 100644
--- a/chromium/net/spdy/spdy_write_queue_unittest.cc
+++ b/chromium/net/spdy/spdy_write_queue_unittest.cc
@@ -23,6 +23,11 @@ namespace net {
namespace {
+using std::string;
+
+const char kOriginal[] = "original";
+const char kRequeued[] = "requeued";
+
class SpdyWriteQueueTest : public ::testing::Test {};
// Makes a SpdyFrameProducer producing a frame with the data in the
@@ -44,6 +49,35 @@ scoped_ptr<SpdyBufferProducer> IntToProducer(int i) {
return StringToProducer(base::IntToString(i));
}
+// Producer whose produced buffer will enqueue yet another buffer into the
+// SpdyWriteQueue upon destruction.
+class RequeingBufferProducer : public SpdyBufferProducer {
+ public:
+ RequeingBufferProducer(SpdyWriteQueue* queue) {
+ buffer_.reset(new SpdyBuffer(kOriginal, arraysize(kOriginal)));
+ buffer_->AddConsumeCallback(
+ base::Bind(RequeingBufferProducer::ConsumeCallback, queue));
+ }
+
+ virtual scoped_ptr<SpdyBuffer> ProduceBuffer() OVERRIDE {
+ return buffer_.Pass();
+ }
+
+ static void ConsumeCallback(SpdyWriteQueue* queue,
+ size_t size,
+ SpdyBuffer::ConsumeSource source) {
+ scoped_ptr<SpdyBufferProducer> producer(
+ new SimpleBufferProducer(scoped_ptr<SpdyBuffer>(
+ new SpdyBuffer(kRequeued, arraysize(kRequeued)))));
+
+ queue->Enqueue(
+ MEDIUM, RST_STREAM, producer.Pass(), base::WeakPtr<SpdyStream>());
+ }
+
+ private:
+ scoped_ptr<SpdyBuffer> buffer_;
+};
+
// Produces a frame with the given producer and returns a copy of its
// data as a string.
std::string ProducerToString(scoped_ptr<SpdyBufferProducer> producer) {
@@ -247,6 +281,96 @@ TEST_F(SpdyWriteQueueTest, Clear) {
EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
}
+TEST_F(SpdyWriteQueueTest, RequeingProducerWithoutReentrance) {
+ SpdyWriteQueue queue;
+ queue.Enqueue(
+ DEFAULT_PRIORITY,
+ SYN_STREAM,
+ scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
+ base::WeakPtr<SpdyStream>());
+ {
+ SpdyFrameType frame_type;
+ scoped_ptr<SpdyBufferProducer> producer;
+ base::WeakPtr<SpdyStream> stream;
+
+ EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
+ EXPECT_TRUE(queue.IsEmpty());
+ EXPECT_EQ(string(kOriginal), producer->ProduceBuffer()->GetRemainingData());
+ }
+ // |producer| was destroyed, and a buffer is re-queued.
+ EXPECT_FALSE(queue.IsEmpty());
+
+ SpdyFrameType frame_type;
+ scoped_ptr<SpdyBufferProducer> producer;
+ base::WeakPtr<SpdyStream> stream;
+
+ EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
+ EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+}
+
+TEST_F(SpdyWriteQueueTest, ReentranceOnClear) {
+ SpdyWriteQueue queue;
+ queue.Enqueue(
+ DEFAULT_PRIORITY,
+ SYN_STREAM,
+ scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
+ base::WeakPtr<SpdyStream>());
+
+ queue.Clear();
+ EXPECT_FALSE(queue.IsEmpty());
+
+ SpdyFrameType frame_type;
+ scoped_ptr<SpdyBufferProducer> producer;
+ base::WeakPtr<SpdyStream> stream;
+
+ EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
+ EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+}
+
+TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesAfter) {
+ scoped_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY));
+ stream->set_stream_id(2);
+
+ SpdyWriteQueue queue;
+ queue.Enqueue(
+ DEFAULT_PRIORITY,
+ SYN_STREAM,
+ scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
+ stream->GetWeakPtr());
+
+ queue.RemovePendingWritesForStreamsAfter(1);
+ EXPECT_FALSE(queue.IsEmpty());
+
+ SpdyFrameType frame_type;
+ scoped_ptr<SpdyBufferProducer> producer;
+ base::WeakPtr<SpdyStream> weak_stream;
+
+ EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream));
+ EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+}
+
+TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesForStream) {
+ scoped_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY));
+ stream->set_stream_id(2);
+
+ SpdyWriteQueue queue;
+ queue.Enqueue(
+ DEFAULT_PRIORITY,
+ SYN_STREAM,
+ scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
+ stream->GetWeakPtr());
+
+ queue.RemovePendingWritesForStream(stream->GetWeakPtr());
+ EXPECT_FALSE(queue.IsEmpty());
+
+ SpdyFrameType frame_type;
+ scoped_ptr<SpdyBufferProducer> producer;
+ base::WeakPtr<SpdyStream> weak_stream;
+
+ EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream));
+ EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/ssl/client_cert_store.h b/chromium/net/ssl/client_cert_store.h
index 394d774f6c3..fe050e5f099 100644
--- a/chromium/net/ssl/client_cert_store.h
+++ b/chromium/net/ssl/client_cert_store.h
@@ -23,7 +23,8 @@ class NET_EXPORT ClientCertStore {
// Get client certs matching the |cert_request_info|. On completion, the
// results will be stored in |selected_certs| and the |callback| will be run.
// The |callback| may be called sychronously. The caller must ensure the
- // |selected_certs| object remains alive until the callback has been run.
+ // ClientCertStore and the |selected_certs| object remain alive until the
+ // callback has been run.
virtual void GetClientCerts(const SSLCertRequestInfo& cert_request_info,
CertificateList* selected_certs,
const base::Closure& callback) = 0;
diff --git a/chromium/net/ssl/client_cert_store_chromeos.cc b/chromium/net/ssl/client_cert_store_chromeos.cc
new file mode 100644
index 00000000000..f357797e34b
--- /dev/null
+++ b/chromium/net/ssl/client_cert_store_chromeos.cc
@@ -0,0 +1,94 @@
+// 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/ssl/client_cert_store_chromeos.h"
+
+#include <cert.h>
+
+#include "base/bind.h"
+#include "crypto/nss_crypto_module_delegate.h"
+#include "crypto/nss_util_internal.h"
+
+namespace net {
+
+ClientCertStoreChromeOS::ClientCertStoreChromeOS(
+ const std::string& username_hash,
+ const PasswordDelegateFactory& password_delegate_factory)
+ : ClientCertStoreNSS(password_delegate_factory),
+ username_hash_(username_hash) {}
+
+ClientCertStoreChromeOS::~ClientCertStoreChromeOS() {}
+
+void ClientCertStoreChromeOS::GetClientCerts(
+ const SSLCertRequestInfo& cert_request_info,
+ CertificateList* selected_certs,
+ const base::Closure& callback) {
+ crypto::ScopedPK11Slot private_slot(crypto::GetPrivateSlotForChromeOSUser(
+ username_hash_,
+ base::Bind(&ClientCertStoreChromeOS::DidGetPrivateSlot,
+ // Caller is responsible for keeping the ClientCertStore alive
+ // until the callback is run.
+ base::Unretained(this),
+ &cert_request_info,
+ selected_certs,
+ callback)));
+ if (private_slot)
+ DidGetPrivateSlot(
+ &cert_request_info, selected_certs, callback, private_slot.Pass());
+}
+
+void ClientCertStoreChromeOS::GetClientCertsImpl(CERTCertList* cert_list,
+ const SSLCertRequestInfo& request,
+ bool query_nssdb,
+ CertificateList* selected_certs) {
+ ClientCertStoreNSS::GetClientCertsImpl(
+ cert_list, request, query_nssdb, selected_certs);
+
+ size_t pre_size = selected_certs->size();
+ selected_certs->erase(
+ std::remove_if(
+ selected_certs->begin(),
+ selected_certs->end(),
+ NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate(
+ profile_filter_)),
+ selected_certs->end());
+ DVLOG(1) << "filtered " << pre_size - selected_certs->size() << " of "
+ << pre_size << " certs";
+}
+
+void ClientCertStoreChromeOS::DidGetPrivateSlot(
+ const SSLCertRequestInfo* request,
+ CertificateList* selected_certs,
+ const base::Closure& callback,
+ crypto::ScopedPK11Slot private_slot) {
+ profile_filter_.Init(crypto::GetPublicSlotForChromeOSUser(username_hash_),
+ private_slot.Pass());
+ ClientCertStoreNSS::GetClientCerts(*request, selected_certs, callback);
+}
+
+void ClientCertStoreChromeOS::InitForTesting(
+ crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot) {
+ profile_filter_.Init(public_slot.Pass(), private_slot.Pass());
+}
+
+bool ClientCertStoreChromeOS::SelectClientCertsForTesting(
+ const CertificateList& input_certs,
+ const SSLCertRequestInfo& request,
+ CertificateList* selected_certs) {
+ CERTCertList* cert_list = CERT_NewCertList();
+ if (!cert_list)
+ return false;
+ for (size_t i = 0; i < input_certs.size(); ++i) {
+ CERT_AddCertToListTail(
+ cert_list, CERT_DupCertificate(input_certs[i]->os_cert_handle()));
+ }
+
+ GetClientCertsImpl(cert_list, request, false, selected_certs);
+ CERT_DestroyCertList(cert_list);
+ return true;
+}
+
+
+} // namespace net
diff --git a/chromium/net/ssl/client_cert_store_chromeos.h b/chromium/net/ssl/client_cert_store_chromeos.h
new file mode 100644
index 00000000000..41339f1b5bb
--- /dev/null
+++ b/chromium/net/ssl/client_cert_store_chromeos.h
@@ -0,0 +1,66 @@
+// 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_SSL_CLIENT_CERT_STORE_CHROMEOS_H_
+#define NET_SSL_CLIENT_CERT_STORE_CHROMEOS_H_
+
+#include <string>
+
+#include "crypto/scoped_nss_types.h"
+#include "net/cert/nss_profile_filter_chromeos.h"
+#include "net/ssl/client_cert_store_nss.h"
+
+namespace net {
+
+class NET_EXPORT ClientCertStoreChromeOS : public ClientCertStoreNSS {
+ public:
+ ClientCertStoreChromeOS(
+ const std::string& username_hash,
+ const PasswordDelegateFactory& password_delegate_factory);
+ virtual ~ClientCertStoreChromeOS();
+
+ // ClientCertStoreNSS:
+ virtual void GetClientCerts(const SSLCertRequestInfo& cert_request_info,
+ CertificateList* selected_certs,
+ const base::Closure& callback) OVERRIDE;
+
+ protected:
+ // ClientCertStoreNSS:
+ virtual void GetClientCertsImpl(CERTCertList* cert_list,
+ const SSLCertRequestInfo& request,
+ bool query_nssdb,
+ CertificateList* selected_certs) OVERRIDE;
+
+ private:
+ friend class ClientCertStoreChromeOSTestDelegate;
+
+ void DidGetPrivateSlot(const SSLCertRequestInfo* request,
+ CertificateList* selected_certs,
+ const base::Closure& callback,
+ crypto::ScopedPK11Slot private_slot);
+
+ // Allows tests to initialize the cert store with the given slots.
+ // Must be called before SelectClientCertsForTesting.
+ void InitForTesting(crypto::ScopedPK11Slot public_slot,
+ crypto::ScopedPK11Slot private_slot);
+
+ // A hook for testing. Filters |input_certs| using the logic being used to
+ // filter the system store when GetClientCerts() is called.
+ // Implemented by creating a list of certificates that otherwise would be
+ // extracted from the system store and filtering it using the common logic
+ // (less adequate than the approach used on Windows).
+ bool SelectClientCertsForTesting(const CertificateList& input_certs,
+ const SSLCertRequestInfo& cert_request_info,
+ CertificateList* selected_certs);
+
+
+ std::string username_hash_;
+ NSSProfileFilterChromeOS profile_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientCertStoreChromeOS);
+};
+
+} // namespace net
+
+#endif // NET_SSL_CLIENT_CERT_STORE_CHROMEOS_H_
diff --git a/chromium/net/ssl/client_cert_store_chromeos_unittest.cc b/chromium/net/ssl/client_cert_store_chromeos_unittest.cc
new file mode 100644
index 00000000000..de0358df15b
--- /dev/null
+++ b/chromium/net/ssl/client_cert_store_chromeos_unittest.cc
@@ -0,0 +1,189 @@
+// 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/ssl/client_cert_store_chromeos.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/ssl/client_cert_store_unittest-inl.h"
+
+namespace net {
+
+class ClientCertStoreChromeOSTestDelegate {
+ public:
+ ClientCertStoreChromeOSTestDelegate()
+ : store_("usernamehash",
+ ClientCertStoreChromeOS::PasswordDelegateFactory()) {
+ store_.InitForTesting(
+ crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()),
+ crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot()));
+ }
+
+ bool SelectClientCerts(const CertificateList& input_certs,
+ const SSLCertRequestInfo& cert_request_info,
+ CertificateList* selected_certs) {
+ return store_.SelectClientCertsForTesting(
+ input_certs, cert_request_info, selected_certs);
+ }
+
+ private:
+ ClientCertStoreChromeOS store_;
+};
+
+class ClientCertStoreChromeOSTest : public ::testing::Test {
+ public:
+ scoped_refptr<X509Certificate> ImportCertForUser(
+ const std::string& username_hash,
+ const std::string& filename,
+ const std::string& password) {
+ crypto::ScopedPK11Slot slot(
+ crypto::GetPublicSlotForChromeOSUser(username_hash));
+ EXPECT_TRUE(slot.get());
+ if (!slot.get())
+ return NULL;
+
+ net::CertificateList cert_list;
+
+ base::FilePath p12_path = GetTestCertsDirectory().AppendASCII(filename);
+ std::string p12_data;
+ if (!base::ReadFileToString(p12_path, &p12_data)) {
+ EXPECT_TRUE(false);
+ return NULL;
+ }
+
+ scoped_refptr<net::CryptoModule> module(
+ net::CryptoModule::CreateFromHandle(slot.get()));
+ int rv = NSSCertDatabase::GetInstance()->ImportFromPKCS12(
+ module.get(), p12_data, base::UTF8ToUTF16(password), false, &cert_list);
+
+ EXPECT_EQ(0, rv);
+ EXPECT_EQ(1U, cert_list.size());
+ if (rv || cert_list.size() != 1)
+ return NULL;
+
+ return cert_list[0];
+ }
+};
+
+// TODO(mattm): Do better testing of cert_authorities matching below. Update
+// net/data/ssl/scripts/generate-client-certificates.sh so that it actually
+// saves the .p12 files, and regenerate them.
+
+TEST_F(ClientCertStoreChromeOSTest, WaitForNSSInit) {
+ crypto::ScopedTestNSSChromeOSUser user("scopeduser");
+ ASSERT_TRUE(user.constructed_successfully());
+ ClientCertStoreChromeOS store(
+ user.username_hash(), ClientCertStoreChromeOS::PasswordDelegateFactory());
+ scoped_refptr<X509Certificate> cert_1(
+ ImportCertForUser(user.username_hash(), "client.p12", "12345"));
+ scoped_refptr<X509Certificate> cert_2(
+ ImportCertForUser(user.username_hash(), "websocket_client_cert.p12", ""));
+
+ std::vector<std::string> authority_1(
+ 1,
+ std::string(reinterpret_cast<const char*>(kAuthority1DN),
+ sizeof(kAuthority1DN)));
+ scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo());
+ request_1->cert_authorities = authority_1;
+
+ scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo());
+
+ base::RunLoop run_loop_1;
+ base::RunLoop run_loop_all;
+ store.GetClientCerts(
+ *request_1, &request_1->client_certs, run_loop_1.QuitClosure());
+ store.GetClientCerts(
+ *request_all, &request_all->client_certs, run_loop_all.QuitClosure());
+
+ // Callbacks won't be run until nss_util init finishes for the user.
+ user.FinishInit();
+
+ run_loop_1.Run();
+ run_loop_all.Run();
+
+ ASSERT_EQ(0u, request_1->client_certs.size());
+ ASSERT_EQ(2u, request_all->client_certs.size());
+}
+
+TEST_F(ClientCertStoreChromeOSTest, NSSAlreadyInitialized) {
+ crypto::ScopedTestNSSChromeOSUser user("scopeduser");
+ ASSERT_TRUE(user.constructed_successfully());
+ user.FinishInit();
+
+ ClientCertStoreChromeOS store(
+ user.username_hash(), ClientCertStoreChromeOS::PasswordDelegateFactory());
+ scoped_refptr<X509Certificate> cert_1(
+ ImportCertForUser(user.username_hash(), "client.p12", "12345"));
+ scoped_refptr<X509Certificate> cert_2(
+ ImportCertForUser(user.username_hash(), "websocket_client_cert.p12", ""));
+
+ std::vector<std::string> authority_1(
+ 1,
+ std::string(reinterpret_cast<const char*>(kAuthority1DN),
+ sizeof(kAuthority1DN)));
+ scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo());
+ request_1->cert_authorities = authority_1;
+
+ scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo());
+
+ base::RunLoop run_loop_1;
+ base::RunLoop run_loop_all;
+ store.GetClientCerts(
+ *request_1, &request_1->client_certs, run_loop_1.QuitClosure());
+ store.GetClientCerts(
+ *request_all, &request_all->client_certs, run_loop_all.QuitClosure());
+
+ run_loop_1.Run();
+ run_loop_all.Run();
+
+ ASSERT_EQ(0u, request_1->client_certs.size());
+ ASSERT_EQ(2u, request_all->client_certs.size());
+}
+
+TEST_F(ClientCertStoreChromeOSTest, TwoUsers) {
+ crypto::ScopedTestNSSChromeOSUser user1("scopeduser1");
+ ASSERT_TRUE(user1.constructed_successfully());
+ crypto::ScopedTestNSSChromeOSUser user2("scopeduser2");
+ ASSERT_TRUE(user2.constructed_successfully());
+ ClientCertStoreChromeOS store1(
+ user1.username_hash(),
+ ClientCertStoreChromeOS::PasswordDelegateFactory());
+ ClientCertStoreChromeOS store2(
+ user2.username_hash(),
+ ClientCertStoreChromeOS::PasswordDelegateFactory());
+ scoped_refptr<X509Certificate> cert_1(
+ ImportCertForUser(user1.username_hash(), "client.p12", "12345"));
+ scoped_refptr<X509Certificate> cert_2(ImportCertForUser(
+ user2.username_hash(), "websocket_client_cert.p12", ""));
+
+ scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo());
+ scoped_refptr<SSLCertRequestInfo> request_2(new SSLCertRequestInfo());
+
+ base::RunLoop run_loop_1;
+ base::RunLoop run_loop_2;
+ store1.GetClientCerts(
+ *request_1, &request_1->client_certs, run_loop_1.QuitClosure());
+ store2.GetClientCerts(
+ *request_2, &request_2->client_certs, run_loop_2.QuitClosure());
+
+ // Callbacks won't be run until nss_util init finishes for the user.
+ user1.FinishInit();
+ user2.FinishInit();
+
+ run_loop_1.Run();
+ run_loop_2.Run();
+
+ ASSERT_EQ(1u, request_1->client_certs.size());
+ EXPECT_TRUE(cert_1->Equals(request_1->client_certs[0]));
+ // TODO(mattm): Request for second user will have zero results due to
+ // crbug.com/315285. Update the test once that is fixed.
+}
+
+} // namespace net
diff --git a/chromium/net/ssl/client_cert_store_mac.cc b/chromium/net/ssl/client_cert_store_mac.cc
index 746d1230632..06d7d0613c8 100644
--- a/chromium/net/ssl/client_cert_store_mac.cc
+++ b/chromium/net/ssl/client_cert_store_mac.cc
@@ -179,8 +179,7 @@ ClientCertStoreMac::~ClientCertStoreMac() {}
void ClientCertStoreMac::GetClientCerts(const SSLCertRequestInfo& request,
CertificateList* selected_certs,
const base::Closure& callback) {
- std::string server_domain =
- HostPortPair::FromString(request.host_and_port).host();
+ std::string server_domain = request.host_and_port.host();
ScopedCFTypeRef<SecIdentityRef> preferred_identity;
if (!server_domain.empty()) {
diff --git a/chromium/net/ssl/client_cert_store_nss.cc b/chromium/net/ssl/client_cert_store_nss.cc
index 9df561271c7..1b44bce92da 100644
--- a/chromium/net/ssl/client_cert_store_nss.cc
+++ b/chromium/net/ssl/client_cert_store_nss.cc
@@ -11,23 +11,47 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
#include "base/threading/worker_pool.h"
#include "crypto/nss_crypto_module_delegate.h"
#include "net/cert/x509_util.h"
namespace net {
-namespace {
-
-// Examines the certificates in |cert_list| to find all certificates that match
-// the client certificate request in |request|, storing the matching
-// certificates in |selected_certs|.
-// If |query_nssdb| is true, NSS will be queried to construct full certificate
-// chains. If it is false, only the certificate will be considered.
-void GetClientCertsImpl(CERTCertList* cert_list,
- const SSLCertRequestInfo& request,
- bool query_nssdb,
- CertificateList* selected_certs) {
+ClientCertStoreNSS::ClientCertStoreNSS(
+ const PasswordDelegateFactory& password_delegate_factory)
+ : password_delegate_factory_(password_delegate_factory) {}
+
+ClientCertStoreNSS::~ClientCertStoreNSS() {}
+
+void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request,
+ CertificateList* selected_certs,
+ const base::Closure& callback) {
+ scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
+ if (!password_delegate_factory_.is_null()) {
+ password_delegate.reset(
+ password_delegate_factory_.Run(request.host_and_port));
+ }
+ if (base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&ClientCertStoreNSS::GetClientCertsOnWorkerThread,
+ // Caller is responsible for keeping the ClientCertStore
+ // alive until the callback is run.
+ base::Unretained(this),
+ base::Passed(&password_delegate),
+ &request,
+ selected_certs),
+ callback,
+ true))
+ return;
+ selected_certs->clear();
+ callback.Run();
+}
+
+void ClientCertStoreNSS::GetClientCertsImpl(CERTCertList* cert_list,
+ const SSLCertRequestInfo& request,
+ bool query_nssdb,
+ CertificateList* selected_certs) {
DCHECK(cert_list);
DCHECK(selected_certs);
@@ -53,12 +77,16 @@ void GetClientCertsImpl(CERTCertList* cert_list,
if (!ca_names_items.empty())
ca_names.names = &ca_names_items[0];
+ size_t num_raw = 0;
for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
!CERT_LIST_END(node, cert_list);
node = CERT_LIST_NEXT(node)) {
+ ++num_raw;
// Only offer unexpired certificates.
if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) !=
secCertTimeValid) {
+ DVLOG(2) << "skipped expired cert: "
+ << base::StringPiece(node->cert->nickname);
continue;
}
@@ -71,15 +99,21 @@ void GetClientCertsImpl(CERTCertList* cert_list,
cert->IsIssuedByEncoded(request.cert_authorities)) ||
(query_nssdb &&
NSS_CmpCertChainWCANames(node->cert, &ca_names) == SECSuccess)) {
+ DVLOG(2) << "matched cert: " << base::StringPiece(node->cert->nickname);
selected_certs->push_back(cert);
}
+ else
+ DVLOG(2) << "skipped non-matching cert: "
+ << base::StringPiece(node->cert->nickname);
}
+ DVLOG(2) << "num_raw:" << num_raw
+ << " num_selected:" << selected_certs->size();
std::sort(selected_certs->begin(), selected_certs->end(),
x509_util::ClientCertSorter());
}
-void GetClientCertsOnWorkerThread(
+void ClientCertStoreNSS::GetClientCertsOnWorkerThread(
scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate,
const SSLCertRequestInfo* request,
CertificateList* selected_certs) {
@@ -91,6 +125,7 @@ void GetClientCertsOnWorkerThread(
password_delegate.get());
// It is ok for a user not to have any client certs.
if (!client_certs) {
+ DVLOG(2) << "No client certs found.";
selected_certs->clear();
return;
}
@@ -99,35 +134,6 @@ void GetClientCertsOnWorkerThread(
CERT_DestroyCertList(client_certs);
}
-} // namespace
-
-ClientCertStoreNSS::ClientCertStoreNSS(
- const PasswordDelegateFactory& password_delegate_factory)
- : password_delegate_factory_(password_delegate_factory) {}
-
-ClientCertStoreNSS::~ClientCertStoreNSS() {}
-
-void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request,
- CertificateList* selected_certs,
- const base::Closure& callback) {
- scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
- if (!password_delegate_factory_.is_null()) {
- password_delegate.reset(
- password_delegate_factory_.Run(request.host_and_port));
- }
- if (!base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&GetClientCertsOnWorkerThread,
- base::Passed(&password_delegate),
- &request,
- selected_certs),
- callback,
- true)) {
- selected_certs->clear();
- callback.Run();
- }
-}
-
bool ClientCertStoreNSS::SelectClientCertsForTesting(
const CertificateList& input_certs,
const SSLCertRequestInfo& request,
diff --git a/chromium/net/ssl/client_cert_store_nss.h b/chromium/net/ssl/client_cert_store_nss.h
index 53b7c608031..4e2e1c459da 100644
--- a/chromium/net/ssl/client_cert_store_nss.h
+++ b/chromium/net/ssl/client_cert_store_nss.h
@@ -12,6 +12,8 @@
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
+typedef struct CERTCertListStr CERTCertList;
+
namespace crypto {
class CryptoModuleBlockingPasswordDelegate;
}
@@ -21,7 +23,7 @@ namespace net {
class NET_EXPORT ClientCertStoreNSS : public ClientCertStore {
public:
typedef base::Callback<crypto::CryptoModuleBlockingPasswordDelegate*(
- const std::string& /* server */)> PasswordDelegateFactory;
+ const HostPortPair& /* server */)> PasswordDelegateFactory;
explicit ClientCertStoreNSS(
const PasswordDelegateFactory& password_delegate_factory);
@@ -32,9 +34,26 @@ class NET_EXPORT ClientCertStoreNSS : public ClientCertStore {
CertificateList* selected_certs,
const base::Closure& callback) OVERRIDE;
+ protected:
+ // Examines the certificates in |cert_list| to find all certificates that
+ // match the client certificate request in |request|, storing the matching
+ // certificates in |selected_certs|.
+ // If |query_nssdb| is true, NSS will be queried to construct full certificate
+ // chains. If it is false, only the certificate will be considered.
+ virtual void GetClientCertsImpl(CERTCertList* cert_list,
+ const SSLCertRequestInfo& request,
+ bool query_nssdb,
+ CertificateList* selected_certs);
+
private:
friend class ClientCertStoreNSSTestDelegate;
+ void GetClientCertsOnWorkerThread(
+ scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate>
+ password_delegate,
+ const SSLCertRequestInfo* request,
+ CertificateList* selected_certs);
+
// A hook for testing. Filters |input_certs| using the logic being used to
// filter the system store when GetClientCerts() is called.
// Implemented by creating a list of certificates that otherwise would be
diff --git a/chromium/net/ssl/openssl_client_key_store.h b/chromium/net/ssl/openssl_client_key_store.h
index 6d90253ff14..e2b57a37fc7 100644
--- a/chromium/net/ssl/openssl_client_key_store.h
+++ b/chromium/net/ssl/openssl_client_key_store.h
@@ -62,7 +62,7 @@ class NET_EXPORT OpenSSLClientKeyStore {
bool FetchClientCertPrivateKey(const X509Certificate* cert,
ScopedEVP_PKEY* private_key);
- // Flush all recorded keys. Used only during testing.
+ // Flush all recorded keys.
void Flush();
protected:
diff --git a/chromium/net/ssl/server_bound_cert_service_unittest.cc b/chromium/net/ssl/server_bound_cert_service_unittest.cc
index 4df003913aa..2be073bb16c 100644
--- a/chromium/net/ssl/server_bound_cert_service_unittest.cc
+++ b/chromium/net/ssl/server_bound_cert_service_unittest.cc
@@ -24,7 +24,6 @@ namespace net {
namespace {
-#if !defined(USE_OPENSSL)
void FailTest(int /* result */) {
FAIL();
}
@@ -117,8 +116,6 @@ MockServerBoundCertStoreWithAsyncGet::CallGetServerBoundCertCallbackWithResult(
cert));
}
-#endif // !defined(USE_OPENSSL)
-
class ServerBoundCertServiceTest : public testing::Test {
public:
ServerBoundCertServiceTest()
@@ -150,9 +147,6 @@ TEST_F(ServerBoundCertServiceTest, GetDomainForHost) {
ServerBoundCertService::GetDomainForHost("127.0.0.1"));
}
-// See http://crbug.com/91512 - implement OpenSSL version of CreateSelfSigned.
-#if !defined(USE_OPENSSL)
-
TEST_F(ServerBoundCertServiceTest, GetCacheMiss) {
std::string host("encrypted.google.com");
@@ -775,8 +769,6 @@ TEST_F(ServerBoundCertServiceTest, AsyncStoreGetThenCreateNoCertsInStore) {
EXPECT_FALSE(request_handle2.is_active());
}
-#endif // !defined(USE_OPENSSL)
-
} // namespace
} // namespace net
diff --git a/chromium/net/ssl/signed_certificate_timestamp_and_status.cc b/chromium/net/ssl/signed_certificate_timestamp_and_status.cc
index 2808fec3e4d..245fcf2ac2e 100644
--- a/chromium/net/ssl/signed_certificate_timestamp_and_status.cc
+++ b/chromium/net/ssl/signed_certificate_timestamp_and_status.cc
@@ -11,7 +11,7 @@ namespace net {
SignedCertificateTimestampAndStatus::SignedCertificateTimestampAndStatus(
const scoped_refptr<ct::SignedCertificateTimestamp>& sct,
const ct::SCTVerifyStatus status)
- : sct_(sct), status_(status) {}
+ : sct(sct), status(status) {}
SignedCertificateTimestampAndStatus::~SignedCertificateTimestampAndStatus() {}
diff --git a/chromium/net/ssl/signed_certificate_timestamp_and_status.h b/chromium/net/ssl/signed_certificate_timestamp_and_status.h
index 042c7ac41ab..c23753ea4d7 100644
--- a/chromium/net/ssl/signed_certificate_timestamp_and_status.h
+++ b/chromium/net/ssl/signed_certificate_timestamp_and_status.h
@@ -8,23 +8,21 @@
#include <vector>
#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
#include "net/cert/sct_status_flags.h"
+#include "net/cert/signed_certificate_timestamp.h"
namespace net {
-namespace ct {
-struct SignedCertificateTimestamp;
-}
-
-struct SignedCertificateTimestampAndStatus {
+struct NET_EXPORT SignedCertificateTimestampAndStatus {
SignedCertificateTimestampAndStatus(
const scoped_refptr<ct::SignedCertificateTimestamp>& sct,
ct::SCTVerifyStatus status);
~SignedCertificateTimestampAndStatus();
- scoped_refptr<ct::SignedCertificateTimestamp> sct_;
- ct::SCTVerifyStatus status_;
+ scoped_refptr<ct::SignedCertificateTimestamp> sct;
+ ct::SCTVerifyStatus status;
};
typedef std::vector<SignedCertificateTimestampAndStatus>
diff --git a/chromium/net/ssl/ssl_cert_request_info.cc b/chromium/net/ssl/ssl_cert_request_info.cc
index 10750b6a44c..e76aa458a92 100644
--- a/chromium/net/ssl/ssl_cert_request_info.cc
+++ b/chromium/net/ssl/ssl_cert_request_info.cc
@@ -12,7 +12,7 @@ SSLCertRequestInfo::SSLCertRequestInfo() : is_proxy(false) {
}
void SSLCertRequestInfo::Reset() {
- host_and_port.clear();
+ host_and_port = HostPortPair();
is_proxy = false;
cert_authorities.clear();
cert_key_types.clear();
diff --git a/chromium/net/ssl/ssl_cert_request_info.h b/chromium/net/ssl/ssl_cert_request_info.h
index fecffb8f31d..13f91d6913b 100644
--- a/chromium/net/ssl/ssl_cert_request_info.h
+++ b/chromium/net/ssl/ssl_cert_request_info.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/memory/ref_counted.h"
+#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
#include "net/ssl/ssl_client_cert_type.h"
@@ -41,7 +42,7 @@ class NET_EXPORT SSLCertRequestInfo
void Reset();
// The host and port of the SSL server that requested client authentication.
- std::string host_and_port;
+ HostPortPair host_and_port;
// True if the server that issues this request was the HTTPS proxy used in
// the request. False, if the server was the origin server.
diff --git a/chromium/net/ssl/ssl_cipher_suite_names.cc b/chromium/net/ssl/ssl_cipher_suite_names.cc
index 8204fc154d5..55b0276ee5c 100644
--- a/chromium/net/ssl/ssl_cipher_suite_names.cc
+++ b/chromium/net/ssl/ssl_cipher_suite_names.cc
@@ -323,6 +323,9 @@ void SSLVersionToString(const char** name, int ssl_version) {
case SSL_CONNECTION_VERSION_TLS1_2:
*name = "TLS 1.2";
break;
+ case SSL_CONNECTION_VERSION_QUIC:
+ *name = "QUIC";
+ break;
default:
NOTREACHED() << ssl_version;
*name = "???";
@@ -342,4 +345,49 @@ bool ParseSSLCipherString(const std::string& cipher_string,
return false;
}
+bool IsSecureTLSCipherSuite(uint16 cipher_suite) {
+ CipherSuite desired = {0};
+ desired.cipher_suite = cipher_suite;
+
+ void* r = bsearch(&desired,
+ kCipherSuites,
+ arraysize(kCipherSuites),
+ sizeof(kCipherSuites[0]),
+ CipherSuiteCmp);
+
+ if (!r)
+ return false;
+
+ const CipherSuite* cs = static_cast<const CipherSuite*>(r);
+
+ const int key_exchange = cs->encoded >> 8;
+ const int cipher = (cs->encoded >> 3) & 0x1f;
+ const int mac = cs->encoded & 0x7;
+
+ // Only allow forward secure key exchanges.
+ switch (key_exchange) {
+ case 10: // DHE_RSA
+ case 14: // ECDHE_ECDSA
+ case 16: // ECDHE_RSA
+ break;
+ default:
+ return false;
+ }
+
+ switch (cipher) {
+ case 13: // AES_128_GCM
+ case 14: // AES_256_GCM
+ case 17: // CHACHA20_POLY1305
+ break;
+ default:
+ return false;
+ }
+
+ // Only AEADs allowed.
+ if (mac != kAEADMACValue)
+ return false;
+
+ return true;
+}
+
} // namespace net
diff --git a/chromium/net/ssl/ssl_cipher_suite_names.h b/chromium/net/ssl/ssl_cipher_suite_names.h
index 5145fb24c5e..29c03a1aea0 100644
--- a/chromium/net/ssl/ssl_cipher_suite_names.h
+++ b/chromium/net/ssl/ssl_cipher_suite_names.h
@@ -46,6 +46,17 @@ NET_EXPORT void SSLVersionToString(const char** name, int ssl_version);
NET_EXPORT bool ParseSSLCipherString(const std::string& cipher_string,
uint16* cipher_suite);
+// |cipher_suite| is the IANA id for the cipher suite. What a "secure"
+// cipher suite is arbitrarily determined here. The intent is to indicate what
+// cipher suites meet modern security standards when backwards compatibility can
+// be ignored. Notably, HTTP/2 requires/encourages this sort of validation of
+// cipher suites: https://http2.github.io/http2-spec/#TLSUsage.
+//
+// Currently, this function follows these criteria:
+// 1) Only uses forward secure key exchanges
+// 2) Only uses AEADs
+NET_EXPORT_PRIVATE bool IsSecureTLSCipherSuite(uint16 cipher_suite);
+
} // namespace net
#endif // NET_SSL_SSL_CIPHER_SUITE_NAMES_H_
diff --git a/chromium/net/ssl/ssl_cipher_suite_names_unittest.cc b/chromium/net/ssl/ssl_cipher_suite_names_unittest.cc
index a9133fb67ea..042ca82a5d3 100644
--- a/chromium/net/ssl/ssl_cipher_suite_names_unittest.cc
+++ b/chromium/net/ssl/ssl_cipher_suite_names_unittest.cc
@@ -56,6 +56,22 @@ TEST(CipherSuiteNamesTest, ParseSSLCipherStringFails) {
}
}
+TEST(CipherSuiteNamesTest, SecureCipherSuites) {
+ // Picked some random cipher suites.
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0x0));
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0x39));
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0xc5));
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0xc00f));
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0xc083));
+
+ // Non-existent cipher suite.
+ EXPECT_FALSE(IsSecureTLSCipherSuite(0xffff)) << "Doesn't exist!";
+
+ // Secure ones.
+ EXPECT_TRUE(IsSecureTLSCipherSuite(0xcc13));
+ EXPECT_TRUE(IsSecureTLSCipherSuite(0xcc14));
+}
+
} // anonymous namespace
} // namespace net
diff --git a/chromium/net/ssl/ssl_client_auth_cache.cc b/chromium/net/ssl/ssl_client_auth_cache.cc
index 0c0704e6208..4c6b0d82f15 100644
--- a/chromium/net/ssl/ssl_client_auth_cache.cc
+++ b/chromium/net/ssl/ssl_client_auth_cache.cc
@@ -18,7 +18,7 @@ SSLClientAuthCache::~SSLClientAuthCache() {
}
bool SSLClientAuthCache::Lookup(
- const std::string& server,
+ const HostPortPair& server,
scoped_refptr<X509Certificate>* certificate) {
DCHECK(certificate);
@@ -30,14 +30,14 @@ bool SSLClientAuthCache::Lookup(
return true;
}
-void SSLClientAuthCache::Add(const std::string& server,
+void SSLClientAuthCache::Add(const HostPortPair& server,
X509Certificate* value) {
cache_[server] = value;
// TODO(wtc): enforce a maximum number of entries.
}
-void SSLClientAuthCache::Remove(const std::string& server) {
+void SSLClientAuthCache::Remove(const HostPortPair& server) {
cache_.erase(server);
}
diff --git a/chromium/net/ssl/ssl_client_auth_cache.h b/chromium/net/ssl/ssl_client_auth_cache.h
index 250841b1b25..93ad8c04047 100644
--- a/chromium/net/ssl/ssl_client_auth_cache.h
+++ b/chromium/net/ssl/ssl_client_auth_cache.h
@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
+#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
#include "net/cert/cert_database.h"
@@ -34,23 +35,23 @@ class NET_EXPORT_PRIVATE SSLClientAuthCache : public CertDatabase::Observer {
// desired client certificate. The desired certificate may be NULL, which
// indicates a preference to not send any certificate to |server|.
// If a certificate preference is not found, returns false.
- bool Lookup(const std::string& server,
+ bool Lookup(const HostPortPair& server,
scoped_refptr<X509Certificate>* certificate);
// Add a client certificate for |server| to the cache. If there is already
// a client certificate for |server|, it will be overwritten. A NULL
// |client_cert| indicates a preference that no client certificate should
// be sent to |server|.
- void Add(const std::string& server, X509Certificate* client_cert);
+ void Add(const HostPortPair& server, X509Certificate* client_cert);
// Remove the client certificate for |server| from the cache, if one exists.
- void Remove(const std::string& server);
+ void Remove(const HostPortPair& server);
// CertDatabase::Observer methods:
virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE;
private:
- typedef std::string AuthCacheKey;
+ typedef HostPortPair AuthCacheKey;
typedef scoped_refptr<X509Certificate> AuthCacheValue;
typedef std::map<AuthCacheKey, AuthCacheValue> AuthCacheMap;
diff --git a/chromium/net/ssl/ssl_client_auth_cache_unittest.cc b/chromium/net/ssl/ssl_client_auth_cache_unittest.cc
index 6b4b8c3b4b9..284fb46eda8 100644
--- a/chromium/net/ssl/ssl_client_auth_cache_unittest.cc
+++ b/chromium/net/ssl/ssl_client_auth_cache_unittest.cc
@@ -16,15 +16,15 @@ TEST(SSLClientAuthCacheTest, LookupAddRemove) {
base::Time start_date = base::Time::Now();
base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
- std::string server1("foo1:443");
+ HostPortPair server1("foo1", 443);
scoped_refptr<X509Certificate> cert1(
new X509Certificate("foo1", "CA", start_date, expiration_date));
- std::string server2("foo2:443");
+ HostPortPair server2("foo2", 443);
scoped_refptr<X509Certificate> cert2(
new X509Certificate("foo2", "CA", start_date, expiration_date));
- std::string server3("foo3:443");
+ HostPortPair server3("foo3", 443);
scoped_refptr<X509Certificate> cert3(
new X509Certificate("foo3", "CA", start_date, expiration_date));
@@ -82,11 +82,11 @@ TEST(SSLClientAuthCacheTest, LookupWithPort) {
base::Time start_date = base::Time::Now();
base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
- std::string server1("foo:443");
+ HostPortPair server1("foo", 443);
scoped_refptr<X509Certificate> cert1(
new X509Certificate("foo", "CA", start_date, expiration_date));
- std::string server2("foo:8443");
+ HostPortPair server2("foo", 8443);
scoped_refptr<X509Certificate> cert2(
new X509Certificate("foo", "CA", start_date, expiration_date));
@@ -107,7 +107,7 @@ TEST(SSLClientAuthCacheTest, LookupNullPreference) {
base::Time start_date = base::Time::Now();
base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
- std::string server1("foo:443");
+ HostPortPair server1("foo", 443);
scoped_refptr<X509Certificate> cert1(
new X509Certificate("foo", "CA", start_date, expiration_date));
@@ -143,13 +143,13 @@ TEST(SSLClientAuthCacheTest, OnCertAdded) {
base::Time start_date = base::Time::Now();
base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
- std::string server1("foo:443");
+ HostPortPair server1("foo", 443);
scoped_refptr<X509Certificate> cert1(
new X509Certificate("foo", "CA", start_date, expiration_date));
cache.Add(server1, cert1.get());
- std::string server2("foo2:443");
+ HostPortPair server2("foo2", 443);
cache.Add(server2, NULL);
scoped_refptr<X509Certificate> cached_cert;
diff --git a/chromium/net/ssl/ssl_config.cc b/chromium/net/ssl/ssl_config.cc
new file mode 100644
index 00000000000..02829918ed4
--- /dev/null
+++ b/chromium/net/ssl/ssl_config.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 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/ssl/ssl_config.h"
+
+#if defined(USE_OPENSSL)
+#include <openssl/ssl.h>
+#endif
+
+namespace net {
+
+const uint16 kDefaultSSLVersionMin = SSL_PROTOCOL_VERSION_SSL3;
+
+const uint16 kDefaultSSLVersionMax =
+#if defined(USE_OPENSSL)
+#if defined(SSL_OP_NO_TLSv1_2)
+ SSL_PROTOCOL_VERSION_TLS1_2;
+#elif defined(SSL_OP_NO_TLSv1_1)
+ SSL_PROTOCOL_VERSION_TLS1_1;
+#else
+ SSL_PROTOCOL_VERSION_TLS1;
+#endif
+#else
+ SSL_PROTOCOL_VERSION_TLS1_2;
+#endif
+
+SSLConfig::CertAndStatus::CertAndStatus() : cert_status(0) {}
+
+SSLConfig::CertAndStatus::~CertAndStatus() {}
+
+SSLConfig::SSLConfig()
+ : rev_checking_enabled(false),
+ rev_checking_required_local_anchors(false),
+ version_min(kDefaultSSLVersionMin),
+ version_max(kDefaultSSLVersionMax),
+ channel_id_enabled(true),
+ false_start_enabled(true),
+ signed_cert_timestamps_enabled(true),
+ require_forward_secrecy(false),
+ send_client_cert(false),
+ verify_ev_cert(false),
+ version_fallback(false),
+ cert_io_enabled(true) {
+}
+
+SSLConfig::~SSLConfig() {}
+
+bool SSLConfig::IsAllowedBadCert(X509Certificate* cert,
+ CertStatus* cert_status) const {
+ std::string der_cert;
+ if (!X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_cert))
+ return false;
+ return IsAllowedBadCert(der_cert, cert_status);
+}
+
+bool SSLConfig::IsAllowedBadCert(const base::StringPiece& der_cert,
+ CertStatus* cert_status) const {
+ for (size_t i = 0; i < allowed_bad_certs.size(); ++i) {
+ if (der_cert == allowed_bad_certs[i].der_cert) {
+ if (cert_status)
+ *cert_status = allowed_bad_certs[i].cert_status;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/ssl/ssl_config.h b/chromium/net/ssl/ssl_config.h
new file mode 100644
index 00000000000..27312147f0f
--- /dev/null
+++ b/chromium/net/ssl/ssl_config.h
@@ -0,0 +1,156 @@
+// Copyright 2014 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_SSL_SSL_CONFIG_H_
+#define NET_SSL_SSL_CONFIG_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+// Various TLS/SSL ProtocolVersion values encoded as uint16
+// struct {
+// uint8 major;
+// uint8 minor;
+// } ProtocolVersion;
+// The most significant byte is |major|, and the least significant byte
+// is |minor|.
+enum {
+ SSL_PROTOCOL_VERSION_SSL3 = 0x0300,
+ SSL_PROTOCOL_VERSION_TLS1 = 0x0301,
+ SSL_PROTOCOL_VERSION_TLS1_1 = 0x0302,
+ SSL_PROTOCOL_VERSION_TLS1_2 = 0x0303,
+};
+
+// Default minimum protocol version.
+NET_EXPORT extern const uint16 kDefaultSSLVersionMin;
+
+// Default maximum protocol version.
+NET_EXPORT extern const uint16 kDefaultSSLVersionMax;
+
+// A collection of SSL-related configuration settings.
+struct NET_EXPORT SSLConfig {
+ // Default to revocation checking.
+ // Default to SSL 3.0 ~ default_version_max() on.
+ SSLConfig();
+ ~SSLConfig();
+
+ // Returns true if |cert| is one of the certs in |allowed_bad_certs|.
+ // The expected cert status is written to |cert_status|. |*cert_status| can
+ // be NULL if user doesn't care about the cert status.
+ bool IsAllowedBadCert(X509Certificate* cert, CertStatus* cert_status) const;
+
+ // Same as above except works with DER encoded certificates instead
+ // of X509Certificate.
+ bool IsAllowedBadCert(const base::StringPiece& der_cert,
+ CertStatus* cert_status) const;
+
+ // rev_checking_enabled is true if online certificate revocation checking is
+ // enabled (i.e. OCSP and CRL fetching).
+ //
+ // Regardless of this flag, CRLSet checking is always enabled and locally
+ // cached revocation information will be considered.
+ bool rev_checking_enabled;
+
+ // rev_checking_required_local_anchors is true if revocation checking is
+ // required to succeed when certificates chain to local trust anchors (that
+ // is, non-public CAs). If revocation information cannot be obtained, such
+ // certificates will be treated as revoked ("hard-fail").
+ // Note: This is distinct from rev_checking_enabled. If true, it is
+ // equivalent to also setting rev_checking_enabled, but only when the
+ // certificate chain chains to a local (non-public) trust anchor.
+ bool rev_checking_required_local_anchors;
+
+ // The minimum and maximum protocol versions that are enabled.
+ // SSL 3.0 is 0x0300, TLS 1.0 is 0x0301, TLS 1.1 is 0x0302, and so on.
+ // (Use the SSL_PROTOCOL_VERSION_xxx enumerators defined above.)
+ // SSL 2.0 is not supported. If version_max < version_min, it means no
+ // protocol versions are enabled.
+ uint16 version_min;
+ uint16 version_max;
+
+ // Presorted list of cipher suites which should be explicitly prevented from
+ // being used in addition to those disabled by the net built-in policy.
+ //
+ // By default, all cipher suites supported by the underlying SSL
+ // implementation will be enabled except for:
+ // - Null encryption cipher suites.
+ // - Weak cipher suites: < 80 bits of security strength.
+ // - FORTEZZA cipher suites (obsolete).
+ // - IDEA cipher suites (RFC 5469 explains why).
+ // - Anonymous cipher suites.
+ // - ECDSA cipher suites on platforms that do not support ECDSA signed
+ // certificates, as servers may use the presence of such ciphersuites as a
+ // hint to send an ECDSA certificate.
+ // The ciphers listed in |disabled_cipher_suites| will be removed in addition
+ // to the above list.
+ //
+ // Though cipher suites are sent in TLS as "uint8 CipherSuite[2]", in
+ // big-endian form, they should be declared in host byte order, with the
+ // first uint8 occupying the most significant byte.
+ // Ex: To disable TLS_RSA_WITH_RC4_128_MD5, specify 0x0004, while to
+ // disable TLS_ECDH_ECDSA_WITH_RC4_128_SHA, specify 0xC002.
+ std::vector<uint16> disabled_cipher_suites;
+
+ bool channel_id_enabled; // True if TLS channel ID extension is enabled.
+ bool false_start_enabled; // True if we'll use TLS False Start.
+ // True if the Certificate Transparency signed_certificate_timestamp
+ // TLS extension is enabled.
+ bool signed_cert_timestamps_enabled;
+
+ // require_forward_secrecy, if true, causes only (EC)DHE cipher suites to be
+ // enabled. NOTE: this only applies to server sockets currently, although
+ // that could be extended if needed.
+ bool require_forward_secrecy;
+
+ // TODO(wtc): move the following members to a new SSLParams structure. They
+ // are not SSL configuration settings.
+
+ struct NET_EXPORT CertAndStatus {
+ CertAndStatus();
+ ~CertAndStatus();
+
+ std::string der_cert;
+ CertStatus cert_status;
+ };
+
+ // Add any known-bad SSL certificate (with its cert status) to
+ // |allowed_bad_certs| that should not trigger an ERR_CERT_* error when
+ // calling SSLClientSocket::Connect. This would normally be done in
+ // response to the user explicitly accepting the bad certificate.
+ std::vector<CertAndStatus> allowed_bad_certs;
+
+ // True if we should send client_cert to the server.
+ bool send_client_cert;
+
+ bool verify_ev_cert; // True if we should verify the certificate for EV.
+
+ bool version_fallback; // True if we are falling back to an older protocol
+ // version (one still needs to decrement
+ // version_max).
+
+ // If cert_io_enabled is false, then certificate verification will not
+ // result in additional HTTP requests. (For example: to fetch missing
+ // intermediates or to perform OCSP/CRL fetches.) It also implies that online
+ // revocation checking is disabled.
+ // NOTE: Only used by NSS.
+ bool cert_io_enabled;
+
+ // The list of application level protocols supported. If set, this will
+ // enable Next Protocol Negotiation (if supported). The order of the
+ // protocols doesn't matter expect for one case: if the server supports Next
+ // Protocol Negotiation, but there is no overlap between the server's and
+ // client's protocol sets, then the first protocol in this list will be
+ // requested by the client.
+ std::vector<std::string> next_protos;
+
+ scoped_refptr<X509Certificate> client_cert;
+};
+
+} // namespace net
+
+#endif // NET_SSL_SSL_CONFIG_H_
diff --git a/chromium/net/ssl/ssl_config_service.cc b/chromium/net/ssl/ssl_config_service.cc
index a28c46d97d7..cd2a00dc6da 100644
--- a/chromium/net/ssl/ssl_config_service.cc
+++ b/chromium/net/ssl/ssl_config_service.cc
@@ -5,82 +5,15 @@
#include "net/ssl/ssl_config_service.h"
#include "base/lazy_instance.h"
-#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
-#include "net/cert/crl_set.h"
#include "net/ssl/ssl_config_service_defaults.h"
-#if defined(USE_OPENSSL)
-#include <openssl/ssl.h>
-#endif
-
namespace net {
-static uint16 g_default_version_min = SSL_PROTOCOL_VERSION_SSL3;
-
-static uint16 g_default_version_max =
-#if defined(USE_OPENSSL)
-#if defined(SSL_OP_NO_TLSv1_2)
- SSL_PROTOCOL_VERSION_TLS1_2;
-#elif defined(SSL_OP_NO_TLSv1_1)
- SSL_PROTOCOL_VERSION_TLS1_1;
-#else
- SSL_PROTOCOL_VERSION_TLS1;
-#endif
-#else
- SSL_PROTOCOL_VERSION_TLS1_2;
-#endif
-
-SSLConfig::CertAndStatus::CertAndStatus() : cert_status(0) {}
-
-SSLConfig::CertAndStatus::~CertAndStatus() {}
-
-SSLConfig::SSLConfig()
- : rev_checking_enabled(false),
- rev_checking_required_local_anchors(false),
- version_min(g_default_version_min),
- version_max(g_default_version_max),
- cached_info_enabled(false),
- channel_id_enabled(true),
- false_start_enabled(true),
- signed_cert_timestamps_enabled(true),
- require_forward_secrecy(false),
- unrestricted_ssl3_fallback_enabled(false),
- send_client_cert(false),
- verify_ev_cert(false),
- version_fallback(false),
- cert_io_enabled(true) {
-}
-
-SSLConfig::~SSLConfig() {
-}
-
-bool SSLConfig::IsAllowedBadCert(X509Certificate* cert,
- CertStatus* cert_status) const {
- std::string der_cert;
- if (!X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_cert))
- return false;
- return IsAllowedBadCert(der_cert, cert_status);
-}
-
-bool SSLConfig::IsAllowedBadCert(const base::StringPiece& der_cert,
- CertStatus* cert_status) const {
- for (size_t i = 0; i < allowed_bad_certs.size(); ++i) {
- if (der_cert == allowed_bad_certs[i].der_cert) {
- if (cert_status)
- *cert_status = allowed_bad_certs[i].cert_status;
- return true;
- }
- }
- return false;
-}
-
SSLConfigService::SSLConfigService()
: observer_list_(ObserverList<Observer>::NOTIFY_EXISTING_ONLY) {
}
-static bool g_cached_info_enabled = false;
-
// GlobalCRLSet holds a reference to the global CRLSet. It simply wraps a lock
// around a scoped_refptr so that getting a reference doesn't race with
// updating the CRLSet.
@@ -114,25 +47,6 @@ scoped_refptr<CRLSet> SSLConfigService::GetCRLSet() {
return g_crl_set.Get().Get();
}
-void SSLConfigService::EnableCachedInfo() {
- g_cached_info_enabled = true;
-}
-
-// static
-bool SSLConfigService::cached_info_enabled() {
- return g_cached_info_enabled;
-}
-
-// static
-uint16 SSLConfigService::default_version_min() {
- return g_default_version_min;
-}
-
-// static
-uint16 SSLConfigService::default_version_max() {
- return g_default_version_max;
-}
-
void SSLConfigService::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
@@ -148,11 +62,6 @@ void SSLConfigService::NotifySSLConfigChange() {
SSLConfigService::~SSLConfigService() {
}
-// static
-void SSLConfigService::SetSSLConfigFlags(SSLConfig* ssl_config) {
- ssl_config->cached_info_enabled = g_cached_info_enabled;
-}
-
void SSLConfigService::ProcessConfigUpdate(const SSLConfig& orig_config,
const SSLConfig& new_config) {
bool config_changed =
@@ -166,9 +75,7 @@ void SSLConfigService::ProcessConfigUpdate(const SSLConfig& orig_config,
(orig_config.channel_id_enabled != new_config.channel_id_enabled) ||
(orig_config.false_start_enabled != new_config.false_start_enabled) ||
(orig_config.require_forward_secrecy !=
- new_config.require_forward_secrecy) ||
- (orig_config.unrestricted_ssl3_fallback_enabled !=
- new_config.unrestricted_ssl3_fallback_enabled);
+ new_config.require_forward_secrecy);
if (config_changed)
NotifySSLConfigChange();
diff --git a/chromium/net/ssl/ssl_config_service.h b/chromium/net/ssl/ssl_config_service.h
index 0b19e301303..d6547223eab 100644
--- a/chromium/net/ssl/ssl_config_service.h
+++ b/chromium/net/ssl/ssl_config_service.h
@@ -7,158 +7,14 @@
#include <vector>
-#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
-#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
-#include "net/cert/cert_status_flags.h"
#include "net/cert/crl_set.h"
-#include "net/cert/x509_certificate.h"
+#include "net/ssl/ssl_config.h"
namespace net {
-// Various TLS/SSL ProtocolVersion values encoded as uint16
-// struct {
-// uint8 major;
-// uint8 minor;
-// } ProtocolVersion;
-// The most significant byte is |major|, and the least significant byte
-// is |minor|.
-enum {
- SSL_PROTOCOL_VERSION_SSL3 = 0x0300,
- SSL_PROTOCOL_VERSION_TLS1 = 0x0301,
- SSL_PROTOCOL_VERSION_TLS1_1 = 0x0302,
- SSL_PROTOCOL_VERSION_TLS1_2 = 0x0303,
-};
-
-// A collection of SSL-related configuration settings.
-struct NET_EXPORT SSLConfig {
- // Default to revocation checking.
- // Default to SSL 3.0 ~ default_version_max() on.
- SSLConfig();
- ~SSLConfig();
-
- // Returns true if |cert| is one of the certs in |allowed_bad_certs|.
- // The expected cert status is written to |cert_status|. |*cert_status| can
- // be NULL if user doesn't care about the cert status.
- bool IsAllowedBadCert(X509Certificate* cert, CertStatus* cert_status) const;
-
- // Same as above except works with DER encoded certificates instead
- // of X509Certificate.
- bool IsAllowedBadCert(const base::StringPiece& der_cert,
- CertStatus* cert_status) const;
-
- // rev_checking_enabled is true if online certificate revocation checking is
- // enabled (i.e. OCSP and CRL fetching).
- //
- // Regardless of this flag, CRLSet checking is always enabled and locally
- // cached revocation information will be considered.
- bool rev_checking_enabled;
-
- // rev_checking_required_local_anchors is true if revocation checking is
- // required to succeed when certificates chain to local trust anchors (that
- // is, non-public CAs). If revocation information cannot be obtained, such
- // certificates will be treated as revoked ("hard-fail").
- // Note: This is distinct from rev_checking_enabled. If true, it is
- // equivalent to also setting rev_checking_enabled, but only when the
- // certificate chain chains to a local (non-public) trust anchor.
- bool rev_checking_required_local_anchors;
-
- // The minimum and maximum protocol versions that are enabled.
- // SSL 3.0 is 0x0300, TLS 1.0 is 0x0301, TLS 1.1 is 0x0302, and so on.
- // (Use the SSL_PROTOCOL_VERSION_xxx enumerators defined above.)
- // SSL 2.0 is not supported. If version_max < version_min, it means no
- // protocol versions are enabled.
- uint16 version_min;
- uint16 version_max;
-
- // Presorted list of cipher suites which should be explicitly prevented from
- // being used in addition to those disabled by the net built-in policy.
- //
- // By default, all cipher suites supported by the underlying SSL
- // implementation will be enabled except for:
- // - Null encryption cipher suites.
- // - Weak cipher suites: < 80 bits of security strength.
- // - FORTEZZA cipher suites (obsolete).
- // - IDEA cipher suites (RFC 5469 explains why).
- // - Anonymous cipher suites.
- // - ECDSA cipher suites on platforms that do not support ECDSA signed
- // certificates, as servers may use the presence of such ciphersuites as a
- // hint to send an ECDSA certificate.
- // The ciphers listed in |disabled_cipher_suites| will be removed in addition
- // to the above list.
- //
- // Though cipher suites are sent in TLS as "uint8 CipherSuite[2]", in
- // big-endian form, they should be declared in host byte order, with the
- // first uint8 occupying the most significant byte.
- // Ex: To disable TLS_RSA_WITH_RC4_128_MD5, specify 0x0004, while to
- // disable TLS_ECDH_ECDSA_WITH_RC4_128_SHA, specify 0xC002.
- std::vector<uint16> disabled_cipher_suites;
-
- bool cached_info_enabled; // True if TLS cached info extension is enabled.
- bool channel_id_enabled; // True if TLS channel ID extension is enabled.
- bool false_start_enabled; // True if we'll use TLS False Start.
- // True if the Certificate Transparency signed_certificate_timestamp
- // TLS extension is enabled.
- bool signed_cert_timestamps_enabled;
-
- // require_forward_secrecy, if true, causes only (EC)DHE cipher suites to be
- // enabled. NOTE: this only applies to server sockets currently, although
- // that could be extended if needed.
- bool require_forward_secrecy;
-
- // If |unrestricted_ssl3_fallback_enabled| is true, SSL 3.0 fallback will be
- // enabled for all sites.
- // If |unrestricted_ssl3_fallback_enabled| is false, SSL 3.0 fallback will be
- // disabled for a site pinned to the Google pin list (indicating that it is a
- // Google site).
- bool unrestricted_ssl3_fallback_enabled;
-
- // TODO(wtc): move the following members to a new SSLParams structure. They
- // are not SSL configuration settings.
-
- struct NET_EXPORT CertAndStatus {
- CertAndStatus();
- ~CertAndStatus();
-
- std::string der_cert;
- CertStatus cert_status;
- };
-
- // Add any known-bad SSL certificate (with its cert status) to
- // |allowed_bad_certs| that should not trigger an ERR_CERT_* error when
- // calling SSLClientSocket::Connect. This would normally be done in
- // response to the user explicitly accepting the bad certificate.
- std::vector<CertAndStatus> allowed_bad_certs;
-
- // True if we should send client_cert to the server.
- bool send_client_cert;
-
- bool verify_ev_cert; // True if we should verify the certificate for EV.
-
- bool version_fallback; // True if we are falling back to an older protocol
- // version (one still needs to decrement
- // version_max).
-
- // If cert_io_enabled is false, then certificate verification will not
- // result in additional HTTP requests. (For example: to fetch missing
- // intermediates or to perform OCSP/CRL fetches.) It also implies that online
- // revocation checking is disabled.
- // NOTE: Only used by NSS.
- bool cert_io_enabled;
-
- // The list of application level protocols supported. If set, this will
- // enable Next Protocol Negotiation (if supported). The order of the
- // protocols doesn't matter expect for one case: if the server supports Next
- // Protocol Negotiation, but there is no overlap between the server's and
- // client's protocol sets, then the first protocol in this list will be
- // requested by the client.
- std::vector<std::string> next_protos;
-
- scoped_refptr<X509Certificate> client_cert;
-};
-
// The interface for retrieving the SSL configuration. This interface
// does not cover setting the SSL configuration, as on some systems, the
// SSLConfigService objects may not have direct access to the configuration, or
@@ -194,17 +50,6 @@ class NET_EXPORT SSLConfigService
static void SetCRLSet(scoped_refptr<CRLSet> crl_set);
static scoped_refptr<CRLSet> GetCRLSet();
- // Enables the TLS cached info extension, which allows the server to send
- // just a digest of its certificate chain.
- static void EnableCachedInfo();
- static bool cached_info_enabled();
-
- // Gets the default minimum protocol version.
- static uint16 default_version_min();
-
- // Gets the default maximum protocol version.
- static uint16 default_version_max();
-
// Is SNI available in this configuration?
static bool IsSNIAvailable(SSLConfigService* service);
@@ -223,9 +68,6 @@ class NET_EXPORT SSLConfigService
virtual ~SSLConfigService();
- // SetFlags sets the values of several flags based on global configuration.
- static void SetSSLConfigFlags(SSLConfig* ssl_config);
-
// Process before/after config update.
void ProcessConfigUpdate(const SSLConfig& orig_config,
const SSLConfig& new_config);
diff --git a/chromium/net/ssl/ssl_config_service_defaults.cc b/chromium/net/ssl/ssl_config_service_defaults.cc
index 1d96977baa1..c512ed8df52 100644
--- a/chromium/net/ssl/ssl_config_service_defaults.cc
+++ b/chromium/net/ssl/ssl_config_service_defaults.cc
@@ -11,7 +11,6 @@ SSLConfigServiceDefaults::SSLConfigServiceDefaults() {
void SSLConfigServiceDefaults::GetSSLConfig(SSLConfig* config) {
*config = default_config_;
- SetSSLConfigFlags(config);
}
SSLConfigServiceDefaults::~SSLConfigServiceDefaults() {
diff --git a/chromium/net/ssl/ssl_config_service_unittest.cc b/chromium/net/ssl/ssl_config_service_unittest.cc
index 42c8ae47b9f..e8a4c33394a 100644
--- a/chromium/net/ssl/ssl_config_service_unittest.cc
+++ b/chromium/net/ssl/ssl_config_service_unittest.cc
@@ -69,7 +69,6 @@ TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) {
SSLConfig initial_config;
initial_config.rev_checking_enabled = true;
initial_config.false_start_enabled = false;
- initial_config.unrestricted_ssl3_fallback_enabled = false;
initial_config.version_min = SSL_PROTOCOL_VERSION_SSL3;
initial_config.version_max = SSL_PROTOCOL_VERSION_TLS1_1;
@@ -87,10 +86,6 @@ TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) {
EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1);
mock_service->SetSSLConfig(initial_config);
- initial_config.unrestricted_ssl3_fallback_enabled = true;
- EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1);
- mock_service->SetSSLConfig(initial_config);
-
// Test that changing the SSL version range triggers updates.
initial_config.version_min = SSL_PROTOCOL_VERSION_TLS1;
EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1);
diff --git a/chromium/net/ssl/ssl_connection_status_flags.h b/chromium/net/ssl/ssl_connection_status_flags.h
index 08d585f7ebc..faae306a505 100644
--- a/chromium/net/ssl/ssl_connection_status_flags.h
+++ b/chromium/net/ssl/ssl_connection_status_flags.h
@@ -5,6 +5,9 @@
#ifndef NET_SSL_SSL_CONNECTION_STATUS_FLAGS_H_
#define NET_SSL_SSL_CONNECTION_STATUS_FLAGS_H_
+#include "base/logging.h"
+#include "base/macros.h"
+
namespace net {
// Status flags for SSLInfo::connection_status.
@@ -43,6 +46,8 @@ enum {
SSL_CONNECTION_VERSION_TLS1 = 3,
SSL_CONNECTION_VERSION_TLS1_1 = 4,
SSL_CONNECTION_VERSION_TLS1_2 = 5,
+ // Reserve 6 for TLS 1.3.
+ SSL_CONNECTION_VERSION_QUIC = 7,
SSL_CONNECTION_VERSION_MAX,
};
COMPILE_ASSERT(SSL_CONNECTION_VERSION_MAX - 1 <= SSL_CONNECTION_VERSION_MASK,
@@ -58,6 +63,28 @@ inline int SSLConnectionStatusToVersion(int connection_status) {
SSL_CONNECTION_VERSION_MASK;
}
+inline void SSLConnectionStatusSetCipherSuite(int cipher_suite,
+ int* connection_status) {
+ // Clear out the old ciphersuite.
+ *connection_status &=
+ ~(SSL_CONNECTION_CIPHERSUITE_MASK << SSL_CONNECTION_CIPHERSUITE_SHIFT);
+ // Set the new ciphersuite.
+ *connection_status |= ((cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK)
+ << SSL_CONNECTION_CIPHERSUITE_SHIFT);
+}
+
+inline void SSLConnectionStatusSetVersion(int version, int* connection_status) {
+ DCHECK_GT(version, 0);
+ DCHECK_LT(version, SSL_CONNECTION_VERSION_MAX);
+
+ // Clear out the old version.
+ *connection_status &=
+ ~(SSL_CONNECTION_VERSION_MASK << SSL_CONNECTION_VERSION_SHIFT);
+ // Set the new version.
+ *connection_status |=
+ ((version & SSL_CONNECTION_VERSION_MASK) << SSL_CONNECTION_VERSION_SHIFT);
+}
+
} // namespace net
#endif // NET_SSL_SSL_CONNECTION_STATUS_FLAGS_H_
diff --git a/chromium/net/ssl/ssl_connection_status_flags_unittest.cc b/chromium/net/ssl/ssl_connection_status_flags_unittest.cc
new file mode 100644
index 00000000000..64fea1313a3
--- /dev/null
+++ b/chromium/net/ssl/ssl_connection_status_flags_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2014 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/ssl/ssl_connection_status_flags.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(SSLConnectionStatusTest, SetCipherSuite) {
+ int connection_status = 0xDEADBEEF;
+ int expected_version = SSLConnectionStatusToVersion(connection_status);
+
+ SSLConnectionStatusSetCipherSuite(12345, &connection_status);
+ EXPECT_EQ(12345, SSLConnectionStatusToCipherSuite(connection_status));
+ EXPECT_EQ(expected_version, SSLConnectionStatusToVersion(connection_status));
+}
+
+TEST(SSLConnectionStatusTest, SetVersion) {
+ int connection_status = 0xDEADBEEF;
+ int expected_cipher_suite =
+ SSLConnectionStatusToCipherSuite(connection_status);
+
+ SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_TLS1_2,
+ &connection_status);
+ EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_2,
+ SSLConnectionStatusToVersion(connection_status));
+ EXPECT_EQ(expected_cipher_suite,
+ SSLConnectionStatusToCipherSuite(connection_status));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/ssl/ssl_info.cc b/chromium/net/ssl/ssl_info.cc
index 77ddb62e774..d2973d238d6 100644
--- a/chromium/net/ssl/ssl_info.cc
+++ b/chromium/net/ssl/ssl_info.cc
@@ -33,6 +33,7 @@ SSLInfo& SSLInfo::operator=(const SSLInfo& info) {
handshake_type = info.handshake_type;
public_key_hashes = info.public_key_hashes;
signed_certificate_timestamps = info.signed_certificate_timestamps;
+ pinning_failure_log = info.pinning_failure_log;
return *this;
}
@@ -48,6 +49,7 @@ void SSLInfo::Reset() {
handshake_type = HANDSHAKE_UNKNOWN;
public_key_hashes.clear();
signed_certificate_timestamps.clear();
+ pinning_failure_log.clear();
}
void SSLInfo::SetCertError(int error) {
diff --git a/chromium/net/ssl/ssl_info.h b/chromium/net/ssl/ssl_info.h
index 00a8815c79c..154c4a0e901 100644
--- a/chromium/net/ssl/ssl_info.h
+++ b/chromium/net/ssl/ssl_info.h
@@ -80,6 +80,11 @@ class NET_EXPORT SSLInfo {
// each certificate in the chain.
HashValueVector public_key_hashes;
+ // pinning_failure_log contains a message produced by
+ // TransportSecurityState::DomainState::CheckPublicKeyPins in the event of a
+ // pinning failure. It is a (somewhat) human-readable string.
+ std::string pinning_failure_log;
+
// List of SignedCertificateTimestamps and their corresponding validation
// status.
SignedCertificateTimestampAndStatusList signed_certificate_timestamps;
diff --git a/chromium/net/test/OWNERS b/chromium/net/test/OWNERS
index 5b08e349893..66d66900859 100644
--- a/chromium/net/test/OWNERS
+++ b/chromium/net/test/OWNERS
@@ -1,5 +1,2 @@
# General reviewer, except sync-specific bits.
phajdan.jr@chromium.org
-
-# For changes to local_sync_test_server.{h|cc}.
-akalin@chromium.org
diff --git a/chromium/net/test/android/OWNERS b/chromium/net/test/android/OWNERS
index 4bcb60f934c..fa129e131b8 100644
--- a/chromium/net/test/android/OWNERS
+++ b/chromium/net/test/android/OWNERS
@@ -1,3 +1,2 @@
digit@chromium.org
-pliard@chromium.org
yfriedman@chromium.org
diff --git a/chromium/net/test/ct_test_util.cc b/chromium/net/test/ct_test_util.cc
index 4e95e190dae..826f03fe755 100644
--- a/chromium/net/test/ct_test_util.cc
+++ b/chromium/net/test/ct_test_util.cc
@@ -12,6 +12,7 @@
#include "base/strings/string_util.h"
#include "net/cert/ct_serialization.h"
#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
#include "net/cert/x509_certificate.h"
namespace net {
@@ -154,6 +155,14 @@ const char kFakeOCSPResponseIssuerCert[] =
const char kFakeOCSPExtensionValue[] = "74657374"; // "test"
+// For the sample STH
+const char kSampleSTHSHA256RootHash[] =
+ "726467216167397babca293dca398e4ce6b621b18b9bc42f30c900d1f92ac1e4";
+const char kSampleSTHTreeHeadSignature[] =
+ "0403004730450220365a91a2a88f2b9332f41d8959fa7086da7e6d634b7b089bc9da066426"
+ "6c7a20022100e38464f3c0fd066257b982074f7ac87655e0c8f714768a050b4be9a7b441cb"
+ "d3";
+
} // namespace
void GetX509CertLogEntry(LogEntry* entry) {
@@ -240,6 +249,24 @@ std::string GetDerEncodedFakeOCSPResponseIssuerCert() {
return HexToBytes(kFakeOCSPResponseIssuerCert);
}
+std::string GetSampleSTHSHA256RootHash() {
+ return HexToBytes(kSampleSTHSHA256RootHash);
+}
+
+// A sample, valid STH
+void GetSignedTreeHead(SignedTreeHead* sth) {
+ sth->version = SignedTreeHead::V1;
+ sth->timestamp = base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(1396877277237);
+ sth->tree_size = 21u;
+ std::string sha256_root_hash = GetSampleSTHSHA256RootHash();
+ memcpy(sth->sha256_root_hash, sha256_root_hash.c_str(), kSthRootHashLength);
+
+ std::string tree_head_signature = HexToBytes(kSampleSTHTreeHeadSignature);
+ base::StringPiece sp(tree_head_signature);
+ DecodeDigitallySigned(&sp, &(sth->signature));
+}
+
} // namespace ct
} // namespace net
diff --git a/chromium/net/test/ct_test_util.h b/chromium/net/test/ct_test_util.h
index 87afc104102..f3b8c6b3f69 100644
--- a/chromium/net/test/ct_test_util.h
+++ b/chromium/net/test/ct_test_util.h
@@ -15,6 +15,7 @@ namespace ct {
struct LogEntry;
struct SignedCertificateTimestamp;
+struct SignedTreeHead;
// Note: unless specified otherwise, all test data is taken from Certificate
// Transparency test data repository.
@@ -62,6 +63,12 @@ std::string GetDerEncodedFakeOCSPResponseCert();
// The issuer of the previous cert.
std::string GetDerEncodedFakeOCSPResponseIssuerCert();
+// A sample, valid STH
+void GetSignedTreeHead(SignedTreeHead* sth);
+
+// The SHA256 root hash for the sample STH
+std::string GetSampleSTHSHA256RootHash();
+
} // namespace ct
} // namespace net
diff --git a/chromium/net/test/embedded_test_server/embedded_test_server.cc b/chromium/net/test/embedded_test_server/embedded_test_server.cc
index b60cea44632..75b46e22166 100644
--- a/chromium/net/test/embedded_test_server/embedded_test_server.cc
+++ b/chromium/net/test/embedded_test_server/embedded_test_server.cc
@@ -20,7 +20,6 @@
#include "net/test/embedded_test_server/http_connection.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
-#include "net/tools/fetch/http_listen_socket.h"
namespace net {
namespace test_server {
diff --git a/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc b/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc
index a9c2efba474..9823947fb5d 100644
--- a/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc
+++ b/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc
@@ -270,7 +270,7 @@ class EmbeddedTestServerThreadingTestDelegate
scoped_ptr<base::MessageLoop> loop;
if (message_loop_present_on_initialize_)
- loop.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ loop.reset(new base::MessageLoopForIO);
// Create the test server instance.
EmbeddedTestServer server;
@@ -280,7 +280,7 @@ class EmbeddedTestServerThreadingTestDelegate
// Make a request and wait for the reply.
if (!loop)
- loop.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ loop.reset(new base::MessageLoopForIO);
scoped_ptr<URLFetcher> fetcher(URLFetcher::Create(
server.GetURL("/test?q=foo"), URLFetcher::GET, this));
diff --git a/chromium/net/test/gtest_util.h b/chromium/net/test/gtest_util.h
new file mode 100644
index 00000000000..14492c2a780
--- /dev/null
+++ b/chromium/net/test/gtest_util.h
@@ -0,0 +1,99 @@
+// Copyright 2014 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.
+//
+// Testing utilities that extend gtest.
+
+#ifndef NET_QUIC_TEST_TOOLS_GTEST_UTIL_H_
+#define NET_QUIC_TEST_TOOLS_GTEST_UTIL_H_
+
+#include "net/test/scoped_disable_exit_on_dfatal.h"
+#include "net/test/scoped_mock_log.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Internal implementation for the EXPECT_DFATAL and ASSERT_DFATAL
+// macros. Do not use this directly.
+#define GTEST_DFATAL_(statement, matcher, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (true) { \
+ ::net::test::ScopedMockLog gtest_log; \
+ ::net::test::ScopedDisableExitOnDFatal gtest_disable_exit; \
+ using ::testing::_; \
+ EXPECT_CALL(gtest_log, Log(_, _, _, _, _)) \
+ .WillRepeatedly(::testing::Return(false)); \
+ EXPECT_CALL(gtest_log, Log(logging::LOG_DFATAL, _, _, _, matcher)) \
+ .Times(::testing::AtLeast(1)) \
+ .WillOnce(::testing::Return(false)); \
+ gtest_log.StartCapturingLogs(); \
+ { statement; } \
+ gtest_log.StopCapturingLogs(); \
+ if (!testing::Mock::VerifyAndClear(&gtest_log)) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_dfatal_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_dfatal_, __LINE__): \
+ fail("")
+
+// The EXPECT_DFATAL and ASSERT_DFATAL macros are lightweight
+// alternatives to EXPECT_DEBUG_DEATH and ASSERT_DEBUG_DEATH. They
+// are appropriate for testing that your code logs a message at the
+// DFATAL level.
+//
+// Unlike EXPECT_DEBUG_DEATH and ASSERT_DEBUG_DEATH, these macros
+// execute the given statement in the current process, not a forked
+// one. This works because we disable exiting the program for
+// LOG(DFATAL). This makes the tests run more quickly.
+//
+// The _WITH() variants allow one to specify any matcher for the
+// DFATAL log message, whereas the other variants assume a regex.
+
+#define EXPECT_DFATAL_WITH(statement, matcher) \
+ GTEST_DFATAL_(statement, matcher, GTEST_NONFATAL_FAILURE_)
+
+#define ASSERT_DFATAL_WITH(statement, matcher) \
+ GTEST_DFATAL_(statement, matcher, GTEST_FATAL_FAILURE_)
+
+#define EXPECT_DFATAL(statement, regex) \
+ EXPECT_DFATAL_WITH(statement, ::testing::ContainsRegex(regex))
+
+#define ASSERT_DFATAL(statement, regex) \
+ ASSERT_DFATAL_WITH(statement, ::testing::ContainsRegex(regex))
+
+// The EXPECT_DEBUG_DFATAL and ASSERT_DEBUG_DFATAL macros are similar to
+// EXPECT_DFATAL and ASSERT_DFATAL. Use them in conjunction with DLOG(DFATAL)
+// or similar macros that produce no-op in opt build and DFATAL in dbg build.
+
+#ifndef NDEBUG
+
+#define EXPECT_DEBUG_DFATAL(statement, regex) \
+ EXPECT_DFATAL(statement, regex)
+#define ASSERT_DEBUG_DFATAL(statement, regex) \
+ ASSERT_DFATAL(statement, regex)
+
+#else // NDEBUG
+
+#define EXPECT_DEBUG_DFATAL(statement, regex) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (true) { \
+ (void)(regex); \
+ statement; \
+ } else \
+ GTEST_NONFATAL_FAILURE_("")
+#define ASSERT_DEBUG_DFATAL(statement, regex) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (true) { \
+ (void)(regex); \
+ statement; \
+ } else \
+ GTEST_NONFATAL_FAILURE_("")
+
+#endif // NDEBUG
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_GTEST_UTIL_H_
diff --git a/chromium/net/test/net_test_suite.cc b/chromium/net/test/net_test_suite.cc
index 175cec29754..2d13877e803 100644
--- a/chromium/net/test/net_test_suite.cc
+++ b/chromium/net/test/net_test_suite.cc
@@ -1,67 +1,67 @@
-// 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/test/net_test_suite.h"
-
-#include "base/message_loop/message_loop.h"
-#include "net/base/network_change_notifier.h"
-#include "net/http/http_stream_factory.h"
-#include "net/spdy/spdy_session.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(USE_NSS) || defined(OS_IOS)
-#include "net/ocsp/nss_ocsp.h"
-#endif
-
-class StaticReset : public ::testing::EmptyTestEventListener {
- virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE {
- net::HttpStreamFactory::ResetStaticSettingsToInit();
- }
-};
-
-NetTestSuite::NetTestSuite(int argc, char** argv)
- : TestSuite(argc, argv) {
-}
-
-NetTestSuite::NetTestSuite(int argc, char** argv,
- bool create_at_exit_manager)
- : TestSuite(argc, argv, create_at_exit_manager) {
-}
-
-NetTestSuite::~NetTestSuite() {}
-
-void NetTestSuite::Initialize() {
- TestSuite::Initialize();
- ::testing::UnitTest::GetInstance()->listeners().Append(new StaticReset());
- InitializeTestThread();
-}
-
-void NetTestSuite::Shutdown() {
-#if defined(USE_NSS) || defined(OS_IOS)
- net::ShutdownNSSHttpIO();
-#endif
-
- // We want to destroy this here before the TestSuite continues to tear down
- // the environment.
- message_loop_.reset();
-
- TestSuite::Shutdown();
-}
-
-void NetTestSuite::InitializeTestThread() {
- network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
-
- InitializeTestThreadNoNetworkChangeNotifier();
-}
-
-void NetTestSuite::InitializeTestThreadNoNetworkChangeNotifier() {
- host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
- scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
- // In case any attempts are made to resolve host names, force them all to
- // be mapped to localhost. This prevents DNS queries from being sent in
- // the process of running these unit tests.
- host_resolver_proc_->AddRule("*", "127.0.0.1");
-
- message_loop_.reset(new base::MessageLoopForIO());
-}
+// 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/test/net_test_suite.h"
+
+#include "base/message_loop/message_loop.h"
+#include "net/base/network_change_notifier.h"
+#include "net/http/http_stream_factory.h"
+#include "net/spdy/spdy_session.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include "net/ocsp/nss_ocsp.h"
+#endif
+
+class StaticReset : public ::testing::EmptyTestEventListener {
+ virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE {
+ net::HttpStreamFactory::ResetStaticSettingsToInit();
+ }
+};
+
+NetTestSuite::NetTestSuite(int argc, char** argv)
+ : TestSuite(argc, argv) {
+}
+
+NetTestSuite::NetTestSuite(int argc, char** argv,
+ bool create_at_exit_manager)
+ : TestSuite(argc, argv, create_at_exit_manager) {
+}
+
+NetTestSuite::~NetTestSuite() {}
+
+void NetTestSuite::Initialize() {
+ TestSuite::Initialize();
+ ::testing::UnitTest::GetInstance()->listeners().Append(new StaticReset());
+ InitializeTestThread();
+}
+
+void NetTestSuite::Shutdown() {
+#if defined(USE_NSS) || defined(OS_IOS)
+ net::ShutdownNSSHttpIO();
+#endif
+
+ // We want to destroy this here before the TestSuite continues to tear down
+ // the environment.
+ message_loop_.reset();
+
+ TestSuite::Shutdown();
+}
+
+void NetTestSuite::InitializeTestThread() {
+ network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+
+ InitializeTestThreadNoNetworkChangeNotifier();
+}
+
+void NetTestSuite::InitializeTestThreadNoNetworkChangeNotifier() {
+ host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
+ scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
+ // In case any attempts are made to resolve host names, force them all to
+ // be mapped to localhost. This prevents DNS queries from being sent in
+ // the process of running these unit tests.
+ host_resolver_proc_->AddRule("*", "127.0.0.1");
+
+ message_loop_.reset(new base::MessageLoopForIO());
+}
diff --git a/chromium/net/test/python_utils.cc b/chromium/net/test/python_utils.cc
index e249a27f27a..2555c55c264 100644
--- a/chromium/net/test/python_utils.cc
+++ b/chromium/net/test/python_utils.cc
@@ -21,7 +21,7 @@ void AppendToPythonPath(const base::FilePath& dir) {
std::string old_path;
std::string dir_path;
#if defined(OS_WIN)
- dir_path = WideToUTF8(dir.value());
+ dir_path = base::WideToUTF8(dir.value());
#elif defined(OS_POSIX)
dir_path = dir.value();
#endif
@@ -106,7 +106,7 @@ bool GetPyProtoPath(base::FilePath* dir) {
return true;
}
-bool GetPythonCommand(CommandLine* python_cmd) {
+bool GetPythonCommand(base::CommandLine* python_cmd) {
DCHECK(python_cmd);
#if defined(OS_WIN)
diff --git a/chromium/net/test/python_utils.h b/chromium/net/test/python_utils.h
index 30776456503..ca06021b729 100644
--- a/chromium/net/test/python_utils.h
+++ b/chromium/net/test/python_utils.h
@@ -7,9 +7,8 @@
#include "base/compiler_specific.h"
-class CommandLine;
-
namespace base {
+class CommandLine;
class FilePath;
}
@@ -23,6 +22,6 @@ void AppendToPythonPath(const base::FilePath& dir);
bool GetPyProtoPath(base::FilePath* dir);
// Returns the command that should be used to launch Python.
-bool GetPythonCommand(CommandLine* python_cmd) WARN_UNUSED_RESULT;
+bool GetPythonCommand(base::CommandLine* python_cmd) WARN_UNUSED_RESULT;
#endif // NET_TEST_PYTHON_UTILS_H_
diff --git a/chromium/net/test/python_utils_unittest.cc b/chromium/net/test/python_utils_unittest.cc
index 04f11ec266d..450a297b7e6 100644
--- a/chromium/net/test/python_utils_unittest.cc
+++ b/chromium/net/test/python_utils_unittest.cc
@@ -45,7 +45,7 @@ TEST(PythonUtils, Append) {
}
TEST(PythonUtils, PythonRunTime) {
- CommandLine cmd_line(CommandLine::NO_PROGRAM);
+ base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
EXPECT_TRUE(GetPythonCommand(&cmd_line));
// Run a python command to print a string and make sure the output is what
@@ -56,6 +56,6 @@ TEST(PythonUtils, PythonRunTime) {
cmd_line.AppendArg(python_cmd);
std::string output;
EXPECT_TRUE(base::GetAppOutput(cmd_line, &output));
- TrimWhitespace(output, TRIM_TRAILING, &output);
+ base::TrimWhitespace(output, base::TRIM_TRAILING, &output);
EXPECT_EQ(input, output);
}
diff --git a/chromium/net/test/run_all_unittests.cc b/chromium/net/test/run_all_unittests.cc
index 9b5dad2b0f7..96e8a3c8346 100644
--- a/chromium/net/test/run_all_unittests.cc
+++ b/chromium/net/test/run_all_unittests.cc
@@ -13,8 +13,10 @@
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
#include "base/test/test_file_util.h"
#include "net/android/net_jni_registrar.h"
+#include "url/android/url_jni_registrar.h"
#endif
#if !defined(OS_IOS)
@@ -29,10 +31,18 @@ int main(int argc, char** argv) {
base::StatisticsRecorder::Initialize();
#if defined(OS_ANDROID)
+ const base::android::RegistrationMethod kNetTestRegisteredMethods[] = {
+ {"NetAndroid", net::android::RegisterJni},
+ {"TestFileUtil", file_util::RegisterContentUriTestUtils},
+ {"UrlAndroid", url::android::RegisterJni},
+ };
+
// Register JNI bindings for android. Doing it early as the test suite setup
// may initiate a call to Java.
- net::android::RegisterJni(base::android::AttachCurrentThread());
- file_util::RegisterContentUriTestUtils(base::android::AttachCurrentThread());
+ base::android::RegisterNativeMethods(
+ base::android::AttachCurrentThread(),
+ kNetTestRegisteredMethods,
+ arraysize(kNetTestRegisteredMethods));
#endif
NetTestSuite test_suite(argc, argv);
@@ -48,8 +58,7 @@ int main(int argc, char** argv) {
net::EnableSSLServerSockets();
#if !defined(OS_IOS)
- // This has to be done on the main thread.
- net::ProxyResolverV8::RememberDefaultIsolate();
+ net::ProxyResolverV8::EnsureIsolateCreated();
#endif
return base::LaunchUnitTests(
diff --git a/chromium/net/test/scoped_disable_exit_on_dfatal.cc b/chromium/net/test/scoped_disable_exit_on_dfatal.cc
new file mode 100644
index 00000000000..f9091261bc5
--- /dev/null
+++ b/chromium/net/test/scoped_disable_exit_on_dfatal.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 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/test/scoped_disable_exit_on_dfatal.h"
+
+#include "base/logging.h"
+
+namespace net {
+namespace test {
+
+// static
+ScopedDisableExitOnDFatal* ScopedDisableExitOnDFatal::g_instance_ = NULL;
+
+ScopedDisableExitOnDFatal::ScopedDisableExitOnDFatal() {
+ CHECK(!g_instance_);
+ g_instance_ = this;
+ logging::SetLogAssertHandler(LogAssertHandler);
+}
+
+ScopedDisableExitOnDFatal::~ScopedDisableExitOnDFatal() {
+ CHECK_EQ(g_instance_, this);
+ logging::SetLogAssertHandler(NULL);
+ g_instance_ = NULL;
+}
+
+// static
+void ScopedDisableExitOnDFatal::LogAssertHandler(const std::string& str) {
+ // Simply swallow the assert.
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/test/scoped_disable_exit_on_dfatal.h b/chromium/net/test/scoped_disable_exit_on_dfatal.h
new file mode 100644
index 00000000000..5df8ff75b97
--- /dev/null
+++ b/chromium/net/test/scoped_disable_exit_on_dfatal.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_SCOPED_DISABLE_EXIT_ON_DFATAL_H_
+#define NET_QUIC_TEST_TOOLS_SCOPED_DISABLE_EXIT_ON_DFATAL_H_
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+
+// The ScopedDisableExitOnDFatal class is used to disable exiting the
+// program when we encounter a LOG(DFATAL) within the current block.
+// After we leave the current block, the default behavior is
+// restored.
+class ScopedDisableExitOnDFatal {
+ public:
+ ScopedDisableExitOnDFatal();
+ ~ScopedDisableExitOnDFatal();
+
+ private:
+ // Currently active instance.
+ static ScopedDisableExitOnDFatal* g_instance_;
+
+ // Static function which is set as the logging assert handler.
+ // Called when there is a check failure.
+ static void LogAssertHandler(const std::string& msg);
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableExitOnDFatal);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_SCOPED_DISABLE_EXIT_ON_DFATAL_H_
diff --git a/chromium/net/test/scoped_mock_log.cc b/chromium/net/test/scoped_mock_log.cc
new file mode 100644
index 00000000000..3bc99cb3d42
--- /dev/null
+++ b/chromium/net/test/scoped_mock_log.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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/test/scoped_mock_log.h"
+
+#include "base/logging.h"
+
+namespace net {
+namespace test {
+
+// static
+ScopedMockLog* ScopedMockLog::g_instance_ = NULL;
+
+ScopedMockLog::ScopedMockLog() : is_capturing_logs_(false) {}
+
+ScopedMockLog::~ScopedMockLog() {
+ if (is_capturing_logs_) {
+ StopCapturingLogs();
+ }
+}
+
+void ScopedMockLog::StartCapturingLogs() {
+ // We don't use CHECK(), which can generate a new LOG message, and
+ // thus can confuse ScopedMockLog objects or other registered
+ // LogSinks.
+ RAW_CHECK(!is_capturing_logs_);
+ RAW_CHECK(!g_instance_);
+
+ is_capturing_logs_ = true;
+ g_instance_ = this;
+ previous_handler_ = logging::GetLogMessageHandler();
+ logging::SetLogMessageHandler(LogMessageHandler);
+}
+
+void ScopedMockLog::StopCapturingLogs() {
+ // We don't use CHECK(), which can generate a new LOG message, and
+ // thus can confuse ScopedMockLog objects or other registered
+ // LogSinks.
+ RAW_CHECK(is_capturing_logs_);
+ RAW_CHECK(g_instance_ == this);
+
+ is_capturing_logs_ = false;
+ logging::SetLogMessageHandler(previous_handler_);
+ g_instance_ = NULL;
+}
+
+// static
+bool ScopedMockLog::LogMessageHandler(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str) {
+ return g_instance_->Log(severity, file, line, message_start, str);
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/test/scoped_mock_log.h b/chromium/net/test/scoped_mock_log.h
new file mode 100644
index 00000000000..e1edfcccde8
--- /dev/null
+++ b/chromium/net/test/scoped_mock_log.h
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_SCOPED_MOCK_LOG_H_
+#define NET_QUIC_TEST_TOOLS_SCOPED_MOCK_LOG_H_
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// A ScopedMockLog object intercepts LOG() messages issued during its
+// lifespan. Using this together with gMock, it's very easy to test
+// how a piece of code calls LOG(). The typical usage:
+//
+// TEST(FooTest, LogsCorrectly) {
+// ScopedMockLog log;
+//
+// // We expect the WARNING "Something bad!" exactly twice.
+// EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
+// .Times(2);
+//
+// // We allow foo.cc to call LOG(INFO) any number of times.
+// EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
+// .Times(AnyNumber());
+//
+// log.StartCapturingLogs(); // Call this after done setting expectations.
+// Foo(); // Exercises the code under test.
+// }
+//
+// CAVEAT: base/logging does not allow a thread to call LOG() again
+// when it's already inside a LOG() call. Doing so will cause a
+// deadlock. Therefore, it's the user's responsibility to not call
+// LOG() in an action triggered by ScopedMockLog::Log(). You may call
+// RAW_LOG() instead.
+class ScopedMockLog {
+ public:
+ // Creates a ScopedMockLog object that is not capturing logs.
+ // If it were to start to capture logs, it could be a problem if
+ // some other threads already exist and are logging, as the user
+ // hasn't had a chance to set up expectation on this object yet
+ // (calling a mock method before setting the expectation is
+ // UNDEFINED behavior).
+ ScopedMockLog();
+
+ // When the object is destructed, it stops intercepting logs.
+ ~ScopedMockLog();
+
+ // Starts log capturing if the object isn't already doing so.
+ // Otherwise crashes. Usually this method is called in the same
+ // thread that created this object. It is the user's responsibility
+ // to not call this method if another thread may be calling it or
+ // StopCapturingLogs() at the same time.
+ void StartCapturingLogs();
+
+ // Stops log capturing if the object is capturing logs. Otherwise
+ // crashes. Usually this method is called in the same thread that
+ // created this object. It is the user's responsibility to not call
+ // this method if another thread may be calling it or
+ // StartCapturingLogs() at the same time.
+ void StopCapturingLogs();
+
+ // Sets the Log Message Handler that gets passed every log message before
+ // it's sent to other log destinations (if any).
+ // Returns true to signal that it handled the message and the message
+ // should not be sent to other log destinations.
+ MOCK_METHOD5(Log, bool(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str));
+
+ private:
+ // The currently active scoped mock log.
+ static ScopedMockLog* g_instance_;
+
+ // Static function which is set as the logging message handler.
+ // Called once for each message.
+ static bool LogMessageHandler(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str);
+
+ // True if this object is currently capturing logs.
+ bool is_capturing_logs_;
+
+ // The previous handler to restore when the ScopedMockLog is destroyed.
+ logging::LogMessageHandlerFunction previous_handler_;
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_SCOPED_MOCK_LOG_H_
diff --git a/chromium/net/test/spawned_test_server/base_test_server.cc b/chromium/net/test/spawned_test_server/base_test_server.cc
index ac37c70d21c..016c9301edf 100644
--- a/chromium/net/test/spawned_test_server/base_test_server.cc
+++ b/chromium/net/test/spawned_test_server/base_test_server.cc
@@ -40,6 +40,27 @@ std::string GetHostname(BaseTestServer::Type type,
return BaseTestServer::kLocalhost;
}
+std::string GetClientCertType(SSLClientCertType type) {
+ switch (type) {
+ case CLIENT_CERT_RSA_SIGN:
+ return "rsa_sign";
+ case CLIENT_CERT_DSS_SIGN:
+ return "dss_sign";
+ case CLIENT_CERT_ECDSA_SIGN:
+ return "ecdsa_sign";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+void GetKeyExchangesList(int key_exchange, base::ListValue* values) {
+ if (key_exchange & BaseTestServer::SSLOptions::KEY_EXCHANGE_RSA)
+ values->Append(new base::StringValue("rsa"));
+ if (key_exchange & BaseTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA)
+ values->Append(new base::StringValue("dhe_rsa"));
+}
+
void GetCiphersList(int cipher, base::ListValue* values) {
if (cipher & BaseTestServer::SSLOptions::BULK_CIPHER_RC4)
values->Append(new base::StringValue("rc4"));
@@ -58,11 +79,13 @@ BaseTestServer::SSLOptions::SSLOptions()
ocsp_status(OCSP_OK),
cert_serial(0),
request_client_certificate(false),
+ key_exchanges(SSLOptions::KEY_EXCHANGE_ANY),
bulk_ciphers(SSLOptions::BULK_CIPHER_ANY),
record_resume(false),
tls_intolerant(TLS_INTOLERANT_NONE),
fallback_scsv_enabled(false),
- staple_ocsp_response(false) {}
+ staple_ocsp_response(false),
+ enable_npn(false) {}
BaseTestServer::SSLOptions::SSLOptions(
BaseTestServer::SSLOptions::ServerCertificate cert)
@@ -70,11 +93,13 @@ BaseTestServer::SSLOptions::SSLOptions(
ocsp_status(OCSP_OK),
cert_serial(0),
request_client_certificate(false),
+ key_exchanges(SSLOptions::KEY_EXCHANGE_ANY),
bulk_ciphers(SSLOptions::BULK_CIPHER_ANY),
record_resume(false),
tls_intolerant(TLS_INTOLERANT_NONE),
fallback_scsv_enabled(false),
- staple_ocsp_response(false) {}
+ staple_ocsp_response(false),
+ enable_npn(false) {}
BaseTestServer::SSLOptions::~SSLOptions() {}
@@ -375,6 +400,14 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
if (ssl_client_certs->GetSize())
arguments->Set("ssl-client-ca", ssl_client_certs.release());
+
+ scoped_ptr<base::ListValue> client_cert_types(new base::ListValue());
+ for (size_t i = 0; i < ssl_options_.client_cert_types.size(); i++) {
+ client_cert_types->Append(new base::StringValue(
+ GetClientCertType(ssl_options_.client_cert_types[i])));
+ }
+ if (client_cert_types->GetSize())
+ arguments->Set("ssl-client-cert-type", client_cert_types.release());
}
if (type_ == TYPE_HTTPS) {
@@ -389,6 +422,11 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
base::Value::CreateIntegerValue(ssl_options_.cert_serial));
}
+ // Check key exchange argument.
+ scoped_ptr<base::ListValue> key_exchange_values(new base::ListValue());
+ GetKeyExchangesList(ssl_options_.key_exchanges, key_exchange_values.get());
+ if (key_exchange_values->GetSize())
+ arguments->Set("ssl-key-exchange", key_exchange_values.release());
// Check bulk cipher argument.
scoped_ptr<base::ListValue> bulk_cipher_values(new base::ListValue());
GetCiphersList(ssl_options_.bulk_ciphers, bulk_cipher_values.get());
@@ -410,6 +448,8 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const {
}
if (ssl_options_.staple_ocsp_response)
arguments->Set("staple-ocsp-response", base::Value::CreateNullValue());
+ if (ssl_options_.enable_npn)
+ arguments->Set("enable-npn", base::Value::CreateNullValue());
}
return GenerateAdditionalArguments(arguments);
diff --git a/chromium/net/test/spawned_test_server/base_test_server.h b/chromium/net/test/spawned_test_server/base_test_server.h
index 163808c17b3..5c47ebc05a3 100644
--- a/chromium/net/test/spawned_test_server/base_test_server.h
+++ b/chromium/net/test/spawned_test_server/base_test_server.h
@@ -13,6 +13,7 @@
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/host_port_pair.h"
+#include "net/ssl/ssl_client_cert_type.h"
class GURL;
@@ -72,6 +73,18 @@ class BaseTestServer {
OCSP_UNKNOWN,
};
+ // Bitmask of key exchange algorithms that the test server supports and that
+ // can be selectively enabled or disabled.
+ enum KeyExchange {
+ // Special value used to indicate that any algorithm the server supports
+ // is acceptable. Preferred over explicitly OR-ing all key exchange
+ // algorithms.
+ KEY_EXCHANGE_ANY = 0,
+
+ KEY_EXCHANGE_RSA = (1 << 0),
+ KEY_EXCHANGE_DHE_RSA = (1 << 1),
+ };
+
// Bitmask of bulk encryption algorithms that the test server supports
// and that can be selectively enabled or disabled.
enum BulkCipher {
@@ -134,6 +147,16 @@ class BaseTestServer {
// field of the CertificateRequest.
std::vector<base::FilePath> client_authorities;
+ // If |request_client_certificate| is true, an optional list of
+ // SSLClientCertType values to populate the certificate_types field of the
+ // CertificateRequest.
+ std::vector<SSLClientCertType> client_cert_types;
+
+ // A bitwise-OR of KeyExchnage that should be used by the
+ // HTTPS server, or KEY_EXCHANGE_ANY to indicate that all implemented
+ // key exchange algorithms are acceptable.
+ int key_exchanges;
+
// A bitwise-OR of BulkCipher that should be used by the
// HTTPS server, or BULK_CIPHER_ANY to indicate that all implemented
// ciphers are acceptable.
@@ -165,6 +188,9 @@ class BaseTestServer {
// Whether to staple the OCSP response.
bool staple_ocsp_response;
+
+ // Whether to enable NPN support.
+ bool enable_npn;
};
// Pass as the 'host' parameter during construction to server on 127.0.0.1
diff --git a/chromium/net/test/spawned_test_server/local_test_server.cc b/chromium/net/test/spawned_test_server/local_test_server.cc
index 8679d7b1554..6516136cea9 100644
--- a/chromium/net/test/spawned_test_server/local_test_server.cc
+++ b/chromium/net/test/spawned_test_server/local_test_server.cc
@@ -22,7 +22,7 @@ namespace {
bool AppendArgumentFromJSONValue(const std::string& key,
const base::Value& value_node,
- CommandLine* command_line) {
+ base::CommandLine* command_line) {
std::string argument_name = "--" + key;
switch (value_node.GetType()) {
case base::Value::TYPE_NULL:
@@ -35,7 +35,7 @@ bool AppendArgumentFromJSONValue(const std::string& key,
command_line->AppendArg(argument_name + "=" + base::IntToString(value));
break;
}
- case Value::TYPE_STRING: {
+ case base::Value::TYPE_STRING: {
std::string value;
bool result = value_node.GetAsString(&value);
if (!result || value.empty())
@@ -198,7 +198,8 @@ bool LocalTestServer::SetPythonPath() const {
return true;
}
-bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const {
+bool LocalTestServer::AddCommandLineArguments(
+ base::CommandLine* command_line) const {
base::DictionaryValue arguments_dict;
if (!GenerateArguments(&arguments_dict))
return false;
@@ -210,7 +211,7 @@ bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const {
const std::string& key = it.key();
// Add arguments from a list.
- if (value.IsType(Value::TYPE_LIST)) {
+ if (value.IsType(base::Value::TYPE_LIST)) {
const base::ListValue* list = NULL;
if (!value.GetAsList(&list) || !list || list->empty())
return false;
diff --git a/chromium/net/test/spawned_test_server/local_test_server.h b/chromium/net/test/spawned_test_server/local_test_server.h
index cfd2eb3baee..334c4344c89 100644
--- a/chromium/net/test/spawned_test_server/local_test_server.h
+++ b/chromium/net/test/spawned_test_server/local_test_server.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/process/process_handle.h"
#include "net/test/spawned_test_server/base_test_server.h"
@@ -15,7 +16,9 @@
#include "base/win/scoped_handle.h"
#endif
+namespace base {
class CommandLine;
+}
namespace net {
@@ -73,7 +76,7 @@ class LocalTestServer : public BaseTestServer {
// Adds the command line arguments for the Python test server to
// |command_line|. Returns true on success.
- virtual bool AddCommandLineArguments(CommandLine* command_line) const
+ virtual bool AddCommandLineArguments(base::CommandLine* command_line) const
WARN_UNUSED_RESULT;
// Returns the actual path of document root for test cases. This function
@@ -105,8 +108,7 @@ class LocalTestServer : public BaseTestServer {
#if defined(OS_POSIX)
// The file descriptor the child writes to when it starts.
- int child_fd_;
- file_util::ScopedFD child_fd_closer_;
+ base::ScopedFD child_fd_;
#endif
DISALLOW_COPY_AND_ASSIGN(LocalTestServer);
diff --git a/chromium/net/test/spawned_test_server/local_test_server_posix.cc b/chromium/net/test/spawned_test_server/local_test_server_posix.cc
index 10c2d0f9932..67911ed68d0 100644
--- a/chromium/net/test/spawned_test_server/local_test_server_posix.cc
+++ b/chromium/net/test/spawned_test_server/local_test_server_posix.cc
@@ -10,6 +10,7 @@
#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
@@ -105,7 +106,7 @@ bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
// the same environment as the TestServer.
VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv);
- CommandLine python_command(CommandLine::NO_PROGRAM);
+ base::CommandLine python_command(base::CommandLine::NO_PROGRAM);
if (!GetPythonCommand(&python_command))
return false;
@@ -120,9 +121,8 @@ bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
}
// Save the read half. The write half is sent to the child.
- child_fd_ = pipefd[0];
- child_fd_closer_.reset(&child_fd_);
- file_util::ScopedFD write_closer(&pipefd[1]);
+ child_fd_.reset(pipefd[0]);
+ base::ScopedFD write_closer(pipefd[1]);
base::FileHandleMappingVector map_write_fd;
map_write_fd.push_back(std::make_pair(pipefd[1], pipefd[1]));
@@ -148,19 +148,19 @@ bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
}
bool LocalTestServer::WaitToStart() {
- file_util::ScopedFD child_fd_closer(child_fd_closer_.release());
+ base::ScopedFD our_fd(child_fd_.release());
base::TimeDelta remaining_time = TestTimeouts::action_timeout();
uint32 server_data_len = 0;
- if (!ReadData(child_fd_, sizeof(server_data_len),
+ if (!ReadData(our_fd.get(), sizeof(server_data_len),
reinterpret_cast<uint8*>(&server_data_len),
&remaining_time)) {
LOG(ERROR) << "Could not read server_data_len";
return false;
}
std::string server_data(server_data_len, '\0');
- if (!ReadData(child_fd_, server_data_len,
+ if (!ReadData(our_fd.get(), server_data_len,
reinterpret_cast<uint8*>(&server_data[0]),
&remaining_time)) {
LOG(ERROR) << "Could not read server_data (" << server_data_len
diff --git a/chromium/net/test/spawned_test_server/local_test_server_win.cc b/chromium/net/test/spawned_test_server/local_test_server_win.cc
index db067428348..726a5810c0d 100644
--- a/chromium/net/test/spawned_test_server/local_test_server_win.cc
+++ b/chromium/net/test/spawned_test_server/local_test_server_win.cc
@@ -122,7 +122,7 @@ ScopedPath::ScopedPath(const base::FilePath& path_to_add)
if (!new_value.empty())
new_value += ";";
- new_value += WideToUTF8(path_to_add.value());
+ new_value += base::WideToUTF8(path_to_add.value());
path_modified_ = environment_->SetVar("PATH", new_value);
}
@@ -141,7 +141,7 @@ ScopedPath::~ScopedPath() {
namespace net {
bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
- CommandLine python_command(CommandLine::NO_PROGRAM);
+ base::CommandLine python_command(base::CommandLine::NO_PROGRAM);
if (!GetPythonCommand(&python_command))
return false;
diff --git a/chromium/net/test/spawned_test_server/remote_test_server.cc b/chromium/net/test/spawned_test_server/remote_test_server.cc
index 594c5d488b4..13286a9b838 100644
--- a/chromium/net/test/spawned_test_server/remote_test_server.cc
+++ b/chromium/net/test/spawned_test_server/remote_test_server.cc
@@ -192,7 +192,12 @@ bool RemoteTestServer::Init(const base::FilePath& document_root) {
return false;
SetPort(test_server_port);
- SetResourcePath(document_root, base::FilePath().AppendASCII("net")
+ // Unlike LocalTestServer, RemoteTestServer passes relative paths to the test
+ // server. The test server fails on empty strings in some configurations.
+ base::FilePath fixed_root = document_root;
+ if (fixed_root.empty())
+ fixed_root = base::FilePath(base::FilePath::kCurrentDirectory);
+ SetResourcePath(fixed_root, base::FilePath().AppendASCII("net")
.AppendASCII("data")
.AppendASCII("ssl")
.AppendASCII("certificates"));
diff --git a/chromium/net/test/spawned_test_server/spawner_communicator.cc b/chromium/net/test/spawned_test_server/spawner_communicator.cc
index 38871571c96..d7736b8d77b 100644
--- a/chromium/net/test/spawned_test_server/spawner_communicator.cc
+++ b/chromium/net/test/spawned_test_server/spawner_communicator.cc
@@ -173,7 +173,7 @@ void SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread(
DCHECK(!cur_request_.get());
context_.reset(new TestURLRequestContext);
cur_request_ = context_->CreateRequest(
- GenerateSpawnerCommandURL(command, port_), DEFAULT_PRIORITY, this);
+ GenerateSpawnerCommandURL(command, port_), DEFAULT_PRIORITY, this, NULL);
DCHECK(cur_request_);
int current_request_id = ++next_id_;
SpawnerRequestData* data = new SpawnerRequestData(current_request_id,
diff --git a/chromium/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp b/chromium/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
index 793d8f73a74..0cf3500c6d3 100644
--- a/chromium/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
+++ b/chromium/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
@@ -340,7 +340,9 @@ class PKCS12InitSingleton {
}
};
-static base::LazyInstance<PKCS12InitSingleton> g_pkcs12_init_singleton =
+// Leaky so it can be initialized on worker threads and because there is no
+// cleanup necessary.
+static base::LazyInstance<PKCS12InitSingleton>::Leaky g_pkcs12_init_singleton =
LAZY_INSTANCE_INITIALIZER;
} // namespace
diff --git a/chromium/net/third_party/nss/README.chromium b/chromium/net/third_party/nss/README.chromium
index 8c4e008c70c..a1c53038b13 100644
--- a/chromium/net/third_party/nss/README.chromium
+++ b/chromium/net/third_party/nss/README.chromium
@@ -1,6 +1,6 @@
Name: Network Security Services (NSS)
URL: http://www.mozilla.org/projects/security/pki/nss/
-Version: 3.15.1
+Version: 3.15.5 Beta 2
Security Critical: Yes
License: MPL 2
License File: NOT_SHIPPED
@@ -11,30 +11,16 @@ This directory includes a copy of NSS's libssl from the hg repo at:
The same module appears in crypto/third_party/nss (and third_party/nss on some
platforms), so we don't repeat the license file here.
-The snapshot was updated to the hg tag: NSS_3_15_1_RTM
+The snapshot was updated to the hg tag: NSS_3_15_5_BETA2
Patches:
- * Commenting out a couple of functions because they need NSS symbols
- which may not exist in the system NSS library.
- patches/versionskew.patch
-
- * Send empty renegotiation info extension instead of SCSV unless TLS is
- disabled.
- patches/renegoscsv.patch
- https://bugzilla.mozilla.org/show_bug.cgi?id=549042
-
* Cache the peer's intermediate CA certificates in session ID, so that
they're available when we resume a session.
patches/cachecerts.patch
https://bugzilla.mozilla.org/show_bug.cgi?id=731478
- * Add the SSL_PeerCertificateChain function
- patches/peercertchain.patch
- patches/peercertchain2.patch
- https://bugzilla.mozilla.org/show_bug.cgi?id=731485
-
- * Add support for client auth with native crypto APIs on Mac and Windows
+ * Add support for client auth with native crypto APIs on Mac and Windows.
patches/clientauth.patch
ssl/sslplatf.c
@@ -43,11 +29,6 @@ Patches:
patches/didhandshakeresume.patch
https://bugzilla.mozilla.org/show_bug.cgi?id=731798
- * Allow SSL_HandshakeNegotiatedExtension to be called before the handshake
- is finished.
- https://bugzilla.mozilla.org/show_bug.cgi?id=681839
- patches/negotiatedextension.patch
-
* Add function to retrieve TLS client cert types requested by server.
https://bugzilla.mozilla.org/show_bug.cgi?id=51413
patches/getrequestedclientcerttypes.patch
@@ -57,28 +38,15 @@ Patches:
* Add support for TLS Channel IDs
patches/channelid.patch
- patches/channelid2.patch
* Add support for extracting the tls-unique channel binding value
patches/tlsunique.patch
https://bugzilla.mozilla.org/show_bug.cgi?id=563276
- * Define the EC_POINT_FORM_UNCOMPRESSED macro. In NSS 3.13.2 the macro
- definition was moved from the internal header ec.h to blapit.h. When
- compiling against older system NSS headers, we need to define the macro.
- patches/ecpointform.patch
-
* SSL_ExportKeyingMaterial should get the RecvBufLock and SSL3HandshakeLock.
This change was made in https://chromiumcodereview.appspot.com/10454066.
patches/secretexporterlocks.patch
- * Allow the constant-time CBC processing code to be compiled against older
- NSS that doesn't contain the CBC constant-time changes.
- patches/cbc.patch
- https://code.google.com/p/chromium/issues/detail?id=172658#c12
- TODO(wtc): remove this patch now that NSS 3.14.3 is the minimum
- compile-time and run-time version.
-
* Change ssl3_SuiteBOnly to always return PR_TRUE. The softoken in NSS
versions older than 3.15 report an EC key size range of 112 bits to 571
bits, even when it is compiled to support only the NIST P-256, P-384, and
@@ -94,29 +62,11 @@ Patches:
* Update Chromium-specific code for TLS 1.2.
patches/tls12chromium.patch
- * Add the Application Layer Protocol Negotiation extension.
- patches/alpn.patch
-
- * Fix an issue with allocating an SSL socket when under memory pressure.
- https://bugzilla.mozilla.org/show_bug.cgi?id=903565
- patches/sslsock_903565.patch
-
- * Implement the AES GCM cipher suites.
- https://bugzilla.mozilla.org/show_bug.cgi?id=880543
- patches/aesgcm.patch
-
* Add Chromium-specific code to detect AES GCM support in the system NSS
- libraries at run time.
+ libraries at run time. Remove this patch when all system NSS packages are
+ NSS 3.15 or later.
patches/aesgcmchromium.patch
- * Support generating SHA-1 signatures for TLS 1.2 client authentication. Use
- SHA-1 instead of SHA-256 if the server's preferences do not allow for
- SHA-256 or if the client private key may only support SHA-1 signatures. The
- latter happens when the key is in a CAPI service provider on Windows or if
- it is a 1024-bit RSA or DSA key.
- patches/tls12backuphash.patch
- patches/tls12backuphash2.patch
-
* Support ChaCha20+Poly1305 ciphersuites
http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-01
patches/chacha20poly1305.patch
@@ -125,33 +75,10 @@ Patches:
patches/cachelocks.patch
https://bugzilla.mozilla.org/show_bug.cgi?id=764646
- * Don't advertise TLS 1.2-only cipher suites in a TLS 1.1 ClientHello.
- https://bugzilla.mozilla.org/show_bug.cgi?id=919677
- patches/ciphersuiteversion.patch
-
- * Don't use record versions greater than 0x0301 in resumption ClientHello
- records either.
- https://bugzilla.mozilla.org/show_bug.cgi?id=923696
- https://code.google.com/p/chromium/issues/detail?id=303398
- patches/resumeclienthelloversion.patch
-
- * Make SSL False Start work with asynchronous certificate validation.
- https://bugzilla.mozilla.org/show_bug.cgi?id=713933
- patches/canfalsestart.patch
-
- * Have the Null Cipher limit output to the maximum allowed
- https://bugzilla.mozilla.org/show_bug.cgi?id=934016
- patches/nullcipher_934016.patch
-
- * In the case that a ClientHello record is between 256 and 511 bytes long,
- add an extension to make it 512 bytes. This works around a bug in F5
- terminators.
- patches/paddingextension.patch
- patches/paddingextensionall.patch
-
* Support the Certificate Transparency (RFC 6962) TLS extension
signed_certificate_timestamp (client only).
patches/signedcertificatetimestamps.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=944175
* Add a function to allow the cipher suites preference order to be set.
patches/cipherorder.patch
@@ -159,19 +86,28 @@ Patches:
* Add TLS_FALLBACK_SCSV cipher suite to version fallback connections.
patches/fallbackscsv.patch
- * Disable session ticket renewal.
- https://bugzilla.mozilla.org/show_bug.cgi?id=930857
- patches/disableticketrenewal.patch
-
* Add explicit functions for managing the SSL/TLS session cache.
This is a temporary workaround until Chromium migrates to NSS's
asynchronous certificate verification.
patches/sessioncache.patch
- * Remove static storage qualifier from variables in sslnonce.c. Due to
- a clang codegen bug on Mac, this caused an infinite loop.
- https://code.google.com/p/chromium/issues/detail?id=326011
- patches/sslnoncestatics.patch
+ * Use NSSRWLock instead of PRRWLock in sslSessionID. This avoids the bugs
+ in the lock rank checking code in PRRWLock.
+ patches/nssrwlock.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=957812
+
+ * Use the IANA-assigned value for the TLS padding extension.
+ patches/paddingextvalue.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=994883
+
+ * Move the signature_algorithms extension to the end of the extension list.
+ This works around a bug in WebSphere Application Server 7.0 which is
+ intolerant to the final extension having zero length.
+ patches/reorderextensions.patch
+
+ * Ignore out-of-order DTLS ChangeCipherSpec.
+ patches/ignorechangecipherspec.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1009227
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/chromium/net/third_party/nss/patches/aesgcm.patch b/chromium/net/third_party/nss/patches/aesgcm.patch
deleted file mode 100644
index 03fdf8e255a..00000000000
--- a/chromium/net/third_party/nss/patches/aesgcm.patch
+++ /dev/null
@@ -1,1363 +0,0 @@
-Index: net/third_party/nss/ssl/sslinfo.c
-===================================================================
---- net/third_party/nss/ssl/sslinfo.c (revision 217715)
-+++ net/third_party/nss/ssl/sslinfo.c (working copy)
-@@ -109,7 +109,7 @@
- #define K_ECDHE "ECDHE", kt_ecdh
-
- #define C_SEED "SEED", calg_seed
--#define C_CAMELLIA "CAMELLIA", calg_camellia
-+#define C_CAMELLIA "CAMELLIA", calg_camellia
- #define C_AES "AES", calg_aes
- #define C_RC4 "RC4", calg_rc4
- #define C_RC2 "RC2", calg_rc2
-@@ -117,6 +117,7 @@
- #define C_3DES "3DES", calg_3des
- #define C_NULL "NULL", calg_null
- #define C_SJ "SKIPJACK", calg_sj
-+#define C_AESGCM "AES-GCM", calg_aes_gcm
-
- #define B_256 256, 256, 256
- #define B_128 128, 128, 128
-@@ -127,12 +128,16 @@
- #define B_40 128, 40, 40
- #define B_0 0, 0, 0
-
-+#define M_AEAD_128 "AEAD", ssl_mac_aead, 128
- #define M_SHA256 "SHA256", ssl_hmac_sha256, 256
- #define M_SHA "SHA1", ssl_mac_sha, 160
- #define M_MD5 "MD5", ssl_mac_md5, 128
-+#define M_NULL "NULL", ssl_mac_null, 0
-
- static const SSLCipherSuiteInfo suiteInfo[] = {
- /* <------ Cipher suite --------------------> <auth> <KEA> <bulk cipher> <MAC> <FIPS> */
-+{0,CS(TLS_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_RSA, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0, },
-+
- {0,CS(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_256, M_SHA, 0, 0, 0, },
- {0,CS(TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_256, M_SHA, 0, 0, 0, },
- {0,CS(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_DHE, C_AES, B_256, M_SHA256, 1, 0, 0, },
-@@ -146,6 +151,7 @@
- {0,CS(TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_128, M_SHA, 0, 0, 0, },
- {0,CS(TLS_DHE_DSS_WITH_RC4_128_SHA), S_DSA, K_DHE, C_RC4, B_128, M_SHA, 0, 0, 0, },
- {0,CS(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_DHE, C_AES, B_128, M_SHA256, 1, 0, 0, },
-+{0,CS(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0, },
- {0,CS(TLS_DHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_DHE, C_AES, B_128, M_SHA, 1, 0, 0, },
- {0,CS(TLS_DHE_DSS_WITH_AES_128_CBC_SHA), S_DSA, K_DHE, C_AES, B_128, M_SHA, 1, 0, 0, },
- {0,CS(TLS_RSA_WITH_SEED_CBC_SHA), S_RSA, K_RSA, C_SEED,B_128, M_SHA, 1, 0, 0, },
-@@ -175,6 +181,9 @@
-
- #ifdef NSS_ENABLE_ECC
- /* ECC cipher suites */
-+{0,CS(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0, },
-+{0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0, },
-+
- {0,CS(TLS_ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, 0, 0, 0, },
- {0,CS(TLS_ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, 0, 0, 0, },
- {0,CS(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, 1, 0, 0, },
-Index: net/third_party/nss/ssl/sslimpl.h
-===================================================================
---- net/third_party/nss/ssl/sslimpl.h (revision 217715)
-+++ net/third_party/nss/ssl/sslimpl.h (working copy)
-@@ -64,6 +64,7 @@
- #define calg_aes ssl_calg_aes
- #define calg_camellia ssl_calg_camellia
- #define calg_seed ssl_calg_seed
-+#define calg_aes_gcm ssl_calg_aes_gcm
-
- #define mac_null ssl_mac_null
- #define mac_md5 ssl_mac_md5
-@@ -71,6 +72,7 @@
- #define hmac_md5 ssl_hmac_md5
- #define hmac_sha ssl_hmac_sha
- #define hmac_sha256 ssl_hmac_sha256
-+#define mac_aead ssl_mac_aead
-
- #define SET_ERROR_CODE /* reminder */
- #define SEND_ALERT /* reminder */
-@@ -290,9 +292,9 @@
- } ssl3CipherSuiteCfg;
-
- #ifdef NSS_ENABLE_ECC
--#define ssl_V3_SUITES_IMPLEMENTED 57
-+#define ssl_V3_SUITES_IMPLEMENTED 61
- #else
--#define ssl_V3_SUITES_IMPLEMENTED 35
-+#define ssl_V3_SUITES_IMPLEMENTED 37
- #endif /* NSS_ENABLE_ECC */
-
- #define MAX_DTLS_SRTP_CIPHER_SUITES 4
-@@ -440,20 +442,6 @@
- #define GS_DATA 3
- #define GS_PAD 4
-
--typedef SECStatus (*SSLCipher)(void * context,
-- unsigned char * out,
-- int * outlen,
-- int maxout,
-- const unsigned char *in,
-- int inlen);
--typedef SECStatus (*SSLCompressor)(void * context,
-- unsigned char * out,
-- int * outlen,
-- int maxout,
-- const unsigned char *in,
-- int inlen);
--typedef SECStatus (*SSLDestroy)(void *context, PRBool freeit);
--
- #if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_WIN32)
- typedef PCERT_KEY_CONTEXT PlatformKey;
- #elif defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_MACOSX)
-@@ -485,11 +473,12 @@
- cipher_camellia_128,
- cipher_camellia_256,
- cipher_seed,
-+ cipher_aes_128_gcm,
- cipher_missing /* reserved for no such supported cipher */
- /* This enum must match ssl3_cipherName[] in ssl3con.c. */
- } SSL3BulkCipher;
-
--typedef enum { type_stream, type_block } CipherType;
-+typedef enum { type_stream, type_block, type_aead } CipherType;
-
- #define MAX_IV_LENGTH 24
-
-@@ -531,6 +520,30 @@
- PRUint64 cipher_context[MAX_CIPHER_CONTEXT_LLONGS];
- } ssl3KeyMaterial;
-
-+typedef SECStatus (*SSLCipher)(void * context,
-+ unsigned char * out,
-+ int * outlen,
-+ int maxout,
-+ const unsigned char *in,
-+ int inlen);
-+typedef SECStatus (*SSLAEADCipher)(
-+ ssl3KeyMaterial * keys,
-+ PRBool doDecrypt,
-+ unsigned char * out,
-+ int * outlen,
-+ int maxout,
-+ const unsigned char *in,
-+ int inlen,
-+ const unsigned char *additionalData,
-+ int additionalDataLen);
-+typedef SECStatus (*SSLCompressor)(void * context,
-+ unsigned char * out,
-+ int * outlen,
-+ int maxout,
-+ const unsigned char *in,
-+ int inlen);
-+typedef SECStatus (*SSLDestroy)(void *context, PRBool freeit);
-+
- /* The DTLS anti-replay window. Defined here because we need it in
- * the cipher spec. Note that this is a ring buffer but left and
- * right represent the true window, with modular arithmetic used to
-@@ -557,6 +570,7 @@
- int mac_size;
- SSLCipher encode;
- SSLCipher decode;
-+ SSLAEADCipher aead;
- SSLDestroy destroy;
- void * encodeContext;
- void * decodeContext;
-@@ -706,8 +720,6 @@
- PRBool tls_keygen;
- } ssl3KEADef;
-
--typedef enum { kg_null, kg_strong, kg_export } SSL3KeyGenMode;
--
- /*
- ** There are tables of these, all const.
- */
-@@ -719,7 +731,8 @@
- CipherType type;
- int iv_size;
- int block_size;
-- SSL3KeyGenMode keygen_mode;
-+ int tag_size; /* authentication tag size for AEAD ciphers. */
-+ int explicit_nonce_size; /* for AEAD ciphers. */
- };
-
- /*
-Index: net/third_party/nss/ssl/ssl3ecc.c
-===================================================================
---- net/third_party/nss/ssl/ssl3ecc.c (revision 217715)
-+++ net/third_party/nss/ssl/ssl3ecc.c (working copy)
-@@ -911,6 +911,7 @@
- TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- 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_NULL_SHA,
- TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
-@@ -921,6 +922,7 @@
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- 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_NULL_SHA,
- TLS_ECDHE_RSA_WITH_RC4_128_SHA,
-@@ -932,12 +934,14 @@
- TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- 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_NULL_SHA,
- TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- 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_NULL_SHA,
- TLS_ECDHE_RSA_WITH_RC4_128_SHA,
-Index: net/third_party/nss/ssl/sslsock.c
-===================================================================
---- net/third_party/nss/ssl/sslsock.c (revision 217715)
-+++ net/third_party/nss/ssl/sslsock.c (working copy)
-@@ -67,8 +67,10 @@
- { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
-+ { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
-+ { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
-@@ -94,6 +96,7 @@
- { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { 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_ECDH_RSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
-@@ -105,6 +108,7 @@
- { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { 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 },
- #endif /* NSS_ENABLE_ECC */
- { 0, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED }
-Index: net/third_party/nss/ssl/ssl3con.c
-===================================================================
---- net/third_party/nss/ssl/ssl3con.c (revision 217715)
-+++ net/third_party/nss/ssl/ssl3con.c (working copy)
-@@ -78,6 +78,13 @@
- static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
- int maxOutputLen, const unsigned char *input,
- int inputLen);
-+#ifndef NO_PKCS11_BYPASS
-+static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
-+ unsigned char *out, int *outlen, int maxout,
-+ const unsigned char *in, int inlen,
-+ const unsigned char *additionalData,
-+ int additionalDataLen);
-+#endif
-
- #define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
- #define MIN_SEND_BUF_LENGTH 4000
-@@ -90,6 +97,13 @@
- static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
- /* cipher_suite policy enabled is_present*/
- #ifdef NSS_ENABLE_ECC
-+ { 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 */
-+ { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-+ { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-+
-+#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- #endif /* NSS_ENABLE_ECC */
-@@ -233,23 +247,30 @@
-
- /* indexed by SSL3BulkCipher */
- static const ssl3BulkCipherDef bulk_cipher_defs[] = {
-- /* cipher calg keySz secretSz type ivSz BlkSz keygen */
-- {cipher_null, calg_null, 0, 0, type_stream, 0, 0, kg_null},
-- {cipher_rc4, calg_rc4, 16, 16, type_stream, 0, 0, kg_strong},
-- {cipher_rc4_40, calg_rc4, 16, 5, type_stream, 0, 0, kg_export},
-- {cipher_rc4_56, calg_rc4, 16, 7, type_stream, 0, 0, kg_export},
-- {cipher_rc2, calg_rc2, 16, 16, type_block, 8, 8, kg_strong},
-- {cipher_rc2_40, calg_rc2, 16, 5, type_block, 8, 8, kg_export},
-- {cipher_des, calg_des, 8, 8, type_block, 8, 8, kg_strong},
-- {cipher_3des, calg_3des, 24, 24, type_block, 8, 8, kg_strong},
-- {cipher_des40, calg_des, 8, 5, type_block, 8, 8, kg_export},
-- {cipher_idea, calg_idea, 16, 16, type_block, 8, 8, kg_strong},
-- {cipher_aes_128, calg_aes, 16, 16, type_block, 16,16, kg_strong},
-- {cipher_aes_256, calg_aes, 32, 32, type_block, 16,16, kg_strong},
-- {cipher_camellia_128, calg_camellia,16, 16, type_block, 16,16, kg_strong},
-- {cipher_camellia_256, calg_camellia,32, 32, type_block, 16,16, kg_strong},
-- {cipher_seed, calg_seed, 16, 16, type_block, 16,16, kg_strong},
-- {cipher_missing, calg_null, 0, 0, type_stream, 0, 0, kg_null},
-+ /* |--------- Lengths --------| */
-+ /* cipher calg k s type i b t n */
-+ /* e e v l a o */
-+ /* y c | o g n */
-+ /* | r | c | c */
-+ /* | e | k | e */
-+ /* | t | | | | */
-+ {cipher_null, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
-+ {cipher_rc4, calg_rc4, 16,16, type_stream, 0, 0, 0, 0},
-+ {cipher_rc4_40, calg_rc4, 16, 5, type_stream, 0, 0, 0, 0},
-+ {cipher_rc4_56, calg_rc4, 16, 7, type_stream, 0, 0, 0, 0},
-+ {cipher_rc2, calg_rc2, 16,16, type_block, 8, 8, 0, 0},
-+ {cipher_rc2_40, calg_rc2, 16, 5, type_block, 8, 8, 0, 0},
-+ {cipher_des, calg_des, 8, 8, type_block, 8, 8, 0, 0},
-+ {cipher_3des, calg_3des, 24,24, type_block, 8, 8, 0, 0},
-+ {cipher_des40, calg_des, 8, 5, type_block, 8, 8, 0, 0},
-+ {cipher_idea, calg_idea, 16,16, type_block, 8, 8, 0, 0},
-+ {cipher_aes_128, calg_aes, 16,16, type_block, 16,16, 0, 0},
-+ {cipher_aes_256, calg_aes, 32,32, type_block, 16,16, 0, 0},
-+ {cipher_camellia_128, calg_camellia, 16,16, type_block, 16,16, 0, 0},
-+ {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_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
- };
-
- static const ssl3KEADef kea_defs[] =
-@@ -371,6 +392,11 @@
- {SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_rsa_fips},
- {SSL_RSA_FIPS_WITH_DES_CBC_SHA, cipher_des, mac_sha, kea_rsa_fips},
-
-+ {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_dhe_rsa},
-+ {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},
-+
- #ifdef NSS_ENABLE_ECC
- {TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_ecdsa},
- {TLS_ECDH_ECDSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdh_ecdsa},
-@@ -434,25 +460,29 @@
- { calg_aes , CKM_AES_CBC },
- { calg_camellia , CKM_CAMELLIA_CBC },
- { calg_seed , CKM_SEED_CBC },
-+ { calg_aes_gcm , CKM_AES_GCM },
- /* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
- };
-
--#define mmech_null (CK_MECHANISM_TYPE)0x80000000L
-+#define mmech_invalid (CK_MECHANISM_TYPE)0x80000000L
- #define mmech_md5 CKM_SSL3_MD5_MAC
- #define mmech_sha CKM_SSL3_SHA1_MAC
- #define mmech_md5_hmac CKM_MD5_HMAC
- #define mmech_sha_hmac CKM_SHA_1_HMAC
- #define mmech_sha256_hmac CKM_SHA256_HMAC
-+#define mmech_sha384_hmac CKM_SHA384_HMAC
-+#define mmech_sha512_hmac CKM_SHA512_HMAC
-
- static const ssl3MACDef mac_defs[] = { /* indexed by SSL3MACAlgorithm */
- /* pad_size is only used for SSL 3.0 MAC. See RFC 6101 Sec. 5.2.3.1. */
- /* mac mmech pad_size mac_size */
-- { mac_null, mmech_null, 0, 0 },
-+ { mac_null, mmech_invalid, 0, 0 },
- { mac_md5, mmech_md5, 48, MD5_LENGTH },
- { mac_sha, mmech_sha, 40, SHA1_LENGTH},
- {hmac_md5, mmech_md5_hmac, 0, MD5_LENGTH },
- {hmac_sha, mmech_sha_hmac, 0, SHA1_LENGTH},
- {hmac_sha256, mmech_sha256_hmac, 0, SHA256_LENGTH},
-+ { mac_aead, mmech_invalid, 0, 0 },
- };
-
- /* indexed by SSL3BulkCipher */
-@@ -472,6 +502,7 @@
- "Camellia-128",
- "Camellia-256",
- "SEED-CBC",
-+ "AES-128-GCM",
- "missing"
- };
-
-@@ -598,9 +629,13 @@
- case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
- case TLS_RSA_WITH_AES_256_CBC_SHA256:
- case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
- case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
- case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
- case TLS_RSA_WITH_AES_128_CBC_SHA256:
-+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
- case TLS_RSA_WITH_NULL_SHA256:
- return version >= SSL_LIBRARY_VERSION_TLS_1_2;
- default:
-@@ -1360,7 +1395,7 @@
- cipher = suite_def->bulk_cipher_alg;
- kea = suite_def->key_exchange_alg;
- mac = suite_def->mac_alg;
-- if (mac <= ssl_mac_sha && isTLS)
-+ if (mac <= ssl_mac_sha && mac != ssl_mac_null && isTLS)
- mac += 2;
-
- ss->ssl3.hs.suite_def = suite_def;
-@@ -1554,7 +1589,6 @@
- unsigned int optArg2 = 0;
- PRBool server_encrypts = ss->sec.isServer;
- SSLCipherAlgorithm calg;
-- SSLCompressionMethod compression_method;
- SECStatus rv;
-
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-@@ -1565,8 +1599,18 @@
- cipher_def = pwSpec->cipher_def;
-
- calg = cipher_def->calg;
-- compression_method = pwSpec->compression_method;
-
-+ if (calg == calg_aes_gcm) {
-+ pwSpec->encode = NULL;
-+ pwSpec->decode = NULL;
-+ pwSpec->destroy = NULL;
-+ pwSpec->encodeContext = NULL;
-+ pwSpec->decodeContext = NULL;
-+ pwSpec->aead = ssl3_AESGCMBypass;
-+ ssl3_InitCompressionContext(pwSpec);
-+ return SECSuccess;
-+ }
-+
- serverContext = pwSpec->server.cipher_context;
- clientContext = pwSpec->client.cipher_context;
-
-@@ -1721,6 +1765,195 @@
- return param;
- }
-
-+/* ssl3_BuildRecordPseudoHeader writes the SSL/TLS pseudo-header (the data
-+ * which is included in the MAC or AEAD additional data) to |out| and returns
-+ * its length. See https://tools.ietf.org/html/rfc5246#section-6.2.3.3 for the
-+ * definition of the AEAD additional data.
-+ *
-+ * TLS pseudo-header includes the record's version field, SSL's doesn't. Which
-+ * pseudo-header defintiion to use should be decided based on the version of
-+ * the protocol that was negotiated when the cipher spec became current, NOT
-+ * based on the version value in the record itself, and the decision is passed
-+ * to this function as the |includesVersion| argument. But, the |version|
-+ * argument should be the record's version value.
-+ */
-+static unsigned int
-+ssl3_BuildRecordPseudoHeader(unsigned char *out,
-+ SSL3SequenceNumber seq_num,
-+ SSL3ContentType type,
-+ PRBool includesVersion,
-+ SSL3ProtocolVersion version,
-+ PRBool isDTLS,
-+ int length)
-+{
-+ out[0] = (unsigned char)(seq_num.high >> 24);
-+ out[1] = (unsigned char)(seq_num.high >> 16);
-+ out[2] = (unsigned char)(seq_num.high >> 8);
-+ out[3] = (unsigned char)(seq_num.high >> 0);
-+ out[4] = (unsigned char)(seq_num.low >> 24);
-+ out[5] = (unsigned char)(seq_num.low >> 16);
-+ out[6] = (unsigned char)(seq_num.low >> 8);
-+ out[7] = (unsigned char)(seq_num.low >> 0);
-+ out[8] = type;
-+
-+ /* SSL3 MAC doesn't include the record's version field. */
-+ if (!includesVersion) {
-+ out[9] = MSB(length);
-+ out[10] = LSB(length);
-+ return 11;
-+ }
-+
-+ /* TLS MAC and AEAD additional data include version. */
-+ if (isDTLS) {
-+ SSL3ProtocolVersion dtls_version;
-+
-+ dtls_version = dtls_TLSVersionToDTLSVersion(version);
-+ out[9] = MSB(dtls_version);
-+ out[10] = LSB(dtls_version);
-+ } else {
-+ out[9] = MSB(version);
-+ out[10] = LSB(version);
-+ }
-+ out[11] = MSB(length);
-+ out[12] = LSB(length);
-+ return 13;
-+}
-+
-+static SECStatus
-+ssl3_AESGCM(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 char nonce[12];
-+ unsigned int uOutLen;
-+ CK_GCM_PARAMS gcmParams;
-+
-+ static const int tagSize = 16;
-+ static const int explicitNonceLen = 8;
-+
-+ /* See https://tools.ietf.org/html/rfc5288#section-3 for details of how the
-+ * nonce is formed. */
-+ memcpy(nonce, keys->write_iv, 4);
-+ if (doDecrypt) {
-+ memcpy(nonce + 4, in, explicitNonceLen);
-+ in += explicitNonceLen;
-+ inlen -= explicitNonceLen;
-+ *outlen = 0;
-+ } else {
-+ if (maxout < explicitNonceLen) {
-+ PORT_SetError(SEC_ERROR_INPUT_LEN);
-+ return SECFailure;
-+ }
-+ /* Use the 64-bit sequence number as the explicit nonce. */
-+ memcpy(nonce + 4, additionalData, explicitNonceLen);
-+ memcpy(out, additionalData, explicitNonceLen);
-+ out += explicitNonceLen;
-+ maxout -= explicitNonceLen;
-+ *outlen = explicitNonceLen;
-+ }
-+
-+ param.type = siBuffer;
-+ param.data = (unsigned char *) &gcmParams;
-+ param.len = sizeof(gcmParams);
-+ gcmParams.pIv = nonce;
-+ gcmParams.ulIvLen = sizeof(nonce);
-+ gcmParams.pAAD = (unsigned char *)additionalData; /* const cast */
-+ gcmParams.ulAADLen = additionalDataLen;
-+ gcmParams.ulTagBits = tagSize * 8;
-+
-+ if (doDecrypt) {
-+ rv = PK11_Decrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
-+ maxout, in, inlen);
-+ } else {
-+ rv = PK11_Encrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
-+ maxout, in, inlen);
-+ }
-+ *outlen += (int) uOutLen;
-+
-+ return rv;
-+}
-+
-+#ifndef NO_PKCS11_BYPASS
-+static SECStatus
-+ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
-+ PRBool doDecrypt,
-+ unsigned char *out,
-+ int *outlen,
-+ int maxout,
-+ const unsigned char *in,
-+ int inlen,
-+ const unsigned char *additionalData,
-+ int additionalDataLen)
-+{
-+ SECStatus rv = SECFailure;
-+ unsigned char nonce[12];
-+ unsigned int uOutLen;
-+ AESContext *cx;
-+ CK_GCM_PARAMS gcmParams;
-+
-+ static const int tagSize = 16;
-+ static const int explicitNonceLen = 8;
-+
-+ /* See https://tools.ietf.org/html/rfc5288#section-3 for details of how the
-+ * nonce is formed. */
-+ PORT_Assert(keys->write_iv_item.len == 4);
-+ if (keys->write_iv_item.len != 4) {
-+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-+ return SECFailure;
-+ }
-+ memcpy(nonce, keys->write_iv_item.data, 4);
-+ if (doDecrypt) {
-+ memcpy(nonce + 4, in, explicitNonceLen);
-+ in += explicitNonceLen;
-+ inlen -= explicitNonceLen;
-+ *outlen = 0;
-+ } else {
-+ if (maxout < explicitNonceLen) {
-+ PORT_SetError(SEC_ERROR_INPUT_LEN);
-+ return SECFailure;
-+ }
-+ /* Use the 64-bit sequence number as the explicit nonce. */
-+ memcpy(nonce + 4, additionalData, explicitNonceLen);
-+ memcpy(out, additionalData, explicitNonceLen);
-+ out += explicitNonceLen;
-+ maxout -= explicitNonceLen;
-+ *outlen = explicitNonceLen;
-+ }
-+
-+ gcmParams.pIv = nonce;
-+ gcmParams.ulIvLen = sizeof(nonce);
-+ gcmParams.pAAD = (unsigned char *)additionalData; /* const cast */
-+ gcmParams.ulAADLen = additionalDataLen;
-+ gcmParams.ulTagBits = tagSize * 8;
-+
-+ cx = (AESContext *)keys->cipher_context;
-+ rv = AES_InitContext(cx, keys->write_key_item.data,
-+ keys->write_key_item.len,
-+ (unsigned char *)&gcmParams, NSS_AES_GCM, !doDecrypt,
-+ AES_BLOCK_SIZE);
-+ if (rv != SECSuccess) {
-+ return rv;
-+ }
-+ if (doDecrypt) {
-+ rv = AES_Decrypt(cx, out, &uOutLen, maxout, in, inlen);
-+ } else {
-+ rv = AES_Encrypt(cx, out, &uOutLen, maxout, in, inlen);
-+ }
-+ AES_DestroyContext(cx, PR_FALSE);
-+ *outlen += (int) uOutLen;
-+
-+ return rv;
-+}
-+#endif
-+
- /* Initialize encryption and MAC contexts for pending spec.
- * Master Secret already is derived.
- * Caller holds Spec write lock.
-@@ -1748,14 +1981,27 @@
- pwSpec = ss->ssl3.pwSpec;
- cipher_def = pwSpec->cipher_def;
- macLength = pwSpec->mac_size;
-+ calg = cipher_def->calg;
-+ PORT_Assert(alg2Mech[calg].calg == calg);
-
-+ pwSpec->client.write_mac_context = NULL;
-+ pwSpec->server.write_mac_context = NULL;
-+
-+ if (calg == calg_aes_gcm) {
-+ pwSpec->encode = NULL;
-+ pwSpec->decode = NULL;
-+ pwSpec->destroy = NULL;
-+ pwSpec->encodeContext = NULL;
-+ pwSpec->decodeContext = NULL;
-+ pwSpec->aead = ssl3_AESGCM;
-+ return SECSuccess;
-+ }
-+
- /*
- ** Now setup the MAC contexts,
- ** crypto contexts are setup below.
- */
-
-- pwSpec->client.write_mac_context = NULL;
-- pwSpec->server.write_mac_context = NULL;
- mac_mech = pwSpec->mac_def->mmech;
- mac_param.data = (unsigned char *)&macLength;
- mac_param.len = sizeof(macLength);
-@@ -1778,9 +2024,6 @@
- ** Now setup the crypto contexts.
- */
-
-- calg = cipher_def->calg;
-- PORT_Assert(alg2Mech[calg].calg == calg);
--
- if (calg == calg_null) {
- pwSpec->encode = Null_Cipher;
- pwSpec->decode = Null_Cipher;
-@@ -1988,10 +2231,8 @@
- ssl3_ComputeRecordMAC(
- ssl3CipherSpec * spec,
- PRBool useServerMacKey,
-- PRBool isDTLS,
-- SSL3ContentType type,
-- SSL3ProtocolVersion version,
-- SSL3SequenceNumber seq_num,
-+ const unsigned char *header,
-+ unsigned int headerLen,
- const SSL3Opaque * input,
- int inputLength,
- unsigned char * outbuf,
-@@ -1999,56 +2240,8 @@
- {
- const ssl3MACDef * mac_def;
- SECStatus rv;
--#ifndef NO_PKCS11_BYPASS
-- PRBool isTLS;
--#endif
-- unsigned int tempLen;
-- unsigned char temp[MAX_MAC_LENGTH];
-
-- temp[0] = (unsigned char)(seq_num.high >> 24);
-- temp[1] = (unsigned char)(seq_num.high >> 16);
-- temp[2] = (unsigned char)(seq_num.high >> 8);
-- temp[3] = (unsigned char)(seq_num.high >> 0);
-- temp[4] = (unsigned char)(seq_num.low >> 24);
-- temp[5] = (unsigned char)(seq_num.low >> 16);
-- temp[6] = (unsigned char)(seq_num.low >> 8);
-- temp[7] = (unsigned char)(seq_num.low >> 0);
-- temp[8] = type;
--
-- /* TLS MAC includes the record's version field, SSL's doesn't.
-- ** We decide which MAC defintiion to use based on the version of
-- ** the protocol that was negotiated when the spec became current,
-- ** NOT based on the version value in the record itself.
-- ** But, we use the record'v version value in the computation.
-- */
-- if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
-- temp[9] = MSB(inputLength);
-- temp[10] = LSB(inputLength);
-- tempLen = 11;
--#ifndef NO_PKCS11_BYPASS
-- isTLS = PR_FALSE;
--#endif
-- } else {
-- /* New TLS hash includes version. */
-- if (isDTLS) {
-- SSL3ProtocolVersion dtls_version;
--
-- dtls_version = dtls_TLSVersionToDTLSVersion(version);
-- temp[9] = MSB(dtls_version);
-- temp[10] = LSB(dtls_version);
-- } else {
-- temp[9] = MSB(version);
-- temp[10] = LSB(version);
-- }
-- temp[11] = MSB(inputLength);
-- temp[12] = LSB(inputLength);
-- tempLen = 13;
--#ifndef NO_PKCS11_BYPASS
-- isTLS = PR_TRUE;
--#endif
-- }
--
-- PRINT_BUF(95, (NULL, "frag hash1: temp", temp, tempLen));
-+ PRINT_BUF(95, (NULL, "frag hash1: header", header, headerLen));
- PRINT_BUF(95, (NULL, "frag hash1: input", input, inputLength));
-
- mac_def = spec->mac_def;
-@@ -2093,7 +2286,10 @@
- return SECFailure;
- }
-
-- if (!isTLS) {
-+ if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
-+ unsigned int tempLen;
-+ unsigned char temp[MAX_MAC_LENGTH];
-+
- /* compute "inner" part of SSL3 MAC */
- hashObj->begin(write_mac_context);
- if (useServerMacKey)
-@@ -2105,7 +2301,7 @@
- spec->client.write_mac_key_item.data,
- spec->client.write_mac_key_item.len);
- hashObj->update(write_mac_context, mac_pad_1, pad_bytes);
-- hashObj->update(write_mac_context, temp, tempLen);
-+ hashObj->update(write_mac_context, header, headerLen);
- hashObj->update(write_mac_context, input, inputLength);
- hashObj->end(write_mac_context, temp, &tempLen, sizeof temp);
-
-@@ -2136,7 +2332,7 @@
- }
- if (rv == SECSuccess) {
- HMAC_Begin(cx);
-- HMAC_Update(cx, temp, tempLen);
-+ HMAC_Update(cx, header, headerLen);
- HMAC_Update(cx, input, inputLength);
- rv = HMAC_Finish(cx, outbuf, outLength, spec->mac_size);
- HMAC_Destroy(cx, PR_FALSE);
-@@ -2150,7 +2346,7 @@
- (useServerMacKey ? spec->server.write_mac_context
- : spec->client.write_mac_context);
- rv = PK11_DigestBegin(mac_context);
-- rv |= PK11_DigestOp(mac_context, temp, tempLen);
-+ rv |= PK11_DigestOp(mac_context, header, headerLen);
- rv |= PK11_DigestOp(mac_context, input, inputLength);
- rv |= PK11_DigestFinal(mac_context, outbuf, outLength, spec->mac_size);
- }
-@@ -2190,10 +2386,8 @@
- ssl3_ComputeRecordMACConstantTime(
- ssl3CipherSpec * spec,
- PRBool useServerMacKey,
-- PRBool isDTLS,
-- SSL3ContentType type,
-- SSL3ProtocolVersion version,
-- SSL3SequenceNumber seq_num,
-+ const unsigned char *header,
-+ unsigned int headerLen,
- const SSL3Opaque * input,
- int inputLen,
- int originalLen,
-@@ -2205,9 +2399,7 @@
- PK11Context * mac_context;
- SECItem param;
- SECStatus rv;
-- unsigned char header[13];
- PK11SymKey * key;
-- int recordLength;
-
- PORT_Assert(inputLen >= spec->mac_size);
- PORT_Assert(originalLen >= inputLen);
-@@ -2223,42 +2415,15 @@
- return SECSuccess;
- }
-
-- header[0] = (unsigned char)(seq_num.high >> 24);
-- header[1] = (unsigned char)(seq_num.high >> 16);
-- header[2] = (unsigned char)(seq_num.high >> 8);
-- header[3] = (unsigned char)(seq_num.high >> 0);
-- header[4] = (unsigned char)(seq_num.low >> 24);
-- header[5] = (unsigned char)(seq_num.low >> 16);
-- header[6] = (unsigned char)(seq_num.low >> 8);
-- header[7] = (unsigned char)(seq_num.low >> 0);
-- header[8] = type;
--
- macType = CKM_NSS_HMAC_CONSTANT_TIME;
-- recordLength = inputLen - spec->mac_size;
- if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
- macType = CKM_NSS_SSL3_MAC_CONSTANT_TIME;
-- header[9] = recordLength >> 8;
-- header[10] = recordLength;
-- params.ulHeaderLen = 11;
-- } else {
-- if (isDTLS) {
-- SSL3ProtocolVersion dtls_version;
--
-- dtls_version = dtls_TLSVersionToDTLSVersion(version);
-- header[9] = dtls_version >> 8;
-- header[10] = dtls_version;
-- } else {
-- header[9] = version >> 8;
-- header[10] = version;
-- }
-- header[11] = recordLength >> 8;
-- header[12] = recordLength;
-- params.ulHeaderLen = 13;
- }
-
- params.macAlg = spec->mac_def->mmech;
- params.ulBodyTotalLen = originalLen;
-- params.pHeader = header;
-+ params.pHeader = (unsigned char *) header; /* const cast */
-+ params.ulHeaderLen = headerLen;
-
- param.data = (unsigned char*) &params;
- param.len = sizeof(params);
-@@ -2291,9 +2456,8 @@
- /* ssl3_ComputeRecordMAC expects the MAC to have been removed from the
- * length already. */
- inputLen -= spec->mac_size;
-- return ssl3_ComputeRecordMAC(spec, useServerMacKey, isDTLS, type,
-- version, seq_num, input, inputLen,
-- outbuf, outLen);
-+ return ssl3_ComputeRecordMAC(spec, useServerMacKey, header, headerLen,
-+ input, inputLen, outbuf, outLen);
- }
-
- static PRBool
-@@ -2345,6 +2509,8 @@
- PRUint16 headerLen;
- int ivLen = 0;
- int cipherBytes = 0;
-+ unsigned char pseudoHeader[13];
-+ unsigned int pseudoHeaderLen;
-
- cipher_def = cwSpec->cipher_def;
- headerLen = isDTLS ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
-@@ -2390,86 +2556,117 @@
- contentLen = outlen;
- }
-
-- /*
-- * Add the MAC
-- */
-- rv = ssl3_ComputeRecordMAC( cwSpec, isServer, isDTLS,
-- type, cwSpec->version, cwSpec->write_seq_num, pIn, contentLen,
-- wrBuf->buf + headerLen + ivLen + contentLen, &macLen);
-- if (rv != SECSuccess) {
-- ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
-- return SECFailure;
-- }
-- p1Len = contentLen;
-- p2Len = macLen;
-- fragLen = contentLen + macLen; /* needs to be encrypted */
-- PORT_Assert(fragLen <= MAX_FRAGMENT_LENGTH + 1024);
-+ pseudoHeaderLen = ssl3_BuildRecordPseudoHeader(
-+ pseudoHeader, cwSpec->write_seq_num, type,
-+ cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->version,
-+ isDTLS, contentLen);
-+ PORT_Assert(pseudoHeaderLen <= sizeof(pseudoHeader));
-+ if (cipher_def->type == type_aead) {
-+ const int nonceLen = cipher_def->explicit_nonce_size;
-+ const int tagLen = cipher_def->tag_size;
-
-- /*
-- * Pad the text (if we're doing a block cipher)
-- * then Encrypt it
-- */
-- if (cipher_def->type == type_block) {
-- unsigned char * pBuf;
-- int padding_length;
-- int i;
-+ if (headerLen + nonceLen + contentLen + tagLen > wrBuf->space) {
-+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-+ return SECFailure;
-+ }
-
-- oddLen = contentLen % cipher_def->block_size;
-- /* Assume blockSize is a power of two */
-- padding_length = cipher_def->block_size - 1 -
-- ((fragLen) & (cipher_def->block_size - 1));
-- fragLen += padding_length + 1;
-- PORT_Assert((fragLen % cipher_def->block_size) == 0);
--
-- /* Pad according to TLS rules (also acceptable to SSL3). */
-- pBuf = &wrBuf->buf[headerLen + ivLen + fragLen - 1];
-- for (i = padding_length + 1; i > 0; --i) {
-- *pBuf-- = padding_length;
-+ cipherBytes = contentLen;
-+ rv = cwSpec->aead(
-+ isServer ? &cwSpec->server : &cwSpec->client,
-+ PR_FALSE, /* do encrypt */
-+ wrBuf->buf + headerLen, /* output */
-+ &cipherBytes, /* out len */
-+ wrBuf->space - headerLen, /* max out */
-+ pIn, contentLen, /* input */
-+ pseudoHeader, pseudoHeaderLen);
-+ if (rv != SECSuccess) {
-+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
-+ return SECFailure;
- }
-- /* now, if contentLen is not a multiple of block size, fix it */
-- p2Len = fragLen - p1Len;
-- }
-- if (p1Len < 256) {
-- oddLen = p1Len;
-- p1Len = 0;
- } else {
-- p1Len -= oddLen;
-- }
-- if (oddLen) {
-- p2Len += oddLen;
-- PORT_Assert( (cipher_def->block_size < 2) || \
-- (p2Len % cipher_def->block_size) == 0);
-- memmove(wrBuf->buf + headerLen + ivLen + p1Len, pIn + p1Len, oddLen);
-- }
-- if (p1Len > 0) {
-- int cipherBytesPart1 = -1;
-- rv = cwSpec->encode( cwSpec->encodeContext,
-- wrBuf->buf + headerLen + ivLen, /* output */
-- &cipherBytesPart1, /* actual outlen */
-- p1Len, /* max outlen */
-- pIn, p1Len); /* input, and inputlen */
-- PORT_Assert(rv == SECSuccess && cipherBytesPart1 == (int) p1Len);
-- if (rv != SECSuccess || cipherBytesPart1 != (int) p1Len) {
-- PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
-+ /*
-+ * Add the MAC
-+ */
-+ rv = ssl3_ComputeRecordMAC(cwSpec, isServer,
-+ pseudoHeader, pseudoHeaderLen, pIn, contentLen,
-+ wrBuf->buf + headerLen + ivLen + contentLen, &macLen);
-+ if (rv != SECSuccess) {
-+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
- return SECFailure;
- }
-- cipherBytes += cipherBytesPart1;
-+ p1Len = contentLen;
-+ p2Len = macLen;
-+ fragLen = contentLen + macLen; /* needs to be encrypted */
-+ PORT_Assert(fragLen <= MAX_FRAGMENT_LENGTH + 1024);
-+
-+ /*
-+ * Pad the text (if we're doing a block cipher)
-+ * then Encrypt it
-+ */
-+ if (cipher_def->type == type_block) {
-+ unsigned char * pBuf;
-+ int padding_length;
-+ int i;
-+
-+ oddLen = contentLen % cipher_def->block_size;
-+ /* Assume blockSize is a power of two */
-+ padding_length = cipher_def->block_size - 1 -
-+ ((fragLen) & (cipher_def->block_size - 1));
-+ fragLen += padding_length + 1;
-+ PORT_Assert((fragLen % cipher_def->block_size) == 0);
-+
-+ /* Pad according to TLS rules (also acceptable to SSL3). */
-+ pBuf = &wrBuf->buf[headerLen + ivLen + fragLen - 1];
-+ for (i = padding_length + 1; i > 0; --i) {
-+ *pBuf-- = padding_length;
-+ }
-+ /* now, if contentLen is not a multiple of block size, fix it */
-+ p2Len = fragLen - p1Len;
-+ }
-+ if (p1Len < 256) {
-+ oddLen = p1Len;
-+ p1Len = 0;
-+ } else {
-+ p1Len -= oddLen;
-+ }
-+ if (oddLen) {
-+ p2Len += oddLen;
-+ PORT_Assert( (cipher_def->block_size < 2) || \
-+ (p2Len % cipher_def->block_size) == 0);
-+ memmove(wrBuf->buf + headerLen + ivLen + p1Len, pIn + p1Len,
-+ oddLen);
-+ }
-+ if (p1Len > 0) {
-+ int cipherBytesPart1 = -1;
-+ rv = cwSpec->encode( cwSpec->encodeContext,
-+ wrBuf->buf + headerLen + ivLen, /* output */
-+ &cipherBytesPart1, /* actual outlen */
-+ p1Len, /* max outlen */
-+ pIn, p1Len); /* input, and inputlen */
-+ PORT_Assert(rv == SECSuccess && cipherBytesPart1 == (int) p1Len);
-+ if (rv != SECSuccess || cipherBytesPart1 != (int) p1Len) {
-+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
-+ return SECFailure;
-+ }
-+ cipherBytes += cipherBytesPart1;
-+ }
-+ if (p2Len > 0) {
-+ int cipherBytesPart2 = -1;
-+ rv = cwSpec->encode( cwSpec->encodeContext,
-+ wrBuf->buf + headerLen + ivLen + p1Len,
-+ &cipherBytesPart2, /* output and actual outLen */
-+ p2Len, /* max outlen */
-+ wrBuf->buf + headerLen + ivLen + p1Len,
-+ p2Len); /* input and inputLen*/
-+ PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
-+ if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
-+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
-+ return SECFailure;
-+ }
-+ cipherBytes += cipherBytesPart2;
-+ }
- }
-- if (p2Len > 0) {
-- int cipherBytesPart2 = -1;
-- rv = cwSpec->encode( cwSpec->encodeContext,
-- wrBuf->buf + headerLen + ivLen + p1Len,
-- &cipherBytesPart2, /* output and actual outLen */
-- p2Len, /* max outlen */
-- wrBuf->buf + headerLen + ivLen + p1Len,
-- p2Len); /* input and inputLen*/
-- PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
-- if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
-- PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
-- return SECFailure;
-- }
-- cipherBytes += cipherBytesPart2;
-- }
-+
- PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
-
- wrBuf->len = cipherBytes + headerLen;
-@@ -3012,9 +3209,6 @@
- static SECStatus
- ssl3_IllegalParameter(sslSocket *ss)
- {
-- PRBool isTLS;
--
-- isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
- (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
- PORT_SetError(ss->sec.isServer ? SSL_ERROR_BAD_CLIENT
- : SSL_ERROR_BAD_SERVER );
-@@ -3538,7 +3732,6 @@
- }
-
- key_material_params.bIsExport = (CK_BBOOL)(kea_def->is_limited);
-- /* was: (CK_BBOOL)(cipher_def->keygen_mode != kg_strong); */
-
- key_material_params.RandomInfo.pClientRandom = cr;
- key_material_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH;
-@@ -9946,7 +10139,6 @@
- static void
- ssl3_RecordKeyLog(sslSocket *ss)
- {
-- sslSessionID *sid;
- SECStatus rv;
- SECItem *keyData;
- char buf[14 /* "CLIENT_RANDOM " */ +
-@@ -9958,8 +10150,6 @@
-
- PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
-- sid = ss->sec.ci.sid;
--
- if (!ssl_keylog_iob)
- return;
-
-@@ -11095,6 +11285,8 @@
- unsigned int originalLen = 0;
- unsigned int good;
- unsigned int minLength;
-+ unsigned char header[13];
-+ unsigned int headerLen;
-
- PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
-
-@@ -11171,12 +11363,14 @@
- /* With >= TLS 1.1, CBC records have an explicit IV. */
- minLength += cipher_def->iv_size;
- }
-+ } else if (cipher_def->type == type_aead) {
-+ minLength = cipher_def->explicit_nonce_size + cipher_def->tag_size;
- }
-
- /* We can perform this test in variable time because the record's total
- * length and the ciphersuite are both public knowledge. */
- if (cText->buf->len < minLength) {
-- goto decrypt_loser;
-+ goto decrypt_loser;
- }
-
- if (cipher_def->type == type_block &&
-@@ -11244,78 +11438,104 @@
- return SECFailure;
- }
-
-- if (cipher_def->type == type_block &&
-- ((cText->buf->len - ivLen) % cipher_def->block_size) != 0) {
-- goto decrypt_loser;
-- }
-+ rType = cText->type;
-+ if (cipher_def->type == type_aead) {
-+ /* XXX For many AEAD ciphers, the plaintext is shorter than the
-+ * ciphertext by a fixed byte count, but it is not true in general.
-+ * Each AEAD cipher should provide a function that returns the
-+ * plaintext length for a given ciphertext. */
-+ unsigned int decryptedLen =
-+ cText->buf->len - cipher_def->explicit_nonce_size -
-+ cipher_def->tag_size;
-+ headerLen = ssl3_BuildRecordPseudoHeader(
-+ header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
-+ rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen);
-+ PORT_Assert(headerLen <= sizeof(header));
-+ rv = crSpec->aead(
-+ ss->sec.isServer ? &crSpec->client : &crSpec->server,
-+ PR_TRUE, /* do decrypt */
-+ plaintext->buf, /* out */
-+ (int*) &plaintext->len, /* outlen */
-+ plaintext->space, /* maxout */
-+ cText->buf->buf, /* in */
-+ cText->buf->len, /* inlen */
-+ header, headerLen);
-+ if (rv != SECSuccess) {
-+ good = 0;
-+ }
-+ } else {
-+ if (cipher_def->type == type_block &&
-+ ((cText->buf->len - ivLen) % cipher_def->block_size) != 0) {
-+ goto decrypt_loser;
-+ }
-
-- /* decrypt from cText buf to plaintext. */
-- rv = crSpec->decode(
-- crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
-- plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
-- if (rv != SECSuccess) {
-- goto decrypt_loser;
-- }
-+ /* decrypt from cText buf to plaintext. */
-+ rv = crSpec->decode(
-+ crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
-+ plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
-+ if (rv != SECSuccess) {
-+ goto decrypt_loser;
-+ }
-
-- PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
-+ PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
-
-- originalLen = plaintext->len;
-+ originalLen = plaintext->len;
-
-- /* If it's a block cipher, check and strip the padding. */
-- if (cipher_def->type == type_block) {
-- const unsigned int blockSize = cipher_def->block_size;
-- const unsigned int macSize = crSpec->mac_size;
-+ /* If it's a block cipher, check and strip the padding. */
-+ if (cipher_def->type == type_block) {
-+ const unsigned int blockSize = cipher_def->block_size;
-+ const unsigned int macSize = crSpec->mac_size;
-
-- if (crSpec->version <= SSL_LIBRARY_VERSION_3_0) {
-- good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
-- plaintext, blockSize, macSize));
-- } else {
-- good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
-- plaintext, macSize));
-+ if (!isTLS) {
-+ good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
-+ plaintext, blockSize, macSize));
-+ } else {
-+ good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
-+ plaintext, macSize));
-+ }
- }
-- }
-
-- /* compute the MAC */
-- rType = cText->type;
-- if (cipher_def->type == type_block) {
-- rv = ssl3_ComputeRecordMACConstantTime(
-- crSpec, (PRBool)(!ss->sec.isServer),
-- IS_DTLS(ss), rType, cText->version,
-- IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
-- plaintext->buf, plaintext->len, originalLen,
-- hash, &hashBytes);
-+ /* compute the MAC */
-+ headerLen = ssl3_BuildRecordPseudoHeader(
-+ header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
-+ rType, isTLS, cText->version, IS_DTLS(ss),
-+ plaintext->len - crSpec->mac_size);
-+ PORT_Assert(headerLen <= sizeof(header));
-+ if (cipher_def->type == type_block) {
-+ rv = ssl3_ComputeRecordMACConstantTime(
-+ crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
-+ plaintext->buf, plaintext->len, originalLen,
-+ hash, &hashBytes);
-
-- ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
-- crSpec->mac_size);
-- givenHash = givenHashBuf;
-+ ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
-+ crSpec->mac_size);
-+ givenHash = givenHashBuf;
-
-- /* plaintext->len will always have enough space to remove the MAC
-- * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
-- * plaintext->len if the result has enough space for the MAC and we
-- * tested the unadjusted size against minLength, above. */
-- plaintext->len -= crSpec->mac_size;
-- } else {
-- /* This is safe because we checked the minLength above. */
-- plaintext->len -= crSpec->mac_size;
-+ /* plaintext->len will always have enough space to remove the MAC
-+ * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
-+ * plaintext->len if the result has enough space for the MAC and we
-+ * tested the unadjusted size against minLength, above. */
-+ plaintext->len -= crSpec->mac_size;
-+ } else {
-+ /* This is safe because we checked the minLength above. */
-+ plaintext->len -= crSpec->mac_size;
-
-- rv = ssl3_ComputeRecordMAC(
-- crSpec, (PRBool)(!ss->sec.isServer),
-- IS_DTLS(ss), rType, cText->version,
-- IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
-- plaintext->buf, plaintext->len,
-- hash, &hashBytes);
-+ rv = ssl3_ComputeRecordMAC(
-+ crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
-+ plaintext->buf, plaintext->len, hash, &hashBytes);
-
-- /* We can read the MAC directly from the record because its location is
-- * public when a stream cipher is used. */
-- givenHash = plaintext->buf + plaintext->len;
-- }
-+ /* We can read the MAC directly from the record because its location
-+ * is public when a stream cipher is used. */
-+ givenHash = plaintext->buf + plaintext->len;
-+ }
-
-- good &= SECStatusToMask(rv);
-+ good &= SECStatusToMask(rv);
-
-- if (hashBytes != (unsigned)crSpec->mac_size ||
-- NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
-- /* We're allowed to leak whether or not the MAC check was correct */
-- good = 0;
-+ if (hashBytes != (unsigned)crSpec->mac_size ||
-+ NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
-+ /* We're allowed to leak whether or not the MAC check was correct */
-+ good = 0;
-+ }
- }
-
- if (good == 0) {
-Index: net/third_party/nss/ssl/sslenum.c
-===================================================================
---- net/third_party/nss/ssl/sslenum.c (revision 217715)
-+++ net/third_party/nss/ssl/sslenum.c (working copy)
-@@ -29,6 +29,14 @@
- * Finally, update the ssl_V3_SUITES_IMPLEMENTED macro in sslimpl.h.
- */
- const PRUint16 SSL_ImplementedCiphers[] = {
-+ /* AES-GCM */
-+#ifdef NSS_ENABLE_ECC
-+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+#endif /* NSS_ENABLE_ECC */
-+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
-+ TLS_RSA_WITH_AES_128_GCM_SHA256,
-+
- /* 256-bit */
- #ifdef NSS_ENABLE_ECC
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-Index: net/third_party/nss/ssl/sslproto.h
-===================================================================
---- net/third_party/nss/ssl/sslproto.h (revision 217715)
-+++ net/third_party/nss/ssl/sslproto.h (working copy)
-@@ -162,6 +162,10 @@
-
- #define TLS_RSA_WITH_SEED_CBC_SHA 0x0096
-
-+#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C
-+#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E
-+#define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2
-+
- /* TLS "Signaling Cipher Suite Value" (SCSV). May be requested by client.
- * Must NEVER be chosen by server. SSL 3.0 server acknowledges by sending
- * back an empty Renegotiation Info (RI) server hello extension.
-@@ -204,6 +208,11 @@
- #define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023
- #define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027
-
-+#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B
-+#define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D
-+#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
-+#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031
-+
- /* Netscape "experimental" cipher suites. */
- #define SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA 0xffe0
- #define SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA 0xffe1
-Index: net/third_party/nss/ssl/sslt.h
-===================================================================
---- net/third_party/nss/ssl/sslt.h (revision 217715)
-+++ net/third_party/nss/ssl/sslt.h (working copy)
-@@ -91,9 +91,10 @@
- ssl_calg_3des = 4,
- ssl_calg_idea = 5,
- ssl_calg_fortezza = 6, /* deprecated, now unused */
-- ssl_calg_aes = 7, /* coming soon */
-+ ssl_calg_aes = 7,
- ssl_calg_camellia = 8,
-- ssl_calg_seed = 9
-+ ssl_calg_seed = 9,
-+ ssl_calg_aes_gcm = 10
- } SSLCipherAlgorithm;
-
- typedef enum {
-@@ -102,7 +103,8 @@
- ssl_mac_sha = 2,
- ssl_hmac_md5 = 3, /* TLS HMAC version of mac_md5 */
- ssl_hmac_sha = 4, /* TLS HMAC version of mac_sha */
-- ssl_hmac_sha256 = 5
-+ ssl_hmac_sha256 = 5,
-+ ssl_mac_aead = 6
- } SSLMACAlgorithm;
-
- typedef enum {
-@@ -158,6 +160,9 @@
- PRUint16 effectiveKeyBits;
-
- /* MAC info */
-+ /* AEAD ciphers don't have a MAC. For an AEAD cipher, macAlgorithmName
-+ * is "AEAD", macAlgorithm is ssl_mac_aead, and macBits is the length in
-+ * bits of the authentication tag. */
- const char * macAlgorithmName;
- SSLMACAlgorithm macAlgorithm;
- PRUint16 macBits;
diff --git a/chromium/net/third_party/nss/patches/aesgcmchromium.patch b/chromium/net/third_party/nss/patches/aesgcmchromium.patch
index f9ec6cb4217..8549c35286e 100644
--- a/chromium/net/third_party/nss/patches/aesgcmchromium.patch
+++ b/chromium/net/third_party/nss/patches/aesgcmchromium.patch
@@ -1,5 +1,6 @@
---- net/third_party/nss/ssl/ssl3con.c.orig 2013-08-20 12:00:16.742760827 -0700
-+++ net/third_party/nss/ssl/ssl3con.c 2013-08-20 11:59:56.782463207 -0700
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:04:43.127747463 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:06:21.919386088 -0800
@@ -44,6 +44,9 @@
#ifdef NSS_ENABLE_ZLIB
#include "zlib.h"
@@ -10,7 +11,7 @@
#ifndef PK11_SETATTRS
#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
-@@ -1819,6 +1822,69 @@ ssl3_BuildRecordPseudoHeader(unsigned ch
+@@ -1842,6 +1845,69 @@ ssl3_BuildRecordPseudoHeader(unsigned ch
return 13;
}
@@ -80,7 +81,7 @@
static SECStatus
ssl3_AESGCM(ssl3KeyMaterial *keys,
PRBool doDecrypt,
-@@ -1870,10 +1936,10 @@ ssl3_AESGCM(ssl3KeyMaterial *keys,
+@@ -1893,10 +1959,10 @@ ssl3_AESGCM(ssl3KeyMaterial *keys,
gcmParams.ulTagBits = tagSize * 8;
if (doDecrypt) {
@@ -93,7 +94,7 @@
maxout, in, inlen);
}
*outlen += (int) uOutLen;
-@@ -5023,6 +5089,10 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+@@ -5103,6 +5169,10 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
ssl3_DisableNonDTLSSuites(ss);
}
@@ -103,8 +104,8 @@
+
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
- if (!num_suites)
-@@ -7728,6 +7798,10 @@ ssl3_HandleClientHello(sslSocket *ss, SS
+ if (!num_suites) {
+@@ -8080,6 +8150,10 @@ ssl3_HandleClientHello(sslSocket *ss, SS
ssl3_DisableNonDTLSSuites(ss);
}
diff --git a/chromium/net/third_party/nss/patches/alpn.patch b/chromium/net/third_party/nss/patches/alpn.patch
deleted file mode 100644
index ad217982f8e..00000000000
--- a/chromium/net/third_party/nss/patches/alpn.patch
+++ /dev/null
@@ -1,245 +0,0 @@
-diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 14:17:20.669282120 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 14:28:56.549496061 -0700
-@@ -9912,8 +9912,10 @@ ssl3_SendNextProto(sslSocket *ss)
- int padding_len;
- static const unsigned char padding[32] = {0};
-
-- if (ss->ssl3.nextProto.len == 0)
-+ if (ss->ssl3.nextProto.len == 0 ||
-+ ss->ssl3.nextProtoState == SSL_NEXT_PROTO_SELECTED) {
- return SECSuccess;
-+ }
-
- PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
- PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
---- a/nss/lib/ssl/ssl3ext.c 2013-07-31 14:10:00.342814862 -0700
-+++ b/nss/lib/ssl/ssl3ext.c 2013-07-31 14:28:56.549496061 -0700
-@@ -53,8 +53,12 @@ static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss,
- PRUint16 ex_type, SECItem *data);
- static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss,
- PRUint16 ex_type, SECItem *data);
-+static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss,
-+ PRUint16 ex_type, SECItem *data);
- static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
- PRUint16 ex_type, SECItem *data);
-+static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append,
-+ PRUint32 maxBytes);
- static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
- PRUint32 maxBytes);
- static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append,
-@@ -252,6 +256,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
- { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn },
- { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
- { ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn },
-+ { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn },
- { ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
- { ssl_channel_id_xtn, &ssl3_ClientHandleChannelIDXtn },
- { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
-@@ -271,18 +276,19 @@ static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
- */
- static const
- ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
-- { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
-- { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
-+ { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
-+ { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
- #ifdef NSS_ENABLE_ECC
-- { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
-- { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
-+ { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
-+ { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
- #endif
-- { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
-- { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
-- { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
-- { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
-- { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
-- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
-+ { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
-+ { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
-+ { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
-+ { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
-+ { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
-+ { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
-+ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
- /* any extra entries will appear as { 0, NULL } */
- };
-
-@@ -606,6 +612,11 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
-
- PORT_Assert(!ss->firstHsDone);
-
-+ if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
-+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-+ return SECFailure;
-+ }
-+
- rv = ssl3_ValidateNextProtoNego(data->data, data->len);
- if (rv != SECSuccess)
- return rv;
-@@ -639,6 +650,44 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
- return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
- }
-
-+static SECStatus
-+ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
-+{
-+ const unsigned char* d = data->data;
-+ PRUint16 name_list_len;
-+ SECItem protocol_name;
-+
-+ if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
-+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-+ return SECFailure;
-+ }
-+
-+ /* The extension data from the server has the following format:
-+ * uint16 name_list_len;
-+ * uint8 len;
-+ * uint8 protocol_name[len]; */
-+ if (data->len < 4 || data->len > 2 + 1 + 255) {
-+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
-+ return SECFailure;
-+ }
-+
-+ name_list_len = ((PRUint16) d[0]) << 8 |
-+ ((PRUint16) d[1]);
-+ if (name_list_len != data->len - 2 ||
-+ d[2] != data->len - 3) {
-+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
-+ return SECFailure;
-+ }
-+
-+ protocol_name.data = data->data + 3;
-+ protocol_name.len = data->len - 3;
-+
-+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
-+ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_SELECTED;
-+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
-+ return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &protocol_name);
-+}
-+
- static PRInt32
- ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append,
- PRUint32 maxBytes)
-@@ -672,6 +721,70 @@ loser:
- return -1;
- }
-
-+static PRInt32
-+ssl3_ClientSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
-+{
-+ PRInt32 extension_length;
-+ unsigned char *alpn_protos = NULL;
-+
-+ /* Renegotiations do not send this extension. */
-+ if (!ss->opt.nextProtoNego.data || ss->firstHsDone) {
-+ return 0;
-+ }
-+
-+ extension_length = 2 /* extension type */ + 2 /* extension length */ +
-+ 2 /* protocol name list length */ +
-+ ss->opt.nextProtoNego.len;
-+
-+ if (append && maxBytes >= extension_length) {
-+ /* NPN requires that the client's fallback protocol is first in the
-+ * list. However, ALPN sends protocols in preference order. So we
-+ * allocate a buffer and move the first protocol to the end of the
-+ * list. */
-+ SECStatus rv;
-+ const unsigned int len = ss->opt.nextProtoNego.len;
-+
-+ alpn_protos = PORT_Alloc(len);
-+ if (alpn_protos == NULL) {
-+ return SECFailure;
-+ }
-+ if (len > 0) {
-+ /* Each protocol string is prefixed with a single byte length. */
-+ unsigned int i = ss->opt.nextProtoNego.data[0] + 1;
-+ if (i <= len) {
-+ memcpy(alpn_protos, &ss->opt.nextProtoNego.data[i], len - i);
-+ memcpy(alpn_protos + len - i, ss->opt.nextProtoNego.data, i);
-+ } else {
-+ /* This seems to be invalid data so we'll send as-is. */
-+ memcpy(alpn_protos, ss->opt.nextProtoNego.data, len);
-+ }
-+ }
-+
-+ rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
-+ if (rv != SECSuccess)
-+ goto loser;
-+ rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
-+ if (rv != SECSuccess)
-+ goto loser;
-+ rv = ssl3_AppendHandshakeVariable(ss, alpn_protos, len, 2);
-+ PORT_Free(alpn_protos);
-+ alpn_protos = NULL;
-+ if (rv != SECSuccess)
-+ goto loser;
-+ ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
-+ ssl_app_layer_protocol_xtn;
-+ } else if (maxBytes < extension_length) {
-+ return 0;
-+ }
-+
-+ return extension_length;
-+
-+loser:
-+ if (alpn_protos)
-+ PORT_Free(alpn_protos);
-+ return -1;
-+}
-+
- static SECStatus
- ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type,
- SECItem *data)
-diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 14:10:35.113325316 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 14:28:56.589496647 -0700
-@@ -203,6 +203,16 @@ SSL_IMPORT SECStatus SSL_SetNextProtoCal
- * protocol in server-preference order. If no matching protocol is found it
- * selects the first supported protocol.
- *
-+ * Using this function also allows the client to transparently support ALPN.
-+ * The same set of protocols will be advertised via ALPN and, if the server
-+ * uses ALPN to select a protocol, SSL_GetNextProto will return
-+ * SSL_NEXT_PROTO_SELECTED as the state.
-+ *
-+ * Since NPN uses the first protocol as the fallback protocol, when sending an
-+ * ALPN extension, the first protocol is moved to the end of the list. This
-+ * indicates that the fallback protocol is the least preferred. The other
-+ * protocols should be in preference order.
-+ *
- * The supported protocols are specified in |data| in wire-format (8-bit
- * length-prefixed). For example: "\010http/1.1\006spdy/2". */
- SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd,
-@@ -212,7 +217,8 @@ SSL_IMPORT SECStatus SSL_SetNextProtoNeg
- typedef enum SSLNextProtoState {
- SSL_NEXT_PROTO_NO_SUPPORT = 0, /* No peer support */
- SSL_NEXT_PROTO_NEGOTIATED = 1, /* Mutual agreement */
-- SSL_NEXT_PROTO_NO_OVERLAP = 2 /* No protocol overlap found */
-+ SSL_NEXT_PROTO_NO_OVERLAP = 2, /* No protocol overlap found */
-+ SSL_NEXT_PROTO_SELECTED = 3 /* Server selected proto (ALPN) */
- } SSLNextProtoState;
-
- /* SSL_GetNextProto can be used in the HandshakeCallback or any time after
-diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
---- a/nss/lib/ssl/sslt.h 2013-07-31 14:13:43.806096237 -0700
-+++ b/nss/lib/ssl/sslt.h 2013-07-31 14:28:56.609496941 -0700
-@@ -195,12 +195,13 @@ typedef enum {
- #endif
- ssl_signature_algorithms_xtn = 13,
- ssl_use_srtp_xtn = 14,
-+ ssl_app_layer_protocol_xtn = 16,
- ssl_session_ticket_xtn = 35,
- ssl_next_proto_nego_xtn = 13172,
- ssl_channel_id_xtn = 30031,
- ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
- } SSLExtensionType;
-
--#define SSL_MAX_EXTENSIONS 10
-+#define SSL_MAX_EXTENSIONS 11
-
- #endif /* __sslt_h_ */
diff --git a/chromium/net/third_party/nss/patches/applypatches.sh b/chromium/net/third_party/nss/patches/applypatches.sh
index 89d97bc922f..b5f9d30a197 100755
--- a/chromium/net/third_party/nss/patches/applypatches.sh
+++ b/chromium/net/third_party/nss/patches/applypatches.sh
@@ -3,27 +3,18 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Run this script in the mozilla/security/nss/lib/ssl directory in a NSS
-# source tree.
+# Run this script in the nss/lib/ssl directory in a NSS source tree.
#
# Point patches_dir to the src/net/third_party/nss/patches directory in a
# chromium source tree.
patches_dir=/Users/wtc/chrome1/src/net/third_party/nss/patches
-patch -p4 < $patches_dir/versionskew.patch
-
-patch -p4 < $patches_dir/renegoscsv.patch
-
patch -p4 < $patches_dir/cachecerts.patch
-patch -p4 < $patches_dir/peercertchain.patch
-
patch -p4 < $patches_dir/clientauth.patch
patch -p4 < $patches_dir/didhandshakeresume.patch
-patch -p4 < $patches_dir/negotiatedextension.patch
-
patch -p4 < $patches_dir/getrequestedclientcerttypes.patch
patch -p4 < $patches_dir/restartclientauth.patch
@@ -32,56 +23,30 @@ patch -p4 < $patches_dir/channelid.patch
patch -p4 < $patches_dir/tlsunique.patch
-patch -p4 < $patches_dir/ecpointform.patch
-
patch -p4 < $patches_dir/secretexporterlocks.patch
-patch -p4 < $patches_dir/cbc.patch
-
patch -p4 < $patches_dir/suitebonly.patch
patch -p4 < $patches_dir/secitemarray.patch
patch -p4 < $patches_dir/tls12chromium.patch
-patch -p4 < $patches_dir/alpn.patch
-
-patch -p5 < $patches_dir/sslsock_903565.patch
-
-patch -p4 < $patches_dir/aesgcm.patch
-
patch -p4 < $patches_dir/aesgcmchromium.patch
-patch -p4 < $patches_dir/tls12backuphash.patch
-
patch -p4 < $patches_dir/chacha20poly1305.patch
patch -p4 < $patches_dir/cachelocks.patch
-patch -p4 < $patches_dir/ciphersuiteversion.patch
-
-patch -p4 < $patches_dir/peercertchain2.patch
-
-patch -p4 < $patches_dir/canfalsestart.patch
-
-patch -p4 < $patches_dir/nullcipher_934016.patch
-
-patch -p4 < $patches_dir/paddingextension.patch
-
-patch -p4 < $patches_dir/paddingextensionall.patch
-
-patch -p4 < $patches_dir/channelid2.patch
-
-patch -p5 < $patches_dir/signedcertificatetimestamps.patch
+patch -p4 < $patches_dir/signedcertificatetimestamps.patch
patch -p4 < $patches_dir/cipherorder.patch
-patch -p5 < $patches_dir/tls12backuphash2.patch
-
patch -p4 < $patches_dir/fallbackscsv.patch
-patch -p4 < $patches_dir/disableticketrenewal.patch
-
patch -p4 < $patches_dir/sessioncache.patch
-patch -p4 < $patches_dir/sslnoncestatics.patch
+patch -p4 < $patches_dir/nssrwlock.patch
+
+patch -p4 < $patches_dir/paddingextvalue.patch
+
+patch -p4 < $patches_dir/reorderextensions.patch
diff --git a/chromium/net/third_party/nss/patches/cachecerts.patch b/chromium/net/third_party/nss/patches/cachecerts.patch
index 6e55deab5df..fce438b66c4 100644
--- a/chromium/net/third_party/nss/patches/cachecerts.patch
+++ b/chromium/net/third_party/nss/patches/cachecerts.patch
@@ -1,6 +1,6 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:29:35.584231452 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:31:22.785789376 -0700
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:49:26.062517203 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:51:23.974478249 -0800
@@ -43,6 +43,7 @@
static SECStatus ssl3_AuthCertificate(sslSocket *ss);
@@ -9,7 +9,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
PK11SlotInfo * serverKeySlot);
static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
-@@ -6141,6 +6142,7 @@ ssl3_HandleServerHello(sslSocket *ss, SS
+@@ -6474,6 +6475,7 @@ ssl3_HandleServerHello(sslSocket *ss, SS
/* copy the peer cert from the SID */
if (sid->peerCert != NULL) {
ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
@@ -17,7 +17,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
}
/* NULL value for PMS signifies re-use of the old MS */
-@@ -7538,6 +7540,7 @@ compression_found:
+@@ -8048,6 +8050,7 @@ compression_found:
ss->sec.ci.sid = sid;
if (sid->peerCert != NULL) {
ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
@@ -25,7 +25,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
}
/*
-@@ -9147,6 +9150,44 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
+@@ -9662,6 +9665,44 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
ss->ssl3.peerCertChain = NULL;
}
@@ -70,7 +70,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
* ssl3 CertificateStatus message.
* Caller must hold Handshake and RecvBuf locks.
-@@ -9432,6 +9473,7 @@ ssl3_AuthCertificate(sslSocket *ss)
+@@ -9940,6 +9981,7 @@ ssl3_AuthCertificate(sslSocket *ss)
}
ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
@@ -79,16 +79,19 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (!ss->sec.isServer) {
CERTCertificate *cert = ss->sec.peerCert;
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 12:31:22.785789376 -0700
-@@ -572,10 +572,13 @@ typedef enum { never_cached,
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:51:23.984478418 -0800
+@@ -595,6 +595,8 @@ typedef enum { never_cached,
invalid_cache /* no longer in any cache. */
} Cached;
+#define MAX_PEER_CERT_CHAIN_SIZE 8
+
struct sslSessionIDStr {
- sslSessionID * next; /* chain used for client sockets, only */
+ /* The global cache lock must be held when accessing these members when the
+ * sid is in any cache.
+@@ -609,6 +611,7 @@ struct sslSessionIDStr {
+ */
CERTCertificate * peerCert;
+ CERTCertificate * peerCertChain[MAX_PEER_CERT_CHAIN_SIZE];
@@ -96,17 +99,17 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
const char * peerID; /* client only */
const char * urlSvrName; /* client only */
diff -pu a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
---- a/nss/lib/ssl/sslnonce.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslnonce.c 2013-07-31 12:31:22.785789376 -0700
+--- a/nss/lib/ssl/sslnonce.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslnonce.c 2014-01-17 17:51:23.984478418 -0800
@@ -164,6 +164,7 @@ lock_cache(void)
static void
ssl_DestroySID(sslSessionID *sid)
{
+ int i;
SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
- PORT_Assert((sid->references == 0));
-
-@@ -183,6 +184,9 @@ ssl_DestroySID(sslSessionID *sid)
+ PORT_Assert(sid->references == 0);
+ PORT_Assert(sid->cached != in_client_cache);
+@@ -194,6 +195,9 @@ ssl_DestroySID(sslSessionID *sid)
if ( sid->peerCert ) {
CERT_DestroyCertificate(sid->peerCert);
}
diff --git a/chromium/net/third_party/nss/patches/cachelocks.patch b/chromium/net/third_party/nss/patches/cachelocks.patch
index 5b3f93ed822..d52bf532cc7 100644
--- a/chromium/net/third_party/nss/patches/cachelocks.patch
+++ b/chromium/net/third_party/nss/patches/cachelocks.patch
@@ -1,8 +1,7 @@
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index 53c29f0..bc54c99 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -5593,7 +5593,6 @@ SSL3_ShutdownServerCache(void)
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:10:16.783281701 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:11:03.734060469 -0800
+@@ -5678,7 +5678,6 @@ SSL3_ShutdownServerCache(void)
}
PZ_Unlock(symWrapKeysLock);
@@ -10,7 +9,7 @@ index 53c29f0..bc54c99 100644
return SECSuccess;
}
-@@ -5645,7 +5644,7 @@ getWrappingKey( sslSocket * ss,
+@@ -5730,7 +5729,7 @@ getWrappingKey( sslSocket * ss,
pSymWrapKey = &symWrapKeys[symWrapMechIndex].symWrapKey[exchKeyType];
@@ -19,11 +18,10 @@ index 53c29f0..bc54c99 100644
PZ_Lock(symWrapKeysLock);
-diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
-index e3ae9ce..59140f8 100644
---- a/nss/lib/ssl/sslimpl.h
-+++ b/nss/lib/ssl/sslimpl.h
-@@ -1845,9 +1845,7 @@ extern SECStatus ssl_InitSymWrapKeysLock(void);
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:10:16.793281867 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:11:03.734060469 -0800
+@@ -1913,9 +1913,7 @@ extern SECStatus ssl_InitSymWrapKeysLock
extern SECStatus ssl_FreeSymWrapKeysLock(void);
@@ -34,51 +32,17 @@ index e3ae9ce..59140f8 100644
/***************** platform client auth ****************/
-diff --git a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
-index 5d8a954..a6f7349 100644
---- a/nss/lib/ssl/sslnonce.c
-+++ b/nss/lib/ssl/sslnonce.c
+diff -pu a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
+--- a/nss/lib/ssl/sslnonce.c 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/sslnonce.c 2014-01-17 18:11:03.754060801 -0800
@@ -35,91 +35,55 @@ static PZLock * cacheLock = NULL;
#define LOCK_CACHE lock_cache()
#define UNLOCK_CACHE PZ_Unlock(cacheLock)
-+static PRCallOnceType lockOnce;
-+
-+/* FreeSessionCacheLocks is a callback from NSS_RegisterShutdown which destroys
-+ * the session cache locks on shutdown and resets them to their initial
-+ * state. */
- static SECStatus
+-static SECStatus
-ssl_InitClientSessionCacheLock(void)
-+FreeSessionCacheLocks(void* appData, void* nssData)
- {
-+ static const PRCallOnceType pristineCallOnce;
-+ SECStatus rv;
-+
-+ if (!cacheLock) {
-+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
-+ return SECFailure;
-+ }
-+
-+ PZ_DestroyLock(cacheLock);
-+ cacheLock = NULL;
-+
-+ rv = ssl_FreeSymWrapKeysLock();
-+ if (rv != SECSuccess) {
-+ return rv;
-+ }
-+
-+ lockOnce = pristineCallOnce;
-+ return SECSuccess;
-+}
-+
-+/* InitSessionCacheLocks is called, protected by lockOnce, to create the
-+ * session cache locks. */
-+static PRStatus
-+InitSessionCacheLocks(void)
-+{
-+ SECStatus rv;
-+
- cacheLock = PZ_NewLock(nssILockCache);
+-{
+- cacheLock = PZ_NewLock(nssILockCache);
- return cacheLock ? SECSuccess : SECFailure;
-}
-
@@ -86,14 +50,8 @@ index 5d8a954..a6f7349 100644
-ssl_FreeClientSessionCacheLock(void)
-{
- if (cacheLock) {
-+ if (cacheLock == NULL) {
-+ return PR_FAILURE;
-+ }
-+ rv = ssl_InitSymWrapKeysLock();
-+ if (rv != SECSuccess) {
-+ PRErrorCode error = PORT_GetError();
- PZ_DestroyLock(cacheLock);
- cacheLock = NULL;
+- PZ_DestroyLock(cacheLock);
+- cacheLock = NULL;
- return SECSuccess;
- }
- PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
@@ -113,10 +71,15 @@ index 5d8a954..a6f7349 100644
- }
- return SECFailure;
-}
--
--static SECStatus
++static PRCallOnceType lockOnce;
+
++/* FreeSessionCacheLocks is a callback from NSS_RegisterShutdown which destroys
++ * the session cache locks on shutdown and resets them to their initial
++ * state. */
+ static SECStatus
-InitSessionCacheLocks(void)
--{
++FreeSessionCacheLocks(void* appData, void* nssData)
+ {
- SECStatus rv1, rv2;
- PRErrorCode rc;
- rv1 = ssl_InitSymWrapKeysLock();
@@ -129,23 +92,28 @@ index 5d8a954..a6f7349 100644
- PORT_SetError(rc);
- return SECFailure;
-}
--
++ static const PRCallOnceType pristineCallOnce;
++ SECStatus rv;
+
-/* free the session cache locks if they were initialized early */
-SECStatus
-ssl_FreeSessionCacheLocks()
-{
- PORT_Assert(PR_TRUE == LocksInitializedEarly);
- if (!LocksInitializedEarly) {
-- PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
-- return SECFailure;
-- }
++ if (!cacheLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
- FreeSessionCacheLocks();
- LocksInitializedEarly = PR_FALSE;
- return SECSuccess;
-}
--
+
-static PRCallOnceType lockOnce;
--
++ PZ_DestroyLock(cacheLock);
++ cacheLock = NULL;
+
-/* free the session cache locks if they were initialized lazily */
-static SECStatus ssl_ShutdownLocks(void* appData, void* nssData)
-{
@@ -153,26 +121,46 @@ index 5d8a954..a6f7349 100644
- if (LocksInitializedEarly) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-- }
++ rv = ssl_FreeSymWrapKeysLock();
++ if (rv != SECSuccess) {
++ return rv;
+ }
- FreeSessionCacheLocks();
- memset(&lockOnce, 0, sizeof(lockOnce));
-- return SECSuccess;
--}
--
++
++ lockOnce = pristineCallOnce;
+ return SECSuccess;
+ }
+
-static PRStatus initSessionCacheLocksLazily(void)
--{
++/* InitSessionCacheLocks is called, protected by lockOnce, to create the
++ * session cache locks. */
++static PRStatus
++InitSessionCacheLocks(void)
+ {
- SECStatus rv = InitSessionCacheLocks();
- if (SECSuccess != rv) {
-+ PORT_SetError(error);
++ SECStatus rv;
++
++ cacheLock = PZ_NewLock(nssILockCache);
++ if (cacheLock == NULL) {
return PR_FAILURE;
}
- rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
++ rv = ssl_InitSymWrapKeysLock();
++ if (rv != SECSuccess) {
++ PRErrorCode error = PORT_GetError();
++ PZ_DestroyLock(cacheLock);
++ cacheLock = NULL;
++ PORT_SetError(error);
++ return PR_FAILURE;
++ }
+
+ rv = NSS_RegisterShutdown(FreeSessionCacheLocks, NULL);
PORT_Assert(SECSuccess == rv);
if (SECSuccess != rv) {
return PR_FAILURE;
-@@ -127,34 +91,18 @@ static PRStatus initSessionCacheLocksLazily(void)
+@@ -127,34 +91,18 @@ static PRStatus initSessionCacheLocksLaz
return PR_SUCCESS;
}
@@ -213,11 +201,10 @@ index 5d8a954..a6f7349 100644
PZ_Lock(cacheLock);
}
-diff --git a/nss/lib/ssl/sslsnce.c b/nss/lib/ssl/sslsnce.c
-index b0446ad..34e07b0 100644
---- a/nss/lib/ssl/sslsnce.c
-+++ b/nss/lib/ssl/sslsnce.c
-@@ -1353,7 +1353,7 @@ SSL_ConfigServerSessionIDCache( int maxCacheEntries,
+diff -pu a/nss/lib/ssl/sslsnce.c b/nss/lib/ssl/sslsnce.c
+--- a/nss/lib/ssl/sslsnce.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslsnce.c 2014-01-17 18:11:03.774061133 -0800
+@@ -1353,7 +1353,7 @@ SSL_ConfigServerSessionIDCache( int
PRUint32 ssl3_timeout,
const char * directory)
{
@@ -235,7 +222,7 @@ index b0446ad..34e07b0 100644
return ssl_ConfigServerSessionIDCacheInstanceWithOpt(&globalCache,
ssl2_timeout, ssl3_timeout, directory, PR_FALSE,
maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
-@@ -1512,7 +1512,7 @@ SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
+@@ -1512,7 +1512,7 @@ SSL_InheritMPServerSIDCacheInstance(cach
return SECSuccess; /* already done. */
}
diff --git a/chromium/net/third_party/nss/patches/canfalsestart.patch b/chromium/net/third_party/nss/patches/canfalsestart.patch
deleted file mode 100644
index a3fb1813d21..00000000000
--- a/chromium/net/third_party/nss/patches/canfalsestart.patch
+++ /dev/null
@@ -1,837 +0,0 @@
-Index: net/third_party/nss/ssl/ssl.h
-===================================================================
---- net/third_party/nss/ssl/ssl.h (revision 227672)
-+++ net/third_party/nss/ssl/ssl.h (working copy)
-@@ -121,14 +121,17 @@
- #define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */
- /* default, applies only to */
- /* clients). False start is a */
--/* mode where an SSL client will start sending application data before */
--/* verifying the server's Finished message. This means that we could end up */
--/* sending data to an imposter. However, the data will be encrypted and */
--/* only the true server can derive the session key. Thus, so long as the */
--/* cipher isn't broken this is safe. Because of this, False Start will only */
--/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */
--/* bits. The advantage of False Start is that it saves a round trip for */
--/* client-speaks-first protocols when performing a full handshake. */
-+/* mode where an SSL client will start sending application data before
-+ * verifying the server's Finished message. This means that we could end up
-+ * sending data to an imposter. However, the data will be encrypted and
-+ * only the true server can derive the session key. Thus, so long as the
-+ * cipher isn't broken this is safe. The advantage of false start is that
-+ * it saves a round trip for client-speaks-first protocols when performing a
-+ * full handshake.
-+ *
-+ * In addition to enabling this option, the application must register a
-+ * callback using the SSL_SetCanFalseStartCallback function.
-+ */
-
- /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks
- * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting
-@@ -741,14 +744,45 @@
- SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString);
-
- /*
--** Set the callback on a particular socket that gets called when we finish
--** performing a handshake.
-+** Set the callback that gets called when a TLS handshake is complete. The
-+** handshake callback is called after verifying the peer's Finished message and
-+** before processing incoming application data.
-+**
-+** For the initial handshake: If the handshake false started (see
-+** SSL_ENABLE_FALSE_START), then application data may already have been sent
-+** before the handshake callback is called. If we did not false start then the
-+** callback will get called before any application data is sent.
- */
- typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd,
- void *client_data);
- SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd,
- SSLHandshakeCallback cb, void *client_data);
-
-+/* Applications that wish to enable TLS false start must set this callback
-+** function. NSS will invoke the functon to determine if a particular
-+** connection should use false start or not. SECSuccess indicates that the
-+** callback completed successfully, and if so *canFalseStart indicates if false
-+** start can be used. If the callback does not return SECSuccess then the
-+** handshake will be canceled. NSS's recommended criteria can be evaluated by
-+** calling SSL_RecommendedCanFalseStart.
-+**
-+** If no false start callback is registered then false start will never be
-+** done, even if the SSL_ENABLE_FALSE_START option is enabled.
-+**/
-+typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)(
-+ PRFileDesc *fd, void *arg, PRBool *canFalseStart);
-+
-+SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback(
-+ PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg);
-+
-+/* This function sets *canFalseStart according to the recommended criteria for
-+** false start. These criteria may change from release to release and may depend
-+** on which handshake features have been negotiated and/or properties of the
-+** certifciates/keys used on the connection.
-+*/
-+SSL_IMPORT SECStatus SSL_RecommendedCanFalseStart(PRFileDesc *fd,
-+ PRBool *canFalseStart);
-+
- /*
- ** For the server, request a new handshake. For the client, begin a new
- ** handshake. If flushCache is non-zero, the SSL3 cache entry will be
-Index: net/third_party/nss/ssl/ssl3con.c
-===================================================================
---- net/third_party/nss/ssl/ssl3con.c (revision 227672)
-+++ net/third_party/nss/ssl/ssl3con.c (working copy)
-@@ -2890,7 +2890,7 @@
- SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d",
- SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type),
- nIn));
-- PRINT_BUF(3, (ss, "Send record (plain text)", pIn, nIn));
-+ PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn));
-
- PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
-
-@@ -7344,36 +7344,72 @@
- return rv;
- }
-
-+static SECStatus
-+ssl3_CheckFalseStart(sslSocket *ss)
-+{
-+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
-+ PORT_Assert( !ss->ssl3.hs.authCertificatePending );
-+ PORT_Assert( !ss->ssl3.hs.canFalseStart );
-+
-+ if (!ss->canFalseStartCallback) {
-+ SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start",
-+ SSL_GETPID(), ss->fd));
-+ } else {
-+ PRBool maybeFalseStart;
-+ SECStatus rv;
-+
-+ /* An attacker can control the selected ciphersuite so we only wish to
-+ * do False Start in the case that the selected ciphersuite is
-+ * sufficiently strong that the attack can gain no advantage.
-+ * Therefore we always require an 80-bit cipher. */
-+ ssl_GetSpecReadLock(ss);
-+ maybeFalseStart = ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10;
-+ ssl_ReleaseSpecReadLock(ss);
-+
-+ if (!maybeFalseStart) {
-+ SSL_TRC(3, ("%d: SSL[%d]: no false start due to weak cipher",
-+ SSL_GETPID(), ss->fd));
-+ } else {
-+ rv = (ss->canFalseStartCallback)(ss->fd,
-+ ss->canFalseStartCallbackData,
-+ &ss->ssl3.hs.canFalseStart);
-+ if (rv == SECSuccess) {
-+ SSL_TRC(3, ("%d: SSL[%d]: false start callback returned %s",
-+ SSL_GETPID(), ss->fd,
-+ ss->ssl3.hs.canFalseStart ? "TRUE" : "FALSE"));
-+ } else {
-+ SSL_TRC(3, ("%d: SSL[%d]: false start callback failed (%s)",
-+ SSL_GETPID(), ss->fd,
-+ PR_ErrorToName(PR_GetError())));
-+ }
-+ return rv;
-+ }
-+ }
-+
-+ ss->ssl3.hs.canFalseStart = PR_FALSE;
-+ return SECSuccess;
-+}
-+
- PRBool
--ssl3_CanFalseStart(sslSocket *ss) {
-- PRBool rv;
-+ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss)
-+{
-+ PRBool result;
-
- PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
-
-- /* XXX: does not take into account whether we are waiting for
-- * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when
-- * that is done, this function could return different results each time it
-- * would be called.
-- */
-+ switch (ss->ssl3.hs.ws) {
-+ case wait_new_session_ticket:
-+ result = PR_TRUE;
-+ break;
-+ case wait_change_cipher:
-+ result = !ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn);
-+ break;
-+ default:
-+ result = PR_FALSE;
-+ break;
-+ }
-
-- ssl_GetSpecReadLock(ss);
-- rv = ss->opt.enableFalseStart &&
-- !ss->sec.isServer &&
-- !ss->ssl3.hs.isResuming &&
-- ss->ssl3.cwSpec &&
--
-- /* An attacker can control the selected ciphersuite so we only wish to
-- * do False Start in the case that the selected ciphersuite is
-- * sufficiently strong that the attack can gain no advantage.
-- * Therefore we require an 80-bit cipher and a forward-secret key
-- * exchange. */
-- ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
-- (ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
-- ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
-- ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
-- ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa);
-- ssl_ReleaseSpecReadLock(ss);
-- return rv;
-+ return result;
- }
-
- static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
-@@ -7463,6 +7499,9 @@
- }
- if (ss->ssl3.hs.authCertificatePending &&
- (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
-+ SSL_TRC(3, ("%d: SSL3[%p]: deferring ssl3_SendClientSecondRound because"
-+ " certificate authentication is still pending.",
-+ SSL_GETPID(), ss->fd));
- ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
- return SECWouldBlock;
- }
-@@ -7500,20 +7539,59 @@
- goto loser; /* err code was set. */
- }
-
-- /* XXX: If the server's certificate hasn't been authenticated by this
-- * point, then we may be leaking this NPN message to an attacker.
-+ /* This must be done after we've set ss->ssl3.cwSpec in
-+ * ssl3_SendChangeCipherSpecs because SSL_GetChannelInfo uses information
-+ * from cwSpec. This must be done before we call ssl3_CheckFalseStart
-+ * because the false start callback (if any) may need the information from
-+ * the functions that depend on this being set.
- */
-+ ss->enoughFirstHsDone = PR_TRUE;
-+
- if (!ss->firstHsDone) {
-+ /* XXX: If the server's certificate hasn't been authenticated by this
-+ * point, then we may be leaking this NPN message to an attacker.
-+ */
- rv = ssl3_SendNextProto(ss);
- if (rv != SECSuccess) {
- goto loser; /* err code was set. */
- }
- }
-+
- rv = ssl3_SendEncryptedExtensions(ss);
- if (rv != SECSuccess) {
- goto loser; /* err code was set. */
- }
-
-+ if (!ss->firstHsDone) {
-+ if (ss->opt.enableFalseStart) {
-+ if (!ss->ssl3.hs.authCertificatePending) {
-+ /* When we fix bug 589047, we will need to know whether we are
-+ * false starting before we try to flush the client second
-+ * round to the network. With that in mind, we purposefully
-+ * call ssl3_CheckFalseStart before calling ssl3_SendFinished,
-+ * which includes a call to ssl3_FlushHandshake, so that
-+ * no application develops a reliance on such flushing being
-+ * done before its false start callback is called.
-+ */
-+ ssl_ReleaseXmitBufLock(ss);
-+ rv = ssl3_CheckFalseStart(ss);
-+ ssl_GetXmitBufLock(ss);
-+ if (rv != SECSuccess) {
-+ goto loser;
-+ }
-+ } else {
-+ /* The certificate authentication and the server's Finished
-+ * message are racing each other. If the certificate
-+ * authentication wins, then we will try to false start in
-+ * ssl3_AuthCertificateComplete.
-+ */
-+ SSL_TRC(3, ("%d: SSL3[%p]: deferring false start check because"
-+ " certificate authentication is still pending.",
-+ SSL_GETPID(), ss->fd));
-+ }
-+ }
-+ }
-+
- rv = ssl3_SendFinished(ss, 0);
- if (rv != SECSuccess) {
- goto loser; /* err code was set. */
-@@ -7526,10 +7604,7 @@
- else
- ss->ssl3.hs.ws = wait_change_cipher;
-
-- /* Do the handshake callback for sslv3 here, if we can false start. */
-- if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
-- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
-- }
-+ PORT_Assert(ssl3_WaitingForStartOfServerSecondRound(ss));
-
- return SECSuccess;
-
-@@ -10147,13 +10222,6 @@
-
- ss->ssl3.hs.authCertificatePending = PR_TRUE;
- rv = SECSuccess;
--
-- /* XXX: Async cert validation and False Start don't work together
-- * safely yet; if we leave False Start enabled, we may end up false
-- * starting (sending application data) before we
-- * SSL_AuthCertificateComplete has been called.
-- */
-- ss->opt.enableFalseStart = PR_FALSE;
- }
-
- if (rv != SECSuccess) {
-@@ -10278,6 +10346,12 @@
- } else if (ss->ssl3.hs.restartTarget != NULL) {
- sslRestartTarget target = ss->ssl3.hs.restartTarget;
- ss->ssl3.hs.restartTarget = NULL;
-+
-+ if (target == ssl3_FinishHandshake) {
-+ SSL_TRC(3,("%d: SSL3[%p]: certificate authentication lost the race"
-+ " with peer's finished message", SSL_GETPID(), ss->fd));
-+ }
-+
- rv = target(ss);
- /* Even if we blocked here, we have accomplished enough to claim
- * success. Any remaining work will be taken care of by subsequent
-@@ -10287,7 +10361,27 @@
- rv = SECSuccess;
- }
- } else {
-- rv = SECSuccess;
-+ SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
-+ " peer's finished message", SSL_GETPID(), ss->fd));
-+
-+ PORT_Assert(!ss->firstHsDone);
-+ PORT_Assert(!ss->sec.isServer);
-+ PORT_Assert(!ss->ssl3.hs.isResuming);
-+ PORT_Assert(ss->ssl3.hs.ws != idle_handshake);
-+
-+ if (ss->opt.enableFalseStart &&
-+ !ss->firstHsDone &&
-+ !ss->sec.isServer &&
-+ !ss->ssl3.hs.isResuming &&
-+ ssl3_WaitingForStartOfServerSecondRound(ss)) {
-+ /* ssl3_SendClientSecondRound deferred the false start check because
-+ * certificate authentication was pending, so we do it now if we still
-+ * haven't received any of the server's second round yet.
-+ */
-+ rv = ssl3_CheckFalseStart(ss);
-+ } else {
-+ rv = SECSuccess;
-+ }
- }
-
- done:
-@@ -10913,9 +11007,6 @@
- return rv;
- }
-
-- ss->gs.writeOffset = 0;
-- ss->gs.readOffset = 0;
--
- if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
- effectiveExchKeyType = kt_rsa;
- } else {
-@@ -10980,6 +11071,9 @@
- return rv;
- }
-
-+/* The return type is SECStatus instead of void because this function needs
-+ * to have type sslRestartTarget.
-+ */
- SECStatus
- ssl3_FinishHandshake(sslSocket * ss)
- {
-@@ -10989,19 +11083,16 @@
-
- /* The first handshake is now completed. */
- ss->handshake = NULL;
-- ss->firstHsDone = PR_TRUE;
-
- if (ss->ssl3.hs.cacheSID) {
- (*ss->sec.cache)(ss->sec.ci.sid);
- ss->ssl3.hs.cacheSID = PR_FALSE;
- }
-
-+ ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
- ss->ssl3.hs.ws = idle_handshake;
-
-- /* Do the handshake callback for sslv3 here, if we cannot false start. */
-- if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
-- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
-- }
-+ ssl_FinishHandshake(ss);
-
- return SECSuccess;
- }
-@@ -11966,7 +12057,6 @@
-
- ssl_ReleaseSSL3HandshakeLock(ss);
- return rv;
--
- }
-
- /*
-Index: net/third_party/nss/ssl/ssl3gthr.c
-===================================================================
---- net/third_party/nss/ssl/ssl3gthr.c (revision 227672)
-+++ net/third_party/nss/ssl/ssl3gthr.c (working copy)
-@@ -275,11 +275,17 @@
- {
- SSL3Ciphertext cText;
- int rv;
-- PRBool canFalseStart = PR_FALSE;
-+ PRBool keepGoing = PR_TRUE;
-
- SSL_TRC(30, ("ssl3_GatherCompleteHandshake"));
-
-+ /* ssl3_HandleRecord may end up eventually calling ssl_FinishHandshake,
-+ * which requires the 1stHandshakeLock, which must be acquired before the
-+ * RecvBufLock.
-+ */
-+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
- PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
-+
- do {
- PRBool handleRecordNow = PR_FALSE;
-
-@@ -364,24 +370,52 @@
-
- cText.buf = &ss->gs.inbuf;
- rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
-+
-+ if (rv == (int) SECSuccess && ss->gs.buf.len > 0) {
-+ /* We have application data to return to the application. This
-+ * prioritizes returning application data to the application over
-+ * completing any renegotiation handshake we may be doing.
-+ */
-+ PORT_Assert(ss->firstHsDone);
-+ PORT_Assert(cText.type == content_application_data);
-+ break;
-+ }
- }
- if (rv < 0) {
- return ss->recvdCloseNotify ? 0 : rv;
- }
-
-- /* If we kicked off a false start in ssl3_HandleServerHelloDone, break
-- * out of this loop early without finishing the handshake.
-- */
-- if (ss->opt.enableFalseStart) {
-- ssl_GetSSL3HandshakeLock(ss);
-- canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
-- ss->ssl3.hs.ws == wait_new_session_ticket) &&
-- ssl3_CanFalseStart(ss);
-- ssl_ReleaseSSL3HandshakeLock(ss);
-+ PORT_Assert(keepGoing);
-+ ssl_GetSSL3HandshakeLock(ss);
-+ if (ss->ssl3.hs.ws == idle_handshake) {
-+ /* We are done with the current handshake so stop trying to
-+ * handshake. Note that it would be safe to test ss->firstHsDone
-+ * instead of ss->ssl3.hs.ws. By testing ss->ssl3.hs.ws instead,
-+ * we prioritize completing a renegotiation handshake over sending
-+ * application data.
-+ */
-+ PORT_Assert(ss->firstHsDone);
-+ PORT_Assert(!ss->ssl3.hs.canFalseStart);
-+ keepGoing = PR_FALSE;
-+ } else if (ss->ssl3.hs.canFalseStart) {
-+ /* Prioritize sending application data over trying to complete
-+ * the handshake if we're false starting.
-+ *
-+ * If we were to do this check at the beginning of the loop instead
-+ * of here, then this function would become be a no-op after
-+ * receiving the ServerHelloDone in the false start case, and we
-+ * would never complete the handshake.
-+ */
-+ PORT_Assert(!ss->firstHsDone);
-+
-+ if (ssl3_WaitingForStartOfServerSecondRound(ss)) {
-+ keepGoing = PR_FALSE;
-+ } else {
-+ ss->ssl3.hs.canFalseStart = PR_FALSE;
-+ }
- }
-- } while (ss->ssl3.hs.ws != idle_handshake &&
-- !canFalseStart &&
-- ss->gs.buf.len == 0);
-+ ssl_ReleaseSSL3HandshakeLock(ss);
-+ } while (keepGoing);
-
- ss->gs.readOffset = 0;
- ss->gs.writeOffset = ss->gs.buf.len;
-@@ -404,7 +438,10 @@
- {
- int rv;
-
-+ /* ssl3_GatherCompleteHandshake requires both of these locks. */
-+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
- PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
-+
- do {
- rv = ssl3_GatherCompleteHandshake(ss, flags);
- } while (rv > 0 && ss->gs.buf.len == 0);
-Index: net/third_party/nss/ssl/sslauth.c
-===================================================================
---- net/third_party/nss/ssl/sslauth.c (revision 227672)
-+++ net/third_party/nss/ssl/sslauth.c (working copy)
-@@ -100,7 +100,6 @@
- sslSocket *ss;
- const char *cipherName;
- PRBool isDes = PR_FALSE;
-- PRBool enoughFirstHsDone = PR_FALSE;
-
- ss = ssl_FindSocket(fd);
- if (!ss) {
-@@ -118,14 +117,7 @@
- *op = SSL_SECURITY_STATUS_OFF;
- }
-
-- if (ss->firstHsDone) {
-- enoughFirstHsDone = PR_TRUE;
-- } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
-- ssl3_CanFalseStart(ss)) {
-- enoughFirstHsDone = PR_TRUE;
-- }
--
-- if (ss->opt.useSecurity && enoughFirstHsDone) {
-+ if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
- if (ss->version < SSL_LIBRARY_VERSION_3_0) {
- cipherName = ssl_cipherName[ss->sec.cipherType];
- } else {
-Index: net/third_party/nss/ssl/sslimpl.h
-===================================================================
---- net/third_party/nss/ssl/sslimpl.h (revision 227672)
-+++ net/third_party/nss/ssl/sslimpl.h (working copy)
-@@ -881,6 +881,8 @@
- /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
- PRBool cacheSID;
-
-+ PRBool canFalseStart; /* Can/did we False Start */
-+
- /* clientSigAndHash contains the contents of the signature_algorithms
- * extension (if any) from the client. This is only valid for TLS 1.2
- * or later. */
-@@ -1162,6 +1164,10 @@
- unsigned long clientAuthRequested;
- unsigned long delayDisabled; /* Nagle delay disabled */
- unsigned long firstHsDone; /* first handshake is complete. */
-+ unsigned long enoughFirstHsDone; /* enough of the first handshake is
-+ * done for callbacks to be able to
-+ * retrieve channel security
-+ * parameters from the SSL socket. */
- unsigned long handshakeBegun;
- unsigned long lastWriteBlocked;
- unsigned long recvdCloseNotify; /* received SSL EOF. */
-@@ -1210,6 +1216,8 @@
- void *badCertArg;
- SSLHandshakeCallback handshakeCallback;
- void *handshakeCallbackData;
-+ SSLCanFalseStartCallback canFalseStartCallback;
-+ void *canFalseStartCallbackData;
- void *pkcs11PinArg;
- SSLNextProtoCallback nextProtoCallback;
- void *nextProtoArg;
-@@ -1423,7 +1431,19 @@
-
- extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
-
--extern PRBool ssl3_CanFalseStart(sslSocket *ss);
-+extern void ssl_FinishHandshake(sslSocket *ss);
-+
-+/* Returns PR_TRUE if we are still waiting for the server to respond to our
-+ * client second round. Once we've received any part of the server's second
-+ * round then we don't bother trying to false start since it is almost always
-+ * the case that the NewSessionTicket, ChangeCipherSoec, and Finished messages
-+ * were sent in the same packet and we want to process them all at the same
-+ * time. If we were to try to false start in the middle of the server's second
-+ * round, then we would increase the number of I/O operations
-+ * (SSL_ForceHandshake/PR_Recv/PR_Send/etc.) needed to finish the handshake.
-+ */
-+extern PRBool ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss);
-+
- extern SECStatus
- ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
- PRBool isServer,
-Index: net/third_party/nss/ssl/sslinfo.c
-===================================================================
---- net/third_party/nss/ssl/sslinfo.c (revision 227672)
-+++ net/third_party/nss/ssl/sslinfo.c (working copy)
-@@ -26,7 +26,6 @@
- sslSocket * ss;
- SSLChannelInfo inf;
- sslSessionID * sid;
-- PRBool enoughFirstHsDone = PR_FALSE;
-
- if (!info || len < sizeof inf.length) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -43,14 +42,7 @@
- memset(&inf, 0, sizeof inf);
- inf.length = PR_MIN(sizeof inf, len);
-
-- if (ss->firstHsDone) {
-- enoughFirstHsDone = PR_TRUE;
-- } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
-- ssl3_CanFalseStart(ss)) {
-- enoughFirstHsDone = PR_TRUE;
-- }
--
-- if (ss->opt.useSecurity && enoughFirstHsDone) {
-+ if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
- sid = ss->sec.ci.sid;
- inf.protocolVersion = ss->version;
- inf.authKeyBits = ss->sec.authKeyBits;
-Index: net/third_party/nss/ssl/sslsecur.c
-===================================================================
---- net/third_party/nss/ssl/sslsecur.c (revision 227672)
-+++ net/third_party/nss/ssl/sslsecur.c (working copy)
-@@ -97,23 +97,13 @@
- ss->securityHandshake = 0;
- }
- if (ss->handshake == 0) {
-- ssl_GetRecvBufLock(ss);
-- ss->gs.recordLen = 0;
-- ssl_ReleaseRecvBufLock(ss);
--
-- SSL_TRC(3, ("%d: SSL[%d]: handshake is completed",
-- SSL_GETPID(), ss->fd));
-- /* call handshake callback for ssl v2 */
-- /* for v3 this is done in ssl3_HandleFinished() */
-- if ((ss->handshakeCallback != NULL) && /* has callback */
-- (!ss->firstHsDone) && /* only first time */
-- (ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */
-- ss->firstHsDone = PR_TRUE;
-- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
-+ /* for v3 this is done in ssl3_FinishHandshake */
-+ if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) {
-+ ssl_GetRecvBufLock(ss);
-+ ss->gs.recordLen = 0;
-+ ssl_FinishHandshake(ss);
-+ ssl_ReleaseRecvBufLock(ss);
- }
-- ss->firstHsDone = PR_TRUE;
-- ss->gs.writeOffset = 0;
-- ss->gs.readOffset = 0;
- break;
- }
- rv = (*ss->handshake)(ss);
-@@ -134,6 +124,24 @@
- return rv;
- }
-
-+void
-+ssl_FinishHandshake(sslSocket *ss)
-+{
-+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
-+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
-+
-+ SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd));
-+
-+ ss->firstHsDone = PR_TRUE;
-+ ss->enoughFirstHsDone = PR_TRUE;
-+ ss->gs.writeOffset = 0;
-+ ss->gs.readOffset = 0;
-+
-+ if (ss->handshakeCallback) {
-+ (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
-+ }
-+}
-+
- /*
- * Handshake function that blocks. Used to force a
- * retry on a connection on the next read/write.
-@@ -206,6 +214,7 @@
- ssl_Get1stHandshakeLock(ss);
-
- ss->firstHsDone = PR_FALSE;
-+ ss->enoughFirstHsDone = PR_FALSE;
- if ( asServer ) {
- ss->handshake = ssl2_BeginServerHandshake;
- ss->handshaking = sslHandshakingAsServer;
-@@ -221,6 +230,8 @@
- ssl_ReleaseRecvBufLock(ss);
-
- ssl_GetSSL3HandshakeLock(ss);
-+ ss->ssl3.hs.canFalseStart = PR_FALSE;
-+ ss->ssl3.hs.restartTarget = NULL;
-
- /*
- ** Blow away old security state and get a fresh setup.
-@@ -331,6 +342,71 @@
- return SECSuccess;
- }
-
-+/* Register an application callback to be called when false start may happen.
-+** Acquires and releases HandshakeLock.
-+*/
-+SECStatus
-+SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
-+ void *arg)
-+{
-+ sslSocket *ss;
-+
-+ ss = ssl_FindSocket(fd);
-+ if (!ss) {
-+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
-+ SSL_GETPID(), fd));
-+ return SECFailure;
-+ }
-+
-+ if (!ss->opt.useSecurity) {
-+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
-+ return SECFailure;
-+ }
-+
-+ ssl_Get1stHandshakeLock(ss);
-+ ssl_GetSSL3HandshakeLock(ss);
-+
-+ ss->canFalseStartCallback = cb;
-+ ss->canFalseStartCallbackData = arg;
-+
-+ ssl_ReleaseSSL3HandshakeLock(ss);
-+ ssl_Release1stHandshakeLock(ss);
-+
-+ return SECSuccess;
-+}
-+
-+SECStatus
-+SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
-+{
-+ sslSocket *ss;
-+
-+ *canFalseStart = PR_FALSE;
-+ ss = ssl_FindSocket(fd);
-+ if (!ss) {
-+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart",
-+ SSL_GETPID(), fd));
-+ return SECFailure;
-+ }
-+
-+ if (!ss->ssl3.initialized) {
-+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
-+ return SECFailure;
-+ }
-+
-+ if (ss->version < SSL_LIBRARY_VERSION_3_0) {
-+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
-+ return SECFailure;
-+ }
-+
-+ /* Require a forward-secret key exchange. */
-+ *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
-+ ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
-+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
-+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
-+
-+ return SECSuccess;
-+}
-+
- /* Try to make progress on an SSL handshake by attempting to read the
- ** next handshake from the peer, and sending any responses.
- ** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot
-@@ -524,6 +600,9 @@
- int amount;
- int available;
-
-+ /* ssl3_GatherAppDataRecord may call ssl_FinishHandshake, which needs the
-+ * 1stHandshakeLock. */
-+ ssl_Get1stHandshakeLock(ss);
- ssl_GetRecvBufLock(ss);
-
- available = ss->gs.writeOffset - ss->gs.readOffset;
-@@ -590,6 +669,7 @@
-
- done:
- ssl_ReleaseRecvBufLock(ss);
-+ ssl_Release1stHandshakeLock(ss);
- return rv;
- }
-
-@@ -1156,7 +1236,8 @@
- int
- ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
- {
-- int rv = 0;
-+ int rv = 0;
-+ PRBool falseStart = PR_FALSE;
-
- SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes",
- SSL_GETPID(), ss->fd, len));
-@@ -1191,19 +1272,14 @@
- ss->writerThread = PR_GetCurrentThread();
- /* If any of these is non-zero, the initial handshake is not done. */
- if (!ss->firstHsDone) {
-- PRBool canFalseStart = PR_FALSE;
- ssl_Get1stHandshakeLock(ss);
-- if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
-+ if (ss->opt.enableFalseStart &&
-+ ss->version >= SSL_LIBRARY_VERSION_3_0) {
- ssl_GetSSL3HandshakeLock(ss);
-- if ((ss->ssl3.hs.ws == wait_change_cipher ||
-- ss->ssl3.hs.ws == wait_finished ||
-- ss->ssl3.hs.ws == wait_new_session_ticket) &&
-- ssl3_CanFalseStart(ss)) {
-- canFalseStart = PR_TRUE;
-- }
-+ falseStart = ss->ssl3.hs.canFalseStart;
- ssl_ReleaseSSL3HandshakeLock(ss);
- }
-- if (!canFalseStart &&
-+ if (!falseStart &&
- (ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
- rv = ssl_Do1stHandshake(ss);
- }
-@@ -1228,6 +1304,17 @@
- goto done;
- }
-
-+ if (!ss->firstHsDone) {
-+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_3_0);
-+#ifdef DEBUG
-+ ssl_GetSSL3HandshakeLock(ss);
-+ PORT_Assert(ss->ssl3.hs.canFalseStart);
-+ ssl_ReleaseSSL3HandshakeLock(ss);
-+#endif
-+ SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
-+ SSL_GETPID(), ss->fd));
-+ }
-+
- /* Send out the data using one of these functions:
- * ssl2_SendClear, ssl2_SendStream, ssl2_SendBlock,
- * ssl3_SendApplicationData
-Index: net/third_party/nss/ssl/sslsock.c
-===================================================================
---- net/third_party/nss/ssl/sslsock.c (revision 227672)
-+++ net/third_party/nss/ssl/sslsock.c (working copy)
-@@ -366,6 +366,8 @@
- ss->badCertArg = os->badCertArg;
- ss->handshakeCallback = os->handshakeCallback;
- ss->handshakeCallbackData = os->handshakeCallbackData;
-+ ss->canFalseStartCallback = os->canFalseStartCallback;
-+ ss->canFalseStartCallbackData = os->canFalseStartCallbackData;
- ss->pkcs11PinArg = os->pkcs11PinArg;
- ss->getChannelID = os->getChannelID;
- ss->getChannelIDArg = os->getChannelIDArg;
-@@ -2457,10 +2459,14 @@
- } else if (new_flags & PR_POLL_WRITE) {
- /* The caller is trying to write, but the handshake is
- ** blocked waiting for data to read, and the first
-- ** handshake has been sent. so do NOT to poll on write.
-+ ** handshake has been sent. So do NOT to poll on write
-+ ** unless we did false start.
- */
-- new_flags ^= PR_POLL_WRITE; /* don't select on write. */
-- new_flags |= PR_POLL_READ; /* do select on read. */
-+ if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 &&
-+ ss->ssl3.hs.canFalseStart)) {
-+ new_flags ^= PR_POLL_WRITE; /* don't select on write. */
-+ }
-+ new_flags |= PR_POLL_READ; /* do select on read. */
- }
- }
- } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
diff --git a/chromium/net/third_party/nss/patches/cbc.patch b/chromium/net/third_party/nss/patches/cbc.patch
deleted file mode 100644
index 032e5c184af..00000000000
--- a/chromium/net/third_party/nss/patches/cbc.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 14:10:35.113325316 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 14:12:00.254575103 -0700
-@@ -2157,6 +2157,20 @@ ssl3_ComputeRecordMAC(
- return rv;
- }
-
-+/* This is a bodge to allow this code to be compiled against older NSS headers
-+ * that don't contain the CBC constant-time changes. */
-+#ifndef CKM_NSS_HMAC_CONSTANT_TIME
-+#define CKM_NSS_HMAC_CONSTANT_TIME (CKM_NSS + 19)
-+#define CKM_NSS_SSL3_MAC_CONSTANT_TIME (CKM_NSS + 20)
-+
-+typedef struct CK_NSS_MAC_CONSTANT_TIME_PARAMS {
-+ CK_MECHANISM_TYPE macAlg; /* in */
-+ CK_ULONG ulBodyTotalLen; /* in */
-+ CK_BYTE * pHeader; /* in */
-+ CK_ULONG ulHeaderLen; /* in */
-+} CK_NSS_MAC_CONSTANT_TIME_PARAMS;
-+#endif
-+
- /* Called from: ssl3_HandleRecord()
- * Caller must already hold the SpecReadLock. (wish we could assert that!)
- *
-@@ -2179,7 +2193,8 @@ ssl3_ComputeRecordMACConstantTime(
- {
- CK_MECHANISM_TYPE macType;
- CK_NSS_MAC_CONSTANT_TIME_PARAMS params;
-- SECItem param, inputItem, outputItem;
-+ PK11Context * mac_context;
-+ SECItem param;
- SECStatus rv;
- unsigned char header[13];
- PK11SymKey * key;
-@@ -2240,34 +2255,27 @@ ssl3_ComputeRecordMACConstantTime(
- param.len = sizeof(params);
- param.type = 0;
-
-- inputItem.data = (unsigned char *) input;
-- inputItem.len = inputLen;
-- inputItem.type = 0;
--
-- outputItem.data = outbuf;
-- outputItem.len = *outLen;
-- outputItem.type = 0;
--
- key = spec->server.write_mac_key;
- if (!useServerMacKey) {
- key = spec->client.write_mac_key;
- }
-+ mac_context = PK11_CreateContextBySymKey(macType, CKA_SIGN, key, &param);
-+ if (mac_context == NULL) {
-+ /* Older versions of NSS may not support constant-time MAC. */
-+ goto fallback;
-+ }
-
-- rv = PK11_SignWithSymKey(key, macType, &param, &outputItem, &inputItem);
-- if (rv != SECSuccess) {
-- if (PORT_GetError() == SEC_ERROR_INVALID_ALGORITHM) {
-- goto fallback;
-- }
-+ rv = PK11_DigestBegin(mac_context);
-+ rv |= PK11_DigestOp(mac_context, input, inputLen);
-+ rv |= PK11_DigestFinal(mac_context, outbuf, outLen, spec->mac_size);
-+ PK11_DestroyContext(mac_context, PR_TRUE);
-
-- *outLen = 0;
-+ PORT_Assert(rv != SECSuccess || *outLen == (unsigned)spec->mac_size);
-+
-+ if (rv != SECSuccess) {
- rv = SECFailure;
- ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
-- return rv;
- }
--
-- PORT_Assert(outputItem.len == (unsigned)spec->mac_size);
-- *outLen = outputItem.len;
--
- return rv;
-
- fallback:
diff --git a/chromium/net/third_party/nss/patches/chacha20poly1305.patch b/chromium/net/third_party/nss/patches/chacha20poly1305.patch
index 89a71bbf6d7..028ed70be5e 100644
--- a/chromium/net/third_party/nss/patches/chacha20poly1305.patch
+++ b/chromium/net/third_party/nss/patches/chacha20poly1305.patch
@@ -1,7 +1,6 @@
-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
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:06:41.659713513 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:07:10.270188062 -0800
@@ -40,6 +40,21 @@
#define CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256 (CKM_NSS + 24)
#endif
@@ -24,16 +23,16 @@ index 8be517c..53c29f0 100644
#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*/
+@@ -104,6 +119,8 @@ static ssl3CipherSuiteCfg cipherSuites[s
+ /* cipher_suite policy enabled isPresent */
+
#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[] = {
++ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_ALLOWED, PR_FALSE, PR_FALSE},
++ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around
+@@ -292,6 +309,7 @@ static const ssl3BulkCipherDef bulk_ciph
{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},
@@ -41,7 +40,7 @@ index 8be517c..53c29f0 100644
{cipher_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
};
-@@ -399,6 +417,8 @@ static const ssl3CipherSuiteDef cipher_suite_defs[] =
+@@ -418,6 +436,8 @@ static const ssl3CipherSuiteDef cipher_s
{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},
@@ -50,7 +49,7 @@ index 8be517c..53c29f0 100644
#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[] = {
+@@ -483,6 +503,7 @@ static const SSLCipher2Mech alg2Mech[] =
{ calg_camellia , CKM_CAMELLIA_CBC },
{ calg_seed , CKM_SEED_CBC },
{ calg_aes_gcm , CKM_AES_GCM },
@@ -58,7 +57,16 @@ index 8be517c..53c29f0 100644
/* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
};
-@@ -2020,6 +2041,46 @@ ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
+@@ -647,6 +668,8 @@ ssl3_CipherSuiteAllowedForVersionRange(
+ * SSL_DH_ANON_EXPORT_WITH_DES40_CBC_SHA: never implemented
+ */
+ return vrange->min <= SSL_LIBRARY_VERSION_TLS_1_0;
++ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
++ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+@@ -2043,6 +2066,46 @@ ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
}
#endif
@@ -105,7 +113,7 @@ index 8be517c..53c29f0 100644
/* 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)
+@@ -2076,13 +2139,17 @@ ssl3_InitPendingContextsPKCS11(sslSocket
pwSpec->client.write_mac_context = NULL;
pwSpec->server.write_mac_context = NULL;
@@ -125,11 +133,10 @@ index 8be517c..53c29f0 100644
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[] = {
+diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
+--- a/nss/lib/ssl/ssl3ecc.c 2014-01-17 18:04:43.127747463 -0800
++++ b/nss/lib/ssl/ssl3ecc.c 2014-01-17 18:07:10.270188062 -0800
+@@ -904,6 +904,7 @@ static const ssl3CipherSuite ecdhe_ecdsa
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
@@ -137,7 +144,7 @@ index a3638e7..21a5e05 100644
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[] = {
+@@ -915,6 +916,7 @@ static const ssl3CipherSuite ecdhe_rsa_s
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
@@ -145,7 +152,7 @@ index a3638e7..21a5e05 100644
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[] = {
+@@ -927,6 +929,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,
@@ -153,7 +160,7 @@ index a3638e7..21a5e05 100644
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[] = {
+@@ -934,6 +937,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,
@@ -161,23 +168,37 @@ index a3638e7..21a5e05 100644
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 @@
+diff -pu a/nss/lib/ssl/sslenum.c b/nss/lib/ssl/sslenum.c
+--- a/nss/lib/ssl/sslenum.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslenum.c 2014-01-17 18:08:43.791739267 -0800
+@@ -37,17 +37,21 @@
+ *
+ * Exception: Because some servers ignore the high-order byte of the cipher
+ * suite ID, we must be careful about adding cipher suites with IDs larger
+- * than 0x00ff; see bug 946147. For these broken servers, the first four cipher
++ * than 0x00ff; see bug 946147. For these broken servers, the first six cipher
+ * suites, with the MSB zeroed, look like:
++ * TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA { 0x00,0x14 }
++ * TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA { 0x00,0x13 }
+ * TLS_KRB5_EXPORT_WITH_RC4_40_MD5 { 0x00,0x2B }
+ * TLS_RSA_WITH_AES_128_CBC_SHA { 0x00,0x2F }
+ * TLS_RSA_WITH_3DES_EDE_CBC_SHA { 0x00,0x0A }
+ * TLS_RSA_WITH_DES_CBC_SHA { 0x00,0x09 }
+- * The broken server only supports the third and fourth ones and will select
+- * the third one.
++ * The broken server only supports the fifth and sixth ones and will select
++ * the fifth one.
+ */
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
+ /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA must appear before
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:03:47.906831535 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:07:10.270188062 -0800
@@ -65,6 +65,7 @@ typedef SSLSignType SSL3SignType;
#define calg_camellia ssl_calg_camellia
#define calg_seed ssl_calg_seed
@@ -186,7 +207,7 @@ index 0fe12d0..e3ae9ce 100644
#define mac_null ssl_mac_null
#define mac_md5 ssl_mac_md5
-@@ -292,7 +293,7 @@ typedef struct {
+@@ -299,7 +300,7 @@ typedef struct {
} ssl3CipherSuiteCfg;
#ifdef NSS_ENABLE_ECC
@@ -195,7 +216,7 @@ index 0fe12d0..e3ae9ce 100644
#else
#define ssl_V3_SUITES_IMPLEMENTED 37
#endif /* NSS_ENABLE_ECC */
-@@ -474,6 +475,7 @@ typedef enum {
+@@ -483,6 +484,7 @@ typedef enum {
cipher_camellia_256,
cipher_seed,
cipher_aes_128_gcm,
@@ -203,11 +224,10 @@ index 0fe12d0..e3ae9ce 100644
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)
+diff -pu a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c
+--- a/nss/lib/ssl/sslinfo.c 2014-01-17 18:00:45.503806125 -0800
++++ b/nss/lib/ssl/sslinfo.c 2014-01-17 18:07:10.270188062 -0800
+@@ -110,6 +110,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLCh
#define C_NULL "NULL", calg_null
#define C_SJ "SKIPJACK", calg_sj
#define C_AESGCM "AES-GCM", calg_aes_gcm
@@ -215,7 +235,7 @@ index 9597209..bfc1676 100644
#define B_256 256, 256, 256
#define B_128 128, 128, 128
-@@ -196,12 +197,14 @@ static const SSLCipherSuiteInfo suiteInfo[] = {
+@@ -188,12 +189,14 @@ static const SSLCipherSuiteInfo suiteInf
{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, },
@@ -230,10 +250,9 @@ index 9597209..bfc1676 100644
{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
+diff -pu a/nss/lib/ssl/sslproto.h b/nss/lib/ssl/sslproto.h
+--- a/nss/lib/ssl/sslproto.h 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslproto.h 2014-01-17 18:07:10.270188062 -0800
@@ -213,6 +213,9 @@
#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031
@@ -244,30 +263,9 @@ index 53bba01..6b60a28 100644
/* 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
+diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
+--- a/nss/lib/ssl/sslt.h 2014-01-17 18:03:47.906831535 -0800
++++ b/nss/lib/ssl/sslt.h 2014-01-17 18:07:10.270188062 -0800
@@ -94,7 +94,8 @@ typedef enum {
ssl_calg_aes = 7,
ssl_calg_camellia = 8,
diff --git a/chromium/net/third_party/nss/patches/channelid.patch b/chromium/net/third_party/nss/patches/channelid.patch
index b2ab19d2831..670a6faf520 100644
--- a/chromium/net/third_party/nss/patches/channelid.patch
+++ b/chromium/net/third_party/nss/patches/channelid.patch
@@ -1,6 +1,6 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:45:11.497944276 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:51:32.663550380 -0700
+--- a/nss/lib/ssl/ssl3con.c 2014-01-18 10:39:50.799150460 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-18 10:40:15.489552270 -0800
@@ -55,6 +55,7 @@ static SECStatus ssl3_SendCertificateSta
static SECStatus ssl3_SendEmptyCertificate( sslSocket *ss);
static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
@@ -9,7 +9,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
static SECStatus ssl3_SendFinished( sslSocket *ss, PRInt32 flags);
static SECStatus ssl3_SendServerHello( sslSocket *ss);
static SECStatus ssl3_SendServerHelloDone( sslSocket *ss);
-@@ -5891,6 +5892,15 @@ ssl3_HandleServerHello(sslSocket *ss, SS
+@@ -6221,6 +6222,15 @@ ssl3_HandleServerHello(sslSocket *ss, SS
}
#endif /* NSS_PLATFORM_CLIENT_AUTH */
@@ -25,7 +25,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (temp < 0) {
goto loser; /* alert has been sent */
-@@ -6170,7 +6180,7 @@ ssl3_HandleServerHello(sslSocket *ss, SS
+@@ -6503,7 +6513,7 @@ ssl3_HandleServerHello(sslSocket *ss, SS
if (rv != SECSuccess) {
goto alert_loser; /* err code was set */
}
@@ -34,7 +34,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
} while (0);
if (sid_match)
-@@ -6196,6 +6206,27 @@ ssl3_HandleServerHello(sslSocket *ss, SS
+@@ -6529,6 +6539,27 @@ ssl3_HandleServerHello(sslSocket *ss, SS
ss->ssl3.hs.isResuming = PR_FALSE;
ss->ssl3.hs.ws = wait_server_cert;
@@ -62,28 +62,66 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
return SECSuccess;
alert_loser:
-@@ -6993,6 +7024,10 @@ ssl3_SendClientSecondRound(sslSocket *ss
+@@ -7490,7 +7521,14 @@ ssl3_SendClientSecondRound(sslSocket *ss
+ if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
- }
++ }
+
+ rv = ssl3_SendEncryptedExtensions(ss);
+ if (rv != SECSuccess) {
+ goto loser; /* err code was set. */
+ }
++
++ if (!ss->firstHsDone) {
+ if (ss->opt.enableFalseStart) {
+ if (!ss->ssl3.hs.authCertificatePending) {
+ /* When we fix bug 589047, we will need to know whether we are
+@@ -7527,6 +7565,33 @@ ssl3_SendClientSecondRound(sslSocket *ss
- rv = ssl3_SendFinished(ss, 0);
- if (rv != SECSuccess) {
-@@ -9947,6 +9982,165 @@ ssl3_RecordKeyLog(sslSocket *ss)
- return;
+ ssl_ReleaseXmitBufLock(ss); /*******************************/
+
++ if (!ss->ssl3.hs.isResuming &&
++ ssl3_ExtensionNegotiated(ss, ssl_channel_id_xtn)) {
++ /* If we are negotiating ChannelID on a full handshake then we record
++ * the handshake hashes in |sid| at this point. They will be needed in
++ * the event that we resume this session and use ChannelID on the
++ * resumption handshake. */
++ SSL3Hashes hashes;
++ SECItem *originalHandshakeHash =
++ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
++ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
++
++ ssl_GetSpecReadLock(ss);
++ PORT_Assert(ss->version > SSL_LIBRARY_VERSION_3_0);
++ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.cwSpec, &hashes, 0);
++ ssl_ReleaseSpecReadLock(ss);
++ if (rv != SECSuccess) {
++ return rv;
++ }
++
++ PORT_Assert(originalHandshakeHash->len == 0);
++ originalHandshakeHash->data = PORT_Alloc(hashes.len);
++ if (!originalHandshakeHash->data)
++ return SECFailure;
++ originalHandshakeHash->len = hashes.len;
++ memcpy(originalHandshakeHash->data, hashes.u.raw, hashes.len);
++ }
++
+ if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
+ ss->ssl3.hs.ws = wait_new_session_ticket;
+ else
+@@ -10494,6 +10559,184 @@ ssl3_RecordKeyLog(sslSocket *ss)
}
-+/* called from ssl3_SendClientSecondRound
+ /* called from ssl3_SendClientSecondRound
+ * ssl3_HandleFinished
+ */
+static SECStatus
+ssl3_SendEncryptedExtensions(sslSocket *ss)
+{
+ static const char CHANNEL_ID_MAGIC[] = "TLS Channel ID signature";
++ static const char CHANNEL_ID_RESUMPTION_MAGIC[] = "Resumption";
+ /* This is the ASN.1 prefix for a P-256 public key. Specifically it's:
+ * SEQUENCE
+ * SEQUENCE
@@ -109,7 +147,10 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+ SECItem *spki = NULL;
+ SSL3Hashes hashes;
+ const unsigned char *pub_bytes;
-+ unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) + sizeof(SSL3Hashes)];
++ unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) +
++ sizeof(CHANNEL_ID_RESUMPTION_MAGIC) +
++ sizeof(SSL3Hashes)*2];
++ size_t signed_data_len;
+ unsigned char digest[SHA256_LENGTH];
+ SECItem digest_item;
+ unsigned char signature[64];
@@ -159,11 +200,26 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+
+ pub_bytes = spki->data + sizeof(P256_SPKI_PREFIX);
+
-+ memcpy(signed_data, CHANNEL_ID_MAGIC, sizeof(CHANNEL_ID_MAGIC));
-+ memcpy(signed_data + sizeof(CHANNEL_ID_MAGIC), hashes.u.raw, hashes.len);
++ signed_data_len = 0;
++ memcpy(signed_data + signed_data_len, CHANNEL_ID_MAGIC,
++ sizeof(CHANNEL_ID_MAGIC));
++ signed_data_len += sizeof(CHANNEL_ID_MAGIC);
++ if (ss->ssl3.hs.isResuming) {
++ SECItem *originalHandshakeHash =
++ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
++ PORT_Assert(originalHandshakeHash->len > 0);
++
++ memcpy(signed_data + signed_data_len, CHANNEL_ID_RESUMPTION_MAGIC,
++ sizeof(CHANNEL_ID_RESUMPTION_MAGIC));
++ signed_data_len += sizeof(CHANNEL_ID_RESUMPTION_MAGIC);
++ memcpy(signed_data + signed_data_len, originalHandshakeHash->data,
++ originalHandshakeHash->len);
++ signed_data_len += originalHandshakeHash->len;
++ }
++ memcpy(signed_data + signed_data_len, hashes.u.raw, hashes.len);
++ signed_data_len += hashes.len;
+
-+ rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data,
-+ sizeof(CHANNEL_ID_MAGIC) + hashes.len);
++ rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data, signed_data_len);
+ if (rv != SECSuccess)
+ goto loser;
+
@@ -236,10 +292,11 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+ return SECSuccess;
+}
+
- /* called from ssl3_HandleServerHelloDone
++/* called from ssl3_SendClientSecondRound
* ssl3_HandleClientHello
* ssl3_HandleFinished
-@@ -10202,11 +10396,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
+ */
+@@ -10753,11 +10996,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER;
}
@@ -260,7 +317,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
}
if (IS_DTLS(ss)) {
-@@ -11635,6 +11834,11 @@ ssl3_DestroySSL3Info(sslSocket *ss)
+@@ -12237,6 +12485,11 @@ ssl3_DestroySSL3Info(sslSocket *ss)
ssl_FreePlatformKey(ss->ssl3.platformClientKey);
#endif /* NSS_PLATFORM_CLIENT_AUTH */
@@ -273,9 +330,9 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
ssl3_CleanupPeerCerts(ss);
diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
---- a/nss/lib/ssl/ssl3ext.c 2013-07-31 12:40:14.493586151 -0700
-+++ b/nss/lib/ssl/ssl3ext.c 2013-07-31 12:45:50.338515793 -0700
-@@ -60,6 +60,10 @@ static PRInt32 ssl3_SendUseSRTPXtn(sslSo
+--- a/nss/lib/ssl/ssl3ext.c 2014-01-18 10:39:50.749149654 -0800
++++ b/nss/lib/ssl/ssl3ext.c 2014-01-18 10:43:52.543083984 -0800
+@@ -64,6 +64,10 @@ static PRInt32 ssl3_SendUseSRTPXtn(sslSo
PRUint32 maxBytes);
static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
SECItem *data);
@@ -286,31 +343,26 @@ diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
static SECStatus ssl3_ServerSendStatusRequestXtn(sslSocket * ss,
PRBool append, PRUint32 maxBytes);
static SECStatus ssl3_ServerHandleStatusRequestXtn(sslSocket *ss,
-@@ -248,6 +252,7 @@ static const ssl3HelloExtensionHandler s
- { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+@@ -253,6 +257,7 @@ static const ssl3HelloExtensionHandler s
{ ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn },
+ { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
+ { ssl_channel_id_xtn, &ssl3_ClientHandleChannelIDXtn },
{ ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
{ -1, NULL }
};
-@@ -274,6 +279,7 @@ ssl3HelloExtensionSender clientHelloSend
- { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
+@@ -280,6 +285,7 @@ ssl3HelloExtensionSender clientHelloSend
{ ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
+ { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
+ { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
{ ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
{ ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
/* any extra entries will appear as { 0, NULL } */
-@@ -660,6 +666,52 @@ ssl3_ClientSendNextProtoNegoXtn(sslSocke
- }
+@@ -795,6 +801,61 @@ loser:
+ return -1;
+ }
- return extension_length;
-+
-+loser:
-+ return -1;
-+}
-+
+static SECStatus
+ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
@@ -339,6 +391,15 @@ diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
+ return 0;
+ }
+
++ if (ss->sec.ci.sid->cached != never_cached &&
++ ss->sec.ci.sid->u.ssl3.originalHandshakeHash.len == 0) {
++ /* We can't do ChannelID on a connection if we're resuming and didn't
++ * do ChannelID on the original connection: without ChannelID on the
++ * original connection we didn't record the handshake hashes needed for
++ * the signature. */
++ return 0;
++ }
++
+ if (append) {
+ SECStatus rv;
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_channel_id_xtn, 2);
@@ -352,12 +413,17 @@ diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
+ }
+
+ return extension_length;
-
- loser:
- return -1;
++
++loser:
++ return -1;
++}
++
+ static SECStatus
+ ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
diff -pu a/nss/lib/ssl/ssl3prot.h b/nss/lib/ssl/ssl3prot.h
---- a/nss/lib/ssl/ssl3prot.h 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/ssl3prot.h 2013-07-31 12:45:50.338515793 -0700
+--- a/nss/lib/ssl/ssl3prot.h 2014-01-18 10:39:34.278881614 -0800
++++ b/nss/lib/ssl/ssl3prot.h 2014-01-18 10:40:15.499552430 -0800
@@ -129,7 +129,8 @@ typedef enum {
client_key_exchange = 16,
finished = 20,
@@ -369,9 +435,9 @@ diff -pu a/nss/lib/ssl/ssl3prot.h b/nss/lib/ssl/ssl3prot.h
typedef struct {
diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c
---- a/nss/lib/ssl/sslauth.c 2013-07-31 12:40:14.503586299 -0700
-+++ b/nss/lib/ssl/sslauth.c 2013-07-31 12:45:50.338515793 -0700
-@@ -219,6 +219,24 @@ SSL_GetClientAuthDataHook(PRFileDesc *s,
+--- a/nss/lib/ssl/sslauth.c 2014-01-18 10:39:50.749149654 -0800
++++ b/nss/lib/ssl/sslauth.c 2014-01-18 10:40:15.499552430 -0800
+@@ -216,6 +216,24 @@ SSL_GetClientAuthDataHook(PRFileDesc *s,
return SECSuccess;
}
@@ -397,8 +463,8 @@ diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c
/* NEED LOCKS IN HERE. */
SECStatus
diff -pu a/nss/lib/ssl/sslerr.h b/nss/lib/ssl/sslerr.h
---- a/nss/lib/ssl/sslerr.h 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslerr.h 2013-07-31 12:45:50.338515793 -0700
+--- a/nss/lib/ssl/sslerr.h 2014-01-18 10:39:34.288881780 -0800
++++ b/nss/lib/ssl/sslerr.h 2014-01-18 10:40:15.499552430 -0800
@@ -193,6 +193,10 @@ SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM = (
SSL_ERROR_DIGEST_FAILURE = (SSL_ERROR_BASE + 127),
SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
@@ -411,8 +477,8 @@ diff -pu a/nss/lib/ssl/sslerr.h b/nss/lib/ssl/sslerr.h
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
diff -pu a/nss/lib/ssl/SSLerrs.h b/nss/lib/ssl/SSLerrs.h
---- a/nss/lib/ssl/SSLerrs.h 2013-07-31 12:07:10.964699464 -0700
-+++ b/nss/lib/ssl/SSLerrs.h 2013-07-31 12:45:50.338515793 -0700
+--- a/nss/lib/ssl/SSLerrs.h 2014-01-18 10:39:34.238880964 -0800
++++ b/nss/lib/ssl/SSLerrs.h 2014-01-18 10:40:15.499552430 -0800
@@ -412,3 +412,12 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR
ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
@@ -427,9 +493,9 @@ diff -pu a/nss/lib/ssl/SSLerrs.h b/nss/lib/ssl/SSLerrs.h
+ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131),
+"The application could not get a TLS Channel ID.")
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:45:11.497944276 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:45:50.338515793 -0700
-@@ -958,6 +958,34 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
+--- a/nss/lib/ssl/ssl.h 2014-01-18 10:39:50.799150460 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-18 10:40:15.499552430 -0800
+@@ -1015,6 +1015,34 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
SSL_IMPORT SECStatus SSL_HandshakeResumedSession(PRFileDesc *fd,
PRBool *last_handshake_resumed);
@@ -465,9 +531,24 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
** How long should we wait before retransmitting the next flight of
** the DTLS handshake? Returns SECFailure if not DTLS or not in a
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 12:45:11.497944276 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 12:45:50.338515793 -0700
-@@ -921,6 +921,9 @@ struct ssl3StateStr {
+--- a/nss/lib/ssl/sslimpl.h 2014-01-18 10:39:50.799150460 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-18 10:40:15.499552430 -0800
+@@ -709,6 +709,14 @@ struct sslSessionIDStr {
+
+ SECItem srvName;
+
++ /* originalHandshakeHash contains the hash of the original, full
++ * handshake prior to the server's final flow. This is either a
++ * SHA-1/MD5 combination (for TLS < 1.2) or the TLS PRF hash (for
++ * TLS 1.2). This is recorded and used only when ChannelID is
++ * negotiated as it's used to bind the ChannelID signature on the
++ * resumption handshake to the original handshake. */
++ SECItem originalHandshakeHash;
++
+ /* This lock is lazily initialized by CacheSID when a sid is first
+ * cached. Before then, there is no need to lock anything because
+ * the sid isn't being shared by anything.
+@@ -978,6 +986,9 @@ struct ssl3StateStr {
CERTCertificateList *clientCertChain; /* used by client */
PRBool sendEmptyCert; /* used by client */
@@ -477,7 +558,7 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
int policy;
/* This says what cipher suites we can do, and should
* be either SSL_ALLOWED or SSL_RESTRICTED
-@@ -1192,6 +1195,8 @@ const unsigned char * preferredCipher;
+@@ -1255,6 +1266,8 @@ const unsigned char * preferredCipher;
void *pkcs11PinArg;
SSLNextProtoCallback nextProtoCallback;
void *nextProtoArg;
@@ -486,7 +567,7 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
PRIntervalTime rTimeout; /* timeout for NSPR I/O */
PRIntervalTime wTimeout; /* timeout for NSPR I/O */
-@@ -1524,6 +1529,11 @@ extern SECStatus ssl3_RestartHandshakeAf
+@@ -1599,6 +1612,11 @@ extern SECStatus ssl3_RestartHandshakeAf
SECKEYPrivateKey * key,
CERTCertificateList *certChain);
@@ -498,10 +579,23 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error);
/*
+diff -pu a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
+--- a/nss/lib/ssl/sslnonce.c 2014-01-18 10:39:50.739149486 -0800
++++ b/nss/lib/ssl/sslnonce.c 2014-01-18 10:40:15.499552430 -0800
+@@ -180,6 +180,9 @@ ssl_DestroySID(sslSessionID *sid)
+ if (sid->u.ssl3.srvName.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+ }
++ if (sid->u.ssl3.originalHandshakeHash.data) {
++ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
++ }
+
+ if (sid->u.ssl3.lock) {
+ PR_DestroyRWLock(sid->u.ssl3.lock);
diff -pu a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c
---- a/nss/lib/ssl/sslsecur.c 2013-07-31 12:45:11.497944276 -0700
-+++ b/nss/lib/ssl/sslsecur.c 2013-07-31 12:45:50.338515793 -0700
-@@ -1502,6 +1502,42 @@ SSL_RestartHandshakeAfterCertReq(PRFileD
+--- a/nss/lib/ssl/sslsecur.c 2014-01-18 10:39:50.799150460 -0800
++++ b/nss/lib/ssl/sslsecur.c 2014-01-18 10:40:15.499552430 -0800
+@@ -1584,6 +1584,42 @@ SSL_RestartHandshakeAfterCertReq(PRFileD
return ret;
}
@@ -545,18 +639,18 @@ diff -pu a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c
* this implementation exists to maintain link-time compatibility.
*/
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 12:44:32.017363288 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 12:45:50.348515937 -0700
-@@ -354,6 +354,8 @@ ssl_DupSocket(sslSocket *os)
- ss->handshakeCallback = os->handshakeCallback;
- ss->handshakeCallbackData = os->handshakeCallbackData;
+--- a/nss/lib/ssl/sslsock.c 2014-01-18 10:39:50.769149984 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-18 10:40:15.499552430 -0800
+@@ -276,6 +276,8 @@ ssl_DupSocket(sslSocket *os)
+ ss->canFalseStartCallback = os->canFalseStartCallback;
+ ss->canFalseStartCallbackData = os->canFalseStartCallbackData;
ss->pkcs11PinArg = os->pkcs11PinArg;
+ ss->getChannelID = os->getChannelID;
+ ss->getChannelIDArg = os->getChannelIDArg;
/* Create security data */
rv = ssl_CopySecurityInfo(ss, os);
-@@ -1754,6 +1756,10 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
+@@ -1691,6 +1693,10 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
ss->handshakeCallbackData = sm->handshakeCallbackData;
if (sm->pkcs11PinArg)
ss->pkcs11PinArg = sm->pkcs11PinArg;
@@ -567,7 +661,7 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
return fd;
loser:
return NULL;
-@@ -3027,6 +3033,8 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
+@@ -2968,6 +2974,8 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
ss->badCertArg = NULL;
ss->pkcs11PinArg = NULL;
ss->ephemeralECDHKeyPair = NULL;
@@ -577,17 +671,18 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
ssl_ChooseOps(ss);
ssl2_InitSocketPolicy(ss);
diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
---- a/nss/lib/ssl/sslt.h 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslt.h 2013-07-31 12:45:50.348515937 -0700
-@@ -184,9 +184,10 @@ typedef enum {
- ssl_use_srtp_xtn = 14,
+--- a/nss/lib/ssl/sslt.h 2014-01-18 10:39:34.328882426 -0800
++++ b/nss/lib/ssl/sslt.h 2014-01-18 10:40:15.499552430 -0800
+@@ -190,10 +190,11 @@ typedef enum {
+ ssl_app_layer_protocol_xtn = 16,
ssl_session_ticket_xtn = 35,
ssl_next_proto_nego_xtn = 13172,
-+ ssl_channel_id_xtn = 30031,
++ ssl_channel_id_xtn = 30032,
+ ssl_padding_xtn = 35655,
ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
} SSLExtensionType;
--#define SSL_MAX_EXTENSIONS 9
-+#define SSL_MAX_EXTENSIONS 10
+-#define SSL_MAX_EXTENSIONS 10 /* doesn't include ssl_padding_xtn. */
++#define SSL_MAX_EXTENSIONS 11 /* doesn't include ssl_padding_xtn. */
#endif /* __sslt_h_ */
diff --git a/chromium/net/third_party/nss/patches/channelid2.patch b/chromium/net/third_party/nss/patches/channelid2.patch
deleted file mode 100644
index 1425c2a80b0..00000000000
--- a/chromium/net/third_party/nss/patches/channelid2.patch
+++ /dev/null
@@ -1,155 +0,0 @@
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index 882e356..396c408 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -7594,6 +7594,33 @@ ssl3_SendClientSecondRound(sslSocket *ss)
-
- ssl_ReleaseXmitBufLock(ss); /*******************************/
-
-+ if (!ss->ssl3.hs.isResuming &&
-+ ssl3_ExtensionNegotiated(ss, ssl_channel_id_xtn)) {
-+ /* If we are negotiating ChannelID on a full handshake then we record
-+ * the handshake hashes in |sid| at this point. They will be needed in
-+ * the event that we resume this session and use ChannelID on the
-+ * resumption handshake. */
-+ SSL3Hashes hashes;
-+ SECItem *originalHandshakeHash =
-+ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
-+ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
-+
-+ ssl_GetSpecReadLock(ss);
-+ PORT_Assert(ss->version > SSL_LIBRARY_VERSION_3_0);
-+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.cwSpec, &hashes, 0);
-+ ssl_ReleaseSpecReadLock(ss);
-+ if (rv != SECSuccess) {
-+ return rv;
-+ }
-+
-+ PORT_Assert(originalHandshakeHash->len == 0);
-+ originalHandshakeHash->data = PORT_Alloc(hashes.len);
-+ if (!originalHandshakeHash->data)
-+ return SECFailure;
-+ originalHandshakeHash->len = hashes.len;
-+ memcpy(originalHandshakeHash->data, hashes.u.raw, hashes.len);
-+ }
-+
- if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
- ss->ssl3.hs.ws = wait_new_session_ticket;
- else
-@@ -10590,6 +10617,7 @@ static SECStatus
- ssl3_SendEncryptedExtensions(sslSocket *ss)
- {
- static const char CHANNEL_ID_MAGIC[] = "TLS Channel ID signature";
-+ static const char CHANNEL_ID_RESUMPTION_MAGIC[] = "Resumption";
- /* This is the ASN.1 prefix for a P-256 public key. Specifically it's:
- * SEQUENCE
- * SEQUENCE
-@@ -10615,7 +10643,10 @@ ssl3_SendEncryptedExtensions(sslSocket *ss)
- SECItem *spki = NULL;
- SSL3Hashes hashes;
- const unsigned char *pub_bytes;
-- unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) + sizeof(SSL3Hashes)];
-+ unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) +
-+ sizeof(CHANNEL_ID_RESUMPTION_MAGIC) +
-+ sizeof(SSL3Hashes)*2];
-+ size_t signed_data_len;
- unsigned char digest[SHA256_LENGTH];
- SECItem digest_item;
- unsigned char signature[64];
-@@ -10665,11 +10696,26 @@ ssl3_SendEncryptedExtensions(sslSocket *ss)
-
- pub_bytes = spki->data + sizeof(P256_SPKI_PREFIX);
-
-- memcpy(signed_data, CHANNEL_ID_MAGIC, sizeof(CHANNEL_ID_MAGIC));
-- memcpy(signed_data + sizeof(CHANNEL_ID_MAGIC), hashes.u.raw, hashes.len);
-+ signed_data_len = 0;
-+ memcpy(signed_data + signed_data_len, CHANNEL_ID_MAGIC,
-+ sizeof(CHANNEL_ID_MAGIC));
-+ signed_data_len += sizeof(CHANNEL_ID_MAGIC);
-+ if (ss->ssl3.hs.isResuming) {
-+ SECItem *originalHandshakeHash =
-+ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
-+ PORT_Assert(originalHandshakeHash->len > 0);
-
-- rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data,
-- sizeof(CHANNEL_ID_MAGIC) + hashes.len);
-+ memcpy(signed_data + signed_data_len, CHANNEL_ID_RESUMPTION_MAGIC,
-+ sizeof(CHANNEL_ID_RESUMPTION_MAGIC));
-+ signed_data_len += sizeof(CHANNEL_ID_RESUMPTION_MAGIC);
-+ memcpy(signed_data + signed_data_len, originalHandshakeHash->data,
-+ originalHandshakeHash->len);
-+ signed_data_len += originalHandshakeHash->len;
-+ }
-+ memcpy(signed_data + signed_data_len, hashes.u.raw, hashes.len);
-+ signed_data_len += hashes.len;
-+
-+ rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data, signed_data_len);
- if (rv != SECSuccess)
- goto loser;
-
-diff --git a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
-index 03cf05c..166022c 100644
---- a/nss/lib/ssl/ssl3ext.c
-+++ b/nss/lib/ssl/ssl3ext.c
-@@ -812,6 +812,15 @@ ssl3_ClientSendChannelIDXtn(sslSocket * ss, PRBool append,
- return 0;
- }
-
-+ if (ss->sec.ci.sid->cached != never_cached &&
-+ ss->sec.ci.sid->u.ssl3.originalHandshakeHash.len == 0) {
-+ /* We can't do ChannelID on a connection if we're resuming and didn't
-+ * do ChannelID on the original connection: without ChannelID on the
-+ * original connection we didn't record the handshake hashes needed for
-+ * the signature. */
-+ return 0;
-+ }
-+
- if (append) {
- SECStatus rv;
- rv = ssl3_AppendHandshakeNumber(ss, ssl_channel_id_xtn, 2);
-diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
-index 9c789bf..ca68727 100644
---- a/nss/lib/ssl/sslimpl.h
-+++ b/nss/lib/ssl/sslimpl.h
-@@ -705,6 +705,14 @@ struct sslSessionIDStr {
- */
- NewSessionTicket sessionTicket;
- SECItem srvName;
-+
-+ /* originalHandshakeHash contains the hash of the original, full
-+ * handshake prior to the server's final flow. This is either a
-+ * SHA-1/MD5 combination (for TLS < 1.2) or the TLS PRF hash (for
-+ * TLS 1.2). This is recorded and used only when ChannelID is
-+ * negotiated as it's used to bind the ChannelID signature on the
-+ * resumption handshake to the original handshake. */
-+ SECItem originalHandshakeHash;
- } ssl3;
- } u;
- };
-diff --git a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
-index a6f7349..eb5004c 100644
---- a/nss/lib/ssl/sslnonce.c
-+++ b/nss/lib/ssl/sslnonce.c
-@@ -148,6 +148,9 @@ ssl_DestroySID(sslSessionID *sid)
- if (sid->u.ssl3.srvName.data) {
- SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
- }
-+ if (sid->u.ssl3.originalHandshakeHash.data) {
-+ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
-+ }
-
- PORT_ZFree(sid, sizeof(sslSessionID));
- }
-diff --git a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
-index e4d188f..b813c04 100644
---- a/nss/lib/ssl/sslt.h
-+++ b/nss/lib/ssl/sslt.h
-@@ -204,7 +204,7 @@ typedef enum {
- ssl_app_layer_protocol_xtn = 16,
- ssl_session_ticket_xtn = 35,
- ssl_next_proto_nego_xtn = 13172,
-- ssl_channel_id_xtn = 30031,
-+ ssl_channel_id_xtn = 30032,
- ssl_padding_xtn = 35655,
- ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
- } SSLExtensionType;
diff --git a/chromium/net/third_party/nss/patches/cipherorder.patch b/chromium/net/third_party/nss/patches/cipherorder.patch
index 16d4745dcd8..36f01919cb5 100644
--- a/chromium/net/third_party/nss/patches/cipherorder.patch
+++ b/chromium/net/third_party/nss/patches/cipherorder.patch
@@ -1,26 +1,7 @@
-diff --git a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
-index 4cf02aa..24627ed 100644
---- a/nss/lib/ssl/ssl.h
-+++ b/nss/lib/ssl/ssl.h
-@@ -265,6 +265,13 @@ SSL_IMPORT SECStatus SSL_CipherPrefGetDefault(PRInt32 cipher, PRBool *enabled);
- SSL_IMPORT SECStatus SSL_CipherPolicySet(PRInt32 cipher, PRInt32 policy);
- SSL_IMPORT SECStatus SSL_CipherPolicyGet(PRInt32 cipher, PRInt32 *policy);
-
-+/* SSL_CipherOrderSet sets the cipher suite preference order from |ciphers|,
-+ * which must be an array of cipher suite ids of length |len|. All the given
-+ * cipher suite ids must appear in the array that is returned by
-+ * |SSL_GetImplementedCiphers| and may only appear once, at most. */
-+SSL_IMPORT SECStatus SSL_CipherOrderSet(PRFileDesc *fd, const PRUint16 *ciphers,
-+ unsigned int len);
-+
- /* SSLChannelBindingType enumerates the types of supported channel binding
- * values. See RFC 5929. */
- typedef enum SSLChannelBindingType {
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index c2d9eeb..350d09c 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -12423,6 +12423,46 @@ ssl3_CipherPrefGet(sslSocket *ss, ssl3CipherSuite which, PRBool *enabled)
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:45:24.378132013 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:46:29.929216162 -0800
+@@ -12540,6 +12540,46 @@ ssl3_CipherPrefGet(sslSocket *ss, ssl3Ci
return rv;
}
@@ -67,11 +48,27 @@ index c2d9eeb..350d09c 100644
/* copy global default policy into socket. */
void
ssl3_InitSocketPolicy(sslSocket *ss)
-diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
-index 1e4655f..7521dba 100644
---- a/nss/lib/ssl/sslimpl.h
-+++ b/nss/lib/ssl/sslimpl.h
-@@ -1711,6 +1711,8 @@ extern SECStatus ssl3_CipherPrefSet(sslSocket *ss, ssl3CipherSuite which, PRBool
+diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
+--- a/nss/lib/ssl/ssl.h 2014-01-17 18:45:24.378132013 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 18:46:29.929216162 -0800
+@@ -285,6 +285,13 @@ SSL_IMPORT SECStatus SSL_CipherPrefGetDe
+ SSL_IMPORT SECStatus SSL_CipherPolicySet(PRInt32 cipher, PRInt32 policy);
+ SSL_IMPORT SECStatus SSL_CipherPolicyGet(PRInt32 cipher, PRInt32 *policy);
+
++/* SSL_CipherOrderSet sets the cipher suite preference order from |ciphers|,
++ * which must be an array of cipher suite ids of length |len|. All the given
++ * cipher suite ids must appear in the array that is returned by
++ * |SSL_GetImplementedCiphers| and may only appear once, at most. */
++SSL_IMPORT SECStatus SSL_CipherOrderSet(PRFileDesc *fd, const PRUint16 *ciphers,
++ unsigned int len);
++
+ /* SSLChannelBindingType enumerates the types of supported channel binding
+ * values. See RFC 5929. */
+ typedef enum SSLChannelBindingType {
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:45:24.378132013 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:46:29.929216162 -0800
+@@ -1743,6 +1743,8 @@ extern SECStatus ssl3_CipherPrefSet(sslS
extern SECStatus ssl3_CipherPrefGet(sslSocket *ss, ssl3CipherSuite which, PRBool *on);
extern SECStatus ssl2_CipherPrefSet(sslSocket *ss, PRInt32 which, PRBool enabled);
extern SECStatus ssl2_CipherPrefGet(sslSocket *ss, PRInt32 which, PRBool *enabled);
@@ -80,11 +77,10 @@ index 1e4655f..7521dba 100644
extern SECStatus ssl3_SetPolicy(ssl3CipherSuite which, PRInt32 policy);
extern SECStatus ssl3_GetPolicy(ssl3CipherSuite which, PRInt32 *policy);
-diff --git a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
-index 965215d..9f8286c 100644
---- a/nss/lib/ssl/sslsock.c
-+++ b/nss/lib/ssl/sslsock.c
-@@ -1344,6 +1344,19 @@ SSL_CipherPrefSet(PRFileDesc *fd, PRInt32 which, PRBool enabled)
+diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 18:45:24.378132013 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 18:46:29.929216162 -0800
+@@ -1278,6 +1278,19 @@ SSL_CipherPrefSet(PRFileDesc *fd, PRInt3
return rv;
}
diff --git a/chromium/net/third_party/nss/patches/ciphersuiteversion.patch b/chromium/net/third_party/nss/patches/ciphersuiteversion.patch
deleted file mode 100644
index 3967f17dcd6..00000000000
--- a/chromium/net/third_party/nss/patches/ciphersuiteversion.patch
+++ /dev/null
@@ -1,169 +0,0 @@
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index bc54c99..1245393 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -631,8 +631,9 @@ void SSL_AtomicIncrementLong(long * x)
- }
-
- static PRBool
--ssl3_CipherSuiteAllowedForVersion(ssl3CipherSuite cipherSuite,
-- SSL3ProtocolVersion version)
-+ssl3_CipherSuiteAllowedForVersionRange(
-+ ssl3CipherSuite cipherSuite,
-+ const SSLVersionRange *vrange)
- {
- switch (cipherSuite) {
- /* See RFC 4346 A.5. Export cipher suites must not be used in TLS 1.1 or
-@@ -649,7 +650,9 @@ ssl3_CipherSuiteAllowedForVersion(ssl3CipherSuite cipherSuite,
- * SSL_DH_ANON_EXPORT_WITH_RC4_40_MD5: never implemented
- * SSL_DH_ANON_EXPORT_WITH_DES40_CBC_SHA: never implemented
- */
-- return version <= SSL_LIBRARY_VERSION_TLS_1_0;
-+ return vrange->min <= SSL_LIBRARY_VERSION_TLS_1_0;
-+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
-+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
- case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
- case TLS_RSA_WITH_AES_256_CBC_SHA256:
- case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-@@ -661,7 +664,7 @@ ssl3_CipherSuiteAllowedForVersion(ssl3CipherSuite cipherSuite,
- case TLS_RSA_WITH_AES_128_CBC_SHA256:
- case TLS_RSA_WITH_AES_128_GCM_SHA256:
- case TLS_RSA_WITH_NULL_SHA256:
-- return version >= SSL_LIBRARY_VERSION_TLS_1_2;
-+ return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_2;
- default:
- return PR_TRUE;
- }
-@@ -804,7 +807,8 @@ ssl3_config_match_init(sslSocket *ss)
- }
-
-
--/* return PR_TRUE if suite matches policy and enabled state */
-+/* return PR_TRUE if suite matches policy, enabled state and is applicable to
-+ * the given version range. */
- /* It would be a REALLY BAD THING (tm) if we ever permitted the use
- ** of a cipher that was NOT_ALLOWED. So, if this is ever called with
- ** policy == SSL_NOT_ALLOWED, report no match.
-@@ -812,7 +816,8 @@ ssl3_config_match_init(sslSocket *ss)
- /* adjust suite enabled to the availability of a token that can do the
- * cipher suite. */
- static PRBool
--config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled)
-+config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled,
-+ const SSLVersionRange *vrange)
- {
- PORT_Assert(policy != SSL_NOT_ALLOWED && enabled != PR_FALSE);
- if (policy == SSL_NOT_ALLOWED || !enabled)
-@@ -820,10 +825,13 @@ config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled)
- return (PRBool)(suite->enabled &&
- suite->isPresent &&
- suite->policy != SSL_NOT_ALLOWED &&
-- suite->policy <= policy);
-+ suite->policy <= policy &&
-+ ssl3_CipherSuiteAllowedForVersionRange(
-+ suite->cipher_suite, vrange));
- }
-
--/* return number of cipher suites that match policy and enabled state */
-+/* return number of cipher suites that match policy, enabled state and are
-+ * applicable for the configured protocol version range. */
- /* called from ssl3_SendClientHello and ssl3_ConstructV2CipherSpecsHack */
- static int
- count_cipher_suites(sslSocket *ss, int policy, PRBool enabled)
-@@ -834,7 +842,7 @@ count_cipher_suites(sslSocket *ss, int policy, PRBool enabled)
- return 0;
- }
- for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
-- if (config_match(&ss->cipherSuites[i], policy, enabled))
-+ if (config_match(&ss->cipherSuites[i], policy, enabled, &ss->vrange))
- count++;
- }
- if (count <= 0) {
-@@ -5294,7 +5302,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- }
- for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
-- if (config_match(suite, ss->ssl3.policy, PR_TRUE)) {
-+ if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
- actual_count++;
- if (actual_count > num_suites) {
- /* set error card removal/insertion error */
-@@ -6359,15 +6367,19 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
- for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
- if (temp == suite->cipher_suite) {
-- if (!config_match(suite, ss->ssl3.policy, PR_TRUE)) {
-+ SSLVersionRange vrange = {ss->version, ss->version};
-+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
-+ /* config_match already checks whether the cipher suite is
-+ * acceptable for the version, but the check is repeated here
-+ * in order to give a more precise error code. */
-+ if (!ssl3_CipherSuiteAllowedForVersionRange(temp, &vrange)) {
-+ desc = handshake_failure;
-+ errCode = SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION;
-+ goto alert_loser;
-+ }
-+
- break; /* failure */
- }
-- if (!ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
-- ss->version)) {
-- desc = handshake_failure;
-- errCode = SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION;
-- goto alert_loser;
-- }
-
- suite_found = PR_TRUE;
- break; /* success */
-@@ -8008,6 +8020,9 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
- */
- if (sid) do {
- ssl3CipherSuiteCfg *suite;
-+#ifdef PARANOID
-+ SSLVersionRange vrange = {ss->version, ss->version};
-+#endif
-
- /* Check that the cached compression method is still enabled. */
- if (!compressionEnabled(ss, sid->u.ssl3.compression))
-@@ -8036,7 +8051,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
- * The product policy won't change during the process lifetime.
- * Implemented ("isPresent") shouldn't change for servers.
- */
-- if (!config_match(suite, ss->ssl3.policy, PR_TRUE))
-+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange))
- break;
- #else
- if (!suite->enabled)
-@@ -8084,9 +8099,8 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
- */
- for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
-- if (!config_match(suite, ss->ssl3.policy, PR_TRUE) ||
-- !ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
-- ss->version)) {
-+ SSLVersionRange vrange = {ss->version, ss->version};
-+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
- continue;
- }
- for (i = 0; i + 1 < suites.len; i += 2) {
-@@ -8619,9 +8633,8 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length)
- */
- for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
-- if (!config_match(suite, ss->ssl3.policy, PR_TRUE) ||
-- !ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
-- ss->version)) {
-+ SSLVersionRange vrange = {ss->version, ss->version};
-+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
- continue;
- }
- for (i = 0; i+2 < suite_length; i += 3) {
-@@ -12324,7 +12337,7 @@ ssl3_ConstructV2CipherSpecsHack(sslSocket *ss, unsigned char *cs, int *size)
- /* ssl3_config_match_init was called by the caller of this function. */
- for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
-- if (config_match(suite, SSL_ALLOWED, PR_TRUE)) {
-+ if (config_match(suite, SSL_ALLOWED, PR_TRUE, &ss->vrange)) {
- if (cs != NULL) {
- *cs++ = 0x00;
- *cs++ = (suite->cipher_suite >> 8) & 0xFF;
diff --git a/chromium/net/third_party/nss/patches/clientauth.patch b/chromium/net/third_party/nss/patches/clientauth.patch
index a2c7299c37a..92836763bc5 100644
--- a/chromium/net/third_party/nss/patches/clientauth.patch
+++ b/chromium/net/third_party/nss/patches/clientauth.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:31:45.326118409 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:35:27.189373289 -0700
-@@ -2284,6 +2284,9 @@ ssl3_ClientAuthTokenPresent(sslSessionID
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:52:00.295082288 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:52:19.745405758 -0800
+@@ -2471,6 +2471,9 @@ ssl3_ClientAuthTokenPresent(sslSessionID
PRBool isPresent = PR_TRUE;
/* we only care if we are doing client auth */
@@ -11,7 +11,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (!sid || !sid->u.ssl3.clAuthValid) {
return PR_TRUE;
}
-@@ -5768,25 +5771,36 @@ ssl3_SendCertificateVerify(sslSocket *ss
+@@ -6103,25 +6106,36 @@ ssl3_SendCertificateVerify(sslSocket *ss
isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
@@ -65,7 +65,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (rv != SECSuccess) {
goto done; /* err code was set by ssl3_SignHashes */
}
-@@ -5870,6 +5884,12 @@ ssl3_HandleServerHello(sslSocket *ss, SS
+@@ -6200,6 +6214,12 @@ ssl3_HandleServerHello(sslSocket *ss, SS
SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
ss->ssl3.clientPrivateKey = NULL;
}
@@ -78,7 +78,26 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (temp < 0) {
-@@ -6496,6 +6516,10 @@ ssl3_HandleCertificateRequest(sslSocket
+@@ -6827,6 +6847,18 @@ ssl3_ExtractClientKeyInfo(sslSocket *ss,
+ goto done;
+ }
+
++#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32)
++ /* If the key is in CAPI, assume conservatively that the CAPI service
++ * provider may be unable to sign SHA-256 hashes.
++ */
++ if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
++ /* CAPI only supports RSA and DSA signatures, so we don't need to
++ * check the key type. */
++ *preferSha1 = PR_TRUE;
++ goto done;
++ }
++#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */
++
+ /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
+ * it may be unable to sign SHA-256 hashes. This is the case for older
+ * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
+@@ -6925,6 +6957,10 @@ ssl3_HandleCertificateRequest(sslSocket
SECItem cert_types = {siBuffer, NULL, 0};
SECItem algorithms = {siBuffer, NULL, 0};
CERTDistNames ca_list;
@@ -89,7 +108,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_request handshake",
SSL_GETPID(), ss->fd));
-@@ -6512,6 +6536,7 @@ ssl3_HandleCertificateRequest(sslSocket
+@@ -6941,6 +6977,7 @@ ssl3_HandleCertificateRequest(sslSocket
PORT_Assert(ss->ssl3.clientCertChain == NULL);
PORT_Assert(ss->ssl3.clientCertificate == NULL);
PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
@@ -97,7 +116,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
-@@ -6591,6 +6616,18 @@ ssl3_HandleCertificateRequest(sslSocket
+@@ -7020,6 +7057,18 @@ ssl3_HandleCertificateRequest(sslSocket
desc = no_certificate;
ss->ssl3.hs.ws = wait_hello_done;
@@ -116,7 +135,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (ss->getClientAuthData != NULL) {
/* XXX Should pass cert_types and algorithms in this call!! */
rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg,
-@@ -6600,12 +6637,52 @@ ssl3_HandleCertificateRequest(sslSocket
+@@ -7029,12 +7078,55 @@ ssl3_HandleCertificateRequest(sslSocket
} else {
rv = SECFailure; /* force it to send a no_certificate alert */
}
@@ -163,13 +182,16 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+ }
+ goto send_no_certificate;
+ }
++ if (ss->ssl3.hs.hashType == handshake_hash_single) {
++ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
++ }
+ break; /* not an error */
+ }
+#endif /* NSS_PLATFORM_CLIENT_AUTH */
/* check what the callback function returned */
if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) {
/* we are missing either the key or cert */
-@@ -6668,6 +6745,10 @@ loser:
+@@ -7096,6 +7188,10 @@ loser:
done:
if (arena != NULL)
PORT_FreeArena(arena, PR_FALSE);
@@ -180,7 +202,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
return rv;
}
-@@ -6749,7 +6830,8 @@ ssl3_SendClientSecondRound(sslSocket *ss
+@@ -7213,7 +7309,8 @@ ssl3_SendClientSecondRound(sslSocket *ss
sendClientCert = !ss->ssl3.sendEmptyCert &&
ss->ssl3.clientCertChain != NULL &&
@@ -188,9 +210,9 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+ (ss->ssl3.platformClientKey ||
+ ss->ssl3.clientPrivateKey != NULL);
- /* We must wait for the server's certificate to be authenticated before
- * sending the client certificate in order to disclosing the client
-@@ -11465,6 +11547,10 @@ ssl3_DestroySSL3Info(sslSocket *ss)
+ if (!sendClientCert &&
+ ss->ssl3.hs.hashType == handshake_hash_single &&
+@@ -12052,6 +12149,10 @@ ssl3_DestroySSL3Info(sslSocket *ss)
if (ss->ssl3.clientPrivateKey != NULL)
SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
@@ -202,8 +224,8 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (ss->ssl3.peerCertArena != NULL)
ssl3_CleanupPeerCerts(ss);
diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
---- a/nss/lib/ssl/ssl3ext.c 2013-07-31 12:07:10.964699464 -0700
-+++ b/nss/lib/ssl/ssl3ext.c 2013-07-31 12:35:27.189373289 -0700
+--- a/nss/lib/ssl/ssl3ext.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/ssl3ext.c 2014-01-17 17:52:19.745405758 -0800
@@ -10,8 +10,8 @@
#include "nssrenam.h"
#include "nss.h"
@@ -215,9 +237,9 @@ diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
#ifdef NO_PKCS11_BYPASS
#include "blapit.h"
diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c
---- a/nss/lib/ssl/sslauth.c 2013-07-31 12:32:29.076760372 -0700
-+++ b/nss/lib/ssl/sslauth.c 2013-07-31 12:35:27.189373289 -0700
-@@ -219,6 +219,28 @@ SSL_GetClientAuthDataHook(PRFileDesc *s,
+--- a/nss/lib/ssl/sslauth.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslauth.c 2014-01-17 17:52:19.755405924 -0800
+@@ -216,6 +216,28 @@ SSL_GetClientAuthDataHook(PRFileDesc *s,
return SECSuccess;
}
@@ -247,9 +269,9 @@ diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c
SECStatus
SSL_SetPKCS11PinArg(PRFileDesc *s, void *arg)
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:32:29.076760372 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:35:27.199373436 -0700
-@@ -503,6 +503,48 @@ typedef SECStatus (PR_CALLBACK *SSLGetCl
+--- a/nss/lib/ssl/ssl.h 2014-01-17 17:49:26.062517203 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 17:52:19.755405924 -0800
+@@ -533,6 +533,48 @@ typedef SECStatus (PR_CALLBACK *SSLGetCl
SSL_IMPORT SECStatus SSL_GetClientAuthDataHook(PRFileDesc *fd,
SSLGetClientAuthData f, void *a);
@@ -299,8 +321,8 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
/*
** SNI extension processing callback function.
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 12:31:45.326118409 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 12:35:27.199373436 -0700
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:52:00.295082288 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:52:19.755405924 -0800
@@ -20,6 +20,7 @@
#include "sslerr.h"
#include "ssl3prot.h"
@@ -325,9 +347,9 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
/* to make some of these old enums public without namespace pollution,
** it was necessary to prepend ssl_ to the names.
** These #defines preserve compatibility with the old code here in libssl.
-@@ -444,6 +454,14 @@ typedef SECStatus (*SSLCompressor)(void
- int inlen);
- typedef SECStatus (*SSLDestroy)(void *context, PRBool freeit);
+@@ -441,6 +451,14 @@ struct sslGatherStr {
+ #define GS_DATA 3
+ #define GS_PAD 4
+#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(XP_WIN32)
+typedef PCERT_KEY_CONTEXT PlatformKey;
@@ -340,7 +362,7 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
/*
-@@ -896,6 +914,10 @@ struct ssl3StateStr {
+@@ -953,6 +971,10 @@ struct ssl3StateStr {
CERTCertificate * clientCertificate; /* used by client */
SECKEYPrivateKey * clientPrivateKey; /* used by client */
@@ -351,7 +373,7 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
CERTCertificateList *clientCertChain; /* used by client */
PRBool sendEmptyCert; /* used by client */
-@@ -1153,6 +1175,10 @@ const unsigned char * preferredCipher;
+@@ -1214,6 +1236,10 @@ const unsigned char * preferredCipher;
void *authCertificateArg;
SSLGetClientAuthData getClientAuthData;
void *getClientAuthDataArg;
@@ -362,15 +384,7 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
SSLSNISocketConfig sniSocketConfig;
void *sniSocketConfigArg;
SSLBadCertHandler handleBadCert;
-@@ -1737,7 +1763,6 @@ extern void ssl_FreePRSocket(PRFileDesc
- * various ciphers */
- extern int ssl3_config_match_init(sslSocket *);
-
--
- /* Create a new ref counted key pair object from two keys. */
- extern ssl3KeyPair * ssl3_NewKeyPair( SECKEYPrivateKey * privKey,
- SECKEYPublicKey * pubKey);
-@@ -1777,6 +1802,26 @@ extern SECStatus ssl_InitSessionCacheLoc
+@@ -1852,6 +1878,26 @@ extern SECStatus ssl_InitSessionCacheLoc
extern SECStatus ssl_FreeSessionCacheLocks(void);
@@ -398,9 +412,9 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
/**************** DTLS-specific functions **************/
extern void dtls_FreeQueuedMessage(DTLSQueuedMessage *msg);
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 12:28:39.283413269 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 12:35:27.199373436 -0700
-@@ -343,6 +343,10 @@ ssl_DupSocket(sslSocket *os)
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 17:49:40.942764689 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 17:52:19.755405924 -0800
+@@ -263,6 +263,10 @@ ssl_DupSocket(sslSocket *os)
ss->authCertificateArg = os->authCertificateArg;
ss->getClientAuthData = os->getClientAuthData;
ss->getClientAuthDataArg = os->getClientAuthDataArg;
@@ -411,7 +425,7 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
ss->sniSocketConfig = os->sniSocketConfig;
ss->sniSocketConfigArg = os->sniSocketConfigArg;
ss->handleBadCert = os->handleBadCert;
-@@ -1730,6 +1734,12 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
+@@ -1667,6 +1671,12 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
ss->getClientAuthData = sm->getClientAuthData;
if (sm->getClientAuthDataArg)
ss->getClientAuthDataArg = sm->getClientAuthDataArg;
@@ -424,7 +438,7 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
if (sm->sniSocketConfig)
ss->sniSocketConfig = sm->sniSocketConfig;
if (sm->sniSocketConfigArg)
-@@ -2980,6 +2990,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
+@@ -2921,6 +2931,10 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
ss->sniSocketConfig = NULL;
ss->sniSocketConfigArg = NULL;
ss->getClientAuthData = NULL;
diff --git a/chromium/net/third_party/nss/patches/didhandshakeresume.patch b/chromium/net/third_party/nss/patches/didhandshakeresume.patch
index fa681ebb3d8..70f878dc68e 100644
--- a/chromium/net/third_party/nss/patches/didhandshakeresume.patch
+++ b/chromium/net/third_party/nss/patches/didhandshakeresume.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:40:14.503586299 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:40:37.913929474 -0700
-@@ -940,6 +940,9 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
+--- a/nss/lib/ssl/ssl.h 2014-01-17 17:52:46.715854283 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 17:53:20.876422375 -0800
+@@ -997,6 +997,9 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
SSLExtensionType extId,
PRBool *yes);
@@ -12,9 +12,9 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
** How long should we wait before retransmitting the next flight of
** the DTLS handshake? Returns SECFailure if not DTLS or not in a
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 12:40:14.503586299 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 12:40:37.913929474 -0700
-@@ -1919,6 +1919,20 @@ SSL_PeerStapledOCSPResponses(PRFileDesc
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 17:52:46.715854283 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 17:53:20.876422375 -0800
+@@ -1855,6 +1855,20 @@ SSL_PeerStapledOCSPResponses(PRFileDesc
return &ss->sec.ci.sid->peerCertStatus;
}
diff --git a/chromium/net/third_party/nss/patches/disableticketrenewal.patch b/chromium/net/third_party/nss/patches/disableticketrenewal.patch
deleted file mode 100644
index ec40174bc3f..00000000000
--- a/chromium/net/third_party/nss/patches/disableticketrenewal.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-Index: net/third_party/nss/ssl/sslnonce.c
-===================================================================
---- net/third_party/nss/ssl/sslnonce.c (revision 240143)
-+++ net/third_party/nss/ssl/sslnonce.c (working copy)
-@@ -438,6 +438,12 @@
- /* We need to lock the cache, as this sid might already be in the cache. */
- LOCK_CACHE;
-
-+ /* Don't modify sid if it has ever been cached. */
-+ if (sid->cached != never_cached) {
-+ UNLOCK_CACHE;
-+ return SECSuccess;
-+ }
-+
- /* A server might have sent us an empty ticket, which has the
- * effect of clearing the previously known ticket.
- */
diff --git a/chromium/net/third_party/nss/patches/ecpointform.patch b/chromium/net/third_party/nss/patches/ecpointform.patch
deleted file mode 100644
index b63c58db289..00000000000
--- a/chromium/net/third_party/nss/patches/ecpointform.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
---- a/nss/lib/ssl/ssl3ecc.c 2013-07-31 12:07:10.964699464 -0700
-+++ b/nss/lib/ssl/ssl3ecc.c 2013-07-31 14:10:54.313607174 -0700
-@@ -32,6 +32,15 @@
-
- #ifdef NSS_ENABLE_ECC
-
-+/*
-+ * In NSS 3.13.2 the definition of the EC_POINT_FORM_UNCOMPRESSED macro
-+ * was moved from the internal header ec.h to the public header blapit.h.
-+ * Define the macro here when compiling against older system NSS headers.
-+ */
-+#ifndef EC_POINT_FORM_UNCOMPRESSED
-+#define EC_POINT_FORM_UNCOMPRESSED 0x04
-+#endif
-+
- #ifndef PK11_SETATTRS
- #define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
- (x)->pValue=(v); (x)->ulValueLen = (l);
diff --git a/chromium/net/third_party/nss/patches/fallbackscsv.patch b/chromium/net/third_party/nss/patches/fallbackscsv.patch
index f8acb2575d8..005132c59e6 100644
--- a/chromium/net/third_party/nss/patches/fallbackscsv.patch
+++ b/chromium/net/third_party/nss/patches/fallbackscsv.patch
@@ -1,34 +1,7 @@
-diff --git a/nss/lib/ssl/SSLerrs.h b/nss/lib/ssl/SSLerrs.h
-index c0d26cc..4ff0b7d 100644
---- a/nss/lib/ssl/SSLerrs.h
-+++ b/nss/lib/ssl/SSLerrs.h
-@@ -421,3 +421,8 @@ ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (SSL_ERROR_BASE + 130),
-
- ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131),
- "The application could not get a TLS Channel ID.")
-+
-+ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 132),
-+"The connection was using a lesser TLS version as a result of a previous"
-+" handshake failure, but the server indicated that it should not have been"
-+" needed.")
-diff --git a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
-index 24627ed..067938c 100644
---- a/nss/lib/ssl/ssl.h
-+++ b/nss/lib/ssl/ssl.h
-@@ -163,6 +163,8 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
- #define SSL_ENABLE_OCSP_STAPLING 24 /* Request OCSP stapling (client) */
- /* Request Signed Certificate Timestamps via TLS extension (client) */
- #define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 25
-+#define SSL_ENABLE_FALLBACK_SCSV 26 /* Send fallback SCSV in
-+ * handshakes. */
-
- #ifdef SSL_DEPRECATED_FUNCTION
- /* Old deprecated function names */
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index cf7ef32..946f780 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -3469,6 +3469,9 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:46:51.999581198 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:47:05.509804656 -0800
+@@ -3473,6 +3473,9 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe
case certificate_unknown: error = SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT;
break;
case illegal_parameter: error = SSL_ERROR_ILLEGAL_PARAMETER_ALERT;break;
@@ -38,19 +11,26 @@ index cf7ef32..946f780 100644
/* All alerts below are TLS only. */
case unknown_ca: error = SSL_ERROR_UNKNOWN_CA_ALERT; break;
-@@ -4973,7 +4976,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
+@@ -4986,6 +4989,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
int num_suites;
int actual_count = 0;
PRBool isTLS = PR_FALSE;
-- PRBool requestingResume = PR_FALSE;
+ PRBool requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE;
PRInt32 total_exten_len = 0;
unsigned paddingExtensionLen;
unsigned numCompressionMethods;
-@@ -5223,8 +5226,15 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
- if (!num_suites)
+@@ -5128,6 +5132,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ }
+
+ if (sid) {
++ requestingResume = PR_TRUE;
+ SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
+
+ PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
+@@ -5246,8 +5251,15 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return SECFailure; /* count_cipher_suites has set error code. */
+ }
+
+ fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume ||
+ ss->version < sid->version);
@@ -64,7 +44,7 @@ index cf7ef32..946f780 100644
}
/* count compression methods */
-@@ -5322,6 +5332,14 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
+@@ -5353,6 +5365,15 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
}
actual_count++;
}
@@ -72,6 +52,7 @@ index cf7ef32..946f780 100644
+ rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
++ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ actual_count++;
@@ -79,7 +60,7 @@ index cf7ef32..946f780 100644
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
-@@ -8037,6 +8055,19 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+@@ -8084,6 +8105,19 @@ ssl3_HandleClientHello(sslSocket *ss, SS
goto loser; /* malformed */
}
@@ -99,10 +80,9 @@ index cf7ef32..946f780 100644
/* grab the list of compression methods. */
rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length);
if (rv != SECSuccess) {
-diff --git a/nss/lib/ssl/ssl3prot.h b/nss/lib/ssl/ssl3prot.h
-index 0eab970..4c19ade 100644
---- a/nss/lib/ssl/ssl3prot.h
-+++ b/nss/lib/ssl/ssl3prot.h
+diff -pu a/nss/lib/ssl/ssl3prot.h b/nss/lib/ssl/ssl3prot.h
+--- a/nss/lib/ssl/ssl3prot.h 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/ssl3prot.h 2014-01-17 18:47:05.509804656 -0800
@@ -98,6 +98,7 @@ typedef enum {
protocol_version = 70,
insufficient_security = 71,
@@ -111,11 +91,10 @@ index 0eab970..4c19ade 100644
user_canceled = 90,
no_renegotiation = 100,
-diff --git a/nss/lib/ssl/sslerr.h b/nss/lib/ssl/sslerr.h
-index 5a949c9..82ae7df 100644
---- a/nss/lib/ssl/sslerr.h
-+++ b/nss/lib/ssl/sslerr.h
-@@ -196,6 +196,7 @@ SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
+diff -pu a/nss/lib/ssl/sslerr.h b/nss/lib/ssl/sslerr.h
+--- a/nss/lib/ssl/sslerr.h 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/sslerr.h 2014-01-17 18:47:05.509804656 -0800
+@@ -196,6 +196,7 @@ SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM
SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 129),
SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 130),
SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 131),
@@ -123,22 +102,44 @@ index 5a949c9..82ae7df 100644
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
-diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
-index 7521dba..6d0bc15 100644
---- a/nss/lib/ssl/sslimpl.h
-+++ b/nss/lib/ssl/sslimpl.h
-@@ -336,6 +336,7 @@ typedef struct sslOptionsStr {
- unsigned int cbcRandomIV : 1; /* 24 */
- unsigned int enableOCSPStapling : 1; /* 25 */
- unsigned int enableSignedCertTimestamps : 1; /* 26 */
-+ unsigned int enableFallbackSCSV : 1; /* 27 */
+diff -pu a/nss/lib/ssl/SSLerrs.h b/nss/lib/ssl/SSLerrs.h
+--- a/nss/lib/ssl/SSLerrs.h 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/SSLerrs.h 2014-01-17 18:47:05.509804656 -0800
+@@ -421,3 +421,8 @@ ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (S
+
+ ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131),
+ "The application could not get a TLS Channel ID.")
++
++ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 132),
++"The connection was using a lesser TLS version as a result of a previous"
++" handshake failure, but the server indicated that it should not have been"
++" needed.")
+diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
+--- a/nss/lib/ssl/ssl.h 2014-01-17 18:46:51.999581198 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 18:48:54.971613341 -0800
+@@ -183,6 +183,8 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
+
+ /* Request Signed Certificate Timestamps via TLS extension (client) */
+ #define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 27
++#define SSL_ENABLE_FALLBACK_SCSV 28 /* Send fallback SCSV in
++ * handshakes. */
+
+ #ifdef SSL_DEPRECATED_FUNCTION
+ /* Old deprecated function names */
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:46:51.999581198 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:51:17.963962287 -0800
+@@ -338,6 +338,7 @@ typedef struct sslOptionsStr {
+ unsigned int enableNPN : 1; /* 26 */
+ unsigned int enableALPN : 1; /* 27 */
+ unsigned int enableSignedCertTimestamps : 1; /* 28 */
++ unsigned int enableFallbackSCSV : 1; /* 29 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
-diff --git a/nss/lib/ssl/sslproto.h b/nss/lib/ssl/sslproto.h
-index 6b60a28..621ef37 100644
---- a/nss/lib/ssl/sslproto.h
-+++ b/nss/lib/ssl/sslproto.h
+diff -pu a/nss/lib/ssl/sslproto.h b/nss/lib/ssl/sslproto.h
+--- a/nss/lib/ssl/sslproto.h 2014-01-17 18:10:16.793281867 -0800
++++ b/nss/lib/ssl/sslproto.h 2014-01-17 18:47:05.509804656 -0800
@@ -172,6 +172,11 @@
*/
#define TLS_EMPTY_RENEGOTIATION_INFO_SCSV 0x00FF
@@ -151,23 +152,22 @@ index 6b60a28..621ef37 100644
/* Cipher Suite Values starting with 0xC000 are defined in informational
* RFCs.
*/
-diff --git a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
-index 9f8286c..f7d44d4 100644
---- a/nss/lib/ssl/sslsock.c
-+++ b/nss/lib/ssl/sslsock.c
-@@ -174,7 +174,8 @@ static sslOptions ssl_defaults = {
- PR_FALSE, /* enableFalseStart */
- PR_TRUE, /* cbcRandomIV */
+diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 18:46:52.009581364 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 18:59:17.931852364 -0800
+@@ -88,7 +88,8 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableOCSPStapling */
+ PR_TRUE, /* enableNPN */
+ PR_FALSE, /* enableALPN */
- PR_FALSE /* enableSignedCertTimestamps */
+ PR_FALSE, /* enableSignedCertTimestamps */
+ PR_FALSE /* enableFallbackSCSV */
};
/*
-@@ -870,6 +871,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
- ss->opt.enableSignedCertTimestamps = on;
- break;
+@@ -792,6 +793,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
+ ss->opt.enableSignedCertTimestamps = on;
+ break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ss->opt.enableFallbackSCSV = on;
@@ -176,27 +176,27 @@ index 9f8286c..f7d44d4 100644
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
-@@ -943,6 +948,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
+@@ -867,6 +872,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ss->opt.enableSignedCertTimestamps;
- break;
+ on = ss->opt.enableSignedCertTimestamps;
+ break;
+ case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -1007,6 +1013,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
+@@ -933,6 +939,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ssl_defaults.enableSignedCertTimestamps;
- break;
+ on = ssl_defaults.enableSignedCertTimestamps;
+ break;
+ case SSL_ENABLE_FALLBACK_SCSV:
-+ on = ssl_defaults.enableFallbackSCSV;
-+ break;
++ on = ssl_defaults.enableFallbackSCSV;
++ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -1178,6 +1187,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
- ssl_defaults.enableSignedCertTimestamps = on;
- break;
+@@ -1112,6 +1121,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
+ ssl_defaults.enableSignedCertTimestamps = on;
+ break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ssl_defaults.enableFallbackSCSV = on;
diff --git a/chromium/net/third_party/nss/patches/getrequestedclientcerttypes.patch b/chromium/net/third_party/nss/patches/getrequestedclientcerttypes.patch
index 7b8cd061e2a..9ca8abd44aa 100644
--- a/chromium/net/third_party/nss/patches/getrequestedclientcerttypes.patch
+++ b/chromium/net/third_party/nss/patches/getrequestedclientcerttypes.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:40:14.493586151 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:42:42.035748760 -0700
-@@ -6544,6 +6544,9 @@ ssl3_HandleCertificateRequest(sslSocket
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:52:46.705854118 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:54:27.087523439 -0800
+@@ -6985,6 +6985,9 @@ ssl3_HandleCertificateRequest(sslSocket
if (rv != SECSuccess)
goto loser; /* malformed, alert has been sent */
@@ -11,7 +11,7 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
if (isTLS12) {
rv = ssl3_ConsumeHandshakeVariable(ss, &algorithms, 2, &b, &length);
if (rv != SECSuccess)
-@@ -6743,6 +6746,7 @@ loser:
+@@ -7186,6 +7189,7 @@ loser:
PORT_SetError(errCode);
rv = SECFailure;
done:
@@ -20,9 +20,9 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
PORT_FreeArena(arena, PR_FALSE);
#ifdef NSS_PLATFORM_CLIENT_AUTH
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:40:53.784162112 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:41:57.515096255 -0700
-@@ -732,6 +732,16 @@ SSL_IMPORT SECStatus SSL_ReHandshakeWith
+--- a/nss/lib/ssl/ssl.h 2014-01-17 17:53:39.726735852 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 17:54:27.087523439 -0800
+@@ -793,6 +793,16 @@ SSL_IMPORT SECStatus SSL_ReHandshakeWith
PRBool flushCache,
PRIntervalTime timeout);
@@ -40,9 +40,9 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
#ifdef SSL_DEPRECATED_FUNCTION
/* deprecated!
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 12:40:14.503586299 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 12:41:57.515096255 -0700
-@@ -1168,6 +1168,10 @@ struct sslSocketStr {
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:52:46.715854283 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:54:27.087523439 -0800
+@@ -1229,6 +1229,10 @@ struct sslSocketStr {
unsigned int sizeCipherSpecs;
const unsigned char * preferredCipher;
@@ -54,9 +54,9 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
/* Callbacks */
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 12:40:53.784162112 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 12:41:57.515096255 -0700
-@@ -1933,6 +1933,20 @@ SSL_HandshakeResumedSession(PRFileDesc *
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 17:53:39.726735852 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 17:54:27.097523605 -0800
+@@ -1869,6 +1869,20 @@ SSL_HandshakeResumedSession(PRFileDesc *
return SECSuccess;
}
@@ -77,7 +77,7 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
/************************************************************************/
/* The following functions are the TOP LEVEL SSL functions.
** They all get called through the NSPRIOMethods table below.
-@@ -2995,6 +3009,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
+@@ -2936,6 +2950,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
sc->serverKeyBits = 0;
ss->certStatusArray[i] = NULL;
}
diff --git a/chromium/net/third_party/nss/patches/ignorechangecipherspec.patch b/chromium/net/third_party/nss/patches/ignorechangecipherspec.patch
new file mode 100644
index 00000000000..b8e176d4521
--- /dev/null
+++ b/chromium/net/third_party/nss/patches/ignorechangecipherspec.patch
@@ -0,0 +1,19 @@
+Index: ssl/ssl3con.c
+===================================================================
+--- ssl/ssl3con.c (revision 274314)
++++ ssl/ssl3con.c (working copy)
+@@ -3621,6 +3621,14 @@
+ SSL_GETPID(), ss->fd));
+
+ if (ws != wait_change_cipher) {
++ if (IS_DTLS(ss)) {
++ /* Ignore this because it's out of order. */
++ SSL_TRC(3, ("%d: SSL3[%d]: discard out of order "
++ "DTLS change_cipher_spec",
++ SSL_GETPID(), ss->fd));
++ buf->len = 0;
++ return SECSuccess;
++ }
+ (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+ return SECFailure;
diff --git a/chromium/net/third_party/nss/patches/negotiatedextension.patch b/chromium/net/third_party/nss/patches/negotiatedextension.patch
deleted file mode 100644
index bbcd2e52f63..00000000000
--- a/chromium/net/third_party/nss/patches/negotiatedextension.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-diff -pu a/nss/lib/ssl/sslreveal.c b/nss/lib/ssl/sslreveal.c
---- a/nss/lib/ssl/sslreveal.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslreveal.c 2013-07-31 12:41:08.684380521 -0700
-@@ -77,7 +77,6 @@ SSL_HandshakeNegotiatedExtension(PRFileD
- {
- /* some decisions derived from SSL_GetChannelInfo */
- sslSocket * sslsocket = NULL;
-- PRBool enoughFirstHsDone = PR_FALSE;
-
- if (!pYes) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -93,14 +92,8 @@ SSL_HandshakeNegotiatedExtension(PRFileD
-
- *pYes = PR_FALSE;
-
-- if (sslsocket->firstHsDone) {
-- enoughFirstHsDone = PR_TRUE;
-- } else if (sslsocket->ssl3.initialized && ssl3_CanFalseStart(sslsocket)) {
-- enoughFirstHsDone = PR_TRUE;
-- }
--
- /* according to public API SSL_GetChannelInfo, this doesn't need a lock */
-- if (sslsocket->opt.useSecurity && enoughFirstHsDone) {
-+ if (sslsocket->opt.useSecurity) {
- if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
- /* now we know this socket went through ssl3_InitState() and
- * ss->xtnData got initialized, which is the only member accessed by
diff --git a/chromium/net/third_party/nss/patches/nssrwlock.patch b/chromium/net/third_party/nss/patches/nssrwlock.patch
new file mode 100644
index 00000000000..2f10a4fbd37
--- /dev/null
+++ b/chromium/net/third_party/nss/patches/nssrwlock.patch
@@ -0,0 +1,235 @@
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 19:01:58.104487211 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 19:02:38.965159506 -0800
+@@ -5211,7 +5211,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ * the lock across the calls to ssl3_CallHelloExtensionSenders.
+ */
+ if (sid->u.ssl3.lock) {
+- PR_RWLock_Rlock(sid->u.ssl3.lock);
++ NSSRWLock_LockRead(sid->u.ssl3.lock);
+ }
+
+ if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
+@@ -5220,7 +5220,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+
+ extLen = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes, NULL);
+ if (extLen < 0) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return SECFailure;
+ }
+ maxBytes -= extLen;
+@@ -5248,7 +5248,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ /* how many suites are permitted by policy and user preference? */
+ num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
+ if (!num_suites) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return SECFailure; /* count_cipher_suites has set error code. */
+ }
+
+@@ -5293,7 +5293,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+
+ rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
+@@ -5312,21 +5312,21 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
+ }
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
+ if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
+ rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by GetNewRandom. */
+ }
+ }
+ rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
+ SSL3_RANDOM_LENGTH);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
+@@ -5336,7 +5336,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ else
+ rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
+@@ -5344,14 +5344,14 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ rv = ssl3_AppendHandshakeVariable(
+ ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ }
+
+ rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
+@@ -5360,7 +5360,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ actual_count++;
+@@ -5369,7 +5369,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ actual_count++;
+@@ -5379,7 +5379,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
+ actual_count++;
+ if (actual_count > num_suites) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ /* set error card removal/insertion error */
+ PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
+ return SECFailure;
+@@ -5387,7 +5387,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ }
+@@ -5398,14 +5398,14 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ * the server.. */
+ if (actual_count != num_suites) {
+ /* Card removal/insertion error */
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
+ return SECFailure;
+ }
+
+ rv = ssl3_AppendHandshakeNumber(ss, numCompressionMethods, 1);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ for (i = 0; i < compressionMethodsCount; i++) {
+@@ -5413,7 +5413,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ continue;
+ rv = ssl3_AppendHandshakeNumber(ss, compressions[i], 1);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ }
+@@ -5424,20 +5424,20 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+
+ rv = ssl3_AppendHandshakeNumber(ss, maxBytes, 2);
+ if (rv != SECSuccess) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return rv; /* err set by AppendHandshake. */
+ }
+
+ extLen = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, maxBytes, NULL);
+ if (extLen < 0) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return SECFailure;
+ }
+ maxBytes -= extLen;
+
+ extLen = ssl3_AppendPaddingExtension(ss, paddingExtensionLen, maxBytes);
+ if (extLen < 0) {
+- if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
++ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
+ return SECFailure;
+ }
+ maxBytes -= extLen;
+@@ -5446,7 +5446,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
+ }
+
+ if (sid->u.ssl3.lock) {
+- PR_RWLock_Unlock(sid->u.ssl3.lock);
++ NSSRWLock_UnlockRead(sid->u.ssl3.lock);
+ }
+
+ if (ss->xtnData.sentSessionTicketInClientHello) {
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 19:00:52.843413560 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 19:02:38.965159506 -0800
+@@ -730,7 +730,7 @@ struct sslSessionIDStr {
+ * cached. Before then, there is no need to lock anything because
+ * the sid isn't being shared by anything.
+ */
+- PRRWLock *lock;
++ NSSRWLock *lock;
+
+ /* The lock must be held while reading or writing these members
+ * because they change while the sid is cached.
+diff -pu a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
+--- a/nss/lib/ssl/sslnonce.c 2014-01-17 19:02:25.844943628 -0800
++++ b/nss/lib/ssl/sslnonce.c 2014-01-17 19:02:38.965159506 -0800
+@@ -136,7 +136,7 @@ ssl_DestroySID(sslSessionID *sid)
+ }
+
+ if (sid->u.ssl3.lock) {
+- PR_DestroyRWLock(sid->u.ssl3.lock);
++ NSSRWLock_Destroy(sid->u.ssl3.lock);
+ }
+ }
+
+@@ -308,7 +308,7 @@ CacheSID(sslSessionID *sid)
+ PRINT_BUF(8, (0, "sessionID:",
+ sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
+
+- sid->u.ssl3.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, NULL);
++ sid->u.ssl3.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+ if (!sid->u.ssl3.lock) {
+ return;
+ }
+@@ -448,7 +448,7 @@ ssl3_SetSIDSessionTicket(sslSessionID *s
+ * yet, so no locking is needed.
+ */
+ if (sid->u.ssl3.lock) {
+- PR_RWLock_Wlock(sid->u.ssl3.lock);
++ NSSRWLock_LockWrite(sid->u.ssl3.lock);
+
+ /* A server might have sent us an empty ticket, which has the
+ * effect of clearing the previously known ticket.
+@@ -467,6 +467,6 @@ ssl3_SetSIDSessionTicket(sslSessionID *s
+ newSessionTicket->ticket.len = 0;
+
+ if (sid->u.ssl3.lock) {
+- PR_RWLock_Unlock(sid->u.ssl3.lock);
++ NSSRWLock_UnlockWrite(sid->u.ssl3.lock);
+ }
+ }
diff --git a/chromium/net/third_party/nss/patches/nullcipher_934016.patch b/chromium/net/third_party/nss/patches/nullcipher_934016.patch
deleted file mode 100644
index 6a4b5c68d26..00000000000
--- a/chromium/net/third_party/nss/patches/nullcipher_934016.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
-index 8395f61..8b8b758 100644
---- a/net/third_party/nss/ssl/ssl3con.c
-+++ b/net/third_party/nss/ssl/ssl3con.c
-@@ -859,6 +859,11 @@ static SECStatus
- Null_Cipher(void *ctx, unsigned char *output, int *outputLen, int maxOutputLen,
- const unsigned char *input, int inputLen)
- {
-+ if (inputLen > maxOutputLen) {
-+ *outputLen = 0; /* Match PK11_CipherOp in setting outputLen */
-+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
-+ return SECFailure;
-+ }
- *outputLen = inputLen;
- if (input != output)
- PORT_Memcpy(output, input, inputLen);
diff --git a/chromium/net/third_party/nss/patches/paddingextension.patch b/chromium/net/third_party/nss/patches/paddingextension.patch
deleted file mode 100644
index 966c4903b21..00000000000
--- a/chromium/net/third_party/nss/patches/paddingextension.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index 8b8b758..882e356 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -4975,6 +4975,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- PRBool isTLS = PR_FALSE;
- PRBool requestingResume = PR_FALSE;
- PRInt32 total_exten_len = 0;
-+ unsigned paddingExtensionLen;
- unsigned numCompressionMethods;
- PRInt32 flags;
-
-@@ -5241,6 +5242,20 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- length += 1 + ss->ssl3.hs.cookieLen;
- }
-
-+ /* A padding extension may be included to ensure that the record containing
-+ * the ClientHello doesn't have a length between 256 and 511 bytes
-+ * (inclusive). Initial, ClientHello records with such lengths trigger bugs
-+ * in F5 devices.
-+ *
-+ * This is not done for DTLS nor for renegotiation. */
-+ if (!IS_DTLS(ss) && isTLS && !ss->firstHsDone) {
-+ paddingExtensionLen = ssl3_CalculatePaddingExtensionLength(length);
-+ total_exten_len += paddingExtensionLen;
-+ length += paddingExtensionLen;
-+ } else {
-+ paddingExtensionLen = 0;
-+ }
-+
- rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
- if (rv != SECSuccess) {
- return rv; /* err set by ssl3_AppendHandshake* */
-@@ -5360,6 +5375,13 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- return SECFailure;
- }
- maxBytes -= extLen;
-+
-+ extLen = ssl3_AppendPaddingExtension(ss, paddingExtensionLen, maxBytes);
-+ if (extLen < 0) {
-+ return SECFailure;
-+ }
-+ maxBytes -= extLen;
-+
- PORT_Assert(!maxBytes);
- }
- if (ss->ssl3.hs.sendingSCSV) {
-diff --git a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
-index 0415770..cdebcc9 100644
---- a/nss/lib/ssl/ssl3ext.c
-+++ b/nss/lib/ssl/ssl3ext.c
-@@ -2297,3 +2297,56 @@ ssl3_ClientSendSigAlgsXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
- loser:
- return -1;
- }
-+
-+unsigned int
-+ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
-+{
-+ unsigned int recordLength = 1 /* handshake message type */ +
-+ 3 /* handshake message length */ +
-+ clientHelloLength;
-+ unsigned int extensionLength;
-+
-+ if (recordLength < 256 || recordLength >= 512) {
-+ return 0;
-+ }
-+
-+ extensionLength = 512 - recordLength;
-+ /* Extensions take at least four bytes to encode. */
-+ if (extensionLength < 4) {
-+ extensionLength = 4;
-+ }
-+
-+ return extensionLength;
-+}
-+
-+/* ssl3_AppendPaddingExtension possibly adds an extension which ensures that a
-+ * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures
-+ * that we don't trigger bugs in F5 products. */
-+PRInt32
-+ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
-+ PRUint32 maxBytes)
-+{
-+ unsigned int paddingLen = extensionLen - 4;
-+ unsigned char padding[256];
-+
-+ if (extensionLen == 0) {
-+ return 0;
-+ }
-+
-+ if (extensionLen < 4 ||
-+ extensionLen > maxBytes ||
-+ paddingLen > sizeof(padding)) {
-+ PORT_Assert(0);
-+ return -1;
-+ }
-+
-+ if (SECSuccess != ssl3_AppendHandshakeNumber(ss, ssl_padding_xtn, 2))
-+ return -1;
-+ if (SECSuccess != ssl3_AppendHandshakeNumber(ss, paddingLen, 2))
-+ return -1;
-+ memset(padding, 0, paddingLen);
-+ if (SECSuccess != ssl3_AppendHandshake(ss, padding, paddingLen))
-+ return -1;
-+
-+ return extensionLen;
-+}
-diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
-index 614eed1..9c789bf 100644
---- a/nss/lib/ssl/sslimpl.h
-+++ b/nss/lib/ssl/sslimpl.h
-@@ -237,6 +237,13 @@ extern PRInt32
- ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes,
- const ssl3HelloExtensionSender *sender);
-
-+extern unsigned int
-+ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength);
-+
-+extern PRInt32
-+ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
-+ PRUint32 maxBytes);
-+
- /* Socket ops */
- struct sslSocketOpsStr {
- int (*connect) (sslSocket *, const PRNetAddr *);
-diff --git a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
-index a8007d8..e4d188f 100644
---- a/nss/lib/ssl/sslt.h
-+++ b/nss/lib/ssl/sslt.h
-@@ -205,9 +205,10 @@ typedef enum {
- ssl_session_ticket_xtn = 35,
- ssl_next_proto_nego_xtn = 13172,
- ssl_channel_id_xtn = 30031,
-+ ssl_padding_xtn = 35655,
- ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
- } SSLExtensionType;
-
--#define SSL_MAX_EXTENSIONS 11
-+#define SSL_MAX_EXTENSIONS 11 /* doesn't include ssl_padding_xtn. */
-
- #endif /* __sslt_h_ */
diff --git a/chromium/net/third_party/nss/patches/paddingextensionall.patch b/chromium/net/third_party/nss/patches/paddingextensionall.patch
deleted file mode 100644
index f226aacaf68..00000000000
--- a/chromium/net/third_party/nss/patches/paddingextensionall.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-diff --git a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
-index cdebcc9..03cf05c 100644
---- a/nss/lib/ssl/ssl3ext.c
-+++ b/nss/lib/ssl/ssl3ext.c
-@@ -2306,7 +2306,11 @@ ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
- clientHelloLength;
- unsigned int extensionLength;
-
-- if (recordLength < 256 || recordLength >= 512) {
-+ /* This condition should be:
-+ * if (recordLength < 256 || recordLength >= 512) {
-+ * It has been changed, temporarily, to test whether 512 byte ClientHellos
-+ * are a compatibility problem. */
-+ if (recordLength >= 512) {
- return 0;
- }
-
-@@ -2327,7 +2331,7 @@ ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
- PRUint32 maxBytes)
- {
- unsigned int paddingLen = extensionLen - 4;
-- unsigned char padding[256];
-+ unsigned char padding[512];
-
- if (extensionLen == 0) {
- return 0;
diff --git a/chromium/net/third_party/nss/patches/paddingextvalue.patch b/chromium/net/third_party/nss/patches/paddingextvalue.patch
new file mode 100644
index 00000000000..5998054e508
--- /dev/null
+++ b/chromium/net/third_party/nss/patches/paddingextvalue.patch
@@ -0,0 +1,16 @@
+Index: net/third_party/nss/ssl/sslt.h
+===================================================================
+--- net/third_party/nss/ssl/sslt.h (revision 262719)
++++ net/third_party/nss/ssl/sslt.h (working copy)
+@@ -203,10 +203,10 @@
+ ssl_use_srtp_xtn = 14,
+ ssl_app_layer_protocol_xtn = 16,
+ ssl_signed_certificate_timestamp_xtn = 18, /* RFC 6962 */
++ ssl_padding_xtn = 21,
+ ssl_session_ticket_xtn = 35,
+ ssl_next_proto_nego_xtn = 13172,
+ ssl_channel_id_xtn = 30032,
+- ssl_padding_xtn = 35655,
+ ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
+ } SSLExtensionType;
+
diff --git a/chromium/net/third_party/nss/patches/peercertchain.patch b/chromium/net/third_party/nss/patches/peercertchain.patch
deleted file mode 100644
index 0ddd316f85f..00000000000
--- a/chromium/net/third_party/nss/patches/peercertchain.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-diff -pu a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c
---- a/nss/lib/ssl/sslauth.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslauth.c 2013-07-31 12:32:07.996451064 -0700
-@@ -28,6 +28,41 @@ SSL_PeerCertificate(PRFileDesc *fd)
- }
-
- /* NEED LOCKS IN HERE. */
-+SECStatus
-+SSL_PeerCertificateChain(PRFileDesc *fd, CERTCertificate **certs,
-+ unsigned int *numCerts, unsigned int maxNumCerts)
-+{
-+ sslSocket *ss;
-+ ssl3CertNode* cur;
-+
-+ ss = ssl_FindSocket(fd);
-+ if (!ss) {
-+ SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificateChain",
-+ SSL_GETPID(), fd));
-+ return SECFailure;
-+ }
-+ if (!ss->opt.useSecurity)
-+ return SECFailure;
-+
-+ if (ss->sec.peerCert == NULL) {
-+ *numCerts = 0;
-+ return SECSuccess;
-+ }
-+
-+ *numCerts = 1; /* for the leaf certificate */
-+ if (maxNumCerts > 0)
-+ certs[0] = CERT_DupCertificate(ss->sec.peerCert);
-+
-+ for (cur = ss->ssl3.peerCertChain; cur; cur = cur->next) {
-+ if (*numCerts < maxNumCerts)
-+ certs[*numCerts] = CERT_DupCertificate(cur->cert);
-+ (*numCerts)++;
-+ }
-+
-+ return SECSuccess;
-+}
-+
-+/* NEED LOCKS IN HERE. */
- CERTCertificate *
- SSL_LocalCertificate(PRFileDesc *fd)
- {
-diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:07:10.964699464 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:32:07.996451065 -0700
-@@ -426,6 +426,18 @@ SSL_SetStapledOCSPResponses(PRFileDesc *
- SSLKEAType kea);
-
- /*
-+** Return references to the certificates presented by the SSL peer.
-+** |maxNumCerts| must contain the size of the |certs| array. On successful
-+** return, |*numCerts| contains the number of certificates available and
-+** |certs| will contain references to as many certificates as would fit.
-+** Therefore if |*numCerts| contains a value less than or equal to
-+** |maxNumCerts|, then all certificates were returned.
-+*/
-+SSL_IMPORT SECStatus SSL_PeerCertificateChain(
-+ PRFileDesc *fd, CERTCertificate **certs,
-+ unsigned int *numCerts, unsigned int maxNumCerts);
-+
-+/*
- ** Authenticate certificate hook. Called when a certificate comes in
- ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
- ** certificate.
diff --git a/chromium/net/third_party/nss/patches/peercertchain2.patch b/chromium/net/third_party/nss/patches/peercertchain2.patch
deleted file mode 100644
index 4b4a4fb5fa7..00000000000
--- a/chromium/net/third_party/nss/patches/peercertchain2.patch
+++ /dev/null
@@ -1,107 +0,0 @@
-Index: net/third_party/nss/ssl/ssl.h
-===================================================================
---- net/third_party/nss/ssl/ssl.h (revision 225295)
-+++ net/third_party/nss/ssl/ssl.h (working copy)
-@@ -434,6 +434,15 @@
- */
- SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
-
-+/*
-+** Return the certificates presented by the SSL peer. If the SSL peer
-+** did not present certificates, return NULL with the
-+** SSL_ERROR_NO_CERTIFICATE error. On failure, return NULL with an error
-+** code other than SSL_ERROR_NO_CERTIFICATE.
-+** "fd" the socket "file" descriptor
-+*/
-+SSL_IMPORT CERTCertList *SSL_PeerCertificateChain(PRFileDesc *fd);
-+
- /* SSL_PeerStapledOCSPResponses returns the OCSP responses that were provided
- * by the TLS server. The return value is a pointer to an internal SECItemArray
- * that contains the returned OCSP responses; it is only valid until the
-@@ -463,18 +472,6 @@
- SSLKEAType kea);
-
- /*
--** Return references to the certificates presented by the SSL peer.
--** |maxNumCerts| must contain the size of the |certs| array. On successful
--** return, |*numCerts| contains the number of certificates available and
--** |certs| will contain references to as many certificates as would fit.
--** Therefore if |*numCerts| contains a value less than or equal to
--** |maxNumCerts|, then all certificates were returned.
--*/
--SSL_IMPORT SECStatus SSL_PeerCertificateChain(
-- PRFileDesc *fd, CERTCertificate **certs,
-- unsigned int *numCerts, unsigned int maxNumCerts);
--
--/*
- ** Authenticate certificate hook. Called when a certificate comes in
- ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
- ** certificate.
-Index: net/third_party/nss/ssl/sslauth.c
-===================================================================
---- net/third_party/nss/ssl/sslauth.c (revision 225295)
-+++ net/third_party/nss/ssl/sslauth.c (working copy)
-@@ -28,38 +28,43 @@
- }
-
- /* NEED LOCKS IN HERE. */
--SECStatus
--SSL_PeerCertificateChain(PRFileDesc *fd, CERTCertificate **certs,
-- unsigned int *numCerts, unsigned int maxNumCerts)
-+CERTCertList *
-+SSL_PeerCertificateChain(PRFileDesc *fd)
- {
- sslSocket *ss;
-- ssl3CertNode* cur;
-+ CERTCertList *chain = NULL;
-+ CERTCertificate *cert;
-+ ssl3CertNode *cur;
-
- ss = ssl_FindSocket(fd);
- if (!ss) {
- SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificateChain",
- SSL_GETPID(), fd));
-- return SECFailure;
-+ return NULL;
- }
-- if (!ss->opt.useSecurity)
-- return SECFailure;
--
-- if (ss->sec.peerCert == NULL) {
-- *numCerts = 0;
-- return SECSuccess;
-+ if (!ss->opt.useSecurity || !ss->sec.peerCert) {
-+ PORT_SetError(SSL_ERROR_NO_CERTIFICATE);
-+ return NULL;
- }
--
-- *numCerts = 1; /* for the leaf certificate */
-- if (maxNumCerts > 0)
-- certs[0] = CERT_DupCertificate(ss->sec.peerCert);
--
-+ chain = CERT_NewCertList();
-+ if (!chain) {
-+ return NULL;
-+ }
-+ cert = CERT_DupCertificate(ss->sec.peerCert);
-+ if (CERT_AddCertToListTail(chain, cert) != SECSuccess) {
-+ goto loser;
-+ }
- for (cur = ss->ssl3.peerCertChain; cur; cur = cur->next) {
-- if (*numCerts < maxNumCerts)
-- certs[*numCerts] = CERT_DupCertificate(cur->cert);
-- (*numCerts)++;
-+ cert = CERT_DupCertificate(cur->cert);
-+ if (CERT_AddCertToListTail(chain, cert) != SECSuccess) {
-+ goto loser;
-+ }
- }
-+ return chain;
-
-- return SECSuccess;
-+loser:
-+ CERT_DestroyCertList(chain);
-+ return NULL;
- }
-
- /* NEED LOCKS IN HERE. */
diff --git a/chromium/net/third_party/nss/patches/renegoscsv.patch b/chromium/net/third_party/nss/patches/renegoscsv.patch
deleted file mode 100644
index ed3b96de203..00000000000
--- a/chromium/net/third_party/nss/patches/renegoscsv.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:07:10.964699464 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:29:07.903829188 -0700
-@@ -4773,9 +4773,9 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
- return SECFailure; /* ssl3_config_match_init has set error code. */
-
- /* HACK for SCSV in SSL 3.0. On initial handshake, prepend SCSV,
-- * only if we're willing to complete an SSL 3.0 handshake.
-+ * only if TLS is disabled.
- */
-- if (!ss->firstHsDone && ss->vrange.min == SSL_LIBRARY_VERSION_3_0) {
-+ if (!ss->firstHsDone && !isTLS) {
- /* Must set this before calling Hello Extension Senders,
- * to suppress sending of empty RI extension.
- */
diff --git a/chromium/net/third_party/nss/patches/reorderextensions.patch b/chromium/net/third_party/nss/patches/reorderextensions.patch
new file mode 100644
index 00000000000..3572fb157d2
--- /dev/null
+++ b/chromium/net/third_party/nss/patches/reorderextensions.patch
@@ -0,0 +1,34 @@
+diff --git a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
+index 6f3fe2f..523e49a 100644
+--- a/nss/lib/ssl/ssl3ext.c
++++ b/nss/lib/ssl/ssl3ext.c
+@@ -295,9 +295,12 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
+ { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
+ { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
+ { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
+- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+ { ssl_signed_certificate_timestamp_xtn,
+- &ssl3_ClientSendSignedCertTimestampXtn }
++ &ssl3_ClientSendSignedCertTimestampXtn },
++ /* WebSphere Application Server 7.0 is intolerant to the last extension
++ * being zero-length. It is not intolerant of TLS 1.2, so move
++ * signature_algorithms to the end. */
++ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
+ /* any extra entries will appear as { 0, NULL } */
+ };
+
+@@ -2347,9 +2350,11 @@ ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
+ }
+
+ extensionLength = 512 - recordLength;
+- /* Extensions take at least four bytes to encode. */
+- if (extensionLength < 4) {
+- extensionLength = 4;
++ /* Extensions take at least four bytes to encode. Always include at least
++ * one byte of data if including the extension. WebSphere Application Server
++ * 7.0 is intolerant to the last extension being zero-length. */
++ if (extensionLength < 4 + 1) {
++ extensionLength = 4 + 1;
+ }
+
+ return extensionLength;
diff --git a/chromium/net/third_party/nss/patches/restartclientauth.patch b/chromium/net/third_party/nss/patches/restartclientauth.patch
index 84f18f391e0..5056cadd8bf 100644
--- a/chromium/net/third_party/nss/patches/restartclientauth.patch
+++ b/chromium/net/third_party/nss/patches/restartclientauth.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 12:44:31.987362835 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 12:44:50.987642452 -0700
-@@ -6756,6 +6756,85 @@ done:
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:55:01.518095989 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:55:19.158389328 -0800
+@@ -7199,6 +7199,85 @@ done:
return rv;
}
@@ -84,13 +84,13 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+ return rv;
+}
+
- PRBool
- ssl3_CanFalseStart(sslSocket *ss) {
- PRBool rv;
+ static SECStatus
+ ssl3_CheckFalseStart(sslSocket *ss)
+ {
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 12:44:31.987362835 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 12:44:50.987642452 -0700
-@@ -366,6 +366,11 @@ SSL_IMPORT SECStatus SSL_ForceHandshake(
+--- a/nss/lib/ssl/ssl.h 2014-01-17 17:55:01.538096321 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 17:55:19.158389328 -0800
+@@ -399,6 +399,11 @@ SSL_IMPORT SECStatus SSL_ForceHandshake(
SSL_IMPORT SECStatus SSL_ForceHandshakeWithTimeout(PRFileDesc *fd,
PRIntervalTime timeout);
@@ -103,9 +103,9 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
** Query security status of socket. *on is set to one if security is
** enabled. *keySize will contain the stream key size used. *issuer will
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 12:44:31.997362988 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 12:44:50.987642452 -0700
-@@ -1513,16 +1513,17 @@ extern SECStatus ssl3_MasterKeyDeriveBy
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:55:01.538096321 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:55:19.158389328 -0800
+@@ -1588,16 +1588,17 @@ extern SECStatus ssl3_MasterKeyDeriveBy
/* These functions are called from secnav, even though they're "private". */
extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
@@ -128,9 +128,9 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
/*
diff -pu a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c
---- a/nss/lib/ssl/sslsecur.c 2013-07-31 12:28:39.283413269 -0700
-+++ b/nss/lib/ssl/sslsecur.c 2013-07-31 12:44:50.987642452 -0700
-@@ -1436,17 +1436,70 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
+--- a/nss/lib/ssl/sslsecur.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslsecur.c 2014-01-17 17:55:19.158389328 -0800
+@@ -1518,17 +1518,70 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
return SECSuccess;
}
diff --git a/chromium/net/third_party/nss/patches/resumeclienthelloversion.patch b/chromium/net/third_party/nss/patches/resumeclienthelloversion.patch
deleted file mode 100644
index 7c330c71fff..00000000000
--- a/chromium/net/third_party/nss/patches/resumeclienthelloversion.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
-index d22a7d6..a7617fb 100644
---- a/nss/lib/ssl/ssl3con.c
-+++ b/nss/lib/ssl/ssl3con.c
-@@ -2865,12 +2865,14 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
- * Forces the use of the provided epoch
- * ssl_SEND_FLAG_CAP_RECORD_VERSION
- * Caps the record layer version number of TLS ClientHello to { 3, 1 }
-- * (TLS 1.0). Some TLS 1.0 servers (which seem to use F5 BIG-IP) ignore
-+ * (TLS 1.0). Some TLS 1.0 servers (which seem to use F5 BIG-IP) ignore
- * ClientHello.client_version and use the record layer version number
- * (TLSPlaintext.version) instead when negotiating protocol versions. In
- * addition, if the record layer version number of ClientHello is { 3, 2 }
-- * (TLS 1.1) or higher, these servers reset the TCP connections. Set this
-- * flag to work around such servers.
-+ * (TLS 1.1) or higher, these servers reset the TCP connections. Lastly,
-+ * some F5 BIG-IP servers hang if a record containing a ClientHello has a
-+ * version greater than 0x0301 and a length greater than 255. Set this flag
-+ * to work around such servers.
- */
- PRInt32
- ssl3_SendRecord( sslSocket * ss,
-@@ -5363,7 +5365,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
- }
-
- flags = 0;
-- if (!ss->firstHsDone && !requestingResume && !IS_DTLS(ss)) {
-+ if (!ss->firstHsDone && !IS_DTLS(ss)) {
- flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION;
- }
- rv = ssl3_FlushHandshake(ss, flags);
diff --git a/chromium/net/third_party/nss/patches/secitemarray.patch b/chromium/net/third_party/nss/patches/secitemarray.patch
index bb0d2ef4ff8..5c48f5bc9cc 100644
--- a/chromium/net/third_party/nss/patches/secitemarray.patch
+++ b/chromium/net/third_party/nss/patches/secitemarray.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 14:10:35.113325316 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 14:13:25.995834521 -0700
-@@ -1293,6 +1293,15 @@ extern sslSessionIDUncacheFunc ssl_sid_u
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:00:11.213237373 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:03:29.176520864 -0800
+@@ -1364,6 +1364,15 @@ extern sslSessionIDUncacheFunc ssl_sid_u
SEC_BEGIN_PROTOS
@@ -18,8 +18,8 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
extern SECStatus ssl_Init(void);
extern SECStatus ssl_InitializePRErrorTable(void);
diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
---- a/nss/lib/ssl/sslt.h 2013-07-31 14:10:00.342814862 -0700
-+++ b/nss/lib/ssl/sslt.h 2013-07-31 14:13:25.995834521 -0700
+--- a/nss/lib/ssl/sslt.h 2014-01-17 17:59:03.252110162 -0800
++++ b/nss/lib/ssl/sslt.h 2014-01-17 18:03:29.186521030 -0800
@@ -10,6 +10,19 @@
#include "prtypes.h"
diff --git a/chromium/net/third_party/nss/patches/secretexporterlocks.patch b/chromium/net/third_party/nss/patches/secretexporterlocks.patch
index f605cd5124f..85d98dfdab4 100644
--- a/chromium/net/third_party/nss/patches/secretexporterlocks.patch
+++ b/chromium/net/third_party/nss/patches/secretexporterlocks.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c
---- a/nss/lib/ssl/sslinfo.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslinfo.c 2013-07-31 14:11:28.834113906 -0700
-@@ -349,8 +349,13 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
+--- a/nss/lib/ssl/sslinfo.c 2014-01-17 17:49:26.072517368 -0800
++++ b/nss/lib/ssl/sslinfo.c 2014-01-17 18:00:29.773545219 -0800
+@@ -350,8 +350,13 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
return SECFailure;
}
@@ -15,7 +15,7 @@ diff -pu a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c
return SECFailure;
}
-@@ -361,13 +366,17 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
+@@ -362,13 +367,17 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
}
val = PORT_Alloc(valLen);
if (!val) {
@@ -33,7 +33,7 @@ diff -pu a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c
if (hasContext) {
val[i++] = contextLen >> 8;
val[i++] = contextLen;
-@@ -388,6 +397,8 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
+@@ -389,6 +398,8 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd,
valLen, out, outLen);
}
ssl_ReleaseSpecReadLock(ss);
diff --git a/chromium/net/third_party/nss/patches/sessioncache.patch b/chromium/net/third_party/nss/patches/sessioncache.patch
index 11fd9fc8e2c..1564648c748 100644
--- a/chromium/net/third_party/nss/patches/sessioncache.patch
+++ b/chromium/net/third_party/nss/patches/sessioncache.patch
@@ -1,18 +1,19 @@
-diff --git a/net/third_party/nss/ssl/exports_win.def b/net/third_party/nss/ssl/exports_win.def
-index e0624f1..a1045bb 100644
---- a/net/third_party/nss/ssl/exports_win.def
-+++ b/net/third_party/nss/ssl/exports_win.def
-@@ -62,3 +62,5 @@ SSL_RestartHandshakeAfterChannelIDReq
- SSL_GetChannelBinding
- SSL_PeerSignedCertTimestamps
- SSL_CipherOrderSet
-+SSL_CacheSession
-+SSL_CacheSessionUnlocked
-diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h
-index bef33fc..6f7c988 100644
---- a/net/third_party/nss/ssl/ssl.h
-+++ b/net/third_party/nss/ssl/ssl.h
-@@ -872,6 +872,18 @@ SSL_IMPORT int SSL_DataPending(PRFileDesc *fd);
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 19:00:52.843413560 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 19:01:36.374129696 -0800
+@@ -11318,7 +11318,7 @@ ssl3_FinishHandshake(sslSocket * ss)
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+ }
+
+- if (ss->ssl3.hs.cacheSID) {
++ if (ss->ssl3.hs.cacheSID && ss->sec.isServer) {
+ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
+ (*ss->sec.cache)(ss->sec.ci.sid);
+ ss->ssl3.hs.cacheSID = PR_FALSE;
+diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
+--- a/nss/lib/ssl/ssl.h 2014-01-17 19:00:52.843413560 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 19:01:36.374129696 -0800
+@@ -892,6 +892,18 @@ SSL_IMPORT int SSL_DataPending(PRFileDes
SSL_IMPORT SECStatus SSL_InvalidateSession(PRFileDesc *fd);
/*
@@ -31,24 +32,10 @@ index bef33fc..6f7c988 100644
** Return a SECItem containing the SSL session ID associated with the fd.
*/
SSL_IMPORT SECItem *SSL_GetSessionID(PRFileDesc *fd);
-diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
-index 307a0fe..e2be5e6 100644
---- a/net/third_party/nss/ssl/ssl3con.c
-+++ b/net/third_party/nss/ssl/ssl3con.c
-@@ -11240,7 +11240,7 @@ ssl3_FinishHandshake(sslSocket * ss)
- /* The first handshake is now completed. */
- ss->handshake = NULL;
-
-- if (ss->ssl3.hs.cacheSID) {
-+ if (ss->ssl3.hs.cacheSID && ss->sec.isServer) {
- (*ss->sec.cache)(ss->sec.ci.sid);
- ss->ssl3.hs.cacheSID = PR_FALSE;
- }
-diff --git a/net/third_party/nss/ssl/sslsecur.c b/net/third_party/nss/ssl/sslsecur.c
-index 31c343f..99538e5 100644
---- a/net/third_party/nss/ssl/sslsecur.c
-+++ b/net/third_party/nss/ssl/sslsecur.c
-@@ -1474,6 +1474,49 @@ SSL_InvalidateSession(PRFileDesc *fd)
+diff -pu a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c
+--- a/nss/lib/ssl/sslsecur.c 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/sslsecur.c 2014-01-17 19:01:36.374129696 -0800
+@@ -1469,6 +1469,49 @@ SSL_InvalidateSession(PRFileDesc *fd)
return rv;
}
diff --git a/chromium/net/third_party/nss/patches/signedcertificatetimestamps.patch b/chromium/net/third_party/nss/patches/signedcertificatetimestamps.patch
index 8ac3079f19f..98643725561 100644
--- a/chromium/net/third_party/nss/patches/signedcertificatetimestamps.patch
+++ b/chromium/net/third_party/nss/patches/signedcertificatetimestamps.patch
@@ -1,45 +1,7 @@
-diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h
-index 67cc3a7..4cf02aa 100644
---- a/net/third_party/nss/ssl/ssl.h
-+++ b/net/third_party/nss/ssl/ssl.h
-@@ -161,6 +161,8 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
- */
- #define SSL_CBC_RANDOM_IV 23
- #define SSL_ENABLE_OCSP_STAPLING 24 /* Request OCSP stapling (client) */
-+/* Request Signed Certificate Timestamps via TLS extension (client) */
-+#define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 25
-
- #ifdef SSL_DEPRECATED_FUNCTION
- /* Old deprecated function names */
-@@ -464,6 +466,23 @@ SSL_IMPORT CERTCertList *SSL_PeerCertificateChain(PRFileDesc *fd);
- */
- SSL_IMPORT const SECItemArray * SSL_PeerStapledOCSPResponses(PRFileDesc *fd);
-
-+/* SSL_PeerSignedCertTimestamps returns the signed_certificate_timestamp
-+ * extension data provided by the TLS server. The return value is a pointer
-+ * to an internal SECItem that contains the returned response (as a serialized
-+ * SignedCertificateTimestampList, see RFC 6962). The returned pointer is only
-+ * valid until the callback function that calls SSL_PeerSignedCertTimestamps
-+ * (e.g. the authenticate certificate hook, or the handshake callback) returns.
-+ *
-+ * If no Signed Certificate Timestamps were given by the server then the result
-+ * will be empty. If there was an error, then the result will be NULL.
-+ *
-+ * You must set the SSL_ENABLE_SIGNED_CERT_TIMESTAMPS option to indicate support
-+ * for Signed Certificate Timestamps to a server.
-+ *
-+ * libssl does not do any parsing or validation of the response itself.
-+ */
-+SSL_IMPORT const SECItem * SSL_PeerSignedCertTimestamps(PRFileDesc *fd);
-+
- /* SSL_SetStapledOCSPResponses stores an array of one or multiple OCSP responses
- * in the fd's data, which may be sent as part of a server side cert_status
- * handshake message. Parameter |responses| is for the server certificate of
-diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
-index 0f1eea4..c2d9eeb 100644
---- a/net/third_party/nss/ssl/ssl3con.c
-+++ b/net/third_party/nss/ssl/ssl3con.c
-@@ -6639,10 +6639,22 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:11:28.314468184 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:23:17.946207727 -0800
+@@ -6682,10 +6682,22 @@ ssl3_HandleServerHello(sslSocket *ss, SS
sid->u.ssl3.sessionIDLength = sidBytes.len;
PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len);
@@ -62,7 +24,7 @@ index 0f1eea4..c2d9eeb 100644
/* If we will need a ChannelID key then we make the callback now. This
* allows the handshake to be restarted cleanly if the callback returns
* SECWouldBlock. */
-@@ -6668,6 +6680,9 @@ alert_loser:
+@@ -6711,6 +6723,9 @@ alert_loser:
(void)SSL3_SendAlert(ss, alert_fatal, desc);
loser:
@@ -72,11 +34,10 @@ index 0f1eea4..c2d9eeb 100644
errCode = ssl_MapLowLevelError(errCode);
return SECFailure;
}
-diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c
-index adb81ed..02e104d 100644
---- a/net/third_party/nss/ssl/ssl3ext.c
-+++ b/net/third_party/nss/ssl/ssl3ext.c
-@@ -81,6 +81,12 @@ static PRInt32 ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append,
+diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
+--- a/nss/lib/ssl/ssl3ext.c 2014-01-17 18:22:54.945827814 -0800
++++ b/nss/lib/ssl/ssl3ext.c 2014-01-17 18:35:21.798168722 -0800
+@@ -81,6 +81,12 @@ static PRInt32 ssl3_ClientSendSigAlgsXtn
PRUint32 maxBytes);
static SECStatus ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type,
SECItem *data);
@@ -89,7 +50,7 @@ index adb81ed..02e104d 100644
/*
* Write bytes. Using this function means the SECItem structure
-@@ -259,6 +265,8 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
+@@ -259,6 +265,8 @@ static const ssl3HelloExtensionHandler s
{ ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
{ ssl_channel_id_xtn, &ssl3_ClientHandleChannelIDXtn },
{ ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
@@ -98,18 +59,18 @@ index adb81ed..02e104d 100644
{ -1, NULL }
};
-@@ -287,7 +295,9 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
- { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
- { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
- { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
-- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
-+ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+@@ -287,7 +295,9 @@ ssl3HelloExtensionSender clientHelloSend
+ { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
+ { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
+ { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
+- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
++ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+ { ssl_signed_certificate_timestamp_xtn,
+ &ssl3_ClientSendSignedCertTimestampXtn }
/* any extra entries will appear as { 0, NULL } */
};
-@@ -2364,3 +2374,65 @@ ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
+@@ -2379,3 +2389,65 @@ ssl3_AppendPaddingExtension(sslSocket *s
return extensionLen;
}
@@ -175,77 +136,67 @@ index adb81ed..02e104d 100644
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+ return SECSuccess;
+}
-diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h
-index 79aca60..1e4655f 100644
---- a/net/third_party/nss/ssl/sslimpl.h
-+++ b/net/third_party/nss/ssl/sslimpl.h
-@@ -312,29 +312,30 @@ typedef struct sslOptionsStr {
- * list of supported protocols. */
- SECItem nextProtoNego;
+diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
+--- a/nss/lib/ssl/ssl.h 2014-01-17 18:00:11.213237373 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 18:38:15.791045050 -0800
+@@ -181,6 +181,9 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
+ */
+ #define SSL_ENABLE_ALPN 26
-- unsigned int useSecurity : 1; /* 1 */
-- unsigned int useSocks : 1; /* 2 */
-- unsigned int requestCertificate : 1; /* 3 */
-- unsigned int requireCertificate : 2; /* 4-5 */
-- unsigned int handshakeAsClient : 1; /* 6 */
-- unsigned int handshakeAsServer : 1; /* 7 */
-- unsigned int enableSSL2 : 1; /* 8 */
-- unsigned int unusedBit9 : 1; /* 9 */
-- unsigned int unusedBit10 : 1; /* 10 */
-- unsigned int noCache : 1; /* 11 */
-- unsigned int fdx : 1; /* 12 */
-- unsigned int v2CompatibleHello : 1; /* 13 */
-- unsigned int detectRollBack : 1; /* 14 */
-- unsigned int noStepDown : 1; /* 15 */
-- unsigned int bypassPKCS11 : 1; /* 16 */
-- unsigned int noLocks : 1; /* 17 */
-- unsigned int enableSessionTickets : 1; /* 18 */
-- unsigned int enableDeflate : 1; /* 19 */
-- unsigned int enableRenegotiation : 2; /* 20-21 */
-- unsigned int requireSafeNegotiation : 1; /* 22 */
-- unsigned int enableFalseStart : 1; /* 23 */
-- unsigned int cbcRandomIV : 1; /* 24 */
-- unsigned int enableOCSPStapling : 1; /* 25 */
-+ unsigned int useSecurity : 1; /* 1 */
-+ unsigned int useSocks : 1; /* 2 */
-+ unsigned int requestCertificate : 1; /* 3 */
-+ unsigned int requireCertificate : 2; /* 4-5 */
-+ unsigned int handshakeAsClient : 1; /* 6 */
-+ unsigned int handshakeAsServer : 1; /* 7 */
-+ unsigned int enableSSL2 : 1; /* 8 */
-+ unsigned int unusedBit9 : 1; /* 9 */
-+ unsigned int unusedBit10 : 1; /* 10 */
-+ unsigned int noCache : 1; /* 11 */
-+ unsigned int fdx : 1; /* 12 */
-+ unsigned int v2CompatibleHello : 1; /* 13 */
-+ unsigned int detectRollBack : 1; /* 14 */
-+ unsigned int noStepDown : 1; /* 15 */
-+ unsigned int bypassPKCS11 : 1; /* 16 */
-+ unsigned int noLocks : 1; /* 17 */
-+ unsigned int enableSessionTickets : 1; /* 18 */
-+ unsigned int enableDeflate : 1; /* 19 */
-+ unsigned int enableRenegotiation : 2; /* 20-21 */
-+ unsigned int requireSafeNegotiation : 1; /* 22 */
-+ unsigned int enableFalseStart : 1; /* 23 */
-+ unsigned int cbcRandomIV : 1; /* 24 */
-+ unsigned int enableOCSPStapling : 1; /* 25 */
-+ unsigned int enableSignedCertTimestamps : 1; /* 26 */
++/* Request Signed Certificate Timestamps via TLS extension (client) */
++#define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 27
++
+ #ifdef SSL_DEPRECATED_FUNCTION
+ /* Old deprecated function names */
+ SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
+@@ -483,6 +486,23 @@ SSL_IMPORT CERTCertList *SSL_PeerCertifi
+ */
+ SSL_IMPORT const SECItemArray * SSL_PeerStapledOCSPResponses(PRFileDesc *fd);
+
++/* SSL_PeerSignedCertTimestamps returns the signed_certificate_timestamp
++ * extension data provided by the TLS server. The return value is a pointer
++ * to an internal SECItem that contains the returned response (as a serialized
++ * SignedCertificateTimestampList, see RFC 6962). The returned pointer is only
++ * valid until the callback function that calls SSL_PeerSignedCertTimestamps
++ * (e.g. the authenticate certificate hook, or the handshake callback) returns.
++ *
++ * If no Signed Certificate Timestamps were given by the server then the result
++ * will be empty. If there was an error, then the result will be NULL.
++ *
++ * You must set the SSL_ENABLE_SIGNED_CERT_TIMESTAMPS option to indicate support
++ * for Signed Certificate Timestamps to a server.
++ *
++ * libssl does not do any parsing or validation of the response itself.
++ */
++SSL_IMPORT const SECItem * SSL_PeerSignedCertTimestamps(PRFileDesc *fd);
++
+ /* SSL_SetStapledOCSPResponses stores an array of one or multiple OCSP responses
+ * in the fd's data, which may be sent as part of a server side cert_status
+ * handshake message. Parameter |responses| is for the server certificate of
+diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:11:28.314468184 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:27:22.540248428 -0800
+@@ -337,6 +337,7 @@ typedef struct sslOptionsStr {
+ unsigned int enableOCSPStapling : 1; /* 25 */
+ unsigned int enableNPN : 1; /* 26 */
+ unsigned int enableALPN : 1; /* 27 */
++ unsigned int enableSignedCertTimestamps : 1; /* 28 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
-@@ -713,6 +714,11 @@ struct sslSessionIDStr {
- * negotiated as it's used to bind the ChannelID signature on the
+@@ -719,6 +720,11 @@ struct sslSessionIDStr {
* resumption handshake to the original handshake. */
SECItem originalHandshakeHash;
-+
+
+ /* Signed certificate timestamps received in a TLS extension.
+ ** (used only in client).
+ */
+ SECItem signedCertTimestamps;
- } ssl3;
- } u;
- };
-@@ -804,6 +810,18 @@ struct TLSExtensionDataStr {
++
+ /* This lock is lazily initialized by CacheSID when a sid is first
+ * cached. Before then, there is no need to lock anything because
+ * the sid isn't being shared by anything.
+@@ -827,6 +833,18 @@ struct TLSExtensionDataStr {
* is beyond ssl3_HandleClientHello function. */
SECItem *sniNameArr;
PRUint32 sniNameArrSize;
@@ -264,107 +215,75 @@ index 79aca60..1e4655f 100644
};
typedef SECStatus (*sslRestartTarget)(sslSocket *);
-diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c
-index eb5004c..1ca19ca 100644
---- a/net/third_party/nss/ssl/sslnonce.c
-+++ b/net/third_party/nss/ssl/sslnonce.c
-@@ -122,7 +122,21 @@ ssl_DestroySID(sslSessionID *sid)
- if (sid->version < SSL_LIBRARY_VERSION_3_0) {
- SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
- SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
-+ } else {
-+ if (sid->u.ssl3.sessionTicket.ticket.data) {
-+ SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
-+ }
-+ if (sid->u.ssl3.srvName.data) {
-+ SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
-+ }
-+ if (sid->u.ssl3.signedCertTimestamps.data) {
-+ SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
-+ }
-+ if (sid->u.ssl3.originalHandshakeHash.data) {
-+ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
-+ }
- }
-+
- if (sid->peerID != NULL)
- PORT_Free((void *)sid->peerID); /* CONST */
-
-@@ -142,16 +156,7 @@ ssl_DestroySID(sslSessionID *sid)
- if ( sid->localCert ) {
- CERT_DestroyCertificate(sid->localCert);
- }
-- if (sid->u.ssl3.sessionTicket.ticket.data) {
-- SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
-- }
-- if (sid->u.ssl3.srvName.data) {
-- SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
-- }
-- if (sid->u.ssl3.originalHandshakeHash.data) {
-- SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
-- }
--
-+
- PORT_ZFree(sid, sizeof(sslSessionID));
- }
+diff -pu a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
+--- a/nss/lib/ssl/sslnonce.c 2014-01-17 18:11:28.314468184 -0800
++++ b/nss/lib/ssl/sslnonce.c 2014-01-17 18:23:17.956207890 -0800
+@@ -131,6 +131,9 @@ ssl_DestroySID(sslSessionID *sid)
+ if (sid->u.ssl3.originalHandshakeHash.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
+ }
++ if (sid->u.ssl3.signedCertTimestamps.data) {
++ SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
++ }
-diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
-index b5c17f0..965215d 100644
---- a/net/third_party/nss/ssl/sslsock.c
-+++ b/net/third_party/nss/ssl/sslsock.c
-@@ -173,7 +173,8 @@ static sslOptions ssl_defaults = {
- PR_FALSE, /* requireSafeNegotiation */
- PR_FALSE, /* enableFalseStart */
+ if (sid->u.ssl3.lock) {
+ PR_DestroyRWLock(sid->u.ssl3.lock);
+diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 18:04:43.127747463 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 18:44:09.246889487 -0800
+@@ -87,7 +87,8 @@ static sslOptions ssl_defaults = {
PR_TRUE, /* cbcRandomIV */
-- PR_FALSE /* enableOCSPStapling */
-+ PR_FALSE, /* enableOCSPStapling */
+ PR_FALSE, /* enableOCSPStapling */
+ PR_TRUE, /* enableNPN */
+- PR_FALSE /* enableALPN */
++ PR_FALSE, /* enableALPN */
+ PR_FALSE /* enableSignedCertTimestamps */
};
/*
-@@ -865,6 +866,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
- ss->opt.enableOCSPStapling = on;
- break;
+@@ -787,6 +788,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
+ ss->opt.enableALPN = on;
+ break;
+ case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
-+ ss->opt.enableSignedCertTimestamps = on;
-+ break;
++ ss->opt.enableSignedCertTimestamps = on;
++ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
-@@ -935,6 +940,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
- case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
- case SSL_CBC_RANDOM_IV: on = ss->opt.cbcRandomIV; break;
+@@ -859,6 +864,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
+ case SSL_ENABLE_NPN: on = ss->opt.enableNPN; break;
+ case SSL_ENABLE_ALPN: on = ss->opt.enableALPN; break;
+ case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
-+ on = ss->opt.enableSignedCertTimestamps;
-+ break;
++ on = ss->opt.enableSignedCertTimestamps;
++ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -996,6 +1004,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
- case SSL_ENABLE_OCSP_STAPLING:
- on = ssl_defaults.enableOCSPStapling;
+@@ -922,6 +930,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
break;
+ case SSL_ENABLE_NPN: on = ssl_defaults.enableNPN; break;
+ case SSL_ENABLE_ALPN: on = ssl_defaults.enableALPN; break;
+ case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
-+ on = ssl_defaults.enableSignedCertTimestamps;
-+ break;
++ on = ssl_defaults.enableSignedCertTimestamps;
++ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
-@@ -1163,6 +1174,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
- ssl_defaults.enableOCSPStapling = on;
- break;
+@@ -1097,6 +1108,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
+ ssl_defaults.enableALPN = on;
+ break;
+ case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
-+ ssl_defaults.enableSignedCertTimestamps = on;
-+ break;
++ ssl_defaults.enableSignedCertTimestamps = on;
++ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
-@@ -1993,6 +2008,29 @@ SSL_PeerStapledOCSPResponses(PRFileDesc *fd)
+@@ -1921,6 +1936,29 @@ SSL_PeerStapledOCSPResponses(PRFileDesc
return &ss->sec.ci.sid->peerCertStatus;
}
@@ -394,15 +313,9 @@ index b5c17f0..965215d 100644
SECStatus
SSL_HandshakeResumedSession(PRFileDesc *fd, PRBool *handshake_resumed) {
sslSocket *ss = ssl_FindSocket(fd);
-@@ -3133,4 +3171,3 @@ loser:
- }
- return ss;
- }
--
-diff --git a/net/third_party/nss/ssl/sslt.h b/net/third_party/nss/ssl/sslt.h
-index b813c04..1f5e2c6 100644
---- a/net/third_party/nss/ssl/sslt.h
-+++ b/net/third_party/nss/ssl/sslt.h
+diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
+--- a/nss/lib/ssl/sslt.h 2014-01-17 18:10:16.793281867 -0800
++++ b/nss/lib/ssl/sslt.h 2014-01-17 18:23:17.956207890 -0800
@@ -202,6 +202,7 @@ typedef enum {
ssl_signature_algorithms_xtn = 13,
ssl_use_srtp_xtn = 14,
diff --git a/chromium/net/third_party/nss/patches/sslnoncestatics.patch b/chromium/net/third_party/nss/patches/sslnoncestatics.patch
deleted file mode 100644
index 336fe1d5587..00000000000
--- a/chromium/net/third_party/nss/patches/sslnoncestatics.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c
-index 758aa4e..a3e6e0a 100644
---- a/net/third_party/nss/ssl/sslnonce.c
-+++ b/net/third_party/nss/ssl/sslnonce.c
-@@ -21,8 +21,8 @@
- PRUint32 ssl_sid_timeout = 100;
- PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
-
--static sslSessionID *cache = NULL;
--static PZLock * cacheLock = NULL;
-+sslSessionID *cache = NULL;
-+PZLock * cacheLock = NULL;
-
- /* sids can be in one of 4 states:
- *
diff --git a/chromium/net/third_party/nss/patches/sslsock_903565.patch b/chromium/net/third_party/nss/patches/sslsock_903565.patch
deleted file mode 100644
index b744c584a3d..00000000000
--- a/chromium/net/third_party/nss/patches/sslsock_903565.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
-index fd71aee..db0da5f 100644
---- a/net/third_party/nss/ssl/sslsock.c
-+++ b/net/third_party/nss/ssl/sslsock.c
-@@ -3057,6 +3057,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
- ss->opt.useSocks = PR_FALSE;
- ss->opt.noLocks = !makeLocks;
- ss->vrange = *VERSIONS_DEFAULTS(protocolVariant);
-+ ss->protocolVariant = protocolVariant;
-
- ss->peerID = NULL;
- ss->rTimeout = PR_INTERVAL_NO_TIMEOUT;
-@@ -3117,7 +3118,6 @@ loser:
- PORT_Free(ss);
- ss = NULL;
- }
-- ss->protocolVariant = protocolVariant;
- }
- return ss;
- }
diff --git a/chromium/net/third_party/nss/patches/suitebonly.patch b/chromium/net/third_party/nss/patches/suitebonly.patch
index 762606dca30..c91314be887 100644
--- a/chromium/net/third_party/nss/patches/suitebonly.patch
+++ b/chromium/net/third_party/nss/patches/suitebonly.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
---- a/nss/lib/ssl/ssl3ecc.c 2013-07-31 14:11:13.633890784 -0700
-+++ b/nss/lib/ssl/ssl3ecc.c 2013-07-31 14:12:59.855450386 -0700
-@@ -1078,6 +1078,7 @@ static const PRUint8 ecPtFmt[6] = {
+--- a/nss/lib/ssl/ssl3ecc.c 2014-01-03 19:28:03.550814608 -0800
++++ b/nss/lib/ssl/ssl3ecc.c 2014-01-03 19:40:46.523288747 -0800
+@@ -1073,6 +1073,7 @@ static const PRUint8 ecPtFmt[6] = {
static PRBool
ssl3_SuiteBOnly(sslSocket *ss)
{
@@ -9,7 +9,7 @@ diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
/* See if we can support small curves (like 163). If not, assume we can
* only support Suite-B curves (P-256, P-384, P-521). */
PK11SlotInfo *slot =
-@@ -1091,6 +1092,9 @@ ssl3_SuiteBOnly(sslSocket *ss)
+@@ -1086,6 +1087,9 @@ ssl3_SuiteBOnly(sslSocket *ss)
/* we can, presume we can do all curves */
PK11_FreeSlot(slot);
return PR_FALSE;
diff --git a/chromium/net/third_party/nss/patches/tls12backuphash.patch b/chromium/net/third_party/nss/patches/tls12backuphash.patch
deleted file mode 100644
index 92dd9183a07..00000000000
--- a/chromium/net/third_party/nss/patches/tls12backuphash.patch
+++ /dev/null
@@ -1,220 +0,0 @@
-Index: net/third_party/nss/ssl/ssl3con.c
-===================================================================
---- net/third_party/nss/ssl/ssl3con.c (revision 220594)
-+++ net/third_party/nss/ssl/ssl3con.c (working copy)
-@@ -3933,6 +3933,20 @@
- ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
- return SECFailure;
- }
-+
-+ /* A backup SHA-1 hash for a potential client auth signature. */
-+ if (!ss->sec.isServer) {
-+ ss->ssl3.hs.md5 = PK11_CreateDigestContext(SEC_OID_SHA1);
-+ if (ss->ssl3.hs.md5 == NULL) {
-+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
-+ return SECFailure;
-+ }
-+
-+ if (PK11_DigestBegin(ss->ssl3.hs.md5) != SECSuccess) {
-+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
-+ return SECFailure;
-+ }
-+ }
- } else {
- /* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or
- * created successfully. */
-@@ -4043,6 +4057,13 @@
- ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
- return rv;
- }
-+ if (ss->ssl3.hs.md5) {
-+ rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
-+ if (rv != SECSuccess) {
-+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
-+ return rv;
-+ }
-+ }
- } else {
- rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
- if (rv != SECSuccess) {
-@@ -4791,6 +4812,30 @@
- return rv;
- }
-
-+static SECStatus
-+ssl3_ComputeBackupHandshakeHashes(sslSocket * ss,
-+ SSL3Hashes * hashes) /* output goes here. */
-+{
-+ SECStatus rv = SECSuccess;
-+
-+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
-+ PORT_Assert( ss->ssl3.hs.hashType == handshake_hash_single );
-+
-+ rv = PK11_DigestFinal(ss->ssl3.hs.md5, hashes->u.raw, &hashes->len,
-+ sizeof(hashes->u.raw));
-+ if (rv != SECSuccess) {
-+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
-+ rv = SECFailure;
-+ goto loser;
-+ }
-+ hashes->hashAlg = SEC_OID_SHA1;
-+
-+loser:
-+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
-+ ss->ssl3.hs.md5 = NULL;
-+ return rv;
-+}
-+
- /*
- * SSL 2 based implementations pass in the initial outbound buffer
- * so that the handshake hash can contain the included information.
-@@ -6044,7 +6089,17 @@
- SSL_GETPID(), ss->fd));
-
- ssl_GetSpecReadLock(ss);
-- rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
-+ /* In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the handshake hash
-+ * function (SHA-256). If the server or the client does not support SHA-256
-+ * as a signature hash, we can either maintain a backup SHA-1 handshake
-+ * hash or buffer all handshake messages.
-+ */
-+ if (ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
-+ rv = ssl3_ComputeBackupHandshakeHashes(ss, &hashes);
-+ PORT_Assert(ss->ssl3.hs.md5 == NULL);
-+ } else {
-+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
-+ }
- ssl_ReleaseSpecReadLock(ss);
- if (rv != SECSuccess) {
- goto done; /* err code was set by ssl3_ComputeHandshakeHashes */
-@@ -6098,11 +6153,6 @@
- if (rv != SECSuccess) {
- goto done;
- }
-- /* We always sign using the handshake hash function. It's possible that
-- * a server could support SHA-256 as the handshake hash but not as a
-- * signature hash. In that case we wouldn't be able to do client
-- * certificates with it. The alternative is to buffer all handshake
-- * messages. */
- sigAndHash.hashAlg = hashes.hashAlg;
-
- rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash);
-@@ -6802,6 +6852,70 @@
- }
-
-
-+/*
-+ * Returns true if the client authentication key is an RSA or DSA key that
-+ * may be able to sign only SHA-1 hashes.
-+ */
-+static PRBool
-+ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
-+{
-+ SECKEYPublicKey *pubk;
-+ PRBool prefer_sha1 = PR_FALSE;
-+
-+#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32)
-+ /* If the key is in CAPI, assume conservatively that the CAPI service
-+ * provider may be unable to sign SHA-256 hashes.
-+ */
-+ if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
-+ /* CAPI only supports RSA and DSA signatures, so we don't need to
-+ * check the key type. */
-+ return PR_TRUE;
-+ }
-+#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */
-+
-+ /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
-+ * it may be unable to sign SHA-256 hashes. This is the case for older
-+ * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
-+ * older, DSA key size is at most 1024 bits and the hash function must
-+ * be SHA-1.
-+ */
-+ pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
-+ if (pubk == NULL) {
-+ return PR_FALSE;
-+ }
-+ if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) {
-+ prefer_sha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
-+ }
-+ SECKEY_DestroyPublicKey(pubk);
-+ return prefer_sha1;
-+}
-+
-+/* Destroys the backup handshake hash context if we don't need it. */
-+static void
-+ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
-+ const SECItem *algorithms)
-+{
-+ PRBool need_backup_hash = PR_FALSE;
-+ unsigned int i;
-+
-+ PORT_Assert(ss->ssl3.hs.md5);
-+ if (ssl3_ClientKeyPrefersSHA1(ss)) {
-+ /* Use SHA-1 if the server supports it. */
-+ for (i = 0; i < algorithms->len; i += 2) {
-+ if (algorithms->data[i] == tls_hash_sha1 &&
-+ (algorithms->data[i+1] == tls_sig_rsa ||
-+ algorithms->data[i+1] == tls_sig_dsa)) {
-+ need_backup_hash = PR_TRUE;
-+ break;
-+ }
-+ }
-+ }
-+ if (!need_backup_hash) {
-+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
-+ ss->ssl3.hs.md5 = NULL;
-+ }
-+}
-+
- typedef struct dnameNode {
- struct dnameNode *next;
- SECItem name;
-@@ -6994,6 +7108,9 @@
- }
- goto send_no_certificate;
- }
-+ if (isTLS12) {
-+ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
-+ }
- break; /* not an error */
- }
- #endif /* NSS_PLATFORM_CLIENT_AUTH */
-@@ -7029,6 +7146,9 @@
- }
- goto send_no_certificate;
- }
-+ if (isTLS12) {
-+ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
-+ }
- break; /* not an error */
-
- case SECFailure:
-@@ -7227,6 +7347,13 @@
- (ss->ssl3.platformClientKey ||
- ss->ssl3.clientPrivateKey != NULL);
-
-+ if (!sendClientCert &&
-+ ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
-+ /* Don't need the backup handshake hash. */
-+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
-+ ss->ssl3.hs.md5 = NULL;
-+ }
-+
- /* We must wait for the server's certificate to be authenticated before
- * sending the client certificate in order to disclosing the client
- * certificate to an attacker that does not have a valid cert for the
-Index: net/third_party/nss/ssl/sslimpl.h
-===================================================================
---- net/third_party/nss/ssl/sslimpl.h (revision 220594)
-+++ net/third_party/nss/ssl/sslimpl.h (working copy)
-@@ -838,6 +838,9 @@
- * SSL 3.0 - TLS 1.1 use both |md5| and |sha|. |md5| is used for MD5 and
- * |sha| for SHA-1.
- * TLS 1.2 and later use only |sha|, for SHA-256. */
-+ /* NOTE: On the client side, TLS 1.2 and later use |md5| as a backup
-+ * handshake hash for generating client auth signatures. Confusingly, the
-+ * backup hash function is SHA-1. */
- PK11Context * md5;
- PK11Context * sha;
-
diff --git a/chromium/net/third_party/nss/patches/tls12backuphash2.patch b/chromium/net/third_party/nss/patches/tls12backuphash2.patch
deleted file mode 100644
index 85e5308db6b..00000000000
--- a/chromium/net/third_party/nss/patches/tls12backuphash2.patch
+++ /dev/null
@@ -1,127 +0,0 @@
-diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
-index 06992e0..cf7ef32 100644
---- a/net/third_party/nss/ssl/ssl3con.c
-+++ b/net/third_party/nss/ssl/ssl3con.c
-@@ -6973,14 +6973,27 @@ no_memory: /* no-memory error has already been set. */
-
-
- /*
-- * Returns true if the client authentication key is an RSA or DSA key that
-- * may be able to sign only SHA-1 hashes.
-+ * Returns the TLS signature algorithm for the client authentication key and
-+ * whether it is an RSA or DSA key that may be able to sign only SHA-1 hashes.
- */
--static PRBool
--ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
-+static SECStatus
-+ssl3_ExtractClientKeyInfo(sslSocket *ss,
-+ TLSSignatureAlgorithm *sigAlg,
-+ PRBool *preferSha1)
- {
-+ SECStatus rv = SECSuccess;
- SECKEYPublicKey *pubk;
-- PRBool prefer_sha1 = PR_FALSE;
-+
-+ pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
-+ if (pubk == NULL) {
-+ rv = SECFailure;
-+ goto done;
-+ }
-+
-+ rv = ssl3_TLSSignatureAlgorithmForKeyType(pubk->keyType, sigAlg);
-+ if (rv != SECSuccess) {
-+ goto done;
-+ }
-
- #if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32)
- /* If the key is in CAPI, assume conservatively that the CAPI service
-@@ -6989,7 +7002,8 @@ ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
- if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
- /* CAPI only supports RSA and DSA signatures, so we don't need to
- * check the key type. */
-- return PR_TRUE;
-+ *preferSha1 = PR_TRUE;
-+ goto done;
- }
- #endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */
-
-@@ -6999,38 +7013,61 @@ ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
- * older, DSA key size is at most 1024 bits and the hash function must
- * be SHA-1.
- */
-- pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
-- if (pubk == NULL) {
-- return PR_FALSE;
-- }
- if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) {
-- prefer_sha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
-+ *preferSha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
-+ } else {
-+ *preferSha1 = PR_FALSE;
- }
-- SECKEY_DestroyPublicKey(pubk);
-- return prefer_sha1;
-+
-+ done:
-+ if (pubk)
-+ SECKEY_DestroyPublicKey(pubk);
-+ return rv;
- }
-
--/* Destroys the backup handshake hash context if we don't need it. */
-+/* Destroys the backup handshake hash context if we don't need it. Note that
-+ * this function selects the hash algorithm for client authentication
-+ * signatures; ssl3_SendCertificateVerify uses the presence of the backup hash
-+ * to determine whether to use SHA-1 or SHA-256. */
- static void
- ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
- const SECItem *algorithms)
- {
-- PRBool need_backup_hash = PR_FALSE;
-+ SECStatus rv;
-+ TLSSignatureAlgorithm sigAlg;
-+ PRBool preferSha1;
-+ PRBool supportsSha1 = PR_FALSE;
-+ PRBool supportsSha256 = PR_FALSE;
-+ PRBool needBackupHash = PR_FALSE;
- unsigned int i;
-
- PORT_Assert(ss->ssl3.hs.md5);
-- if (ssl3_ClientKeyPrefersSHA1(ss)) {
-- /* Use SHA-1 if the server supports it. */
-- for (i = 0; i < algorithms->len; i += 2) {
-- if (algorithms->data[i] == tls_hash_sha1 &&
-- (algorithms->data[i+1] == tls_sig_rsa ||
-- algorithms->data[i+1] == tls_sig_dsa)) {
-- need_backup_hash = PR_TRUE;
-- break;
-+
-+ /* Determine the key's signature algorithm and whether it prefers SHA-1. */
-+ rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1);
-+ if (rv != SECSuccess) {
-+ goto done;
-+ }
-+
-+ /* Determine the server's hash support for that signature algorithm. */
-+ for (i = 0; i < algorithms->len; i += 2) {
-+ if (algorithms->data[i+1] == sigAlg) {
-+ if (algorithms->data[i] == tls_hash_sha1) {
-+ supportsSha1 = PR_TRUE;
-+ } else if (algorithms->data[i] == tls_hash_sha256) {
-+ supportsSha256 = PR_TRUE;
- }
- }
- }
-- if (!need_backup_hash) {
-+
-+ /* If either the server does not support SHA-256 or the client key prefers
-+ * SHA-1, leave the backup hash. */
-+ if (supportsSha1 && (preferSha1 || !supportsSha256)) {
-+ needBackupHash = PR_TRUE;
-+ }
-+
-+done:
-+ if (!needBackupHash) {
- PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
- ss->ssl3.hs.md5 = NULL;
- }
diff --git a/chromium/net/third_party/nss/patches/tls12chromium.patch b/chromium/net/third_party/nss/patches/tls12chromium.patch
index 671b6ca3e13..0d6d407cf2b 100644
--- a/chromium/net/third_party/nss/patches/tls12chromium.patch
+++ b/chromium/net/third_party/nss/patches/tls12chromium.patch
@@ -1,6 +1,6 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 14:12:19.414856329 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 14:13:56.916288878 -0700
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:00:11.213237373 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:04:22.497405273 -0800
@@ -31,6 +31,15 @@
#include "blapi.h"
#endif
@@ -18,8 +18,8 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
#ifdef NSS_ENABLE_ZLIB
#include "zlib.h"
diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
---- a/nss/lib/ssl/ssl3ecc.c 2013-07-31 14:13:15.115674638 -0700
-+++ b/nss/lib/ssl/ssl3ecc.c 2013-07-31 14:13:56.916288878 -0700
+--- a/nss/lib/ssl/ssl3ecc.c 2014-01-17 18:01:31.474568608 -0800
++++ b/nss/lib/ssl/ssl3ecc.c 2014-01-17 18:04:22.497405273 -0800
@@ -30,6 +30,12 @@
#include <stdio.h>
@@ -32,10 +32,10 @@ diff -pu a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
+
#ifdef NSS_ENABLE_ECC
- /*
+ #ifndef PK11_SETATTRS
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 14:10:35.113325316 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 14:16:39.538677991 -0700
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 18:00:11.213237373 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 18:04:22.497405273 -0800
@@ -17,8 +17,15 @@
#ifndef NO_PKCS11_BYPASS
#include "blapi.h"
@@ -51,8 +51,8 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
+
#define SET_ERROR_CODE /* reminder */
- struct cipherPolicyStr {
-@@ -1900,6 +1907,24 @@ SSL_VersionRangeGet(PRFileDesc *fd, SSLV
+ static const sslSocketOps ssl_default_ops = { /* No SSL. */
+@@ -1836,6 +1843,24 @@ SSL_VersionRangeGet(PRFileDesc *fd, SSLV
return SECSuccess;
}
@@ -77,7 +77,7 @@ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
SECStatus
SSL_VersionRangeSet(PRFileDesc *fd, const SSLVersionRange *vrange)
{
-@@ -1920,6 +1945,20 @@ SSL_VersionRangeSet(PRFileDesc *fd, cons
+@@ -1856,6 +1881,20 @@ SSL_VersionRangeSet(PRFileDesc *fd, cons
ssl_GetSSL3HandshakeLock(ss);
ss->vrange = *vrange;
diff --git a/chromium/net/third_party/nss/patches/tlsunique.patch b/chromium/net/third_party/nss/patches/tlsunique.patch
index 69370a35e30..5b094a7c811 100644
--- a/chromium/net/third_party/nss/patches/tlsunique.patch
+++ b/chromium/net/third_party/nss/patches/tlsunique.patch
@@ -1,7 +1,7 @@
diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
---- a/nss/lib/ssl/ssl3con.c 2013-07-31 14:10:00.332814714 -0700
-+++ b/nss/lib/ssl/ssl3con.c 2013-07-31 14:10:12.202988980 -0700
-@@ -11732,6 +11732,68 @@ ssl3_InitSocketPolicy(sslSocket *ss)
+--- a/nss/lib/ssl/ssl3con.c 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/ssl3con.c 2014-01-17 17:59:45.862816905 -0800
+@@ -12383,6 +12383,68 @@ ssl3_InitSocketPolicy(sslSocket *ss)
PORT_Memcpy(ss->cipherSuites, cipherSuites, sizeof cipherSuites);
}
@@ -71,9 +71,9 @@ diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
* the caller of this function.
*/
diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
---- a/nss/lib/ssl/ssl.h 2013-07-31 14:10:00.342814862 -0700
-+++ b/nss/lib/ssl/ssl.h 2013-07-31 14:10:12.202988980 -0700
-@@ -249,6 +249,27 @@ SSL_IMPORT SECStatus SSL_CipherPrefGetDe
+--- a/nss/lib/ssl/ssl.h 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/ssl.h 2014-01-17 17:59:45.862816905 -0800
+@@ -282,6 +282,27 @@ SSL_IMPORT SECStatus SSL_CipherPrefGetDe
SSL_IMPORT SECStatus SSL_CipherPolicySet(PRInt32 cipher, PRInt32 policy);
SSL_IMPORT SECStatus SSL_CipherPolicyGet(PRInt32 cipher, PRInt32 *policy);
@@ -102,9 +102,9 @@ diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h
**
** This API should be used to control SSL 3.0 & TLS support instead of the
diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
---- a/nss/lib/ssl/sslimpl.h 2013-07-31 14:10:00.342814862 -0700
-+++ b/nss/lib/ssl/sslimpl.h 2013-07-31 14:10:12.202988980 -0700
-@@ -1770,6 +1770,11 @@ extern PRBool ssl_GetSessionTicketKeysPK
+--- a/nss/lib/ssl/sslimpl.h 2014-01-17 17:59:03.242109996 -0800
++++ b/nss/lib/ssl/sslimpl.h 2014-01-17 17:59:45.862816905 -0800
+@@ -1853,6 +1853,11 @@ extern PRBool ssl_GetSessionTicketKeysPK
extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data,
unsigned int length);
@@ -117,9 +117,9 @@ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
extern void ssl_FreePRSocket(PRFileDesc *fd);
diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 14:10:00.342814862 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 14:10:12.202988980 -0700
-@@ -1366,6 +1366,27 @@ NSS_SetFrancePolicy(void)
+--- a/nss/lib/ssl/sslsock.c 2014-01-17 17:59:03.252110162 -0800
++++ b/nss/lib/ssl/sslsock.c 2014-01-17 17:59:45.872817074 -0800
+@@ -1308,6 +1308,27 @@ NSS_SetFrancePolicy(void)
return NSS_SetDomesticPolicy();
}
diff --git a/chromium/net/third_party/nss/patches/versionskew.patch b/chromium/net/third_party/nss/patches/versionskew.patch
deleted file mode 100644
index 1a9061e46ac..00000000000
--- a/chromium/net/third_party/nss/patches/versionskew.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-diff -pu a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c
---- a/nss/lib/ssl/sslsecur.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslsecur.c 2013-07-31 12:27:24.322323927 -0700
-@@ -1311,6 +1311,10 @@ SSL_SetURL(PRFileDesc *fd, const char *u
- SECStatus
- SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
- {
-+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
-+ PR_NOT_REACHED("not implemented");
-+ return SECFailure;
-+#if 0
- sslSocket * ss = ssl_FindSocket(fd);
- CERTDistNames *names = NULL;
-
-@@ -1338,6 +1342,7 @@ SSL_SetTrustAnchors(PRFileDesc *fd, CERT
- ssl_Release1stHandshakeLock(ss);
-
- return SECSuccess;
-+#endif
- }
-
- /*
-diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
---- a/nss/lib/ssl/sslsock.c 2013-07-31 12:07:10.974699609 -0700
-+++ b/nss/lib/ssl/sslsock.c 2013-07-31 12:27:24.322323927 -0700
-@@ -1625,6 +1625,11 @@ SSL_GetSRTPCipher(PRFileDesc *fd, PRUint
- PRFileDesc *
- SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
- {
-+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
-+ PR_NOT_REACHED("not implemented");
-+ return NULL;
-+
-+#if 0
- sslSocket * sm = NULL, *ss = NULL;
- int i;
- sslServerCerts * mc = NULL;
-@@ -1742,6 +1747,7 @@ SSL_ReconfigFD(PRFileDesc *model, PRFile
- return fd;
- loser:
- return NULL;
-+#endif
- }
-
- PRBool
diff --git a/chromium/net/third_party/nss/ssl.gyp b/chromium/net/third_party/nss/ssl.gyp
index 986b563ebf8..f0c47d6feea 100644
--- a/chromium/net/third_party/nss/ssl.gyp
+++ b/chromium/net/third_party/nss/ssl.gyp
@@ -8,7 +8,7 @@
'conditions': [
['sysroot!=""', {
'variables': {
- 'pkg-config': '../../../build/linux/pkg-config-wrapper "<(sysroot)" "<(target_arch)"',
+ 'pkg-config': '../../../build/linux/pkg-config-wrapper "<(sysroot)" "<(target_arch)" "<(system_libdir)"',
},
}, {
'variables': {
diff --git a/chromium/net/third_party/nss/ssl/BUILD.gn b/chromium/net/third_party/nss/ssl/BUILD.gn
new file mode 100644
index 00000000000..7f3ad2ca834
--- /dev/null
+++ b/chromium/net/third_party/nss/ssl/BUILD.gn
@@ -0,0 +1,139 @@
+# 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.
+
+config("ssl_config") {
+ include_dirs = [ "." ]
+
+ if (is_mac || is_win) {
+ defines = [ "NSS_PLATFORM_CLIENT_AUTH" ]
+ }
+}
+
+component("libssl") {
+ output_name = "crssl"
+
+ sources = [
+ "authcert.c",
+ "cmpcert.c",
+ "derive.c",
+ "dtlscon.c",
+ "preenc.h",
+ "prelib.c",
+ "ssl.h",
+ "ssl3con.c",
+ "ssl3ecc.c",
+ "ssl3ext.c",
+ "ssl3gthr.c",
+ "ssl3prot.h",
+ "sslauth.c",
+ "sslcon.c",
+ "ssldef.c",
+ "sslenum.c",
+ "sslerr.c",
+ "sslerr.h",
+ "SSLerrs.h",
+ "sslerrstrs.c",
+ "sslgathr.c",
+ "sslimpl.h",
+ "sslinfo.c",
+ "sslinit.c",
+ "sslmutex.c",
+ "sslmutex.h",
+ "sslnonce.c",
+ "sslplatf.c",
+ "sslproto.h",
+ "sslreveal.c",
+ "sslsecur.c",
+ "sslsnce.c",
+ "sslsock.c",
+ "sslt.h",
+ "ssltrace.c",
+ "sslver.c",
+ "unix_err.c",
+ "unix_err.h",
+ "win32err.c",
+ "win32err.h",
+ "bodge/secitem_array.c",
+ ]
+
+ direct_dependent_configs = [ ":ssl_config" ]
+
+ cflags = []
+ defines = [
+ "NO_PKCS11_BYPASS",
+ "NSS_ENABLE_ECC",
+ "USE_UTIL_DIRECTLY",
+ ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+
+ if (is_win) {
+ cflags += [ "/wd4267" ] # Disable warning: Conversion from size_t to 'type'.
+
+ sources -= [
+ "unix_err.c",
+ "unix_err.h",
+ ]
+ sources += [ "exports_win.def" ]
+ } else if (is_linux) {
+ if (component_mode == "shared_library") {
+ configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+ }
+
+ libs = [ "dl" ]
+
+ include_dirs = [ "bodge" ]
+
+ # Must be after ssl_config since we want our SSL headers to take
+ # precedence.
+ direct_dependent_configs += [
+ "//third_party/nss:system_nss_no_ssl_config"
+ ]
+ } else if (is_mac) {
+ libs = [ "Security.framework" ]
+ }
+
+ if (is_posix) {
+ sources -= [
+ "win32err.c",
+ "win32err.h",
+ ]
+ }
+
+ if (is_mac || is_ios) {
+ defines += [
+ "XP_UNIX",
+ "DARWIN",
+ "XP_MACOSX",
+ ]
+ }
+
+ if (is_mac || is_ios || is_win) {
+ sources -= [
+ "bodge/secitem_array.c",
+ ]
+ deps = [
+ "//third_party/nss:nspr",
+ "//third_party/nss:nss",
+ ]
+ forward_dependent_configs_from = deps
+ }
+
+ if (is_clang) {
+ cflags += [
+ # See http://crbug.com/138571#c8. In short, sslsecur.c picks up the
+ # system's cert.h because cert.h isn't in chromium's repo.
+ "-Wno-incompatible-pointer-types",
+
+ # There is a broken header guard in /usr/include/nss/secmod.h:
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=884072
+ "-Wno-header-guard",
+ ]
+ }
+
+ if (is_debug) {
+ defines += [ "DEBUG" ]
+ }
+}
diff --git a/chromium/net/third_party/nss/ssl/ssl.h b/chromium/net/third_party/nss/ssl/ssl.h
index 6f7c9889f00..51b557a7354 100644
--- a/chromium/net/third_party/nss/ssl/ssl.h
+++ b/chromium/net/third_party/nss/ssl/ssl.h
@@ -161,9 +161,29 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
*/
#define SSL_CBC_RANDOM_IV 23
#define SSL_ENABLE_OCSP_STAPLING 24 /* Request OCSP stapling (client) */
+
+/* SSL_ENABLE_NPN controls whether the NPN extension is enabled for the initial
+ * handshake when protocol negotiation is used. SSL_SetNextProtoCallback
+ * or SSL_SetNextProtoNego must be used to control the protocol negotiation;
+ * otherwise, the NPN extension will not be negotiated. SSL_ENABLE_NPN is
+ * currently enabled by default but this may change in future versions.
+ */
+#define SSL_ENABLE_NPN 25
+
+/* SSL_ENABLE_ALPN controls whether the ALPN extension is enabled for the
+ * initial handshake when protocol negotiation is used. SSL_SetNextProtoNego
+ * (not SSL_SetNextProtoCallback) must be used to control the protocol
+ * negotiation; otherwise, the ALPN extension will not be negotiated. ALPN is
+ * not negotiated for renegotiation handshakes, even though the ALPN
+ * specification defines a way to use ALPN during renegotiations.
+ * SSL_ENABLE_ALPN is currently disabled by default, but this may change in
+ * future versions.
+ */
+#define SSL_ENABLE_ALPN 26
+
/* Request Signed Certificate Timestamps via TLS extension (client) */
-#define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 25
-#define SSL_ENABLE_FALLBACK_SCSV 26 /* Send fallback SCSV in
+#define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 27
+#define SSL_ENABLE_FALLBACK_SCSV 28 /* Send fallback SCSV in
* handshakes. */
#ifdef SSL_DEPRECATED_FUNCTION
@@ -366,7 +386,7 @@ SSL_IMPORT SECStatus SSL_VersionRangeSet(PRFileDesc *fd,
const SSLVersionRange *vrange);
-/* Values for "policy" argument to SSL_PolicySet */
+/* Values for "policy" argument to SSL_CipherPolicySet */
/* Values returned by SSL_CipherPolicyGet. */
#define SSL_NOT_ALLOWED 0 /* or invalid or unimplemented */
#define SSL_ALLOWED 1
@@ -964,24 +984,20 @@ SSL_IMPORT SECStatus NSS_CmpCertChainWCANames(CERTCertificate *cert,
SSL_IMPORT SSLKEAType NSS_FindCertKEAType(CERTCertificate * cert);
/* Set cipher policies to a predefined Domestic (U.S.A.) policy.
- * This essentially enables all supported ciphers.
+ * This essentially allows all supported ciphers.
*/
SSL_IMPORT SECStatus NSS_SetDomesticPolicy(void);
/* Set cipher policies to a predefined Policy that is exportable from the USA
* according to present U.S. policies as we understand them.
- * See documentation for the list.
- * Note that your particular application program may be able to obtain
- * an export license with more or fewer capabilities than those allowed
- * by this function. In that case, you should use SSL_SetPolicy()
- * to explicitly allow those ciphers you may legally export.
+ * It is the same as NSS_SetDomesticPolicy now.
*/
SSL_IMPORT SECStatus NSS_SetExportPolicy(void);
/* Set cipher policies to a predefined Policy that is exportable from the USA
* according to present U.S. policies as we understand them, and that the
* nation of France will permit to be imported into their country.
- * See documentation for the list.
+ * It is the same as NSS_SetDomesticPolicy now.
*/
SSL_IMPORT SECStatus NSS_SetFrancePolicy(void);
diff --git a/chromium/net/third_party/nss/ssl/ssl3con.c b/chromium/net/third_party/nss/ssl/ssl3con.c
index e2be5e6f5da..7ff7beacbc1 100644
--- a/chromium/net/third_party/nss/ssl/ssl3con.c
+++ b/chromium/net/third_party/nss/ssl/ssl3con.c
@@ -111,97 +111,116 @@ static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
* precedence (desirability). It only includes cipher suites we implement.
* This table is modified by SSL3_SetPolicy(). The ordering of cipher suites
* in this table must match the ordering in SSL_ImplementedCiphers (sslenum.c)
+ *
+ * Important: See bug 946147 before enabling, reordering, or adding any cipher
+ * suites to this list.
*/
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 */
- { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+ /* cipher_suite policy enabled isPresent */
#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around
+ * bug 946147.
+ */
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_RSA_WITH_SEED_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { SSL_RSA_WITH_RC4_128_MD5, SSL_NOT_ALLOWED, PR_TRUE, PR_FALSE},
- { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+
+ /* RSA */
+ { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_SEED_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_RSA_WITH_RC4_128_MD5, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+
+ /* 56-bit DES "domestic" cipher suites */
+ { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* export ciphersuites with 1024-bit public key exchange keys */
+ { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* export ciphersuites with 512-bit public key exchange keys */
+ { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* ciphersuites with no encryption */
#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE, PR_FALSE},
-
-
- { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_NULL_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_NULL_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+};
- { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
+/* Verify that SSL_ImplementedCiphers and cipherSuites are in consistent order.
+ */
+#ifdef DEBUG
+void ssl3_CheckCipherSuiteOrderConsistency()
+{
+ unsigned int i;
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDHE_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDH_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { SSL_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_NULL_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_WITH_NULL_MD5, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ /* Note that SSL_ImplementedCiphers has more elements than cipherSuites
+ * because it SSL_ImplementedCiphers includes SSL 2.0 cipher suites.
+ */
+ PORT_Assert(SSL_NumImplementedCiphers >= PR_ARRAY_SIZE(cipherSuites));
-};
+ for (i = 0; i < PR_ARRAY_SIZE(cipherSuites); ++i) {
+ PORT_Assert(SSL_ImplementedCiphers[i] == cipherSuites[i].cipher_suite);
+ }
+}
+#endif
/* This list of SSL3 compression methods is sorted in descending order of
* precedence (desirability). It only includes compression methods we
@@ -494,8 +513,6 @@ static const SSLCipher2Mech alg2Mech[] = {
#define mmech_md5_hmac CKM_MD5_HMAC
#define mmech_sha_hmac CKM_SHA_1_HMAC
#define mmech_sha256_hmac CKM_SHA256_HMAC
-#define mmech_sha384_hmac CKM_SHA384_HMAC
-#define mmech_sha512_hmac CKM_SHA512_HMAC
static const ssl3MACDef mac_defs[] = { /* indexed by SSL3MACAlgorithm */
/* pad_size is only used for SSL 3.0 MAC. See RFC 6101 Sec. 5.2.3.1. */
@@ -839,7 +856,7 @@ count_cipher_suites(sslSocket *ss, int policy, PRBool enabled)
int i, count = 0;
if (SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) {
- return 0;
+ return 0;
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
if (config_match(&ss->cipherSuites[i], policy, enabled, &ss->vrange))
@@ -905,16 +922,10 @@ ssl3_NegotiateVersion(sslSocket *ss, SSL3ProtocolVersion peerVersion,
static SECStatus
ssl3_GetNewRandom(SSL3Random *random)
{
- PRUint32 gmt = ssl_Time();
SECStatus rv;
- random->rand[0] = (unsigned char)(gmt >> 24);
- random->rand[1] = (unsigned char)(gmt >> 16);
- random->rand[2] = (unsigned char)(gmt >> 8);
- random->rand[3] = (unsigned char)(gmt);
-
/* first 4 bytes are reserverd for time */
- rv = PK11_GenerateRandom(&random->rand[4], SSL3_RANDOM_LENGTH - 4);
+ rv = PK11_GenerateRandom(random->rand, SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
}
@@ -1052,7 +1063,7 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash, CERTCertificate *cert,
}
/* Allow DER encoded DSA signatures in SSL 3.0 */
if (isTLS || buf->len != SECKEY_SignatureLen(key)) {
- signature = DSAU_DecodeDerSig(buf);
+ signature = DSAU_DecodeDerSigToLen(buf, SECKEY_SignatureLen(key));
if (!signature) {
PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
return SECFailure;
@@ -1637,7 +1648,7 @@ ssl3_InitPendingContextsBypass(sslSocket *ss)
calg = cipher_def->calg;
- if (calg == calg_aes_gcm) {
+ if (calg == ssl_calg_aes_gcm) {
pwSpec->encode = NULL;
pwSpec->decode = NULL;
pwSpec->destroy = NULL;
@@ -1753,6 +1764,7 @@ ssl3_InitPendingContextsBypass(sslSocket *ss)
case ssl_calg_rc2:
case ssl_calg_idea:
case ssl_calg_fortezza:
+ case ssl_calg_aes_gcm:
break;
}
@@ -2506,20 +2518,6 @@ ssl3_ComputeRecordMAC(
return rv;
}
-/* This is a bodge to allow this code to be compiled against older NSS headers
- * that don't contain the CBC constant-time changes. */
-#ifndef CKM_NSS_HMAC_CONSTANT_TIME
-#define CKM_NSS_HMAC_CONSTANT_TIME (CKM_NSS + 19)
-#define CKM_NSS_SSL3_MAC_CONSTANT_TIME (CKM_NSS + 20)
-
-typedef struct CK_NSS_MAC_CONSTANT_TIME_PARAMS {
- CK_MECHANISM_TYPE macAlg; /* in */
- CK_ULONG ulBodyTotalLen; /* in */
- CK_BYTE * pHeader; /* in */
- CK_ULONG ulHeaderLen; /* in */
-} CK_NSS_MAC_CONSTANT_TIME_PARAMS;
-#endif
-
/* Called from: ssl3_HandleRecord()
* Caller must already hold the SpecReadLock. (wish we could assert that!)
*
@@ -2540,8 +2538,7 @@ ssl3_ComputeRecordMACConstantTime(
{
CK_MECHANISM_TYPE macType;
CK_NSS_MAC_CONSTANT_TIME_PARAMS params;
- PK11Context * mac_context;
- SECItem param;
+ SECItem param, inputItem, outputItem;
SECStatus rv;
PK11SymKey * key;
@@ -2573,27 +2570,34 @@ ssl3_ComputeRecordMACConstantTime(
param.len = sizeof(params);
param.type = 0;
+ inputItem.data = (unsigned char *) input;
+ inputItem.len = inputLen;
+ inputItem.type = 0;
+
+ outputItem.data = outbuf;
+ outputItem.len = *outLen;
+ outputItem.type = 0;
+
key = spec->server.write_mac_key;
if (!useServerMacKey) {
key = spec->client.write_mac_key;
}
- mac_context = PK11_CreateContextBySymKey(macType, CKA_SIGN, key, &param);
- if (mac_context == NULL) {
- /* Older versions of NSS may not support constant-time MAC. */
- goto fallback;
- }
-
- rv = PK11_DigestBegin(mac_context);
- rv |= PK11_DigestOp(mac_context, input, inputLen);
- rv |= PK11_DigestFinal(mac_context, outbuf, outLen, spec->mac_size);
- PK11_DestroyContext(mac_context, PR_TRUE);
-
- PORT_Assert(rv != SECSuccess || *outLen == (unsigned)spec->mac_size);
+ rv = PK11_SignWithSymKey(key, macType, &param, &outputItem, &inputItem);
if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_INVALID_ALGORITHM) {
+ goto fallback;
+ }
+
+ *outLen = 0;
rv = SECFailure;
ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
+ return rv;
}
+
+ PORT_Assert(outputItem.len == (unsigned)spec->mac_size);
+ *outLen = outputItem.len;
+
return rv;
fallback:
@@ -2876,8 +2880,8 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
* addition, if the record layer version number of ClientHello is { 3, 2 }
* (TLS 1.1) or higher, these servers reset the TCP connections. Lastly,
* some F5 BIG-IP servers hang if a record containing a ClientHello has a
- * version greater than 0x0301 and a length greater than 255. Set this flag
- * to work around such servers.
+ * version greater than { 3, 1 } and a length greater than 255. Set this
+ * flag to work around such servers.
*/
PRInt32
ssl3_SendRecord( sslSocket * ss,
@@ -3617,6 +3621,14 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
SSL_GETPID(), ss->fd));
if (ws != wait_change_cipher) {
+ if (IS_DTLS(ss)) {
+ /* Ignore this because it's out of order. */
+ SSL_TRC(3, ("%d: SSL3[%d]: discard out of order "
+ "DTLS change_cipher_spec",
+ SSL_GETPID(), ss->fd));
+ buf->len = 0;
+ return SECSuccess;
+ }
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
return SECFailure;
@@ -4017,15 +4029,23 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
return SECFailure;
}
- /* A backup SHA-1 hash for a potential client auth signature. */
+ /* Create a backup SHA-1 hash for a potential client auth
+ * signature.
+ *
+ * In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the
+ * handshake hash function (SHA-256). If the server or the client
+ * does not support SHA-256 as a signature hash, we can either
+ * maintain a backup SHA-1 handshake hash or buffer all handshake
+ * messages.
+ */
if (!ss->sec.isServer) {
- ss->ssl3.hs.md5 = PK11_CreateDigestContext(SEC_OID_SHA1);
- if (ss->ssl3.hs.md5 == NULL) {
+ ss->ssl3.hs.backupHash = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (ss->ssl3.hs.backupHash == NULL) {
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
return SECFailure;
}
- if (PK11_DigestBegin(ss->ssl3.hs.md5) != SECSuccess) {
+ if (PK11_DigestBegin(ss->ssl3.hs.backupHash) != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
return SECFailure;
}
@@ -4140,8 +4160,8 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b,
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return rv;
}
- if (ss->ssl3.hs.md5) {
- rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
+ if (ss->ssl3.hs.backupHash) {
+ rv = PK11_DigestOp(ss->ssl3.hs.backupHash, b, l);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
return rv;
@@ -4902,9 +4922,10 @@ ssl3_ComputeBackupHandshakeHashes(sslSocket * ss,
SECStatus rv = SECSuccess;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert( !ss->sec.isServer );
PORT_Assert( ss->ssl3.hs.hashType == handshake_hash_single );
- rv = PK11_DigestFinal(ss->ssl3.hs.md5, hashes->u.raw, &hashes->len,
+ rv = PK11_DigestFinal(ss->ssl3.hs.backupHash, hashes->u.raw, &hashes->len,
sizeof(hashes->u.raw));
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
@@ -4914,8 +4935,8 @@ ssl3_ComputeBackupHandshakeHashes(sslSocket * ss,
hashes->hashAlg = SEC_OID_SHA1;
loser:
- PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
- ss->ssl3.hs.md5 = NULL;
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
return rv;
}
@@ -4995,6 +5016,9 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
PORT_Assert(IS_DTLS(ss) || !resending);
+ SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+
/* We might be starting a session renegotiation in which case we should
* clear previous state.
*/
@@ -5119,11 +5143,6 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
requestingResume = PR_TRUE;
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
- /* Are we attempting a stateless session resume? */
- if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
- sid->u.ssl3.sessionTicket.ticket.data)
- SSL_AtomicIncrementLong(& ssl3stats.sch_sid_stateless_resumes );
-
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
@@ -5192,12 +5211,24 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
ss->ssl3.hs.sendingSCSV = PR_TRUE;
}
+ /* When we attempt session resumption (only), we must lock the sid to
+ * prevent races with other resumption connections that receive a
+ * NewSessionTicket that will cause the ticket in the sid to be replaced.
+ * Once we've copied the session ticket into our ClientHello message, it
+ * is OK for the ticket to change, so we just need to make sure we hold
+ * the lock across the calls to ssl3_CallHelloExtensionSenders.
+ */
+ if (sid->u.ssl3.lock) {
+ NSSRWLock_LockRead(sid->u.ssl3.lock);
+ }
+
if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
PRUint32 maxBytes = 65535; /* 2^16 - 1 */
PRInt32 extLen;
extLen = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes, NULL);
if (extLen < 0) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return SECFailure;
}
maxBytes -= extLen;
@@ -5224,8 +5255,10 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
- if (!num_suites)
+ if (!num_suites) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return SECFailure; /* count_cipher_suites has set error code. */
+ }
fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume ||
ss->version < sid->version);
@@ -5268,6 +5301,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
@@ -5286,18 +5320,21 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
}
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by GetNewRandom. */
}
}
rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
@@ -5307,6 +5344,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
else
rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
@@ -5314,12 +5352,14 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeVariable(
ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
@@ -5328,6 +5368,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
@@ -5336,6 +5377,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
@@ -5345,6 +5387,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
actual_count++;
if (actual_count > num_suites) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
/* set error card removal/insertion error */
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
return SECFailure;
@@ -5352,6 +5395,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
@@ -5362,12 +5406,14 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
* the server.. */
if (actual_count != num_suites) {
/* Card removal/insertion error */
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
return SECFailure;
}
rv = ssl3_AppendHandshakeNumber(ss, numCompressionMethods, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
for (i = 0; i < compressionMethodsCount; i++) {
@@ -5375,6 +5421,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
continue;
rv = ssl3_AppendHandshakeNumber(ss, compressions[i], 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
@@ -5385,23 +5432,35 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
rv = ssl3_AppendHandshakeNumber(ss, maxBytes, 2);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return rv; /* err set by AppendHandshake. */
}
extLen = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, maxBytes, NULL);
if (extLen < 0) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return SECFailure;
}
maxBytes -= extLen;
extLen = ssl3_AppendPaddingExtension(ss, paddingExtensionLen, maxBytes);
if (extLen < 0) {
+ if (sid->u.ssl3.lock) { NSSRWLock_UnlockRead(sid->u.ssl3.lock); }
return SECFailure;
}
maxBytes -= extLen;
PORT_Assert(!maxBytes);
}
+
+ if (sid->u.ssl3.lock) {
+ NSSRWLock_UnlockRead(sid->u.ssl3.lock);
+ }
+
+ if (ss->xtnData.sentSessionTicketInClientHello) {
+ SSL_AtomicIncrementLong(&ssl3stats.sch_sid_stateless_resumes);
+ }
+
if (ss->ssl3.hs.sendingSCSV) {
/* Since we sent the SCSV, pretend we sent empty RI extension. */
TLSExtensionData *xtnData = &ss->xtnData;
@@ -6208,14 +6267,10 @@ ssl3_SendCertificateVerify(sslSocket *ss)
SSL_GETPID(), ss->fd));
ssl_GetSpecReadLock(ss);
- /* In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the handshake hash
- * function (SHA-256). If the server or the client does not support SHA-256
- * as a signature hash, we can either maintain a backup SHA-1 handshake
- * hash or buffer all handshake messages.
- */
- if (ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
+ if (ss->ssl3.hs.hashType == handshake_hash_single &&
+ ss->ssl3.hs.backupHash) {
rv = ssl3_ComputeBackupHandshakeHashes(ss, &hashes);
- PORT_Assert(ss->ssl3.hs.md5 == NULL);
+ PORT_Assert(!ss->ssl3.hs.backupHash);
} else {
rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
}
@@ -6611,8 +6666,7 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits );
/* If we sent a session ticket, then this is a stateless resume. */
- if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
- sid->u.ssl3.sessionTicket.ticket.data != NULL)
+ if (ss->xtnData.sentSessionTicketInClientHello)
SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
@@ -7037,7 +7091,7 @@ ssl3_ExtractClientKeyInfo(sslSocket *ss,
*preferSha1 = PR_FALSE;
}
- done:
+done:
if (pubk)
SECKEY_DestroyPublicKey(pubk);
return rv;
@@ -7059,7 +7113,14 @@ ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
PRBool needBackupHash = PR_FALSE;
unsigned int i;
- PORT_Assert(ss->ssl3.hs.md5);
+#ifndef NO_PKCS11_BYPASS
+ /* Backup handshake hash is not supported in PKCS #11 bypass mode. */
+ if (ss->opt.bypassPKCS11) {
+ PORT_Assert(!ss->ssl3.hs.backupHash);
+ return;
+ }
+#endif
+ PORT_Assert(ss->ssl3.hs.backupHash);
/* Determine the key's signature algorithm and whether it prefers SHA-1. */
rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1);
@@ -7086,8 +7147,8 @@ ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
done:
if (!needBackupHash) {
- PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
- ss->ssl3.hs.md5 = NULL;
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
}
}
@@ -7283,7 +7344,7 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
goto send_no_certificate;
}
- if (isTLS12) {
+ if (ss->ssl3.hs.hashType == handshake_hash_single) {
ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
}
break; /* not an error */
@@ -7311,17 +7372,13 @@ ssl3_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
ss->ssl3.clientCertificate,
certUsageSSLClient, PR_FALSE);
if (ss->ssl3.clientCertChain == NULL) {
- if (ss->ssl3.clientCertificate != NULL) {
- CERT_DestroyCertificate(ss->ssl3.clientCertificate);
- ss->ssl3.clientCertificate = NULL;
- }
- if (ss->ssl3.clientPrivateKey != NULL) {
- SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
- ss->ssl3.clientPrivateKey = NULL;
- }
+ CERT_DestroyCertificate(ss->ssl3.clientCertificate);
+ ss->ssl3.clientCertificate = NULL;
+ SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
+ ss->ssl3.clientPrivateKey = NULL;
goto send_no_certificate;
}
- if (isTLS12) {
+ if (ss->ssl3.hs.hashType == handshake_hash_single) {
ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
}
break; /* not an error */
@@ -7559,10 +7616,11 @@ ssl3_SendClientSecondRound(sslSocket *ss)
ss->ssl3.clientPrivateKey != NULL);
if (!sendClientCert &&
- ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
+ ss->ssl3.hs.hashType == handshake_hash_single &&
+ ss->ssl3.hs.backupHash) {
/* Don't need the backup handshake hash. */
- PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
- ss->ssl3.hs.md5 = NULL;
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
}
/* We must wait for the server's certificate to be authenticated before
@@ -8331,7 +8389,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto alert_loser;
suite_found:
- /* Look for a matching compression algorithm. */
+ /* Select a compression algorithm. */
for (i = 0; i < comps.len; i++) {
if (!compressionEnabled(ss, comps.data[i]))
continue;
@@ -9773,8 +9831,8 @@ ssl3_SendEmptyCertificate(sslSocket *ss)
SECStatus
ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
{
- SECStatus rv;
- NewSessionTicket session_ticket;
+ SECStatus rv;
+ SECItem ticketData;
SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
SSL_GETPID(), ss->fd));
@@ -9782,35 +9840,41 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+ PORT_Assert(!ss->ssl3.hs.receivedNewSessionTicket);
+
if (ss->ssl3.hs.ws != wait_new_session_ticket) {
SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
return SECFailure;
}
- session_ticket.received_timestamp = ssl_Time();
+ /* RFC5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+ * until it has verified the server's Finished message." See the comment in
+ * ssl3_FinishHandshake for more details.
+ */
+ ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time();
if (length < 4) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
return SECFailure;
}
- session_ticket.ticket_lifetime_hint =
+ ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint =
(PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
- rv = ssl3_ConsumeHandshakeVariable(ss, &session_ticket.ticket, 2,
- &b, &length);
+ rv = ssl3_ConsumeHandshakeVariable(ss, &ticketData, 2, &b, &length);
if (length != 0 || rv != SECSuccess) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
return SECFailure; /* malformed */
}
-
- rv = ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &session_ticket);
+ rv = SECITEM_CopyItem(NULL, &ss->ssl3.hs.newSessionTicket.ticket,
+ &ticketData);
if (rv != SECSuccess) {
- (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
- PORT_SetError(SSL_ERROR_INTERNAL_ERROR_ALERT);
- return SECFailure;
+ return rv;
}
+ ss->ssl3.hs.receivedNewSessionTicket = PR_TRUE;
+
ss->ssl3.hs.ws = wait_change_cipher;
return SECSuccess;
}
@@ -10501,14 +10565,11 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error)
SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
" peer's finished message", SSL_GETPID(), ss->fd));
- PORT_Assert(!ss->firstHsDone);
- PORT_Assert(!ss->sec.isServer);
PORT_Assert(!ss->ssl3.hs.isResuming);
PORT_Assert(ss->ssl3.hs.ws != idle_handshake);
if (ss->opt.enableFalseStart &&
!ss->firstHsDone &&
- !ss->sec.isServer &&
!ss->ssl3.hs.isResuming &&
ssl3_WaitingForStartOfServerSecondRound(ss)) {
/* ssl3_SendClientSecondRound deferred the false start check because
@@ -10607,7 +10668,8 @@ ssl3_TLSPRFWithMasterSecret(ssl3CipherSpec *spec, const char *label,
return rv;
}
-/* called from ssl3_HandleServerHelloDone
+/* called from ssl3_SendClientSecondRound
+ * ssl3_HandleFinished
*/
static SECStatus
ssl3_SendNextProto(sslSocket *ss)
@@ -10617,7 +10679,7 @@ ssl3_SendNextProto(sslSocket *ss)
static const unsigned char padding[32] = {0};
if (ss->ssl3.nextProto.len == 0 ||
- ss->ssl3.nextProtoState == SSL_NEXT_PROTO_SELECTED) {
+ ss->ssl3.nextProtoState == SSL_NEXT_PROTO_SELECTED) {
return SECSuccess;
}
@@ -10880,7 +10942,7 @@ ssl3_RestartHandshakeAfterChannelIDReq(sslSocket *ss,
return SECSuccess;
}
-/* called from ssl3_HandleServerHelloDone
+/* called from ssl3_SendClientSecondRound
* ssl3_HandleClientHello
* ssl3_HandleFinished
*/
@@ -11116,6 +11178,11 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
*/
if (isServer && !ss->ssl3.hs.isResuming &&
ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) {
+ /* RFC 5077 Section 3.3: "In the case of a full handshake, the
+ * server MUST verify the client's Finished message before sending
+ * the ticket." Presumably, this also means that the client's
+ * certificate, if any, must be verified beforehand too.
+ */
rv = ssl3_SendNewSessionTicket(ss);
if (rv != SECSuccess) {
goto xmit_loser;
@@ -11240,7 +11307,27 @@ ssl3_FinishHandshake(sslSocket * ss)
/* The first handshake is now completed. */
ss->handshake = NULL;
+ /* RFC 5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+ * until it has verified the server's Finished message." When the server
+ * sends a NewSessionTicket in a resumption handshake, we must wait until
+ * the handshake is finished (we have verified the server's Finished
+ * AND the server's certificate) before we update the ticket in the sid.
+ *
+ * This must be done before we call (*ss->sec.cache)(ss->sec.ci.sid)
+ * because CacheSID requires the session ticket to already be set, and also
+ * because of the lazy lock creation scheme used by CacheSID and
+ * ssl3_SetSIDSessionTicket.
+ */
+ if (ss->ssl3.hs.receivedNewSessionTicket) {
+ PORT_Assert(!ss->sec.isServer);
+ ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ss->ssl3.hs.newSessionTicket);
+ /* The sid took over the ticket data */
+ PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+ }
+
if (ss->ssl3.hs.cacheSID && ss->sec.isServer) {
+ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
(*ss->sec.cache)(ss->sec.ci.sid);
ss->ssl3.hs.cacheSID = PR_FALSE;
}
@@ -12311,6 +12398,10 @@ ssl3_InitState(sslSocket *ss)
ss->ssl3.hs.messages.buf = NULL;
ss->ssl3.hs.messages.space = 0;
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+ PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
+ sizeof(ss->ssl3.hs.newSessionTicket));
+
ss->ssl3.initialized = PR_TRUE;
return SECSuccess;
}
@@ -12745,6 +12836,8 @@ ssl3_DestroySSL3Info(sslSocket *ss)
/* free the SSL3Buffer (msg_body) */
PORT_Free(ss->ssl3.hs.msg_body.buf);
+ SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+
/* free up the CipherSpecs */
ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
diff --git a/chromium/net/third_party/nss/ssl/ssl3ecc.c b/chromium/net/third_party/nss/ssl/ssl3ecc.c
index 21a5e05cf2d..39f9af12c56 100644
--- a/chromium/net/third_party/nss/ssl/ssl3ecc.c
+++ b/chromium/net/third_party/nss/ssl/ssl3ecc.c
@@ -38,15 +38,6 @@
#ifdef NSS_ENABLE_ECC
-/*
- * In NSS 3.13.2 the definition of the EC_POINT_FORM_UNCOMPRESSED macro
- * was moved from the internal header ec.h to the public header blapit.h.
- * Define the macro here when compiling against older system NSS headers.
- */
-#ifndef EC_POINT_FORM_UNCOMPRESSED
-#define EC_POINT_FORM_UNCOMPRESSED 0x04
-#endif
-
#ifndef PK11_SETATTRS
#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
(x)->pValue=(v); (x)->ulValueLen = (l);
diff --git a/chromium/net/third_party/nss/ssl/ssl3ext.c b/chromium/net/third_party/nss/ssl/ssl3ext.c
index f2db28c924f..523e49a2753 100644
--- a/chromium/net/third_party/nss/ssl/ssl3ext.c
+++ b/chromium/net/third_party/nss/ssl/ssl3ext.c
@@ -57,7 +57,7 @@ static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss,
static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
PRUint16 ex_type, SECItem *data);
static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append,
- PRUint32 maxBytes);
+ PRUint32 maxBytes);
static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
PRUint32 maxBytes);
static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append,
@@ -283,21 +283,24 @@ static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
*/
static const
ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
- { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
- { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
+ { ssl_server_name_xtn, &ssl3_SendServerNameXtn },
+ { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
#ifdef NSS_ENABLE_ECC
- { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
- { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
+ { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
+ { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
#endif
- { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
- { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
- { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
- { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
- { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
- { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+ { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
+ { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
+ { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
+ { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
+ { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn },
+ { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
{ ssl_signed_certificate_timestamp_xtn,
- &ssl3_ClientSendSignedCertTimestampXtn }
+ &ssl3_ClientSendSignedCertTimestampXtn },
+ /* WebSphere Application Server 7.0 is intolerant to the last extension
+ * being zero-length. It is not intolerant of TLS 1.2, so move
+ * signature_algorithms to the end. */
+ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
/* any extra entries will appear as { 0, NULL } */
};
@@ -501,6 +504,7 @@ ssl3_SendSessionTicketXtn(
{
PRInt32 extension_length;
NewSessionTicket *session_ticket = NULL;
+ sslSessionID *sid = ss->sec.ci.sid;
/* Ignore the SessionTicket extension if processing is disabled. */
if (!ss->opt.enableSessionTickets)
@@ -516,8 +520,15 @@ ssl3_SendSessionTicketXtn(
* the extension always respond with an empty extension.
*/
if (!ss->sec.isServer) {
- sslSessionID *sid = ss->sec.ci.sid;
- session_ticket = &sid->u.ssl3.sessionTicket;
+ /* The caller must be holding sid->u.ssl3.lock for reading. We cannot
+ * just acquire and release the lock within this function because the
+ * caller will call this function twice, and we need the inputs to be
+ * consistent between the two calls. Note that currently the caller
+ * will only be holding the lock when we are the client and when we're
+ * attempting to resume an existing session.
+ */
+
+ session_ticket = &sid->u.ssl3.locked.sessionTicket;
if (session_ticket->ticket.data) {
if (ss->xtnData.ticketTimestampVerified) {
extension_length += session_ticket->ticket.len;
@@ -542,6 +553,7 @@ ssl3_SendSessionTicketXtn(
rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
session_ticket->ticket.len, 2);
ss->xtnData.ticketTimestampVerified = PR_FALSE;
+ ss->xtnData.sentSessionTicketInClientHello = PR_TRUE;
} else {
rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
}
@@ -622,6 +634,11 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
PORT_Assert(!ss->firstHsDone);
if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
+ /* If the server negotiated ALPN then it has already told us what protocol
+ * to use, so it doesn't make sense for us to try to negotiate a different
+ * one by sending the NPN handshake message. However, if we've negotiated
+ * NPN then we're required to send the NPN handshake message. Thus, these
+ * two extensions cannot both be negotiated on the same connection. */
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
@@ -682,8 +699,7 @@ ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
name_list_len = ((PRUint16) d[0]) << 8 |
((PRUint16) d[1]);
- if (name_list_len != data->len - 2 ||
- d[2] != data->len - 3) {
+ if (name_list_len != data->len - 2 || d[2] != data->len - 3) {
PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
return SECFailure;
}
@@ -704,7 +720,7 @@ ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append,
PRInt32 extension_length;
/* Renegotiations do not send this extension. */
- if (!ss->nextProtoCallback || ss->firstHsDone) {
+ if (!ss->opt.enableNPN || !ss->nextProtoCallback || ss->firstHsDone) {
return 0;
}
@@ -737,13 +753,13 @@ ssl3_ClientSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
unsigned char *alpn_protos = NULL;
/* Renegotiations do not send this extension. */
- if (!ss->opt.nextProtoNego.data || ss->firstHsDone) {
+ if (!ss->opt.enableALPN || !ss->opt.nextProtoNego.data || ss->firstHsDone) {
return 0;
}
extension_length = 2 /* extension type */ + 2 /* extension length */ +
- 2 /* protocol name list length */ +
- ss->opt.nextProtoNego.len;
+ 2 /* protocol name list length */ +
+ ss->opt.nextProtoNego.len;
if (append && maxBytes >= extension_length) {
/* NPN requires that the client's fallback protocol is first in the
@@ -770,16 +786,19 @@ ssl3_ClientSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
}
rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
- if (rv != SECSuccess)
+ if (rv != SECSuccess) {
goto loser;
+ }
rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
- if (rv != SECSuccess)
+ if (rv != SECSuccess) {
goto loser;
+ }
rv = ssl3_AppendHandshakeVariable(ss, alpn_protos, len, 2);
PORT_Free(alpn_protos);
alpn_protos = NULL;
- if (rv != SECSuccess)
+ if (rv != SECSuccess) {
goto loser;
+ }
ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
ssl_app_layer_protocol_xtn;
} else if (maxBytes < extension_length) {
@@ -789,8 +808,9 @@ ssl3_ClientSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
return extension_length;
loser:
- if (alpn_protos)
+ if (alpn_protos) {
PORT_Free(alpn_protos);
+ }
return -1;
}
@@ -1595,7 +1615,7 @@ ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
goto no_ticket;
/* Allow for the wrapped master secret to be longer. */
- if (buffer_len < sizeof(SSL3_MASTER_SECRET_LENGTH))
+ if (buffer_len < parsed_session_ticket->ms_length)
goto no_ticket;
PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
parsed_session_ticket->ms_length);
@@ -2329,13 +2349,15 @@ ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
return 0;
}
- extensionLength = 512 - recordLength;
- /* Extensions take at least four bytes to encode. */
- if (extensionLength < 4) {
- extensionLength = 4;
- }
+ extensionLength = 512 - recordLength;
+ /* Extensions take at least four bytes to encode. Always include at least
+ * one byte of data if including the extension. WebSphere Application Server
+ * 7.0 is intolerant to the last extension being zero-length. */
+ if (extensionLength < 4 + 1) {
+ extensionLength = 4 + 1;
+ }
- return extensionLength;
+ return extensionLength;
}
/* ssl3_AppendPaddingExtension possibly adds an extension which ensures that a
@@ -2346,7 +2368,7 @@ ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
PRUint32 maxBytes)
{
unsigned int paddingLen = extensionLen - 4;
- unsigned char padding[256];
+ static unsigned char padding[256];
if (extensionLen == 0) {
return 0;
@@ -2363,7 +2385,6 @@ ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
return -1;
if (SECSuccess != ssl3_AppendHandshakeNumber(ss, paddingLen, 2))
return -1;
- memset(padding, 0, paddingLen);
if (SECSuccess != ssl3_AppendHandshake(ss, padding, paddingLen))
return -1;
diff --git a/chromium/net/third_party/nss/ssl/ssl3gthr.c b/chromium/net/third_party/nss/ssl/ssl3gthr.c
index 39cefa7c310..cd487c6670a 100644
--- a/chromium/net/third_party/nss/ssl/ssl3gthr.c
+++ b/chromium/net/third_party/nss/ssl/ssl3gthr.c
@@ -325,6 +325,12 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
} else {
/* bring in the next sslv3 record. */
+ if (ss->recvdCloseNotify) {
+ /* RFC 5246 Section 7.2.1:
+ * Any data received after a closure alert is ignored.
+ */
+ return 0;
+ }
if (!IS_DTLS(ss)) {
rv = ssl3_GatherData(ss, &ss->gs, flags);
} else {
@@ -370,20 +376,19 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
cText.buf = &ss->gs.inbuf;
rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
-
- if (rv == (int) SECSuccess && ss->gs.buf.len > 0) {
- /* We have application data to return to the application. This
- * prioritizes returning application data to the application over
- * completing any renegotiation handshake we may be doing.
- */
- PORT_Assert(ss->firstHsDone);
- PORT_Assert(cText.type == content_application_data);
- break;
- }
}
if (rv < 0) {
return ss->recvdCloseNotify ? 0 : rv;
}
+ if (ss->gs.buf.len > 0) {
+ /* We have application data to return to the application. This
+ * prioritizes returning application data to the application over
+ * completing any renegotiation handshake we may be doing.
+ */
+ PORT_Assert(ss->firstHsDone);
+ PORT_Assert(cText.type == content_application_data);
+ break;
+ }
PORT_Assert(keepGoing);
ssl_GetSSL3HandshakeLock(ss);
diff --git a/chromium/net/third_party/nss/ssl/sslauth.c b/chromium/net/third_party/nss/ssl/sslauth.c
index 695cab854e4..c2d920120bb 100644
--- a/chromium/net/third_party/nss/ssl/sslauth.c
+++ b/chromium/net/third_party/nss/ssl/sslauth.c
@@ -299,9 +299,14 @@ SSL_AuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
certStatusArray = &ss->sec.ci.sid->peerCertStatus;
if (certStatusArray->len) {
- CERT_CacheOCSPResponseFromSideChannel(handle, ss->sec.peerCert,
- now, &certStatusArray->items[0],
- ss->pkcs11PinArg);
+ PORT_SetError(0);
+ if (CERT_CacheOCSPResponseFromSideChannel(handle, ss->sec.peerCert, now,
+ &certStatusArray->items[0],
+ ss->pkcs11PinArg)
+ != SECSuccess) {
+ PRErrorCode error = PR_GetError();
+ PORT_Assert(error != 0);
+ }
}
/* this may seem backwards, but isn't. */
diff --git a/chromium/net/third_party/nss/ssl/sslcon.c b/chromium/net/third_party/nss/ssl/sslcon.c
index 2fc6602a2b6..2763654e7f0 100644
--- a/chromium/net/third_party/nss/ssl/sslcon.c
+++ b/chromium/net/third_party/nss/ssl/sslcon.c
@@ -20,7 +20,6 @@
#include "prinit.h"
#include "prtime.h" /* for PR_Now() */
-#define XXX
static PRBool policyWasSet;
/* This ordered list is indexed by (SSL_CK_xx * 3) */
@@ -628,8 +627,9 @@ ssl2_SendServerFinishedMessage(sslSocket *ss)
(*ss->sec.uncache)(sid);
rv = (SECStatus)sent;
} else if (!ss->opt.noCache) {
- /* Put the sid in session-id cache, (may already be there) */
- (*ss->sec.cache)(sid);
+ if (sid->cached == never_cached) {
+ (*ss->sec.cache)(sid);
+ }
rv = SECSuccess;
}
ssl_FreeSID(sid);
@@ -2171,7 +2171,7 @@ ssl2_ClientRegSessionID(sslSocket *ss, PRUint8 *s)
sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
}
- if (!ss->opt.noCache)
+ if (!ss->opt.noCache && sid->cached == never_cached)
(*ss->sec.cache)(sid);
}
diff --git a/chromium/net/third_party/nss/ssl/sslenum.c b/chromium/net/third_party/nss/ssl/sslenum.c
index fc6b85423b1..d601207f789 100644
--- a/chromium/net/third_party/nss/ssl/sslenum.c
+++ b/chromium/net/third_party/nss/ssl/sslenum.c
@@ -10,16 +10,6 @@
#include "sslproto.h"
/*
- * The ciphers are listed in the following order:
- * - stronger ciphers before weaker ciphers
- * - national ciphers before international ciphers
- * - faster ciphers before slower ciphers
- *
- * National ciphers such as Camellia are listed before international ciphers
- * such as AES and RC4 to allow servers that prefer Camellia to negotiate
- * Camellia without having to disable AES and RC4, which are needed for
- * interoperability with clients that don't yet implement Camellia.
- *
* The ordering of cipher suites in this table must match the ordering in
* the cipherSuites table in ssl3con.c.
*
@@ -27,77 +17,96 @@
* in ssl3ecc.c.
*
* Finally, update the ssl_V3_SUITES_IMPLEMENTED macro in sslimpl.h.
+ *
+ * The ordering is as follows:
+ * * No-encryption cipher suites last
+ * * Export/weak/obsolete cipher suites before no-encryption cipher suites
+ * * Order by key exchange algorithm: ECDHE, then DHE, then ECDH, RSA.
+ * * Within key agreement sections, order by symmetric encryption algorithm:
+ * AES-128, then Camellia-128, then AES-256, then Camellia-256, then SEED,
+ * then FIPS-3DES, then 3DES, then RC4. AES is commonly accepted as a
+ * strong cipher internationally, and is often hardware-accelerated.
+ * Camellia also has wide international support across standards
+ * organizations. SEED is only recommended by the Korean government. 3DES
+ * only provides 112 bits of security. RC4 is now deprecated or forbidden
+ * by many standards organizations.
+ * * Within symmetric algorithm sections, order by message authentication
+ * algorithm: GCM, then HMAC-SHA1, then HMAC-SHA256, then HMAC-MD5.
+ * * Within message authentication algorithm sections, order by asymmetric
+ * signature algorithm: ECDSA, then RSA, then DSS.
+ *
+ * Exception: Because some servers ignore the high-order byte of the cipher
+ * suite ID, we must be careful about adding cipher suites with IDs larger
+ * than 0x00ff; see bug 946147. For these broken servers, the first six cipher
+ * suites, with the MSB zeroed, look like:
+ * TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA { 0x00,0x14 }
+ * TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA { 0x00,0x13 }
+ * TLS_KRB5_EXPORT_WITH_RC4_40_MD5 { 0x00,0x2B }
+ * TLS_RSA_WITH_AES_128_CBC_SHA { 0x00,0x2F }
+ * TLS_RSA_WITH_3DES_EDE_CBC_SHA { 0x00,0x0A }
+ * TLS_RSA_WITH_DES_CBC_SHA { 0x00,0x09 }
+ * The broken server only supports the fifth and sixth ones and will select
+ * the fifth one.
*/
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 */
- TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
- TLS_RSA_WITH_AES_128_GCM_SHA256,
-
- /* 256-bit */
-#ifdef NSS_ENABLE_ECC
+ /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA must appear before
+ * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA to work around bug 946147.
+ */
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-#endif /* NSS_ENABLE_ECC */
- TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
- TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
- TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
- TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
-#ifdef NSS_ENABLE_ECC
- TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
- TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
-#endif /* NSS_ENABLE_ECC */
- TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
- TLS_RSA_WITH_AES_256_CBC_SHA,
- TLS_RSA_WITH_AES_256_CBC_SHA256,
-
- /* 128-bit */
-#ifdef NSS_ENABLE_ECC
- TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
- TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
#endif /* NSS_ENABLE_ECC */
+
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
+ TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
+ SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
TLS_DHE_DSS_WITH_RC4_128_SHA,
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
- TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+
#ifdef NSS_ENABLE_ECC
- TLS_ECDH_RSA_WITH_RC4_128_SHA,
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
- TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDH_RSA_WITH_RC4_128_SHA,
#endif /* NSS_ENABLE_ECC */
- TLS_RSA_WITH_SEED_CBC_SHA,
- TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
- SSL_RSA_WITH_RC4_128_SHA,
- SSL_RSA_WITH_RC4_128_MD5,
+
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA256,
-
- /* 112-bit 3DES */
-#ifdef NSS_ENABLE_ECC
- TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
-#endif /* NSS_ENABLE_ECC */
- SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
-#ifdef NSS_ENABLE_ECC
- TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
- TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
-#endif /* NSS_ENABLE_ECC */
+ TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA256,
+ TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
+ TLS_RSA_WITH_SEED_CBC_SHA,
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,
SSL_RSA_WITH_3DES_EDE_CBC_SHA,
+ SSL_RSA_WITH_RC4_128_SHA,
+ SSL_RSA_WITH_RC4_128_MD5,
/* 56-bit DES "domestic" cipher suites */
SSL_DHE_RSA_WITH_DES_CBC_SHA,
diff --git a/chromium/net/third_party/nss/ssl/sslimpl.h b/chromium/net/third_party/nss/ssl/sslimpl.h
index 6d0bc154aaf..8754e16f7d3 100644
--- a/chromium/net/third_party/nss/ssl/sslimpl.h
+++ b/chromium/net/third_party/nss/ssl/sslimpl.h
@@ -312,31 +312,33 @@ typedef struct sslOptionsStr {
* list of supported protocols. */
SECItem nextProtoNego;
- unsigned int useSecurity : 1; /* 1 */
- unsigned int useSocks : 1; /* 2 */
- unsigned int requestCertificate : 1; /* 3 */
- unsigned int requireCertificate : 2; /* 4-5 */
- unsigned int handshakeAsClient : 1; /* 6 */
- unsigned int handshakeAsServer : 1; /* 7 */
- unsigned int enableSSL2 : 1; /* 8 */
- unsigned int unusedBit9 : 1; /* 9 */
- unsigned int unusedBit10 : 1; /* 10 */
- unsigned int noCache : 1; /* 11 */
- unsigned int fdx : 1; /* 12 */
- unsigned int v2CompatibleHello : 1; /* 13 */
- unsigned int detectRollBack : 1; /* 14 */
- unsigned int noStepDown : 1; /* 15 */
- unsigned int bypassPKCS11 : 1; /* 16 */
- unsigned int noLocks : 1; /* 17 */
- unsigned int enableSessionTickets : 1; /* 18 */
- unsigned int enableDeflate : 1; /* 19 */
- unsigned int enableRenegotiation : 2; /* 20-21 */
- unsigned int requireSafeNegotiation : 1; /* 22 */
- unsigned int enableFalseStart : 1; /* 23 */
- unsigned int cbcRandomIV : 1; /* 24 */
- unsigned int enableOCSPStapling : 1; /* 25 */
- unsigned int enableSignedCertTimestamps : 1; /* 26 */
- unsigned int enableFallbackSCSV : 1; /* 27 */
+ unsigned int useSecurity : 1; /* 1 */
+ unsigned int useSocks : 1; /* 2 */
+ unsigned int requestCertificate : 1; /* 3 */
+ unsigned int requireCertificate : 2; /* 4-5 */
+ unsigned int handshakeAsClient : 1; /* 6 */
+ unsigned int handshakeAsServer : 1; /* 7 */
+ unsigned int enableSSL2 : 1; /* 8 */
+ unsigned int unusedBit9 : 1; /* 9 */
+ unsigned int unusedBit10 : 1; /* 10 */
+ unsigned int noCache : 1; /* 11 */
+ unsigned int fdx : 1; /* 12 */
+ unsigned int v2CompatibleHello : 1; /* 13 */
+ unsigned int detectRollBack : 1; /* 14 */
+ unsigned int noStepDown : 1; /* 15 */
+ unsigned int bypassPKCS11 : 1; /* 16 */
+ unsigned int noLocks : 1; /* 17 */
+ unsigned int enableSessionTickets : 1; /* 18 */
+ unsigned int enableDeflate : 1; /* 19 */
+ unsigned int enableRenegotiation : 2; /* 20-21 */
+ unsigned int requireSafeNegotiation : 1; /* 22 */
+ unsigned int enableFalseStart : 1; /* 23 */
+ unsigned int cbcRandomIV : 1; /* 24 */
+ unsigned int enableOCSPStapling : 1; /* 25 */
+ unsigned int enableNPN : 1; /* 26 */
+ unsigned int enableALPN : 1; /* 27 */
+ unsigned int enableSignedCertTimestamps : 1; /* 28 */
+ unsigned int enableFallbackSCSV : 1; /* 29 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -618,7 +620,17 @@ typedef enum { never_cached,
#define MAX_PEER_CERT_CHAIN_SIZE 8
struct sslSessionIDStr {
+ /* The global cache lock must be held when accessing these members when the
+ * sid is in any cache.
+ */
sslSessionID * next; /* chain used for client sockets, only */
+ Cached cached;
+ int references;
+ PRUint32 lastAccessTime; /* seconds since Jan 1, 1970 */
+
+ /* The rest of the members, except for the members of u.ssl3.locked, may
+ * be modified only when the sid is not in any cache.
+ */
CERTCertificate * peerCert;
CERTCertificate * peerCertChain[MAX_PEER_CERT_CHAIN_SIZE];
@@ -633,10 +645,7 @@ struct sslSessionIDStr {
SSL3ProtocolVersion version;
PRUint32 creationTime; /* seconds since Jan 1, 1970 */
- PRUint32 lastAccessTime; /* seconds since Jan 1, 1970 */
PRUint32 expirationTime; /* seconds since Jan 1, 1970 */
- Cached cached;
- int references;
SSLSignType authAlgorithm;
PRUint32 authKeyBits;
@@ -702,11 +711,7 @@ struct sslSessionIDStr {
char masterValid;
char clAuthValid;
- /* Session ticket if we have one, is sent as an extension in the
- * ClientHello message. This field is used by clients.
- */
- NewSessionTicket sessionTicket;
- SECItem srvName;
+ SECItem srvName;
/* originalHandshakeHash contains the hash of the original, full
* handshake prior to the server's final flow. This is either a
@@ -720,11 +725,28 @@ struct sslSessionIDStr {
** (used only in client).
*/
SECItem signedCertTimestamps;
+
+ /* This lock is lazily initialized by CacheSID when a sid is first
+ * cached. Before then, there is no need to lock anything because
+ * the sid isn't being shared by anything.
+ */
+ NSSRWLock *lock;
+
+ /* The lock must be held while reading or writing these members
+ * because they change while the sid is cached.
+ */
+ struct {
+ /* The session ticket, if we have one, is sent as an extension
+ * in the ClientHello message. This field is used only by
+ * clients. It is protected by lock when lock is non-null
+ * (after the sid has been added to the client session cache).
+ */
+ NewSessionTicket sessionTicket;
+ } locked;
} ssl3;
} u;
};
-
typedef struct ssl3CipherSuiteDefStr {
ssl3CipherSuite cipher_suite;
SSL3BulkCipher bulk_cipher_alg;
@@ -804,6 +826,7 @@ struct TLSExtensionDataStr {
/* SessionTicket Extension related data. */
PRBool ticketTimestampVerified;
PRBool emptySessionTicket;
+ PRBool sentSessionTicketInClientHello;
/* SNI Extension related data
* Names data is not coppied from the input buffer. It can not be
@@ -877,6 +900,7 @@ typedef struct SSL3HandshakeStateStr {
/* NOTE: On the client side, TLS 1.2 and later use |md5| as a backup
* handshake hash for generating client auth signatures. Confusingly, the
* backup hash function is SHA-1. */
+#define backupHash md5
PK11Context * md5;
PK11Context * sha;
@@ -897,6 +921,14 @@ const ssl3CipherSuiteDef *suite_def;
PRBool sendingSCSV; /* instead of empty RI */
sslBuffer msgState; /* current state for handshake messages*/
/* protected by recvBufLock */
+
+ /* The session ticket received in a NewSessionTicket message is temporarily
+ * stored in newSessionTicket until the handshake is finished; then it is
+ * moved to the sid.
+ */
+ PRBool receivedNewSessionTicket;
+ NewSessionTicket newSessionTicket;
+
PRUint16 finishedBytes; /* size of single finished below */
union {
TLSFinished tFinished[2]; /* client, then server */
@@ -1836,8 +1868,8 @@ extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss,
/* Hello Extension related routines. */
extern PRBool ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type);
-extern SECStatus ssl3_SetSIDSessionTicket(sslSessionID *sid,
- NewSessionTicket *session_ticket);
+extern void ssl3_SetSIDSessionTicket(sslSessionID *sid,
+ /*in/out*/ NewSessionTicket *session_ticket);
extern SECStatus ssl3_SendNewSessionTicket(sslSocket *ss);
extern PRBool ssl_GetSessionTicketKeys(unsigned char *keyName,
unsigned char *encKey, unsigned char *macKey);
@@ -1862,10 +1894,11 @@ extern SECStatus ssl3_GetTLSUniqueChannelBinding(sslSocket *ss,
extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
extern void ssl_FreePRSocket(PRFileDesc *fd);
-/* Internal config function so SSL2 can initialize the present state of
+/* Internal config function so SSL3 can initialize the present state of
* various ciphers */
extern int ssl3_config_match_init(sslSocket *);
+
/* Create a new ref counted key pair object from two keys. */
extern ssl3KeyPair * ssl3_NewKeyPair( SECKEYPrivateKey * privKey,
SECKEYPublicKey * pubKey);
@@ -1962,6 +1995,10 @@ dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
/********************** misc calls *********************/
+#ifdef DEBUG
+extern void ssl3_CheckCipherSuiteOrderConsistency();
+#endif
+
extern int ssl_MapLowLevelError(int hiLevelError);
extern PRUint32 ssl_Time(void);
diff --git a/chromium/net/third_party/nss/ssl/sslinit.c b/chromium/net/third_party/nss/ssl/sslinit.c
index 047cc8d1d94..bb9df255f35 100644
--- a/chromium/net/third_party/nss/ssl/sslinit.c
+++ b/chromium/net/third_party/nss/ssl/sslinit.c
@@ -22,6 +22,11 @@ ssl_Init(void)
PORT_SetError(SEC_ERROR_NO_MEMORY);
return (SECFailure);
}
+
+#ifdef DEBUG
+ ssl3_CheckCipherSuiteOrderConsistency();
+#endif
+
ssl_inited = 1;
}
return SECSuccess;
diff --git a/chromium/net/third_party/nss/ssl/sslnonce.c b/chromium/net/third_party/nss/ssl/sslnonce.c
index a3e6e0a33f9..3e42bed34e5 100644
--- a/chromium/net/third_party/nss/ssl/sslnonce.c
+++ b/chromium/net/third_party/nss/ssl/sslnonce.c
@@ -21,8 +21,8 @@
PRUint32 ssl_sid_timeout = 100;
PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
-sslSessionID *cache = NULL;
-PZLock * cacheLock = NULL;
+static sslSessionID *cache = NULL;
+static PZLock * cacheLock = NULL;
/* sids can be in one of 4 states:
*
@@ -114,27 +114,30 @@ ssl_DestroySID(sslSessionID *sid)
{
int i;
SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
- PORT_Assert((sid->references == 0));
-
- if (sid->cached == in_client_cache)
- return; /* it will get taken care of next time cache is traversed. */
+ PORT_Assert(sid->references == 0);
+ PORT_Assert(sid->cached != in_client_cache);
if (sid->version < SSL_LIBRARY_VERSION_3_0) {
SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
} else {
- if (sid->u.ssl3.sessionTicket.ticket.data) {
- SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
- }
- if (sid->u.ssl3.srvName.data) {
- SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
- }
- if (sid->u.ssl3.signedCertTimestamps.data) {
- SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
- }
- if (sid->u.ssl3.originalHandshakeHash.data) {
- SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
- }
+ if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
+ PR_FALSE);
+ }
+ if (sid->u.ssl3.srvName.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+ }
+ if (sid->u.ssl3.originalHandshakeHash.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
+ }
+ if (sid->u.ssl3.signedCertTimestamps.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
+ }
+
+ if (sid->u.ssl3.lock) {
+ NSSRWLock_Destroy(sid->u.ssl3.lock);
+ }
}
if (sid->peerID != NULL)
@@ -156,7 +159,7 @@ ssl_DestroySID(sslSessionID *sid)
if ( sid->localCert ) {
CERT_DestroyCertificate(sid->localCert);
}
-
+
PORT_ZFree(sid, sizeof(sslSessionID));
}
@@ -217,9 +220,9 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
- if (sid->expirationTime < now || !sid->references) {
+ if (sid->expirationTime < now) {
/*
- ** This session-id timed out, or was orphaned.
+ ** This session-id timed out.
** Don't even care who it belongs to, blow it out of our cache.
*/
SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
@@ -227,11 +230,7 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
*sidp = sid->next; /* delink it from the list. */
sid->cached = invalid_cache; /* mark not on list. */
- if (!sid->references)
- ssl_DestroySID(sid);
- else
- ssl_FreeLockedSID(sid); /* drop ref count, free. */
-
+ ssl_FreeLockedSID(sid); /* drop ref count, free. */
} else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
(sid->port == port) && /* server port matches */
/* proxy (peerID) matches */
@@ -267,6 +266,9 @@ static void
CacheSID(sslSessionID *sid)
{
PRUint32 expirationPeriod;
+
+ PORT_Assert(sid->cached == never_cached);
+
SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
"time=%x cached=%d",
sid, sid->cached, sid->addr.pr_s6_addr32[0],
@@ -274,9 +276,6 @@ CacheSID(sslSessionID *sid)
sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
sid->cached));
- if (sid->cached == in_client_cache)
- return;
-
if (!sid->urlSvrName) {
/* don't cache this SID because it can never be matched */
return;
@@ -293,8 +292,9 @@ CacheSID(sslSessionID *sid)
sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
} else {
if (sid->u.ssl3.sessionIDLength == 0 &&
- sid->u.ssl3.sessionTicket.ticket.data == NULL)
+ sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
return;
+
/* Client generates the SessionID if this was a stateless resume. */
if (sid->u.ssl3.sessionIDLength == 0) {
SECStatus rv;
@@ -307,6 +307,11 @@ CacheSID(sslSessionID *sid)
expirationPeriod = ssl3_sid_timeout;
PRINT_BUF(8, (0, "sessionID:",
sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
+
+ sid->u.ssl3.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+ if (!sid->u.ssl3.lock) {
+ return;
+ }
}
PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
if (!sid->creationTime)
@@ -430,41 +435,38 @@ ssl_Time(void)
return myTime;
}
-SECStatus
-ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
+void
+ssl3_SetSIDSessionTicket(sslSessionID *sid,
+ /*in/out*/ NewSessionTicket *newSessionTicket)
{
- SECStatus rv;
-
- /* We need to lock the cache, as this sid might already be in the cache. */
- LOCK_CACHE;
-
- /* Don't modify sid if it has ever been cached. */
- if (sid->cached != never_cached) {
- UNLOCK_CACHE;
- return SECSuccess;
- }
+ PORT_Assert(sid);
+ PORT_Assert(newSessionTicket);
- /* A server might have sent us an empty ticket, which has the
- * effect of clearing the previously known ticket.
+ /* if sid->u.ssl3.lock, we are updating an existing entry that is already
+ * cached or was once cached, so we need to acquire and release the write
+ * lock. Otherwise, this is a new session that isn't shared with anything
+ * yet, so no locking is needed.
*/
- if (sid->u.ssl3.sessionTicket.ticket.data)
- SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
- if (session_ticket->ticket.len > 0) {
- rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
- &session_ticket->ticket);
- if (rv != SECSuccess) {
- UNLOCK_CACHE;
- return rv;
+ if (sid->u.ssl3.lock) {
+ NSSRWLock_LockWrite(sid->u.ssl3.lock);
+
+ /* A server might have sent us an empty ticket, which has the
+ * effect of clearing the previously known ticket.
+ */
+ if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
+ PR_FALSE);
}
- } else {
- sid->u.ssl3.sessionTicket.ticket.data = NULL;
- sid->u.ssl3.sessionTicket.ticket.len = 0;
}
- sid->u.ssl3.sessionTicket.received_timestamp =
- session_ticket->received_timestamp;
- sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
- session_ticket->ticket_lifetime_hint;
- UNLOCK_CACHE;
- return SECSuccess;
+ PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
+
+ /* Do a shallow copy, moving the ticket data. */
+ sid->u.ssl3.locked.sessionTicket = *newSessionTicket;
+ newSessionTicket->ticket.data = NULL;
+ newSessionTicket->ticket.len = 0;
+
+ if (sid->u.ssl3.lock) {
+ NSSRWLock_UnlockWrite(sid->u.ssl3.lock);
+ }
}
diff --git a/chromium/net/third_party/nss/ssl/sslplatf.c b/chromium/net/third_party/nss/ssl/sslplatf.c
index fee702191a7..6f3cca64da8 100644
--- a/chromium/net/third_party/nss/ssl/sslplatf.c
+++ b/chromium/net/third_party/nss/ssl/sslplatf.c
@@ -550,12 +550,37 @@ ssl3_GetDigestInfoPrefix(SECOidTag hashAlg,
return SECSuccess;
}
+/* Given the length of a raw DSA signature (consisting of two integers
+ * r and s), returns the maximum length of the DER encoding of the
+ * following structure:
+ *
+ * Dss-Sig-Value ::= SEQUENCE {
+ * r INTEGER,
+ * s INTEGER
+ * }
+ */
+static unsigned int
+ssl3_DSAMaxDerEncodedLength(unsigned int rawDsaLen)
+{
+ /* The length of one INTEGER. */
+ unsigned int integerDerLen = rawDsaLen/2 + /* the integer itself */
+ 1 + /* additional zero byte if high bit is 1 */
+ SEC_ASN1LengthLength(rawDsaLen/2 + 1) + /* length */
+ 1; /* INTEGER tag */
+
+ /* The length of two INTEGERs in a SEQUENCE */
+ return 2 * integerDerLen + /* two INTEGERs */
+ SEC_ASN1LengthLength(2 * integerDerLen) + /* length */
+ 1; /* SEQUENCE tag */
+}
+
SECStatus
ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
SECStatus rv = SECFailure;
- PRBool doDerEncode = PR_FALSE;
+ PRBool doDerDecode = PR_FALSE;
+ unsigned int rawDsaLen;
unsigned int signatureLen;
OSStatus status = noErr;
CSSM_CSP_HANDLE cspHandle = 0;
@@ -585,26 +610,13 @@ ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
goto done;
}
- /* SecKeyGetBlockSize wasn't addeded until OS X 10.6 - but the
- * needed information is readily available on the key itself.
- */
- signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8;
-
- if (signatureLen == 0) {
- PORT_SetError(SEC_ERROR_INVALID_KEY);
- goto done;
- }
-
- buf->data = (unsigned char *)PORT_Alloc(signatureLen);
- if (!buf->data)
- goto done; /* error code was set. */
-
sigAlg = cssmKey->KeyHeader.AlgorithmId;
digestAlg = CSSM_ALGID_NONE;
switch (keyType) {
case rsaKey:
PORT_Assert(sigAlg == CSSM_ALGID_RSA);
+ signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8;
if (ssl3_GetDigestInfoPrefix(hash->hashAlg, &prefix, &prefixLen) !=
SECSuccess) {
goto done;
@@ -620,13 +632,29 @@ ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
break;
case dsaKey:
case ecKey:
+ /* SSL3 DSA signatures are raw, not DER-encoded. CSSM gives back
+ * DER-encoded signatures, so they must be decoded. */
+ doDerDecode = (keyType == dsaKey) && !isTLS;
+
+ /* Compute the maximum size of a DER-encoded signature: */
if (keyType == ecKey) {
PORT_Assert(sigAlg == CSSM_ALGID_ECDSA);
- doDerEncode = PR_TRUE;
+ /* LogicalKeySizeInBits is the size of an EC public key. But an
+ * ECDSA signature length depends on the size of the base
+ * point's order. For P-256, P-384, and P-521, these two sizes
+ * are the same. */
+ rawDsaLen =
+ (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8 * 2;
} else {
+ /* TODO(davidben): Get the size of the subprime out of CSSM. For
+ * now, assume 160; Apple's implementation hardcodes it. */
PORT_Assert(sigAlg == CSSM_ALGID_DSA);
- doDerEncode = isTLS;
+ rawDsaLen = 2 * (160 / 8);
}
+ signatureLen = ssl3_DSAMaxDerEncodedLength(rawDsaLen);
+
+ /* SEC_OID_UNKNOWN is used to specify the MD5/SHA1 concatenated
+ * hash. In that case, we use just the SHA1 part. */
if (hash->hashAlg == SEC_OID_UNKNOWN) {
hashData.Data = hash->u.s.sha;
hashData.Length = sizeof(hash->u.s.sha);
@@ -641,6 +669,15 @@ ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
}
PRINT_BUF(60, (NULL, "hash(es) to be signed", hashData.Data, hashData.Length));
+ if (signatureLen == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ goto done;
+ }
+
+ buf->data = (unsigned char *)PORT_Alloc(signatureLen);
+ if (!buf->data)
+ goto done; /* error code was set. */
+
/* TODO(rsleevi): Should it be kSecCredentialTypeNoUI? In Win32, at least,
* you can prevent the UI by setting the provider handle on the
* certificate to be opened with CRYPT_SILENT, but is there an equivalent?
@@ -684,16 +721,13 @@ ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
}
buf->len = signatureData.Length;
- if (doDerEncode) {
- SECItem derSig = {siBuffer, NULL, 0};
-
- /* This also works for an ECDSA signature */
- rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
- if (rv == SECSuccess) {
- PORT_Free(buf->data); /* discard unencoded signature. */
- *buf = derSig; /* give caller encoded signature. */
- } else if (derSig.data) {
- PORT_Free(derSig.data);
+ if (doDerDecode) {
+ SECItem* rawSig = DSAU_DecodeDerSigToLen(buf, rawDsaLen);
+ if (rawSig != NULL) {
+ PORT_Free(buf->data); /* discard encoded signature. */
+ *buf = *rawSig; /* give caller unencoded signature. */
+ PORT_Free(rawSig);
+ rv = SECSuccess;
}
} else {
rv = SECSuccess;
diff --git a/chromium/net/third_party/nss/ssl/sslsecur.c b/chromium/net/third_party/nss/ssl/sslsecur.c
index 99538e532a6..e03d7f93abe 100644
--- a/chromium/net/third_party/nss/ssl/sslsecur.c
+++ b/chromium/net/third_party/nss/ssl/sslsecur.c
@@ -277,7 +277,7 @@ SSL_ReHandshake(PRFileDesc *fd, PRBool flushCache)
/* SSL v2 protocol does not support subsequent handshakes. */
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
rv = SECFailure;
} else {
ssl_GetSSL3HandshakeLock(ss);
@@ -1237,7 +1237,6 @@ int
ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
{
int rv = 0;
- PRBool falseStart = PR_FALSE;
SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes",
SSL_GETPID(), ss->fd, len));
@@ -1272,6 +1271,7 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
ss->writerThread = PR_GetCurrentThread();
/* If any of these is non-zero, the initial handshake is not done. */
if (!ss->firstHsDone) {
+ PRBool falseStart = PR_FALSE;
ssl_Get1stHandshakeLock(ss);
if (ss->opt.enableFalseStart &&
ss->version >= SSL_LIBRARY_VERSION_3_0) {
@@ -1398,10 +1398,6 @@ SSL_SetURL(PRFileDesc *fd, const char *url)
SECStatus
SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
{
- PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
- PR_NOT_REACHED("not implemented");
- return SECFailure;
-#if 0
sslSocket * ss = ssl_FindSocket(fd);
CERTDistNames *names = NULL;
@@ -1429,7 +1425,6 @@ SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
ssl_Release1stHandshakeLock(ss);
return SECSuccess;
-#endif
}
/*
diff --git a/chromium/net/third_party/nss/ssl/sslsock.c b/chromium/net/third_party/nss/ssl/sslsock.c
index 998c1371999..028cd98f5ae 100644
--- a/chromium/net/third_party/nss/ssl/sslsock.c
+++ b/chromium/net/third_party/nss/ssl/sslsock.c
@@ -28,94 +28,6 @@
#define SET_ERROR_CODE /* reminder */
-struct cipherPolicyStr {
- int cipher;
- unsigned char export; /* policy value for export policy */
- unsigned char france; /* policy value for france policy */
-};
-
-typedef struct cipherPolicyStr cipherPolicy;
-
-/* This table contains two preconfigured policies: Export and France.
-** It is used only by the functions NSS_SetDomesticPolicy,
-** NSS_SetExportPolicy, and NSS_SetFrancePolicy.
-** Order of entries is not important.
-*/
-static cipherPolicy ssl_ciphers[] = { /* Export France */
- { SSL_EN_RC4_128_WITH_MD5, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL_ALLOWED, SSL_ALLOWED },
- { SSL_EN_RC2_128_CBC_WITH_MD5, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_ALLOWED, SSL_ALLOWED },
- { SSL_EN_DES_64_CBC_WITH_MD5, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_RSA_WITH_RC4_128_MD5, SSL_RESTRICTED, SSL_NOT_ALLOWED },
- { SSL_RSA_WITH_RC4_128_SHA, SSL_RESTRICTED, SSL_NOT_ALLOWED },
- { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RESTRICTED, SSL_NOT_ALLOWED },
- { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED, SSL_ALLOWED },
- { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_ALLOWED, SSL_ALLOWED },
- { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_DSS_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { SSL_RSA_WITH_NULL_MD5, SSL_ALLOWED, SSL_ALLOWED },
- { SSL_RSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_RSA_WITH_NULL_SHA256, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_WITH_SEED_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_ALLOWED, SSL_NOT_ALLOWED },
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { 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 },
- { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_RSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
- { TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
- { 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 }
-};
-
static const sslSocketOps ssl_default_ops = { /* No SSL. */
ssl_DefConnect,
NULL,
@@ -174,6 +86,8 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableFalseStart */
PR_TRUE, /* cbcRandomIV */
PR_FALSE, /* enableOCSPStapling */
+ PR_TRUE, /* enableNPN */
+ PR_FALSE, /* enableALPN */
PR_FALSE, /* enableSignedCertTimestamps */
PR_FALSE /* enableFallbackSCSV */
};
@@ -867,9 +781,17 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
ss->opt.enableOCSPStapling = on;
break;
+ case SSL_ENABLE_NPN:
+ ss->opt.enableNPN = on;
+ break;
+
+ case SSL_ENABLE_ALPN:
+ ss->opt.enableALPN = on;
+ break;
+
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- ss->opt.enableSignedCertTimestamps = on;
- break;
+ ss->opt.enableSignedCertTimestamps = on;
+ break;
case SSL_ENABLE_FALLBACK_SCSV:
ss->opt.enableFallbackSCSV = on;
@@ -945,9 +867,11 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
case SSL_CBC_RANDOM_IV: on = ss->opt.cbcRandomIV; break;
case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
+ case SSL_ENABLE_NPN: on = ss->opt.enableNPN; break;
+ case SSL_ENABLE_ALPN: on = ss->opt.enableALPN; break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ss->opt.enableSignedCertTimestamps;
- break;
+ on = ss->opt.enableSignedCertTimestamps;
+ break;
case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
default:
@@ -1010,12 +934,14 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
case SSL_ENABLE_OCSP_STAPLING:
on = ssl_defaults.enableOCSPStapling;
break;
+ case SSL_ENABLE_NPN: on = ssl_defaults.enableNPN; break;
+ case SSL_ENABLE_ALPN: on = ssl_defaults.enableALPN; break;
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- on = ssl_defaults.enableSignedCertTimestamps;
- break;
+ on = ssl_defaults.enableSignedCertTimestamps;
+ break;
case SSL_ENABLE_FALLBACK_SCSV:
- on = ssl_defaults.enableFallbackSCSV;
- break;
+ on = ssl_defaults.enableFallbackSCSV;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -1183,9 +1109,17 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
ssl_defaults.enableOCSPStapling = on;
break;
+ case SSL_ENABLE_NPN:
+ ssl_defaults.enableNPN = on;
+ break;
+
+ case SSL_ENABLE_ALPN:
+ ssl_defaults.enableALPN = on;
+ break;
+
case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS:
- ssl_defaults.enableSignedCertTimestamps = on;
- break;
+ ssl_defaults.enableSignedCertTimestamps = on;
+ break;
case SSL_ENABLE_FALLBACK_SCSV:
ssl_defaults.enableFallbackSCSV = on;
@@ -1400,10 +1334,10 @@ SECStatus
NSS_SetDomesticPolicy(void)
{
SECStatus status = SECSuccess;
- cipherPolicy * policy;
+ const PRUint16 *cipher;
- for (policy = ssl_ciphers; policy->cipher != 0; ++policy) {
- status = SSL_SetPolicy(policy->cipher, SSL_ALLOWED);
+ for (cipher = SSL_ImplementedCiphers; *cipher != 0; ++cipher) {
+ status = SSL_SetPolicy(*cipher, SSL_ALLOWED);
if (status != SECSuccess)
break;
}
@@ -1708,11 +1642,6 @@ SSL_GetSRTPCipher(PRFileDesc *fd, PRUint16 *cipher)
PRFileDesc *
SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
{
- PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
- PR_NOT_REACHED("not implemented");
- return NULL;
-
-#if 0
sslSocket * sm = NULL, *ss = NULL;
int i;
sslServerCerts * mc = NULL;
@@ -1840,7 +1769,6 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
return fd;
loser:
return NULL;
-#endif
}
PRBool
@@ -3197,3 +3125,4 @@ loser:
}
return ss;
}
+
diff --git a/chromium/net/third_party/nss/ssl/sslt.h b/chromium/net/third_party/nss/ssl/sslt.h
index 1f5e2c659ff..566074d7c8c 100644
--- a/chromium/net/third_party/nss/ssl/sslt.h
+++ b/chromium/net/third_party/nss/ssl/sslt.h
@@ -203,10 +203,10 @@ typedef enum {
ssl_use_srtp_xtn = 14,
ssl_app_layer_protocol_xtn = 16,
ssl_signed_certificate_timestamp_xtn = 18, /* RFC 6962 */
+ ssl_padding_xtn = 21,
ssl_session_ticket_xtn = 35,
ssl_next_proto_nego_xtn = 13172,
ssl_channel_id_xtn = 30032,
- ssl_padding_xtn = 35655,
ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
} SSLExtensionType;
diff --git a/chromium/net/tools/DEPS b/chromium/net/tools/DEPS
index 1ea12e4c3b9..4648a84aacb 100644
--- a/chromium/net/tools/DEPS
+++ b/chromium/net/tools/DEPS
@@ -1,3 +1,7 @@
+include_rules = [
+ "+base/i18n",
+]
+
skip_child_includes = [
"balsa",
"flip_server",
diff --git a/chromium/net/tools/balsa/balsa_enums.h b/chromium/net/tools/balsa/balsa_enums.h
index a59dcda687c..2a49abada92 100644
--- a/chromium/net/tools/balsa/balsa_enums.h
+++ b/chromium/net/tools/balsa/balsa_enums.h
@@ -24,6 +24,11 @@ struct BalsaFrameEnums {
};
enum ErrorCode {
+#if defined(_WIN32)
+ // On Windows, <WinError.h> defines the NO_ERROR macro as 0L, which
+ // breaks the compilation of the "NO_ERROR = 0" line.
+#undef NO_ERROR
+#endif
NO_ERROR = 0, // A sentinel value for convenience, none of the callbacks
// should ever see this error code.
// Header parsing errors
diff --git a/chromium/net/tools/balsa/balsa_frame.cc b/chromium/net/tools/balsa/balsa_frame.cc
index 96e91935597..463a678dead 100644
--- a/chromium/net/tools/balsa/balsa_frame.cc
+++ b/chromium/net/tools/balsa/balsa_frame.cc
@@ -8,7 +8,6 @@
#if __SSE2__
#include <emmintrin.h>
#endif // __SSE2__
-#include <strings.h>
#include <limits>
#include <string>
@@ -26,6 +25,13 @@
#include "net/tools/balsa/split.h"
#include "net/tools/balsa/string_piece_utils.h"
+#if defined(COMPILER_MSVC)
+#include <string.h>
+#define strncasecmp _strnicmp
+#else
+#include <strings.h>
+#endif
+
namespace net {
// Constants holding some header names for headers which can affect the way the
@@ -426,7 +432,7 @@ void BalsaFrame::ProcessFirstLine(const char* begin, const char* end) {
}
if (is_request_) {
- int version_length =
+ size_t version_length =
headers_->whitespace_4_idx_ - headers_->non_whitespace_3_idx_;
visitor_->ProcessRequestFirstLine(
begin + headers_->non_whitespace_1_idx_,
@@ -1556,9 +1562,4 @@ size_t BalsaFrame::ProcessInput(const char* input, size_t size) {
return current - input;
}
-const uint32 BalsaFrame::kValidTerm1;
-const uint32 BalsaFrame::kValidTerm1Mask;
-const uint32 BalsaFrame::kValidTerm2;
-const uint32 BalsaFrame::kValidTerm2Mask;
-
} // namespace net
diff --git a/chromium/net/tools/balsa/balsa_frame.h b/chromium/net/tools/balsa/balsa_frame.h
index 8e1240c86e9..6178d795d90 100644
--- a/chromium/net/tools/balsa/balsa_frame.h
+++ b/chromium/net/tools/balsa/balsa_frame.h
@@ -5,8 +5,6 @@
#ifndef NET_TOOLS_BALSA_BALSA_FRAME_H_
#define NET_TOOLS_BALSA_BALSA_FRAME_H_
-#include <strings.h>
-
#include <utility>
#include <vector>
diff --git a/chromium/net/tools/balsa/balsa_headers.cc b/chromium/net/tools/balsa/balsa_headers.cc
index 27bfd24a514..a77b75b89e5 100644
--- a/chromium/net/tools/balsa/balsa_headers.cc
+++ b/chromium/net/tools/balsa/balsa_headers.cc
@@ -6,11 +6,11 @@
#include <stdio.h>
#include <algorithm>
-#include <ext/hash_set>
#include <string>
#include <utility>
#include <vector>
+#include "base/containers/hash_tables.h"
#include "base/logging.h"
#include "base/port.h"
#include "base/strings/string_piece.h"
@@ -20,15 +20,28 @@
#include "net/tools/balsa/simple_buffer.h"
#include "third_party/tcmalloc/chromium/src/base/googleinit.h"
+#if defined(COMPILER_MSVC)
+#include <string.h>
+#define snprintf _snprintf
+#define strncasecmp _strnicmp
+#else
+#include <strings.h>
+#endif
+
namespace {
const char kContentLength[] = "Content-Length";
const char kTransferEncoding[] = "Transfer-Encoding";
const char kSpaceChar = ' ';
-__gnu_cxx::hash_set<base::StringPiece,
- net::StringPieceCaseHash,
- net::StringPieceCaseEqual> g_multivalued_headers;
+#if defined(COMPILER_MSVC)
+base::hash_set<base::StringPiece,
+ net::StringPieceCaseCompare> g_multivalued_headers;
+#else
+base::hash_set<base::StringPiece,
+ net::StringPieceCaseHash,
+ net::StringPieceCaseEqual> g_multivalued_headers;
+#endif
void InitMultivaluedHeaders() {
g_multivalued_headers.insert("accept");
@@ -66,8 +79,6 @@ const int kFastToBufferSize = 32; // I think 22 is adequate, but anyway..
namespace net {
-const size_t BalsaBuffer::kDefaultBlocksize;
-
BalsaHeaders::iterator_base::iterator_base() : headers_(NULL), idx_(0) { }
BalsaHeaders::iterator_base::iterator_base(const iterator_base& it)
@@ -542,7 +553,7 @@ const base::StringPiece BalsaHeaders::GetHeader(
const HeaderLines::const_iterator begin = header_lines_.begin();
HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin);
if (i == end) {
- return base::StringPiece(NULL, 0);
+ return base::StringPiece();
}
return GetValueFromHeaderLineDescription(*i);
}
diff --git a/chromium/net/tools/balsa/balsa_headers.h b/chromium/net/tools/balsa/balsa_headers.h
index 47cf388b8ab..840aae5e72e 100644
--- a/chromium/net/tools/balsa/balsa_headers.h
+++ b/chromium/net/tools/balsa/balsa_headers.h
@@ -938,7 +938,7 @@ class BalsaHeaders {
return transfer_encoding_is_chunked_;
}
- static bool ResponseCodeImpliesNoBody(int code) {
+ static bool ResponseCodeImpliesNoBody(size_t code) {
// From HTTP spec section 6.1.1 all 1xx responses must not have a body,
// as well as 204 No Content and 304 Not Modified.
return ((code >= 100) && (code <= 199)) || (code == 204) || (code == 304);
diff --git a/chromium/net/tools/balsa/string_piece_utils.h b/chromium/net/tools/balsa/string_piece_utils.h
index eb0eaf365c8..12fc69be85f 100644
--- a/chromium/net/tools/balsa/string_piece_utils.h
+++ b/chromium/net/tools/balsa/string_piece_utils.h
@@ -12,6 +12,34 @@
namespace net {
+#if defined(COMPILER_MSVC)
+struct StringPieceCaseCompare {
+ static const size_t bucket_size = 4;
+
+ size_t operator()(const base::StringPiece& sp) const {
+ // based on __stl_string_hash in http://www.sgi.com/tech/stl/string
+ size_t hash_val = 0;
+ for (base::StringPiece::const_iterator it = sp.begin();
+ it != sp.end(); ++it) {
+ hash_val = 5 * hash_val + tolower(*it);
+ }
+ return hash_val;
+ }
+
+ bool operator()(const base::StringPiece& sp1,
+ const base::StringPiece& sp2) const {
+ size_t len1 = sp1.length();
+ size_t len2 = sp2.length();
+ bool sp1_shorter = len1 < len2;
+ size_t len = sp1_shorter ? len1 : len2;
+ int rv = _memicmp(sp1.data(), sp2.data(), len);
+ if (rv == 0) {
+ return sp1_shorter;
+ }
+ return rv < 0;
+ }
+};
+#else // COMPILER_MSVC
struct StringPieceCaseHash {
size_t operator()(const base::StringPiece& sp) const {
// based on __stl_string_hash in http://www.sgi.com/tech/stl/string
@@ -23,6 +51,7 @@ struct StringPieceCaseHash {
return hash_val;
}
};
+#endif // COMPILER_MSVC
struct StringPieceUtils {
static bool EqualIgnoreCase(const base::StringPiece& piece1,
diff --git a/chromium/net/tools/crash_cache/crash_cache.cc b/chromium/net/tools/crash_cache/crash_cache.cc
index 32934a6a870..aeeafbe56bf 100644
--- a/chromium/net/tools/crash_cache/crash_cache.cc
+++ b/chromium/net/tools/crash_cache/crash_cache.cc
@@ -25,10 +25,10 @@
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/rankings.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/disk_cache_test_util.h"
-#include "net/disk_cache/rankings.h"
using base::Time;
@@ -47,7 +47,7 @@ int RunSlave(RankCrashes action) {
base::FilePath exe;
PathService::Get(base::FILE_EXE, &exe);
- CommandLine cmdline(exe);
+ base::CommandLine cmdline(exe);
cmdline.AppendArg(base::IntToString(action));
base::ProcessHandle handle;
diff --git a/chromium/net/tools/crl_set_dump/crl_set_dump.cc b/chromium/net/tools/crl_set_dump/crl_set_dump.cc
index 6c3bb54bab5..ce6f5e5570d 100644
--- a/chromium/net/tools/crl_set_dump/crl_set_dump.cc
+++ b/chromium/net/tools/crl_set_dump/crl_set_dump.cc
@@ -62,8 +62,7 @@ int main(int argc, char** argv) {
if (!output_filename.empty()) {
const std::string out = final_crl_set->Serialize();
- if (file_util::WriteFile(output_filename, out.data(),
- out.size()) == -1) {
+ if (base::WriteFile(output_filename, out.data(), out.size()) == -1) {
fprintf(stderr, "Failed to write resulting CRL set\n");
return 1;
}
diff --git a/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc b/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc
index aafd2caf216..f129ef22984 100644
--- a/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc
+++ b/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc
@@ -267,8 +267,9 @@ bool ParseAndStoreSpec(const std::string& spec_str,
bool Main(int argc, char** argv) {
base::AtExitManager at_exit_manager;
base::MessageLoopForIO message_loop;
- CommandLine::Init(argc, argv);
- const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch("help")) {
PrintUsage(&std::cout);
return true;
diff --git a/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc b/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc
index f9caa79df62..d77abeee881 100644
--- a/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc
+++ b/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc
@@ -58,7 +58,7 @@ bool ReadTestCase(const char* filename,
return false;
}
- scoped_ptr<Value> value(base::JSONReader::Read(json));
+ scoped_ptr<base::Value> value(base::JSONReader::Read(json));
if (!value.get()) {
LOG(ERROR) << filename << ": couldn't parse JSON.";
return false;
diff --git a/chromium/net/tools/dump_cache/cache_dumper.cc b/chromium/net/tools/dump_cache/cache_dumper.cc
index 7d29a5c1785..28981644282 100644
--- a/chromium/net/tools/dump_cache/cache_dumper.cc
+++ b/chromium/net/tools/dump_cache/cache_dumper.cc
@@ -8,7 +8,7 @@
#include "base/strings/utf_string_conversions.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
-#include "net/disk_cache/entry_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
diff --git a/chromium/net/tools/dump_cache/cache_dumper.h b/chromium/net/tools/dump_cache/cache_dumper.h
index 4bdf4a1bd2f..9c5837ce566 100644
--- a/chromium/net/tools/dump_cache/cache_dumper.h
+++ b/chromium/net/tools/dump_cache/cache_dumper.h
@@ -8,7 +8,7 @@
#include <string>
#include "base/files/file_path.h"
-#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
#ifdef WIN32
// Dumping the cache often creates very large filenames, which are tricky
diff --git a/chromium/net/tools/dump_cache/dump_cache.cc b/chromium/net/tools/dump_cache/dump_cache.cc
index d4ccc294448..b3970e6053f 100644
--- a/chromium/net/tools/dump_cache/dump_cache.cc
+++ b/chromium/net/tools/dump_cache/dump_cache.cc
@@ -13,7 +13,7 @@
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "net/disk_cache/disk_format.h"
+#include "net/disk_cache/blockfile/disk_format.h"
#include "net/tools/dump_cache/dump_files.h"
#include "net/tools/dump_cache/simple_cache_dumper.h"
@@ -96,7 +96,7 @@ int LaunchSlave(CommandLine command_line,
if (!base::LaunchProcess(command_line, base::LaunchOptions(), NULL)) {
printf("Unable to launch the needed version of this tool: %ls\n",
command_line.GetProgram().value().c_str());
- printf(kUpgradeHelp);
+ printf("%s", kUpgradeHelp);
return TOOL_NOT_FOUND;
}
return ALL_GOOD;
@@ -110,9 +110,10 @@ int main(int argc, const char* argv[]) {
// Setup an AtExitManager so Singleton objects will be destroyed.
base::AtExitManager at_exit_manager;
- CommandLine::Init(argc, argv);
+ base::CommandLine::Init(argc, argv);
- const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
base::FilePath input_path = command_line.GetSwitchValuePath(kInputPath);
if (input_path.empty())
return Help();
diff --git a/chromium/net/tools/dump_cache/dump_files.cc b/chromium/net/tools/dump_cache/dump_files.cc
index 7f4fb58343d..796f205e818 100644
--- a/chromium/net/tools/dump_cache/dump_files.cc
+++ b/chromium/net/tools/dump_cache/dump_files.cc
@@ -14,16 +14,16 @@
#include <string>
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/format_macros.h"
#include "base/message_loop/message_loop.h"
-#include "net/base/file_stream.h"
-#include "net/disk_cache/block_files.h"
-#include "net/disk_cache/disk_format.h"
-#include "net/disk_cache/mapped_file.h"
-#include "net/disk_cache/stats.h"
-#include "net/disk_cache/storage_block-inl.h"
-#include "net/disk_cache/storage_block.h"
+#include "net/disk_cache/blockfile/block_files.h"
+#include "net/disk_cache/blockfile/disk_format.h"
+#include "net/disk_cache/blockfile/mapped_file.h"
+#include "net/disk_cache/blockfile/stats.h"
+#include "net/disk_cache/blockfile/storage_block-inl.h"
+#include "net/disk_cache/blockfile/storage_block.h"
namespace {
@@ -31,14 +31,13 @@ const base::FilePath::CharType kIndexName[] = FILE_PATH_LITERAL("index");
// Reads the |header_size| bytes from the beginning of file |name|.
bool ReadHeader(const base::FilePath& name, char* header, int header_size) {
- net::FileStream file(NULL);
- file.OpenSync(name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
- if (!file.IsOpen()) {
+ base::File file(name, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid()) {
printf("Unable to open file %s\n", name.MaybeAsASCII().c_str());
return false;
}
- int read = file.ReadSync(header, header_size);
+ int read = file.Read(0, header, header_size);
if (read != header_size) {
printf("Unable to read file %s\n", name.MaybeAsASCII().c_str());
return false;
@@ -57,7 +56,7 @@ int GetMajorVersionFromFile(const base::FilePath& name) {
// Dumps the contents of the Stats record.
void DumpStats(const base::FilePath& path, disk_cache::CacheAddr addr) {
// We need a message loop, although we really don't run any task.
- base::MessageLoop loop(base::MessageLoop::TYPE_IO);
+ base::MessageLoopForIO loop;
disk_cache::BlockFiles block_files(path);
if (!block_files.Init(false)) {
@@ -355,7 +354,7 @@ int DumpContents(const base::FilePath& input_path) {
DumpHeaders(input_path);
// We need a message loop, although we really don't run any task.
- base::MessageLoop loop(base::MessageLoop::TYPE_IO);
+ base::MessageLoopForIO loop;
CacheDumper dumper(input_path);
if (!dumper.Init())
return -1;
diff --git a/chromium/net/tools/dump_cache/upgrade_win.cc b/chromium/net/tools/dump_cache/upgrade_win.cc
index 5135592429b..dfb9e5c32fc 100644
--- a/chromium/net/tools/dump_cache/upgrade_win.cc
+++ b/chromium/net/tools/dump_cache/upgrade_win.cc
@@ -17,8 +17,8 @@
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/backend_impl.h"
-#include "net/disk_cache/entry_impl.h"
+#include "net/disk_cache/blockfile/backend_impl.h"
+#include "net/disk_cache/blockfile/entry_impl.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
@@ -893,7 +893,7 @@ HANDLE CreateServer(base::string16* pipe_number) {
// This is the controller process for an upgrade operation.
int UpgradeCache(const base::FilePath& output_path, HANDLE pipe) {
- base::MessageLoop loop(base::MessageLoop::TYPE_IO);
+ base::MessageLoopForIO loop;
MasterSM master(output_path, pipe);
if (!master.DoInit()) {
@@ -908,7 +908,7 @@ int UpgradeCache(const base::FilePath& output_path, HANDLE pipe) {
// This process will only execute commands from the controller.
int RunSlave(const base::FilePath& input_path,
const base::string16& pipe_number) {
- base::MessageLoop loop(base::MessageLoop::TYPE_IO);
+ base::MessageLoopForIO loop;
base::win::ScopedHandle pipe(OpenServer(pipe_number));
if (!pipe.IsValid()) {
diff --git a/chromium/net/tools/fetch/fetch_client.cc b/chromium/net/tools/fetch/fetch_client.cc
deleted file mode 100644
index 12fae24399a..00000000000
--- a/chromium/net/tools/fetch/fetch_client.cc
+++ /dev/null
@@ -1,231 +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 "build/build_config.h"
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/lazy_instance.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/stats_counters.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "net/base/completion_callback.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/request_priority.h"
-#include "net/cert/cert_verifier.h"
-#include "net/dns/host_resolver.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_cache.h"
-#include "net/http/http_network_layer.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/http/http_stream_factory.h"
-#include "net/http/http_transaction.h"
-#include "net/http/transport_security_state.h"
-#include "net/proxy/proxy_service.h"
-#include "net/ssl/ssl_config_service_defaults.h"
-
-void usage(const char* program_name) {
- printf("usage: %s --url=<url> [--n=<clients>] [--stats] [--use_cache]\n",
- program_name);
- exit(1);
-}
-
-// Test Driver
-class Driver {
- public:
- Driver()
- : clients_(0) {}
-
- void ClientStarted() { clients_++; }
- void ClientStopped() {
- if (!--clients_) {
- base::MessageLoop::current()->Quit();
- }
- }
-
- private:
- int clients_;
-};
-
-static base::LazyInstance<Driver> g_driver = LAZY_INSTANCE_INITIALIZER;
-
-// A network client
-class Client {
- public:
- Client(net::HttpTransactionFactory* factory, const std::string& url) :
- url_(url),
- buffer_(new net::IOBuffer(kBufferSize)) {
- int rv = factory->CreateTransaction(
- net::DEFAULT_PRIORITY, &transaction_, NULL);
- DCHECK_EQ(net::OK, rv);
- buffer_->AddRef();
- g_driver.Get().ClientStarted();
- request_info_.url = url_;
- request_info_.method = "GET";
- int state = transaction_->Start(
- &request_info_,
- base::Bind(&Client::OnConnectComplete, base::Unretained(this)),
- net::BoundNetLog());
- DCHECK(state == net::ERR_IO_PENDING);
- };
-
- private:
- void OnConnectComplete(int result) {
- // Do work here.
- int state = transaction_->Read(
- buffer_.get(), kBufferSize,
- base::Bind(&Client::OnReadComplete, base::Unretained(this)));
- if (state == net::ERR_IO_PENDING)
- return; // IO has started.
- if (state < 0)
- return; // ERROR!
- OnReadComplete(state);
- }
-
- void OnReadComplete(int result) {
- if (result == 0) {
- OnRequestComplete(result);
- return;
- }
-
- // Deal with received data here.
- base::StatsCounter bytes_read("FetchClient.bytes_read");
- bytes_read.Add(result);
-
- // Issue a read for more data.
- int state = transaction_->Read(
- buffer_.get(), kBufferSize,
- base::Bind(&Client::OnReadComplete, base::Unretained(this)));
- if (state == net::ERR_IO_PENDING)
- return; // IO has started.
- if (state < 0)
- return; // ERROR!
- OnReadComplete(state);
- }
-
- void OnRequestComplete(int result) {
- base::StatsCounter requests("FetchClient.requests");
- requests.Increment();
- g_driver.Get().ClientStopped();
- printf(".");
- }
-
- static const int kBufferSize = (16 * 1024);
- GURL url_;
- net::HttpRequestInfo request_info_;
- scoped_ptr<net::HttpTransaction> transaction_;
- scoped_refptr<net::IOBuffer> buffer_;
-};
-
-int main(int argc, char** argv) {
- base::AtExitManager exit;
- base::StatsTable table("fetchclient", 50, 1000);
- table.set_current(&table);
-
- CommandLine::Init(argc, argv);
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
- std::string url = parsed_command_line.GetSwitchValueASCII("url");
- if (!url.length())
- usage(argv[0]);
- int client_limit = 1;
- if (parsed_command_line.HasSwitch("n")) {
- base::StringToInt(parsed_command_line.GetSwitchValueASCII("n"),
- &client_limit);
- }
- bool use_cache = parsed_command_line.HasSwitch("use-cache");
-
- // Do work here.
- base::MessageLoop loop(base::MessageLoop::TYPE_IO);
-
- net::HttpStreamFactory::EnableNpnHttp2Draft04();
-
- scoped_ptr<net::HostResolver> host_resolver(
- net::HostResolver::CreateDefaultResolver(NULL));
- scoped_ptr<net::CertVerifier> cert_verifier(
- net::CertVerifier::CreateDefault());
- scoped_ptr<net::TransportSecurityState> transport_security_state(
- new net::TransportSecurityState);
- scoped_ptr<net::ProxyService> proxy_service(
- net::ProxyService::CreateDirect());
- scoped_refptr<net::SSLConfigService> ssl_config_service(
- new net::SSLConfigServiceDefaults);
- net::HttpTransactionFactory* factory = NULL;
- scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory(
- net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
- net::HttpServerPropertiesImpl http_server_properties;
-
- net::HttpNetworkSession::Params session_params;
- session_params.host_resolver = host_resolver.get();
- session_params.cert_verifier = cert_verifier.get();
- session_params.transport_security_state = transport_security_state.get();
- session_params.proxy_service = proxy_service.get();
- session_params.http_auth_handler_factory = http_auth_handler_factory.get();
- session_params.http_server_properties = http_server_properties.GetWeakPtr();
- session_params.ssl_config_service = ssl_config_service.get();
-
- scoped_refptr<net::HttpNetworkSession> network_session(
- new net::HttpNetworkSession(session_params));
- if (use_cache) {
- factory = new net::HttpCache(network_session.get(),
- net::HttpCache::DefaultBackend::InMemory(0));
- } else {
- factory = new net::HttpNetworkLayer(network_session.get());
- }
-
- {
- base::StatsCounterTimer driver_time("FetchClient.total_time");
- base::StatsScope<base::StatsCounterTimer> scope(driver_time);
-
- Client** clients = new Client*[client_limit];
- for (int i = 0; i < client_limit; i++)
- clients[i] = new Client(factory, url);
-
- base::MessageLoop::current()->Run();
- }
-
- // Print Statistics here.
- int num_clients = table.GetCounterValue("c:FetchClient.requests");
- int test_time = table.GetCounterValue("t:FetchClient.total_time");
- int bytes_read = table.GetCounterValue("c:FetchClient.bytes_read");
-
- printf("\n");
- printf("Clients : %d\n", num_clients);
- printf("Time : %dms\n", test_time);
- printf("Bytes Read : %d\n", bytes_read);
- if (test_time > 0) {
- const char *units = "bps";
- double bps = static_cast<float>(bytes_read * 8) /
- (static_cast<float>(test_time) / 1000.0);
-
- if (bps > (1024*1024)) {
- bps /= (1024*1024);
- units = "Mbps";
- } else if (bps > 1024) {
- bps /= 1024;
- units = "Kbps";
- }
- printf("Bandwidth : %.2f%s\n", bps, units);
- }
-
- if (parsed_command_line.HasSwitch("stats")) {
- // Dump the stats table.
- printf("<stats>\n");
- int counter_max = table.GetMaxCounters();
- for (int index = 0; index < counter_max; index++) {
- std::string name(table.GetRowName(index));
- if (name.length() > 0) {
- int value = table.GetRowValue(index);
- printf("%s:\t%d\n", name.c_str(), value);
- }
- }
- printf("</stats>\n");
- }
- return 0;
-}
diff --git a/chromium/net/tools/fetch/fetch_server.cc b/chromium/net/tools/fetch/fetch_server.cc
deleted file mode 100644
index a2ffc5b5708..00000000000
--- a/chromium/net/tools/fetch/fetch_server.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2011 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 "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/stats_counters.h"
-#include "net/base/completion_callback.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/winsock_init.h"
-#include "net/http/http_network_layer.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_transaction.h"
-#include "net/proxy/proxy_service.h"
-#include "net/tools/fetch/http_server.h"
-
-void usage(const char* program_name) {
- printf("usage: %s\n", program_name);
- exit(-1);
-}
-
-int main(int argc, char**argv) {
- base::AtExitManager exit;
- base::StatsTable table("fetchserver", 50, 1000);
- table.set_current(&table);
-
-#if defined(OS_WIN)
- net::EnsureWinsockInit();
-#endif // defined(OS_WIN)
-
- CommandLine::Init(0, NULL);
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
-
- // Do work here.
- base::MessageLoop loop;
- HttpServer server(std::string(),
- 80); // TODO(mbelshe): make port configurable
- base::MessageLoop::current()->Run();
-
- if (parsed_command_line.HasSwitch("stats")) {
- // Dump the stats table.
- printf("<stats>\n");
- int counter_max = table.GetMaxCounters();
- for (int index=0; index < counter_max; index++) {
- std::string name(table.GetRowName(index));
- if (name.length() > 0) {
- int value = table.GetRowValue(index);
- printf("%s:\t%d\n", name.c_str(), value);
- }
- }
- printf("</stats>\n");
- }
-
-}
diff --git a/chromium/net/tools/fetch/http_listen_socket.cc b/chromium/net/tools/fetch/http_listen_socket.cc
deleted file mode 100644
index 410a0ba55e5..00000000000
--- a/chromium/net/tools/fetch/http_listen_socket.cc
+++ /dev/null
@@ -1,249 +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/tools/fetch/http_listen_socket.h"
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "net/tools/fetch/http_server_request_info.h"
-#include "net/tools/fetch/http_server_response_info.h"
-
-HttpListenSocket::HttpListenSocket(net::SocketDescriptor s,
- HttpListenSocket::Delegate* delegate)
- : net::TCPListenSocket(s, this),
- delegate_(delegate) {
-}
-
-HttpListenSocket::~HttpListenSocket() {
- STLDeleteElements(&connections_);
-}
-
-void HttpListenSocket::Accept() {
- net::SocketDescriptor conn = net::TCPListenSocket::AcceptSocket();
- DCHECK_NE(conn, net::kInvalidSocket);
- if (conn == net::kInvalidSocket) {
- // TODO
- } else {
- scoped_ptr<StreamListenSocket> sock(
- new HttpListenSocket(conn, delegate_));
- DidAccept(this, sock.Pass());
- }
-}
-
-// static
-scoped_ptr<HttpListenSocket> HttpListenSocket::CreateAndListen(
- const std::string& ip,
- int port,
- HttpListenSocket::Delegate* delegate) {
- net::SocketDescriptor s = net::TCPListenSocket::CreateAndBind(ip, port);
- if (s == net::kInvalidSocket) {
- // TODO (ibrar): error handling.
- } else {
- scoped_ptr<HttpListenSocket> serv(new HttpListenSocket(s, delegate));
- serv->Listen();
- return serv.Pass();
- }
- return scoped_ptr<HttpListenSocket>();
-}
-
-//
-// HTTP Request Parser
-// This HTTP request parser uses a simple state machine to quickly parse
-// through the headers. The parser is not 100% complete, as it is designed
-// for use in this simple test driver.
-//
-// Known issues:
-// - does not handle whitespace on first HTTP line correctly. Expects
-// a single space between the method/url and url/protocol.
-
-// Input character types.
-enum header_parse_inputs {
- INPUT_SPACE,
- INPUT_CR,
- INPUT_LF,
- INPUT_COLON,
- INPUT_DEFAULT,
- MAX_INPUTS
-};
-
-// Parser states.
-enum header_parse_states {
- ST_METHOD, // Receiving the method.
- ST_URL, // Receiving the URL.
- ST_PROTO, // Receiving the protocol.
- ST_HEADER, // Starting a Request Header.
- ST_NAME, // Receiving a request header name.
- ST_SEPARATOR, // Receiving the separator between header name and value.
- ST_VALUE, // Receiving a request header value.
- ST_DONE, // Parsing is complete and successful.
- ST_ERR, // Parsing encountered invalid syntax.
- MAX_STATES
-};
-
-// State transition table.
-int parser_state[MAX_STATES][MAX_INPUTS] = {
-/* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_METHOD },
-/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_URL },
-/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_PROTO },
-/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR },
-/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_NAME },
-/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_VALUE },
-/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_VALUE },
-/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE },
-/* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR }
-};
-
-// Convert an input character to the parser's input token.
-int charToInput(char ch) {
- switch(ch) {
- case ' ':
- return INPUT_SPACE;
- case '\r':
- return INPUT_CR;
- case '\n':
- return INPUT_LF;
- case ':':
- return INPUT_COLON;
- }
- return INPUT_DEFAULT;
-}
-
-HttpServerRequestInfo* HttpListenSocket::ParseHeaders() {
- int pos = 0;
- int data_len = recv_data_.length();
- int state = ST_METHOD;
- HttpServerRequestInfo* info = new HttpServerRequestInfo();
- std::string buffer;
- std::string header_name;
- std::string header_value;
- while (pos < data_len) {
- char ch = recv_data_[pos++];
- int input = charToInput(ch);
- int next_state = parser_state[state][input];
-
- bool transition = (next_state != state);
- if (transition) {
- // Do any actions based on state transitions.
- switch (state) {
- case ST_METHOD:
- info->method = buffer;
- buffer.clear();
- break;
- case ST_URL:
- info->url = GURL(buffer);
- buffer.clear();
- break;
- case ST_PROTO:
- // TODO(mbelshe): Deal better with parsing protocol.
- DCHECK(buffer == "HTTP/1.1");
- buffer.clear();
- break;
- case ST_NAME:
- header_name = buffer;
- buffer.clear();
- break;
- case ST_VALUE:
- header_value = buffer;
- // TODO(mbelshe): Deal better with duplicate headers.
- DCHECK(info->headers.find(header_name) == info->headers.end());
- info->headers[header_name] = header_value;
- buffer.clear();
- break;
- }
- state = next_state;
- } else {
- // Do any actions based on current state.
- switch (state) {
- case ST_METHOD:
- case ST_URL:
- case ST_PROTO:
- case ST_VALUE:
- case ST_NAME:
- buffer.append(&ch, 1);
- break;
- case ST_DONE:
- recv_data_ = recv_data_.substr(pos);
- return info;
- case ST_ERR:
- delete info;
- return NULL;
- }
- }
- }
- // No more characters, but we haven't finished parsing yet.
- delete info;
- return NULL;
-}
-
-void HttpListenSocket::DidAccept(
- net::StreamListenSocket* server,
- scoped_ptr<net::StreamListenSocket> connection) {
- connections_.insert(connection.release());
-}
-
-void HttpListenSocket::DidRead(net::StreamListenSocket* connection,
- const char* data,
- int len) {
- recv_data_.append(data, len);
- while (recv_data_.length()) {
- HttpServerRequestInfo* request = ParseHeaders();
- if (!request)
- break;
- delegate_->OnRequest(this, request);
- delete request;
- }
-}
-
-void HttpListenSocket::DidClose(net::StreamListenSocket* sock) {
- size_t count = connections_.erase(sock);
- DCHECK_EQ(1u, count);
- delete sock;
-}
-
-// Convert the numeric status code to a string.
-// e.g. 200 -> "200 OK".
-std::string ServerStatus(int code) {
- switch(code) {
- case 200:
- return std::string("200 OK");
- // TODO(mbelshe): handle other codes.
- }
- NOTREACHED();
- return std::string();
-}
-
-void HttpListenSocket::Respond(HttpServerResponseInfo* info,
- std::string& data) {
- std::string response;
-
- // Status line.
- response = info->protocol + " ";
- response += ServerStatus(info->status);
- response += "\r\n";
-
- // Standard headers.
- if (info->content_type.length())
- response += "Content-type: " + info->content_type + "\r\n";
-
- if (info->content_length > 0)
- response += "Content-length: " + base::IntToString(info->content_length) +
- "\r\n";
-
- if (info->connection_close)
- response += "Connection: close\r\n";
-
- // TODO(mbelshe): support additional headers.
-
- // End of headers.
- response += "\r\n";
-
- // Add data.
- response += data;
-
- // Write it all out.
- this->Send(response, false);
-}
diff --git a/chromium/net/tools/fetch/http_listen_socket.h b/chromium/net/tools/fetch/http_listen_socket.h
deleted file mode 100644
index e0a58c03e2c..00000000000
--- a/chromium/net/tools/fetch/http_listen_socket.h
+++ /dev/null
@@ -1,70 +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_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_
-#define NET_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_
-
-#include <set>
-
-#include "base/message_loop/message_loop.h"
-#include "net/socket/stream_listen_socket.h"
-#include "net/socket/tcp_listen_socket.h"
-
-class HttpServerRequestInfo;
-class HttpServerResponseInfo;
-
-// Implements a simple HTTP listen socket on top of the raw socket interface.
-class HttpListenSocket : public net::TCPListenSocket,
- public net::StreamListenSocket::Delegate {
- public:
- class Delegate {
- public:
- virtual void OnRequest(HttpListenSocket* connection,
- HttpServerRequestInfo* info) = 0;
-
- protected:
- virtual ~Delegate() {}
- };
-
- virtual ~HttpListenSocket();
-
- static scoped_ptr<HttpListenSocket> CreateAndListen(
- const std::string& ip, int port, HttpListenSocket::Delegate* delegate);
-
- // Send a server response.
- // TODO(mbelshe): make this capable of non-ascii data.
- void Respond(HttpServerResponseInfo* info, std::string& data);
-
- // StreamListenSocket::Delegate.
- virtual void DidAccept(
- net::StreamListenSocket* server,
- scoped_ptr<net::StreamListenSocket> connection) OVERRIDE;
- virtual void DidRead(net::StreamListenSocket* connection,
- const char* data, int len) OVERRIDE;
- virtual void DidClose(net::StreamListenSocket* sock) OVERRIDE;
-
- protected:
- // Overrides TCPListenSocket::Accept().
- virtual void Accept() OVERRIDE;
-
- private:
- static const int kReadBufSize = 16 * 1024;
-
- // Must run in the IO thread.
- HttpListenSocket(net::SocketDescriptor s, HttpListenSocket::Delegate* del);
-
- // Expects the raw data to be stored in recv_data_. If parsing is successful,
- // will remove the data parsed from recv_data_, leaving only the unused
- // recv data.
- HttpServerRequestInfo* ParseHeaders();
-
- HttpListenSocket::Delegate* const delegate_;
- std::string recv_data_;
-
- std::set<StreamListenSocket*> connections_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpListenSocket);
-};
-
-#endif // NET_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_
diff --git a/chromium/net/tools/fetch/http_server.cc b/chromium/net/tools/fetch/http_server.cc
deleted file mode 100644
index 71afb67bc30..00000000000
--- a/chromium/net/tools/fetch/http_server.cc
+++ /dev/null
@@ -1,12 +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/tools/fetch/http_server.h"
-
-HttpServer::HttpServer(std::string ip, int port)
- : session_(new HttpSession(ip, port)) {
-}
-
-HttpServer::~HttpServer() {
-}
diff --git a/chromium/net/tools/fetch/http_server.h b/chromium/net/tools/fetch/http_server.h
deleted file mode 100644
index 691413078e5..00000000000
--- a/chromium/net/tools/fetch/http_server.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2011 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_BASE_TOOLS_HTTP_SERVER_H_
-#define NET_BASE_TOOLS_HTTP_SERVER_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/tools/fetch/http_session.h"
-
-// Implements a simple, single-threaded HttpServer.
-// Right now, this class implements no functionality above and beyond
-// the HttpSession.
-class HttpServer {
-public:
- HttpServer(std::string ip, int port);
- ~HttpServer();
-
-private:
- scoped_ptr<HttpSession> session_;
- DISALLOW_COPY_AND_ASSIGN(HttpServer);
-};
-
-#endif // NET_BASE_TOOLS_HTTP_SERVER_H_
-
diff --git a/chromium/net/tools/fetch/http_server_request_info.cc b/chromium/net/tools/fetch/http_server_request_info.cc
deleted file mode 100644
index 52b6860e0a4..00000000000
--- a/chromium/net/tools/fetch/http_server_request_info.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2011 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/fetch/http_server_request_info.h"
-
-HttpServerRequestInfo::HttpServerRequestInfo()
- : net::HttpRequestInfo() {
-}
-
-HttpServerRequestInfo::~HttpServerRequestInfo() {}
diff --git a/chromium/net/tools/fetch/http_server_request_info.h b/chromium/net/tools/fetch/http_server_request_info.h
deleted file mode 100644
index 9b0fae18d16..00000000000
--- a/chromium/net/tools/fetch/http_server_request_info.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2011 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_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
-#define NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
-
-#include <map>
-#include <string>
-
-#include "net/http/http_request_info.h"
-
-// Meta information about an HTTP request.
-// This is geared toward servers in that it keeps a map of the headers and
-// values rather than just a list of header strings (which net::HttpRequestInfo
-// does).
-class HttpServerRequestInfo : public net::HttpRequestInfo {
- public:
- HttpServerRequestInfo();
- virtual ~HttpServerRequestInfo();
-
- // A map of the names -> values for HTTP headers.
- std::map<std::string, std::string> headers;
-};
-
-#endif // NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
diff --git a/chromium/net/tools/fetch/http_server_response_info.cc b/chromium/net/tools/fetch/http_server_response_info.cc
deleted file mode 100644
index d9ca02a6d0d..00000000000
--- a/chromium/net/tools/fetch/http_server_response_info.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2011 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/fetch/http_server_response_info.h"
-
-HttpServerResponseInfo::HttpServerResponseInfo()
- : status(200), content_length(0), connection_close(false) {
-}
-
-HttpServerResponseInfo::~HttpServerResponseInfo() {}
diff --git a/chromium/net/tools/fetch/http_server_response_info.h b/chromium/net/tools/fetch/http_server_response_info.h
deleted file mode 100644
index e01f7300d97..00000000000
--- a/chromium/net/tools/fetch/http_server_response_info.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2011 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_HTTP_HTTP_RESPONSE_INFO_H_
-#define NET_HTTP_HTTP_RESPONSE_INFO_H_
-
-#include <map>
-#include <string>
-
-// Meta information about a server response.
-class HttpServerResponseInfo {
- public:
- HttpServerResponseInfo();
- ~HttpServerResponseInfo();
-
- // The response protocol.
- std::string protocol;
-
- // The status code.
- int status;
-
- // The server identifier.
- std::string server_name;
-
- // The content type.
- std::string content_type;
-
- // The content length.
- int content_length;
-
- // Should we close the connection.
- bool connection_close;
-
- // Additional response headers.
- std::map<std::string, std::string> headers;
-};
-
-#endif // NET_HTTP_HTTP_RESPONSE_INFO_H_
diff --git a/chromium/net/tools/fetch/http_session.cc b/chromium/net/tools/fetch/http_session.cc
deleted file mode 100644
index d9e991b798a..00000000000
--- a/chromium/net/tools/fetch/http_session.cc
+++ /dev/null
@@ -1,33 +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/tools/fetch/http_session.h"
-#include "net/tools/fetch/http_server_response_info.h"
-
-HttpSession::HttpSession(const std::string& ip, int port)
- : socket_(HttpListenSocket::CreateAndListen(ip, port, this)) {
-}
-
-HttpSession::~HttpSession() {
-}
-
-void HttpSession::OnRequest(HttpListenSocket* connection,
- HttpServerRequestInfo* info) {
- // TODO(mbelshe): Make this function more interesting.
-
- // Generate a 10KB sequence of data.
- CR_DEFINE_STATIC_LOCAL(std::string, data, ());
- if (data.length() == 0) {
- while (data.length() < (10 * 1024))
- data += 'a' + (rand() % 26);
- }
-
- HttpServerResponseInfo response_info;
- response_info.protocol = "HTTP/1.1";
- response_info.status = 200;
- response_info.content_type = "text/plain";
- response_info.content_length = data.length();
-
- connection->Respond(&response_info, data);
-}
diff --git a/chromium/net/tools/fetch/http_session.h b/chromium/net/tools/fetch/http_session.h
deleted file mode 100644
index b0266f2a9f7..00000000000
--- a/chromium/net/tools/fetch/http_session.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2011 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_BASE_TOOLS_HTTP_SESSION_H_
-#define NET_BASE_TOOLS_HTTP_SESSION_H_
-
-#include "base/basictypes.h"
-#include "net/http/http_request_info.h"
-#include "net/tools/fetch/http_listen_socket.h"
-
-// An HttpSession encapsulates a server-side HTTP listen socket.
-class HttpSession : HttpListenSocket::Delegate {
- public:
- HttpSession(const std::string& ip, int port);
- virtual ~HttpSession();
-
- virtual void OnRequest(HttpListenSocket* connection,
- HttpServerRequestInfo* info) OVERRIDE;
-
- private:
- scoped_ptr<HttpListenSocket> socket_;
- DISALLOW_COPY_AND_ASSIGN(HttpSession);
-};
-
-#endif // NET_BASE_TOOLS_HTTP_SESSION_H_
-
diff --git a/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc b/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc
index ef1cefd6125..a8459b63c97 100644
--- a/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc
+++ b/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc
@@ -161,8 +161,8 @@ int main(int argc, char** argv) {
signal(SIGINT, SignalHandler);
signal(SIGHUP, SignalHandler);
- CommandLine::Init(argc, argv);
- CommandLine cl(argc, argv);
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine cl(argc, argv);
if (cl.HasSwitch("help") || argc < 2) {
printf("%s <options>\n", argv[0]);
diff --git a/chromium/net/tools/flip_server/loadtime_measurement.h b/chromium/net/tools/flip_server/loadtime_measurement.h
index ccbb2e53234..59fcc0bfcdc 100644
--- a/chromium/net/tools/flip_server/loadtime_measurement.h
+++ b/chromium/net/tools/flip_server/loadtime_measurement.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H__
-#define NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H__
+#ifndef NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
+#define NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
#include <errno.h>
#include <fcntl.h>
@@ -15,6 +15,9 @@
#include <string>
#include <vector>
+#include "base/file_util.h"
+#include "base/strings/string_split.h"
+
// Class to handle loadtime measure related urls, which all start with testing
// The in memory server has a singleton object of this class. It includes a
// html file containing javascript to go through a list of urls and upload the
@@ -26,8 +29,8 @@ class LoadtimeMeasurement {
const std::string& pageload_html_file)
: num_urls_(0), pageload_html_file_(pageload_html_file) {
std::string urls_string;
- read_file_to_string(urls_file.c_str(), &urls_string);
- split_string(urls_string, '\n', &urls_);
+ base::ReadFileToString(urls_file, &urls_string);
+ base::SplitString(urls_string, '\n', &urls_);
num_urls_ = urls_.size();
}
@@ -39,7 +42,7 @@ class LoadtimeMeasurement {
// remove "/testing/" from uri to get the action
std::string action = uri.substr(9);
if (pageload_html_file_.find(action) != std::string::npos) {
- read_file_to_string(pageload_html_file_.c_str(), &output);
+ base::ReadFileToString(pageload_html_file_, &output);
return;
}
if (action.find("get_total_iteration") == 0) {
@@ -70,13 +73,13 @@ class LoadtimeMeasurement {
}
if (action.find("record_page_load") == 0) {
std::vector<std::string> query;
- split_string(action, '?', &query);
+ base::SplitString(action, '?', &query);
std::vector<std::string> params;
- split_string(query[1], '&', &params);
+ base::SplitString(query[1], '&', &params);
std::vector<std::string> url;
std::vector<std::string> loadtime;
- split_string(params[1], '=', &url);
- split_string(params[2], '=', &loadtime);
+ base::SplitString(params[1], '=', &url);
+ base::SplitString(params[2], '=', &loadtime);
loadtimes_[url[1]] = atoi(loadtime[1].c_str());
output.append("OK");
return;
@@ -84,41 +87,10 @@ class LoadtimeMeasurement {
}
private:
- void read_file_to_string(const char* filename, std::string* output) {
- output->clear();
- int fd = open(filename, 0, "r");
- if (fd == -1)
- return;
- char buffer[4096];
- ssize_t read_status = read(fd, buffer, sizeof(buffer));
- while (read_status > 0) {
- output->append(buffer, static_cast<size_t>(read_status));
- do {
- read_status = read(fd, buffer, sizeof(buffer));
- } while (read_status <= 0 && errno == EINTR);
- }
- close(fd);
- }
-
- void split_string(const std::string& str,
- char sepa,
- std::vector<std::string>* sub_strs) {
- size_t b = 0;
- size_t e = str.find_first_of(sepa, b);
- while (e != std::string::npos && e > b) {
- sub_strs->push_back(str.substr(b, e - b));
- b = e + 1;
- e = str.find_first_of(sepa, b);
- }
- if (b < str.size()) {
- sub_strs->push_back(str.substr(b));
- }
- }
-
int num_urls_;
std::vector<std::string> urls_;
std::map<std::string, int> loadtimes_;
const std::string pageload_html_file_;
};
-#endif // NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H__
+#endif // NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_
diff --git a/chromium/net/tools/flip_server/sm_connection.cc b/chromium/net/tools/flip_server/sm_connection.cc
index 158cd30ff4f..4acdea44adf 100644
--- a/chromium/net/tools/flip_server/sm_connection.cc
+++ b/chromium/net/tools/flip_server/sm_connection.cc
@@ -391,7 +391,9 @@ bool SMConnection::SetupProtocolInterfaces() {
VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT
<< (sm_spdy_interface_ ? "Creating" : "Reusing")
<< " SPDY interface.";
- if (!sm_spdy_interface_)
+ if (sm_spdy_interface_)
+ sm_spdy_interface_->CreateFramer(version);
+ else
sm_spdy_interface_ = new SpdySM(
this, NULL, epoll_server_, memory_cache_, acceptor_, version);
sm_interface_ = sm_spdy_interface_;
diff --git a/chromium/net/tools/flip_server/sm_connection.h b/chromium/net/tools/flip_server/sm_connection.h
index 93ad42e7e5a..2fe3228c0a1 100644
--- a/chromium/net/tools/flip_server/sm_connection.h
+++ b/chromium/net/tools/flip_server/sm_connection.h
@@ -25,6 +25,7 @@ namespace net {
class FlipAcceptor;
class MemoryCache;
struct SSLState;
+class SpdySM;
// A frame of data to be sent.
class DataFrame {
@@ -146,7 +147,7 @@ class SMConnection : public SMConnectionInterface,
RingBuffer read_buffer_;
OutputList output_list_;
- SMInterface* sm_spdy_interface_;
+ SpdySM* sm_spdy_interface_;
SMInterface* sm_http_interface_;
SMInterface* sm_streamer_interface_;
SMInterface* sm_interface_;
diff --git a/chromium/net/tools/flip_server/spdy_interface.cc b/chromium/net/tools/flip_server/spdy_interface.cc
index 41ad43025e2..73025dd5407 100644
--- a/chromium/net/tools/flip_server/spdy_interface.cc
+++ b/chromium/net/tools/flip_server/spdy_interface.cc
@@ -51,7 +51,7 @@ SpdySM::SpdySM(SMConnection* connection,
buffered_spdy_framer_->set_visitor(this);
}
-SpdySM::~SpdySM() { delete buffered_spdy_framer_; }
+SpdySM::~SpdySM() { }
void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
SMInterface* sm_interface,
@@ -130,7 +130,6 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn(" << stream_id << ")";
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: " << headers.size();
- SpdyHeaderBlock supplement;
SpdyHeaderBlock::const_iterator method = headers.end();
SpdyHeaderBlock::const_iterator host = headers.end();
SpdyHeaderBlock::const_iterator path = headers.end();
@@ -138,6 +137,8 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
SpdyHeaderBlock::const_iterator version = headers.end();
SpdyHeaderBlock::const_iterator url = headers.end();
+ std::string path_string, host_string, version_string;
+
if (spdy_version() == SPDY2) {
url = headers.find("url");
method = headers.find("method");
@@ -153,22 +154,23 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
// path contains a query string with a http:// in one of its values,
// UrlUtilities::GetUrlPath will fail and always return a / breaking
// the request. GetUrlPath assumes the absolute URL is being passed in.
- std::string path_string = UrlUtilities::GetUrlPath(url->second);
- std::string host_string = UrlUtilities::GetUrlHost(url->second);
- path = supplement.insert(std::make_pair(":path", path_string)).first;
- host = supplement.insert(std::make_pair(":host", host_string)).first;
+ path_string = UrlUtilities::GetUrlPath(url->second);
+ host_string = UrlUtilities::GetUrlHost(url->second);
+ version_string = version->second;
} else {
method = headers.find(":method");
host = headers.find(":host");
path = headers.find(":path");
scheme = headers.find(":scheme");
- version = supplement.insert(std::make_pair(":version", "HTTP/1.1")).first;
if (method == headers.end() || host == headers.end() ||
path == headers.end() || scheme == headers.end()) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is "
<< "missing. Not creating stream";
return 0;
}
+ host_string = host->second;
+ path_string = path->second;
+ version_string = "HTTP/1.1";
}
if (scheme->second.compare("https") == 0) {
@@ -177,16 +179,16 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id,
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) {
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second
- << " " << path->second;
- std::string filename = EncodeURL(path->second,
- host->second,
+ << " " << path_string;
+ std::string filename = EncodeURL(path_string,
+ host_string,
method->second);
NewStream(stream_id, priority, filename);
} else {
http_data +=
- method->second + " " + path->second + " " + version->second + "\r\n";
+ method->second + " " + path_string + " " + version_string + "\r\n";
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " "
- << path->second << " " << version->second;
+ << path_string << " " << version_string;
http_data += "Host: " + (*is_https_scheme ?
acceptor_->https_server_ip_ :
acceptor_->http_server_ip_) + "\r\n";
@@ -241,7 +243,6 @@ void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
void SpdySM::OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) {
@@ -296,21 +297,25 @@ void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) {
}
size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
+ DCHECK(buffered_spdy_framer_);
return buffered_spdy_framer_->ProcessInput(data, len);
}
size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; }
bool SpdySM::MessageFullyRead() const {
+ DCHECK(buffered_spdy_framer_);
return buffered_spdy_framer_->MessageFullyRead();
}
bool SpdySM::Error() const {
+ DCHECK(buffered_spdy_framer_);
return close_on_error_ || buffered_spdy_framer_->HasError();
}
const char* SpdySM::ErrorAsString() const {
DCHECK(Error());
+ DCHECK(buffered_spdy_framer_);
return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code());
}
@@ -322,9 +327,7 @@ void SpdySM::ResetForNewInterface(int32 server_idx) {
void SpdySM::ResetForNewConnection() {
// seq_num is not cleared, intentionally.
- delete buffered_spdy_framer_;
- buffered_spdy_framer_ = new BufferedSpdyFramer(SPDY2, true);
- buffered_spdy_framer_->set_visitor(this);
+ buffered_spdy_framer_.reset();
valid_spdy_session_ = false;
client_output_ordering_.Reset();
next_outgoing_stream_id_ = 2;
@@ -332,6 +335,8 @@ void SpdySM::ResetForNewConnection() {
// Send a settings frame
int SpdySM::PostAcceptHook() {
+ // We should have buffered_spdy_framer_ set after reuse
+ DCHECK(buffered_spdy_framer_);
SettingsMap settings;
settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100);
@@ -469,8 +474,9 @@ size_t SpdySM::SendSynStreamImpl(uint32 stream_id,
}
}
+ DCHECK(buffered_spdy_framer_);
SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream(
- stream_id, 0, 0, 0, CONTROL_FLAG_NONE, &block);
+ stream_id, 0, 0, CONTROL_FLAG_NONE, &block);
size_t df_size = fsrcf->size();
EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf));
@@ -492,6 +498,7 @@ size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
block[":version"] = headers.response_version().as_string();
}
+ DCHECK(buffered_spdy_framer_);
SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply(
stream_id, CONTROL_FLAG_NONE, &block);
size_t df_size = fsrcf->size();
@@ -507,6 +514,7 @@ void SpdySM::SendDataFrameImpl(uint32 stream_id,
int64 len,
SpdyDataFlags flags,
bool compress) {
+ DCHECK(buffered_spdy_framer_);
// TODO(mbelshe): We can't compress here - before going into the
// priority queue. Compression needs to be done
// with late binding.
@@ -607,4 +615,10 @@ void SpdySM::GetOutput() {
}
}
+void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) {
+ DCHECK(!buffered_spdy_framer_);
+ buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true));
+ buffered_spdy_framer_->set_visitor(this);
+}
+
} // namespace net
diff --git a/chromium/net/tools/flip_server/spdy_interface.h b/chromium/net/tools/flip_server/spdy_interface.h
index 6e5ad0b0a8f..e472cf2ac59 100644
--- a/chromium/net/tools/flip_server/spdy_interface.h
+++ b/chromium/net/tools/flip_server/spdy_interface.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
#include "net/spdy/buffered_spdy_framer.h"
#include "net/spdy/spdy_protocol.h"
#include "net/tools/balsa/balsa_headers.h"
@@ -45,6 +46,9 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
std::string remote_ip,
bool use_ssl) OVERRIDE;
+ // Create new SPDY framer after reusing SpdySM and negotiating new version
+ void CreateFramer(SpdyMajorVersion spdy_version);
+
private:
virtual void set_is_request() OVERRIDE {}
SMInterface* NewConnectionInterface();
@@ -66,7 +70,6 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
- uint8 credential_slot,
bool fin,
bool unidirectional,
const SpdyHeaderBlock& headers) OVERRIDE;
@@ -108,7 +111,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
uint32 value) OVERRIDE {}
// Called when a PING frame has been parsed.
- virtual void OnPing(uint32 unique_id) OVERRIDE {}
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
// Called when a RST_STREAM frame has been parsed.
virtual void OnRstStream(SpdyStreamId stream_id,
@@ -124,7 +127,8 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
// Called when a PUSH_PROMISE frame has been parsed.
virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {}
+ SpdyStreamId promised_stream_id,
+ const SpdyHeaderBlock& headers) OVERRIDE {}
public:
virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE;
@@ -159,7 +163,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
int64 len,
uint32 flags,
bool compress) OVERRIDE;
- BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_; }
+ BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_.get(); }
const OutputOrdering& output_ordering() const {
return client_output_ordering_;
@@ -170,6 +174,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
forward_ip_header_ = value;
}
SpdyMajorVersion spdy_version() const {
+ DCHECK(buffered_spdy_framer_);
return buffered_spdy_framer_->protocol_version();
}
@@ -189,7 +194,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
virtual void GetOutput() OVERRIDE;
private:
- BufferedSpdyFramer* buffered_spdy_framer_;
+ scoped_ptr<BufferedSpdyFramer> buffered_spdy_framer_;
bool valid_spdy_session_; // True if we have seen valid data on this session.
// Use this to fail fast when junk is sent to our
// port.
diff --git a/chromium/net/tools/flip_server/spdy_interface_test.cc b/chromium/net/tools/flip_server/spdy_interface_test.cc
index 3b988520158..7e2e5245cbb 100644
--- a/chromium/net/tools/flip_server/spdy_interface_test.cc
+++ b/chromium/net/tools/flip_server/spdy_interface_test.cc
@@ -44,15 +44,13 @@ class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface {
virtual ~SpdyFramerVisitor() {}
MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError));
MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&));
- MOCK_METHOD7(OnSynStream,
+ MOCK_METHOD6(OnSynStream,
void(SpdyStreamId,
SpdyStreamId,
SpdyPriority,
- uint8,
bool,
bool,
const SpdyHeaderBlock&));
- MOCK_METHOD3(OnSynStream, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool));
@@ -62,11 +60,12 @@ class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface {
bool));
MOCK_METHOD1(OnSettings, void(bool clear_persisted));
MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32));
- MOCK_METHOD1(OnPing, void(uint32 unique_id));
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus));
MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus));
MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32));
- MOCK_METHOD2(OnPushPromise, void(SpdyStreamId, SpdyStreamId));
+ MOCK_METHOD3(OnPushPromise,
+ void(SpdyStreamId, SpdyStreamId, const SpdyHeaderBlock&));
};
class FakeSMConnection : public SMConnection {
@@ -243,7 +242,7 @@ TEST_P(SpdySMProxyTest, OnSynStream_SPDY2) {
InvokeWithoutArgs(&saver, &StringSaver::Save),
Return(0)));
}
- visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
+ visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
ASSERT_EQ(expected, saver.string);
}
@@ -278,7 +277,7 @@ TEST_P(SpdySMProxyTest, OnSynStream) {
InvokeWithoutArgs(&saver, &StringSaver::Save),
Return(0)));
}
- visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
+ visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
ASSERT_EQ(expected, saver.string);
}
@@ -294,7 +293,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) {
SpdyHeaderBlock block;
testing::MockFunction<void(int)> checkpoint; // NOLINT
- scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12));
+ scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
block["method"] = "GET";
block["url"] = "http://www.example.com/path";
block["scheme"] = "http";
@@ -310,7 +309,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) {
ProcessWriteInput(frame->data(), frame->size())).Times(1);
}
- visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
+ visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
checkpoint.Call(0);
visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
}
@@ -327,7 +326,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData) {
SpdyHeaderBlock block;
testing::MockFunction<void(int)> checkpoint; // NOLINT
- scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12));
+ scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
block[":method"] = "GET";
block[":host"] = "www.example.com";
block[":path"] = "/path";
@@ -345,7 +344,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData) {
ProcessWriteInput(frame->data(), frame->size())).Times(1);
}
- visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
+ visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
checkpoint.Call(0);
visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
}
@@ -397,7 +396,19 @@ TEST_P(SpdySMProxyTest, ResetForNewConnection) {
interface_->ResetForNewConnection();
ASSERT_FALSE(HasStream(stream_id));
- ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
+ ASSERT_TRUE(interface_->spdy_framer() == NULL);
+}
+
+TEST_P(SpdySMProxyTest, CreateFramer) {
+ interface_->ResetForNewConnection();
+ interface_->CreateFramer(SPDY2);
+ ASSERT_TRUE(interface_->spdy_framer() != NULL);
+ ASSERT_EQ(interface_->spdy_version(), SPDY2);
+
+ interface_->ResetForNewConnection();
+ interface_->CreateFramer(SPDY3);
+ ASSERT_TRUE(interface_->spdy_framer() != NULL);
+ ASSERT_EQ(interface_->spdy_version(), SPDY3);
}
TEST_P(SpdySMProxyTest, PostAcceptHook) {
@@ -455,8 +466,13 @@ TEST_P(SpdySMProxyTest, SendErrorNotFound_SPDY2) {
{
InSequence s;
- EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
- .WillOnce(SaveArg<2>(&actual_header_block));
+ if (GetParam() < SPDY4) {
+ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ } else {
+ EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ }
EXPECT_CALL(checkpoint, Call(0));
EXPECT_CALL(*spdy_framer_visitor_,
OnDataFrameHeader(stream_id, _, true));
@@ -499,9 +515,15 @@ TEST_P(SpdySMProxyTest, SendErrorNotFound) {
{
InSequence s;
- EXPECT_CALL(*spdy_framer_visitor_,
- OnSynReply(stream_id, false, _))
- .WillOnce(SaveArg<2>(&actual_header_block));
+ if (GetParam() < SPDY4) {
+ EXPECT_CALL(*spdy_framer_visitor_,
+ OnSynReply(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ } else {
+ EXPECT_CALL(*spdy_framer_visitor_,
+ OnHeaders(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ }
EXPECT_CALL(checkpoint, Call(0));
EXPECT_CALL(*spdy_framer_visitor_,
OnDataFrameHeader(stream_id, _, true));
@@ -547,8 +569,8 @@ TEST_P(SpdySMProxyTest, SendSynStream_SPDY2) {
{
InSequence s;
EXPECT_CALL(*spdy_framer_visitor_,
- OnSynStream(stream_id, 0, _, _, false, false, _))
- .WillOnce(SaveArg<6>(&actual_header_block));
+ OnSynStream(stream_id, 0, _, false, false, _))
+ .WillOnce(SaveArg<5>(&actual_header_block));
}
spdy_framer_->ProcessInput(df->data, df->size);
@@ -581,8 +603,8 @@ TEST_P(SpdySMProxyTest, SendSynStream) {
{
InSequence s;
EXPECT_CALL(*spdy_framer_visitor_,
- OnSynStream(stream_id, 0, _, _, false, false, _))
- .WillOnce(SaveArg<6>(&actual_header_block));
+ OnSynStream(stream_id, 0, _, false, false, _))
+ .WillOnce(SaveArg<5>(&actual_header_block));
}
spdy_framer_->ProcessInput(df->data, df->size);
@@ -614,8 +636,13 @@ TEST_P(SpdySMProxyTest, SendSynReply_SPDY2) {
{
InSequence s;
- EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
- .WillOnce(SaveArg<2>(&actual_header_block));
+ if (GetParam() < SPDY4) {
+ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ } else {
+ EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ }
}
spdy_framer_->ProcessInput(df->data, df->size);
@@ -645,8 +672,13 @@ TEST_P(SpdySMProxyTest, SendSynReply) {
{
InSequence s;
- EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
- .WillOnce(SaveArg<2>(&actual_header_block));
+ if (GetParam() < SPDY4) {
+ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ } else {
+ EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ }
}
spdy_framer_->ProcessInput(df->data, df->size);
@@ -787,7 +819,7 @@ TEST_P(SpdySMServerTest, OnSynStream) {
BalsaHeaders headers;
memory_cache_->InsertFile(&headers, "GET_/path", "");
}
- visitor->OnSynStream(stream_id, 0, 0, 0, true, true, spdy_headers);
+ visitor->OnSynStream(stream_id, 0, 0, true, true, spdy_headers);
ASSERT_TRUE(HasStream(stream_id));
}
@@ -817,8 +849,13 @@ TEST_P(SpdySMServerTest, NewStreamError) {
{
InSequence s;
- EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
- .WillOnce(SaveArg<2>(&actual_header_block));
+ if (GetParam() < SPDY4) {
+ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ } else {
+ EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
+ .WillOnce(SaveArg<2>(&actual_header_block));
+ }
EXPECT_CALL(checkpoint, Call(0));
EXPECT_CALL(*spdy_framer_visitor_,
OnDataFrameHeader(stream_id, _, true));
diff --git a/chromium/net/tools/flip_server/spdy_ssl.cc b/chromium/net/tools/flip_server/spdy_ssl.cc
index 1f18a0b30ff..2a5cb0e4953 100644
--- a/chromium/net/tools/flip_server/spdy_ssl.cc
+++ b/chromium/net/tools/flip_server/spdy_ssl.cc
@@ -42,9 +42,7 @@ void InitSSL(SSLState* state,
PrintSslError();
state->ssl_method = SSLv23_method();
- // TODO(joth): Remove const_cast when the openssl 1.0.0 upgrade is complete.
- // (See http://codereview.chromium.org/9254031).
- state->ssl_ctx = SSL_CTX_new(const_cast<SSL_METHOD*>(state->ssl_method));
+ state->ssl_ctx = SSL_CTX_new(state->ssl_method);
if (!state->ssl_ctx) {
PrintSslError();
LOG(FATAL) << "Unable to create SSL context";
diff --git a/chromium/net/tools/gdig/file_net_log.cc b/chromium/net/tools/gdig/file_net_log.cc
index 7c755c855fe..5020f6de647 100644
--- a/chromium/net/tools/gdig/file_net_log.cc
+++ b/chromium/net/tools/gdig/file_net_log.cc
@@ -27,7 +27,7 @@ void FileNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
const char* source = NetLog::SourceTypeToString(entry.source().type);
const char* type = NetLog::EventTypeToString(entry.type());
- scoped_ptr<Value> param_value(entry.ParametersToValue());
+ scoped_ptr<base::Value> param_value(entry.ParametersToValue());
std::string params;
if (param_value.get() != NULL) {
JSONStringValueSerializer serializer(&params);
diff --git a/chromium/net/tools/gdig/gdig.cc b/chromium/net/tools/gdig/gdig.cc
index db0704c5a84..fbee8860ad6 100644
--- a/chromium/net/tools/gdig/gdig.cc
+++ b/chromium/net/tools/gdig/gdig.cc
@@ -16,6 +16,7 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h"
@@ -275,8 +276,9 @@ GDig::Result GDig::Main(int argc, const char* argv[]) {
}
bool GDig::ParseCommandLine(int argc, const char* argv[]) {
- CommandLine::Init(argc, argv);
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& parsed_command_line =
+ *base::CommandLine::ForCurrentProcess();
if (parsed_command_line.HasSwitch("config_timeout")) {
int timeout_seconds = 0;
@@ -299,7 +301,6 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) {
std::map<std::string, NetLog::LogLevel> log_levels;
log_levels["all"] = NetLog::LOG_ALL;
log_levels["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES;
- log_levels["basic"] = NetLog::LOG_BASIC;
if (log_levels.find(log_param) != log_levels.end()) {
level = log_levels[log_param];
@@ -362,7 +363,7 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) {
ReplayLogEntry entry;
entry.start_time = base::TimeDelta();
#if defined(OS_WIN)
- entry.domain_name = WideToASCII(parsed_command_line.GetArgs()[0]);
+ entry.domain_name = base::UTF16ToASCII(parsed_command_line.GetArgs()[0]);
#else
entry.domain_name = parsed_command_line.GetArgs()[0];
#endif
@@ -420,12 +421,11 @@ void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
dns_client->SetConfig(dns_config);
+ HostResolver::Options options;
+ options.max_concurrent_resolves = parallellism_;
+ options.max_retry_attempts = 1u;
scoped_ptr<HostResolverImpl> resolver(
- new HostResolverImpl(
- HostCache::CreateDefaultCache(),
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, parallellism_),
- HostResolverImpl::ProcTaskParams(NULL, 1),
- log_.get()));
+ new HostResolverImpl(options, log_.get()));
resolver->SetDnsClient(dns_client.Pass());
resolver_ = resolver.Pass();
diff --git a/chromium/net/tools/get_server_time/get_server_time.cc b/chromium/net/tools/get_server_time/get_server_time.cc
index 19a2010d83e..ddc08b2f5c1 100644
--- a/chromium/net/tools/get_server_time/get_server_time.cc
+++ b/chromium/net/tools/get_server_time/get_server_time.cc
@@ -48,6 +48,8 @@
#include "net/proxy/proxy_config_service_fixed.h"
#endif
+using base::UTF16ToUTF8;
+
namespace {
// base::TimeTicks::Now() is documented to have a resolution of
@@ -148,32 +150,6 @@ BuildURLRequestContext(net::NetLog* net_log) {
return context.Pass();
}
-class SingleThreadRequestContextGetter : public net::URLRequestContextGetter {
- public:
- // Since there's only a single thread, there's no need to worry
- // about when |context_| gets created.
- SingleThreadRequestContextGetter(
- net::NetLog* net_log,
- const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner)
- : context_(BuildURLRequestContext(net_log)),
- main_task_runner_(main_task_runner) {}
-
- virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
- return context_.get();
- }
-
- virtual scoped_refptr<base::SingleThreadTaskRunner>
- GetNetworkTaskRunner() const OVERRIDE {
- return main_task_runner_;
- }
-
- private:
- virtual ~SingleThreadRequestContextGetter() {}
-
- const scoped_ptr<net::URLRequestContext> context_;
- const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-};
-
// Assuming that the time |server_time| was received from a server,
// that the request for the server was started on |start_ticks|, and
// that it ended on |end_ticks|, fills |server_now| with an estimate
@@ -218,12 +194,13 @@ int main(int argc, char* argv[]) {
#endif
base::AtExitManager exit_manager;
- CommandLine::Init(argc, argv);
+ base::CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
logging::InitLogging(settings);
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
+ const base::CommandLine& parsed_command_line =
+ *base::CommandLine::ForCurrentProcess();
GURL url(parsed_command_line.GetSwitchValueASCII("url"));
if (!url.is_valid() ||
(url.scheme() != "http" && url.scheme() != "https")) {
@@ -249,15 +226,19 @@ int main(int argc, char* argv[]) {
net::NetLog net_log;
PrintingLogObserver printing_log_observer;
net_log.AddThreadSafeObserver(&printing_log_observer, net::NetLog::LOG_ALL);
- scoped_refptr<SingleThreadRequestContextGetter> context_getter(
- new SingleThreadRequestContextGetter(&net_log,
- main_loop.message_loop_proxy()));
QuitDelegate delegate;
scoped_ptr<net::URLFetcher> fetcher(
net::URLFetcher::Create(url, net::URLFetcher::HEAD, &delegate));
- fetcher->SetRequestContext(context_getter.get());
-
+ scoped_ptr<net::URLRequestContext> url_request_context(
+ BuildURLRequestContext(&net_log));
+ fetcher->SetRequestContext(
+ // Since there's only a single thread, there's no need to worry
+ // about when the URLRequestContext gets created.
+ // The URLFetcher will take a reference on the object, and hence
+ // implicitly take ownership.
+ new net::TrivialURLRequestContextGetter(url_request_context.get(),
+ main_loop.message_loop_proxy()));
const base::Time start_time = base::Time::Now();
const base::TimeTicks start_ticks = base::TimeTicks::Now();
diff --git a/chromium/net/tools/net_watcher/net_watcher.cc b/chromium/net/tools/net_watcher/net_watcher.cc
index 67eb124f8ac..421e6951271 100644
--- a/chromium/net/tools/net_watcher/net_watcher.cc
+++ b/chromium/net/tools/net_watcher/net_watcher.cc
@@ -21,7 +21,7 @@
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_service.h"
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#if defined(USE_GLIB) && !defined(OS_CHROMEOS)
#include <glib-object.h>
#endif
@@ -50,6 +50,8 @@ const char* ConnectionTypeToString(
return "CONNECTION_4G";
case net::NetworkChangeNotifier::CONNECTION_NONE:
return "CONNECTION_NONE";
+ case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ return "CONNECTION_BLUETOOTH";
default:
return "CONNECTION_UNEXPECTED";
}
@@ -131,7 +133,7 @@ int main(int argc, char* argv[]) {
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool pool;
#endif
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#if defined(USE_GLIB) && !defined(OS_CHROMEOS)
// g_type_init will be deprecated in 2.36. 2.35 is the development
// version for 2.36, hence do not call g_type_init starting 2.35.
// http://developer.gnome.org/gobject/unstable/gobject-Type-Information.html#g-type-init
@@ -140,9 +142,9 @@ int main(int argc, char* argv[]) {
// Normally handled by BrowserMainLoop::InitializeToolkit().
g_type_init();
#endif
-#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#endif // defined(USE_GLIB) && !defined(OS_CHROMEOS)
base::AtExitManager exit_manager;
- CommandLine::Init(argc, argv);
+ base::CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
logging::InitLogging(settings);
diff --git a/chromium/net/tools/quic/end_to_end_test.cc b/chromium/net/tools/quic/end_to_end_test.cc
index 2b94b0f2fd1..a21dc4082a3 100644
--- a/chromium/net/tools/quic/end_to_end_test.cc
+++ b/chromium/net/tools/quic/end_to_end_test.cc
@@ -4,30 +4,39 @@
#include <stddef.h>
#include <string>
+#include <sys/epoll.h>
#include <vector>
+#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/congestion_control/tcp_cubic_sender.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
-#include "net/quic/test_tools/quic_test_writer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
+#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_packet_writer_wrapper.h"
#include "net/tools/quic/quic_server.h"
#include "net/tools/quic/quic_socket_utils.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
-#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "net/tools/quic/test_tools/http_message.h"
#include "net/tools/quic/test_tools/packet_dropping_test_writer.h"
#include "net/tools/quic/test_tools/quic_client_peer.h"
#include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
@@ -39,10 +48,14 @@
using base::StringPiece;
using base::WaitableEvent;
+using net::EpollServer;
+using net::test::GenerateBody;
using net::test::QuicConnectionPeer;
+using net::test::QuicFlowControllerPeer;
using net::test::QuicSessionPeer;
-using net::test::QuicTestWriter;
using net::test::ReliableQuicStreamPeer;
+using net::test::ValueRestore;
+using net::test::kClientDataStreamId1;
using net::tools::test::PacketDroppingTestWriter;
using net::tools::test::QuicDispatcherPeer;
using net::tools::test::QuicServerPeer;
@@ -58,14 +71,6 @@ namespace {
const char* kFooResponseBody = "Artichoke hearts make me happy.";
const char* kBarResponseBody = "Palm hearts are pretty delicious, also.";
-void GenerateBody(string* body, int length) {
- body->clear();
- body->reserve(length);
- for (int i = 0; i < length; ++i) {
- body->append(1, static_cast<char>(32 + i % (126 - 32)));
- }
-}
-
// Run all tests with the cross products of all versions.
struct TestParams {
TestParams(const QuicVersionVector& client_supported_versions,
@@ -98,7 +103,6 @@ struct TestParams {
vector<TestParams> GetTestParams() {
vector<TestParams> params;
QuicVersionVector all_supported_versions = QuicSupportedVersions();
-
for (int use_pacing = 0; use_pacing < 2; ++use_pacing) {
// Add an entry for server and client supporting all versions.
params.push_back(TestParams(all_supported_versions,
@@ -106,19 +110,6 @@ vector<TestParams> GetTestParams() {
all_supported_versions[0],
use_pacing != 0));
- // Test client supporting 1 version and server supporting all versions.
- // Simulate an old client and exercise version downgrade in the server.
- // No protocol negotiation should occur. Skip the i = 0 case because it
- // is essentially the same as the default case.
- for (size_t i = 1; i < all_supported_versions.size(); ++i) {
- QuicVersionVector client_supported_versions;
- client_supported_versions.push_back(all_supported_versions[i]);
- params.push_back(TestParams(client_supported_versions,
- all_supported_versions,
- client_supported_versions[0],
- use_pacing != 0));
- }
-
// Test client supporting all versions and server supporting 1 version.
// Simulate an old server and exercise version downgrade in the client.
// Protocol negotiation should occur. Skip the i = 0 case because it is
@@ -126,6 +117,12 @@ vector<TestParams> GetTestParams() {
for (size_t i = 1; i < all_supported_versions.size(); ++i) {
QuicVersionVector server_supported_versions;
server_supported_versions.push_back(all_supported_versions[i]);
+ if (all_supported_versions[i] >= QUIC_VERSION_17) {
+ // Until flow control is globally rolled out and we remove
+ // QUIC_VERSION_16, the server MUST support at least one QUIC version
+ // that does not use flow control.
+ server_supported_versions.push_back(QUIC_VERSION_16);
+ }
params.push_back(TestParams(all_supported_versions,
server_supported_versions,
server_supported_versions[0],
@@ -135,6 +132,28 @@ vector<TestParams> GetTestParams() {
return params;
}
+class ServerDelegate : public PacketDroppingTestWriter::Delegate {
+ public:
+ explicit ServerDelegate(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+ virtual ~ServerDelegate() {}
+ virtual void OnCanWrite() OVERRIDE { dispatcher_->OnCanWrite(); }
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class ClientDelegate : public PacketDroppingTestWriter::Delegate {
+ public:
+ explicit ClientDelegate(QuicClient* client) : client_(client) {}
+ virtual ~ClientDelegate() {}
+ virtual void OnCanWrite() OVERRIDE {
+ EpollEvent event(EPOLLOUT, false);
+ client_->OnEvent(client_->fd(), &event);
+ }
+ private:
+ QuicClient* client_;
+};
+
class EndToEndTest : public ::testing::TestWithParam<TestParams> {
protected:
EndToEndTest()
@@ -148,14 +167,32 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
client_supported_versions_ = GetParam().client_supported_versions;
server_supported_versions_ = GetParam().server_supported_versions;
negotiated_version_ = GetParam().negotiated_version;
- FLAGS_limit_rto_increase_for_tests = true;
FLAGS_enable_quic_pacing = GetParam().use_pacing;
- LOG(INFO) << "Using Configuration: " << GetParam();
+
+ if (negotiated_version_ >= QUIC_VERSION_17) {
+ FLAGS_enable_quic_stream_flow_control_2 = true;
+ }
+ if (negotiated_version_ >= QUIC_VERSION_19) {
+ FLAGS_enable_quic_connection_flow_control_2 = true;
+ }
+ VLOG(1) << "Using Configuration: " << GetParam();
client_config_.SetDefaults();
server_config_.SetDefaults();
- server_config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs,
- 0);
+
+ // Use different flow control windows for client/server.
+ client_config_.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ client_config_.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config_.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialFlowControlWindowToSend(
+ 3 * kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialStreamFlowControlWindowToSend(
+ 3 * kInitialStreamFlowControlWindowForTest);
+ server_config_.SetInitialSessionFlowControlWindowToSend(
+ 3 * kInitialSessionFlowControlWindowForTest);
QuicInMemoryCachePeer::ResetForTests();
AddToCache("GET", "https://www.google.com/foo",
@@ -170,54 +207,99 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
QuicInMemoryCachePeer::ResetForTests();
}
- virtual QuicTestClient* CreateQuicClient(QuicTestWriter* writer) {
- QuicTestClient* client = new QuicTestClient(server_address_,
- server_hostname_,
- false, // not secure
- client_config_,
- client_supported_versions_);
+ QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) {
+ QuicTestClient* client = new QuicTestClient(
+ server_address_,
+ server_hostname_,
+ false, // not secure
+ client_config_,
+ client_supported_versions_);
client->UseWriter(writer);
client->Connect();
return client;
}
- virtual bool Initialize() {
+ void set_client_initial_flow_control_receive_window(uint32 window) {
+ CHECK(client_.get() == NULL);
+ DVLOG(1) << "Setting client initial flow control window: " << window;
+ client_config_.SetInitialFlowControlWindowToSend(window);
+ }
+
+ void set_client_initial_stream_flow_control_receive_window(uint32 window) {
+ CHECK(client_.get() == NULL);
+ DLOG(INFO) << "Setting client initial stream flow control window: "
+ << window;
+ client_config_.SetInitialStreamFlowControlWindowToSend(window);
+ }
+
+ void set_client_initial_session_flow_control_receive_window(uint32 window) {
+ CHECK(client_.get() == NULL);
+ DLOG(INFO) << "Setting client initial session flow control window: "
+ << window;
+ client_config_.SetInitialSessionFlowControlWindowToSend(window);
+ }
+
+ void set_server_initial_flow_control_receive_window(uint32 window) {
+ CHECK(server_thread_.get() == NULL);
+ DVLOG(1) << "Setting server initial flow control window: " << window;
+ server_config_.SetInitialFlowControlWindowToSend(window);
+ }
+
+ void set_server_initial_stream_flow_control_receive_window(uint32 window) {
+ CHECK(server_thread_.get() == NULL);
+ DLOG(INFO) << "Setting server initial stream flow control window: "
+ << window;
+ server_config_.SetInitialStreamFlowControlWindowToSend(window);
+ }
+
+ void set_server_initial_session_flow_control_receive_window(uint32 window) {
+ CHECK(server_thread_.get() == NULL);
+ DLOG(INFO) << "Setting server initial session flow control window: "
+ << window;
+ server_config_.SetInitialSessionFlowControlWindowToSend(window);
+ }
+
+ bool Initialize() {
// Start the server first, because CreateQuicClient() attempts
// to connect to the server.
StartServer();
client_.reset(CreateQuicClient(client_writer_));
- QuicEpollConnectionHelper* helper =
+ static EpollEvent event(EPOLLOUT, false);
+ client_writer_->Initialize(
reinterpret_cast<QuicEpollConnectionHelper*>(
QuicConnectionPeer::GetHelper(
- client_->client()->session()->connection()));
- client_writer_->SetConnectionHelper(helper);
+ client_->client()->session()->connection())),
+ new ClientDelegate(client_->client()));
return client_->client()->connected();
}
- virtual void SetUp() {
- // The ownership of these gets transferred to the QuicTestWriter and
- // QuicDispatcher when Initialize() is executed.
+ virtual void SetUp() OVERRIDE {
+ // The ownership of these gets transferred to the QuicPacketWriterWrapper
+ // and QuicDispatcher when Initialize() is executed.
client_writer_ = new PacketDroppingTestWriter();
server_writer_ = new PacketDroppingTestWriter();
}
- virtual void TearDown() {
+ virtual void TearDown() OVERRIDE {
StopServer();
}
void StartServer() {
- server_thread_.reset(new ServerThread(server_address_, server_config_,
- server_supported_versions_,
- strike_register_no_startup_period_));
- server_thread_->Start();
- server_thread_->WaitForServerStartup();
+ server_thread_.reset(
+ new ServerThread(
+ new QuicServer(server_config_, server_supported_versions_),
+ server_address_,
+ strike_register_no_startup_period_));
+ server_thread_->Initialize();
server_address_ = IPEndPoint(server_address_.address(),
server_thread_->GetPort());
QuicDispatcher* dispatcher =
QuicServerPeer::GetDispatcher(server_thread_->server());
- server_writer_->SetConnectionHelper(
- QuicDispatcherPeer::GetHelper(dispatcher));
QuicDispatcherPeer::UseWriter(dispatcher, server_writer_);
+ server_writer_->Initialize(
+ QuicDispatcherPeer::GetHelper(dispatcher),
+ new ServerDelegate(dispatcher));
+ server_thread_->Start();
server_started_ = true;
}
@@ -243,8 +325,10 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
void SetPacketLossPercentage(int32 loss) {
// TODO(rtenneti): enable when we can do random packet loss tests in
// chrome's tree.
- // client_writer_->set_fake_packet_loss_percentage(loss);
- // server_writer_->set_fake_packet_loss_percentage(loss);
+ if (loss != 0 && loss != 100)
+ return;
+ client_writer_->set_fake_packet_loss_percentage(loss);
+ server_writer_->set_fake_packet_loss_percentage(loss);
}
void SetPacketSendDelay(QuicTime::Delta delay) {
@@ -261,6 +345,36 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
// server_writer_->set_fake_reorder_percentage(reorder);
}
+ // Verifies that the client and server connections were both free of packets
+ // being discarded, based on connection stats.
+ // Calls server_thread_ Pause() and Resume(), which may only be called once
+ // per test.
+ void VerifyCleanConnection(bool had_packet_loss) {
+ QuicConnectionStats client_stats =
+ client_->client()->session()->connection()->GetStats();
+ if (!had_packet_loss) {
+ EXPECT_EQ(0u, client_stats.packets_lost);
+ }
+ EXPECT_EQ(0u, client_stats.packets_discarded);
+ EXPECT_EQ(0u, client_stats.packets_dropped);
+ EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed);
+
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(1u, dispatcher->session_map().size());
+ QuicSession* session = dispatcher->session_map().begin()->second;
+ QuicConnectionStats server_stats = session->connection()->GetStats();
+ if (!had_packet_loss) {
+ EXPECT_EQ(0u, server_stats.packets_lost);
+ }
+ EXPECT_EQ(0u, server_stats.packets_discarded);
+ // TODO(ianswett): Restore the check for packets_dropped equals 0.
+ // The expect for packets received is equal to packets processed fails
+ // due to version negotiation packets.
+ server_thread_->Resume();
+ }
+
IPEndPoint server_address_;
string server_hostname_;
scoped_ptr<ServerThread> server_thread_;
@@ -398,12 +512,14 @@ TEST_P(EndToEndTest, PostMissingBytes) {
EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
}
-TEST_P(EndToEndTest, LargePostNoPacketLoss) {
+// TODO(rtenneti): DISABLED_LargePostNoPacketLoss seems to be flaky.
+// http://crbug.com/297040.
+TEST_P(EndToEndTest, DISABLED_LargePostNoPacketLoss) {
ASSERT_TRUE(Initialize());
client_->client()->WaitForCryptoHandshakeConfirmed();
- // 1 Mb body.
+ // 1 MB body.
string body;
GenerateBody(&body, 1024 * 1024);
@@ -412,6 +528,25 @@ TEST_P(EndToEndTest, LargePostNoPacketLoss) {
request.AddBody(body, true);
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) {
+ ASSERT_TRUE(Initialize());
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000));
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // 100 KB body.
+ string body;
+ GenerateBody(&body, 100 * 1024);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+ VerifyCleanConnection(false);
}
TEST_P(EndToEndTest, LargePostWithPacketLoss) {
@@ -424,7 +559,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) {
client_->client()->WaitForCryptoHandshakeConfirmed();
SetPacketLossPercentage(30);
- // 10 Kb body.
+ // 10 KB body.
string body;
GenerateBody(&body, 1024 * 10);
@@ -433,19 +568,23 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) {
request.AddBody(body, true);
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+ VerifyCleanConnection(true);
}
-TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
+TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) {
+ // Connect with lower fake packet loss than we'd like to test. Until
+ // b/10126687 is fixed, losing handshake packets is pretty brutal.
+ SetPacketLossPercentage(5);
ASSERT_TRUE(Initialize());
+ // Wait for the server SHLO before upping the packet loss.
client_->client()->WaitForCryptoHandshakeConfirmed();
- // Both of these must be called when the writer is not actively used.
- SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
- SetReorderPercentage(30);
+ SetPacketLossPercentage(10);
+ client_writer_->set_fake_blocked_socket_percentage(10);
- // 1 Mb body.
+ // 10 KB body.
string body;
- GenerateBody(&body, 1024 * 1024);
+ GenerateBody(&body, 1024 * 10);
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
@@ -454,20 +593,17 @@ TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
}
-TEST_P(EndToEndTest, LargePostWithPacketLossAndBlocketSocket) {
- // Connect with lower fake packet loss than we'd like to test. Until
- // b/10126687 is fixed, losing handshake packets is pretty brutal.
- SetPacketLossPercentage(5);
+TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
ASSERT_TRUE(Initialize());
- // Wait for the server SHLO before upping the packet loss.
client_->client()->WaitForCryptoHandshakeConfirmed();
- SetPacketLossPercentage(30);
- client_writer_->set_fake_blocked_socket_percentage(10);
+ // Both of these must be called when the writer is not actively used.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
- // 10 Kb body.
+ // 1 MB body.
string body;
- GenerateBody(&body, 1024 * 10);
+ GenerateBody(&body, 1024 * 1024);
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
@@ -500,6 +636,14 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) {
// The 0-RTT handshake should succeed.
client_->Connect();
+ if (client_supported_versions_[0] >= QUIC_VERSION_17 &&
+ negotiated_version_ < QUIC_VERSION_17) {
+ // If the version negotiation has resulted in a downgrade, then the client
+ // must wait for the handshake to complete before sending any data.
+ // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a
+ // DFATAL when they are serialized after the downgrade.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ }
client_->WaitForResponseForMs(-1);
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
@@ -513,16 +657,35 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) {
StartServer();
client_->Connect();
+ if (client_supported_versions_[0] >= QUIC_VERSION_17 &&
+ negotiated_version_ < QUIC_VERSION_17) {
+ // If the version negotiation has resulted in a downgrade, then the client
+ // must wait for the handshake to complete before sending any data.
+ // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a
+ // DFATAL when they are serialized after the downgrade.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ }
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
EXPECT_EQ(2, client_->client()->session()->GetNumSentClientHellos());
+ VerifyCleanConnection(false);
}
-// TODO(ianswett): Enable once b/9295090 is fixed.
-TEST_P(EndToEndTest, DISABLED_LargePostFEC) {
- SetPacketLossPercentage(30);
+TEST_P(EndToEndTest, LargePostFEC) {
+ // Connect without packet loss to avoid issues with losing handshake packets,
+ // and then up the packet loss rate (b/10126687).
ASSERT_TRUE(Initialize());
- client_->options()->max_packets_per_fec_group = 6;
+
+ // Wait for the server SHLO before upping the packet loss.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ SetPacketLossPercentage(30);
+
+ // Enable FEC protection.
+ QuicPacketCreator* creator = QuicConnectionPeer::GetPacketCreator(
+ client_->client()->session()->connection());
+ creator->set_max_packets_per_fec_group(3);
+ // Set FecPolicy to always protect data on all streams.
+ client_->SetFecPolicy(FEC_PROTECT_ALWAYS);
string body;
GenerateBody(&body, 10240);
@@ -530,21 +693,23 @@ TEST_P(EndToEndTest, DISABLED_LargePostFEC) {
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
request.AddBody(body, true);
-
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+ VerifyCleanConnection(true);
}
-TEST_P(EndToEndTest, LargePostLargeBuffer) {
+// TODO(shess): This is flaky on ChromiumOS bots.
+// http://crbug.com/374871
+TEST_P(EndToEndTest, DISABLED_LargePostSmallBandwidthLargeBuffer) {
ASSERT_TRUE(Initialize());
SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1));
- // 1Mbit per second with a 128k buffer from server to client. Wireless
+ // 256KB per second with a 256KB buffer from server to client. Wireless
// clients commonly have larger buffers, but our max CWND is 200.
server_writer_->set_max_bandwidth_and_buffer_size(
- QuicBandwidth::FromBytesPerSecond(256 * 1024), 128 * 1024);
+ QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024);
client_->client()->WaitForCryptoHandshakeConfirmed();
- // 1 Mb body.
+ // 1 MB body.
string body;
GenerateBody(&body, 1024 * 1024);
@@ -553,6 +718,52 @@ TEST_P(EndToEndTest, LargePostLargeBuffer) {
request.AddBody(body, true);
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+ // This connection will not drop packets, because the buffer size is larger
+ // than the default receive window.
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, DoNotSetResumeWriteAlarmIfConnectionFlowControlBlocked) {
+ // Regression test for b/14677858.
+ // Test that the resume write alarm is not set in QuicConnection::OnCanWrite
+ // if currently connection level flow control blocked. If set, this results in
+ // an infinite loop in the EpollServer, as the alarm fires and is immediately
+ // rescheduled.
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_ < QUIC_VERSION_19) {
+ return;
+ }
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // Ensure both stream and connection level are flow control blocked by setting
+ // the send window offset to 0.
+ const uint64 kFlowControlWindow =
+ server_config_.GetInitialFlowControlWindowToSend();
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ QuicSession* session = client_->client()->session();
+ QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0);
+ QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0);
+ EXPECT_TRUE(stream->flow_controller()->IsBlocked());
+ EXPECT_TRUE(session->flow_controller()->IsBlocked());
+
+ // Make sure that the stream has data pending so that it will be marked as
+ // write blocked when it receives a stream level WINDOW_UPDATE.
+ stream->SendBody("hello", false);
+
+ // The stream now attempts to write, fails because it is still connection
+ // level flow control blocked, and is added to the write blocked list.
+ QuicWindowUpdateFrame window_update(stream->id(), 2 * kFlowControlWindow);
+ stream->OnWindowUpdateFrame(window_update);
+
+ // Prior to fixing b/14677858 this call would result in an infinite loop in
+ // Chromium. As a proxy for detecting this, we now check whether the
+ // resume_writes_alarm is set after OnCanWrite. It should not be, as the
+ // connection is still flow control blocked.
+ session->connection()->OnCanWrite();
+
+ QuicAlarm* resume_writes_alarm =
+ QuicConnectionPeer::GetResumeWritesAlarm(session->connection());
+ EXPECT_FALSE(resume_writes_alarm->IsSet());
}
TEST_P(EndToEndTest, InvalidStream) {
@@ -570,7 +781,7 @@ TEST_P(EndToEndTest, InvalidStream) {
QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2);
client_->SendCustomSynchronousRequest(request);
-// EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ // EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
EXPECT_EQ(QUIC_PACKET_FOR_NONEXISTENT_STREAM, client_->connection_error());
}
@@ -598,23 +809,7 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) {
ReliableQuicStreamPeer::SetWriteSideClosed(
false, client_->GetOrCreateStream());
-#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
-#if !defined(DCHECK_ALWAYS_ON)
- EXPECT_DEBUG_DEATH({
- client_->SendData("eep", true);
- client_->WaitForResponse();
- EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error());
- },
- "Check failed: !fin_buffered_");
-#else
- EXPECT_DEATH({
- client_->SendData("eep", true);
- client_->WaitForResponse();
- EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error());
- },
- "Check failed: !fin_buffered_");
-#endif
-#endif
+ EXPECT_DFATAL(client_->SendData("eep", true), "Fin already buffered");
}
TEST_P(EndToEndTest, Timeout) {
@@ -629,6 +824,30 @@ TEST_P(EndToEndTest, Timeout) {
}
}
+TEST_P(EndToEndTest, NegotiateMaxOpenStreams) {
+ // Negotiate 1 max open stream.
+ client_config_.set_max_streams_per_connection(1, 1);
+ ASSERT_TRUE(Initialize());
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // Make the client misbehave after negotiation.
+ QuicSessionPeer::SetMaxOpenStreams(client_->client()->session(), 10);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddHeader("content-length", "3");
+ request.set_has_complete_message(false);
+
+ // Open two simultaneous streams.
+ client_->SendMessage(request);
+ client_->SendMessage(request);
+ client_->WaitForResponse();
+
+ EXPECT_FALSE(client_->connected());
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_TOO_MANY_OPEN_STREAMS, client_->connection_error());
+}
+
TEST_P(EndToEndTest, LimitMaxOpenStreams) {
// Server limits the number of max streams to 2.
server_config_.set_max_streams_per_connection(2, 2);
@@ -644,13 +863,10 @@ TEST_P(EndToEndTest, LimitMaxOpenStreams) {
// TODO(rtenneti): DISABLED_LimitCongestionWindowAndRTT seems to be flaky.
// http://crbug.com/321870.
TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) {
- server_config_.set_server_initial_congestion_window(kMaxInitialWindow,
- kDefaultInitialWindow);
- // Client tries to negotiate twice the server's max and negotiation settles
- // on the max.
- client_config_.set_server_initial_congestion_window(2 * kMaxInitialWindow,
- kDefaultInitialWindow);
- client_config_.set_initial_round_trip_time_us(1, 1);
+ // Client tries to request twice the server's max initial window, and the
+ // server limits it to the max.
+ client_config_.SetInitialCongestionWindowToSend(2 * kMaxInitialWindow);
+ client_config_.SetInitialRoundTripTimeUsToSend(1);
ASSERT_TRUE(Initialize());
client_->client()->WaitForCryptoHandshakeConfirmed();
@@ -662,17 +878,11 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) {
QuicServerPeer::GetDispatcher(server_thread_->server());
ASSERT_EQ(1u, dispatcher->session_map().size());
QuicSession* session = dispatcher->session_map().begin()->second;
- QuicConfig* client_negotiated_config = client_->client()->session()->config();
- QuicConfig* server_negotiated_config = session->config();
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->session()->connection()->sent_packet_manager();
const QuicSentPacketManager& server_sent_packet_manager =
session->connection()->sent_packet_manager();
- EXPECT_EQ(kMaxInitialWindow,
- client_negotiated_config->server_initial_congestion_window());
- EXPECT_EQ(kMaxInitialWindow,
- server_negotiated_config->server_initial_congestion_window());
// The client shouldn't set it's initial window based on the negotiated value.
EXPECT_EQ(kDefaultInitialWindow * kDefaultTCPMSS,
client_sent_packet_manager.GetCongestionWindow());
@@ -684,13 +894,14 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) {
EXPECT_EQ(FLAGS_enable_quic_pacing,
client_sent_packet_manager.using_pacing());
- EXPECT_EQ(1u, client_negotiated_config->initial_round_trip_time_us());
- EXPECT_EQ(1u, server_negotiated_config->initial_round_trip_time_us());
+ EXPECT_EQ(100000u,
+ client_sent_packet_manager.GetRttStats()->initial_rtt_us());
+ EXPECT_EQ(1u, server_sent_packet_manager.GetRttStats()->initial_rtt_us());
// Now use the negotiated limits with packet loss.
SetPacketLossPercentage(30);
- // 10 Kb body.
+ // 10 KB body.
string body;
GenerateBody(&body, 1024 * 10);
@@ -703,11 +914,42 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) {
EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
}
-TEST_P(EndToEndTest, InitialRTT) {
- // Client tries to negotiate twice the server's max and negotiation settles
- // on the max.
- client_config_.set_initial_round_trip_time_us(2 * kMaxInitialRoundTripTimeUs,
- 0);
+TEST_P(EndToEndTest, MaxInitialRTT) {
+ // Client tries to suggest twice the server's max initial rtt and the server
+ // uses the max.
+ client_config_.SetInitialRoundTripTimeUsToSend(
+ 2 * kMaxInitialRoundTripTimeUs);
+
+ ASSERT_TRUE(Initialize());
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(1u, dispatcher->session_map().size());
+ QuicSession* session = dispatcher->session_map().begin()->second;
+ const QuicSentPacketManager& client_sent_packet_manager =
+ client_->client()->session()->connection()->sent_packet_manager();
+ const QuicSentPacketManager& server_sent_packet_manager =
+ session->connection()->sent_packet_manager();
+
+ // Now that acks have been exchanged, the RTT estimate has decreased on the
+ // server and is not infinite on the client.
+ EXPECT_FALSE(
+ client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite());
+ EXPECT_EQ(static_cast<int64>(kMaxInitialRoundTripTimeUs),
+ server_sent_packet_manager.GetRttStats()->initial_rtt_us());
+ EXPECT_GE(
+ static_cast<int64>(kMaxInitialRoundTripTimeUs),
+ server_sent_packet_manager.GetRttStats()->SmoothedRtt().ToMicroseconds());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, MinInitialRTT) {
+ // Client tries to suggest 0 and the server uses the default.
+ client_config_.SetInitialRoundTripTimeUsToSend(0);
ASSERT_TRUE(Initialize());
client_->client()->WaitForCryptoHandshakeConfirmed();
@@ -719,22 +961,22 @@ TEST_P(EndToEndTest, InitialRTT) {
QuicServerPeer::GetDispatcher(server_thread_->server());
ASSERT_EQ(1u, dispatcher->session_map().size());
QuicSession* session = dispatcher->session_map().begin()->second;
- QuicConfig* client_negotiated_config = client_->client()->session()->config();
- QuicConfig* server_negotiated_config = session->config();
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->session()->connection()->sent_packet_manager();
const QuicSentPacketManager& server_sent_packet_manager =
session->connection()->sent_packet_manager();
- EXPECT_EQ(kMaxInitialRoundTripTimeUs,
- client_negotiated_config->initial_round_trip_time_us());
- EXPECT_EQ(kMaxInitialRoundTripTimeUs,
- server_negotiated_config->initial_round_trip_time_us());
// Now that acks have been exchanged, the RTT estimate has decreased on the
// server and is not infinite on the client.
- EXPECT_FALSE(client_sent_packet_manager.SmoothedRtt().IsInfinite());
- EXPECT_GE(static_cast<int64>(kMaxInitialRoundTripTimeUs),
- server_sent_packet_manager.SmoothedRtt().ToMicroseconds());
+ EXPECT_FALSE(
+ client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite());
+ // Expect the default rtt of 100ms.
+ EXPECT_EQ(static_cast<int64>(100 * base::Time::kMicrosecondsPerMillisecond),
+ server_sent_packet_manager.GetRttStats()->initial_rtt_us());
+ // Ensure the bandwidth is valid.
+ client_sent_packet_manager.BandwidthEstimate();
+ server_sent_packet_manager.BandwidthEstimate();
+ server_thread_->Resume();
}
TEST_P(EndToEndTest, ResetConnection) {
@@ -770,7 +1012,35 @@ TEST_P(EndToEndTest, MaxStreamsUberTest) {
}
}
-class WrongAddressWriter : public QuicTestWriter {
+TEST_P(EndToEndTest, StreamCancelErrorTest) {
+ ASSERT_TRUE(Initialize());
+ string small_body;
+ GenerateBody(&small_body, 256);
+
+ AddToCache("GET", "/small_response", "HTTP/1.1", "200", "OK", small_body);
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ QuicSession* session = client_->client()->session();
+ // Lose the request.
+ SetPacketLossPercentage(100);
+ EXPECT_LT(0, client_->SendRequest("/small_response"));
+ client_->client()->WaitForEvents();
+ // Transmit the cancel, and ensure the connection is torn down properly.
+ SetPacketLossPercentage(0);
+ QuicStreamId stream_id = kClientDataStreamId1;
+ session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+
+ // WaitForEvents waits 50ms and returns true if there are outstanding
+ // requests.
+ while (client_->client()->WaitForEvents() == true) {
+ }
+ // It should be completely fine to RST a stream before any data has been
+ // received for that stream.
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+class WrongAddressWriter : public QuicPacketWriterWrapper {
public:
WrongAddressWriter() {
IPAddressNumber ip;
@@ -779,12 +1049,13 @@ class WrongAddressWriter : public QuicTestWriter {
}
virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
+ const char* buffer,
+ size_t buf_len,
const IPAddressNumber& real_self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
- return writer()->WritePacket(buffer, buf_len, self_address_.address(),
- peer_address, blocked_writer);
+ const IPEndPoint& peer_address) OVERRIDE {
+ // Use wrong address!
+ return QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_address_.address(), peer_address);
}
virtual bool IsWriteBlockedDataBuffered() const OVERRIDE {
@@ -794,7 +1065,10 @@ class WrongAddressWriter : public QuicTestWriter {
IPEndPoint self_address_;
};
-TEST_P(EndToEndTest, ConnectionMigration) {
+TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) {
+ // Tests that the client's IP can not change during an established QUIC
+ // connection. If it changes, the connection is closed by the server as we do
+ // not yet support IP migration.
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
@@ -802,8 +1076,7 @@ TEST_P(EndToEndTest, ConnectionMigration) {
scoped_ptr<WrongAddressWriter> writer(new WrongAddressWriter());
- writer->set_writer(new QuicDefaultPacketWriter(
- QuicClientPeer::GetFd(client_->client())));
+ writer->set_writer(new QuicDefaultPacketWriter(client_->client()->fd()));
QuicConnectionPeer::SetWriter(client_->client()->session()->connection(),
writer.get());
@@ -813,6 +1086,157 @@ TEST_P(EndToEndTest, ConnectionMigration) {
EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error());
}
+TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) {
+ // Tests that the client's port can change during an established QUIC
+ // connection, and that doing so does not result in the connection being
+ // closed by the server.
+ FLAGS_quic_allow_port_migration = true;
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ // Store the client address which was used to send the first request.
+ IPEndPoint old_address = client_->client()->client_address();
+
+ // Stop listening on the old FD.
+ EpollServer* eps = client_->epoll_server();
+ int old_fd = client_->client()->fd();
+ eps->UnregisterFD(old_fd);
+ // Create a new socket before closing the old one, which will result in a new
+ // ephemeral port.
+ QuicClientPeer::CreateUDPSocket(client_->client());
+ close(old_fd);
+
+ // The packet writer needs to be updated to use the new FD.
+ client_->client()->CreateQuicPacketWriter();
+
+ // Change the internal state of the client and connection to use the new port,
+ // this is done because in a real NAT rebinding the client wouldn't see any
+ // port change, and so expects no change to incoming port.
+ // This is kind of ugly, but needed as we are simply swapping out the client
+ // FD rather than any more complex NAT rebinding simulation.
+ int new_port = client_->client()->client_address().port();
+ QuicClientPeer::SetClientPort(client_->client(), new_port);
+ QuicConnectionPeer::SetSelfAddress(
+ client_->client()->session()->connection(),
+ IPEndPoint(
+ client_->client()->session()->connection()->self_address().address(),
+ new_port));
+
+ // Register the new FD for epoll events.
+ int new_fd = client_->client()->fd();
+ eps->RegisterFD(new_fd, client_->client(), EPOLLIN | EPOLLOUT | EPOLLET);
+
+ // Send a second request, using the new FD.
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ // Verify that the client's ephemeral port is different.
+ IPEndPoint new_address = client_->client()->client_address();
+ EXPECT_EQ(old_address.address(), new_address.address());
+ EXPECT_NE(old_address.port(), new_address.port());
+}
+
+
+TEST_P(EndToEndTest, DifferentFlowControlWindowsQ019) {
+ // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
+ // Client and server can set different initial flow control receive windows.
+ // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
+ // in the crypto handshake.
+
+ const uint32 kClientIFCW = 123456;
+ set_client_initial_flow_control_receive_window(kClientIFCW);
+
+ const uint32 kServerIFCW = 654321;
+ set_server_initial_flow_control_receive_window(kServerIFCW);
+
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_ > QUIC_VERSION_19) {
+ return;
+ }
+
+ // Values are exchanged during crypto handshake, so wait for that to finish.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Client should have the right value for server's receive window.
+ EXPECT_EQ(kServerIFCW, client_->client()
+ ->session()
+ ->config()
+ ->ReceivedInitialFlowControlWindowBytes());
+
+ // Server should have the right value for client's receive window.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ QuicSession* session = dispatcher->session_map().begin()->second;
+ EXPECT_EQ(kClientIFCW,
+ session->config()->ReceivedInitialFlowControlWindowBytes());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, DifferentFlowControlWindowsQ020) {
+ // TODO(rjshade): Rename to DifferentFlowControlWindows when removing
+ // QUIC_VERSION_19.
+ // Client and server can set different initial flow control receive windows.
+ // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
+ // in the crypto handshake.
+ const uint32 kClientStreamIFCW = 123456;
+ const uint32 kClientSessionIFCW = 234567;
+ set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
+ set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
+
+ const uint32 kServerStreamIFCW = 654321;
+ const uint32 kServerSessionIFCW = 765432;
+ set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
+ set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
+
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_ <= QUIC_VERSION_19) {
+ return;
+ }
+
+ // Values are exchanged during crypto handshake, so wait for that to finish.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Open a data stream to make sure the stream level flow control is updated.
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ stream->SendBody("hello", false);
+
+ // Client should have the right values for server's receive window.
+ EXPECT_EQ(kServerStreamIFCW,
+ client_->client()
+ ->session()
+ ->config()
+ ->ReceivedInitialStreamFlowControlWindowBytes());
+ EXPECT_EQ(kServerSessionIFCW,
+ client_->client()
+ ->session()
+ ->config()
+ ->ReceivedInitialSessionFlowControlWindowBytes());
+ EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
+ stream->flow_controller()));
+ EXPECT_EQ(kServerSessionIFCW,
+ QuicFlowControllerPeer::SendWindowOffset(
+ client_->client()->session()->flow_controller()));
+
+ // Server should have the right values for client's receive window.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ QuicSession* session = dispatcher->session_map().begin()->second;
+ EXPECT_EQ(kClientStreamIFCW,
+ session->config()->ReceivedInitialStreamFlowControlWindowBytes());
+ EXPECT_EQ(kClientSessionIFCW,
+ session->config()->ReceivedInitialSessionFlowControlWindowBytes());
+ EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset(
+ session->flow_controller()));
+ server_thread_->Resume();
+}
+
} // namespace
} // namespace test
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_client.cc b/chromium/net/tools/quic/quic_client.cc
index dcf9612a38f..19e98013af2 100644
--- a/chromium/net/tools/quic/quic_client.cc
+++ b/chromium/net/tools/quic/quic_client.cc
@@ -12,11 +12,14 @@
#include <unistd.h>
#include "base/logging.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_data_reader.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_socket_utils.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
@@ -31,12 +34,14 @@ namespace tools {
const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
QuicClient::QuicClient(IPEndPoint server_address,
- const string& server_hostname,
+ const QuicServerId& server_id,
const QuicVersionVector& supported_versions,
- bool print_response)
+ bool print_response,
+ EpollServer* epoll_server)
: server_address_(server_address),
- server_hostname_(server_hostname),
+ server_id_(server_id),
local_port_(0),
+ epoll_server_(epoll_server),
fd_(-1),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
@@ -48,20 +53,23 @@ QuicClient::QuicClient(IPEndPoint server_address,
}
QuicClient::QuicClient(IPEndPoint server_address,
- const string& server_hostname,
+ const QuicServerId& server_id,
+ const QuicVersionVector& supported_versions,
+ bool print_response,
const QuicConfig& config,
- const QuicVersionVector& supported_versions)
+ EpollServer* epoll_server)
: server_address_(server_address),
- server_hostname_(server_hostname),
+ server_id_(server_id),
config_(config),
local_port_(0),
+ epoll_server_(epoll_server),
fd_(-1),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
packets_dropped_(0),
overflow_supported_(false),
supported_versions_(supported_versions),
- print_response_(false) {
+ print_response_(print_response) {
}
QuicClient::~QuicClient() {
@@ -69,14 +77,27 @@ QuicClient::~QuicClient() {
session()->connection()->SendConnectionClosePacket(
QUIC_PEER_GOING_AWAY, "");
}
+ if (fd_ > 0) {
+ epoll_server_->UnregisterFD(fd_);
+ }
}
bool QuicClient::Initialize() {
DCHECK(!initialized_);
- epoll_server_.set_timeout_in_us(50 * 1000);
+ epoll_server_->set_timeout_in_us(50 * 1000);
crypto_config_.SetDefaults();
+ if (!CreateUDPSocket()) {
+ return false;
+ }
+
+ epoll_server_->RegisterFD(fd_, this, kEpollFlags);
+ initialized_ = true;
+ return true;
+}
+
+bool QuicClient::CreateUDPSocket() {
int address_family = server_address_.GetSockAddrFamily();
fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
if (fd_ < 0) {
@@ -93,6 +114,16 @@ bool QuicClient::Initialize() {
overflow_supported_ = true;
}
+ if (!QuicSocketUtils::SetReceiveBufferSize(fd_,
+ TcpReceiver::kReceiveWindowTCP)) {
+ return false;
+ }
+
+ if (!QuicSocketUtils::SetSendBufferSize(fd_,
+ TcpReceiver::kReceiveWindowTCP)) {
+ return false;
+ }
+
int get_local_ip = 1;
if (address_family == AF_INET) {
rc = setsockopt(fd_, IPPROTO_IP, IP_PKTINFO,
@@ -137,8 +168,6 @@ bool QuicClient::Initialize() {
LOG(ERROR) << "Unable to get self address. Error: " << strerror(errno);
}
- epoll_server_.RegisterFD(fd_, this, kEpollFlags);
- initialized_ = true;
return true;
}
@@ -153,7 +182,8 @@ bool QuicClient::Connect() {
}
bool QuicClient::StartConnect() {
- DCHECK(!connected() && initialized_);
+ DCHECK(initialized_);
+ DCHECK(!connected());
QuicPacketWriter* writer = CreateQuicPacketWriter();
if (writer_.get() != writer) {
@@ -161,9 +191,9 @@ bool QuicClient::StartConnect() {
}
session_.reset(new QuicClientSession(
- server_hostname_,
+ server_id_,
config_,
- new QuicConnection(GenerateGuid(), server_address_, helper_.get(),
+ new QuicConnection(GenerateConnectionId(), server_address_, helper_.get(),
writer_.get(), false, supported_versions_),
&crypto_config_));
return session_->CryptoConnect();
@@ -180,23 +210,24 @@ void QuicClient::Disconnect() {
if (connected()) {
session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
}
- epoll_server_.UnregisterFD(fd_);
+ epoll_server_->UnregisterFD(fd_);
close(fd_);
fd_ = -1;
initialized_ = false;
}
void QuicClient::SendRequestsAndWaitForResponse(
- const CommandLine::StringVector& args) {
+ const base::CommandLine::StringVector& args) {
for (size_t i = 0; i < args.size(); ++i) {
BalsaHeaders headers;
headers.SetRequestFirstlineFromStringPieces("GET", args[i], "HTTP/1.1");
QuicSpdyClientStream* stream = CreateReliableClientStream();
+ DCHECK(stream != NULL);
stream->SendRequest(headers, "", true);
stream->set_visitor(this);
}
- while (WaitForEvents()) { }
+ while (WaitForEvents()) {}
}
QuicSpdyClientStream* QuicClient::CreateReliableClientStream() {
@@ -210,23 +241,23 @@ QuicSpdyClientStream* QuicClient::CreateReliableClientStream() {
void QuicClient::WaitForStreamToClose(QuicStreamId id) {
DCHECK(connected());
- while (!session_->IsClosedStream(id)) {
- epoll_server_.WaitForEventsAndExecuteCallbacks();
+ while (connected() && !session_->IsClosedStream(id)) {
+ epoll_server_->WaitForEventsAndExecuteCallbacks();
}
}
void QuicClient::WaitForCryptoHandshakeConfirmed() {
DCHECK(connected());
- while (!session_->IsCryptoHandshakeConfirmed()) {
- epoll_server_.WaitForEventsAndExecuteCallbacks();
+ while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
+ epoll_server_->WaitForEventsAndExecuteCallbacks();
}
}
bool QuicClient::WaitForEvents() {
DCHECK(connected());
- epoll_server_.WaitForEventsAndExecuteCallbacks();
+ epoll_server_->WaitForEventsAndExecuteCallbacks();
return session_->num_active_requests() != 0;
}
@@ -238,20 +269,26 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) {
}
}
if (connected() && (event->in_events & EPOLLOUT)) {
+ writer_->SetWritable();
session_->connection()->OnCanWrite();
}
if (event->in_events & EPOLLERR) {
- DLOG(INFO) << "Epollerr";
+ DVLOG(1) << "Epollerr";
}
}
void QuicClient::OnClose(QuicDataStream* stream) {
+ QuicSpdyClientStream* client_stream =
+ static_cast<QuicSpdyClientStream*>(stream);
+ if (response_listener_.get() != NULL) {
+ response_listener_->OnCompleteResponse(
+ stream->id(), client_stream->headers(), client_stream->data());
+ }
+
if (!print_response_) {
return;
}
- QuicSpdyClientStream* client_stream =
- static_cast<QuicSpdyClientStream*>(stream);
const BalsaHeaders& headers = client_stream->headers();
printf("%s\n", headers.first_line().as_string().c_str());
for (BalsaHeaders::const_header_lines_iterator i =
@@ -263,30 +300,32 @@ void QuicClient::OnClose(QuicDataStream* stream) {
printf("%s\n", client_stream->data().c_str());
}
-QuicPacketCreator::Options* QuicClient::options() {
- if (session() == NULL) {
- return NULL;
- }
- return session_->options();
-}
-
bool QuicClient::connected() const {
return session_.get() && session_->connection() &&
session_->connection()->connected();
}
-QuicGuid QuicClient::GenerateGuid() {
+QuicConnectionId QuicClient::GenerateConnectionId() {
return QuicRandom::GetInstance()->RandUint64();
}
QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() {
- return new QuicEpollConnectionHelper(&epoll_server_);
+ return new QuicEpollConnectionHelper(epoll_server_);
}
QuicPacketWriter* QuicClient::CreateQuicPacketWriter() {
return new QuicDefaultPacketWriter(fd_);
}
+int QuicClient::ReadPacket(char* buffer,
+ int buffer_len,
+ IPEndPoint* server_address,
+ IPAddressNumber* client_ip) {
+ return QuicSocketUtils::ReadPacket(
+ fd_, buffer, buffer_len, overflow_supported_ ? &packets_dropped_ : NULL,
+ client_ip, server_address);
+}
+
bool QuicClient::ReadAndProcessPacket() {
// Allocate some extra space so we can send an error if the server goes over
// the limit.
@@ -295,27 +334,13 @@ bool QuicClient::ReadAndProcessPacket() {
IPEndPoint server_address;
IPAddressNumber client_ip;
- int bytes_read = QuicSocketUtils::ReadPacket(
- fd_, buf, arraysize(buf), overflow_supported_ ? &packets_dropped_ : NULL,
- &client_ip, &server_address);
+ int bytes_read = ReadPacket(buf, arraysize(buf), &server_address, &client_ip);
if (bytes_read < 0) {
return false;
}
QuicEncryptedPacket packet(buf, bytes_read, false);
- QuicGuid our_guid = session_->connection()->guid();
- QuicGuid packet_guid;
-
- if (!QuicFramer::ReadGuidFromPacket(packet, &packet_guid)) {
- DLOG(INFO) << "Could not read GUID from packet";
- return true;
- }
- if (packet_guid != our_guid) {
- DLOG(INFO) << "Ignoring packet from unexpected GUID: "
- << packet_guid << " instead of " << our_guid;
- return true;
- }
IPEndPoint client_address(client_ip, client_address_.port());
session_->connection()->ProcessUdpPacket(
diff --git a/chromium/net/tools/quic/quic_client.h b/chromium/net/tools/quic/quic_client.h
index 02f45e6c56a..13aff636ada 100644
--- a/chromium/net/tools/quic/quic_client.h
+++ b/chromium/net/tools/quic/quic_client.h
@@ -10,6 +10,7 @@
#include <string>
+#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/ip_endpoint.h"
@@ -24,6 +25,7 @@
namespace net {
class ProofVerifier;
+class QuicServerId;
namespace tools {
@@ -36,14 +38,28 @@ class QuicClientPeer;
class QuicClient : public EpollCallbackInterface,
public QuicDataStream::Visitor {
public:
+ class ResponseListener {
+ public:
+ ResponseListener() {}
+ virtual ~ResponseListener() {}
+ virtual void OnCompleteResponse(QuicStreamId id,
+ const BalsaHeaders& response_headers,
+ const string& response_body) = 0;
+ };
+
+ // Create a quic client, which will have events managed by an externally owned
+ // EpollServer.
QuicClient(IPEndPoint server_address,
- const string& server_hostname,
+ const QuicServerId& server_id,
const QuicVersionVector& supported_versions,
- bool print_response);
+ bool print_response,
+ EpollServer* epoll_server);
QuicClient(IPEndPoint server_address,
- const std::string& server_hostname,
+ const QuicServerId& server_id,
+ const QuicVersionVector& supported_versions,
+ bool print_response,
const QuicConfig& config,
- const QuicVersionVector& supported_versions);
+ EpollServer* epoll_server);
virtual ~QuicClient();
@@ -69,11 +85,12 @@ class QuicClient : public EpollCallbackInterface,
// Disconnects from the QUIC server.
void Disconnect();
- // Sends a request simple GET for each URL in arg, and then waits for
+ // Sends a request simple GET for each URL in |args|, and then waits for
// each to complete.
- void SendRequestsAndWaitForResponse(const CommandLine::StringVector& args);
+ void SendRequestsAndWaitForResponse(const
+ base::CommandLine::StringVector& args);
- // Returns a newly created CreateReliableClientStream, owned by the
+ // Returns a newly created QuicSpdyClientStream, owned by the
// QuicClient.
QuicSpdyClientStream* CreateReliableClientStream();
@@ -88,8 +105,9 @@ class QuicClient : public EpollCallbackInterface,
bool WaitForEvents();
// From EpollCallbackInterface
- virtual void OnRegistration(
- EpollServer* eps, int fd, int event_mask) OVERRIDE {}
+ virtual void OnRegistration(EpollServer* eps,
+ int fd,
+ int event_mask) OVERRIDE {}
virtual void OnModification(int fd, int event_mask) OVERRIDE {}
virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
// |fd_| can be unregistered without the client being disconnected. This
@@ -101,8 +119,6 @@ class QuicClient : public EpollCallbackInterface,
// QuicDataStream::Visitor
virtual void OnClose(QuicDataStream* stream) OVERRIDE;
- QuicPacketCreator::Options* options();
-
QuicClientSession* session() { return session_.get(); }
bool connected() const;
@@ -119,13 +135,19 @@ class QuicClient : public EpollCallbackInterface,
const IPEndPoint& client_address() const { return client_address_; }
- EpollServer* epoll_server() { return &epoll_server_; }
+ EpollServer* epoll_server() { return epoll_server_; }
int fd() { return fd_; }
+ const QuicServerId& server_id() const { return server_id_; }
+
// This should only be set before the initial Connect()
- void set_server_hostname(const string& hostname) {
- server_hostname_ = hostname;
+ void set_server_id(const QuicServerId& server_id) {
+ server_id_ = server_id;
+ }
+
+ void SetUserAgentID(const string& user_agent_id) {
+ crypto_config_.set_user_agent_id(user_agent_id);
}
// SetProofVerifier sets the ProofVerifier that will be used to verify the
@@ -135,29 +157,48 @@ class QuicClient : public EpollCallbackInterface,
crypto_config_.SetProofVerifier(verifier);
}
- // SetChannelIDSigner sets a ChannelIDSigner that will be called when the
- // server supports channel IDs to sign a message proving possession of the
- // given ChannelID. This object takes ownership of |signer|.
- void SetChannelIDSigner(ChannelIDSigner* signer) {
- crypto_config_.SetChannelIDSigner(signer);
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID. This object takes ownership of
+ // |source|.
+ void SetChannelIDSource(ChannelIDSource* source) {
+ crypto_config_.SetChannelIDSource(source);
+ }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
+ // Takes ownership of the listener.
+ void set_response_listener(ResponseListener* listener) {
+ response_listener_.reset(listener);
}
protected:
- virtual QuicGuid GenerateGuid();
+ virtual QuicConnectionId GenerateConnectionId();
virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper();
virtual QuicPacketWriter* CreateQuicPacketWriter();
+ virtual int ReadPacket(char* buffer,
+ int buffer_len,
+ IPEndPoint* server_address,
+ IPAddressNumber* client_ip);
+
private:
friend class net::tools::test::QuicClientPeer;
+ // Used during initialization: creates the UDP socket FD, sets socket options,
+ // and binds the socket to our address.
+ bool CreateUDPSocket();
+
// Read a UDP packet and hand it to the framer.
bool ReadAndProcessPacket();
// Address of the server.
const IPEndPoint server_address_;
- // Hostname of the server. This may be a DNS name or an IP address literal.
- std::string server_hostname_;
+ // |server_id_| is a tuple (hostname, port, is_https) of the server.
+ QuicServerId server_id_;
// config_ and crypto_config_ contain configuration and cached state about
// servers.
@@ -175,13 +216,16 @@ class QuicClient : public EpollCallbackInterface,
// Session which manages streams.
scoped_ptr<QuicClientSession> session_;
// Listens for events on the client socket.
- EpollServer epoll_server_;
+ EpollServer* epoll_server_;
// UDP socket.
int fd_;
// Helper to be used by created connections.
scoped_ptr<QuicEpollConnectionHelper> helper_;
+ // Listens for full responses.
+ scoped_ptr<ResponseListener> response_listener_;
+
// Writer used to actually send packets to the wire.
scoped_ptr<QuicPacketWriter> writer_;
@@ -191,7 +235,7 @@ class QuicClient : public EpollCallbackInterface,
// If overflow_supported_ is true, this will be the number of packets dropped
// during the lifetime of the server. This may overflow if enough packets
// are dropped.
- int packets_dropped_;
+ uint32 packets_dropped_;
// True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
// because the socket would otherwise overflow.
diff --git a/chromium/net/tools/quic/quic_client_bin.cc b/chromium/net/tools/quic/quic_client_bin.cc
index 2f8cfac1736..f743a7dc127 100644
--- a/chromium/net/tools/quic/quic_client_bin.cc
+++ b/chromium/net/tools/quic/quic_client_bin.cc
@@ -4,6 +4,11 @@
// A binary wrapper for QuicClient. Connects to --hostname via --address
// on --port and requests URLs specified on the command line.
+// Pass --secure to check the certificates using proof verifier.
+// Pass --initial_stream_flow_control_window to specify the size of the initial
+// stream flow control receive window to advertise to server.
+// Pass --initial_session_flow_control_window to specify the size of the initial
+// session flow control receive window to advertise to server.
//
// For example:
// quic_client --address=127.0.0.1 --port=6122 --hostname=www.google.com
@@ -16,16 +21,34 @@
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/ip_endpoint.h"
+#include "net/base/privacy_mode.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
+#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_client.h"
+// The port the quic client will connect to.
int32 FLAGS_port = 6121;
std::string FLAGS_address = "127.0.0.1";
+// The hostname the quic client will connect to.
std::string FLAGS_hostname = "localhost";
+// Size of the initial stream flow control receive window to advertise to
+// server.
+int32 FLAGS_initial_stream_flow_control_window = 100 * net::kMaxPacketSize;
+// Size of the initial session flow control receive window to advertise to
+// server.
+int32 FLAGS_initial_session_flow_control_window = 200 * net::kMaxPacketSize;
+// Check the certificates using proof verifier.
+bool FLAGS_secure = false;
int main(int argc, char *argv[]) {
- CommandLine::Init(argc, argv);
- CommandLine* line = CommandLine::ForCurrentProcess();
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* line = base::CommandLine::ForCurrentProcess();
+
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ CHECK(logging::InitLogging(settings));
+
if (line->HasSwitch("h") || line->HasSwitch("help")) {
const char* help_str =
"Usage: quic_client [options]\n"
@@ -34,7 +57,8 @@ int main(int argc, char *argv[]) {
"-h, --help show this help message and exit\n"
"--port=<port> specify the port to connect to\n"
"--address=<address> specify the IP address to connect to\n"
- "--host=<host> specify the SNI hostname to use\n";
+ "--host=<host> specify the SNI hostname to use\n"
+ "--secure check certificates\n";
std::cout << help_str;
exit(0);
}
@@ -50,18 +74,35 @@ int main(int argc, char *argv[]) {
if (line->HasSwitch("hostname")) {
FLAGS_hostname = line->GetSwitchValueASCII("hostname");
}
- LOG(INFO) << "server port: " << FLAGS_port
- << " address: " << FLAGS_address
- << " hostname: " << FLAGS_hostname;
+ if (line->HasSwitch("secure")) {
+ FLAGS_secure = true;
+ }
+ VLOG(1) << "server port: " << FLAGS_port
+ << " address: " << FLAGS_address
+ << " hostname: " << FLAGS_hostname
+ << " secure: " << FLAGS_secure;
base::AtExitManager exit_manager;
net::IPAddressNumber addr;
CHECK(net::ParseIPLiteralToNumber(FLAGS_address, &addr));
+
+ net::QuicConfig config;
+ config.SetDefaults();
+ config.SetInitialFlowControlWindowToSend(
+ FLAGS_initial_session_flow_control_window);
+ config.SetInitialStreamFlowControlWindowToSend(
+ FLAGS_initial_stream_flow_control_window);
+ config.SetInitialSessionFlowControlWindowToSend(
+ FLAGS_initial_session_flow_control_window);
+
// TODO(rjshade): Set version on command line.
+ net::EpollServer epoll_server;
net::tools::QuicClient client(
- net::IPEndPoint(addr, FLAGS_port), FLAGS_hostname,
- net::QuicSupportedVersions(), true);
+ net::IPEndPoint(addr, FLAGS_port),
+ net::QuicServerId(FLAGS_hostname, FLAGS_port, FLAGS_secure,
+ net::PRIVACY_MODE_DISABLED),
+ net::QuicSupportedVersions(), true, config, &epoll_server);
client.Initialize();
diff --git a/chromium/net/tools/quic/quic_client_session.cc b/chromium/net/tools/quic/quic_client_session.cc
index 5bcf6f201f4..aca5418ac37 100644
--- a/chromium/net/tools/quic/quic_client_session.cc
+++ b/chromium/net/tools/quic/quic_client_session.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
using std::string;
@@ -14,30 +15,38 @@ namespace net {
namespace tools {
QuicClientSession::QuicClientSession(
- const string& server_hostname,
+ const QuicServerId& server_id,
const QuicConfig& config,
QuicConnection* connection,
QuicCryptoClientConfig* crypto_config)
- : QuicSession(connection, config),
- crypto_stream_(server_hostname, this, crypto_config) {
+ : QuicClientSessionBase(connection, config),
+ crypto_stream_(server_id, this, NULL, crypto_config) {
}
QuicClientSession::~QuicClientSession() {
}
+void QuicClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& /*cached*/) {
+}
+
+void QuicClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& /*verify_details*/) {
+}
+
QuicSpdyClientStream* QuicClientSession::CreateOutgoingDataStream() {
if (!crypto_stream_.encryption_established()) {
- DLOG(INFO) << "Encryption not active so no outgoing stream created.";
+ DVLOG(1) << "Encryption not active so no outgoing stream created.";
return NULL;
}
if (GetNumOpenStreams() >= get_max_open_streams()) {
- DLOG(INFO) << "Failed to create a new outgoing stream. "
- << "Already " << GetNumOpenStreams() << " open.";
+ DVLOG(1) << "Failed to create a new outgoing stream. "
+ << "Already " << GetNumOpenStreams() << " open.";
return NULL;
}
if (goaway_received()) {
- DLOG(INFO) << "Failed to create a new outgoing stream. "
- << "Already received goaway.";
+ DVLOG(1) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
return NULL;
}
QuicSpdyClientStream* stream
diff --git a/chromium/net/tools/quic/quic_client_session.h b/chromium/net/tools/quic/quic_client_session.h
index dcee15e4275..3aad445fcd2 100644
--- a/chromium/net/tools/quic/quic_client_session.h
+++ b/chromium/net/tools/quic/quic_client_session.h
@@ -9,26 +9,34 @@
#include <string>
+#include "base/basictypes.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_session.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
namespace net {
class QuicConnection;
+class QuicServerId;
class ReliableQuicStream;
namespace tools {
-class QuicClientSession : public QuicSession {
+class QuicClientSession : public QuicClientSessionBase {
public:
- QuicClientSession(const std::string& server_hostname,
+ QuicClientSession(const QuicServerId& server_id,
const QuicConfig& config,
QuicConnection* connection,
QuicCryptoClientConfig* crypto_config);
virtual ~QuicClientSession();
+ // QuicClientSessionBase methods:
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) OVERRIDE;
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) OVERRIDE;
+
// QuicSession methods:
virtual QuicSpdyClientStream* CreateOutgoingDataStream() OVERRIDE;
virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
diff --git a/chromium/net/tools/quic/quic_client_session_test.cc b/chromium/net/tools/quic/quic_client_session_test.cc
index c893c20b5e3..7b50f42ab39 100644
--- a/chromium/net/tools/quic/quic_client_session_test.cc
+++ b/chromium/net/tools/quic/quic_client_session_test.cc
@@ -16,6 +16,7 @@
using net::test::CryptoTestUtils;
using net::test::DefaultQuicConfig;
using net::test::PacketSavingConnection;
+using net::test::SupportedVersions;
using testing::_;
namespace net {
@@ -24,14 +25,20 @@ namespace test {
namespace {
const char kServerHostname[] = "www.example.com";
+const uint16 kPort = 80;
-class ToolsQuicClientSessionTest : public ::testing::Test {
+class ToolsQuicClientSessionTest
+ : public ::testing::TestWithParam<QuicVersion> {
protected:
ToolsQuicClientSessionTest()
- : connection_(new PacketSavingConnection(false)) {
+ : connection_(new PacketSavingConnection(false,
+ SupportedVersions(GetParam()))) {
crypto_config_.SetDefaults();
- session_.reset(new QuicClientSession(kServerHostname, DefaultQuicConfig(),
- connection_, &crypto_config_));
+ session_.reset(new QuicClientSession(
+ QuicServerId(kServerHostname, kPort, false, PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(),
+ connection_,
+ &crypto_config_));
session_->config()->SetDefaults();
}
@@ -46,11 +53,14 @@ class ToolsQuicClientSessionTest : public ::testing::Test {
QuicCryptoClientConfig crypto_config_;
};
-TEST_F(ToolsQuicClientSessionTest, CryptoConnect) {
+INSTANTIATE_TEST_CASE_P(Tests, ToolsQuicClientSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(ToolsQuicClientSessionTest, CryptoConnect) {
CompleteCryptoHandshake();
}
-TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) {
+TEST_P(ToolsQuicClientSessionTest, MaxNumStreams) {
session_->config()->set_max_streams_per_connection(1, 1);
// FLAGS_max_streams_per_connection = 1;
// Initialize crypto before the client session will create a stream.
@@ -67,7 +77,7 @@ TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) {
EXPECT_TRUE(stream);
}
-TEST_F(ToolsQuicClientSessionTest, GoAwayReceived) {
+TEST_P(ToolsQuicClientSessionTest, GoAwayReceived) {
CompleteCryptoHandshake();
// After receiving a GoAway, I should no longer be able to create outgoing
diff --git a/chromium/net/tools/quic/quic_default_packet_writer.cc b/chromium/net/tools/quic/quic_default_packet_writer.cc
index 9d3e30831c1..84f118aee00 100644
--- a/chromium/net/tools/quic/quic_default_packet_writer.cc
+++ b/chromium/net/tools/quic/quic_default_packet_writer.cc
@@ -9,22 +9,37 @@
namespace net {
namespace tools {
-QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd) : fd_(fd) {}
+QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd)
+ : fd_(fd),
+ write_blocked_(false) {}
QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
WriteResult QuicDefaultPacketWriter::WritePacket(
- const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) {
- return QuicSocketUtils::WritePacket(fd_, buffer, buf_len,
- self_address, peer_address);
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) {
+ DCHECK(!IsWriteBlocked());
+ WriteResult result = QuicSocketUtils::WritePacket(
+ fd_, buffer, buf_len, self_address, peer_address);
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ write_blocked_ = true;
+ }
+ return result;
}
bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const {
return false;
}
+bool QuicDefaultPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicDefaultPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
} // namespace tools
} // namespace net
diff --git a/chromium/net/tools/quic/quic_default_packet_writer.h b/chromium/net/tools/quic/quic_default_packet_writer.h
index 20f5fb0db61..7b5a36bdce1 100644
--- a/chromium/net/tools/quic/quic_default_packet_writer.h
+++ b/chromium/net/tools/quic/quic_default_packet_writer.h
@@ -11,7 +11,6 @@
namespace net {
-class QuicBlockedWriterInterface;
struct WriteResult;
namespace tools {
@@ -23,15 +22,27 @@ class QuicDefaultPacketWriter : public QuicPacketWriter {
virtual ~QuicDefaultPacketWriter();
// QuicPacketWriter
- virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE;
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ void set_fd(int fd) { fd_ = fd; }
+
+ protected:
+ void set_write_blocked(bool is_blocked) {
+ write_blocked_ = is_blocked;
+ }
+ int fd() { return fd_; }
private:
int fd_;
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_dispatcher.cc b/chromium/net/tools/quic/quic_dispatcher.cc
index 297234d9755..b873f98618a 100644
--- a/chromium/net/tools/quic/quic_dispatcher.cc
+++ b/chromium/net/tools/quic/quic_dispatcher.cc
@@ -6,17 +6,23 @@
#include <errno.h>
+#include "base/debug/stack_trace.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_default_packet_writer.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_socket_utils.h"
+#include "net/tools/quic/quic_time_wait_list_manager.h"
namespace net {
+
namespace tools {
+using base::StringPiece;
using std::make_pair;
class DeleteSessionsAlarm : public EpollAlarm {
@@ -35,22 +41,140 @@ class DeleteSessionsAlarm : public EpollAlarm {
QuicDispatcher* dispatcher_;
};
+class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicFramerVisitor(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher),
+ connection_id_(0) {}
+
+ // QuicFramerVisitorInterface implementation
+ virtual void OnPacket() OVERRIDE {}
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ connection_id_ = header.connection_id;
+ return dispatcher_->OnUnauthenticatedPublicHeader(header);
+ }
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ dispatcher_->OnUnauthenticatedHeader(header);
+ return false;
+ }
+ virtual void OnError(QuicFramer* framer) OVERRIDE {
+ DVLOG(1) << QuicUtils::ErrorToString(framer->error());
+ }
+
+ virtual bool OnProtocolVersionMismatch(
+ QuicVersion /*received_version*/) OVERRIDE {
+ if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait(
+ connection_id_)) {
+ // Keep processing after protocol mismatch - this will be dealt with by
+ // the TimeWaitListManager.
+ return true;
+ } else {
+ DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_
+ << ") not in time wait list.";
+ return false;
+ }
+ }
+
+ // The following methods should never get called because we always return
+ // false from OnUnauthenticatedHeader(). As a result, we never process the
+ // payload of the packet.
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnRevivedPacket() OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/)
+ OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnPacketComplete() OVERRIDE {
+ DCHECK(false);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+
+ // Latched in OnUnauthenticatedPublicHeader for use later.
+ QuicConnectionId connection_id_;
+};
+
QuicDispatcher::QuicDispatcher(const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
const QuicVersionVector& supported_versions,
- int fd,
EpollServer* epoll_server)
: config_(config),
crypto_config_(crypto_config),
- time_wait_list_manager_(
- new QuicTimeWaitListManager(this, epoll_server, supported_versions)),
delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
epoll_server_(epoll_server),
- fd_(fd),
- write_blocked_(false),
helper_(new QuicEpollConnectionHelper(epoll_server_)),
- writer_(new QuicDefaultPacketWriter(fd)),
- supported_versions_(supported_versions) {
+ supported_versions_(supported_versions),
+ supported_versions_no_flow_control_(supported_versions),
+ supported_versions_no_connection_flow_control_(supported_versions),
+ current_packet_(NULL),
+ framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
+ framer_visitor_(new QuicFramerVisitor(this)) {
+ framer_.set_visitor(framer_visitor_.get());
}
QuicDispatcher::~QuicDispatcher() {
@@ -58,89 +182,121 @@ QuicDispatcher::~QuicDispatcher() {
STLDeleteElements(&closed_session_list_);
}
-void QuicDispatcher::set_fd(int fd) {
- fd_ = fd;
- writer_.reset(new QuicDefaultPacketWriter(fd));
-}
+void QuicDispatcher::Initialize(int fd) {
+ DCHECK(writer_ == NULL);
+ writer_.reset(CreateWriter(fd));
+ time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
-WriteResult QuicDispatcher::WritePacket(const char* buffer, size_t buf_len,
- const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* writer) {
- if (write_blocked_) {
- write_blocked_list_.insert(make_pair(writer, true));
- return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
+ // Remove all versions > QUIC_VERSION_16 from the
+ // supported_versions_no_flow_control_ vector.
+ QuicVersionVector::iterator it =
+ find(supported_versions_no_flow_control_.begin(),
+ supported_versions_no_flow_control_.end(), QUIC_VERSION_17);
+ if (it != supported_versions_no_flow_control_.end()) {
+ supported_versions_no_flow_control_.erase(
+ supported_versions_no_flow_control_.begin(), it + 1);
}
+ CHECK(!supported_versions_no_flow_control_.empty());
- WriteResult result =
- writer_->WritePacket(buffer, buf_len, self_address, peer_address, writer);
- if (result.status == WRITE_STATUS_BLOCKED) {
- write_blocked_list_.insert(make_pair(writer, true));
- write_blocked_ = true;
+ // Remove all versions > QUIC_VERSION_18 from the
+ // supported_versions_no_connection_flow_control_ vector.
+ QuicVersionVector::iterator connection_it = find(
+ supported_versions_no_connection_flow_control_.begin(),
+ supported_versions_no_connection_flow_control_.end(), QUIC_VERSION_19);
+ if (connection_it != supported_versions_no_connection_flow_control_.end()) {
+ supported_versions_no_connection_flow_control_.erase(
+ supported_versions_no_connection_flow_control_.begin(),
+ connection_it + 1);
}
- return result;
-}
-
-bool QuicDispatcher::IsWriteBlockedDataBuffered() const {
- return writer_->IsWriteBlockedDataBuffered();
+ CHECK(!supported_versions_no_connection_flow_control_.empty());
}
void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
- bool has_version_flag,
const QuicEncryptedPacket& packet) {
+ current_server_address_ = server_address;
+ current_client_address_ = client_address;
+ current_packet_ = &packet;
+ // ProcessPacket will cause the packet to be dispatched in
+ // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
+ // in OnAuthenticatedHeader.
+ framer_.ProcessPacket(packet);
+ // TODO(rjshade): Return a status describing if/why a packet was dropped,
+ // and log somehow. Maybe expose as a varz.
+}
+
+bool QuicDispatcher::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
QuicSession* session = NULL;
- SessionMap::iterator it = session_map_.find(guid);
+ QuicConnectionId connection_id = header.connection_id;
+ SessionMap::iterator it = session_map_.find(connection_id);
if (it == session_map_.end()) {
- if (time_wait_list_manager_->IsGuidInTimeWait(guid)) {
- time_wait_list_manager_->ProcessPacket(server_address,
- client_address,
- guid,
- packet);
- return;
+ if (header.reset_flag) {
+ return false;
+ }
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ return HandlePacketForTimeWait(header);
}
// Ensure the packet has a version negotiation bit set before creating a new
// session for it. All initial packets for a new connection are required to
// have the flag set. Otherwise it may be a stray packet.
- if (has_version_flag) {
- session = CreateQuicSession(guid, server_address, client_address);
+ if (header.version_flag) {
+ session = CreateQuicSession(connection_id, current_server_address_,
+ current_client_address_);
}
if (session == NULL) {
- DLOG(INFO) << "Failed to create session for " << guid;
- // Add this guid fo the time-wait state, to safely reject future packets.
- // We don't know the version here, so assume latest.
- // TODO(ianswett): Produce a no-version version negotiation packet.
- time_wait_list_manager_->AddGuidToTimeWait(guid,
- supported_versions_.front(),
- NULL);
- time_wait_list_manager_->ProcessPacket(server_address,
- client_address,
- guid,
- packet);
- return;
+ DVLOG(1) << "Failed to create session for " << connection_id;
+ // Add this connection_id fo the time-wait state, to safely reject future
+ // packets.
+
+ if (header.version_flag &&
+ !framer_.IsSupportedVersion(header.versions.front())) {
+ // TODO(ianswett): Produce a no-version version negotiation packet.
+ return false;
+ }
+
+ // Use the version in the packet if possible, otherwise assume the latest.
+ QuicVersion version = header.version_flag ? header.versions.front() :
+ supported_versions_.front();
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id, version, NULL);
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ return HandlePacketForTimeWait(header);
}
- DLOG(INFO) << "Created new session for " << guid;
- session_map_.insert(make_pair(guid, session));
+ DVLOG(1) << "Created new session for " << connection_id;
+ session_map_.insert(make_pair(connection_id, session));
} else {
session = it->second;
}
session->connection()->ProcessUdpPacket(
- server_address, client_address, packet);
+ current_server_address_, current_client_address_, *current_packet_);
+
+ // Do not parse the packet further. The session will process it completely.
+ return false;
+}
+
+void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
+ header.public_header.connection_id));
+ time_wait_list_manager_->ProcessPacket(current_server_address_,
+ current_client_address_,
+ header.public_header.connection_id,
+ header.packet_sequence_number,
+ *current_packet_);
}
void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
QuicConnection* connection = it->second->connection();
QuicEncryptedPacket* connection_close_packet =
- connection->ReleaseConnectionClosePacket();
+ connection->ReleaseConnectionClosePacket();
write_blocked_list_.erase(connection);
- time_wait_list_manager_->AddGuidToTimeWait(it->first,
- connection->version(),
- connection_close_packet);
+ time_wait_list_manager_->AddConnectionIdToTimeWait(it->first,
+ connection->version(),
+ connection_close_packet);
session_map_.erase(it);
}
@@ -148,37 +304,29 @@ void QuicDispatcher::DeleteSessions() {
STLDeleteElements(&closed_session_list_);
}
-void QuicDispatcher::UseWriter(QuicPacketWriter* writer) {
- writer_.reset(writer);
-}
-
-bool QuicDispatcher::OnCanWrite() {
+void QuicDispatcher::OnCanWrite() {
// We got an EPOLLOUT: the socket should not be blocked.
- write_blocked_ = false;
+ writer_->SetWritable();
// Give each writer one attempt to write.
int num_writers = write_blocked_list_.size();
for (int i = 0; i < num_writers; ++i) {
if (write_blocked_list_.empty()) {
- break;
+ return;
}
- QuicBlockedWriterInterface* writer = write_blocked_list_.begin()->first;
+ QuicBlockedWriterInterface* blocked_writer =
+ write_blocked_list_.begin()->first;
write_blocked_list_.erase(write_blocked_list_.begin());
- bool can_write_more = writer->OnCanWrite();
- if (write_blocked_) {
- // We were unable to write. Wait for the next EPOLLOUT.
- // In this case, the session would have been added to the blocked list
- // up in WritePacket.
- return false;
- }
- // The socket is not blocked but the writer has ceded work. Add it to the
- // end of the list.
- if (can_write_more) {
- write_blocked_list_.insert(make_pair(writer, true));
+ blocked_writer->OnCanWrite();
+ if (writer_->IsWriteBlocked()) {
+ // We were unable to write. Wait for the next EPOLLOUT. The writer is
+ // responsible for adding itself to the blocked list via OnWriteBlocked().
+ return;
}
}
+}
- // We're not write blocked. Return true if there's more work to do.
+bool QuicDispatcher::HasPendingWrites() const {
return !write_blocked_list_.empty();
}
@@ -192,15 +340,19 @@ void QuicDispatcher::Shutdown() {
DeleteSessions();
}
-void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) {
- SessionMap::iterator it = session_map_.find(guid);
+void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) {
+ SessionMap::iterator it = session_map_.find(connection_id);
if (it == session_map_.end()) {
- LOG(DFATAL) << "GUID " << guid << " does not exist in the session map. "
+ LOG(DFATAL) << "ConnectionId " << connection_id
+ << " does not exist in the session map. "
<< "Error: " << QuicUtils::ErrorToString(error);
+ LOG(DFATAL) << base::debug::StackTrace().ToString();
return;
}
- DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection (" << guid
+ DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection ("
+ << connection_id
<< ") due to error: "
<< QuicUtils::ErrorToString(error);
@@ -212,16 +364,75 @@ void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) {
CleanUpSession(it);
}
+void QuicDispatcher::OnWriteBlocked(QuicBlockedWriterInterface* writer) {
+ DCHECK(writer_->IsWriteBlocked());
+ write_blocked_list_.insert(make_pair(writer, true));
+}
+
+QuicPacketWriter* QuicDispatcher::CreateWriter(int fd) {
+ return new QuicDefaultPacketWriter(fd);
+}
+
QuicSession* QuicDispatcher::CreateQuicSession(
- QuicGuid guid,
+ QuicConnectionId connection_id,
const IPEndPoint& server_address,
const IPEndPoint& client_address) {
QuicServerSession* session = new QuicServerSession(
- config_, new QuicConnection(guid, client_address, helper_.get(), this,
- true, supported_versions_), this);
+ config_,
+ CreateQuicConnection(connection_id, server_address, client_address),
+ this);
session->InitializeSession(crypto_config_);
return session;
}
+QuicConnection* QuicDispatcher::CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ FLAGS_enable_quic_connection_flow_control_2) {
+ DLOG(INFO) << "Creating QuicDispatcher with all versions.";
+ return new QuicConnection(connection_id, client_address, helper_.get(),
+ writer_.get(), true, supported_versions_);
+ }
+
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ !FLAGS_enable_quic_connection_flow_control_2) {
+ DLOG(INFO) << "Connection flow control disabled, creating QuicDispatcher "
+ << "WITHOUT version 19 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_.get(),
+ writer_.get(), true,
+ supported_versions_no_connection_flow_control_);
+ }
+
+ DLOG(INFO) << "Flow control disabled, creating QuicDispatcher WITHOUT "
+ << "version 17 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_.get(),
+ writer_.get(), true,
+ supported_versions_no_flow_control_);
+}
+
+QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
+ return new QuicTimeWaitListManager(
+ writer_.get(), this, epoll_server(), supported_versions());
+}
+
+bool QuicDispatcher::HandlePacketForTimeWait(
+ const QuicPacketPublicHeader& header) {
+ if (header.reset_flag) {
+ // Public reset packets do not have sequence numbers, so ignore the packet.
+ return false;
+ }
+
+ // Switch the framer to the correct version, so that the sequence number can
+ // be parsed correctly.
+ framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId(
+ header.connection_id));
+
+ // Continue parsing the packet to extract the sequence number. Then
+ // send it to the time wait manager in OnUnathenticatedHeader.
+ return true;
+}
+
} // namespace tools
} // namespace net
diff --git a/chromium/net/tools/quic/quic_dispatcher.h b/chromium/net/tools/quic/quic_dispatcher.h
index 58a4c0c1423..d60ba69883d 100644
--- a/chromium/net/tools/quic/quic_dispatcher.h
+++ b/chromium/net/tools/quic/quic_dispatcher.h
@@ -10,14 +10,13 @@
#include <list>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/ip_endpoint.h"
#include "net/base/linked_hash_map.h"
#include "net/quic/quic_blocked_writer_interface.h"
-#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_protocol.h"
-#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_server_session.h"
#include "net/tools/quic/quic_time_wait_list_manager.h"
@@ -42,6 +41,8 @@ class QuicSession;
namespace tools {
+class QuicPacketWriterWrapper;
+
namespace test {
class QuicDispatcherPeer;
} // namespace test
@@ -49,7 +50,7 @@ class QuicDispatcherPeer;
class DeleteSessionsAlarm;
class QuicEpollConnectionHelper;
-class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner {
+class QuicDispatcher : public QuicServerSessionVisitor {
public:
// Ideally we'd have a linked_hash_set: the boolean is unused.
typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList;
@@ -60,87 +61,134 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner {
QuicDispatcher(const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
const QuicVersionVector& supported_versions,
- int fd,
EpollServer* epoll_server);
+
virtual ~QuicDispatcher();
- // QuicPacketWriter
- virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
- const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* writer) OVERRIDE;
- virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual void Initialize(int fd);
// Process the incoming packet by creating a new session, passing it to
// an existing session, or passing it to the TimeWaitListManager.
virtual void ProcessPacket(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
- bool has_version_flag,
const QuicEncryptedPacket& packet);
- // Called when the underyling connection becomes writable to allow
- // queued writes to happen.
- //
- // Returns true if more writes are possible, false otherwise.
- virtual bool OnCanWrite();
+ // Called when the socket becomes writable to allow queued writes to happen.
+ virtual void OnCanWrite();
+
+ // Returns true if there's anything in the blocked writer list.
+ virtual bool HasPendingWrites() const;
// Sends ConnectionClose frames to all connected clients.
void Shutdown();
+ // QuicServerSessionVisitor interface implementation:
// Ensure that the closed connection is cleaned up asynchronously.
- virtual void OnConnectionClosed(QuicGuid guid, QuicErrorCode error) OVERRIDE;
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) OVERRIDE;
- // Sets the fd and creates a default packet writer with that fd.
- void set_fd(int fd);
+ // Queues the blocked writer for later resumption.
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) OVERRIDE;
- typedef base::hash_map<QuicGuid, QuicSession*> SessionMap;
-
- virtual QuicSession* CreateQuicSession(
- QuicGuid guid,
- const IPEndPoint& server_address,
- const IPEndPoint& client_address);
+ typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
// Deletes all sessions on the closed session list and clears the list.
void DeleteSessions();
const SessionMap& session_map() const { return session_map_; }
- // Uses the specified |writer| instead of QuicSocketUtils and takes ownership
- // of writer.
- void UseWriter(QuicPacketWriter* writer);
-
WriteBlockedList* write_blocked_list() { return &write_blocked_list_; }
protected:
- const QuicConfig& config_;
- const QuicCryptoServerConfig& crypto_config_;
+ // Instantiates a new low-level packet writer. Caller takes ownership of the
+ // returned object.
+ virtual QuicPacketWriter* CreateWriter(int fd);
+
+ virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ virtual QuicConnection* CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ // Called by |framer_visitor_| when the public header has been parsed.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header);
+
+ // Create and return the time wait list manager for this dispatcher, which
+ // will be owned by the dispatcher as time_wait_list_manager_
+ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+
+ // Replaces the packet writer with |writer|. Takes ownership of |writer|.
+ void set_writer(QuicPacketWriter* writer) {
+ writer_.reset(writer);
+ }
QuicTimeWaitListManager* time_wait_list_manager() {
return time_wait_list_manager_.get();
}
- QuicEpollConnectionHelper* helper() { return helper_.get(); }
EpollServer* epoll_server() { return epoll_server_; }
const QuicVersionVector& supported_versions() const {
return supported_versions_;
}
+ const QuicVersionVector& supported_versions_no_flow_control() const {
+ return supported_versions_no_flow_control_;
+ }
+
+ const QuicVersionVector& supported_versions_no_connection_flow_control()
+ const {
+ return supported_versions_no_connection_flow_control_;
+ }
+
+ const IPEndPoint& current_server_address() {
+ return current_server_address_;
+ }
+ const IPEndPoint& current_client_address() {
+ return current_client_address_;
+ }
+ const QuicEncryptedPacket& current_packet() {
+ return *current_packet_;
+ }
+
+ const QuicConfig& config() const { return config_; }
+
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+
+ QuicFramer* framer() { return &framer_; }
+
+ QuicEpollConnectionHelper* helper() { return helper_.get(); }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+
private:
+ class QuicFramerVisitor;
friend class net::tools::test::QuicDispatcherPeer;
+ // Called by |framer_visitor_| when the private header has been parsed
+ // of a data packet that is destined for the time wait manager.
+ void OnUnauthenticatedHeader(const QuicPacketHeader& header);
+
// Removes the session from the session map and write blocked list, and
- // adds the GUID to the time-wait list.
+ // adds the ConnectionId to the time-wait list.
void CleanUpSession(SessionMap::iterator it);
+ bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header);
+
+ const QuicConfig& config_;
+
+ const QuicCryptoServerConfig& crypto_config_;
+
// The list of connections waiting to write.
WriteBlockedList write_blocked_list_;
SessionMap session_map_;
- // Entity that manages guids in time wait state.
+ // Entity that manages connection_ids in time wait state.
scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
// An alarm which deletes closed sessions.
@@ -151,13 +199,6 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner {
EpollServer* epoll_server_; // Owned by the server.
- // The connection for client-server communication
- int fd_;
-
- // True if the session is write blocked due to the socket returning EAGAIN.
- // False if we have gotten a call to OnCanWrite after the last failed write.
- bool write_blocked_;
-
// The helper used for all connections.
scoped_ptr<QuicEpollConnectionHelper> helper_;
@@ -170,6 +211,28 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner {
// skipped as necessary).
const QuicVersionVector supported_versions_;
+ // Versions which do not support flow control (introduced in QUIC_VERSION_17).
+ // This is used to construct new QuicConnections when flow control is disabled
+ // via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_stream_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_flow_control_;
+ // Versions which do not support *connection* flow control (introduced in
+ // QUIC_VERSION_19).
+ // This is used to construct new QuicConnections when connection flow control
+ // is disabled via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_connection_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_connection_flow_control_;
+
+ // Information about the packet currently being handled.
+ IPEndPoint current_client_address_;
+ IPEndPoint current_server_address_;
+ const QuicEncryptedPacket* current_packet_;
+
+ QuicFramer framer_;
+ scoped_ptr<QuicFramerVisitor> framer_visitor_;
+
DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
};
diff --git a/chromium/net/tools/quic/quic_dispatcher_test.cc b/chromium/net/tools/quic/quic_dispatcher_test.cc
index 74cab69e6a7..41c3c27946a 100644
--- a/chromium/net/tools/quic/quic_dispatcher_test.cc
+++ b/chromium/net/tools/quic/quic_dispatcher_test.cc
@@ -11,8 +11,11 @@
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/quic_packet_writer_wrapper.h"
#include "net/tools/quic/quic_time_wait_list_manager.h"
#include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
#include "net/tools/quic/test_tools/quic_test_utils.h"
@@ -21,15 +24,16 @@
using base::StringPiece;
using net::EpollServer;
+using net::test::ConstructEncryptedPacket;
using net::test::MockSession;
+using net::test::ValueRestore;
using net::tools::test::MockConnection;
using std::make_pair;
-using testing::_;
using testing::DoAll;
-using testing::Invoke;
using testing::InSequence;
-using testing::Return;
+using testing::Invoke;
using testing::WithoutArgs;
+using testing::_;
namespace net {
namespace tools {
@@ -41,14 +45,20 @@ class TestDispatcher : public QuicDispatcher {
explicit TestDispatcher(const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
EpollServer* eps)
- : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), 1, eps) {
+ : QuicDispatcher(config,
+ crypto_config,
+ QuicSupportedVersions(),
+ eps) {
}
MOCK_METHOD3(CreateQuicSession, QuicSession*(
- QuicGuid guid,
+ QuicConnectionId connection_id,
const IPEndPoint& server_address,
const IPEndPoint& client_address));
using QuicDispatcher::write_blocked_list;
+
+ using QuicDispatcher::current_server_address;
+ using QuicDispatcher::current_client_address;
};
// A Connection class which unregisters the session from the dispatcher
@@ -57,30 +67,31 @@ class TestDispatcher : public QuicDispatcher {
// involve a lot more mocking.
class MockServerConnection : public MockConnection {
public:
- MockServerConnection(QuicGuid guid,
+ MockServerConnection(QuicConnectionId connection_id,
QuicDispatcher* dispatcher)
- : MockConnection(guid, true),
- dispatcher_(dispatcher) {
- }
+ : MockConnection(connection_id, true),
+ dispatcher_(dispatcher) {}
+
void UnregisterOnConnectionClosed() {
- LOG(ERROR) << "Unregistering " << guid();
- dispatcher_->OnConnectionClosed(guid(), QUIC_NO_ERROR);
+ LOG(ERROR) << "Unregistering " << connection_id();
+ dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR);
}
private:
QuicDispatcher* dispatcher_;
};
QuicSession* CreateSession(QuicDispatcher* dispatcher,
- QuicGuid guid,
- const IPEndPoint& addr,
+ QuicConnectionId connection_id,
+ const IPEndPoint& client_address,
MockSession** session) {
- MockServerConnection* connection = new MockServerConnection(guid, dispatcher);
+ MockServerConnection* connection =
+ new MockServerConnection(connection_id, dispatcher);
*session = new MockSession(connection);
ON_CALL(*connection, SendConnectionClose(_)).WillByDefault(
WithoutArgs(Invoke(
connection, &MockServerConnection::UnregisterOnConnectionClosed)));
EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()),
- ProcessUdpPacket(_, addr, _));
+ ProcessUdpPacket(_, client_address, _));
return *session;
}
@@ -93,6 +104,7 @@ class QuicDispatcherTest : public ::testing::Test {
dispatcher_(config_, crypto_config_, &eps_),
session1_(NULL),
session2_(NULL) {
+ dispatcher_.Initialize(1);
}
virtual ~QuicDispatcherTest() {}
@@ -105,22 +117,25 @@ class QuicDispatcherTest : public ::testing::Test {
return reinterpret_cast<MockConnection*>(session2_->connection());
}
- void ProcessPacket(IPEndPoint addr,
- QuicGuid guid,
+ void ProcessPacket(IPEndPoint client_address,
+ QuicConnectionId connection_id,
bool has_version_flag,
const string& data) {
- dispatcher_.ProcessPacket(
- IPEndPoint(), addr, guid, has_version_flag,
- QuicEncryptedPacket(data.data(), data.length()));
+ scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ connection_id, has_version_flag, false, 1, data));
+ data_ = string(packet->data(), packet->length());
+ dispatcher_.ProcessPacket(server_address_, client_address, *packet);
}
void ValidatePacket(const QuicEncryptedPacket& packet) {
- EXPECT_TRUE(packet.AsStringPiece().find(data_) != StringPiece::npos);
+ EXPECT_EQ(data_.length(), packet.AsStringPiece().length());
+ EXPECT_EQ(data_, packet.AsStringPiece());
}
EpollServer eps_;
QuicConfig config_;
QuicCryptoServerConfig crypto_config_;
+ IPEndPoint server_address_;
TestDispatcher dispatcher_;
MockSession* session1_;
MockSession* session2_;
@@ -128,34 +143,39 @@ class QuicDispatcherTest : public ::testing::Test {
};
TEST_F(QuicDispatcherTest, ProcessPackets) {
- IPEndPoint addr(net::test::Loopback4(), 1);
+ IPEndPoint client_address(net::test::Loopback4(), 1);
+ IPAddressNumber any4;
+ CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4));
+ server_address_ = IPEndPoint(any4, 5);
- EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, addr))
+ EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, 1, addr, &session1_)));
- ProcessPacket(addr, 1, true, "foo");
+ &dispatcher_, 1, client_address, &session1_)));
+ ProcessPacket(client_address, 1, true, "foo");
+ EXPECT_EQ(client_address, dispatcher_.current_client_address());
+ EXPECT_EQ(server_address_, dispatcher_.current_server_address());
- EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, addr))
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, 2, addr, &session2_)));
- ProcessPacket(addr, 2, true, "bar");
+ &dispatcher_, 2, client_address, &session2_)));
+ ProcessPacket(client_address, 2, true, "bar");
- data_ = "eep";
EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _)).Times(1).
WillOnce(testing::WithArgs<2>(Invoke(
this, &QuicDispatcherTest::ValidatePacket)));
- ProcessPacket(addr, 1, false, "eep");
+ ProcessPacket(client_address, 1, false, "eep");
}
TEST_F(QuicDispatcherTest, Shutdown) {
- IPEndPoint addr(net::test::Loopback4(), 1);
+ IPEndPoint client_address(net::test::Loopback4(), 1);
- EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr))
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, 1, addr, &session1_)));
+ &dispatcher_, 1, client_address, &session1_)));
- ProcessPacket(addr, 1, true, "foo");
+ ProcessPacket(client_address, 1, true, "foo");
EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
SendConnectionClose(QUIC_PEER_GOING_AWAY));
@@ -166,34 +186,36 @@ TEST_F(QuicDispatcherTest, Shutdown) {
class MockTimeWaitListManager : public QuicTimeWaitListManager {
public:
MockTimeWaitListManager(QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
EpollServer* eps)
- : QuicTimeWaitListManager(writer, eps, QuicSupportedVersions()) {
+ : QuicTimeWaitListManager(writer, visitor, eps, QuicSupportedVersions()) {
}
- MOCK_METHOD4(ProcessPacket, void(const IPEndPoint& server_address,
+ MOCK_METHOD5(ProcessPacket, void(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
const QuicEncryptedPacket& packet));
};
TEST_F(QuicDispatcherTest, TimeWaitListManager) {
MockTimeWaitListManager* time_wait_list_manager =
new MockTimeWaitListManager(
- QuicDispatcherPeer::GetWriter(&dispatcher_), &eps_);
+ QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &eps_);
// dispatcher takes the ownership of time_wait_list_manager.
QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_,
time_wait_list_manager);
// Create a new session.
- IPEndPoint addr(net::test::Loopback4(), 1);
- QuicGuid guid = 1;
- EXPECT_CALL(dispatcher_, CreateQuicSession(guid, _, addr))
+ IPEndPoint client_address(net::test::Loopback4(), 1);
+ QuicConnectionId connection_id = 1;
+ EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, guid, addr, &session1_)));
- ProcessPacket(addr, guid, true, "foo");
+ &dispatcher_, connection_id, client_address, &session1_)));
+ ProcessPacket(client_address, connection_id, true, "foo");
// Close the connection by sending public reset packet.
QuicPublicResetPacket packet;
- packet.public_header.guid = guid;
+ packet.public_header.connection_id = connection_id;
packet.public_header.reset_flag = true;
packet.public_header.version_flag = false;
packet.rejected_sequence_number = 19191;
@@ -209,47 +231,117 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) {
.WillOnce(Invoke(
reinterpret_cast<MockConnection*>(session1_->connection()),
&MockConnection::ReallyProcessUdpPacket));
- dispatcher_.ProcessPacket(IPEndPoint(), addr, guid, true, *encrypted);
- EXPECT_TRUE(time_wait_list_manager->IsGuidInTimeWait(guid));
-
- // Dispatcher forwards subsequent packets for this guid to the time wait list
- // manager.
- EXPECT_CALL(*time_wait_list_manager, ProcessPacket(_, _, guid, _)).Times(1);
- ProcessPacket(addr, guid, true, "foo");
+ dispatcher_.ProcessPacket(IPEndPoint(), client_address, *encrypted);
+ EXPECT_TRUE(time_wait_list_manager->IsConnectionIdInTimeWait(connection_id));
+
+ // Dispatcher forwards subsequent packets for this connection_id to the time
+ // wait list manager.
+ EXPECT_CALL(*time_wait_list_manager,
+ ProcessPacket(_, _, connection_id, _, _)).Times(1);
+ ProcessPacket(client_address, connection_id, true, "foo");
}
TEST_F(QuicDispatcherTest, StrayPacketToTimeWaitListManager) {
MockTimeWaitListManager* time_wait_list_manager =
new MockTimeWaitListManager(
- QuicDispatcherPeer::GetWriter(&dispatcher_), &eps_);
+ QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &eps_);
// dispatcher takes the ownership of time_wait_list_manager.
QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_,
time_wait_list_manager);
- IPEndPoint addr(net::test::Loopback4(), 1);
- QuicGuid guid = 1;
- // Dispatcher forwards all packets for this guid to the time wait list
- // manager.
+ IPEndPoint client_address(net::test::Loopback4(), 1);
+ QuicConnectionId connection_id = 1;
+ // Dispatcher forwards all packets for this connection_id to the time wait
+ // list manager.
EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0);
- EXPECT_CALL(*time_wait_list_manager, ProcessPacket(_, _, guid, _)).Times(1);
+ EXPECT_CALL(*time_wait_list_manager,
+ ProcessPacket(_, _, connection_id, _, _)).Times(1);
string data = "foo";
- ProcessPacket(addr, guid, false, "foo");
+ ProcessPacket(client_address, connection_id, false, "foo");
+}
+
+TEST(QuicDispatcherFlowControlTest, NoNewVersion17ConnectionsIfFlagDisabled) {
+ // If FLAGS_enable_quic_stream_flow_control_2 is disabled
+ // then the dispatcher should stop creating connections that support
+ // QUIC_VERSION_17 (existing connections will stay alive).
+ // TODO(rjshade): Remove once
+ // FLAGS_enable_quic_stream_flow_control_2 is removed.
+
+ EpollServer eps;
+ QuicConfig config;
+ QuicCryptoServerConfig server_config(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance());
+ IPEndPoint client(net::test::Loopback4(), 1);
+ IPEndPoint server(net::test::Loopback4(), 1);
+ QuicConnectionId kCID = 1234;
+
+ QuicVersion kTestQuicVersions[] = {QUIC_VERSION_17,
+ QUIC_VERSION_16,
+ QUIC_VERSION_15};
+ QuicVersionVector kTestVersions;
+ for (size_t i = 0; i < arraysize(kTestQuicVersions); ++i) {
+ kTestVersions.push_back(kTestQuicVersions[i]);
+ }
+
+ QuicDispatcher dispatcher(config, server_config, kTestVersions, &eps);
+ dispatcher.Initialize(0);
+
+ // When flag is enabled, new connections should support QUIC_VERSION_17.
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ scoped_ptr<QuicConnection> connection_1(
+ QuicDispatcherPeer::CreateQuicConnection(&dispatcher, kCID, client,
+ server));
+ EXPECT_EQ(QUIC_VERSION_17, connection_1->version());
+
+
+ // When flag is disabled, new connections should not support QUIC_VERSION_17.
+ FLAGS_enable_quic_stream_flow_control_2 = false;
+ scoped_ptr<QuicConnection> connection_2(
+ QuicDispatcherPeer::CreateQuicConnection(&dispatcher, kCID, client,
+ server));
+ EXPECT_EQ(QUIC_VERSION_16, connection_2->version());
}
-class QuicWriteBlockedListTest : public QuicDispatcherTest {
+class BlockingWriter : public QuicPacketWriterWrapper {
+ public:
+ BlockingWriter() : write_blocked_(false) {}
+
+ virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; }
+ virtual void SetWritable() OVERRIDE { write_blocked_ = false; }
+
+ virtual WriteResult WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_client_address,
+ const IPEndPoint& peer_client_address) OVERRIDE {
+ if (write_blocked_) {
+ return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
+ } else {
+ return QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_client_address, peer_client_address);
+ }
+ }
+
+ bool write_blocked_;
+};
+
+class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
public:
virtual void SetUp() {
- IPEndPoint addr(net::test::Loopback4(), 1);
+ writer_ = new BlockingWriter;
+ QuicDispatcherPeer::UseWriter(&dispatcher_, writer_);
- EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr))
+ IPEndPoint client_address(net::test::Loopback4(), 1);
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, 1, addr, &session1_)));
- ProcessPacket(addr, 1, true, "foo");
+ &dispatcher_, 1, client_address, &session1_)));
+ ProcessPacket(client_address, 1, true, "foo");
- EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr))
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
.WillOnce(testing::Return(CreateSession(
- &dispatcher_, 2, addr, &session2_)));
- ProcessPacket(addr, 2, true, "bar");
+ &dispatcher_, 2, client_address, &session2_)));
+ ProcessPacket(client_address, 2, true, "bar");
blocked_list_ = dispatcher_.write_blocked_list();
}
@@ -260,90 +352,105 @@ class QuicWriteBlockedListTest : public QuicDispatcherTest {
dispatcher_.Shutdown();
}
- bool SetBlocked() {
- QuicDispatcherPeer::SetWriteBlocked(&dispatcher_);
- return true;
+ void SetBlocked() {
+ writer_->write_blocked_ = true;
+ }
+
+ void BlockConnection2() {
+ writer_->write_blocked_ = true;
+ dispatcher_.OnWriteBlocked(connection2());
}
protected:
+ BlockingWriter* writer_;
QuicDispatcher::WriteBlockedList* blocked_list_;
};
-TEST_F(QuicWriteBlockedListTest, BasicOnCanWrite) {
+TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) {
// No OnCanWrite calls because no connections are blocked.
dispatcher_.OnCanWrite();
- // Register connection 1 for events, and make sure it's nofitied.
- blocked_list_->insert(make_pair(connection1(), true));
+ // Register connection 1 for events, and make sure it's notified.
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite());
dispatcher_.OnCanWrite();
// It should get only one notification.
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
- EXPECT_FALSE(dispatcher_.OnCanWrite());
+ dispatcher_.OnCanWrite();
+ EXPECT_FALSE(dispatcher_.HasPendingWrites());
}
-TEST_F(QuicWriteBlockedListTest, OnCanWriteOrder) {
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) {
// Make sure we handle events in order.
InSequence s;
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection2(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_.OnCanWrite();
// Check the other ordering.
- blocked_list_->insert(make_pair(connection2(), true));
- blocked_list_->insert(make_pair(connection1(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection2());
+ dispatcher_.OnWriteBlocked(connection1());
EXPECT_CALL(*connection2(), OnCanWrite());
EXPECT_CALL(*connection1(), OnCanWrite());
dispatcher_.OnCanWrite();
}
-TEST_F(QuicWriteBlockedListTest, OnCanWriteRemove) {
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) {
// Add and remove one connction.
- blocked_list_->insert(make_pair(connection1(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
dispatcher_.OnCanWrite();
// Add and remove one connction and make sure it doesn't affect others.
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection2(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection2());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_.OnCanWrite();
// Add it, remove it, and add it back and make sure things are OK.
- blocked_list_->insert(make_pair(connection1(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
- blocked_list_->insert(make_pair(connection1(), true));
+ dispatcher_.OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
dispatcher_.OnCanWrite();
}
-TEST_F(QuicWriteBlockedListTest, DoubleAdd) {
+TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) {
// Make sure a double add does not necessitate a double remove.
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection1(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
dispatcher_.OnCanWrite();
// Make sure a double add does not result in two OnCanWrite calls.
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection1(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
dispatcher_.OnCanWrite();
}
-TEST_F(QuicWriteBlockedListTest, OnCanWriteHandleBlock) {
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlock) {
// Finally make sure if we write block on a write call, we stop calling.
InSequence s;
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection2(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
- Invoke(this, &QuicWriteBlockedListTest::SetBlocked));
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked));
EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
dispatcher_.OnCanWrite();
@@ -352,34 +459,41 @@ TEST_F(QuicWriteBlockedListTest, OnCanWriteHandleBlock) {
dispatcher_.OnCanWrite();
}
-TEST_F(QuicWriteBlockedListTest, LimitedWrites) {
+TEST_F(QuicDispatcherWriteBlockedListTest, LimitedWrites) {
// Make sure we call both writers. The first will register for more writing
// but should not be immediately called due to limits.
InSequence s;
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection2(), true));
- EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(Return(true));
- EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce(Return(false));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce(
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
dispatcher_.OnCanWrite();
+ EXPECT_TRUE(dispatcher_.HasPendingWrites());
// Now call OnCanWrite again, and connection1 should get its second chance
- EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_.OnCanWrite();
+ EXPECT_FALSE(dispatcher_.HasPendingWrites());
}
-TEST_F(QuicWriteBlockedListTest, TestWriteLimits) {
+TEST_F(QuicDispatcherWriteBlockedListTest, TestWriteLimits) {
// Finally make sure if we write block on a write call, we stop calling.
InSequence s;
- blocked_list_->insert(make_pair(connection1(), true));
- blocked_list_->insert(make_pair(connection2(), true));
+ SetBlocked();
+ dispatcher_.OnWriteBlocked(connection1());
+ dispatcher_.OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
- Invoke(this, &QuicWriteBlockedListTest::SetBlocked));
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked));
EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
dispatcher_.OnCanWrite();
+ EXPECT_TRUE(dispatcher_.HasPendingWrites());
// And we'll resume where we left off when we get another call.
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_.OnCanWrite();
+ EXPECT_FALSE(dispatcher_.HasPendingWrites());
}
} // namespace
diff --git a/chromium/net/tools/quic/quic_epoll_clock.h b/chromium/net/tools/quic/quic_epoll_clock.h
index fb21354a9e0..d96bff64389 100644
--- a/chromium/net/tools/quic/quic_epoll_clock.h
+++ b/chromium/net/tools/quic/quic_epoll_clock.h
@@ -5,6 +5,7 @@
#ifndef NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_
#define NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_
+#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "net/quic/quic_clock.h"
#include "net/quic/quic_time.h"
@@ -31,6 +32,9 @@ class QuicEpollClock : public QuicClock {
protected:
EpollServer* epoll_server_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicEpollClock);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_in_memory_cache.cc b/chromium/net/tools/quic/quic_in_memory_cache.cc
index c7c7cd04ebf..af46ca37daa 100644
--- a/chromium/net/tools/quic/quic_in_memory_cache.cc
+++ b/chromium/net/tools/quic/quic_in_memory_cache.cc
@@ -99,7 +99,7 @@ void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
const BalsaHeaders& response_headers,
StringPiece response_body) {
- LOG(INFO) << "Adding response for: " << GetKey(request_headers);
+ VLOG(1) << "Adding response for: " << GetKey(request_headers);
if (ContainsKey(responses_, GetKey(request_headers))) {
LOG(DFATAL) << "Response for given request already exists!";
return;
@@ -110,6 +110,18 @@ void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
responses_[GetKey(request_headers)] = new_response;
}
+void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ SpecialResponseType response_type) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ AddResponse(request_headers, response_headers, "");
+ responses_[GetKey(request_headers)]->response_type_ = response_type;
+}
+
QuicInMemoryCache::QuicInMemoryCache() {
Initialize();
}
@@ -122,11 +134,11 @@ void QuicInMemoryCache::ResetForTests() {
void QuicInMemoryCache::Initialize() {
// If there's no defined cache dir, we have no initialization to do.
if (FLAGS_quic_in_memory_cache_dir.empty()) {
- LOG(WARNING) << "No cache directory found. Skipping initialization.";
+ VLOG(1) << "No cache directory found. Skipping initialization.";
return;
}
- LOG(INFO) << "Attempting to initialize QuicInMemoryCache from directory: "
- << FLAGS_quic_in_memory_cache_dir;
+ VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << FLAGS_quic_in_memory_cache_dir;
FilePath directory(FLAGS_quic_in_memory_cache_dir);
base::FileEnumerator file_list(directory,
@@ -199,8 +211,8 @@ void QuicInMemoryCache::Initialize() {
"HTTP/1.1");
request_headers.ReplaceOrAppendHeader("host", host);
- LOG(INFO) << "Inserting 'http://" << GetKey(request_headers)
- << "' into QuicInMemoryCache.";
+ VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
+ << "' into QuicInMemoryCache.";
AddResponse(request_headers, response_headers, caching_visitor.body());
@@ -214,6 +226,9 @@ QuicInMemoryCache::~QuicInMemoryCache() {
string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
StringPiece uri = request_headers.request_uri();
+ if (uri.size() == 0) {
+ return "";
+ }
StringPiece host;
if (uri[0] == '/') {
host = request_headers.GetHeader("host");
diff --git a/chromium/net/tools/quic/quic_in_memory_cache.h b/chromium/net/tools/quic/quic_in_memory_cache.h
index 3be25a6cac1..be091d5579e 100644
--- a/chromium/net/tools/quic/quic_in_memory_cache.h
+++ b/chromium/net/tools/quic/quic_in_memory_cache.h
@@ -32,12 +32,19 @@ class QuicServer;
// `wget -p --save_headers <url>`
class QuicInMemoryCache {
public:
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ };
+
// Container for response header/body pairs.
class Response {
public:
- Response() {}
+ Response() : response_type_(REGULAR_RESPONSE) {}
~Response() {}
+ const SpecialResponseType response_type() const { return response_type_; }
const BalsaHeaders& headers() const { return headers_; }
const base::StringPiece body() const { return base::StringPiece(body_); }
@@ -51,6 +58,7 @@ class QuicInMemoryCache {
body.CopyToString(&body_);
}
+ SpecialResponseType response_type_;
BalsaHeaders headers_;
std::string body_;
@@ -79,6 +87,12 @@ class QuicInMemoryCache {
const BalsaHeaders& response_headers,
base::StringPiece response_body);
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(base::StringPiece method,
+ base::StringPiece path,
+ base::StringPiece version,
+ SpecialResponseType response_type);
+
private:
typedef base::hash_map<std::string, Response*> ResponseMap;
friend struct DefaultSingletonTraits<QuicInMemoryCache>;
diff --git a/chromium/net/tools/quic/quic_in_memory_cache_test.cc b/chromium/net/tools/quic/quic_in_memory_cache_test.cc
index adcb59917c5..1d4d24cff9f 100644
--- a/chromium/net/tools/quic/quic_in_memory_cache_test.cc
+++ b/chromium/net/tools/quic/quic_in_memory_cache_test.cc
@@ -39,7 +39,7 @@ class QuicInMemoryCacheTest : public ::testing::Test {
headers->ReplaceOrAppendHeader("host", host);
}
- virtual void SetUp() {
+ virtual void SetUp() OVERRIDE {
QuicInMemoryCachePeer::ResetForTests();
}
diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.cc b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc
new file mode 100644
index 00000000000..3d1b03d48e9
--- /dev/null
+++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 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_packet_writer_wrapper.h"
+
+#include "net/quic/quic_types.h"
+
+namespace net {
+namespace tools {
+
+QuicPacketWriterWrapper::QuicPacketWriterWrapper() {}
+
+QuicPacketWriterWrapper::QuicPacketWriterWrapper(QuicPacketWriter* writer)
+ : writer_(writer) {}
+
+QuicPacketWriterWrapper::~QuicPacketWriterWrapper() {}
+
+WriteResult QuicPacketWriterWrapper::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) {
+ return writer_->WritePacket(buffer, buf_len, self_address, peer_address);
+}
+
+bool QuicPacketWriterWrapper::IsWriteBlockedDataBuffered() const {
+ return writer_->IsWriteBlockedDataBuffered();
+}
+
+bool QuicPacketWriterWrapper::IsWriteBlocked() const {
+ return writer_->IsWriteBlocked();
+}
+
+void QuicPacketWriterWrapper::SetWritable() {
+ writer_->SetWritable();
+}
+
+void QuicPacketWriterWrapper::set_writer(QuicPacketWriter* writer) {
+ writer_.reset(writer);
+}
+
+QuicPacketWriter* QuicPacketWriterWrapper::release_writer() {
+ return writer_.release();
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.h b/chromium/net/tools/quic/quic_packet_writer_wrapper.h
new file mode 100644
index 00000000000..9dafe776e24
--- /dev/null
+++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_
+#define NET_TOOLS_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_packet_writer.h"
+
+namespace net {
+
+namespace tools {
+
+// Wraps a writer object to allow dynamically extending functionality. Use
+// cases: replace writer while dispatcher and connections hold on to the
+// wrapper; mix in monitoring in internal server; mix in mocks in unit tests.
+class QuicPacketWriterWrapper : public net::QuicPacketWriter {
+ public:
+ QuicPacketWriterWrapper();
+ explicit QuicPacketWriterWrapper(QuicPacketWriter* writer);
+ virtual ~QuicPacketWriterWrapper();
+
+ // Default implementation of the QuicPacketWriter interface. Passes everything
+ // to |writer_|.
+ virtual WriteResult WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ // Takes ownership of |writer|.
+ void set_writer(QuicPacketWriter* writer);
+
+ // Releases ownership of |writer_|.
+ QuicPacketWriter* release_writer();
+
+ private:
+ scoped_ptr<QuicPacketWriter> writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketWriterWrapper);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_
diff --git a/chromium/net/tools/quic/quic_server.cc b/chromium/net/tools/quic/quic_server.cc
index a62cfbbdf15..be3d9fbd128 100644
--- a/chromium/net/tools/quic/quic_server.cc
+++ b/chromium/net/tools/quic/quic_server.cc
@@ -12,6 +12,7 @@
#include <sys/socket.h>
#include "net/base/ip_endpoint.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_clock.h"
@@ -29,6 +30,7 @@
const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
static const char kSourceAddressTokenSecret[] = "secret";
+const uint32 kServerInitialFlowControlWindow = 100 * net::kMaxPacketSize;
namespace net {
namespace tools {
@@ -43,9 +45,6 @@ QuicServer::QuicServer()
supported_versions_(QuicSupportedVersions()) {
// Use hardcoded crypto parameters for now.
config_.SetDefaults();
- config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0);
- config_.set_server_initial_congestion_window(kMaxInitialWindow,
- kDefaultInitialWindow);
Initialize();
}
@@ -76,6 +75,9 @@ void QuicServer::Initialize() {
crypto_config_.AddDefaultConfig(
QuicRandom::GetInstance(), &clock,
QuicCryptoServerConfig::ConfigOptions()));
+
+ // Set flow control options in the config.
+ config_.SetInitialCongestionWindowToSend(kServerInitialFlowControlWindow);
}
QuicServer::~QuicServer() {
@@ -107,6 +109,19 @@ bool QuicServer::Listen(const IPEndPoint& address) {
overflow_supported_ = true;
}
+ // These send and receive buffer sizes are sized for a single connection,
+ // because the default usage of QuicServer is as a test server with one or
+ // two clients. Adjust higher for use with many clients.
+ if (!QuicSocketUtils::SetReceiveBufferSize(fd_,
+ TcpReceiver::kReceiveWindowTCP)) {
+ return false;
+ }
+
+ if (!QuicSocketUtils::SetSendBufferSize(fd_,
+ TcpReceiver::kReceiveWindowTCP)) {
+ return false;
+ }
+
// Enable the socket option that allows the local address to be
// returned if the socket is bound to more than on address.
int get_local_ip = 1;
@@ -133,7 +148,7 @@ bool QuicServer::Listen(const IPEndPoint& address) {
return false;
}
- LOG(INFO) << "Listening on " << address.ToString();
+ DVLOG(1) << "Listening on " << address.ToString();
if (port_ == 0) {
SockaddrStorage storage;
IPEndPoint server_address;
@@ -143,17 +158,24 @@ bool QuicServer::Listen(const IPEndPoint& address) {
return false;
}
port_ = server_address.port();
- LOG(INFO) << "Kernel assigned port is " << port_;
+ DVLOG(1) << "Kernel assigned port is " << port_;
}
epoll_server_.RegisterFD(fd_, this, kEpollFlags);
- dispatcher_.reset(new QuicDispatcher(config_, crypto_config_,
- supported_versions_,
- fd_, &epoll_server_));
+ dispatcher_.reset(CreateQuicDispatcher());
+ dispatcher_->Initialize(fd_);
return true;
}
+QuicDispatcher* QuicServer::CreateQuicDispatcher() {
+ return new QuicDispatcher(
+ config_,
+ crypto_config_,
+ supported_versions_,
+ &epoll_server_);
+}
+
void QuicServer::WaitForEvents() {
epoll_server_.WaitForEventsAndExecuteCallbacks();
}
@@ -172,7 +194,7 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) {
event->out_ready_mask = 0;
if (event->in_events & EPOLLIN) {
- LOG(ERROR) << "EPOLLIN";
+ DVLOG(1) << "EPOLLIN";
bool read = true;
while (read) {
read = ReadAndDispatchSinglePacket(
@@ -181,8 +203,8 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) {
}
}
if (event->in_events & EPOLLOUT) {
- bool can_write_more = dispatcher_->OnCanWrite();
- if (can_write_more) {
+ dispatcher_->OnCanWrite();
+ if (dispatcher_->HasPendingWrites()) {
event->out_ready_mask |= EPOLLOUT;
}
}
@@ -191,25 +213,10 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) {
}
/* static */
-void QuicServer::MaybeDispatchPacket(QuicDispatcher* dispatcher,
- const QuicEncryptedPacket& packet,
- const IPEndPoint& server_address,
- const IPEndPoint& client_address) {
- QuicGuid guid;
- if (!QuicFramer::ReadGuidFromPacket(packet, &guid)) {
- return;
- }
-
- bool has_version_flag = QuicFramer::HasVersionFlag(packet);
-
- dispatcher->ProcessPacket(
- server_address, client_address, guid, has_version_flag, packet);
-}
-
bool QuicServer::ReadAndDispatchSinglePacket(int fd,
int port,
QuicDispatcher* dispatcher,
- int* packets_dropped) {
+ uint32* packets_dropped) {
// Allocate some extra space so we can send an error if the client goes over
// the limit.
char buf[2 * kMaxPacketSize];
@@ -228,7 +235,7 @@ bool QuicServer::ReadAndDispatchSinglePacket(int fd,
QuicEncryptedPacket packet(buf, bytes_read, false);
IPEndPoint server_address(server_ip, port);
- MaybeDispatchPacket(dispatcher, packet, server_address, client_address);
+ dispatcher->ProcessPacket(server_address, client_address, packet);
return true;
}
diff --git a/chromium/net/tools/quic/quic_server.h b/chromium/net/tools/quic/quic_server.h
index 7c8e4054f53..6285fd769d9 100644
--- a/chromium/net/tools/quic/quic_server.h
+++ b/chromium/net/tools/quic/quic_server.h
@@ -8,6 +8,7 @@
#ifndef NET_TOOLS_QUIC_QUIC_SERVER_H_
#define NET_TOOLS_QUIC_QUIC_SERVER_H_
+#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
@@ -44,8 +45,9 @@ class QuicServer : public EpollCallbackInterface {
void Shutdown();
// From EpollCallbackInterface
- virtual void OnRegistration(
- EpollServer* eps, int fd, int event_mask) OVERRIDE {}
+ virtual void OnRegistration(EpollServer* eps,
+ int fd,
+ int event_mask) OVERRIDE {}
virtual void OnModification(int fd, int event_mask) OVERRIDE {}
virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
virtual void OnUnregistration(int fd, bool replaced) OVERRIDE {}
@@ -58,28 +60,38 @@ class QuicServer : public EpollCallbackInterface {
// dropped packets.
static bool ReadAndDispatchSinglePacket(int fd, int port,
QuicDispatcher* dispatcher,
- int* packets_dropped);
+ uint32* packets_dropped);
virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE {}
- // Dispatches the given packet only if it looks like a valid QUIC packet.
- // TODO(rjshade): Return a status describing why a packet was dropped, and log
- // somehow. Maybe expose as a varz.
- static void MaybeDispatchPacket(QuicDispatcher* dispatcher,
- const QuicEncryptedPacket& packet,
- const IPEndPoint& server_address,
- const IPEndPoint& client_address);
-
void SetStrikeRegisterNoStartupPeriod() {
crypto_config_.set_strike_register_no_startup_period();
}
+ // SetProofSource sets the ProofSource that will be used to verify the
+ // server's certificate, and takes ownership of |source|.
+ void SetProofSource(ProofSource* source) {
+ crypto_config_.SetProofSource(source);
+ }
+
bool overflow_supported() { return overflow_supported_; }
- int packets_dropped() { return packets_dropped_; }
+ uint32 packets_dropped() { return packets_dropped_; }
int port() { return port_; }
+ protected:
+ virtual QuicDispatcher* CreateQuicDispatcher();
+
+ const QuicConfig& config() const { return config_; }
+ const QuicCryptoServerConfig& crypto_config() const {
+ return crypto_config_;
+ }
+ const QuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+ EpollServer* epoll_server() { return &epoll_server_; }
+
private:
friend class net::tools::test::QuicServerPeer;
@@ -100,7 +112,7 @@ class QuicServer : public EpollCallbackInterface {
// If overflow_supported_ is true this will be the number of packets dropped
// during the lifetime of the server. This may overflow if enough packets
// are dropped.
- int packets_dropped_;
+ uint32 packets_dropped_;
// True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
// because the socket would otherwise overflow.
@@ -121,6 +133,10 @@ class QuicServer : public EpollCallbackInterface {
// skipped as necessary).
QuicVersionVector supported_versions_;
+ // Size of flow control receive window to advertise to clients on new
+ // connections.
+ uint32 server_initial_flow_control_receive_window_;
+
DISALLOW_COPY_AND_ASSIGN(QuicServer);
};
diff --git a/chromium/net/tools/quic/quic_server_bin.cc b/chromium/net/tools/quic/quic_server_bin.cc
index a71cbcf6218..2d349ac4792 100644
--- a/chromium/net/tools/quic/quic_server_bin.cc
+++ b/chromium/net/tools/quic/quic_server_bin.cc
@@ -10,6 +10,7 @@
#include "base/at_exit.h"
#include "base/basictypes.h"
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/ip_endpoint.h"
#include "net/tools/quic/quic_in_memory_cache.h"
@@ -20,8 +21,13 @@
int32 FLAGS_port = 6121;
int main(int argc, char *argv[]) {
- CommandLine::Init(argc, argv);
- CommandLine* line = CommandLine::ForCurrentProcess();
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* line = base::CommandLine::ForCurrentProcess();
+
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ CHECK(logging::InitLogging(settings));
+
if (line->HasSwitch("h") || line->HasSwitch("help")) {
const char* help_str =
"Usage: quic_server [options]\n"
diff --git a/chromium/net/tools/quic/quic_server_session.cc b/chromium/net/tools/quic/quic_server_session.cc
index dcb112e0204..d7bc3b84e70 100644
--- a/chromium/net/tools/quic/quic_server_session.cc
+++ b/chromium/net/tools/quic/quic_server_session.cc
@@ -12,16 +12,13 @@
namespace net {
namespace tools {
-QuicServerSession::QuicServerSession(
- const QuicConfig& config,
- QuicConnection* connection,
- QuicSessionOwner* owner)
+QuicServerSession::QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor)
: QuicSession(connection, config),
- owner_(owner) {
-}
+ visitor_(visitor) {}
-QuicServerSession::~QuicServerSession() {
-}
+QuicServerSession::~QuicServerSession() {}
void QuicServerSession::InitializeSession(
const QuicCryptoServerConfig& crypto_config) {
@@ -36,18 +33,28 @@ QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
void QuicServerSession::OnConnectionClosed(QuicErrorCode error,
bool from_peer) {
QuicSession::OnConnectionClosed(error, from_peer);
- owner_->OnConnectionClosed(connection()->guid(), error);
+ // In the unlikely event we get a connection close while doing an asynchronous
+ // crypto event, make sure we cancel the callback.
+ if (crypto_stream_.get() != NULL) {
+ crypto_stream_->CancelOutstandingCallbacks();
+ }
+ visitor_->OnConnectionClosed(connection()->connection_id(), error);
+}
+
+void QuicServerSession::OnWriteBlocked() {
+ QuicSession::OnWriteBlocked();
+ visitor_->OnWriteBlocked(connection());
}
bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) {
if (id % 2 == 0) {
- DLOG(INFO) << "Invalid incoming even stream_id:" << id;
+ DVLOG(1) << "Invalid incoming even stream_id:" << id;
connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
return false;
}
if (GetNumOpenStreams() >= get_max_open_streams()) {
- DLOG(INFO) << "Failed to create a new incoming stream with id:" << id
- << " Already " << GetNumOpenStreams() << " open.";
+ DVLOG(1) << "Failed to create a new incoming stream with id:" << id
+ << " Already " << GetNumOpenStreams() << " open.";
connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
return false;
}
diff --git a/chromium/net/tools/quic/quic_server_session.h b/chromium/net/tools/quic/quic_server_session.h
index f4705519791..e05ba161d4f 100644
--- a/chromium/net/tools/quic/quic_server_session.h
+++ b/chromium/net/tools/quic/quic_server_session.h
@@ -10,6 +10,7 @@
#include <set>
#include <vector>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_crypto_server_stream.h"
@@ -18,6 +19,7 @@
namespace net {
+class QuicBlockedWriterInterface;
class QuicConfig;
class QuicConnection;
class QuicCryptoServerConfig;
@@ -31,28 +33,33 @@ class QuicServerSessionPeer;
// 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.
-class QuicSessionOwner {
+// is closed or blocked.
+class QuicServerSessionVisitor {
public:
- virtual ~QuicSessionOwner() {}
+ virtual ~QuicServerSessionVisitor() {}
- virtual void OnConnectionClosed(QuicGuid guid, QuicErrorCode error) = 0;
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) = 0;
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) = 0;
};
class QuicServerSession : public QuicSession {
public:
QuicServerSession(const QuicConfig& config,
- QuicConnection *connection,
- QuicSessionOwner* owner);
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor);
// Override the base class to notify the owner of the connection close.
virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE;
virtual ~QuicServerSession();
virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config);
- const QuicCryptoServerStream* crypto_stream() { return crypto_stream_.get(); }
+ const QuicCryptoServerStream* crypto_stream() const {
+ return crypto_stream_.get();
+ }
protected:
// QuicSession methods:
@@ -66,13 +73,13 @@ class QuicServerSession : public QuicSession {
virtual bool ShouldCreateIncomingDataStream(QuicStreamId id);
virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
- const QuicCryptoServerConfig& crypto_config);
+ const QuicCryptoServerConfig& crypto_config);
private:
friend class test::QuicServerSessionPeer;
scoped_ptr<QuicCryptoServerStream> crypto_stream_;
- QuicSessionOwner* owner_;
+ QuicServerSessionVisitor* visitor_;
DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
};
diff --git a/chromium/net/tools/quic/quic_server_session_test.cc b/chromium/net/tools/quic/quic_server_session_test.cc
index a3dd9968285..1e04a7fd768 100644
--- a/chromium/net/tools/quic/quic_server_session_test.cc
+++ b/chromium/net/tools/quic/quic_server_session_test.cc
@@ -7,10 +7,10 @@
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_connection.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_data_stream_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
-#include "net/tools/epoll_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"
@@ -20,8 +20,13 @@ using __gnu_cxx::vector;
using net::test::MockConnection;
using net::test::QuicConnectionPeer;
using net::test::QuicDataStreamPeer;
-using testing::_;
+using net::test::SupportedVersions;
+using net::test::kClientDataStreamId1;
+using net::test::kClientDataStreamId2;
+using net::test::kClientDataStreamId3;
+using net::test::kClientDataStreamId4;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace tools {
@@ -29,206 +34,152 @@ namespace test {
class QuicServerSessionPeer {
public:
- static QuicDataStream* GetIncomingReliableStream(
+ static QuicDataStream* GetIncomingDataStream(
QuicServerSession* s, QuicStreamId id) {
- return s->GetIncomingReliableStream(id);
+ return s->GetIncomingDataStream(id);
}
static QuicDataStream* GetDataStream(QuicServerSession* s, QuicStreamId id) {
return s->GetDataStream(id);
}
};
-class CloseOnDataStream : public QuicDataStream {
- public:
- CloseOnDataStream(QuicStreamId id, QuicSession* session)
- : QuicDataStream(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 QuicDataStream* CreateIncomingDataStream(
- QuicStreamId id) OVERRIDE {
- if (!ShouldCreateIncomingDataStream(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 {
+class QuicServerSessionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicServerSessionTest()
: crypto_config_(QuicCryptoServerConfig::TESTING,
QuicRandom::GetInstance()) {
config_.SetDefaults();
config_.set_max_streams_per_connection(3, 3);
-
- connection_ = new MockConnection(true);
- session_.reset(new TestQuicQuicServerSession(
- config_, connection_, &owner_));
+ config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+
+ connection_ =
+ new StrictMock<MockConnection>(true, SupportedVersions(GetParam()));
+ session_.reset(new QuicServerSession(config_, connection_, &owner_));
session_->InitializeSession(crypto_config_);
visitor_ = QuicConnectionPeer::GetVisitor(connection_);
}
- void MarkHeadersReadForStream(QuicStreamId id) {
- QuicDataStream* stream = QuicServerSessionPeer::GetDataStream(
- session_.get(), id);
- ASSERT_TRUE(stream != NULL);
- QuicDataStreamPeer::SetHeadersDecompressed(stream, true);
- }
+ QuicVersion version() const { return connection_->version(); }
- StrictMock<MockQuicSessionOwner> owner_;
- MockConnection* connection_;
+ StrictMock<MockQuicServerSessionVisitor> owner_;
+ StrictMock<MockConnection>* connection_;
QuicConfig config_;
QuicCryptoServerConfig crypto_config_;
- scoped_ptr<TestQuicQuicServerSession> session_;
+ scoped_ptr<QuicServerSession> session_;
QuicConnectionVisitorInterface* visitor_;
};
-TEST_F(QuicServerSessionTest, CloseStreamDueToReset) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicServerSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicServerSessionTest, CloseStreamDueToReset) {
// Open a stream, then reset it.
// Send two bytes of payload to open it.
- QuicStreamFrame data1(3, false, 0, MakeIOVector("HT"));
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
vector<QuicStreamFrame> frames;
frames.push_back(data1);
- EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+ session_->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);
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1,
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0));
visitor_->OnRstStream(rst1);
EXPECT_EQ(0u, session_->GetNumOpenStreams());
// Send the same two bytes of payload in a new packet.
- EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+ visitor_->OnStreamFrames(frames);
// The stream should not be re-opened.
EXPECT_EQ(0u, session_->GetNumOpenStreams());
+ EXPECT_TRUE(connection_->connected());
}
-TEST_F(QuicServerSessionTest, NeverOpenStreamDueToReset) {
- // Send a reset.
- QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
+TEST_P(QuicServerSessionTest, NeverOpenStreamDueToReset) {
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1,
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0));
visitor_->OnRstStream(rst1);
EXPECT_EQ(0u, session_->GetNumOpenStreams());
// Send two bytes of payload.
- QuicStreamFrame data1(3, false, 0, MakeIOVector("HT"));
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("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));
+ visitor_->OnStreamFrames(frames);
// The stream should never be opened, now that the reset is received.
EXPECT_EQ(0u, session_->GetNumOpenStreams());
+ EXPECT_TRUE(connection_->connected());
}
-TEST_F(QuicServerSessionTest, GoOverPrematureClosedStreamLimit) {
- QuicStreamFrame data1(3, false, 0, MakeIOVector("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) {
+TEST_P(QuicServerSessionTest, AcceptClosedStream) {
vector<QuicStreamFrame> frames;
// Send (empty) compressed headers followed by two bytes of data.
- frames.push_back(
- QuicStreamFrame(3, false, 0, MakeIOVector("\1\0\0\0\0\0\0\0HT")));
- frames.push_back(
- QuicStreamFrame(5, false, 0, MakeIOVector("\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);
+ frames.push_back(QuicStreamFrame(kClientDataStreamId1, false, 0,
+ MakeIOVector("\1\0\0\0\0\0\0\0HT")));
+ frames.push_back(QuicStreamFrame(kClientDataStreamId2, false, 0,
+ MakeIOVector("\2\0\0\0\0\0\0\0HT")));
+ visitor_->OnStreamFrames(frames);
+ EXPECT_EQ(2u, session_->GetNumOpenStreams());
+
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1,
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0));
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, MakeIOVector("TP")));
- frames.push_back(QuicStreamFrame(5, false, 2, MakeIOVector("TP")));
- EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+ frames.push_back(
+ QuicStreamFrame(kClientDataStreamId1, false, 2, MakeIOVector("TP")));
+ frames.push_back(
+ QuicStreamFrame(kClientDataStreamId2, false, 2, MakeIOVector("TP")));
+ visitor_->OnStreamFrames(frames);
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(1u, session_->GetNumOpenStreams());
+ EXPECT_TRUE(connection_->connected());
}
-TEST_F(QuicServerSessionTest, MaxNumConnections) {
+TEST_P(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));
+ EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId1));
+ EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId2));
+ EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId3));
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS));
+ EXPECT_FALSE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId4));
}
-TEST_F(QuicServerSessionTest, MaxNumConnectionsImplicit) {
+TEST_P(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));
+ EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId1));
+ // Implicitly opens two more streams.
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS));
+ EXPECT_FALSE(QuicServerSessionPeer::GetIncomingDataStream(
+ session_.get(), kClientDataStreamId4));
}
-TEST_F(QuicServerSessionTest, GetEvenIncomingError) {
+TEST_P(QuicServerSessionTest, GetEvenIncomingError) {
// Incoming streams on the server session must be odd.
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
EXPECT_EQ(NULL,
- QuicServerSessionPeer::GetIncomingReliableStream(
- session_.get(), 2));
+ QuicServerSessionPeer::GetIncomingDataStream(session_.get(), 4));
}
} // namespace
diff --git a/chromium/net/tools/quic/quic_server_test.cc b/chromium/net/tools/quic/quic_server_test.cc
index 7d24edc28bb..e2d7c4f3288 100644
--- a/chromium/net/tools/quic/quic_server_test.cc
+++ b/chromium/net/tools/quic/quic_server_test.cc
@@ -21,13 +21,13 @@ class QuicServerDispatchPacketTest : public ::testing::Test {
public:
QuicServerDispatchPacketTest()
: crypto_config_("blah", QuicRandom::GetInstance()),
- dispatcher_(config_, crypto_config_, 1234, &eps_) {}
-
+ dispatcher_(config_, crypto_config_, &eps_) {
+ dispatcher_.Initialize(1234);
+ }
- void MaybeDispatchPacket(const QuicEncryptedPacket& packet) {
+ void DispatchPacket(const QuicEncryptedPacket& packet) {
IPEndPoint client_addr, server_addr;
- QuicServer::MaybeDispatchPacket(&dispatcher_, packet,
- client_addr, server_addr);
+ dispatcher_.ProcessPacket(server_addr, client_addr, packet);
}
protected:
@@ -37,23 +37,11 @@ class QuicServerDispatchPacketTest : public ::testing::Test {
MockQuicDispatcher dispatcher_;
};
-TEST_F(QuicServerDispatchPacketTest, DoNotDispatchPacketWithoutGUID) {
- // Packet too short to be considered valid.
- unsigned char invalid_packet[] = { 0x00 };
- QuicEncryptedPacket encrypted_invalid_packet(
- QuicUtils::AsChars(invalid_packet), arraysize(invalid_packet), false);
-
- // We expect the invalid packet to be dropped, and ProcessPacket should never
- // be called.
- EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _, _)).Times(0);
- MaybeDispatchPacket(encrypted_invalid_packet);
-}
-
-TEST_F(QuicServerDispatchPacketTest, DispatchValidPacket) {
+TEST_F(QuicServerDispatchPacketTest, DispatchPacket) {
unsigned char valid_packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -64,8 +52,8 @@ TEST_F(QuicServerDispatchPacketTest, DispatchValidPacket) {
QuicEncryptedPacket encrypted_valid_packet(QuicUtils::AsChars(valid_packet),
arraysize(valid_packet), false);
- EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _, _)).Times(1);
- MaybeDispatchPacket(encrypted_valid_packet);
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1);
+ DispatchPacket(encrypted_valid_packet);
}
} // namespace
diff --git a/chromium/net/tools/quic/quic_socket_utils.cc b/chromium/net/tools/quic/quic_socket_utils.cc
index 87071a681ae..3202150917b 100644
--- a/chromium/net/tools/quic/quic_socket_utils.cc
+++ b/chromium/net/tools/quic/quic_socket_utils.cc
@@ -11,6 +11,7 @@
#include <sys/uio.h>
#include <string>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/quic_protocol.h"
@@ -51,7 +52,7 @@ IPAddressNumber QuicSocketUtils::GetAddressFromMsghdr(struct msghdr *hdr) {
// static
bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr *hdr,
- int *dropped_packets) {
+ uint32 *dropped_packets) {
if (hdr->msg_controllen > 0) {
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(hdr);
@@ -79,8 +80,26 @@ int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
}
// static
+bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) {
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) {
+ LOG(ERROR) << "Failed to set socket send size";
+ return false;
+ }
+ return true;
+}
+
+// static
+bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) {
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) {
+ LOG(ERROR) << "Failed to set socket recv size";
+ return false;
+ }
+ return true;
+}
+
+// static
int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len,
- int* dropped_packets,
+ uint32* dropped_packets,
IPAddressNumber* self_address,
IPEndPoint* peer_address) {
CHECK(peer_address != NULL);
@@ -135,6 +154,28 @@ int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len,
return bytes_read;
}
+size_t QuicSocketUtils::SetIpInfoInCmsg(const IPAddressNumber& self_address,
+ cmsghdr* cmsg) {
+ if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) {
+ cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
+ memset(pktinfo, 0, sizeof(in_pktinfo));
+ pktinfo->ipi_ifindex = 0;
+ memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size());
+ return sizeof(in_pktinfo);
+ } else {
+ cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
+ memset(pktinfo, 0, sizeof(in6_pktinfo));
+ memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size());
+ return sizeof(in6_pktinfo);
+ }
+}
+
// static
WriteResult QuicSocketUtils::WritePacket(int fd,
const char* buffer,
@@ -164,30 +205,11 @@ WriteResult QuicSocketUtils::WritePacket(int fd,
if (self_address.empty()) {
hdr.msg_control = 0;
hdr.msg_controllen = 0;
- } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) {
- hdr.msg_control = cbuf;
- hdr.msg_controllen = kSpaceForIp;
- cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-
- cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
- memset(pktinfo, 0, sizeof(in_pktinfo));
- pktinfo->ipi_ifindex = 0;
- memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size());
- hdr.msg_controllen = cmsg->cmsg_len;
} else {
hdr.msg_control = cbuf;
hdr.msg_controllen = kSpaceForIp;
- cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-
- cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
- in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
- memset(pktinfo, 0, sizeof(in6_pktinfo));
- memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size());
+ cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
+ SetIpInfoInCmsg(self_address, cmsg);
hdr.msg_controllen = cmsg->cmsg_len;
}
diff --git a/chromium/net/tools/quic/quic_socket_utils.h b/chromium/net/tools/quic/quic_socket_utils.h
index 8f0feffced2..03b95e5179e 100644
--- a/chromium/net/tools/quic/quic_socket_utils.h
+++ b/chromium/net/tools/quic/quic_socket_utils.h
@@ -11,8 +11,9 @@
#include <sys/socket.h>
#include <string>
+#include "base/basictypes.h"
#include "net/base/ip_endpoint.h"
-#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_types.h"
namespace net {
namespace tools {
@@ -26,12 +27,19 @@ class QuicSocketUtils {
// If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets
// to the correct value and return true. Otherwise it will return false.
- static bool GetOverflowFromMsghdr(struct msghdr *hdr, int *dropped_packets);
+ static bool GetOverflowFromMsghdr(struct msghdr *hdr,
+ uint32 *dropped_packets);
// Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on
// address_family. Returns the return code from setsockopt.
static int SetGetAddressInfo(int fd, int address_family);
+ // Sets the send buffer size to |size| and returns false if it fails.
+ static bool SetSendBufferSize(int fd, size_t size);
+
+ // Sets the receive buffer size to |size| and returns false if it fails.
+ static bool SetReceiveBufferSize(int fd, size_t size);
+
// Reads buf_len from the socket. If reading is successful, returns bytes
// read and sets peer_address to the peer address. Otherwise returns -1.
//
@@ -41,8 +49,10 @@ class QuicSocketUtils {
//
// If self_address is non-null, it will be set to the address the peer sent
// packets to, assuming a packet was read.
- static int ReadPacket(int fd, char* buffer, size_t buf_len,
- int* dropped_packets,
+ static int ReadPacket(int fd,
+ char* buffer,
+ size_t buf_len,
+ uint32* dropped_packets,
IPAddressNumber* self_address,
IPEndPoint* peer_address);
@@ -50,9 +60,20 @@ class QuicSocketUtils {
// status to WRITE_STATUS_OK and sets bytes_written. Otherwise sets the
// result's status to WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and sets
// error_code to errno.
- static WriteResult WritePacket(int fd, const char* buffer, size_t buf_len,
+ static WriteResult WritePacket(int fd,
+ const char* buffer,
+ size_t buf_len,
const IPAddressNumber& self_address,
const IPEndPoint& peer_address);
+
+ // A helper for WritePacket which fills in the cmsg with the supplied self
+ // address.
+ // Returns the length of the packet info structure used.
+ static size_t SetIpInfoInCmsg(const IPAddressNumber& self_address,
+ cmsghdr* cmsg);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicSocketUtils);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.cc b/chromium/net/tools/quic/quic_spdy_client_stream.cc
index 1956c7940db..8423295a1ac 100644
--- a/chromium/net/tools/quic/quic_spdy_client_stream.cc
+++ b/chromium/net/tools/quic/quic_spdy_client_stream.cc
@@ -20,7 +20,9 @@ QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
QuicClientSession* session)
: QuicDataStream(id, session),
read_buf_(new GrowableIOBuffer()),
- response_headers_received_(false) {
+ response_headers_received_(false),
+ header_bytes_read_(0),
+ header_bytes_written_(0) {
}
QuicSpdyClientStream::~QuicSpdyClientStream() {
@@ -28,29 +30,37 @@ QuicSpdyClientStream::~QuicSpdyClientStream() {
bool QuicSpdyClientStream::OnStreamFrame(const QuicStreamFrame& frame) {
if (!write_side_closed()) {
- DLOG(INFO) << "Got a response before the request was complete. "
- << "Aborting request.";
+ DVLOG(1) << "Got a response before the request was complete. "
+ << "Aborting request.";
CloseWriteSide();
}
return QuicDataStream::OnStreamFrame(frame);
}
-uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 length) {
- uint32 total_bytes_processed = 0;
+void QuicSpdyClientStream::OnStreamHeadersComplete(bool fin,
+ size_t frame_len) {
+ header_bytes_read_ = frame_len;
+ QuicDataStream::OnStreamHeadersComplete(fin, frame_len);
+}
+
+uint32 QuicSpdyClientStream::ProcessData(const char* data,
+ uint32 data_len) {
+ int total_bytes_processed = 0;
// Are we still reading the response headers.
if (!response_headers_received_) {
// Grow the read buffer if necessary.
- if (read_buf_->RemainingCapacity() < (int)length) {
+ if (read_buf_->RemainingCapacity() < (int)data_len) {
read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
}
- memcpy(read_buf_->data(), data, length);
- read_buf_->set_offset(read_buf_->offset() + length);
+ memcpy(read_buf_->data(), data, data_len);
+ read_buf_->set_offset(read_buf_->offset() + data_len);
ParseResponseHeaders();
} else {
- data_.append(data + total_bytes_processed, length - total_bytes_processed);
+ data_.append(data + total_bytes_processed,
+ data_len - total_bytes_processed);
}
- return length;
+ return data_len;
}
void QuicSpdyClientStream::OnFinRead() {
@@ -71,15 +81,16 @@ ssize_t QuicSpdyClientStream::SendRequest(const BalsaHeaders& headers,
SpdyUtils::RequestHeadersToSpdyHeaders(headers);
bool send_fin_with_headers = fin && body.empty();
- string headers_string = session()->compressor()->CompressHeadersWithPriority(
- priority(), header_block);
- WriteOrBufferData(headers_string, send_fin_with_headers);
+ size_t bytes_sent = body.size();
+ header_bytes_written_ = WriteHeaders(
+ header_block, send_fin_with_headers, NULL);
+ bytes_sent += header_bytes_written_;
if (!body.empty()) {
- WriteOrBufferData(body, fin);
+ WriteOrBufferData(body, fin, NULL);
}
- return headers_string.size() + body.size();
+ return bytes_sent;
}
int QuicSpdyClientStream::ParseResponseHeaders() {
@@ -109,7 +120,7 @@ int QuicSpdyClientStream::ParseResponseHeaders() {
// Sends body data to the server and returns the number of bytes sent.
void QuicSpdyClientStream::SendBody(const string& data, bool fin) {
- return WriteOrBufferData(data, fin);
+ WriteOrBufferData(data, fin, NULL);
}
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.h b/chromium/net/tools/quic/quic_spdy_client_stream.h
index 2b17205f53e..1292e197603 100644
--- a/chromium/net/tools/quic/quic_spdy_client_stream.h
+++ b/chromium/net/tools/quic/quic_spdy_client_stream.h
@@ -8,6 +8,7 @@
#include <sys/types.h>
#include <string>
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "net/base/io_buffer.h"
#include "net/quic/quic_data_stream.h"
@@ -33,6 +34,9 @@ class QuicSpdyClientStream : public QuicDataStream {
// SPDY/HTTP does not support bidirectional streaming.
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+ // Override the base class to store the size of the headers.
+ virtual void OnStreamHeadersComplete(bool fin, size_t frame_len) OVERRIDE;
+
// ReliableQuicStream implementation called by the session when there's
// data for us.
virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
@@ -54,6 +58,10 @@ class QuicSpdyClientStream : public QuicDataStream {
// Returns whatever headers have been received for this stream.
const BalsaHeaders& headers() { return headers_; }
+ size_t header_bytes_read() const { return header_bytes_read_; }
+
+ size_t header_bytes_written() const { return header_bytes_written_; }
+
// 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 QuicDataStream::set_priority;
@@ -66,6 +74,10 @@ class QuicSpdyClientStream : public QuicDataStream {
scoped_refptr<GrowableIOBuffer> read_buf_;
bool response_headers_received_;
+ size_t header_bytes_read_;
+ size_t header_bytes_written_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientStream);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc
index 44c640cbdea..475f832b1c5 100644
--- a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -7,7 +7,6 @@
#include "base/strings/string_number_conversions.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
-#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_client_session.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
#include "net/tools/quic/spdy_utils.h"
@@ -16,6 +15,8 @@
#include "testing/gtest/include/gtest/gtest.h"
using net::test::DefaultQuicConfig;
+using net::test::SupportedVersions;
+using testing::StrictMock;
using testing::TestWithParam;
namespace net {
@@ -23,23 +24,35 @@ namespace tools {
namespace test {
namespace {
-class QuicSpdyClientStreamTest : public ::testing::Test {
+class QuicSpdyClientStreamTest : public TestWithParam<QuicVersion> {
public:
QuicSpdyClientStreamTest()
- : session_("example.com", DefaultQuicConfig(),
- new MockConnection(false),
+ : connection_(new StrictMock<MockConnection>(
+ false, SupportedVersions(GetParam()))),
+ session_(QuicServerId("example.com", 80, false, PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(),
+ connection_,
&crypto_config_),
body_("hello world") {
- session_.config()->SetDefaults();
crypto_config_.SetDefaults();
headers_.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "Ok");
headers_.ReplaceOrAppendHeader("content-length", "11");
headers_string_ = SpdyUtils::SerializeResponseHeaders(headers_);
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ session_.config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
stream_.reset(new QuicSpdyClientStream(3, &session_));
}
+ StrictMock<MockConnection>* connection_;
QuicClientSession session_;
scoped_ptr<QuicSpdyClientStream> stream_;
BalsaHeaders headers_;
@@ -48,7 +61,10 @@ class QuicSpdyClientStreamTest : public ::testing::Test {
QuicCryptoClientConfig crypto_config_;
};
-TEST_F(QuicSpdyClientStreamTest, TestFraming) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicSpdyClientStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicSpdyClientStreamTest, TestFraming) {
EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
headers_string_.c_str(), headers_string_.size()));
EXPECT_EQ(body_.size(),
@@ -57,7 +73,7 @@ TEST_F(QuicSpdyClientStreamTest, TestFraming) {
EXPECT_EQ(body_, stream_->data());
}
-TEST_F(QuicSpdyClientStreamTest, TestFramingOnePacket) {
+TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) {
string message = headers_string_ + body_;
EXPECT_EQ(message.size(), stream_->ProcessData(
@@ -66,7 +82,7 @@ TEST_F(QuicSpdyClientStreamTest, TestFramingOnePacket) {
EXPECT_EQ(body_, stream_->data());
}
-TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
+TEST_P(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
string large_body = "hello world!!!!!!";
EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
@@ -75,12 +91,14 @@ TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+ EXPECT_CALL(*connection_,
+ SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0));
stream_->ProcessData(large_body.c_str(), large_body.size());
EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
}
-TEST_F(QuicSpdyClientStreamTest, TestNoBidirectionalStreaming) {
+TEST_P(QuicSpdyClientStreamTest, TestNoBidirectionalStreaming) {
QuicStreamFrame frame(3, false, 3, MakeIOVector("asd"));
EXPECT_FALSE(stream_->write_side_closed());
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.cc b/chromium/net/tools/quic/quic_spdy_server_stream.cc
index c1a9cf15e18..7ae20d02e97 100644
--- a/chromium/net/tools/quic/quic_spdy_server_stream.cc
+++ b/chromium/net/tools/quic/quic_spdy_server_stream.cc
@@ -28,22 +28,23 @@ QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
QuicSpdyServerStream::~QuicSpdyServerStream() {
}
-uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 length) {
+uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
uint32 total_bytes_processed = 0;
// Are we still reading the request headers.
if (!request_headers_received_) {
// Grow the read buffer if necessary.
- if (read_buf_->RemainingCapacity() < (int)length) {
+ if (read_buf_->RemainingCapacity() < (int)data_len) {
read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
}
- memcpy(read_buf_->data(), data, length);
- read_buf_->set_offset(read_buf_->offset() + length);
+ memcpy(read_buf_->data(), data, data_len);
+ read_buf_->set_offset(read_buf_->offset() + data_len);
ParseRequestHeaders();
} else {
- body_.append(data + total_bytes_processed, length - total_bytes_processed);
+ body_.append(data + total_bytes_processed,
+ data_len - total_bytes_processed);
}
- return length;
+ return data_len;
}
void QuicSpdyServerStream::OnFinRead() {
@@ -55,7 +56,7 @@ void QuicSpdyServerStream::OnFinRead() {
if (!request_headers_received_) {
SendErrorResponse(); // We're not done reading headers.
} else if ((headers_.content_length_status() ==
- BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
+ BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
body_.size() != headers_.content_length()) {
SendErrorResponse(); // Invalid content length
} else {
@@ -97,12 +98,23 @@ void QuicSpdyServerStream::SendResponse() {
return;
}
- DLOG(INFO) << "Sending response for stream " << id();
+ if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
+ DVLOG(1) << "Special response: closing connection.";
+ CloseConnection(QUIC_NO_ERROR);
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
+ DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ DVLOG(1) << "Sending response for stream " << id();
SendHeadersAndBody(response->headers(), response->body());
}
void QuicSpdyServerStream::SendErrorResponse() {
- DLOG(INFO) << "Sending error response for stream " << id();
+ DVLOG(1) << "Sending error response for stream " << id();
BalsaHeaders headers;
headers.SetResponseFirstlineFromStringPieces(
"HTTP/1.1", "500", "Server Error");
@@ -110,7 +122,7 @@ void QuicSpdyServerStream::SendErrorResponse() {
SendHeadersAndBody(headers, "bad");
}
-void QuicSpdyServerStream:: SendHeadersAndBody(
+void QuicSpdyServerStream::SendHeadersAndBody(
const BalsaHeaders& response_headers,
StringPiece body) {
// We only support SPDY and HTTP, and neither handles bidirectional streaming.
@@ -121,12 +133,10 @@ void QuicSpdyServerStream:: SendHeadersAndBody(
SpdyHeaderBlock header_block =
SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
- string headers_string =
- session()->compressor()->CompressHeaders(header_block);
- WriteOrBufferData(headers_string, body.empty());
+ WriteHeaders(header_block, body.empty(), NULL);
if (!body.empty()) {
- WriteOrBufferData(body, true);
+ WriteOrBufferData(body, true, NULL);
}
}
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.h b/chromium/net/tools/quic/quic_spdy_server_stream.h
index 574ef76f33d..741f7867331 100644
--- a/chromium/net/tools/quic/quic_spdy_server_stream.h
+++ b/chromium/net/tools/quic/quic_spdy_server_stream.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/basictypes.h"
#include "net/base/io_buffer.h"
#include "net/quic/quic_data_stream.h"
#include "net/quic/quic_protocol.h"
@@ -56,6 +57,8 @@ class QuicSpdyServerStream : public QuicDataStream {
// Buffer into which response header data is read.
scoped_refptr<GrowableIOBuffer> read_buf_;
bool request_headers_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream_test.cc b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc
index c0ec9449707..e01569ede79 100644
--- a/chromium/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -8,7 +8,6 @@
#include "base/strings/string_piece.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_spdy_compressor.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/tools/epoll_server/epoll_server.h"
@@ -20,8 +19,9 @@
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
-using net::tools::test::MockConnection;
using net::test::MockSession;
+using net::test::SupportedVersions;
+using net::tools::test::MockConnection;
using std::string;
using testing::_;
using testing::AnyNumber;
@@ -29,7 +29,6 @@ using testing::Invoke;
using testing::InvokeArgument;
using testing::InSequence;
using testing::Return;
-using testing::StrEq;
using testing::StrictMock;
using testing::WithArgs;
@@ -46,25 +45,35 @@ class QuicSpdyServerStreamPeer : public QuicSpdyServerStream {
using QuicSpdyServerStream::SendResponse;
using QuicSpdyServerStream::SendErrorResponse;
- const string& body() {
- return body_;
+ BalsaHeaders* mutable_headers() {
+ return &headers_;
}
- const BalsaHeaders& headers() {
- return headers_;
+ static void SendResponse(QuicSpdyServerStream* stream) {
+ stream->SendResponse();
}
- BalsaHeaders* mutable_headers() {
- return &headers_;
+ static void SendErrorResponse(QuicSpdyServerStream* stream) {
+ stream->SendResponse();
+ }
+
+ static const string& body(QuicSpdyServerStream* stream) {
+ return stream->body_;
+ }
+
+ static const BalsaHeaders& headers(QuicSpdyServerStream* stream) {
+ return stream->headers_;
}
};
namespace {
-class QuicSpdyServerStreamTest : public ::testing::Test {
+class QuicSpdyServerStreamTest : public ::testing::TestWithParam<QuicVersion> {
public:
QuicSpdyServerStreamTest()
- : session_(new MockConnection(true)),
+ : connection_(new StrictMock<MockConnection>(
+ true, SupportedVersions(GetParam()))),
+ session_(connection_),
body_("hello world") {
BalsaHeaders request_headers;
request_headers.SetRequestFirstlineFromStringPieces(
@@ -72,35 +81,23 @@ class QuicSpdyServerStreamTest : public ::testing::Test {
request_headers.ReplaceOrAppendHeader("content-length", "11");
headers_string_ = SpdyUtils::SerializeRequestHeaders(request_headers);
- stream_.reset(new QuicSpdyServerStreamPeer(3, &session_));
- }
- 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;
- TestDecompressorVisitor visitor;
-
- // First the header id, then the compressed data.
- EXPECT_EQ(1, headers[0]);
- EXPECT_EQ(0, headers[1]);
- EXPECT_EQ(0, headers[2]);
- EXPECT_EQ(0, headers[3]);
- EXPECT_EQ(static_cast<size_t>(headers.length() - 4),
- decompressor.DecompressData(headers.substr(4), &visitor));
-
- EXPECT_EQ(headers_string_, visitor.data());
-
- return QuicConsumedData(headers.size(), false);
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ session_.config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ stream_.reset(new QuicSpdyServerStreamPeer(3, &session_));
}
static void SetUpTestCase() {
QuicInMemoryCachePeer::ResetForTests();
}
- virtual void SetUp() {
+ virtual void SetUp() OVERRIDE {
QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
BalsaHeaders request_headers, response_headers;
@@ -130,8 +127,17 @@ class QuicSpdyServerStreamTest : public ::testing::Test {
cache->AddResponse(request_headers, response_headers, body);
}
+ const string& StreamBody() {
+ return QuicSpdyServerStreamPeer::body(stream_.get());
+ }
+
+ const BalsaHeaders& StreamHeaders() {
+ return QuicSpdyServerStreamPeer::headers(stream_.get());
+ }
+
BalsaHeaders response_headers_;
EpollServer eps_;
+ StrictMock<MockConnection>* connection_;
StrictMock<MockSession> session_;
scoped_ptr<QuicSpdyServerStreamPeer> stream_;
string headers_string_;
@@ -140,32 +146,31 @@ class QuicSpdyServerStreamTest : public ::testing::Test {
QuicConsumedData ConsumeAllData(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection /*fec_protection_*/,
QuicAckNotifier::DelegateInterface* /*ack_notifier_delegate*/) {
- ssize_t consumed_length = 0;
- for (int i = 0; i < iov_count; ++i) {
- consumed_length += iov[i].iov_len;
- }
- return QuicConsumedData(consumed_length, fin);
+ return QuicConsumedData(data.TotalBufferSize(), fin);
}
-TEST_F(QuicSpdyServerStreamTest, TestFraming) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicSpdyServerStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicSpdyServerStreamTest, TestFraming) {
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()).
WillRepeatedly(Invoke(ConsumeAllData));
EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
headers_string_.c_str(), headers_string_.size()));
EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
- EXPECT_EQ(11u, stream_->headers().content_length());
- EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri());
- EXPECT_EQ("POST", stream_->headers().request_method());
- EXPECT_EQ(body_, stream_->body());
+ EXPECT_EQ(11u, StreamHeaders().content_length());
+ EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
+ EXPECT_EQ("POST", StreamHeaders().request_method());
+ EXPECT_EQ(body_, StreamBody());
}
-TEST_F(QuicSpdyServerStreamTest, TestFramingOnePacket) {
+TEST_P(QuicSpdyServerStreamTest, TestFramingOnePacket) {
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()).
WillRepeatedly(Invoke(ConsumeAllData));
@@ -173,14 +178,13 @@ TEST_F(QuicSpdyServerStreamTest, TestFramingOnePacket) {
EXPECT_EQ(message.size(), stream_->ProcessData(
message.c_str(), message.size()));
- EXPECT_EQ(11u, stream_->headers().content_length());
- EXPECT_EQ("https://www.google.com/",
- stream_->headers().request_uri());
- EXPECT_EQ("POST", stream_->headers().request_method());
- EXPECT_EQ(body_, stream_->body());
+ EXPECT_EQ(11u, StreamHeaders().content_length());
+ EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
+ EXPECT_EQ("POST", StreamHeaders().request_method());
+ EXPECT_EQ(body_, StreamBody());
}
-TEST_F(QuicSpdyServerStreamTest, TestFramingExtraData) {
+TEST_P(QuicSpdyServerStreamTest, TestFramingExtraData) {
string large_body = "hello world!!!!!!";
// We'll automatically write out an error (headers + body)
@@ -192,12 +196,12 @@ TEST_F(QuicSpdyServerStreamTest, TestFramingExtraData) {
// Content length is still 11. This will register as an error and we won't
// accept the bytes.
stream_->ProcessData(large_body.c_str(), large_body.size());
- EXPECT_EQ(11u, stream_->headers().content_length());
- EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri());
- EXPECT_EQ("POST", stream_->headers().request_method());
+ EXPECT_EQ(11u, StreamHeaders().content_length());
+ EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
+ EXPECT_EQ("POST", StreamHeaders().request_method());
}
-TEST_F(QuicSpdyServerStreamTest, TestSendResponse) {
+TEST_P(QuicSpdyServerStreamTest, TestSendResponse) {
BalsaHeaders* request_headers = stream_->mutable_headers();
request_headers->SetRequestFirstlineFromStringPieces(
"GET",
@@ -209,40 +213,34 @@ TEST_F(QuicSpdyServerStreamTest, TestSendResponse) {
response_headers_.ReplaceOrAppendHeader("content-length", "3");
InSequence s;
- EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1)
- .WillOnce(WithArgs<1>(Invoke(
- this, &QuicSpdyServerStreamTest::ValidateHeaders)));
-
- EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1).
+ EXPECT_CALL(session_,
+ WritevData(kHeadersStreamId, _, 0, false, _, NULL));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(1).
WillOnce(Return(QuicConsumedData(3, true)));
- stream_->SendResponse();
+ QuicSpdyServerStreamPeer::SendResponse(stream_.get());
EXPECT_TRUE(stream_->read_side_closed());
EXPECT_TRUE(stream_->write_side_closed());
}
-TEST_F(QuicSpdyServerStreamTest, TestSendErrorResponse) {
+TEST_P(QuicSpdyServerStreamTest, TestSendErrorResponse) {
response_headers_.SetResponseFirstlineFromStringPieces(
"HTTP/1.1", "500", "Server Error");
response_headers_.ReplaceOrAppendHeader("content-length", "3");
InSequence s;
- EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1)
- .WillOnce(WithArgs<1>(Invoke(
- this, &QuicSpdyServerStreamTest::ValidateHeaders)));
-
- EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1).
+ EXPECT_CALL(session_,
+ WritevData(kHeadersStreamId, _, 0, false, _, NULL));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(1).
WillOnce(Return(QuicConsumedData(3, true)));
- stream_->SendErrorResponse();
+ QuicSpdyServerStreamPeer::SendErrorResponse(stream_.get());
EXPECT_TRUE(stream_->read_side_closed());
EXPECT_TRUE(stream_->write_side_closed());
}
-TEST_F(QuicSpdyServerStreamTest, InvalidHeadersWithFin) {
+TEST_P(QuicSpdyServerStreamTest, InvalidHeadersWithFin) {
char arr[] = {
- 0x00, 0x00, 0x00, 0x05, // ....
- 0x00, 0x00, 0x00, 0x05, // ....
0x3a, 0x68, 0x6f, 0x73, // :hos
0x74, 0x00, 0x00, 0x00, // t...
0x00, 0x00, 0x00, 0x00, // ....
@@ -265,8 +263,8 @@ TEST_F(QuicSpdyServerStreamTest, InvalidHeadersWithFin) {
0x54, 0x54, 0x50, 0x2f, // TTP/
0x31, 0x2e, 0x31, // 1.1
};
- QuicStreamFrame frame(
- stream_->id(), true, 0, MakeIOVector(StringPiece(arr, arraysize(arr))));
+ StringPiece data(arr, arraysize(arr));
+ QuicStreamFrame frame(stream_->id(), true, 0, MakeIOVector(data));
// Verify that we don't crash when we get a invalid headers in stream frame.
stream_->OnStreamFrame(frame);
}
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.cc b/chromium/net/tools/quic/quic_time_wait_list_manager.cc
index df9e378e722..9744ff89e53 100644
--- a/chromium/net/tools/quic/quic_time_wait_list_manager.cc
+++ b/chromium/net/tools/quic/quic_time_wait_list_manager.cc
@@ -17,6 +17,8 @@
#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_utils.h"
+#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/quic_server_session.h"
using base::StringPiece;
using std::make_pair;
@@ -26,23 +28,24 @@ namespace tools {
namespace {
-// Time period for which the guid should live in time wait state..
+// Time period for which the connection_id should live in time wait state..
const int kTimeWaitSeconds = 5;
} // namespace
// A very simple alarm that just informs the QuicTimeWaitListManager to clean
-// up old guids. This alarm should be unregistered and deleted before the
-// QuicTimeWaitListManager is deleted.
-class GuidCleanUpAlarm : public EpollAlarm {
+// up old connection_ids. This alarm should be unregistered and deleted before
+// the QuicTimeWaitListManager is deleted.
+class ConnectionIdCleanUpAlarm : public EpollAlarm {
public:
- explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager)
+ explicit ConnectionIdCleanUpAlarm(
+ QuicTimeWaitListManager* time_wait_list_manager)
: time_wait_list_manager_(time_wait_list_manager) {
}
virtual int64 OnAlarm() OVERRIDE {
EpollAlarm::OnAlarm();
- time_wait_list_manager_->CleanUpOldGuids();
+ time_wait_list_manager_->CleanUpOldConnectionIds();
// Let the time wait manager register the alarm at appropriate time.
return 0;
}
@@ -52,19 +55,9 @@ class GuidCleanUpAlarm : public EpollAlarm {
QuicTimeWaitListManager* time_wait_list_manager_;
};
-struct QuicTimeWaitListManager::GuidAddTime {
- GuidAddTime(QuicGuid guid, const QuicTime& time)
- : guid(guid),
- time_added(time) {
- }
-
- QuicGuid guid;
- QuicTime time_added;
-};
-
// This class stores pending public reset packets to be sent to clients.
// server_address - server address on which a packet what was received for
-// a guid in time wait state.
+// a connection_id in time wait state.
// client_address - address of the client that sent that packet. Needed to send
// the public reset packet back to the client.
// packet - the pending public reset packet that is to be sent to the client.
@@ -93,174 +86,103 @@ class QuicTimeWaitListManager::QueuedPacket {
QuicTimeWaitListManager::QuicTimeWaitListManager(
QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
EpollServer* epoll_server,
const QuicVersionVector& supported_versions)
- : framer_(supported_versions,
- QuicTime::Zero(), // unused
- true),
- epoll_server_(epoll_server),
+ : epoll_server_(epoll_server),
kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
- guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
- clock_(epoll_server),
+ connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)),
+ clock_(epoll_server_),
writer_(writer),
- is_write_blocked_(false) {
- framer_.set_visitor(this);
- SetGuidCleanUpAlarm();
+ visitor_(visitor) {
+ SetConnectionIdCleanUpAlarm();
}
QuicTimeWaitListManager::~QuicTimeWaitListManager() {
- guid_clean_up_alarm_->UnregisterIfRegistered();
- STLDeleteElements(&time_ordered_guid_list_);
+ connection_id_clean_up_alarm_->UnregisterIfRegistered();
STLDeleteElements(&pending_packets_queue_);
- for (GuidMapIterator it = guid_map_.begin(); it != guid_map_.end(); ++it) {
+ for (ConnectionIdMap::iterator it = connection_id_map_.begin();
+ it != connection_id_map_.end();
+ ++it) {
delete it->second.close_packet;
}
}
-void QuicTimeWaitListManager::AddGuidToTimeWait(
- QuicGuid guid,
+void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
QuicVersion version,
QuicEncryptedPacket* close_packet) {
- DCHECK(!IsGuidInTimeWait(guid));
- // Initialize the guid with 0 packets received.
- GuidData data(0, version, close_packet);
- guid_map_.insert(make_pair(guid, data));
- time_ordered_guid_list_.push_back(new GuidAddTime(guid,
- clock_.ApproximateNow()));
-}
-
-bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const {
- return guid_map_.find(guid) != guid_map_.end();
+ DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
+ int num_packets = 0;
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
+ num_packets = it->second.num_packets;
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ ConnectionIdData data(num_packets, version, clock_.ApproximateNow(),
+ close_packet);
+ connection_id_map_.insert(make_pair(connection_id, data));
}
-void QuicTimeWaitListManager::ProcessPacket(
- const IPEndPoint& server_address,
- const IPEndPoint& client_address,
- QuicGuid guid,
- const QuicEncryptedPacket& packet) {
- DCHECK(IsGuidInTimeWait(guid));
- server_address_ = server_address;
- client_address_ = client_address;
-
- // Set the framer to the appropriate version for this GUID, before processing.
- QuicVersion version = GetQuicVersionFromGuid(guid);
- framer_.set_version(version);
-
- framer_.ProcessPacket(packet);
+bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
+ QuicConnectionId connection_id) const {
+ return ContainsKey(connection_id_map_, connection_id);
}
-QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) {
- GuidMapIterator it = guid_map_.find(guid);
- DCHECK(it != guid_map_.end());
+QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
+ QuicConnectionId connection_id) {
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
return (it->second).version;
}
-bool QuicTimeWaitListManager::OnCanWrite() {
- is_write_blocked_ = false;
- while (!is_write_blocked_ && !pending_packets_queue_.empty()) {
+void QuicTimeWaitListManager::OnCanWrite() {
+ while (!pending_packets_queue_.empty()) {
QueuedPacket* queued_packet = pending_packets_queue_.front();
- WriteToWire(queued_packet);
- if (!is_write_blocked_) {
- pending_packets_queue_.pop_front();
- delete queued_packet;
+ if (!WriteToWire(queued_packet)) {
+ return;
}
+ pending_packets_queue_.pop_front();
+ delete queued_packet;
}
-
- return !is_write_blocked_;
}
-void QuicTimeWaitListManager::OnError(QuicFramer* framer) {
- DLOG(INFO) << QuicUtils::ErrorToString(framer->error());
-}
-
-bool QuicTimeWaitListManager::OnProtocolVersionMismatch(
- QuicVersion received_version) {
- // Drop such packets whose version don't match.
- return false;
-}
-
-bool QuicTimeWaitListManager::OnUnauthenticatedHeader(
- const QuicPacketHeader& header) {
+void QuicTimeWaitListManager::ProcessPacket(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& /*packet*/) {
+ DCHECK(IsConnectionIdInTimeWait(connection_id));
+ DVLOG(1) << "Processing " << connection_id << " in time wait state.";
// TODO(satyamshekhar): Think about handling packets from different client
// addresses.
- GuidMapIterator it = guid_map_.find(header.public_header.guid);
- DCHECK(it != guid_map_.end());
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
// Increment the received packet count.
++((it->second).num_packets);
if (!ShouldSendResponse((it->second).num_packets)) {
- return false;
+ return;
}
if (it->second.close_packet) {
QueuedPacket* queued_packet =
- new QueuedPacket(server_address_,
- client_address_,
+ new QueuedPacket(server_address,
+ client_address,
it->second.close_packet->Clone());
// Takes ownership of the packet.
SendOrQueuePacket(queued_packet);
} else {
- // We don't need the packet anymore. Just tell the client what sequence
- // number we rejected.
- SendPublicReset(server_address_,
- client_address_,
- header.public_header.guid,
- header.packet_sequence_number);
+ SendPublicReset(server_address,
+ client_address,
+ connection_id,
+ sequence_number);
}
- // Never process the body of the packet in time wait state.
- return false;
-}
-
-bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) {
- DCHECK(false);
- return false;
-}
-
-void QuicTimeWaitListManager::OnRevivedPacket() {
- DCHECK(false);
-}
-
-void QuicTimeWaitListManager::OnFecProtectedPayload(StringPiece /*payload*/) {
- DCHECK(false);
-}
-
-bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& /*frame*/) {
- DCHECK(false);
- return false;
-}
-
-bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& /*frame*/) {
- DCHECK(false);
- return false;
-}
-
-bool QuicTimeWaitListManager::OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& /*frame*/) {
- DCHECK(false);
- return false;
}
-bool QuicTimeWaitListManager::OnRstStreamFrame(
- const QuicRstStreamFrame& /*frame*/) {
- DCHECK(false);
- return false;
-}
-
-bool QuicTimeWaitListManager::OnConnectionCloseFrame(
- const QuicConnectionCloseFrame & /*frame*/) {
- DCHECK(false);
- return false;
-}
-
-bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) {
- DCHECK(false);
- return false;
-}
-
-void QuicTimeWaitListManager::OnFecData(const QuicFecData& /*fec*/) {
- DCHECK(false);
-}
-
-// Returns true if the number of packets received for this guid is a power of 2
-// to throttle the number of public reset packets we send to a client.
+// Returns true if the number of packets received for this connection_id is a
+// power of 2 to throttle the number of public reset packets we send to a
+// client.
bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
return (received_packet_count & (received_packet_count - 1)) == 0;
}
@@ -268,95 +190,100 @@ bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
void QuicTimeWaitListManager::SendPublicReset(
const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
+ QuicConnectionId connection_id,
QuicPacketSequenceNumber rejected_sequence_number) {
QuicPublicResetPacket packet;
- packet.public_header.guid = guid;
+ packet.public_header.connection_id = connection_id;
packet.public_header.reset_flag = true;
packet.public_header.version_flag = false;
packet.rejected_sequence_number = rejected_sequence_number;
- // TODO(satyamshekhar): generate a valid nonce for this guid.
+ // TODO(satyamshekhar): generate a valid nonce for this connection_id.
packet.nonce_proof = 1010101;
+ packet.client_address = client_address;
QueuedPacket* queued_packet = new QueuedPacket(
server_address,
client_address,
- QuicFramer::BuildPublicResetPacket(packet));
+ BuildPublicReset(packet));
// Takes ownership of the packet.
SendOrQueuePacket(queued_packet);
}
+QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
+ const QuicPublicResetPacket& packet) {
+ return QuicFramer::BuildPublicResetPacket(packet);
+}
+
// Either sends the packet and deletes it or makes pending queue the
// owner of the packet.
void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
- if (!is_write_blocked_) {
- // TODO(satyamshekhar): Handle packets that fail due to error other than
- // EAGAIN or EWOULDBLOCK.
- WriteToWire(packet);
- }
-
- if (is_write_blocked_) {
+ if (WriteToWire(packet)) {
+ delete packet;
+ } else {
// pending_packets_queue takes the ownership of the queued packet.
pending_packets_queue_.push_back(packet);
- } else {
- delete packet;
}
}
-void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
- DCHECK(!is_write_blocked_);
+bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked(this);
+ return false;
+ }
WriteResult result = writer_->WritePacket(
queued_packet->packet()->data(),
queued_packet->packet()->length(),
queued_packet->server_address().address(),
- queued_packet->client_address(),
- this);
-
+ queued_packet->client_address());
if (result.status == WRITE_STATUS_BLOCKED) {
- is_write_blocked_ = true;
+ // If blocked and unbuffered, return false to retry sending.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked(this);
+ return writer_->IsWriteBlockedDataBuffered();
} else if (result.status == WRITE_STATUS_ERROR) {
LOG(WARNING) << "Received unknown error while sending reset packet to "
<< queued_packet->client_address().ToString() << ": "
<< strerror(result.error_code);
}
+ return true;
}
-void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
- guid_clean_up_alarm_->UnregisterIfRegistered();
+void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
+ connection_id_clean_up_alarm_->UnregisterIfRegistered();
int64 next_alarm_interval;
- if (!time_ordered_guid_list_.empty()) {
- GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
+ if (!connection_id_map_.empty()) {
+ QuicTime oldest_connection_id =
+ connection_id_map_.begin()->second.time_added;
QuicTime now = clock_.ApproximateNow();
- DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_);
- next_alarm_interval = oldest_guid->time_added
- .Add(kTimeWaitPeriod_)
- .Subtract(now)
- .ToMicroseconds();
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ next_alarm_interval = oldest_connection_id.Add(kTimeWaitPeriod_)
+ .Subtract(now)
+ .ToMicroseconds();
+ } else {
+ LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
+ next_alarm_interval = 0;
+ }
} else {
- // No guids added so none will expire before kTimeWaitPeriod_.
+ // No connection_ids added so none will expire before kTimeWaitPeriod_.
next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
}
- epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval,
- guid_clean_up_alarm_.get());
+ epoll_server_->RegisterAlarmApproximateDelta(
+ next_alarm_interval, connection_id_clean_up_alarm_.get());
}
-void QuicTimeWaitListManager::CleanUpOldGuids() {
+void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
QuicTime now = clock_.ApproximateNow();
- while (time_ordered_guid_list_.size() > 0) {
- DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size());
- GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
- if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) {
+ while (!connection_id_map_.empty()) {
+ ConnectionIdMap::iterator it = connection_id_map_.begin();
+ QuicTime oldest_connection_id = it->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
break;
}
- // This guid has lived its age, retire it now.
- GuidMapIterator it = guid_map_.find(oldest_guid->guid);
- DCHECK(it != guid_map_.end());
+ // This connection_id has lived its age, retire it now.
delete it->second.close_packet;
- guid_map_.erase(oldest_guid->guid);
- time_ordered_guid_list_.pop_front();
- delete oldest_guid;
+ connection_id_map_.erase(it);
}
- SetGuidCleanUpAlarm();
+ SetConnectionIdCleanUpAlarm();
}
} // namespace tools
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.h b/chromium/net/tools/quic/quic_time_wait_list_manager.h
index bb24f144a02..426f09f0872 100644
--- a/chromium/net/tools/quic/quic_time_wait_list_manager.h
+++ b/chromium/net/tools/quic/quic_time_wait_list_manager.h
@@ -2,195 +2,175 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Handles packets for guids in time wait state by discarding the packet and
-// sending the clients a public reset packet with exponential backoff.
+// Handles packets for connection_ids in time wait state by discarding the
+// packet and sending the clients a public reset packet with exponential
+// backoff.
#ifndef NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
#define NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
#include <deque>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/strings/string_piece.h"
+#include "net/base/linked_hash_map.h"
#include "net/quic/quic_blocked_writer_interface.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_protocol.h"
-#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_epoll_clock.h"
namespace net {
+
+class EpollServer;
+
namespace tools {
-class GuidCleanUpAlarm;
+class ConnectionIdCleanUpAlarm;
+class QuicServerSessionVisitor;
namespace test {
class QuicTimeWaitListManagerPeer;
} // namespace test
-// Maintains a list of all guids that have been recently closed. A guid lives in
-// this state for kTimeWaitPeriod. All packets received for guids in this state
-// are handed over to the QuicTimeWaitListManager by the QuicDispatcher.
-// Decides whether to send a public reset packet, a copy of the previously sent
-// connection close packet, or nothing to the client which sent a packet
-// with the guid in time wait state. After the guid expires its time wait
-// period, a new connection/session will be created if a packet is received
-// for this guid.
-class QuicTimeWaitListManager : public QuicBlockedWriterInterface,
- public QuicFramerVisitorInterface {
+// Maintains a list of all connection_ids that have been recently closed. A
+// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// for connection_ids in this state are handed over to the
+// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a
+// public reset packet, a copy of the previously sent connection close packet,
+// or nothing to the client which sent a packet with the connection_id in time
+// wait state. After the connection_id expires its time wait period, a new
+// connection/session will be created if a packet is received for this
+// connection_id.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface {
public:
// writer - the entity that writes to the socket. (Owned by the dispatcher)
+ // visitor - the entity that manages blocked writers. (The dispatcher)
// epoll_server - used to run clean up alarms. (Owned by the dispatcher)
QuicTimeWaitListManager(QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
EpollServer* epoll_server,
const QuicVersionVector& supported_versions);
virtual ~QuicTimeWaitListManager();
- // Adds the given guid to time wait state for kTimeWaitPeriod. Henceforth,
- // any packet bearing this guid should not be processed while the guid remains
- // in this list. If a non-NULL |close_packet| is provided, it is sent again
- // when packets are received for added guids. If NULL, a public reset packet
- // is sent with the specified |version|. DCHECKs that guid is not already on
- // the list.
- void AddGuidToTimeWait(QuicGuid guid,
- QuicVersion version,
- QuicEncryptedPacket* close_packet); // Owned.
-
- // Returns true if the guid is in time wait state, false otherwise. Packets
- // received for this guid should not lead to creation of new QuicSessions.
- bool IsGuidInTimeWait(QuicGuid guid) const;
-
- // Called when a packet is received for a guid that is in time wait state.
- // Sends a public reset packet to the client which sent this guid. Sending
- // of the public reset packet is throttled by using exponential back off.
- // DCHECKs for the guid to be in time wait state.
- // virtual to override in tests.
+ // Adds the given connection_id to time wait state for kTimeWaitPeriod.
+ // Henceforth, any packet bearing this connection_id should not be processed
+ // while the connection_id remains in this list. If a non-NULL |close_packet|
+ // is provided, it is sent again when packets are received for added
+ // connection_ids. If NULL, a public reset packet is sent with the specified
+ // |version|. DCHECKs that connection_id is not already on the list.
+ void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet); // Owned.
+
+ // Returns true if the connection_id is in time wait state, false otherwise.
+ // Packets received for this connection_id should not lead to creation of new
+ // QuicSessions.
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const;
+
+ // Called when a packet is received for a connection_id that is in time wait
+ // state. Sends a public reset packet to the client which sent this
+ // connection_id. Sending of the public reset packet is throttled by using
+ // exponential back off. DCHECKs for the connection_id to be in time wait
+ // state. virtual to override in tests.
virtual void ProcessPacket(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
const QuicEncryptedPacket& packet);
// Called by the dispatcher when the underlying socket becomes writable again,
// since we might need to send pending public reset packets which we didn't
// send because the underlying socket was write blocked.
- virtual bool OnCanWrite() OVERRIDE;
-
- // Used to delete guid entries that have outlived their time wait period.
- void CleanUpOldGuids();
-
- // QuicFramerVisitorInterface
- virtual void OnError(QuicFramer* framer) OVERRIDE;
- virtual bool OnProtocolVersionMismatch(QuicVersion received_version) OVERRIDE;
- virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
- virtual void OnPacket() OVERRIDE {}
- virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& /*packet*/) OVERRIDE {}
- virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {}
-
- virtual void OnPacketComplete() OVERRIDE {}
- // The following methods should never get called because we always return
- // false from OnUnauthenticatedHeader(). We never process the encrypted bytes.
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
- virtual void OnRevivedPacket() OVERRIDE;
- virtual void OnFecProtectedPayload(base::StringPiece /*payload*/) OVERRIDE;
- virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE;
- virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE;
- virtual bool OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE;
- virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE;
- virtual bool OnConnectionCloseFrame(
- const QuicConnectionCloseFrame & /*frame*/) OVERRIDE;
- virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE;
- virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE;
+ virtual void OnCanWrite() OVERRIDE;
+
+ // Used to delete connection_id entries that have outlived their time wait
+ // period.
+ void CleanUpOldConnectionIds();
+
+ // Given a ConnectionId that exists in the time wait list, returns the
+ // QuicVersion associated with it.
+ QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id);
+
+ protected:
+ virtual QuicEncryptedPacket* BuildPublicReset(
+ const QuicPublicResetPacket& packet);
private:
friend class test::QuicTimeWaitListManagerPeer;
- // Stores the guid and the time it was added to time wait state.
- struct GuidAddTime;
// Internal structure to store pending public reset packets.
class QueuedPacket;
- // Decides if a packet should be sent for this guid based on the number of
- // received packets.
+ // Decides if a packet should be sent for this connection_id based on the
+ // number of received packets.
bool ShouldSendResponse(int received_packet_count);
- // Given a GUID that exists in the time wait list, returns the QuicVersion
- // associated with it. Used internally to set the framer version before
- // writing the public reset packet.
- QuicVersion GetQuicVersionFromGuid(QuicGuid guid);
-
// Creates a public reset packet and sends it or queues it to be sent later.
void SendPublicReset(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
+ QuicConnectionId connection_id,
QuicPacketSequenceNumber rejected_sequence_number);
// Either sends the packet and deletes it or makes pending_packets_queue_ the
// owner of the packet.
void SendOrQueuePacket(QueuedPacket* packet);
- // Should only be called when write_blocked_ == false. We only care if the
- // writing was unsuccessful because the socket got blocked, which can be
- // tested using write_blocked_ == true. In case of all other errors we drop
- // the packet. Hence, we return void.
- void WriteToWire(QueuedPacket* packet);
+ // Sends the packet out. Returns true if the packet was successfully consumed.
+ // If the writer got blocked and did not buffer the packet, we'll need to keep
+ // the packet and retry sending. In case of all other errors we drop the
+ // packet.
+ bool WriteToWire(QueuedPacket* packet);
// Register the alarm with the epoll server to wake up at appropriate time.
- void SetGuidCleanUpAlarm();
-
- // A map from a recently closed guid to the number of packets received after
- // the termination of the connection bound to the guid.
- struct GuidData {
- GuidData(int num_packets_,
- QuicVersion version_,
- QuicEncryptedPacket* close_packet)
+ void SetConnectionIdCleanUpAlarm();
+
+ // A map from a recently closed connection_id to the number of packets
+ // received after the termination of the connection bound to the
+ // connection_id.
+ struct ConnectionIdData {
+ ConnectionIdData(int num_packets_,
+ QuicVersion version_,
+ QuicTime time_added_,
+ QuicEncryptedPacket* close_packet)
: num_packets(num_packets_),
version(version_),
+ time_added(time_added_),
close_packet(close_packet) {}
int num_packets;
QuicVersion version;
+ QuicTime time_added;
QuicEncryptedPacket* close_packet;
};
- base::hash_map<QuicGuid, GuidData> guid_map_;
- typedef base::hash_map<QuicGuid, GuidData>::iterator GuidMapIterator;
- // Maintains a list of GuidAddTime elements which it owns, in the
- // order they should be deleted.
- std::deque<GuidAddTime*> time_ordered_guid_list_;
+ // linked_hash_map allows lookup by ConnectionId and traversal in add order.
+ typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap;
+ ConnectionIdMap connection_id_map_;
// Pending public reset packets that need to be sent out to the client
// when we are given a chance to write by the dispatcher.
std::deque<QueuedPacket*> pending_packets_queue_;
- // Used to parse incoming packets.
- QuicFramer framer_;
-
- // Server and client address of the last packet processed.
- IPEndPoint server_address_;
- IPEndPoint client_address_;
-
- // Used to schedule alarms to delete old guids which have been in the list for
- // too long. Owned by the dispatcher.
+ // Used to schedule alarms to delete old connection_ids which have been in the
+ // list for too long.
EpollServer* epoll_server_;
- // Time period for which guids should remain in time wait state.
+ // Time period for which connection_ids should remain in time wait state.
const QuicTime::Delta kTimeWaitPeriod_;
- // Alarm registered with the epoll server to clean up guids that have out
- // lived their duration in time wait state.
- scoped_ptr<GuidCleanUpAlarm> guid_clean_up_alarm_;
+ // Alarm registered with the epoll server to clean up connection_ids that have
+ // out lived their duration in time wait state.
+ scoped_ptr<ConnectionIdCleanUpAlarm> connection_id_clean_up_alarm_;
// Clock to efficiently measure approximate time from the epoll server.
QuicEpollClock clock_;
- // Interface that writes given buffer to the socket. Owned by the dispatcher.
+ // Interface that writes given buffer to the socket.
QuicPacketWriter* writer_;
- // True if the underlying udp socket is write blocked, i.e will return EAGAIN
- // on sendmsg.
- bool is_write_blocked_;
+ // Interface that manages blocked writers.
+ QuicServerSessionVisitor* visitor_;
DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager);
};
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc
index 22fd8ef4d06..67abd5928f7 100644
--- a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc
+++ b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc
@@ -14,37 +14,56 @@
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.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 net::test::BuildUnsizedDataPacket;
+using net::test::NoOpFramerVisitor;
+using net::test::QuicVersionMax;
+using net::test::QuicVersionMin;
using testing::Args;
+using testing::Assign;
+using testing::DoAll;
using testing::Matcher;
using testing::MatcherInterface;
+using testing::NiceMock;
using testing::Return;
+using testing::ReturnPointee;
using testing::SetArgPointee;
+using testing::StrictMock;
using testing::Truly;
+using testing::_;
namespace net {
namespace tools {
namespace test {
-class QuicTimeWaitListManagerPeer {
+class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
public:
- static QuicVersion version(QuicTimeWaitListManager* manager) {
- return manager->framer_.version();
+ FramerVisitorCapturingPublicReset() {}
+ virtual ~FramerVisitorCapturingPublicReset() OVERRIDE {}
+
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& public_reset) OVERRIDE {
+ public_reset_packet_ = public_reset;
}
- static bool is_write_blocked(QuicTimeWaitListManager* manager) {
- return manager->is_write_blocked_;
+ const QuicPublicResetPacket public_reset_packet() {
+ return public_reset_packet_;
}
+ private:
+ QuicPublicResetPacket public_reset_packet_;
+};
+
+class QuicTimeWaitListManagerPeer {
+ public:
static bool ShouldSendResponse(QuicTimeWaitListManager* manager,
- int received_packet_count) {
+ int received_packet_count) {
return manager->ShouldSendResponse(received_packet_count);
}
@@ -52,67 +71,72 @@ class QuicTimeWaitListManagerPeer {
return manager->kTimeWaitPeriod_;
}
- static QuicVersion GetQuicVersionFromGuid(QuicTimeWaitListManager* manager,
- QuicGuid guid) {
- return manager->GetQuicVersionFromGuid(guid);
+ static QuicVersion GetQuicVersionFromConnectionId(
+ QuicTimeWaitListManager* manager,
+ QuicConnectionId connection_id) {
+ return manager->GetQuicVersionFromConnectionId(connection_id);
}
};
namespace {
-class TestTimeWaitListManager : public QuicTimeWaitListManager {
- public:
- TestTimeWaitListManager(QuicPacketWriter* writer,
- EpollServer* epoll_server)
- : QuicTimeWaitListManager(writer, epoll_server, QuicSupportedVersions()) {
- }
-};
-
class MockFakeTimeEpollServer : public FakeTimeEpollServer {
public:
MOCK_METHOD2(RegisterAlarm, void(int64 timeout_in_us,
EpollAlarmCallbackInterface* alarm));
};
-class QuicTimeWaitListManagerTest : public testing::Test {
+class QuicTimeWaitListManagerTest : public ::testing::Test {
protected:
QuicTimeWaitListManagerTest()
- : time_wait_list_manager_(
- &writer_, &epoll_server_, QuicSupportedVersions()),
+ : time_wait_list_manager_(&writer_, &visitor_,
+ &epoll_server_, QuicSupportedVersions()),
framer_(QuicSupportedVersions(), QuicTime::Zero(), true),
- guid_(45) {
- }
+ connection_id_(45),
+ client_address_(net::test::TestPeerIPAddress(), kTestPort),
+ writer_is_blocked_(false) {}
- virtual ~QuicTimeWaitListManagerTest() {}
+ virtual ~QuicTimeWaitListManagerTest() OVERRIDE {}
- void AddGuid(QuicGuid guid) {
- AddGuid(guid, net::test::QuicVersionMax(), NULL);
+ virtual void SetUp() OVERRIDE {
+ EXPECT_CALL(writer_, IsWriteBlocked())
+ .WillRepeatedly(ReturnPointee(&writer_is_blocked_));
+ EXPECT_CALL(writer_, IsWriteBlockedDataBuffered())
+ .WillRepeatedly(Return(false));
}
- void AddGuid(QuicGuid guid,
- QuicVersion version,
- QuicEncryptedPacket* packet) {
- time_wait_list_manager_.AddGuidToTimeWait(guid, version, packet);
+ void AddConnectionId(QuicConnectionId connection_id) {
+ AddConnectionId(connection_id, QuicVersionMax(), NULL);
}
- bool IsGuidInTimeWait(QuicGuid guid) {
- return time_wait_list_manager_.IsGuidInTimeWait(guid);
+ void AddConnectionId(QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* packet) {
+ time_wait_list_manager_.AddConnectionIdToTimeWait(
+ connection_id, version, packet);
}
- void ProcessPacket(QuicGuid guid, const QuicEncryptedPacket& packet) {
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) {
+ return time_wait_list_manager_.IsConnectionIdInTimeWait(connection_id);
+ }
+
+ void ProcessPacket(QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number) {
+ QuicEncryptedPacket packet(NULL, 0);
time_wait_list_manager_.ProcessPacket(server_address_,
client_address_,
- guid,
+ connection_id,
+ sequence_number,
packet);
}
QuicEncryptedPacket* ConstructEncryptedPacket(
EncryptionLevel level,
- QuicGuid guid,
+ QuicConnectionId connection_id,
QuicPacketSequenceNumber sequence_number) {
QuicPacketHeader header;
- header.public_header.guid = guid;
- header.public_header.guid_length = PACKET_8BYTE_GUID;
+ header.public_header.connection_id = connection_id;
+ header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID;
header.public_header.version_flag = false;
header.public_header.reset_flag = false;
header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER;
@@ -127,7 +151,7 @@ class QuicTimeWaitListManagerTest : public testing::Test {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
EXPECT_TRUE(packet != NULL);
QuicEncryptedPacket* encrypted = framer_.EncryptPacket(ENCRYPTION_NONE,
sequence_number,
@@ -136,26 +160,28 @@ class QuicTimeWaitListManagerTest : public testing::Test {
return encrypted;
}
- MockFakeTimeEpollServer epoll_server_;
- MockPacketWriter writer_;
+ NiceMock<MockFakeTimeEpollServer> epoll_server_;
+ StrictMock<MockPacketWriter> writer_;
+ StrictMock<MockQuicServerSessionVisitor> visitor_;
QuicTimeWaitListManager time_wait_list_manager_;
QuicFramer framer_;
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
IPEndPoint server_address_;
IPEndPoint client_address_;
+ bool writer_is_blocked_;
};
class ValidatePublicResetPacketPredicate
: public MatcherInterface<const std::tr1::tuple<const char*, int> > {
public:
- explicit ValidatePublicResetPacketPredicate(QuicGuid guid,
+ explicit ValidatePublicResetPacketPredicate(QuicConnectionId connection_id,
QuicPacketSequenceNumber number)
- : guid_(guid), sequence_number_(number) {
+ : connection_id_(connection_id), sequence_number_(number) {
}
virtual bool MatchAndExplain(
const std::tr1::tuple<const char*, int> packet_buffer,
- testing::MatchResultListener* /* listener */) const {
+ testing::MatchResultListener* /* listener */) const OVERRIDE {
FramerVisitorCapturingPublicReset visitor;
QuicFramer framer(QuicSupportedVersions(),
QuicTime::Zero(),
@@ -165,119 +191,73 @@ class ValidatePublicResetPacketPredicate
std::tr1::get<1>(packet_buffer));
framer.ProcessPacket(encrypted);
QuicPublicResetPacket packet = visitor.public_reset_packet();
- return guid_ == packet.public_header.guid &&
+ return connection_id_ == packet.public_header.connection_id &&
packet.public_header.reset_flag && !packet.public_header.version_flag &&
- sequence_number_ == packet.rejected_sequence_number;
+ sequence_number_ == packet.rejected_sequence_number &&
+ net::test::TestPeerIPAddress() == packet.client_address.address() &&
+ kTestPort == packet.client_address.port();
}
- virtual void DescribeTo(::std::ostream* os) const { }
+ virtual void DescribeTo(::std::ostream* os) const OVERRIDE {}
- virtual void DescribeNegationTo(::std::ostream* os) const { }
+ virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE {}
private:
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
QuicPacketSequenceNumber sequence_number_;
};
Matcher<const std::tr1::tuple<const char*, int> > PublicResetPacketEq(
- QuicGuid guid,
+ QuicConnectionId connection_id,
QuicPacketSequenceNumber sequence_number) {
- return MakeMatcher(new ValidatePublicResetPacketPredicate(guid,
+ return MakeMatcher(new ValidatePublicResetPacketPredicate(connection_id,
sequence_number));
}
-TEST_F(QuicTimeWaitListManagerTest, CheckGuidInTimeWait) {
- EXPECT_FALSE(IsGuidInTimeWait(guid_));
- AddGuid(guid_);
- EXPECT_TRUE(IsGuidInTimeWait(guid_));
+TEST_F(QuicTimeWaitListManagerTest, CheckConnectionIdInTimeWait) {
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_));
+ AddConnectionId(connection_id_);
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
}
TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) {
size_t kConnectionCloseLength = 100;
- AddGuid(guid_,
- net::test::QuicVersionMax(),
- new QuicEncryptedPacket(
- new char[kConnectionCloseLength], kConnectionCloseLength, true));
+ AddConnectionId(
+ connection_id_,
+ QuicVersionMax(),
+ new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true));
const int kRandomSequenceNumber = 1;
- scoped_ptr<QuicEncryptedPacket> packet(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber));
EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength,
server_address_.address(),
- client_address_,
- &time_wait_list_manager_))
+ client_address_))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
- ProcessPacket(guid_, *packet);
+ ProcessPacket(connection_id_, kRandomSequenceNumber);
}
TEST_F(QuicTimeWaitListManagerTest, SendPublicReset) {
- AddGuid(guid_);
- const int kRandomSequenceNumber = 1;
- scoped_ptr<QuicEncryptedPacket> packet(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber));
- EXPECT_CALL(writer_, WritePacket(_, _,
- server_address_.address(),
- client_address_,
- &time_wait_list_manager_))
- .With(Args<0, 1>(PublicResetPacketEq(guid_,
- kRandomSequenceNumber)))
- .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
-
- ProcessPacket(guid_, *packet);
-}
-
-TEST_F(QuicTimeWaitListManagerTest, SendPublicResetUndecryptable) {
- AddGuid(guid_);
+ AddConnectionId(connection_id_);
const int kRandomSequenceNumber = 1;
- scoped_ptr<QuicEncryptedPacket> packet(
- ConstructEncryptedPacket(
- ENCRYPTION_INITIAL, guid_, kRandomSequenceNumber));
EXPECT_CALL(writer_, WritePacket(_, _,
server_address_.address(),
- client_address_,
- &time_wait_list_manager_))
- .With(Args<0, 1>(PublicResetPacketEq(guid_,
+ client_address_))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id_,
kRandomSequenceNumber)))
- .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
-
- ProcessPacket(guid_, *packet);
-}
-
-TEST_F(QuicTimeWaitListManagerTest, DropInvalidPacket) {
- AddGuid(guid_);
- const char buffer[] = "invalid";
- QuicEncryptedPacket packet(buffer, arraysize(buffer));
- // Will get called for a valid packet since received packet count = 1 (2 ^ 0).
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0);
- ProcessPacket(guid_, packet);
-}
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 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));
- // Will get called for a data packet since received packet count = 1 (2 ^ 0).
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0);
- ProcessPacket(guid_, *public_reset_packet);
+ ProcessPacket(connection_id_, kRandomSequenceNumber);
}
TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) {
- AddGuid(guid_);
+ AddConnectionId(connection_id_);
for (int sequence_number = 1; sequence_number < 101; ++sequence_number) {
- scoped_ptr<QuicEncryptedPacket> packet(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, sequence_number));
if ((sequence_number & (sequence_number - 1)) == 0) {
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _))
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
}
- ProcessPacket(guid_, *packet);
+ ProcessPacket(connection_id_, sequence_number);
// Send public reset with exponential back off.
if ((sequence_number & (sequence_number - 1)) == 0) {
EXPECT_TRUE(QuicTimeWaitListManagerPeer::ShouldSendResponse(
@@ -289,157 +269,192 @@ TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) {
}
}
-TEST_F(QuicTimeWaitListManagerTest, CleanUpOldGuids) {
- const int kGuidCount = 100;
- const int kOldGuidCount = 31;
+TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) {
+ const int kConnectionIdCount = 100;
+ const int kOldConnectionIdCount = 31;
- // Add guids such that their expiry time is kTimeWaitPeriod_.
+ // Add connection_ids such that their expiry time is kTimeWaitPeriod_.
epoll_server_.set_now_in_usec(0);
- for (int guid = 1; guid <= kOldGuidCount; ++guid) {
- AddGuid(guid);
+ for (int connection_id = 1;
+ connection_id <= kOldConnectionIdCount;
+ ++connection_id) {
+ AddConnectionId(connection_id);
}
- // Add remaining guids such that their add time is 2 * kTimeWaitPeriod.
+ // Add remaining connection_ids such that their add time is
+ // 2 * kTimeWaitPeriod.
const QuicTime::Delta time_wait_period =
QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds());
- for (int guid = kOldGuidCount + 1; guid <= kGuidCount; ++guid) {
- AddGuid(guid);
+ for (int connection_id = kOldConnectionIdCount + 1;
+ connection_id <= kConnectionIdCount;
+ ++connection_id) {
+ AddConnectionId(connection_id);
}
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.
+ // After all the old connection_ids 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;
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ for (int connection_id = 1;
+ connection_id <= kConnectionIdCount;
+ ++connection_id) {
+ EXPECT_EQ(connection_id > kOldConnectionIdCount,
+ IsConnectionIdInTimeWait(connection_id))
+ << "kOldConnectionIdCount: " << kOldConnectionIdCount
+ << " connection_id: " << connection_id;
}
}
TEST_F(QuicTimeWaitListManagerTest, SendQueuedPackets) {
- QuicGuid guid = 1;
- AddGuid(guid);
+ QuicConnectionId connection_id = 1;
+ AddConnectionId(connection_id);
QuicPacketSequenceNumber sequence_number = 234;
- scoped_ptr<QuicEncryptedPacket> packet(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid, sequence_number));
+ scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ ENCRYPTION_NONE, connection_id, 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,
+ client_address_))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id,
sequence_number)))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
- ProcessPacket(guid, *packet);
- EXPECT_FALSE(
- QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_));
+ ProcessPacket(connection_id, sequence_number);
// 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,
+ client_address_))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id,
sequence_number)))
- .WillOnce(Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN)));
- ProcessPacket(guid, *packet);
+ .WillOnce(DoAll(
+ Assign(&writer_is_blocked_, true),
+ Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN))));
+ EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_));
+ ProcessPacket(connection_id, sequence_number);
// 3rd packet. No public reset should be sent;
- ProcessPacket(guid, *packet);
- EXPECT_TRUE(
- QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_));
+ ProcessPacket(connection_id, sequence_number);
- // write packet should not be called since already write blocked but the
+ // write packet should not be called since we are write blocked but the
// should be queued.
- QuicGuid other_guid = 2;
- AddGuid(other_guid);
+ QuicConnectionId other_connection_id = 2;
+ AddConnectionId(other_connection_id);
QuicPacketSequenceNumber other_sequence_number = 23423;
scoped_ptr<QuicEncryptedPacket> other_packet(
ConstructEncryptedPacket(
- ENCRYPTION_NONE, other_guid, other_sequence_number));
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _))
+ ENCRYPTION_NONE, other_connection_id, other_sequence_number));
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _))
.Times(0);
- ProcessPacket(other_guid, *other_packet);
+ EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_));
+ ProcessPacket(other_connection_id, other_sequence_number);
// Now expect all the write blocked public reset packets to be sent again.
+ writer_is_blocked_ = false;
EXPECT_CALL(writer_, WritePacket(_, _,
server_address_.address(),
- client_address_,
- &time_wait_list_manager_))
- .With(Args<0, 1>(PublicResetPacketEq(guid,
+ client_address_))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id,
sequence_number)))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
EXPECT_CALL(writer_, WritePacket(_, _,
server_address_.address(),
- client_address_,
- &time_wait_list_manager_))
- .With(Args<0, 1>(PublicResetPacketEq(other_guid,
+ client_address_))
+ .With(Args<0, 1>(PublicResetPacketEq(other_connection_id,
other_sequence_number)))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK,
other_packet->length())));
time_wait_list_manager_.OnCanWrite();
- EXPECT_FALSE(
- QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_));
}
-TEST_F(QuicTimeWaitListManagerTest, MakeSureFramerUsesCorrectVersion) {
+TEST_F(QuicTimeWaitListManagerTest, GetQuicVersionFromMap) {
+ const int kConnectionId1 = 123;
+ const int kConnectionId2 = 456;
+ const int kConnectionId3 = 789;
+
+ AddConnectionId(kConnectionId1, QuicVersionMin(), NULL);
+ AddConnectionId(kConnectionId2, QuicVersionMax(), NULL);
+ AddConnectionId(kConnectionId3, QuicVersionMax(), NULL);
+
+ EXPECT_EQ(QuicVersionMin(),
+ QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId(
+ &time_wait_list_manager_, kConnectionId1));
+ EXPECT_EQ(QuicVersionMax(),
+ QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId(
+ &time_wait_list_manager_, kConnectionId2));
+ EXPECT_EQ(QuicVersionMax(),
+ QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId(
+ &time_wait_list_manager_, kConnectionId3));
+}
+
+TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) {
+ // Add connection_ids such that their expiry time is kTimeWaitPeriod_.
+ epoll_server_.set_now_in_usec(0);
+ AddConnectionId(connection_id_);
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+ size_t kConnectionCloseLength = 100;
+ AddConnectionId(
+ connection_id_,
+ QuicVersionMax(),
+ new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true));
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+
+ EXPECT_CALL(writer_, WritePacket(_,
+ kConnectionCloseLength,
+ server_address_.address(),
+ client_address_))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
const int kRandomSequenceNumber = 1;
- scoped_ptr<QuicEncryptedPacket> packet;
+ ProcessPacket(connection_id_, kRandomSequenceNumber);
- AddGuid(guid_, net::test::QuicVersionMin(), NULL);
- framer_.set_version(net::test::QuicVersionMin());
- packet.reset(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber));
+ const QuicTime::Delta time_wait_period =
+ QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
- // Reset packet should be written, using the minimum quic version.
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(1)
- .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
- ProcessPacket(guid_, *packet);
- EXPECT_EQ(QuicTimeWaitListManagerPeer::version(&time_wait_list_manager_),
- net::test::QuicVersionMin());
-
- // New guid
- ++guid_;
-
- AddGuid(guid_, net::test::QuicVersionMax(), NULL);
- framer_.set_version(net::test::QuicVersionMax());
- packet.reset(
- ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber));
-
- // Reset packet should be written, using the maximum quic version.
- EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(1)
- .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
- ProcessPacket(guid_, *packet);
- EXPECT_EQ(QuicTimeWaitListManagerPeer::version(&time_wait_list_manager_),
- net::test::QuicVersionMax());
-}
+ 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 the connection_ids are cleaned up, check the next alarm interval.
+ int64 next_alarm_time = epoll_server_.ApproximateNowInUsec() +
+ time_wait_period.ToMicroseconds();
-TEST_F(QuicTimeWaitListManagerTest, GetQuicVersionFromMap) {
- const int kGuid1 = 123;
- const int kGuid2 = 456;
- const int kGuid3 = 789;
-
- AddGuid(kGuid1, net::test::QuicVersionMin(), NULL);
- AddGuid(kGuid2, net::test::QuicVersionMax(), NULL);
- AddGuid(kGuid3, net::test::QuicVersionMax(), NULL);
-
- EXPECT_EQ(net::test::QuicVersionMin(),
- QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid(
- &time_wait_list_manager_, kGuid1));
- EXPECT_EQ(net::test::QuicVersionMax(),
- QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid(
- &time_wait_list_manager_, kGuid2));
- EXPECT_EQ(net::test::QuicVersionMax(),
- QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid(
- &time_wait_list_manager_, kGuid3));
+ EXPECT_CALL(epoll_server_, RegisterAlarm(next_alarm_time, _));
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_));
}
+TEST_F(QuicTimeWaitListManagerTest, ConnectionIdsOrderedByTime) {
+ // Simple randomization: the values of connection_ids are swapped based on the
+ // current seconds on the clock. If the container is broken, the test will be
+ // 50% flaky.
+ int odd_second = static_cast<int>(epoll_server_.ApproximateNowInUsec()) % 2;
+ EXPECT_TRUE(odd_second == 0 || odd_second == 1);
+ const QuicConnectionId kConnectionId1 = odd_second;
+ const QuicConnectionId kConnectionId2 = 1 - odd_second;
+
+ // 1 will hash lower than 2, but we add it later. They should come out in the
+ // add order, not hash order.
+ epoll_server_.set_now_in_usec(0);
+ AddConnectionId(kConnectionId1);
+ epoll_server_.set_now_in_usec(10);
+ AddConnectionId(kConnectionId2);
+
+ const QuicTime::Delta time_wait_period =
+ QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
+ epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds() + 1);
+
+ EXPECT_CALL(epoll_server_, RegisterAlarm(_, _));
+
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ EXPECT_FALSE(IsConnectionIdInTimeWait(kConnectionId1));
+ EXPECT_TRUE(IsConnectionIdInTimeWait(kConnectionId2));
+}
} // namespace
} // namespace test
} // namespace tools
diff --git a/chromium/net/tools/quic/spdy_utils.cc b/chromium/net/tools/quic/spdy_utils.cc
index c350a96f9e2..7f146493671 100644
--- a/chromium/net/tools/quic/spdy_utils.cc
+++ b/chromium/net/tools/quic/spdy_utils.cc
@@ -38,8 +38,8 @@ void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
hi != headers.header_lines_end();
++hi) {
if ((hi->second.length() == 0) && !allow_empty_values) {
- DLOG(INFO) << "Dropping empty header " << hi->first.as_string()
- << " from headers";
+ DVLOG(1) << "Dropping empty header " << hi->first.as_string()
+ << " from headers";
continue;
}
@@ -157,8 +157,8 @@ string SpdyUtils::SerializeResponseHeaders(
// static
string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
- int length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
- SpdyFrameBuilder builder(length);
+ size_t length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
+ SpdyFrameBuilder builder(length, SPDY3);
SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
scoped_ptr<SpdyFrame> block(builder.take());
return string(block->data(), length);
diff --git a/chromium/net/tools/quic/spdy_utils.h b/chromium/net/tools/quic/spdy_utils.h
index cfad5f1918b..e0ae4edb905 100644
--- a/chromium/net/tools/quic/spdy_utils.h
+++ b/chromium/net/tools/quic/spdy_utils.h
@@ -37,6 +37,9 @@ class SpdyUtils {
static std::string SerializeUncompressedHeaders(
const SpdyHeaderBlock& headers);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SpdyUtils);
};
} // namespace tools
diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc b/chromium/net/tools/quic/test_tools/http_message.cc
index 70eb59290fa..9bd3cffc312 100644
--- a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc
+++ b/chromium/net/tools/quic/test_tools/http_message.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "net/tools/quic/test_tools/http_message.h"
#include <vector>
diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.h b/chromium/net/tools/quic/test_tools/http_message.h
index fefdb4909cd..6b63dafd76c 100644
--- a/chromium/net/tools/quic/test_tools/http_message_test_utils.h
+++ b/chromium/net/tools/quic/test_tools/http_message.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
-#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_
#include <string>
#include <vector>
@@ -130,4 +130,4 @@ class HTTPMessage {
} // namespace tools
} // namespace net
-#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_
diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.h b/chromium/net/tools/quic/test_tools/mock_epoll_server.h
index fbc16956d72..cdb6a36a7ce 100644
--- a/chromium/net/tools/quic/test_tools/mock_epoll_server.h
+++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.h
@@ -43,6 +43,8 @@ class FakeTimeEpollServer : public EpollServer {
private:
int64 now_in_usec_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTimeEpollServer);
};
class MockEpollServer : public FakeTimeEpollServer {
@@ -83,20 +85,22 @@ class MockEpollServer : public FakeTimeEpollServer {
protected: // functions
// These functions do nothing here, as we're not actually
// using the epoll_* syscalls.
- virtual void DelFD(int fd) const OVERRIDE { }
- virtual void AddFD(int fd, int event_mask) const OVERRIDE { }
- virtual void ModFD(int fd, int event_mask) const OVERRIDE { }
+ virtual void DelFD(int fd) const OVERRIDE {}
+ virtual void AddFD(int fd, int event_mask) const OVERRIDE {}
+ virtual void ModFD(int fd, int event_mask) const OVERRIDE {}
// Replaces the epoll_server's epoll_wait_impl.
virtual int epoll_wait_impl(int epfd,
struct epoll_event* events,
int max_events,
int timeout_in_ms) OVERRIDE;
- virtual void SetNonblocking (int fd) OVERRIDE { }
+ virtual void SetNonblocking (int fd) OVERRIDE {}
private: // members
EventQueue event_queue_;
int64 until_in_usec_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockEpollServer);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc
index 2d9c1ec184f..13271ca8afc 100644
--- a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc
+++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc
@@ -4,6 +4,8 @@
#include "net/tools/quic/test_tools/mock_quic_dispatcher.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
namespace net {
namespace tools {
namespace test {
@@ -11,11 +13,12 @@ namespace test {
MockQuicDispatcher::MockQuicDispatcher(
const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
- QuicGuid guid,
EpollServer* eps)
- : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), guid,
- eps) {
-}
+ : QuicDispatcher(config,
+ crypto_config,
+ QuicSupportedVersions(),
+ eps) {}
+
MockQuicDispatcher::~MockQuicDispatcher() {}
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h
index 5f885e875b6..d1559115bf2 100644
--- a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h
+++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h
@@ -21,15 +21,16 @@ class MockQuicDispatcher : public QuicDispatcher {
public:
MockQuicDispatcher(const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
- QuicGuid guid,
EpollServer* eps);
+
virtual ~MockQuicDispatcher();
- MOCK_METHOD5(ProcessPacket, void(const IPEndPoint& server_address,
+ MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address,
const IPEndPoint& client_address,
- QuicGuid guid,
- bool has_version_flag,
const QuicEncryptedPacket& packet));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc
index 11aafb69495..05a7385181f 100644
--- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc
+++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc
@@ -10,8 +10,6 @@
#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_socket_utils.h"
-using net::test::QuicTestWriter;
-
namespace net {
namespace tools {
namespace test {
@@ -21,11 +19,11 @@ namespace test {
class WriteUnblockedAlarm : public QuicAlarm::Delegate {
public:
explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
- : writer_(writer) { }
+ : writer_(writer) {}
virtual QuicTime OnAlarm() OVERRIDE {
- DCHECK(writer_->blocked_writer());
- writer_->blocked_writer()->OnCanWrite();
+ DVLOG(1) << "Unblocking socket.";
+ writer_->OnCanWrite();
return QuicTime::Zero();
}
@@ -37,8 +35,7 @@ class WriteUnblockedAlarm : public QuicAlarm::Delegate {
// later point.
class DelayAlarm : public QuicAlarm::Delegate {
public:
- explicit DelayAlarm(PacketDroppingTestWriter* writer)
- : writer_(writer) { }
+ explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
virtual QuicTime OnAlarm() OVERRIDE {
return writer_->ReleaseOldPackets();
@@ -50,7 +47,6 @@ class DelayAlarm : public QuicAlarm::Delegate {
PacketDroppingTestWriter::PacketDroppingTestWriter()
: clock_(NULL),
- blocked_writer_(NULL),
cur_buffer_size_(0),
config_mutex_(),
fake_packet_loss_percentage_(0),
@@ -60,45 +56,45 @@ PacketDroppingTestWriter::PacketDroppingTestWriter()
fake_bandwidth_(QuicBandwidth::Zero()),
buffer_size_(0) {
uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
- LOG(INFO) << "Seeding packet loss with " << seed;
+ VLOG(1) << "Seeding packet loss with " << seed;
simple_random_.set_seed(seed);
}
-PacketDroppingTestWriter::~PacketDroppingTestWriter() { }
+PacketDroppingTestWriter::~PacketDroppingTestWriter() {}
-void PacketDroppingTestWriter::SetConnectionHelper(
- QuicEpollConnectionHelper* helper) {
+void PacketDroppingTestWriter::Initialize(
+ QuicEpollConnectionHelper* helper,
+ Delegate* on_can_write) {
clock_ = helper->GetClock();
write_unblocked_alarm_.reset(
helper->CreateAlarm(new WriteUnblockedAlarm(this)));
delay_alarm_.reset(
helper->CreateAlarm(new DelayAlarm(this)));
+ on_can_write_.reset(on_can_write);
}
WriteResult PacketDroppingTestWriter::WritePacket(
- const char* buffer, size_t buf_len,
+ const char* buffer,
+ size_t buf_len,
const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) {
+ const net::IPEndPoint& peer_address) {
ReleaseOldPackets();
base::AutoLock locked(config_mutex_);
if (fake_packet_loss_percentage_ > 0 &&
simple_random_.RandUint64() % 100 <
static_cast<uint64>(fake_packet_loss_percentage_)) {
- DLOG(INFO) << "Dropping packet.";
+ DVLOG(1) << "Dropping packet.";
return WriteResult(WRITE_STATUS_OK, buf_len);
}
if (fake_blocked_socket_percentage_ > 0 &&
simple_random_.RandUint64() % 100 <
static_cast<uint64>(fake_blocked_socket_percentage_)) {
- DLOG(INFO) << "Blocking socket.";
+ CHECK(on_can_write_.get() != NULL);
+ DVLOG(1) << "Blocking socket.";
if (!write_unblocked_alarm_->IsSet()) {
- blocked_writer_ = blocked_writer;
- // Set the alarm for 1ms in the future.
- write_unblocked_alarm_->Set(
- clock_->ApproximateNow().Add(
- QuicTime::Delta::FromMilliseconds(1)));
+ // Set the alarm to fire immediately.
+ write_unblocked_alarm_->Set(clock_->ApproximateNow());
}
return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
}
@@ -106,7 +102,7 @@ WriteResult PacketDroppingTestWriter::WritePacket(
if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
// Drop packets which do not fit into the buffer.
- DLOG(INFO) << "Dropping packet because the buffer is full.";
+ DVLOG(1) << "Dropping packet because the buffer is full.";
return WriteResult(WRITE_STATUS_OK, buf_len);
}
@@ -133,12 +129,22 @@ WriteResult PacketDroppingTestWriter::WritePacket(
return WriteResult(WRITE_STATUS_OK, buf_len);
}
- return writer()->WritePacket(buffer, buf_len, self_address, peer_address,
- blocked_writer);
+ return QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_address, peer_address);
}
-bool PacketDroppingTestWriter::IsWriteBlockedDataBuffered() const {
- return false;
+bool PacketDroppingTestWriter::IsWriteBlocked() const {
+ if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
+ return true;
+ }
+ return QuicPacketWriterWrapper::IsWriteBlocked();
+}
+
+void PacketDroppingTestWriter::SetWritable() {
+ if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
+ write_unblocked_alarm_->Cancel();
+ }
+ QuicPacketWriterWrapper::SetWritable();
}
QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
@@ -151,17 +157,18 @@ QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
simple_random_.RandUint64() % 100 <
static_cast<uint64>(fake_packet_reorder_percentage_)) {
- DLOG(INFO) << "Reordering packets.";
+ DVLOG(1) << "Reordering packets.";
++iter;
// Swap the send times when re-ordering packets.
delayed_packets_.begin()->send_time = iter->send_time;
}
- DLOG(INFO) << "Releasing packet. " << (delayed_packets_.size() - 1)
- << " remaining.";
+ DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
+ << " remaining.";
// Grab the next one off the queue and send it.
- writer()->WritePacket(iter->buffer.data(), iter->buffer.length(),
- iter->self_address, iter->peer_address, NULL);
+ QuicPacketWriterWrapper::WritePacket(
+ iter->buffer.data(), iter->buffer.length(),
+ iter->self_address, iter->peer_address);
DCHECK_GE(cur_buffer_size_, iter->buffer.length());
cur_buffer_size_ -= iter->buffer.length();
delayed_packets_.erase(iter);
@@ -184,6 +191,10 @@ QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
return QuicTime::Zero();
}
+void PacketDroppingTestWriter::OnCanWrite() {
+ on_can_write_->OnCanWrite();
+}
+
PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
const char* buffer,
size_t buf_len,
@@ -193,8 +204,7 @@ PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
: buffer(buffer, buf_len),
self_address(self_address),
peer_address(peer_address),
- send_time(send_time) {
-}
+ send_time(send_time) {}
PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() {}
diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h
index 2a736e0cfe2..35097229cf3 100644
--- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h
+++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h
@@ -7,14 +7,14 @@
#include <list>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "net/quic/quic_alarm.h"
-#include "net/quic/quic_blocked_writer_interface.h"
-#include "net/quic/quic_packet_writer.h"
-#include "net/quic/test_tools/quic_test_writer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "net/tools/quic/quic_epoll_clock.h"
+#include "net/tools/quic/quic_packet_writer_wrapper.h"
#include "net/tools/quic/test_tools/quic_test_client.h"
#include "net/tools/quic/test_tools/quic_test_utils.h"
@@ -25,29 +25,41 @@ namespace test {
// Simulates a connection that drops packets a configured percentage of the time
// and has a blocked socket a configured percentage of the time. Also provides
// the options to delay packets and reorder packets if delay is enabled.
-class PacketDroppingTestWriter : public net::test::QuicTestWriter {
+class PacketDroppingTestWriter : public QuicPacketWriterWrapper {
public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void OnCanWrite() = 0;
+ };
+
PacketDroppingTestWriter();
virtual ~PacketDroppingTestWriter();
- void SetConnectionHelper(QuicEpollConnectionHelper* helper);
+ // Must be called before blocking, reordering or delaying (loss is OK). May be
+ // called after connecting if the helper is not available before.
+ // |on_can_write| will be triggered when fake-unblocking; ownership will be
+ // assumed.
+ void Initialize(QuicEpollConnectionHelper* helper, Delegate* on_can_write);
// QuicPacketWriter methods:
virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
+ const char* buffer,
+ size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE;
+ const IPEndPoint& peer_address) OVERRIDE;
+
+ virtual bool IsWriteBlocked() const OVERRIDE;
- virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
// Writes out any packet which should have been sent by now
// to the contained writer and returns the time
// for the next delayed packet to be written.
QuicTime ReleaseOldPackets();
- QuicBlockedWriterInterface* blocked_writer() { return blocked_writer_; }
+ void OnCanWrite();
// The percent of time a packet is simulated as being lost.
void set_fake_packet_loss_percentage(int32 fake_packet_loss_percentage) {
@@ -102,7 +114,7 @@ class PacketDroppingTestWriter : public net::test::QuicTestWriter {
QuicTime ReleaseNextPacket();
// A single packet which will be sent at the supplied send_time.
- class DelayedWrite {
+ struct DelayedWrite {
public:
DelayedWrite(const char* buffer,
size_t buf_len,
@@ -122,8 +134,8 @@ class PacketDroppingTestWriter : public net::test::QuicTestWriter {
const QuicClock* clock_;
scoped_ptr<QuicAlarm> write_unblocked_alarm_;
scoped_ptr<QuicAlarm> delay_alarm_;
- QuicBlockedWriterInterface* blocked_writer_;
- SimpleRandom simple_random_;
+ scoped_ptr<Delegate> on_can_write_;
+ net::test::SimpleRandom simple_random_;
// Stored packets delayed by fake packet delay or bandwidth restrictions.
DelayedPacketList delayed_packets_;
QuicByteCount cur_buffer_size_;
diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.cc b/chromium/net/tools/quic/test_tools/quic_client_peer.cc
index 25fdb7eedc5..89f1c82650c 100644
--- a/chromium/net/tools/quic/test_tools/quic_client_peer.cc
+++ b/chromium/net/tools/quic/test_tools/quic_client_peer.cc
@@ -11,8 +11,18 @@ namespace tools {
namespace test {
// static
-int QuicClientPeer::GetFd(QuicClient* client) {
- return client->fd_;
+QuicCryptoClientConfig* QuicClientPeer::GetCryptoConfig(QuicClient* client) {
+ return &client->crypto_config_;
+}
+
+// static
+bool QuicClientPeer::CreateUDPSocket(QuicClient* client) {
+ return client->CreateUDPSocket();
+}
+
+// static
+void QuicClientPeer::SetClientPort(QuicClient* client, int port) {
+ client->client_address_ = IPEndPoint(client->client_address_.address(), port);
}
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.h b/chromium/net/tools/quic/test_tools/quic_client_peer.h
index 016120aa8bf..b26fc6d829c 100644
--- a/chromium/net/tools/quic/test_tools/quic_client_peer.h
+++ b/chromium/net/tools/quic/test_tools/quic_client_peer.h
@@ -5,7 +5,12 @@
#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+#include "base/basictypes.h"
+
namespace net {
+
+class QuicCryptoClientConfig;
+
namespace tools {
class QuicClient;
@@ -14,7 +19,12 @@ namespace test {
class QuicClientPeer {
public:
- static int GetFd(QuicClient* client);
+ static QuicCryptoClientConfig* GetCryptoConfig(QuicClient* client);
+ static bool CreateUDPSocket(QuicClient* client);
+ static void SetClientPort(QuicClient* client, int port);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientPeer);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc
index c96eafd57e5..cd27802f512 100644
--- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc
+++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc
@@ -5,8 +5,7 @@
#include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
#include "net/tools/quic/quic_dispatcher.h"
-
-using net::test::QuicTestWriter;
+#include "net/tools/quic/quic_packet_writer_wrapper.h"
namespace net {
namespace tools {
@@ -20,13 +19,8 @@ void QuicDispatcherPeer::SetTimeWaitListManager(
}
// static
-void QuicDispatcherPeer::SetWriteBlocked(QuicDispatcher* dispatcher) {
- dispatcher->write_blocked_ = true;
-}
-
-// static
void QuicDispatcherPeer::UseWriter(QuicDispatcher* dispatcher,
- QuicTestWriter* writer) {
+ QuicPacketWriterWrapper* writer) {
writer->set_writer(dispatcher->writer_.release());
dispatcher->writer_.reset(writer);
}
@@ -42,6 +36,17 @@ QuicEpollConnectionHelper* QuicDispatcherPeer::GetHelper(
return dispatcher->helper_.get();
}
+// static
+QuicConnection* QuicDispatcherPeer::CreateQuicConnection(
+ QuicDispatcher* dispatcher,
+ QuicConnectionId connection_id,
+ const IPEndPoint& server,
+ const IPEndPoint& client) {
+ return dispatcher->CreateQuicConnection(connection_id,
+ server,
+ client);
+}
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h
index f463453f17a..45125c251d2 100644
--- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h
+++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h
@@ -5,11 +5,15 @@
#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
-#include "net/quic/test_tools/quic_test_writer.h"
#include "net/tools/quic/quic_dispatcher.h"
+#include "net/base/ip_endpoint.h"
+
namespace net {
namespace tools {
+
+class QuicPacketWriterWrapper;
+
namespace test {
class QuicDispatcherPeer {
@@ -18,14 +22,22 @@ class QuicDispatcherPeer {
QuicDispatcher* dispatcher,
QuicTimeWaitListManager* time_wait_list_manager);
- static void SetWriteBlocked(QuicDispatcher* dispatcher);
-
+ // Injects |writer| into |dispatcher| as the top level writer.
static void UseWriter(QuicDispatcher* dispatcher,
- net::test::QuicTestWriter* writer);
+ QuicPacketWriterWrapper* writer);
static QuicPacketWriter* GetWriter(QuicDispatcher* dispatcher);
static QuicEpollConnectionHelper* GetHelper(QuicDispatcher* dispatcher);
+
+ static QuicConnection* CreateQuicConnection(
+ QuicDispatcher* dispatcher,
+ QuicConnectionId connection_id,
+ const IPEndPoint& server,
+ const IPEndPoint& client);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicDispatcherPeer);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/quic_server_peer.cc b/chromium/net/tools/quic/test_tools/quic_server_peer.cc
index 15f31297d17..33a086f2096 100644
--- a/chromium/net/tools/quic/test_tools/quic_server_peer.cc
+++ b/chromium/net/tools/quic/test_tools/quic_server_peer.cc
@@ -28,11 +28,6 @@ QuicDispatcher* QuicServerPeer::GetDispatcher(QuicServer* server) {
return server->dispatcher_.get();
}
-// static
-int QuicServerPeer::GetFD(QuicServer* server) {
- return server->fd_;
-}
-
} // namespace test
} // namespace tools
} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_server_peer.h b/chromium/net/tools/quic/test_tools/quic_server_peer.h
index 65e2c5e96b2..f5f625db74f 100644
--- a/chromium/net/tools/quic/test_tools/quic_server_peer.h
+++ b/chromium/net/tools/quic/test_tools/quic_server_peer.h
@@ -5,6 +5,8 @@
#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
+#include "base/basictypes.h"
+
namespace net {
namespace tools {
@@ -19,7 +21,9 @@ class QuicServerPeer {
static bool SetSmallSocket(QuicServer* server);
static void DisableRecvmmsg(QuicServer* server);
static QuicDispatcher* GetDispatcher(QuicServer* server);
- static int GetFD(QuicServer* server);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicServerPeer);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.cc b/chromium/net/tools/quic/test_tools/quic_test_client.cc
index 57edadeec70..3e77bb51d6d 100644
--- a/chromium/net/tools/quic/test_tools/quic_test_client.cc
+++ b/chromium/net/tools/quic/test_tools/quic_test_client.cc
@@ -10,39 +10,49 @@
#include "net/cert/cert_verify_result.h"
#include "net/cert/x509_certificate.h"
#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
#include "net/tools/balsa/balsa_headers.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_packet_writer_wrapper.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
-#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "net/tools/quic/test_tools/http_message.h"
+#include "net/tools/quic/test_tools/quic_client_peer.h"
#include "url/gurl.h"
using base::StringPiece;
+using net::QuicServerId;
using net::test::QuicConnectionPeer;
-using net::test::QuicTestWriter;
+using net::test::QuicSessionPeer;
+using net::test::ReliableQuicStreamPeer;
using std::string;
using std::vector;
+namespace net {
+namespace tools {
+namespace test {
namespace {
// RecordingProofVerifier accepts any certificate chain and records the common
// name of the leaf.
-class RecordingProofVerifier : public net::ProofVerifier {
+class RecordingProofVerifier : public ProofVerifier {
public:
// ProofVerifier interface.
- virtual net::ProofVerifier::Status VerifyProof(
+ virtual QuicAsyncStatus VerifyProof(
const string& hostname,
const string& server_config,
const vector<string>& certs,
const string& signature,
+ const ProofVerifyContext* context,
string* error_details,
- scoped_ptr<net::ProofVerifyDetails>* details,
- net::ProofVerifierCallback* callback) OVERRIDE {
- delete callback;
-
+ scoped_ptr<ProofVerifyDetails>* details,
+ ProofVerifierCallback* callback) OVERRIDE {
common_name_.clear();
if (certs.empty()) {
- return FAILURE;
+ return QUIC_FAILURE;
}
// Convert certs to X509Certificate.
@@ -53,11 +63,11 @@ class RecordingProofVerifier : public net::ProofVerifier {
scoped_refptr<net::X509Certificate> cert =
net::X509Certificate::CreateFromDERCertChain(cert_pieces);
if (!cert.get()) {
- return FAILURE;
+ return QUIC_FAILURE;
}
common_name_ = cert->subject().GetDisplayName();
- return SUCCESS;
+ return QUIC_SUCCESS;
}
const string& common_name() const { return common_name_; }
@@ -68,10 +78,6 @@ class RecordingProofVerifier : public net::ProofVerifier {
} // anonymous namespace
-namespace net {
-namespace tools {
-namespace test {
-
BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers,
bool secure) {
StringPiece uri = const_headers->request_uri();
@@ -94,101 +100,130 @@ BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers,
return headers;
}
-// A quic client which allows mocking out writes.
-class QuicEpollClient : public QuicClient {
- public:
- typedef QuicClient Super;
-
- QuicEpollClient(IPEndPoint server_address,
- const string& server_hostname,
- const QuicVersionVector& supported_versions)
- : Super(server_address, server_hostname, supported_versions, false),
- override_guid_(0), test_writer_(NULL) {
- }
-
- QuicEpollClient(IPEndPoint server_address,
- const string& server_hostname,
- const QuicConfig& config,
- const QuicVersionVector& supported_versions)
- : Super(server_address, server_hostname, config, supported_versions),
- override_guid_(0), test_writer_(NULL) {
- }
-
- virtual ~QuicEpollClient() {
- if (connected()) {
- Disconnect();
- }
- }
-
- virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE {
- QuicPacketWriter* writer = Super::CreateQuicPacketWriter();
- if (!test_writer_) {
- return writer;
- }
- test_writer_->set_writer(writer);
- return test_writer_;
+MockableQuicClient::MockableQuicClient(
+ IPEndPoint server_address,
+ const QuicServerId& server_id,
+ const QuicVersionVector& supported_versions,
+ EpollServer* epoll_server)
+ : QuicClient(server_address,
+ server_id,
+ supported_versions,
+ false,
+ epoll_server),
+ override_connection_id_(0),
+ test_writer_(NULL) {}
+
+MockableQuicClient::MockableQuicClient(
+ IPEndPoint server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const QuicVersionVector& supported_versions,
+ EpollServer* epoll_server)
+ : QuicClient(server_address,
+ server_id,
+ supported_versions,
+ false,
+ config,
+ epoll_server),
+ override_connection_id_(0),
+ test_writer_(NULL) {}
+
+MockableQuicClient::~MockableQuicClient() {
+ if (connected()) {
+ Disconnect();
}
+}
- virtual QuicGuid GenerateGuid() OVERRIDE {
- return override_guid_ ? override_guid_ : Super::GenerateGuid();
+QuicPacketWriter* MockableQuicClient::CreateQuicPacketWriter() {
+ QuicPacketWriter* writer = QuicClient::CreateQuicPacketWriter();
+ if (!test_writer_) {
+ return writer;
}
+ test_writer_->set_writer(writer);
+ return test_writer_;
+}
- // Takes ownership of writer.
- void UseWriter(QuicTestWriter* writer) { test_writer_ = writer; }
-
- void UseGuid(QuicGuid guid) {
- override_guid_ = guid;
- }
+QuicConnectionId MockableQuicClient::GenerateConnectionId() {
+ return override_connection_id_ ? override_connection_id_
+ : QuicClient::GenerateConnectionId();
+}
- private:
- QuicGuid override_guid_; // GUID to use, if nonzero
- QuicTestWriter* test_writer_;
-};
+// Takes ownership of writer.
+void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ CHECK(test_writer_ == NULL);
+ test_writer_ = writer;
+}
-QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname,
- const QuicVersionVector& supported_versions)
- : client_(new QuicEpollClient(address, hostname, supported_versions)) {
- Initialize(address, hostname, true);
+void MockableQuicClient::UseConnectionId(QuicConnectionId connection_id) {
+ override_connection_id_ = connection_id;
}
-QuicTestClient::QuicTestClient(IPEndPoint address,
- const string& hostname,
+QuicTestClient::QuicTestClient(IPEndPoint server_address,
+ const string& server_hostname,
+ const QuicVersionVector& supported_versions)
+ : client_(new MockableQuicClient(server_address,
+ QuicServerId(server_hostname,
+ server_address.port(),
+ false,
+ PRIVACY_MODE_DISABLED),
+ supported_versions,
+ &epoll_server_)) {
+ Initialize(true);
+}
+
+QuicTestClient::QuicTestClient(IPEndPoint server_address,
+ const string& server_hostname,
bool secure,
const QuicVersionVector& supported_versions)
- : client_(new QuicEpollClient(address, hostname, supported_versions)) {
- Initialize(address, hostname, secure);
+ : client_(new MockableQuicClient(server_address,
+ QuicServerId(server_hostname,
+ server_address.port(),
+ secure,
+ PRIVACY_MODE_DISABLED),
+ supported_versions,
+ &epoll_server_)) {
+ Initialize(secure);
+}
+
+QuicTestClient::QuicTestClient(
+ IPEndPoint server_address,
+ const string& server_hostname,
+ bool secure,
+ const QuicConfig& config,
+ const QuicVersionVector& supported_versions)
+ : client_(
+ new MockableQuicClient(server_address,
+ QuicServerId(server_hostname,
+ server_address.port(),
+ secure,
+ PRIVACY_MODE_DISABLED),
+ config,
+ supported_versions,
+ &epoll_server_)) {
+ Initialize(secure);
+}
+
+QuicTestClient::QuicTestClient() {
}
-QuicTestClient::QuicTestClient(IPEndPoint address,
- const string& hostname,
- bool secure,
- const QuicConfig& config,
- const QuicVersionVector& supported_versions)
- : client_(new QuicEpollClient(address, hostname, config,
- supported_versions)) {
- Initialize(address, hostname, secure);
+QuicTestClient::~QuicTestClient() {
+ if (stream_) {
+ stream_->set_visitor(NULL);
+ }
}
-void QuicTestClient::Initialize(IPEndPoint address,
- const string& hostname,
- bool secure) {
- server_address_ = address;
+void QuicTestClient::Initialize(bool secure) {
priority_ = 3;
connect_attempted_ = false;
secure_ = secure;
auto_reconnect_ = false;
buffer_body_ = true;
+ fec_policy_ = FEC_PROTECT_OPTIONAL;
proof_verifier_ = NULL;
ClearPerRequestState();
ExpectCertificates(secure_);
}
-QuicTestClient::~QuicTestClient() {
- if (stream_) {
- stream_->set_visitor(NULL);
- }
-}
-
void QuicTestClient::ExpectCertificates(bool on) {
if (on) {
proof_verifier_ = new RecordingProofVerifier;
@@ -199,8 +234,14 @@ void QuicTestClient::ExpectCertificates(bool on) {
}
}
+void QuicTestClient::SetUserAgentID(const string& user_agent_id) {
+ client_->SetUserAgentID(user_agent_id);
+}
+
ssize_t QuicTestClient::SendRequest(const string& uri) {
- HTTPMessage message(HttpConstants::HTTP_1_1, HttpConstants::GET, uri);
+ HTTPMessage message(HttpConstants::HTTP_1_1,
+ HttpConstants::GET,
+ uri);
return SendMessage(message);
}
@@ -211,7 +252,11 @@ ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) {
if (!connected()) {
GURL url(message.headers()->request_uri().as_string());
if (!url.host().empty()) {
- client_->set_server_hostname(url.host());
+ client_->set_server_id(
+ QuicServerId(url.host(),
+ url.EffectiveIntPort(),
+ url.SchemeIs("https"),
+ PRIVACY_MODE_DISABLED));
}
}
@@ -236,6 +281,34 @@ ssize_t QuicTestClient::SendData(string data, bool last_data) {
return data.length();
}
+bool QuicTestClient::response_complete() const {
+ return response_complete_;
+}
+
+int QuicTestClient::response_header_size() const {
+ return response_header_size_;
+}
+
+int64 QuicTestClient::response_body_size() const {
+ return response_body_size_;
+}
+
+bool QuicTestClient::buffer_body() const {
+ return buffer_body_;
+}
+
+void QuicTestClient::set_buffer_body(bool buffer_body) {
+ buffer_body_ = buffer_body;
+}
+
+bool QuicTestClient::ServerInLameDuckMode() const {
+ return false;
+}
+
+const string& QuicTestClient::response_body() {
+ return response_;
+}
+
string QuicTestClient::SendCustomSynchronousRequest(
const HTTPMessage& message) {
SendMessage(message);
@@ -268,26 +341,39 @@ QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() {
}
stream_->set_visitor(this);
reinterpret_cast<QuicSpdyClientStream*>(stream_)->set_priority(priority_);
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_, fec_policy_);
}
return stream_;
}
+QuicErrorCode QuicTestClient::connection_error() {
+ return client()->session()->error();
+}
+
+MockableQuicClient* QuicTestClient::client() { return client_.get(); }
+
const string& QuicTestClient::cert_common_name() const {
return reinterpret_cast<RecordingProofVerifier*>(proof_verifier_)
->common_name();
}
-bool QuicTestClient::connected() const {
- return client_->connected();
+QuicTagValueMap QuicTestClient::GetServerConfig() const {
+ QuicCryptoClientConfig* config =
+ QuicClientPeer::GetCryptoConfig(client_.get());
+ QuicCryptoClientConfig::CachedState* state =
+ config->LookupOrCreate(client_->server_id());
+ const CryptoHandshakeMessage* handshake_msg = state->GetServerConfig();
+ if (handshake_msg != NULL) {
+ return handshake_msg->tag_value_map();
+ } else {
+ return QuicTagValueMap();
+ }
}
-void QuicTestClient::WaitForResponse() {
- if (stream_ == NULL) {
- // The client has likely disconnected.
- return;
- }
- client_->WaitForStreamToClose(stream_->id());
+bool QuicTestClient::connected() const {
+ return client_->connected();
}
void QuicTestClient::Connect() {
@@ -328,9 +414,9 @@ void QuicTestClient::ClearPerRequestState() {
void QuicTestClient::WaitForResponseForMs(int timeout_ms) {
int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond;
- int64 old_timeout_us = client()->epoll_server()->timeout_in_us();
+ int64 old_timeout_us = epoll_server()->timeout_in_us();
if (timeout_us > 0) {
- client()->epoll_server()->set_timeout_in_us(timeout_us);
+ epoll_server()->set_timeout_in_us(timeout_us);
}
const QuicClock* clock =
QuicConnectionPeer::GetHelper(client()->session()->connection())->
@@ -343,15 +429,15 @@ void QuicTestClient::WaitForResponseForMs(int timeout_ms) {
client_->WaitForEvents();
}
if (timeout_us > 0) {
- client()->epoll_server()->set_timeout_in_us(old_timeout_us);
+ epoll_server()->set_timeout_in_us(old_timeout_us);
}
}
void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) {
int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond;
- int64 old_timeout_us = client()->epoll_server()->timeout_in_us();
+ int64 old_timeout_us = epoll_server()->timeout_in_us();
if (timeout_us > 0) {
- client()->epoll_server()->set_timeout_in_us(timeout_us);
+ epoll_server()->set_timeout_in_us(timeout_us);
}
const QuicClock* clock =
QuicConnectionPeer::GetHelper(client()->session()->connection())->
@@ -365,7 +451,7 @@ void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) {
client_->WaitForEvents();
}
if (timeout_us > 0) {
- client()->epoll_server()->set_timeout_in_us(old_timeout_us);
+ epoll_server()->set_timeout_in_us(old_timeout_us);
}
}
@@ -389,7 +475,7 @@ const BalsaHeaders* QuicTestClient::response_headers() const {
}
}
-int QuicTestClient::response_size() const {
+int64 QuicTestClient::response_size() const {
return bytes_read_;
}
@@ -413,28 +499,71 @@ void QuicTestClient::OnClose(QuicDataStream* stream) {
response_headers_complete_ = stream_->headers_decompressed();
headers_.CopyFrom(stream_->headers());
stream_error_ = stream_->stream_error();
- bytes_read_ = stream_->stream_bytes_read();
- bytes_written_ = stream_->stream_bytes_written();
+ bytes_read_ = stream_->stream_bytes_read() + stream_->header_bytes_read();
+ bytes_written_ =
+ stream_->stream_bytes_written() + stream_->header_bytes_written();
response_header_size_ = headers_.GetSizeForWriteBuffer();
response_body_size_ = stream_->data().size();
stream_ = NULL;
}
-void QuicTestClient::UseWriter(QuicTestWriter* writer) {
- reinterpret_cast<QuicEpollClient*>(client_.get())->UseWriter(writer);
+void QuicTestClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ client_->UseWriter(writer);
}
-void QuicTestClient::UseGuid(QuicGuid guid) {
+void QuicTestClient::UseConnectionId(QuicConnectionId connection_id) {
DCHECK(!connected());
- reinterpret_cast<QuicEpollClient*>(client_.get())->UseGuid(guid);
+ client_->UseConnectionId(connection_id);
+}
+
+ssize_t QuicTestClient::SendAndWaitForResponse(const void *buffer,
+ size_t size) {
+ LOG(DFATAL) << "Not implemented";
+ return 0;
+}
+
+void QuicTestClient::Bind(IPEndPoint* local_address) {
+ DLOG(WARNING) << "Bind will be done during connect";
+}
+
+string QuicTestClient::SerializeMessage(const HTTPMessage& message) {
+ LOG(DFATAL) << "Not implemented";
+ return "";
+}
+
+IPAddressNumber QuicTestClient::bind_to_address() const {
+ return client_->bind_to_address();
+}
+
+void QuicTestClient::set_bind_to_address(IPAddressNumber address) {
+ client_->set_bind_to_address(address);
+}
+
+const IPEndPoint& QuicTestClient::address() const {
+ LOG(DFATAL) << "Not implemented";
+ return client_->server_address();
+}
+
+size_t QuicTestClient::requests_sent() const {
+ LOG(DFATAL) << "Not implemented";
+ return 0;
}
void QuicTestClient::WaitForWriteToFlush() {
- while (connected() && client()->session()->HasQueuedData()) {
+ while (connected() && client()->session()->HasDataToWrite()) {
client_->WaitForEvents();
}
}
+void QuicTestClient::SetFecPolicy(FecPolicy fec_policy) {
+ fec_policy_ = fec_policy;
+ // Set policy for headers and crypto streams.
+ ReliableQuicStreamPeer::SetFecPolicy(
+ QuicSessionPeer::GetHeadersStream(client()->session()), fec_policy);
+ ReliableQuicStreamPeer::SetFecPolicy(client()->session()->GetCryptoStream(),
+ fec_policy);
+}
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.h b/chromium/net/tools/quic/test_tools/quic_test_client.h
index e67a2a159bb..93c0352bca6 100644
--- a/chromium/net/tools/quic/test_tools/quic_test_client.h
+++ b/chromium/net/tools/quic/test_tools/quic_test_client.h
@@ -2,18 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
-#define NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/test_tools/quic_test_writer.h"
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_client.h"
+#include "net/tools/quic/test_tools/simple_client.h"
namespace net {
@@ -21,14 +24,46 @@ class ProofVerifier;
namespace tools {
+class QuicPacketWriterWrapper;
+
namespace test {
class HTTPMessage;
+class MockableQuicClient;
-// A toy QUIC client used for testing.
-class QuicTestClient : public QuicDataStream::Visitor {
+// A quic client which allows mocking out writes.
+class MockableQuicClient : public QuicClient {
public:
- QuicTestClient(IPEndPoint server_address, const string& server_hostname,
+ MockableQuicClient(IPEndPoint server_address,
+ const QuicServerId& server_id,
+ const QuicVersionVector& supported_versions,
+ EpollServer* epoll_server);
+
+ MockableQuicClient(IPEndPoint server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const QuicVersionVector& supported_versions,
+ EpollServer* epoll_server);
+
+ virtual ~MockableQuicClient() OVERRIDE;
+ virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE;
+ virtual QuicConnectionId GenerateConnectionId() OVERRIDE;
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ void UseConnectionId(QuicConnectionId connection_id);
+
+ private:
+ QuicConnectionId override_connection_id_; // ConnectionId to use, if nonzero
+ QuicPacketWriterWrapper* test_writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockableQuicClient);
+};
+
+// A toy QUIC client used for testing, mostly following the SimpleClient APIs.
+class QuicTestClient : public SimpleClient,
+ public QuicDataStream::Visitor {
+ public:
+ QuicTestClient(IPEndPoint server_address,
+ const string& server_hostname,
const QuicVersionVector& supported_versions);
QuicTestClient(IPEndPoint server_address,
const string& server_hostname,
@@ -47,77 +82,99 @@ class QuicTestClient : public QuicDataStream::Visitor {
// name is recorded and available with |cert_common_name()|.
void ExpectCertificates(bool on);
- // Clears any outstanding state and sends a simple GET of 'uri' to the
- // server. Returns 0 if the request failed and no bytes were written.
- ssize_t SendRequest(const string& uri);
- ssize_t SendMessage(const HTTPMessage& message);
-
- string SendCustomSynchronousRequest(const HTTPMessage& message);
- string SendSynchronousRequest(const string& uri);
+ // Sets the |user_agent_id| of the |client_|.
+ void SetUserAgentID(const string& user_agent_id);
// Wraps data in a quic packet and sends it.
ssize_t SendData(string data, bool last_data);
- QuicPacketCreator::Options* options() { return client_->options(); }
-
- void WaitForResponse();
-
- void Connect();
- void ResetConnection();
- void Disconnect();
- IPEndPoint LocalSocketAddress() const;
- void ClearPerRequestState();
- void WaitForResponseForMs(int timeout_ms);
- void WaitForInitialResponseForMs(int timeout_ms);
- ssize_t Send(const void *buffer, size_t size);
- bool response_complete() const { return response_complete_; }
- bool response_headers_complete() const;
- const BalsaHeaders* response_headers() const;
- int response_size() const;
- int response_header_size() const { return response_header_size_; }
- int response_body_size() const { return response_body_size_; }
- size_t bytes_read() const;
- size_t bytes_written() const;
- bool buffer_body() const { return buffer_body_; }
- void set_buffer_body(bool buffer_body) { buffer_body_ = buffer_body; }
+ // From SimpleClient
+ // Clears any outstanding state and sends a simple GET of 'uri' to the
+ // server. Returns 0 if the request failed and no bytes were written.
+ virtual ssize_t SendRequest(const string& uri) OVERRIDE;
+ virtual ssize_t SendMessage(const HTTPMessage& message) OVERRIDE;
+ virtual string SendCustomSynchronousRequest(
+ const HTTPMessage& message) OVERRIDE;
+ virtual string SendSynchronousRequest(const string& uri) OVERRIDE;
+ virtual void Connect() OVERRIDE;
+ virtual void ResetConnection() OVERRIDE;
+ virtual void Disconnect() OVERRIDE;
+ virtual IPEndPoint LocalSocketAddress() const OVERRIDE;
+ virtual void ClearPerRequestState() OVERRIDE;
+ virtual void WaitForResponseForMs(int timeout_ms) OVERRIDE;
+ virtual void WaitForInitialResponseForMs(int timeout_ms) OVERRIDE;
+ virtual ssize_t Send(const void *buffer, size_t size) OVERRIDE;
+ virtual bool response_complete() const OVERRIDE;
+ virtual bool response_headers_complete() const OVERRIDE;
+ virtual const BalsaHeaders* response_headers() const OVERRIDE;
+ virtual int64 response_size() const OVERRIDE;
+ virtual int response_header_size() const OVERRIDE;
+ virtual int64 response_body_size() const OVERRIDE;
+ virtual size_t bytes_read() const OVERRIDE;
+ virtual size_t bytes_written() const OVERRIDE;
+ virtual bool buffer_body() const OVERRIDE;
+ virtual void set_buffer_body(bool buffer_body) OVERRIDE;
+ virtual bool ServerInLameDuckMode() const OVERRIDE;
+ virtual const string& response_body() OVERRIDE;
+ virtual bool connected() const OVERRIDE;
+ // These functions are all unimplemented functions from SimpleClient, and log
+ // DFATAL if called by users of SimpleClient.
+ virtual ssize_t SendAndWaitForResponse(const void *buffer,
+ size_t size) OVERRIDE;
+ virtual void Bind(IPEndPoint* local_address) OVERRIDE;
+ virtual string SerializeMessage(const HTTPMessage& message) OVERRIDE;
+ virtual IPAddressNumber bind_to_address() const OVERRIDE;
+ virtual void set_bind_to_address(IPAddressNumber address) OVERRIDE;
+ virtual const IPEndPoint& address() const OVERRIDE;
+ virtual size_t requests_sent() const OVERRIDE;
// From QuicDataStream::Visitor
virtual void OnClose(QuicDataStream* stream) OVERRIDE;
// Configures client_ to take ownership of and use the writer.
// Must be called before initial connect.
- void UseWriter(net::test::QuicTestWriter* writer);
- // If the given GUID is nonzero, configures client_ to use a specific GUID
- // instead of a random one.
- void UseGuid(QuicGuid guid);
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ // If the given ConnectionId is nonzero, configures client_ to use a specific
+ // ConnectionId instead of a random one.
+ void UseConnectionId(QuicConnectionId connection_id);
// Returns NULL if the maximum number of streams have already been created.
QuicSpdyClientStream* GetOrCreateStream();
QuicRstStreamErrorCode stream_error() { return stream_error_; }
- QuicErrorCode connection_error() { return client()->session()->error(); }
+ QuicErrorCode connection_error();
- QuicClient* client() { return client_.get(); }
+ MockableQuicClient* client();
// cert_common_name returns the common name value of the server's certificate,
// or the empty string if no certificate was presented.
const string& cert_common_name() const;
- const string& response_body() {return response_;}
- bool connected() const;
+ // Get the server config map.
+ QuicTagValueMap GetServerConfig() const;
void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
void set_priority(QuicPriority priority) { priority_ = priority; }
+ // Sets client's FEC policy. This policy applies to the data stream(s), and
+ // also to the headers and crypto streams.
+ void SetFecPolicy(FecPolicy fec_policy);
+
void WaitForWriteToFlush();
- private:
- void Initialize(IPEndPoint address, const string& hostname, bool secure);
+ EpollServer* epoll_server() { return &epoll_server_; }
+
+ protected:
+ QuicTestClient();
- IPEndPoint server_address_;
- IPEndPoint client_address_;
- scoped_ptr<QuicClient> client_; // The actual client
+ void Initialize(bool secure);
+
+ void set_client(MockableQuicClient* client) { client_.reset(client); }
+
+ private:
+ EpollServer epoll_server_;
+ scoped_ptr<MockableQuicClient> client_; // The actual client
QuicSpdyClientStream* stream_;
QuicRstStreamErrorCode stream_error_;
@@ -132,7 +189,7 @@ class QuicTestClient : public QuicDataStream::Visitor {
// The number of uncompressed HTTP header bytes received.
int response_header_size_;
// The number of HTTP body bytes received.
- int response_body_size_;
+ int64 response_body_size_;
// True if we tried to connect already since the last call to Disconnect().
bool connect_attempted_;
bool secure_;
@@ -142,10 +199,13 @@ class QuicTestClient : public QuicDataStream::Visitor {
bool auto_reconnect_;
// Should we buffer the response body? Defaults to true.
bool buffer_body_;
-
+ // FEC policy for data sent by this client.
+ FecPolicy fec_policy_;
// proof_verifier_ points to a RecordingProofVerifier that is owned by
// client_.
ProofVerifier* proof_verifier_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTestClient);
};
} // namespace test
@@ -153,4 +213,4 @@ class QuicTestClient : public QuicDataStream::Visitor {
} // namespace tools
} // namespace net
-#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.cc b/chromium/net/tools/quic/test_tools/quic_test_utils.cc
index 1aad3273832..d46eae5b734 100644
--- a/chromium/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/chromium/net/tools/quic/test_tools/quic_test_utils.cc
@@ -4,47 +4,59 @@
#include "net/tools/quic/test_tools/quic_test_utils.h"
-#include "base/sha1.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/tools/quic/quic_epoll_connection_helper.h"
using base::StringPiece;
+using net::test::MakeAckFrame;
using net::test::MockHelper;
+using net::test::QuicConnectionPeer;
namespace net {
namespace tools {
namespace test {
MockConnection::MockConnection(bool is_server)
- : QuicConnection(kTestGuid,
+ : QuicConnection(kTestConnectionId,
IPEndPoint(net::test::Loopback4(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
- writer_(net::test::QuicConnectionPeer::GetWriter(this)),
+ writer_(QuicConnectionPeer::GetWriter(this)),
helper_(helper()) {
}
MockConnection::MockConnection(IPEndPoint address,
bool is_server)
- : QuicConnection(kTestGuid, address,
+ : QuicConnection(kTestConnectionId, address,
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
- writer_(net::test::QuicConnectionPeer::GetWriter(this)),
+ writer_(QuicConnectionPeer::GetWriter(this)),
helper_(helper()) {
}
-MockConnection::MockConnection(QuicGuid guid,
+MockConnection::MockConnection(QuicConnectionId connection_id,
bool is_server)
- : QuicConnection(guid,
+ : QuicConnection(connection_id,
IPEndPoint(net::test::Loopback4(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
- writer_(net::test::QuicConnectionPeer::GetWriter(this)),
+ writer_(QuicConnectionPeer::GetWriter(this)),
+ helper_(helper()) {
+}
+
+MockConnection::MockConnection(bool is_server,
+ const QuicVersionVector& supported_versions)
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(net::test::Loopback4(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ new testing::NiceMock<MockPacketWriter>(),
+ is_server, QuicSupportedVersions()),
+ writer_(QuicConnectionPeer::GetWriter(this)),
helper_(helper()) {
}
@@ -55,18 +67,21 @@ void MockConnection::AdvanceTime(QuicTime::Delta delta) {
static_cast<MockHelper*>(helper())->AdvanceTime(delta);
}
-uint64 SimpleRandom::RandUint64() {
- unsigned char hash[base::kSHA1Length];
- base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
- hash);
- memcpy(&seed_, hash, sizeof(seed_));
- return seed_;
+QuicAckFrame MakeAckFrameWithNackRanges(
+ size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked,
+ least_unacked);
+ // Add enough missing packets to get num_nack_ranges nack ranges.
+ for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) {
+ ack.received_info.missing_packets.insert(least_unacked + i);
+ }
+ return ack;
}
TestSession::TestSession(QuicConnection* connection,
const QuicConfig& config)
- : QuicSession(connection, config),
- crypto_stream_(NULL) {
+ : QuicSession(connection, config),
+ crypto_stream_(NULL) {
}
TestSession::~TestSession() {}
@@ -85,19 +100,10 @@ MockPacketWriter::MockPacketWriter() {
MockPacketWriter::~MockPacketWriter() {
}
-MockQuicSessionOwner::MockQuicSessionOwner() {
-}
-
-MockQuicSessionOwner::~MockQuicSessionOwner() {
-}
-
-bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
- data.AppendToString(&data_);
- return true;
+MockQuicServerSessionVisitor::MockQuicServerSessionVisitor() {
}
-void TestDecompressorVisitor::OnDecompressionError() {
- error_ = true;
+MockQuicServerSessionVisitor::~MockQuicServerSessionVisitor() {
}
MockAckNotifierDelegate::MockAckNotifierDelegate() {
diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.h b/chromium/net/tools/quic/test_tools/quic_test_utils.h
index 986665bd1ed..a889a42ee0d 100644
--- a/chromium/net/tools/quic/test_tools/quic_test_utils.h
+++ b/chromium/net/tools/quic/test_tools/quic_test_utils.h
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// Common utilities for Quic tests
+
#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
@@ -11,7 +13,6 @@
#include "net/quic/quic_connection.h"
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
#include "net/spdy/spdy_framer.h"
#include "net/tools/quic/quic_server_session.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -24,38 +25,31 @@ class IPEndPoint;
namespace tools {
namespace test {
-static const QuicGuid kTestGuid = 42;
+static const QuicConnectionId kTestConnectionId = 42;
static const int kTestPort = 123;
+static const uint32 kInitialStreamFlowControlWindowForTest =
+ 32 * 1024; // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+ 64 * 1024; // 64 KB
-// Simple random number generator used to compute random numbers suitable
-// for pseudo-randomly dropping packets in tests. It works by computing
-// the sha1 hash of the current seed, and using the first 64 bits as
-// the next random number, and the next seed.
-class SimpleRandom {
- public:
- SimpleRandom() : seed_(0) {}
-
- // Returns a random number in the range [0, kuint64max].
- uint64 RandUint64();
-
- void set_seed(uint64 seed) { seed_ = seed; }
-
- private:
- uint64 seed_;
-};
+// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges|
+// nack ranges of width 1 packet, starting from |least_unacked|.
+QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges,
+ QuicPacketSequenceNumber least_unacked);
class MockConnection : public QuicConnection {
public:
- // Uses a MockHelper, GUID of 42, and 127.0.0.1:123.
+ // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123.
explicit MockConnection(bool is_server);
- // Uses a MockHelper, GUID of 42.
- MockConnection(IPEndPoint address,
- bool is_server);
+ // Uses a MockHelper, ConnectionId of 42.
+ MockConnection(IPEndPoint address, bool is_server);
// Uses a MockHelper, and 127.0.0.1:123
- MockConnection(QuicGuid guid,
- bool is_server);
+ MockConnection(QuicConnectionId connection_id, bool is_server);
+
+ // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123.
+ MockConnection(bool is_server, const QuicVersionVector& supported_versions);
virtual ~MockConnection();
@@ -70,12 +64,19 @@ class MockConnection : public QuicConnection {
MOCK_METHOD2(SendConnectionCloseWithDetails, void(
QuicErrorCode error,
const std::string& details));
- MOCK_METHOD2(SendRstStream, void(QuicStreamId id,
- QuicRstStreamErrorCode error));
+ MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error,
+ const std::string& details));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const std::string& reason));
- MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id,
+ QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_CONST_METHOD0(HasPendingWrites, bool());
void ReallyProcessUdpPacket(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
@@ -102,10 +103,11 @@ class TestSession : public QuicSession {
void SetCryptoStream(QuicCryptoStream* stream);
- virtual QuicCryptoStream* GetCryptoStream();
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
private:
QuicCryptoStream* crypto_stream_;
+
DISALLOW_COPY_AND_ASSIGN(TestSession);
};
@@ -114,42 +116,46 @@ class MockPacketWriter : public QuicPacketWriter {
MockPacketWriter();
virtual ~MockPacketWriter();
- MOCK_METHOD5(WritePacket,
+ MOCK_METHOD4(WritePacket,
WriteResult(const char* buffer,
size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer));
+ const IPEndPoint& peer_address));
MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool());
-};
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
-class MockQuicSessionOwner : public QuicSessionOwner {
- public:
- MockQuicSessionOwner();
- ~MockQuicSessionOwner();
- MOCK_METHOD2(OnConnectionClosed, void(QuicGuid guid, QuicErrorCode error));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPacketWriter);
};
-class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
+class MockQuicServerSessionVisitor : public QuicServerSessionVisitor {
public:
- virtual ~TestDecompressorVisitor() {}
- virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
- virtual void OnDecompressionError() OVERRIDE;
-
- std::string data() { return data_; }
- bool error() { return error_; }
+ MockQuicServerSessionVisitor();
+ virtual ~MockQuicServerSessionVisitor();
+ MOCK_METHOD2(OnConnectionClosed, void(QuicConnectionId connection_id,
+ QuicErrorCode error));
+ MOCK_METHOD1(OnWriteBlocked, void(QuicBlockedWriterInterface* writer));
private:
- std::string data_;
- bool error_;
+ DISALLOW_COPY_AND_ASSIGN(MockQuicServerSessionVisitor);
};
class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
public:
MockAckNotifierDelegate();
+
+ MOCK_METHOD5(OnAckNotification, void(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed));
+
+ protected:
+ // Object is ref counted.
virtual ~MockAckNotifierDelegate();
- MOCK_METHOD0(OnAckNotification, void());
+ DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate);
};
} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/server_thread.cc b/chromium/net/tools/quic/test_tools/server_thread.cc
index a1bebc760d5..337cc64cd08 100644
--- a/chromium/net/tools/quic/test_tools/server_thread.cc
+++ b/chromium/net/tools/quic/test_tools/server_thread.cc
@@ -10,57 +10,62 @@ namespace net {
namespace tools {
namespace test {
-ServerThread::ServerThread(IPEndPoint address,
- const QuicConfig& config,
- const QuicVersionVector& supported_versions,
+ServerThread::ServerThread(QuicServer* server,
+ IPEndPoint address,
bool strike_register_no_startup_period)
: SimpleThread("server_thread"),
- listening_(true, false),
confirmed_(true, false),
pause_(true, false),
paused_(true, false),
resume_(true, false),
quit_(true, false),
- server_(config, supported_versions),
+ server_(server),
address_(address),
- port_(0) {
+ port_(0),
+ initialized_(false) {
if (strike_register_no_startup_period) {
- server_.SetStrikeRegisterNoStartupPeriod();
+ server_->SetStrikeRegisterNoStartupPeriod();
}
}
-ServerThread::~ServerThread() {
-}
+ServerThread::~ServerThread() {}
-void ServerThread::Run() {
- server_.Listen(address_);
+void ServerThread::Initialize() {
+ if (initialized_) {
+ return;
+ }
+
+ server_->Listen(address_);
port_lock_.Acquire();
- port_ = server_.port();
+ port_ = server_->port();
port_lock_.Release();
- listening_.Signal();
+ initialized_ = true;
+}
+
+void ServerThread::Run() {
+ if (!initialized_) {
+ Initialize();
+ }
+
while (!quit_.IsSignaled()) {
if (pause_.IsSignaled() && !resume_.IsSignaled()) {
paused_.Signal();
resume_.Wait();
}
- server_.WaitForEvents();
+ server_->WaitForEvents();
MaybeNotifyOfHandshakeConfirmation();
}
- server_.Shutdown();
+ server_->Shutdown();
}
int ServerThread::GetPort() {
port_lock_.Acquire();
int rc = port_;
port_lock_.Release();
- return rc;
-}
-
-void ServerThread::WaitForServerStartup() {
- listening_.Wait();
+ return rc;
}
void ServerThread::WaitForCryptoHandshakeConfirmed() {
diff --git a/chromium/net/tools/quic/test_tools/server_thread.h b/chromium/net/tools/quic/test_tools/server_thread.h
index ed36c37b349..6066d974d18 100644
--- a/chromium/net/tools/quic/test_tools/server_thread.h
+++ b/chromium/net/tools/quic/test_tools/server_thread.h
@@ -17,18 +17,18 @@ namespace test {
// Simple wrapper class to run server in a thread.
class ServerThread : public base::SimpleThread {
public:
- ServerThread(IPEndPoint address,
- const QuicConfig& config,
- const QuicVersionVector& supported_versions,
+ ServerThread(QuicServer* server,
+ IPEndPoint address,
bool strike_register_no_startup_period);
virtual ~ServerThread();
- // SimpleThread implementation.
- virtual void Run() OVERRIDE;
+ // Prepares the server, but does not start accepting connections. Useful for
+ // injecting mocks.
+ void Initialize();
- // Waits until the server has started and is listening for requests.
- void WaitForServerStartup();
+ // Runs the event loop. Will initialize if necessary.
+ virtual void Run() OVERRIDE;
// Waits for the handshake to be confirmed for the first session created.
void WaitForCryptoHandshakeConfirmed();
@@ -48,7 +48,7 @@ class ServerThread : public base::SimpleThread {
// Returns the underlying server. Care must be taken to avoid data races
// when accessing the server. It is always safe to access the server
// after calling Pause() and before calling Resume().
- QuicServer* server() { return &server_; }
+ QuicServer* server() { return server_.get(); }
// Returns the port that the server is listening on.
int GetPort();
@@ -56,7 +56,6 @@ class ServerThread : public base::SimpleThread {
private:
void MaybeNotifyOfHandshakeConfirmation();
- base::WaitableEvent listening_; // Notified when the server is listening.
base::WaitableEvent confirmed_; // Notified when the first handshake is
// confirmed.
base::WaitableEvent pause_; // Notified when the server should pause.
@@ -64,11 +63,13 @@ class ServerThread : public base::SimpleThread {
base::WaitableEvent resume_; // Notified when the server should resume.
base::WaitableEvent quit_; // Notified when the server should quit.
- tools::QuicServer server_;
+ scoped_ptr<QuicServer> server_;
IPEndPoint address_;
base::Lock port_lock_;
int port_;
+ bool initialized_;
+
DISALLOW_COPY_AND_ASSIGN(ServerThread);
};
diff --git a/chromium/net/tools/quic/test_tools/simple_client.cc b/chromium/net/tools/quic/test_tools/simple_client.cc
new file mode 100644
index 00000000000..46f5b9d6877
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/simple_client.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/test_tools/simple_client.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+void SimpleClient::WaitForResponse() {
+ WaitForResponseForMs(-1);
+}
+
+// Waits for some data or response from the server.
+void SimpleClient::WaitForInitialResponse() {
+ WaitForInitialResponseForMs(-1);
+}
+
+int SimpleClient::ResetSocket() {
+ LOG(FATAL) << "SimpleClient::ResetSocket is not implemented";
+ return 0;
+}
+
+int SimpleClient::HalfClose() {
+ LOG(FATAL) << "SimpleClient::HalfClose is not implemented";
+ return 0;
+}
+
+int SimpleClient::response_header_size() const { return 0; }
+
+int64 SimpleClient::response_body_size() const { return 0; }
+
+} // namespace net
+} // namespace tools
+} // namespace test
diff --git a/chromium/net/tools/quic/test_tools/simple_client.h b/chromium/net/tools/quic/test_tools/simple_client.h
new file mode 100644
index 00000000000..9277fcbf18d
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/simple_client.h
@@ -0,0 +1,159 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/ip_endpoint.h"
+#include "net/tools/balsa/balsa_frame.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+class HTTPMessage;
+
+class SimpleClient {
+ public:
+ virtual ~SimpleClient() {}
+
+ // Clears any outstanding state and sends 'size' bytes from 'buffer' to the
+ // server, possibly with multiple send operations. Returns 'size' on success
+ // and -1 on error. Callers should assume that any return value other than
+ // 'size' indicates failure.
+ virtual ssize_t Send(const void *buffer, size_t size) = 0;
+
+ // Serialize and send an HTTP request.
+ virtual ssize_t SendMessage(const HTTPMessage& message) = 0;
+
+ // Clears any outstanding state, sends 'size' bytes from 'buffer' and waits
+ // for a response or an error.
+ virtual ssize_t SendAndWaitForResponse(const void *buffer, size_t size) = 0;
+
+ // Clears any outstanding state and sends a simple GET of 'uri' to the
+ // server.
+ virtual ssize_t SendRequest(const std::string& uri) = 0;
+
+ // The response body is returned as a string.
+ virtual std::string SendCustomSynchronousRequest(
+ const HTTPMessage& message) = 0;
+ virtual std::string SendSynchronousRequest(const std::string& url) = 0;
+
+ // Returns once a complete response or a connection close has been received
+ // from the server.
+ virtual void WaitForResponse();
+
+ // Waits for some data or response from the server.
+ virtual void WaitForInitialResponse();
+
+ // Returns once a complete response or a connection close has been received
+ // from the server, or once the timeout expires. -1 for no timeout.
+ virtual void WaitForResponseForMs(int timeout_ms) = 0;
+
+ // Waits for some data or response from the server, or once the timeout
+ // expires. -1 for no timeout.
+ virtual void WaitForInitialResponseForMs(int timeout_ms) = 0;
+
+ // Clears any outstanding state from the last request.
+ virtual void ClearPerRequestState() = 0;
+
+ // Closes and reopens the connection to the server.
+ virtual void ResetConnection() = 0;
+
+ // Closes the connection to the server.
+ virtual void Disconnect() = 0;
+
+ // Both will return 0 on success, -1 otherwise.
+ // Sends out RST packet to peer.
+ // TODO(yongfa): Probably should be an interface too. LOG(FATAL) here
+ // to prevent accidental invocation.
+ virtual int ResetSocket();
+
+ virtual int HalfClose();
+
+ // Connects to the server. This should be done implicitly by Send*
+ // functions, but can be done explicitly as well.
+ virtual void Connect() = 0;
+
+ // Bind to the specified address. If set_bind_to_address() is called, this
+ // is called automatically on connect, but can be done explicitly to make
+ // LocalIPEndPoint() meaningful before actually connecting.
+ // Sets *local_address to the actual address bound to, which can be different
+ // if the given address has port 0.
+ virtual void Bind(IPEndPoint* local_address) = 0;
+
+ // Returns the local socket address of the client fd. Call only when
+ // connected.
+ // To get the local IPAdress, use LocalSocketAddress().host().
+ // To get the local port, use LocalSocketAddress.port().
+ virtual IPEndPoint LocalSocketAddress() const = 0;
+
+ // Returns the serialized message that would be sent by any of the HTTPMessage
+ // functions above.
+ virtual std::string SerializeMessage(const HTTPMessage& message) = 0;
+
+ // Sets the IP address to bind to on future Connect()s in case Bind() is not
+ // called in advance. If it's set to uninitialized IPAddress, default loopback
+ // address will be used.
+ virtual IPAddressNumber bind_to_address() const = 0;
+ virtual void set_bind_to_address(IPAddressNumber address) = 0;
+
+ // Returns true if the headers have been processed and are available.
+ virtual bool response_headers_complete() const = 0;
+
+ // Returns the response headers, if a response was completely framed.
+ // Undefined behavior otherwise.
+ virtual const BalsaHeaders* response_headers() const = 0;
+
+ // Returns true iff response has been fully received.
+ virtual bool response_complete() const = 0;
+
+ // Returns the number of bytes read from the server during this request.
+ virtual int64 response_size() const = 0;
+
+ // Returns the number of header bytes received during this request, if
+ // meaningful for the protocol.
+ virtual int response_header_size() const;
+
+ // Returns the number of body bytes received during this request, if
+ // meaningful for the protocol.
+ virtual int64 response_body_size() const;
+
+ // Returns the response body, if there was one. If there was no response, or
+ // if buffer_body() is false, returns an empty string.
+ virtual const std::string& response_body() = 0;
+
+ // The address the client is connected to.
+ virtual const IPEndPoint& address() const = 0;
+
+ // Returns true if the client is connected, false otherwise.
+ virtual bool connected() const = 0;
+
+ // Returns true if the server has informed the client that it is
+ // in "lame duck" mode, indicating intent to shut down and
+ // requesting that no further connections be established.
+ virtual bool ServerInLameDuckMode() const = 0;
+
+ // Return the number of bytes read off the wire by this client.
+ virtual size_t bytes_read() const = 0;
+
+ // Returns the number of bytes written to the wire by this client.
+ virtual size_t bytes_written() const = 0;
+
+ // Return the number of requests sent.
+ virtual size_t requests_sent() const = 0;
+
+ // Instructs the client to populate response_body().
+ virtual bool buffer_body() const = 0;
+ virtual void set_buffer_body(bool buffer_body) = 0;
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_
diff --git a/chromium/net/tools/testserver/run_testserver.cc b/chromium/net/tools/testserver/run_testserver.cc
index acf4177a8dd..717fee17a19 100644
--- a/chromium/net/tools/testserver/run_testserver.cc
+++ b/chromium/net/tools/testserver/run_testserver.cc
@@ -25,8 +25,8 @@ int main(int argc, const char* argv[]) {
base::MessageLoopForIO message_loop;
// Process command line
- CommandLine::Init(argc, argv);
- CommandLine* command_line = CommandLine::ForCurrentProcess();
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
@@ -113,7 +113,8 @@ int main(int argc, const char* argv[]) {
if (!base::DirectoryExists(test_server->document_root())) {
printf("Error: invalid doc root: \"%s\" does not exist!\n",
- UTF16ToUTF8(test_server->document_root().LossyDisplayName()).c_str());
+ base::UTF16ToUTF8(
+ test_server->document_root().LossyDisplayName()).c_str());
return -1;
}
diff --git a/chromium/net/tools/testserver/testserver.py b/chromium/net/tools/testserver/testserver.py
index 0a1f59b0c59..33faf463d3a 100755
--- a/chromium/net/tools/testserver/testserver.py
+++ b/chromium/net/tools/testserver/testserver.py
@@ -27,6 +27,7 @@ import re
import select
import socket
import SocketServer
+import ssl
import struct
import sys
import threading
@@ -38,20 +39,36 @@ import zlib
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(BASE_DIR)))
-import echo_message
-import testserver_base
+# Temporary hack to deal with tlslite 0.3.8 -> 0.4.6 upgrade.
+#
+# TODO(davidben): Remove this when it has cycled through all the bots and
+# developer checkouts or when http://crbug.com/356276 is resolved.
+try:
+ os.remove(os.path.join(ROOT_DIR, 'third_party', 'tlslite',
+ 'tlslite', 'utils', 'hmac.pyc'))
+except Exception:
+ pass
# Append at the end of sys.path, it's fine to use the system library.
sys.path.append(os.path.join(ROOT_DIR, 'third_party', 'pyftpdlib', 'src'))
-sys.path.append(os.path.join(ROOT_DIR, 'third_party', 'tlslite'))
-import pyftpdlib.ftpserver
-import tlslite
-import tlslite.api
-# Insert at the beginning of the path, we want this to be used
+# Insert at the beginning of the path, we want to use our copies of the library
# unconditionally.
sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'pywebsocket', 'src'))
+sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'tlslite'))
+
+import mod_pywebsocket.standalone
from mod_pywebsocket.standalone import WebSocketServer
+# import manually
+mod_pywebsocket.standalone.ssl = ssl
+
+import pyftpdlib.ftpserver
+
+import tlslite
+import tlslite.api
+
+import echo_message
+import testserver_base
SERVER_HTTP = 0
SERVER_FTP = 1
@@ -84,6 +101,7 @@ class WebSocketOptions:
self.certificate = None
self.tls_client_auth = False
self.tls_client_ca = None
+ self.tls_module = 'ssl'
self.use_basic_auth = False
@@ -134,10 +152,12 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
client verification."""
def __init__(self, server_address, request_hander_class, pem_cert_and_key,
- ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
+ ssl_client_auth, ssl_client_cas, ssl_client_cert_types,
+ ssl_bulk_ciphers, ssl_key_exchanges, enable_npn,
record_resume_info, tls_intolerant, signed_cert_timestamps,
fallback_scsv_enabled, ocsp_response):
- self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key)
+ self.cert_chain = tlslite.api.X509CertChain()
+ self.cert_chain.parsePemList(pem_cert_and_key)
# Force using only python implementation - otherwise behavior is different
# depending on whether m2crypto Python module is present (error is thrown
# when it is). m2crypto uses a C (based on OpenSSL) implementation under
@@ -147,19 +167,38 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
implementations=['python'])
self.ssl_client_auth = ssl_client_auth
self.ssl_client_cas = []
- self.tls_intolerant = tls_intolerant
+ self.ssl_client_cert_types = []
+ if enable_npn:
+ self.next_protos = ['http/1.1']
+ else:
+ self.next_protos = None
+ if tls_intolerant == 0:
+ self.tls_intolerant = None
+ else:
+ self.tls_intolerant = (3, tls_intolerant)
self.signed_cert_timestamps = signed_cert_timestamps
self.fallback_scsv_enabled = fallback_scsv_enabled
self.ocsp_response = ocsp_response
- for ca_file in ssl_client_cas:
- s = open(ca_file).read()
- x509 = tlslite.api.X509()
- x509.parse(s)
- self.ssl_client_cas.append(x509.subject)
+ if ssl_client_auth:
+ for ca_file in ssl_client_cas:
+ s = open(ca_file).read()
+ x509 = tlslite.api.X509()
+ x509.parse(s)
+ self.ssl_client_cas.append(x509.subject)
+
+ for cert_type in ssl_client_cert_types:
+ self.ssl_client_cert_types.append({
+ "rsa_sign": tlslite.api.ClientCertificateType.rsa_sign,
+ "dss_sign": tlslite.api.ClientCertificateType.dss_sign,
+ "ecdsa_sign": tlslite.api.ClientCertificateType.ecdsa_sign,
+ }[cert_type])
+
self.ssl_handshake_settings = tlslite.api.HandshakeSettings()
if ssl_bulk_ciphers is not None:
self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
+ if ssl_key_exchanges is not None:
+ self.ssl_handshake_settings.keyExchangeNames = ssl_key_exchanges
if record_resume_info:
# If record_resume_info is true then we'll replace the session cache with
@@ -182,6 +221,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
reqCert=self.ssl_client_auth,
settings=self.ssl_handshake_settings,
reqCAs=self.ssl_client_cas,
+ reqCertTypes=self.ssl_client_cert_types,
+ nextProtos=self.next_protos,
tlsIntolerant=self.tls_intolerant,
signedCertTimestamps=
self.signed_cert_timestamps,
@@ -288,7 +329,6 @@ class TestPageHandler(testserver_base.BasePageHandler):
self.NoContentHandler,
self.ServerRedirectHandler,
self.ClientRedirectHandler,
- self.MultipartHandler,
self.GetSSLSessionCacheHandler,
self.SSLManySmallRecords,
self.GetChannelID,
@@ -1398,29 +1438,6 @@ class TestPageHandler(testserver_base.BasePageHandler):
return True
- def MultipartHandler(self):
- """Send a multipart response (10 text/html pages)."""
-
- test_name = '/multipart'
- if not self._ShouldHandleRequest(test_name):
- return False
-
- num_frames = 10
- bound = '12345'
- self.send_response(200)
- self.send_header('Content-Type',
- 'multipart/x-mixed-replace;boundary=' + bound)
- self.end_headers()
-
- for i in xrange(num_frames):
- self.wfile.write('--' + bound + '\r\n')
- self.wfile.write('Content-Type: text/html\r\n\r\n')
- self.wfile.write('<title>page ' + str(i) + '</title>')
- self.wfile.write('page ' + str(i))
-
- self.wfile.write('--' + bound + '--')
- return True
-
def GetSSLSessionCacheHandler(self):
"""Send a reply containing a log of the session cache operations."""
@@ -1431,11 +1448,14 @@ class TestPageHandler(testserver_base.BasePageHandler):
self.send_header('Content-Type', 'text/plain')
self.end_headers()
try:
- for (action, sessionID) in self.server.session_cache.log:
- self.wfile.write('%s\t%s\n' % (action, sessionID.encode('hex')))
+ log = self.server.session_cache.log
except AttributeError:
self.wfile.write('Pass --https-record-resume in order to use' +
' this request')
+ return True
+
+ for (action, sessionID) in log:
+ self.wfile.write('%s\t%s\n' % (action, bytes(sessionID).encode('hex')))
return True
def SSLManySmallRecords(self):
@@ -1465,7 +1485,7 @@ class TestPageHandler(testserver_base.BasePageHandler):
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
- channel_id = self.server.tlsConnection.channel_id.tostring()
+ channel_id = bytes(self.server.tlsConnection.channel_id)
self.wfile.write(hashlib.sha256(channel_id).digest().encode('base64'))
return True
@@ -1956,17 +1976,22 @@ class ServerRunner(testserver_base.TestServerRunner):
server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
self.options.ssl_client_auth,
self.options.ssl_client_ca,
+ self.options.ssl_client_cert_type,
self.options.ssl_bulk_cipher,
+ self.options.ssl_key_exchange,
+ self.options.enable_npn,
self.options.record_resume,
self.options.tls_intolerant,
self.options.signed_cert_timestamps_tls_ext.decode(
"base64"),
self.options.fallback_scsv,
stapled_ocsp_response)
- print 'HTTPS server started on %s:%d...' % (host, server.server_port)
+ print 'HTTPS server started on https://%s:%d...' % \
+ (host, server.server_port)
else:
server = HTTPServer((host, port), TestPageHandler)
- print 'HTTP server started on %s:%d...' % (host, server.server_port)
+ print 'HTTP server started on http://%s:%d...' % \
+ (host, server.server_port)
server.data_dir = self.__make_data_dir()
server.file_root_url = self.options.file_root_url
@@ -1979,7 +2004,9 @@ class ServerRunner(testserver_base.TestServerRunner):
# is required to work correctly. It should be fixed from pywebsocket side.
os.chdir(self.__make_data_dir())
websocket_options = WebSocketOptions(host, port, '.')
+ scheme = "ws"
if self.options.cert_and_key_file:
+ scheme = "wss"
websocket_options.use_tls = True
websocket_options.private_key = self.options.cert_and_key_file
websocket_options.certificate = self.options.cert_and_key_file
@@ -1994,7 +2021,8 @@ class ServerRunner(testserver_base.TestServerRunner):
self.options.ssl_client_ca[0] + ' exiting...')
websocket_options.tls_client_ca = self.options.ssl_client_ca[0]
server = WebSocketServer(websocket_options)
- print 'WebSocket server started on %s:%d...' % (host, server.server_port)
+ print 'WebSocket server started on %s://%s:%d...' % \
+ (scheme, host, server.server_port)
server_data['port'] = server.server_port
elif self.options.server_type == SERVER_TCP_ECHO:
# Used for generating the key (randomly) that encodes the "echo request"
@@ -2137,6 +2165,15 @@ class ServerRunner(testserver_base.TestServerRunner):
'file. This option may appear multiple '
'times, indicating multiple CA names should '
'be sent in the request.')
+ self.option_parser.add_option('--ssl-client-cert-type', action='append',
+ default=[], help='Specify that the client '
+ 'certificate request should include the '
+ 'specified certificate_type value. This '
+ 'option may appear multiple times, '
+ 'indicating multiple values should be send '
+ 'in the request. Valid values are '
+ '"rsa_sign", "dss_sign", and "ecdsa_sign". '
+ 'If omitted, "rsa_sign" will be used.')
self.option_parser.add_option('--ssl-bulk-cipher', action='append',
help='Specify the bulk encryption '
'algorithm(s) that will be accepted by the '
@@ -2145,6 +2182,21 @@ class ServerRunner(testserver_base.TestServerRunner):
'algorithms will be used. This option may '
'appear multiple times, indicating '
'multiple algorithms should be enabled.');
+ self.option_parser.add_option('--ssl-key-exchange', action='append',
+ help='Specify the key exchange algorithm(s)'
+ 'that will be accepted by the SSL server. '
+ 'Valid values are "rsa", "dhe_rsa". If '
+ 'omitted, all algorithms will be used. This '
+ 'option may appear multiple times, '
+ 'indicating multiple algorithms should be '
+ 'enabled.');
+ # TODO(davidben): Add ALPN support to tlslite.
+ self.option_parser.add_option('--enable-npn', dest='enable_npn',
+ default=False, const=True,
+ action='store_const',
+ help='Enable server support for the NPN '
+ 'extension. The server will advertise '
+ 'support for exactly one protocol, http/1.1')
self.option_parser.add_option('--file-root-url', default='/files/',
help='Specify a root URL for files served.')
diff --git a/chromium/net/tools/testserver/testserver_base.py b/chromium/net/tools/testserver/testserver_base.py
index 455ca5c3202..0d3f65fdca9 100644
--- a/chromium/net/tools/testserver/testserver_base.py
+++ b/chromium/net/tools/testserver/testserver_base.py
@@ -14,6 +14,8 @@ import struct
import sys
import warnings
+import tlslite.errors
+
# Ignore deprecation warnings, they make our output more cluttered.
warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -71,6 +73,9 @@ class BrokenPipeHandlerMixIn:
def handle_error(self, request, client_address):
value = sys.exc_info()[1]
+ if isinstance(value, tlslite.errors.TLSClosedConnectionError):
+ print "testserver.py: Closed connection"
+ return
if isinstance(value, socket.error):
err = value.args[0]
if sys.platform in ('win32', 'cygwin'):
@@ -81,6 +86,9 @@ class BrokenPipeHandlerMixIn:
if err == pipe_err:
print "testserver.py: Broken pipe"
return
+ if err == errno.ECONNRESET:
+ print "testserver.py: Connection reset by peer"
+ return
SocketServer.BaseServer.handle_error(self, request, client_address)
diff --git a/chromium/net/tools/tld_cleanup/BUILD.gn b/chromium/net/tools/tld_cleanup/BUILD.gn
new file mode 100644
index 00000000000..bf89c008bb0
--- /dev/null
+++ b/chromium/net/tools/tld_cleanup/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2014 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.
+
+source_set("tld_cleanup") {
+ sources = [
+ "tld_cleanup_util.cc",
+ "tld_cleanup_util.h",
+ ]
+ deps = [
+ "//base",
+ "//url",
+ ]
+}
diff --git a/chromium/net/tools/tld_cleanup/PRESUBMIT.py b/chromium/net/tools/tld_cleanup/PRESUBMIT.py
new file mode 100644
index 00000000000..8391e0e61a1
--- /dev/null
+++ b/chromium/net/tools/tld_cleanup/PRESUBMIT.py
@@ -0,0 +1,32 @@
+# Copyright 2014 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.
+
+
+"""Chromium presubmit script for src/net/tools/tld_cleanup."""
+
+
+def _RunMakeDafsaTests(input_api, output_api):
+ """Runs unittest for make_dafsa if any related file has been modified."""
+ files = ('net/tools/tld_cleanup/make_dafsa.py',
+ 'net/tools/tld_cleanup/make_dafsa_unittest.py')
+ if not any(f in input_api.LocalPaths() for f in files):
+ return []
+ test_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
+ 'make_dafsa_unittest.py')
+ cmd_name = 'make_dafsa_unittest'
+ cmd = [input_api.python_executable, test_path]
+ test_cmd = input_api.Command(
+ name=cmd_name,
+ cmd=cmd,
+ kwargs={},
+ message=output_api.PresubmitPromptWarning)
+ return input_api.RunTests([test_cmd])
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return _RunMakeDafsaTests(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return _RunMakeDafsaTests(input_api, output_api)
diff --git a/chromium/net/tools/tld_cleanup/README b/chromium/net/tools/tld_cleanup/README
index a7f137d7660..13614700e42 100644
--- a/chromium/net/tools/tld_cleanup/README
+++ b/chromium/net/tools/tld_cleanup/README
@@ -20,12 +20,9 @@ When updating src/net/base/registry_controlled_domains/effective_tld_names.dat:
src/build/Debug. It will re-generate
src/net/base/registry_controlled_domains/effective_tld_names.gperf.
-6. Run gperf on the new effective_tld_names.gperf:
- pushd src/net/base/registry_controlled_domains;
- gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -P -K name_offset -D -m 10 \
- effective_tld_names.gperf > effective_tld_names.cc;
- popd;
- It will produce a new effective_tld_names.cc.
+6. Check in the updated effective_tld_names.dat, effective_tld_names.gperf
-7. Check in the updated effective_tld_names.dat, effective_tld_names.gperf,
- and effective_tld_names.cc together.
+Note that gperf is no longer used for effective_tld_names, but when building
+chromium the file effective_tld_names.gperf will be parsed by make_dafsa.py
+to generate the file effective_tld_names-inc.cc, which is included in
+registry_controlled_domain.cc
diff --git a/chromium/net/tools/tld_cleanup/make_dafsa.py b/chromium/net/tools/tld_cleanup/make_dafsa.py
new file mode 100755
index 00000000000..78358effa84
--- /dev/null
+++ b/chromium/net/tools/tld_cleanup/make_dafsa.py
@@ -0,0 +1,469 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""
+A Deterministic acyclic finite state automaton (DAFSA) is a compact
+representation of an unordered word list (dictionary).
+
+http://en.wikipedia.org/wiki/Deterministic_acyclic_finite_state_automaton
+
+This python program converts a list of strings to a byte array in C++.
+This python program fetches strings and return values from a gperf file
+and generates a C++ file with a byte array representing graph that can be
+used as a memory efficient replacement for the perfect hash table.
+
+The input strings are assumed to consist of printable 7-bit ASCII characters
+and the return values are assumed to be one digit integers.
+
+In this program a DAFSA is a diamond shaped graph starting at a common
+source node and ending at a common sink node. All internal nodes contain
+a label and each word is represented by the labels in one path from
+the source node to the sink node.
+
+The following python represention is used for nodes:
+
+ Source node: [ children ]
+ Internal node: (label, [ children ])
+ Sink node: None
+
+The graph is first compressed by prefixes like a trie. In the next step
+suffixes are compressed so that the graph gets diamond shaped. Finally
+one to one linked nodes are replaced by nodes with the labels joined.
+
+The order of the operations is crucial since lookups will be performed
+starting from the source with no backtracking. Thus a node must have at
+most one child with a label starting by the same character. The output
+is also arranged so that all jumps are to increasing addresses, thus forward
+in memory.
+
+The generated output has suffix free decoding so that the sign of leading
+bits in a link (a reference to a child node) indicate if it has a size of one,
+two or three bytes and if it is the last outgoing link from the actual node.
+A node label is terminated by a byte with the leading bit set.
+
+The generated byte array can described by the following BNF:
+
+<byte> ::= < 8-bit value in range [0x00-0xFF] >
+
+<char> ::= < printable 7-bit ASCII character, byte in range [0x20-0x7F] >
+<end_char> ::= < char + 0x80, byte in range [0xA0-0xFF] >
+<return value> ::= < value + 0x80, byte in range [0x80-0x8F] >
+
+<offset1> ::= < byte in range [0x00-0x3F] >
+<offset2> ::= < byte in range [0x40-0x5F] >
+<offset3> ::= < byte in range [0x60-0x7F] >
+
+<end_offset1> ::= < byte in range [0x80-0xBF] >
+<end_offset2> ::= < byte in range [0xC0-0xDF] >
+<end_offset3> ::= < byte in range [0xE0-0xFF] >
+
+<prefix> ::= <char>
+
+<label> ::= <end_char>
+ | <char> <label>
+
+<end_label> ::= <return_value>
+ | <char> <end_label>
+
+<offset> ::= <offset1>
+ | <offset2> <byte>
+ | <offset3> <byte> <byte>
+
+<end_offset> ::= <end_offset1>
+ | <end_offset2> <byte>
+ | <end_offset3> <byte> <byte>
+
+<offsets> ::= <end_offset>
+ | <offset> <offsets>
+
+<source> ::= <offsets>
+
+<node> ::= <label> <offsets>
+ | <prefix> <node>
+ | <end_label>
+
+<dafsa> ::= <source>
+ | <dafsa> <node>
+
+Decoding:
+
+<char> -> printable 7-bit ASCII character
+<end_char> & 0x7F -> printable 7-bit ASCII character
+<return value> & 0x0F -> integer
+<offset1 & 0x3F> -> integer
+((<offset2> & 0x1F>) << 8) + <byte> -> integer
+((<offset3> & 0x1F>) << 16) + (<byte> << 8) + <byte> -> integer
+
+end_offset1, end_offset2 and and_offset3 are decoded same as offset1,
+offset2 and offset3 respectively.
+
+The first offset in a list of offsets is the distance in bytes between the
+offset itself and the first child node. Subsequent offsets are the distance
+between previous child node and next child node. Thus each offset links a node
+to a child node. The distance is always counted between start addresses, i.e.
+first byte in decoded offset or first byte in child node.
+
+Example 1:
+
+%%
+aa, 1
+a, 2
+%%
+
+The input is first parsed to a list of words:
+["aa1", "a2"]
+
+A fully expanded graph is created from the words:
+source = [node1, node4]
+node1 = ("a", [node2])
+node2 = ("a", [node3])
+node3 = ("\x01", [sink])
+node4 = ("a", [node5])
+node5 = ("\x02", [sink])
+sink = None
+
+Compression results in the following graph:
+source = [node1]
+node1 = ("a", [node2, node3])
+node2 = ("\x02", [sink])
+node3 = ("a\x01", [sink])
+sink = None
+
+A C++ representation of the compressed graph is generated:
+
+const unsigned char dafsa[7] = {
+ 0x81, 0xE1, 0x02, 0x81, 0x82, 0x61, 0x81,
+};
+
+The bytes in the generated array has the following meaning:
+
+ 0: 0x81 <end_offset1> child at position 0 + (0x81 & 0x3F) -> jump to 1
+
+ 1: 0xE1 <end_char> label character (0xE1 & 0x7F) -> match "a"
+ 2: 0x02 <offset1> child at position 2 + (0x02 & 0x3F) -> jump to 4
+
+ 3: 0x81 <end_offset1> child at position 4 + (0x81 & 0x3F) -> jump to 5
+ 4: 0x82 <return_value> 0x82 & 0x0F -> return 2
+
+ 5: 0x61 <char> label character 0x61 -> match "a"
+ 6: 0x81 <return_value> 0x81 & 0x0F -> return 1
+
+Example 2:
+
+%%
+aa, 1
+bbb, 2
+baa, 1
+%%
+
+The input is first parsed to a list of words:
+["aa1", "bbb2", "baa1"]
+
+Compression results in the following graph:
+source = [node1, node2]
+node1 = ("b", [node2, node3])
+node2 = ("aa\x01", [sink])
+node3 = ("bb\x02", [sink])
+sink = None
+
+A C++ representation of the compressed graph is generated:
+
+const unsigned char dafsa[11] = {
+ 0x02, 0x83, 0xE2, 0x02, 0x83, 0x61, 0x61, 0x81, 0x62, 0x62, 0x82,
+};
+
+The bytes in the generated array has the following meaning:
+
+ 0: 0x02 <offset1> child at position 0 + (0x02 & 0x3F) -> jump to 2
+ 1: 0x83 <end_offset1> child at position 2 + (0x83 & 0x3F) -> jump to 5
+
+ 2: 0xE2 <end_char> label character (0xE2 & 0x7F) -> match "b"
+ 3: 0x02 <offset1> child at position 3 + (0x02 & 0x3F) -> jump to 5
+ 4: 0x83 <end_offset1> child at position 5 + (0x83 & 0x3F) -> jump to 8
+
+ 5: 0x61 <char> label character 0x61 -> match "a"
+ 6: 0x61 <char> label character 0x61 -> match "a"
+ 7: 0x81 <return_value> 0x81 & 0x0F -> return 1
+
+ 8: 0x62 <char> label character 0x62 -> match "b"
+ 9: 0x62 <char> label character 0x62 -> match "b"
+10: 0x82 <return_value> 0x82 & 0x0F -> return 2
+"""
+
+import sys
+
+class InputError(Exception):
+ """Exception raised for errors in the input file."""
+
+
+def to_dafsa(words):
+ """Generates a DAFSA from a word list and returns the source node.
+
+ Each word is split into characters so that each character is represented by
+ a unique node. It is assumed the word list is not empty.
+ """
+ if not words:
+ raise InputError('The domain list must not be empty')
+ def ToNodes(word):
+ """Split words into characters"""
+ if not 0x1F < ord(word[0]) < 0x80:
+ raise InputError('Domain names must be printable 7-bit ASCII')
+ if len(word) == 1:
+ return chr(ord(word[0]) & 0x0F), [None]
+ return word[0], [ToNodes(word[1:])]
+ return [ToNodes(word) for word in words]
+
+
+def to_words(node):
+ """Generates a word list from all paths starting from an internal node."""
+ if not node:
+ return ['']
+ return [(node[0] + word) for child in node[1] for word in to_words(child)]
+
+
+def reverse(dafsa):
+ """Generates a new DAFSA that is reversed, so that the old sink node becomes
+ the new source node.
+ """
+ sink = []
+ nodemap = {}
+
+ def dfs(node, parent):
+ """Creates reverse nodes.
+
+ A new reverse node will be created for each old node. The new node will
+ get a reversed label and the parents of the old node as children.
+ """
+ if not node:
+ sink.append(parent)
+ elif id(node) not in nodemap:
+ nodemap[id(node)] = (node[0][::-1], [parent])
+ for child in node[1]:
+ dfs(child, nodemap[id(node)])
+ else:
+ nodemap[id(node)][1].append(parent)
+
+ for node in dafsa:
+ dfs(node, None)
+ return sink
+
+
+def join_labels(dafsa):
+ """Generates a new DAFSA where internal nodes are merged if there is a one to
+ one connection.
+ """
+ parentcount = { id(None): 2 }
+ nodemap = { id(None): None }
+
+ def count_parents(node):
+ """Count incoming references"""
+ if id(node) in parentcount:
+ parentcount[id(node)] += 1
+ else:
+ parentcount[id(node)] = 1
+ for child in node[1]:
+ count_parents(child)
+
+ def join(node):
+ """Create new nodes"""
+ if id(node) not in nodemap:
+ children = [join(child) for child in node[1]]
+ if len(children) == 1 and parentcount[id(node[1][0])] == 1:
+ child = children[0]
+ nodemap[id(node)] = (node[0] + child[0], child[1])
+ else:
+ nodemap[id(node)] = (node[0], children)
+ return nodemap[id(node)]
+
+ for node in dafsa:
+ count_parents(node)
+ return [join(node) for node in dafsa]
+
+
+def join_suffixes(dafsa):
+ """Generates a new DAFSA where nodes that represent the same word lists
+ towards the sink are merged.
+ """
+ nodemap = { frozenset(('',)): None }
+
+ def join(node):
+ """Returns a macthing node. A new node is created if no matching node
+ exists. The graph is accessed in dfs order.
+ """
+ suffixes = frozenset(to_words(node))
+ if suffixes not in nodemap:
+ nodemap[suffixes] = (node[0], [join(child) for child in node[1]])
+ return nodemap[suffixes]
+
+ return [join(node) for node in dafsa]
+
+
+def top_sort(dafsa):
+ """Generates list of nodes in topological sort order."""
+ incoming = {}
+
+ def count_incoming(node):
+ """Counts incoming references."""
+ if node:
+ if id(node) not in incoming:
+ incoming[id(node)] = 1
+ for child in node[1]:
+ count_incoming(child)
+ else:
+ incoming[id(node)] += 1
+
+ for node in dafsa:
+ count_incoming(node)
+
+ for node in dafsa:
+ incoming[id(node)] -= 1
+
+ waiting = [node for node in dafsa if incoming[id(node)] == 0]
+ nodes = []
+
+ while waiting:
+ node = waiting.pop()
+ assert incoming[id(node)] == 0
+ nodes.append(node)
+ for child in node[1]:
+ if child:
+ incoming[id(child)] -= 1
+ if incoming[id(child)] == 0:
+ waiting.append(child)
+ return nodes
+
+
+def encode_links(children, offsets, current):
+ """Encodes a list of children as one, two or three byte offsets."""
+ if not children[0]:
+ # This is an <end_label> node and no links follow such nodes
+ assert len(children) == 1
+ return []
+ guess = 3 * len(children)
+ assert children
+ children = sorted(children, key = lambda x: -offsets[id(x)])
+ while True:
+ offset = current + guess
+ buf = []
+ for child in children:
+ last = len(buf)
+ distance = offset - offsets[id(child)]
+ assert distance > 0 and distance < (1 << 21)
+
+ if distance < (1 << 6):
+ # A 6-bit offset: "s0xxxxxx"
+ buf.append(distance)
+ elif distance < (1 << 13):
+ # A 13-bit offset: "s10xxxxxxxxxxxxx"
+ buf.append(0x40 | (distance >> 8))
+ buf.append(distance & 0xFF)
+ else:
+ # A 21-bit offset: "s11xxxxxxxxxxxxxxxxxxxxx"
+ buf.append(0x60 | (distance >> 16))
+ buf.append((distance >> 8) & 0xFF)
+ buf.append(distance & 0xFF)
+ # Distance in first link is relative to following record.
+ # Distance in other links are relative to previous link.
+ offset -= distance
+ if len(buf) == guess:
+ break
+ guess = len(buf)
+ # Set most significant bit to mark end of links in this node.
+ buf[last] |= (1 << 7)
+ buf.reverse()
+ return buf
+
+
+def encode_prefix(label):
+ """Encodes a node label as a list of bytes without a trailing high byte.
+
+ This method encodes a node if there is exactly one child and the
+ child follows immidiately after so that no jump is needed. This label
+ will then be a prefix to the label in the child node.
+ """
+ assert label
+ return [ord(c) for c in reversed(label)]
+
+
+def encode_label(label):
+ """Encodes a node label as a list of bytes with a trailing high byte >0x80.
+ """
+ buf = encode_prefix(label)
+ # Set most significant bit to mark end of label in this node.
+ buf[0] |= (1 << 7)
+ return buf
+
+
+def encode(dafsa):
+ """Encodes a DAFSA to a list of bytes"""
+ output = []
+ offsets = {}
+
+ for node in reversed(top_sort(dafsa)):
+ if (len(node[1]) == 1 and node[1][0] and
+ (offsets[id(node[1][0])] == len(output))):
+ output.extend(encode_prefix(node[0]))
+ else:
+ output.extend(encode_links(node[1], offsets, len(output)))
+ output.extend(encode_label(node[0]))
+ offsets[id(node)] = len(output)
+
+ output.extend(encode_links(dafsa, offsets, len(output)))
+ output.reverse()
+ return output
+
+
+def to_cxx(data):
+ """Generates C++ code from a list of encoded bytes."""
+ text = '/* This file is generated. DO NOT EDIT!\n\n'
+ text += 'The byte array encodes effective tld names. See make_dafsa.py for'
+ text += ' documentation.'
+ text += '*/\n\n'
+ text += 'const unsigned char kDafsa[%s] = {\n' % len(data)
+ for i in range(0, len(data), 12):
+ text += ' '
+ text += ', '.join('0x%02x' % byte for byte in data[i:i + 12])
+ text += ',\n'
+ text += '};\n'
+ return text
+
+
+def words_to_cxx(words):
+ """Generates C++ code from a word list"""
+ dafsa = to_dafsa(words)
+ for fun in (reverse, join_suffixes, reverse, join_suffixes, join_labels):
+ dafsa = fun(dafsa)
+ return to_cxx(encode(dafsa))
+
+
+def parse_gperf(infile):
+ """Parses gperf file and extract strings and return code"""
+ lines = [line.strip() for line in infile]
+ # Extract strings after the first '%%' and before the second '%%'.
+ begin = lines.index('%%') + 1
+ end = lines.index('%%', begin)
+ lines = lines[begin:end]
+ for line in lines:
+ if line[-3:-1] != ', ':
+ raise InputError('Expected "domainname, <digit>", found "%s"' % line)
+ # Technically the DAFSA format could support return values in range [0-31],
+ # but the values below are the only with a defined meaning.
+ if line[-1] not in '0124':
+ raise InputError('Expected value to be one of {0,1,2,4}, found "%s"' %
+ line[-1])
+ return [line[:-3] + line[-1] for line in lines]
+
+
+def main():
+ if len(sys.argv) != 3:
+ print('usage: %s infile outfile' % sys.argv[0])
+ return 1
+ with open(sys.argv[1], 'r') as infile, open(sys.argv[2], 'w') as outfile:
+ outfile.write(words_to_cxx(parse_gperf(infile)))
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py b/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py
new file mode 100755
index 00000000000..5ff92e62292
--- /dev/null
+++ b/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py
@@ -0,0 +1,757 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+
+import sys
+import unittest
+import make_dafsa
+
+
+class ParseGperfTest(unittest.TestCase):
+ def testMalformedKey(self):
+ """Tests exception is thrown at bad format."""
+ infile1 = [ '%%', '', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile1)
+
+ infile2 = [ '%%', 'apa,1', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile2)
+
+ infile3 = [ '%%', 'apa, 1', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile3)
+
+ def testBadValues(self):
+ """Tests exception is thrown when value is out of range."""
+ infile1 = [ '%%', 'a, -1', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile1)
+
+ infile2 = [ '%%', 'a, x', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile2)
+
+ infile3 = [ '%%', 'a, 3', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile3)
+
+ infile4 = [ '%%', 'a, 6', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile4)
+
+ infile5 = [ '%%', 'a, 12', '%%' ]
+ self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile5)
+
+ def testValues(self):
+ """Tests legal values are accepted."""
+ infile1 = [ '%%', 'a, 0', '%%' ]
+ words1 = [ 'a0' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile1), words1)
+
+ infile2 = [ '%%', 'a, 1', '%%' ]
+ words2 = [ 'a1' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile2), words2)
+
+ infile3 = [ '%%', 'a, 2', '%%' ]
+ words3 = [ 'a2' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile3), words3)
+
+ infile4 = [ '%%', 'a, 4', '%%' ]
+ words4 = [ 'a4' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile4), words4)
+
+ def testOneWord(self):
+ """Tests a single key can be parsed."""
+ infile = [ '%%', 'apa, 1', '%%' ]
+ words = [ 'apa1' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile), words)
+
+ def testTwoWords(self):
+ """Tests a sequence of keys can be parsed."""
+ infile = [ '%%', 'apa, 1', 'bepa.com, 2', '%%' ]
+ words = [ 'apa1', 'bepa.com2' ]
+ self.assertEqual(make_dafsa.parse_gperf(infile), words)
+
+
+class ToDafsaTest(unittest.TestCase):
+ def testEmptyInput(self):
+ """Tests exception is thrown at empty input."""
+ words = ()
+ self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words)
+
+ def testNonASCII(self):
+ """Tests exception is thrown if illegal characters are used."""
+ words1 = ( chr(0x1F) + 'a1', )
+ self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words1)
+
+ words2 = ( 'a' + chr(0x1F) + '1', )
+ self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words2)
+
+ words3 = ( chr(0x80) + 'a1', )
+ self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words3)
+
+ words4 = ( 'a' + chr(0x80) + '1', )
+ self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words4)
+
+ def testChar(self):
+ """Tests a DAFSA can be created from a single character domain name."""
+ words = [ 'a0' ]
+ node2 = ( chr(0), [ None ] )
+ node1 = ( 'a', [ node2 ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.to_dafsa(words), source)
+
+ def testChars(self):
+ """Tests a DAFSA can be created from a multi character domain name."""
+ words = [ 'ab0' ]
+ node3 = ( chr(0), [ None ] )
+ node2 = ( 'b', [ node3 ] )
+ node1 = ( 'a', [ node2 ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.to_dafsa(words), source)
+
+ def testWords(self):
+ """Tests a DAFSA can be created from a sequence of domain names."""
+ words = [ 'a0', 'b1' ]
+ node4 = ( chr(1), [ None ] )
+ node3 = ( 'b', [ node4 ] )
+ node2 = ( chr(0), [ None ] )
+ node1 = ( 'a', [ node2 ] )
+ source = [ node1, node3 ]
+ self.assertEqual(make_dafsa.to_dafsa(words), source)
+
+
+class ToWordsTest(unittest.TestCase):
+ def testSink(self):
+ """Tests the sink is exapnded to a list with an empty string."""
+ node1 = None
+ words = [ '' ]
+ self.assertEqual(make_dafsa.to_words(node1), words)
+
+ def testSingleNode(self):
+ """Tests a single node is expanded to a list with the label string."""
+
+ # 'ab' -> [ 'ab' ]
+
+ node1 = ( 'ab', [ None ] )
+ words = [ 'ab' ]
+ self.assertEqual(make_dafsa.to_words(node1), words)
+
+ def testChain(self):
+ """Tests a sequence of nodes are preoperly expanded."""
+
+ # 'ab' -> 'cd' => [ 'abcd' ]
+
+ node2 = ( 'cd', [ None ] )
+ node1 = ( 'ab', [ node2 ] )
+ words = [ 'abcd' ]
+ self.assertEqual(make_dafsa.to_words(node1), words)
+
+ def testInnerTerminator(self):
+ """Tests a sequence with an inner terminator is expanded to two strings."""
+
+ # 'a' -> 'b'
+ # \ => [ 'ab', 'a' ]
+ # {sink}
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, None ] )
+ words = [ 'ab', 'a' ]
+ self.assertEqual(make_dafsa.to_words(node1), words)
+
+ def testDiamond(self):
+ """Tests a diamond can be expanded to a word list."""
+
+ # 'cd'
+ # / \
+ # 'ab' 'gh'
+ # \ /
+ # 'ef'
+
+ node4 = ( 'gh', [ None ] )
+ node3 = ( 'ef', [ node4 ] )
+ node2 = ( 'cd', [ node4 ] )
+ node1 = ( 'ab', [ node2, node3 ] )
+ words = [ 'abcdgh', 'abefgh' ]
+ self.assertEqual(make_dafsa.to_words(node1), words)
+
+
+class JoinLabelsTest(unittest.TestCase):
+ def testLabel(self):
+ """Tests a single label passes unchanged."""
+
+ # 'a' => 'a'
+
+ node1 = ( 'a', [ None ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_labels(source), source)
+
+ def testInnerTerminator(self):
+ """Tests a sequence with an inner terminator passes unchanged."""
+
+ # 'a' -> 'b' 'a' -> 'b'
+ # \ => \
+ # {sink} {sink}
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, None ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_labels(source), source)
+
+ def testLabels(self):
+ """Tests a sequence of labels can be joined."""
+
+ # 'a' -> 'b' => 'ab'
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2 ] )
+ source1 = [ node1 ]
+ node3 = ( 'ab', [ None ] )
+ source2 = [ node3 ]
+ self.assertEqual(make_dafsa.join_labels(source1), source2)
+
+ def testCompositeLabels(self):
+ """Tests a sequence of multi character labels can be joined."""
+
+ # 'ab' -> 'cd' => 'abcd'
+
+ node2 = ( 'cd', [ None ] )
+ node1 = ( 'ab', [ node2 ] )
+ source1 = [ node1 ]
+ node3 = ( 'abcd', [ None ] )
+ source2 = [ node3 ]
+ self.assertEqual(make_dafsa.join_labels(source1), source2)
+
+ def testAtomicTrie(self):
+ """Tests a trie formed DAFSA with atomic labels passes unchanged."""
+
+ # 'b' 'b'
+ # / /
+ # 'a' => 'a'
+ # \ \
+ # 'c' 'c'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, node3 ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_labels(source), source)
+
+ def testReverseAtomicTrie(self):
+ """Tests a reverse trie formed DAFSA with atomic labels passes unchanged."""
+
+ # 'a' 'a'
+ # \ \
+ # 'c' => 'c'
+ # / /
+ # 'b' 'b'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ node3 ] )
+ node1 = ( 'a', [ node3 ] )
+ source = [ node1, node2 ]
+ self.assertEqual(make_dafsa.join_labels(source), source)
+
+ def testChainedTrie(self):
+ """Tests a trie formed DAFSA with chained labels can be joined."""
+
+ # 'c' -> 'd' 'cd'
+ # / /
+ # 'a' -> 'b' => 'ab'
+ # \ \
+ # 'e' -> 'f' 'ef'
+
+ node6 = ( 'f', [ None ] )
+ node5 = ( 'e', [ node6 ] )
+ node4 = ( 'd', [ None ] )
+ node3 = ( 'c', [ node4 ] )
+ node2 = ( 'b', [ node3, node5 ] )
+ node1 = ( 'a', [ node2 ] )
+ source1 = [ node1 ]
+ node9 = ( 'ef', [ None ] )
+ node8 = ( 'cd', [ None ] )
+ node7 = ( 'ab', [ node8, node9 ] )
+ source2 = [ node7 ]
+ self.assertEqual(make_dafsa.join_labels(source1), source2)
+
+ def testReverseChainedTrie(self):
+ """Tests a reverse trie formed DAFSA with chained labels can be joined."""
+
+ # 'a' -> 'b' 'ab'
+ # \ \
+ # 'e' -> 'f' => 'ef'
+ # / /
+ # 'c' -> 'd' 'cd'
+
+ node6 = ( 'f', [ None ] )
+ node5 = ( 'e', [ node6 ] )
+ node4 = ( 'd', [ node5 ] )
+ node3 = ( 'c', [ node4 ] )
+ node2 = ( 'b', [ node5 ] )
+ node1 = ( 'a', [ node2 ] )
+ source1 = [ node1, node3 ]
+ node9 = ( 'ef', [ None ] )
+ node8 = ( 'cd', [ node9 ] )
+ node7 = ( 'ab', [ node9 ] )
+ source2 = [ node7, node8 ]
+ self.assertEqual(make_dafsa.join_labels(source1), source2)
+
+
+class JoinSuffixesTest(unittest.TestCase):
+ def testSingleLabel(self):
+ """Tests a single label passes unchanged."""
+
+ # 'a' => 'a'
+
+ node1 = ( 'a', [ None ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_suffixes(source), source)
+
+ def testInnerTerminator(self):
+ """Tests a sequence with an inner terminator passes unchanged."""
+
+ # 'a' -> 'b' 'a' -> 'b'
+ # \ => \
+ # {sink} {sink}
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, None ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_suffixes(source), source)
+
+ def testDistinctTrie(self):
+ """Tests a trie formed DAFSA with distinct labels passes unchanged."""
+
+ # 'b' 'b'
+ # / /
+ # 'a' => 'a'
+ # \ \
+ # 'c' 'c'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, node3 ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.join_suffixes(source), source)
+
+ def testReverseDistinctTrie(self):
+ """Tests a reverse trie formed DAFSA with distinct labels passes unchanged.
+ """
+
+ # 'a' 'a'
+ # \ \
+ # 'c' => 'c'
+ # / /
+ # 'b' 'b'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ node3 ] )
+ node1 = ( 'a', [ node3 ] )
+ source = [ node1, node2 ]
+ self.assertEqual(make_dafsa.join_suffixes(source), source)
+
+ def testJoinTwoHeads(self):
+ """Tests two heads can be joined even if there is something else between."""
+
+ # 'a' ------'a'
+ # /
+ # 'b' => 'b' /
+ # /
+ # 'a' ---
+ #
+ # The picture above should shows that the new version should have just one
+ # instance of the node with label 'a'.
+
+ node3 = ( 'a', [ None ] )
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ None ] )
+ source1 = [ node1, node2, node3 ]
+ source2 = make_dafsa.join_suffixes(source1)
+
+ # Both versions should expand to the same content.
+ self.assertEqual(source1, source2)
+ # But the new version should have just one instance of 'a'.
+ self.assertIs(source2[0], source2[2])
+
+ def testJoinTails(self):
+ """Tests tails can be joined."""
+
+ # 'a' -> 'c' 'a'
+ # \
+ # => 'c'
+ # /
+ # 'b' -> 'c' 'b'
+
+ node4 = ( 'c', [ None ] )
+ node3 = ( 'b', [ node4 ] )
+ node2 = ( 'c', [ None ] )
+ node1 = ( 'a', [ node2 ] )
+ source1 = [ node1, node3 ]
+ source2 = make_dafsa.join_suffixes(source1)
+
+ # Both versions should expand to the same content.
+ self.assertEqual(source1, source2)
+ # But the new version should have just one tail.
+ self.assertIs(source2[0][1][0], source2[1][1][0])
+
+ def testMakeRecursiveTrie(self):
+ """Tests recursive suffix join."""
+
+ # 'a' -> 'e' -> 'g' 'a'
+ # \
+ # 'e'
+ # / \
+ # 'b' -> 'e' -> 'g' 'b' \
+ # \
+ # => 'g'
+ # /
+ # 'c' -> 'f' -> 'g' 'c' /
+ # \ /
+ # 'f'
+ # /
+ # 'd' -> 'f' -> 'g' 'd'
+
+ node7 = ( 'g', [ None ] )
+ node6 = ( 'f', [ node7 ] )
+ node5 = ( 'e', [ node7 ] )
+ node4 = ( 'd', [ node6 ] )
+ node3 = ( 'c', [ node6 ] )
+ node2 = ( 'b', [ node5 ] )
+ node1 = ( 'a', [ node5 ] )
+ source1 = [ node1, node2, node3, node4 ]
+ source2 = make_dafsa.join_suffixes(source1)
+
+ # Both versions should expand to the same content.
+ self.assertEqual(source1, source2)
+ # But the new version should have just one 'e'.
+ self.assertIs(source2[0][1][0], source2[1][1][0])
+ # And one 'f'.
+ self.assertIs(source2[2][1][0], source2[3][1][0])
+ # And one 'g'.
+ self.assertIs(source2[0][1][0][1][0], source2[2][1][0][1][0])
+
+ def testMakeDiamond(self):
+ """Test we can join suffixes of a trie."""
+
+ # 'b' -> 'd' 'b'
+ # / / \
+ # 'a' => 'a' 'd'
+ # \ \ /
+ # 'c' -> 'd' 'c'
+
+ node5 = ( 'd', [ None ] )
+ node4 = ( 'c', [ node5 ] )
+ node3 = ( 'd', [ None ] )
+ node2 = ( 'b', [ node3 ] )
+ node1 = ( 'a', [ node2, node4 ] )
+ source1 = [ node1 ]
+ source2 = make_dafsa.join_suffixes(source1)
+
+ # Both versions should expand to the same content.
+ self.assertEqual(source1, source2)
+ # But the new version should have just one 'd'.
+ self.assertIs(source2[0][1][0][1][0], source2[0][1][1][1][0])
+
+ def testJoinOneChild(self):
+ """Tests that we can join some children but not all."""
+
+ # 'c' ----'c'
+ # / / /
+ # 'a' 'a' /
+ # \ \ /
+ # 'd' 'd'/
+ # => /
+ # 'c' /
+ # / /
+ # 'b' 'b'
+ # \ \
+ # 'e' 'e'
+
+ node6 = ( 'e', [ None ] )
+ node5 = ( 'c', [ None ] )
+ node4 = ( 'b', [ node5, node6 ] )
+ node3 = ( 'd', [ None ] )
+ node2 = ( 'c', [ None ] )
+ node1 = ( 'a', [ node2, node3 ] )
+ source1 = [ node1, node4 ]
+ source2 = make_dafsa.join_suffixes(source1)
+
+ # Both versions should expand to the same content.
+ self.assertEqual(source1, source2)
+ # But the new version should have just one 'c'.
+ self.assertIs(source2[0][1][0], source2[1][1][0])
+
+
+class ReverseTest(unittest.TestCase):
+ def testAtomicLabel(self):
+ """Tests an atomic label passes unchanged."""
+
+ # 'a' => 'a'
+
+ node1 = ( 'a', [ None ] )
+ source = [ node1 ]
+ self.assertEqual(make_dafsa.reverse(source), source)
+
+ def testLabel(self):
+ """Tests that labels are reversed."""
+
+ # 'ab' => 'ba'
+
+ node1 = ( 'ab', [ None ] )
+ source1 = [ node1 ]
+ node2 = ( 'ba', [ None ] )
+ source2 = [ node2 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+ def testChain(self):
+ """Tests that edges are reversed."""
+
+ # 'a' -> 'b' => 'b' -> 'a'
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2 ] )
+ source1 = [ node1 ]
+ node4 = ( 'a', [ None ] )
+ node3 = ( 'b', [ node4 ] )
+ source2 = [ node3 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+ def testInnerTerminator(self):
+ """Tests a sequence with an inner terminator can be reversed."""
+
+ # 'a' -> 'b' 'b' -> 'a'
+ # \ => /
+ # {sink} ------
+
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, None ] )
+ source1 = [ node1 ]
+ node4 = ( 'a', [ None ] )
+ node3 = ( 'b', [ node4 ] )
+ source2 = [ node3, node4 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+ def testAtomicTrie(self):
+ """Tests a trie formed DAFSA can be reversed."""
+
+ # 'b' 'b'
+ # / \
+ # 'a' => 'a'
+ # \ /
+ # 'c' 'c'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ None ] )
+ node1 = ( 'a', [ node2, node3 ] )
+ source1 = [ node1 ]
+ node6 = ( 'a', [ None ] )
+ node5 = ( 'c', [ node6 ] )
+ node4 = ( 'b', [ node6 ] )
+ source2 = [ node4, node5 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+ def testReverseAtomicTrie(self):
+ """Tests a reverse trie formed DAFSA can be reversed."""
+
+ # 'a' 'a'
+ # \ /
+ # 'c' => 'c'
+ # / \
+ # 'b' 'b'
+
+ node3 = ( 'c', [ None ] )
+ node2 = ( 'b', [ node3 ] )
+ node1 = ( 'a', [ node3 ] )
+ source1 = [ node1, node2 ]
+ node6 = ( 'b', [ None ] )
+ node5 = ( 'a', [ None ] )
+ node4 = ( 'c', [ node5, node6 ] )
+ source2 = [ node4 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+ def testDiamond(self):
+ """Tests we can reverse both edges and nodes in a diamond."""
+
+ # 'cd' 'dc'
+ # / \ / \
+ # 'ab' 'gh' => 'hg' 'ba'
+ # \ / \ /
+ # 'ef' 'fe'
+
+ node4 = ( 'gh', [ None ] )
+ node3 = ( 'ef', [ node4 ] )
+ node2 = ( 'cd', [ node4 ] )
+ node1 = ( 'ab', [ node2, node3 ] )
+ source1 = [ node1 ]
+ node8 = ( 'ba', [ None ] )
+ node7 = ( 'fe', [ node8 ] )
+ node6 = ( 'dc', [ node8 ] )
+ node5 = ( 'hg', [ node6, node7 ] )
+ source2 = [ node5 ]
+ self.assertEqual(make_dafsa.reverse(source1), source2)
+
+
+class TopSortTest(unittest.TestCase):
+ def testNode(self):
+ """Tests a DAFSA with one node can be sorted."""
+
+ # 'a' => [ 'a' ]
+
+ node1 = ( 'a', [ None ] )
+ source = [ node1 ]
+ nodes = [ node1 ]
+ self.assertEqual(make_dafsa.top_sort(source), nodes)
+
+ def testDiamond(self):
+ """Tests nodes in a diamond can be sorted."""
+
+ # 'b'
+ # / \
+ # 'a' 'd'
+ # \ /
+ # 'c'
+
+ node4 = ( 'd', [ None ] )
+ node3 = ( 'c', [ node4 ] )
+ node2 = ( 'b', [ node4 ] )
+ node1 = ( 'a', [ node2, node3 ] )
+ source = [ node1 ]
+ nodes = make_dafsa.top_sort(source)
+ self.assertLess(nodes.index(node1), nodes.index(node2))
+ self.assertLess(nodes.index(node2), nodes.index(node4))
+ self.assertLess(nodes.index(node3), nodes.index(node4))
+
+
+class EncodePrefixTest(unittest.TestCase):
+ def testChar(self):
+ """Tests to encode a single character prefix."""
+ label = 'a'
+ bytes = [ ord('a') ]
+ self.assertEqual(make_dafsa.encode_prefix(label), bytes)
+
+ def testChars(self):
+ """Tests to encode a multi character prefix."""
+ label = 'ab'
+ bytes = [ ord('b'), ord('a') ]
+ self.assertEqual(make_dafsa.encode_prefix(label), bytes)
+
+
+class EncodeLabelTest(unittest.TestCase):
+ def testChar(self):
+ """Tests to encode a single character label."""
+ label = 'a'
+ bytes = [ ord('a') + 0x80 ]
+ self.assertEqual(make_dafsa.encode_label(label), bytes)
+
+ def testChars(self):
+ """Tests to encode a multi character label."""
+ label = 'ab'
+ bytes = [ ord('b') + 0x80, ord('a') ]
+ self.assertEqual(make_dafsa.encode_label(label), bytes)
+
+
+class EncodeLinksTest(unittest.TestCase):
+ def testEndLabel(self):
+ """Tests to encode link to the sink."""
+ children = [ None ]
+ offsets = {}
+ bytes = 0
+ output = []
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testOneByteOffset(self):
+ """Tests to encode a single one byte offset."""
+ node = ( '', [ None ] )
+ children = [ node ]
+ offsets = { id(node) : 2 }
+ bytes = 5
+ output = [ 132 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testOneByteOffsets(self):
+ """Tests to encode a sequence of one byte offsets."""
+ node1 = ( '', [ None ] )
+ node2 = ( '', [ None ] )
+ children = [ node1, node2 ]
+ offsets = { id(node1) : 2, id(node2) : 1 }
+ bytes = 5
+ output = [ 129, 5 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testTwoBytesOffset(self):
+ """Tests to encode a single two byte offset."""
+ node = ( '', [ None ] )
+ children = [ node ]
+ offsets = { id(node) : 2 }
+ bytes = 1005
+ output = [ 237, 195]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testTwoBytesOffsets(self):
+ """Tests to encode a sequence of two byte offsets."""
+ node1 = ( '', [ None ] )
+ node2 = ( '', [ None ] )
+ node3 = ( '', [ None ] )
+ children = [ node1, node2, node3 ]
+ offsets = { id(node1) : 1002, id(node2) : 2, id(node3) : 2002 }
+ bytes = 3005
+ output = [ 232, 195, 232, 67, 241, 67 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testThreeBytesOffset(self):
+ """Tests to encode a single three byte offset."""
+ node = ( '', [ None ] )
+ children = [ node ]
+ offsets = { id(node) : 2 }
+ bytes = 100005
+ output = [ 166, 134, 225 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testThreeBytesOffsets(self):
+ """Tests to encode a sequence of three byte offsets."""
+ node1 = ( '', [ None ] )
+ node2 = ( '', [ None ] )
+ node3 = ( '', [ None ] )
+ children = [ node1, node2, node3 ]
+ offsets = { id(node1) : 100002, id(node2) : 2, id(node3) : 200002 }
+ bytes = 300005
+ output = [ 160, 134, 225, 160, 134, 97, 172, 134, 97 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+ def testOneTwoThreeBytesOffsets(self):
+ """Tests to encode offsets of different sizes."""
+ node1 = ( '', [ None ] )
+ node2 = ( '', [ None ] )
+ node3 = ( '', [ None ] )
+ children = [ node1, node2, node3 ]
+ offsets = { id(node1) : 10003, id(node2) : 10002, id(node3) : 100002 }
+ bytes = 300005
+ output = [ 129, 143, 95, 97, 74, 13, 99 ]
+ self.assertEqual(make_dafsa.encode_links(children, offsets, bytes),
+ output)
+
+
+class ExamplesTest(unittest.TestCase):
+ def testExample1(self):
+ """Tests Example 1 from make_dafsa.py."""
+ infile = [ '%%', 'aa, 1', 'a, 2', '%%' ]
+ bytes = [ 0x81, 0xE1, 0x02, 0x81, 0x82, 0x61, 0x81 ]
+ outfile = make_dafsa.to_cxx(bytes)
+ self.assertEqual(make_dafsa.words_to_cxx(make_dafsa.parse_gperf(infile)),
+ outfile)
+
+ def testExample2(self):
+ """Tests Example 2 from make_dafsa.py."""
+ infile = [ '%%', 'aa, 1', 'bbb, 2', 'baa, 1', '%%' ]
+ bytes = [ 0x02, 0x83, 0xE2, 0x02, 0x83, 0x61, 0x61, 0x81, 0x62, 0x62,
+ 0x82 ]
+ outfile = make_dafsa.to_cxx(bytes)
+ self.assertEqual(make_dafsa.words_to_cxx(make_dafsa.parse_gperf(infile)),
+ outfile)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/net/tools/tld_cleanup/tld_cleanup.cc b/chromium/net/tools/tld_cleanup/tld_cleanup.cc
index a4b127bdf2c..5594145cd39 100644
--- a/chromium/net/tools/tld_cleanup/tld_cleanup.cc
+++ b/chromium/net/tools/tld_cleanup/tld_cleanup.cc
@@ -53,7 +53,7 @@ int main(int argc, const char* argv[]) {
logging::LOG_TO_ALL;
#endif
- CommandLine::Init(argc, argv);
+ base::CommandLine::Init(argc, argv);
base::FilePath log_filename;
PathService::Get(base::DIR_EXE, &log_filename);
diff --git a/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc b/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc
index 623403fed93..5a206e4c61a 100644
--- a/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc
+++ b/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc
@@ -61,7 +61,7 @@ bool WriteRules(const RuleMap& rules, const base::FilePath& outfile) {
data.append("%%\n");
- int written = file_util::WriteFile(outfile,
+ int written = base::WriteFile(outfile,
data.data(),
static_cast<int>(data.size()));
@@ -115,7 +115,7 @@ NormalizeResult NormalizeRule(std::string* domain, Rule* rule) {
url.append(*domain);
GURL gurl(url);
const std::string& spec = gurl.possibly_invalid_spec();
- url_parse::Component host = gurl.parsed_for_possibly_invalid_spec().host;
+ url::Component host = gurl.parsed_for_possibly_invalid_spec().host;
if (host.len < 0) {
LOG(ERROR) << "Ignoring rule that couldn't be normalized: " << *domain;
return kError;
diff --git a/chromium/net/udp/datagram_server_socket.h b/chromium/net/udp/datagram_server_socket.h
index ed5ab7a7c4f..964883fa5b2 100644
--- a/chromium/net/udp/datagram_server_socket.h
+++ b/chromium/net/udp/datagram_server_socket.h
@@ -56,10 +56,12 @@ class NET_EXPORT DatagramServerSocket : public DatagramSocket {
const CompletionCallback& callback) = 0;
// Set the receive buffer size (in bytes) for the socket.
- virtual bool SetReceiveBufferSize(int32 size) = 0;
+ // Returns a net error code.
+ virtual int SetReceiveBufferSize(int32 size) = 0;
// Set the send buffer size (in bytes) for the socket.
- virtual bool SetSendBufferSize(int32 size) = 0;
+ // Returns a net error code.
+ virtual int SetSendBufferSize(int32 size) = 0;
// Allow the socket to share the local address to which the socket will
// be bound with other processes. Should be called before Listen().
@@ -103,6 +105,9 @@ class NET_EXPORT DatagramServerSocket : public DatagramSocket {
// Set the Differentiated Services Code Point. May do nothing on
// some platforms. Returns a network error code.
virtual int SetDiffServCodePoint(DiffServCodePoint dscp) = 0;
+
+ // Resets the thread to be used for thread-safety checks.
+ virtual void DetachFromThread() = 0;
};
} // namespace net
diff --git a/chromium/net/udp/udp_client_socket.cc b/chromium/net/udp/udp_client_socket.cc
index bbc32d4f28e..f6f904a6beb 100644
--- a/chromium/net/udp/udp_client_socket.cc
+++ b/chromium/net/udp/udp_client_socket.cc
@@ -46,11 +46,11 @@ int UDPClientSocket::GetLocalAddress(IPEndPoint* address) const {
return socket_.GetLocalAddress(address);
}
-bool UDPClientSocket::SetReceiveBufferSize(int32 size) {
+int UDPClientSocket::SetReceiveBufferSize(int32 size) {
return socket_.SetReceiveBufferSize(size);
}
-bool UDPClientSocket::SetSendBufferSize(int32 size) {
+int UDPClientSocket::SetSendBufferSize(int32 size) {
return socket_.SetSendBufferSize(size);
}
diff --git a/chromium/net/udp/udp_client_socket.h b/chromium/net/udp/udp_client_socket.h
index e5cbdab23a5..c5fc8c03b6b 100644
--- a/chromium/net/udp/udp_client_socket.h
+++ b/chromium/net/udp/udp_client_socket.h
@@ -32,8 +32,8 @@ class NET_EXPORT_PRIVATE UDPClientSocket : public DatagramClientSocket {
virtual void Close() OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual const BoundNetLog& NetLog() const OVERRIDE;
private:
diff --git a/chromium/net/udp/udp_server_socket.cc b/chromium/net/udp/udp_server_socket.cc
index 99d741b4700..796c04da503 100644
--- a/chromium/net/udp/udp_server_socket.cc
+++ b/chromium/net/udp/udp_server_socket.cc
@@ -37,11 +37,11 @@ int UDPServerSocket::SendTo(IOBuffer* buf,
return socket_.SendTo(buf, buf_len, address, callback);
}
-bool UDPServerSocket::SetReceiveBufferSize(int32 size) {
+int UDPServerSocket::SetReceiveBufferSize(int32 size) {
return socket_.SetReceiveBufferSize(size);
}
-bool UDPServerSocket::SetSendBufferSize(int32 size) {
+int UDPServerSocket::SetSendBufferSize(int32 size) {
return socket_.SetSendBufferSize(size);
}
@@ -93,4 +93,8 @@ int UDPServerSocket::SetDiffServCodePoint(DiffServCodePoint dscp) {
return socket_.SetDiffServCodePoint(dscp);
}
+void UDPServerSocket::DetachFromThread() {
+ socket_.DetachFromThread();
+}
+
} // namespace net
diff --git a/chromium/net/udp/udp_server_socket.h b/chromium/net/udp/udp_server_socket.h
index c593413d628..4ce73545412 100644
--- a/chromium/net/udp/udp_server_socket.h
+++ b/chromium/net/udp/udp_server_socket.h
@@ -31,8 +31,8 @@ class NET_EXPORT UDPServerSocket : public DatagramServerSocket {
int buf_len,
const IPEndPoint& address,
const CompletionCallback& callback) OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual void Close() OVERRIDE;
virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
@@ -45,6 +45,7 @@ class NET_EXPORT UDPServerSocket : public DatagramServerSocket {
virtual int SetMulticastTimeToLive(int time_to_live) OVERRIDE;
virtual int SetMulticastLoopbackMode(bool loopback) OVERRIDE;
virtual int SetDiffServCodePoint(DiffServCodePoint dscp) OVERRIDE;
+ virtual void DetachFromThread() OVERRIDE;
private:
UDPSocket socket_;
diff --git a/chromium/net/udp/udp_socket_libevent.cc b/chromium/net/udp/udp_socket_libevent.cc
index ef2bae06a06..7db3f96dd12 100644
--- a/chromium/net/udp/udp_socket_libevent.cc
+++ b/chromium/net/udp/udp_socket_libevent.cc
@@ -47,10 +47,10 @@ int GetIPv4AddressFromIndex(int socket, uint32 index, uint32* address){
ifreq ifr;
ifr.ifr_addr.sa_family = AF_INET;
if (!if_indextoname(index, ifr.ifr_name))
- return ERR_FAILED;
+ return MapSystemError(errno);
int rv = ioctl(socket, SIOCGIFADDR, &ifr);
- if (!rv)
- return MapSystemError(rv);
+ if (rv == -1)
+ return MapSystemError(errno);
*address = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr)->sin_addr.s_addr;
return OK;
}
@@ -128,7 +128,7 @@ int UDPSocketLibevent::GetPeerAddress(IPEndPoint* address) const {
return MapSystemError(errno);
scoped_ptr<IPEndPoint> address(new IPEndPoint());
if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
+ return ERR_ADDRESS_INVALID;
remote_address_.reset(address.release());
}
@@ -148,7 +148,7 @@ int UDPSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
return MapSystemError(errno);
scoped_ptr<IPEndPoint> address(new IPEndPoint());
if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
+ return ERR_ADDRESS_INVALID;
local_address_.reset(address.release());
net_log_.AddEvent(NetLog::TYPE_UDP_LOCAL_ADDRESS,
CreateNetLogUDPConnectCallback(local_address_.get()));
@@ -271,7 +271,7 @@ int UDPSocketLibevent::InternalConnect(const IPEndPoint& address) {
// else connect() does the DatagramSocket::DEFAULT_BIND
if (rv < 0) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", rv);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", -rv);
Close();
return rv;
}
@@ -315,20 +315,22 @@ int UDPSocketLibevent::Bind(const IPEndPoint& address) {
return rv;
}
-bool UDPSocketLibevent::SetReceiveBufferSize(int32 size) {
+int UDPSocketLibevent::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;
+ int last_error = errno;
+ DCHECK(!rv) << "Could not set socket receive buffer size: " << last_error;
+ return rv == 0 ? OK : MapSystemError(last_error);
}
-bool UDPSocketLibevent::SetSendBufferSize(int32 size) {
+int UDPSocketLibevent::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;
+ int last_error = errno;
+ DCHECK(!rv) << "Could not set socket send buffer size: " << last_error;
+ return rv == 0 ? OK : MapSystemError(last_error);
}
void UDPSocketLibevent::AllowAddressReuse() {
@@ -397,7 +399,7 @@ void UDPSocketLibevent::LogRead(int result,
return;
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
DCHECK(addr_len > 0);
DCHECK(addr);
@@ -448,7 +450,7 @@ void UDPSocketLibevent::LogWrite(int result,
return;
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_UDP_BYTES_SENT,
CreateNetLogUDPDataTranferCallback(result, bytes, address));
@@ -476,7 +478,7 @@ int UDPSocketLibevent::InternalRecvFrom(IOBuffer* buf, int buf_len,
if (bytes_transferred >= 0) {
result = bytes_transferred;
if (address && !address->FromSockAddr(storage.addr, storage.addr_len))
- result = ERR_FAILED;
+ result = ERR_ADDRESS_INVALID;
} else {
result = MapSystemError(errno);
}
@@ -494,7 +496,7 @@ int UDPSocketLibevent::InternalSendTo(IOBuffer* buf, int buf_len,
storage.addr_len = 0;
} else {
if (!address->ToSockAddr(storage.addr, &storage.addr_len)) {
- int result = ERR_FAILED;
+ int result = ERR_ADDRESS_INVALID;
LogWrite(result, NULL, NULL);
return result;
}
@@ -612,6 +614,13 @@ int UDPSocketLibevent::DoBind(const IPEndPoint& address) {
return OK;
int last_error = errno;
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketBindErrorFromPosix", last_error);
+#if defined(OS_CHROMEOS)
+ if (last_error == EINVAL)
+ return ERR_ADDRESS_IN_USE;
+#elif defined(OS_MACOSX)
+ if (last_error == EADDRNOTAVAIL)
+ return ERR_ADDRESS_IN_USE;
+#endif
return MapSystemError(last_error);
}
@@ -760,4 +769,8 @@ int UDPSocketLibevent::SetDiffServCodePoint(DiffServCodePoint dscp) {
return OK;
}
+void UDPSocketLibevent::DetachFromThread() {
+ base::NonThreadSafe::DetachFromThread();
+}
+
} // namespace net
diff --git a/chromium/net/udp/udp_socket_libevent.h b/chromium/net/udp/udp_socket_libevent.h
index 2d1cefd3aa9..8ec5c212c09 100644
--- a/chromium/net/udp/udp_socket_libevent.h
+++ b/chromium/net/udp/udp_socket_libevent.h
@@ -94,10 +94,10 @@ class NET_EXPORT UDPSocketLibevent : public base::NonThreadSafe {
const CompletionCallback& callback);
// Set the receive buffer size (in bytes) for the socket.
- bool SetReceiveBufferSize(int32 size);
+ int SetReceiveBufferSize(int32 size);
// Set the send buffer size (in bytes) for the socket.
- bool SetSendBufferSize(int32 size);
+ int SetSendBufferSize(int32 size);
// Returns true if the socket is already connected or bound.
bool is_connected() const { return socket_ != kInvalidSocket; }
@@ -163,6 +163,9 @@ class NET_EXPORT UDPSocketLibevent : public base::NonThreadSafe {
// Return a network error code.
int SetDiffServCodePoint(DiffServCodePoint dscp);
+ // Resets the thread to be used for thread-safety checks.
+ void DetachFromThread();
+
private:
enum SocketOptions {
SOCKET_OPTION_REUSE_ADDRESS = 1 << 0,
diff --git a/chromium/net/udp/udp_socket_win.cc b/chromium/net/udp/udp_socket_win.cc
index 17fec44d7f0..4f742b93f03 100644
--- a/chromium/net/udp/udp_socket_win.cc
+++ b/chromium/net/udp/udp_socket_win.cc
@@ -339,7 +339,7 @@ int UDPSocketWin::InternalConnect(const IPEndPoint& address) {
// else connect() does the DatagramSocket::DEFAULT_BIND
if (rv < 0) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", rv);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", -rv);
Close();
return rv;
}
@@ -388,20 +388,47 @@ int UDPSocketWin::CreateSocket(int addr_family) {
return OK;
}
-bool UDPSocketWin::SetReceiveBufferSize(int32 size) {
+int UDPSocketWin::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;
+ if (rv != 0)
+ return MapSystemError(WSAGetLastError());
+
+ // According to documentation, setsockopt may succeed, but we need to check
+ // the results via getsockopt to be sure it works on Windows.
+ int32 actual_size = 0;
+ int option_size = sizeof(actual_size);
+ rv = getsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
+ reinterpret_cast<char*>(&actual_size), &option_size);
+ if (rv != 0)
+ return MapSystemError(WSAGetLastError());
+ if (actual_size >= size)
+ return OK;
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SocketUnchangeableReceiveBuffer",
+ actual_size, 1000, 1000000, 50);
+ return ERR_SOCKET_RECEIVE_BUFFER_SIZE_UNCHANGEABLE;
}
-bool UDPSocketWin::SetSendBufferSize(int32 size) {
+int UDPSocketWin::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;
+ if (rv != 0)
+ return MapSystemError(WSAGetLastError());
+ // According to documentation, setsockopt may succeed, but we need to check
+ // the results via getsockopt to be sure it works on Windows.
+ int32 actual_size = 0;
+ int option_size = sizeof(actual_size);
+ rv = getsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<char*>(&actual_size), &option_size);
+ if (rv != 0)
+ return MapSystemError(WSAGetLastError());
+ if (actual_size >= size)
+ return OK;
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SocketUnchangeableSendBuffer",
+ actual_size, 1000, 1000000, 50);
+ return ERR_SOCKET_SEND_BUFFER_SIZE_UNCHANGEABLE;
}
void UDPSocketWin::AllowAddressReuse() {
@@ -461,7 +488,7 @@ void UDPSocketWin::LogRead(int result, const char* bytes) const {
return;
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
// Get address for logging, if |address| is NULL.
IPEndPoint address;
bool is_address_valid = ReceiveAddressToIPEndpoint(&address);
@@ -497,7 +524,7 @@ void UDPSocketWin::LogWrite(int result,
return;
}
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_UDP_BYTES_SENT,
CreateNetLogUDPDataTranferCallback(result, bytes, address));
@@ -529,7 +556,7 @@ int UDPSocketWin::InternalRecvFrom(IOBuffer* buf, int buf_len,
// Convert address.
if (address && result >= 0) {
if (!ReceiveAddressToIPEndpoint(address))
- result = ERR_FAILED;
+ result = ERR_ADDRESS_INVALID;
}
LogRead(result, buf->data());
return result;
@@ -558,7 +585,7 @@ int UDPSocketWin::InternalSendTo(IOBuffer* buf, int buf_len,
storage.addr_len = 0;
} else {
if (!address->ToSockAddr(addr, &storage.addr_len)) {
- int result = ERR_FAILED;
+ int result = ERR_ADDRESS_INVALID;
LogWrite(result, NULL, NULL);
return result;
}
@@ -670,7 +697,12 @@ int UDPSocketWin::DoBind(const IPEndPoint& address) {
int last_error = WSAGetLastError();
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketBindErrorFromWinOS", last_error);
// Map some codes that are special to bind() separately.
- if (last_error == WSAEACCES || last_error == WSAEINVAL)
+ // * WSAEACCES: If a port is already bound to a socket, WSAEACCES may be
+ // returned instead of WSAEADDRINUSE, depending on whether the socket
+ // option SO_REUSEADDR or SO_EXCLUSIVEADDRUSE is set and whether the
+ // conflicting socket is owned by a different user account. See the MSDN
+ // page "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" for the gory details.
+ if (last_error == WSAEACCES || last_error == WSAEADDRNOTAVAIL)
return ERR_ADDRESS_IN_USE;
return MapSystemError(last_error);
}
@@ -805,4 +837,8 @@ int UDPSocketWin::SetDiffServCodePoint(DiffServCodePoint dscp) {
return ERR_NOT_IMPLEMENTED;
}
+void UDPSocketWin::DetachFromThread() {
+ base::NonThreadSafe::DetachFromThread();
+}
+
} // namespace net
diff --git a/chromium/net/udp/udp_socket_win.h b/chromium/net/udp/udp_socket_win.h
index 2f24877c1e0..7ccb73f64f9 100644
--- a/chromium/net/udp/udp_socket_win.h
+++ b/chromium/net/udp/udp_socket_win.h
@@ -95,10 +95,12 @@ class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) {
const CompletionCallback& callback);
// Set the receive buffer size (in bytes) for the socket.
- bool SetReceiveBufferSize(int32 size);
+ // Returns a net error code.
+ int SetReceiveBufferSize(int32 size);
// Set the send buffer size (in bytes) for the socket.
- bool SetSendBufferSize(int32 size);
+ // Returns a net error code.
+ int SetSendBufferSize(int32 size);
// Returns true if the socket is already connected or bound.
bool is_connected() const { return socket_ != INVALID_SOCKET; }
@@ -161,6 +163,9 @@ class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) {
// do anything on some platforms.
int SetDiffServCodePoint(DiffServCodePoint dscp);
+ // Resets the thread to be used for thread-safety checks.
+ void DetachFromThread();
+
private:
enum SocketOptions {
SOCKET_OPTION_REUSE_ADDRESS = 1 << 0,
diff --git a/chromium/net/url_request/file_protocol_handler.cc b/chromium/net/url_request/file_protocol_handler.cc
index ef8096f8798..ceed930985c 100644
--- a/chromium/net/url_request/file_protocol_handler.cc
+++ b/chromium/net/url_request/file_protocol_handler.cc
@@ -6,8 +6,8 @@
#include "base/logging.h"
#include "base/task_runner.h"
+#include "net/base/filename_util.h"
#include "net/base/net_errors.h"
-#include "net/base/net_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_dir_job.h"
diff --git a/chromium/net/url_request/http_user_agent_settings.h b/chromium/net/url_request/http_user_agent_settings.h
index d475a88ebd8..40eb1a6739c 100644
--- a/chromium/net/url_request/http_user_agent_settings.h
+++ b/chromium/net/url_request/http_user_agent_settings.h
@@ -24,9 +24,8 @@ class NET_EXPORT HttpUserAgentSettings {
// Gets the value of 'Accept-Language' header field.
virtual std::string GetAcceptLanguage() const = 0;
- // Gets the UA string to use for the given URL. Pass an empty URL to get
- // the default UA string.
- virtual std::string GetUserAgent(const GURL& url) const = 0;
+ // Gets the UA string.
+ virtual std::string GetUserAgent() const = 0;
private:
DISALLOW_COPY_AND_ASSIGN(HttpUserAgentSettings);
diff --git a/chromium/net/url_request/protocol_intercept_job_factory.cc b/chromium/net/url_request/protocol_intercept_job_factory.cc
deleted file mode 100644
index d0f92f4bff3..00000000000
--- a/chromium/net/url_request/protocol_intercept_job_factory.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2011 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/url_request/protocol_intercept_job_factory.h"
-
-#include "base/logging.h"
-
-namespace net {
-
-ProtocolInterceptJobFactory::ProtocolInterceptJobFactory(
- scoped_ptr<URLRequestJobFactory> job_factory,
- scoped_ptr<ProtocolHandler> protocol_handler)
- : job_factory_(job_factory.Pass()),
- protocol_handler_(protocol_handler.Pass()) {
-}
-
-ProtocolInterceptJobFactory::~ProtocolInterceptJobFactory() {}
-
-URLRequestJob* ProtocolInterceptJobFactory::MaybeCreateJobWithProtocolHandler(
- const std::string& scheme,
- URLRequest* request,
- NetworkDelegate* network_delegate) const {
- DCHECK(CalledOnValidThread());
- URLRequestJob* job = protocol_handler_->MaybeCreateJob(request,
- network_delegate);
- if (job)
- return job;
- return job_factory_->MaybeCreateJobWithProtocolHandler(
- scheme, request, network_delegate);
-}
-
-bool ProtocolInterceptJobFactory::IsHandledProtocol(
- const std::string& scheme) const {
- return job_factory_->IsHandledProtocol(scheme);
-}
-
-bool ProtocolInterceptJobFactory::IsHandledURL(const GURL& url) const {
- return job_factory_->IsHandledURL(url);
-}
-
-bool ProtocolInterceptJobFactory::IsSafeRedirectTarget(
- const GURL& location) const {
- return job_factory_->IsSafeRedirectTarget(location);
-}
-
-} // namespace net
diff --git a/chromium/net/url_request/protocol_intercept_job_factory.h b/chromium/net/url_request/protocol_intercept_job_factory.h
deleted file mode 100644
index abc2fb88d69..00000000000
--- a/chromium/net/url_request/protocol_intercept_job_factory.h
+++ /dev/null
@@ -1,51 +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_URL_REQUEST_PROTOCOL_INTERCEPT_JOB_FACTORY_H_
-#define NET_URL_REQUEST_PROTOCOL_INTERCEPT_JOB_FACTORY_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/url_request/url_request_job_factory.h"
-
-class GURL;
-
-namespace net {
-
-class URLRequest;
-class URLRequestJob;
-
-// This class acts as a wrapper for URLRequestJobFactory. |protocol_handler_| is
-// given the option of creating a URLRequestJob for each potential URLRequest.
-// If |protocol_handler_| does not create a job (i.e. MaybeCreateJob() returns
-// NULL) the URLRequest is forwarded to the |job_factory_| to be handled there.
-// Only the MaybeCreateJob() member of |protocol_handler_| is called; the
-// IsSafeRedirectTarget() member is not used.
-class NET_EXPORT ProtocolInterceptJobFactory : public URLRequestJobFactory {
- public:
- ProtocolInterceptJobFactory(scoped_ptr<URLRequestJobFactory> job_factory,
- scoped_ptr<ProtocolHandler> protocol_handler);
- virtual ~ProtocolInterceptJobFactory();
-
- // URLRequestJobFactory implementation
- virtual URLRequestJob* MaybeCreateJobWithProtocolHandler(
- const std::string& scheme,
- URLRequest* request,
- NetworkDelegate* network_delegate) const OVERRIDE;
- virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE;
- virtual bool IsHandledURL(const GURL& url) const OVERRIDE;
- virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE;
-
- private:
- scoped_ptr<URLRequestJobFactory> job_factory_;
- scoped_ptr<ProtocolHandler> protocol_handler_;
-
- DISALLOW_COPY_AND_ASSIGN(ProtocolInterceptJobFactory);
-};
-
-} // namespace net
-
-#endif // NET_URL_REQUEST_PROTOCOL_INTERCEPT_JOB_FACTORY_H_
diff --git a/chromium/net/url_request/static_http_user_agent_settings.cc b/chromium/net/url_request/static_http_user_agent_settings.cc
index 1fd199234da..dd74f965eb3 100644
--- a/chromium/net/url_request/static_http_user_agent_settings.cc
+++ b/chromium/net/url_request/static_http_user_agent_settings.cc
@@ -20,7 +20,7 @@ std::string StaticHttpUserAgentSettings::GetAcceptLanguage() const {
return accept_language_;
}
-std::string StaticHttpUserAgentSettings::GetUserAgent(const GURL& url) const {
+std::string StaticHttpUserAgentSettings::GetUserAgent() const {
return user_agent_;
}
diff --git a/chromium/net/url_request/static_http_user_agent_settings.h b/chromium/net/url_request/static_http_user_agent_settings.h
index 8819daa8942..7406abcb21e 100644
--- a/chromium/net/url_request/static_http_user_agent_settings.h
+++ b/chromium/net/url_request/static_http_user_agent_settings.h
@@ -24,7 +24,7 @@ class NET_EXPORT StaticHttpUserAgentSettings : public HttpUserAgentSettings {
// HttpUserAgentSettings implementation
virtual std::string GetAcceptLanguage() const OVERRIDE;
- virtual std::string GetUserAgent(const GURL& url) const OVERRIDE;
+ virtual std::string GetUserAgent() const OVERRIDE;
private:
const std::string accept_language_;
diff --git a/chromium/net/url_request/test_url_fetcher_factory.cc b/chromium/net/url_request/test_url_fetcher_factory.cc
index 5a72d71dfb2..bd4f5e187a3 100644
--- a/chromium/net/url_request/test_url_fetcher_factory.cc
+++ b/chromium/net/url_request/test_url_fetcher_factory.cc
@@ -8,9 +8,12 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/file_util.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_restrictions.h"
#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_fetcher_delegate.h"
@@ -91,6 +94,10 @@ int TestURLFetcher::GetLoadFlags() const {
void TestURLFetcher::SetReferrer(const std::string& referrer) {
}
+void TestURLFetcher::SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) {
+}
+
void TestURLFetcher::SetExtraRequestHeaders(
const std::string& extra_request_headers) {
fake_extra_request_headers_.Clear();
@@ -101,11 +108,6 @@ void TestURLFetcher::AddExtraRequestHeader(const std::string& header_line) {
fake_extra_request_headers_.AddHeaderFromString(header_line);
}
-void TestURLFetcher::GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const {
- *headers = fake_extra_request_headers_;
-}
-
void TestURLFetcher::SetRequestContext(
URLRequestContextGetter* request_context_getter) {
}
@@ -143,6 +145,12 @@ void TestURLFetcher::SetAutomaticallyRetryOnNetworkChanges(int max_retries) {
void TestURLFetcher::SaveResponseToFileAtPath(
const base::FilePath& file_path,
scoped_refptr<base::SequencedTaskRunner> file_task_runner) {
+ SetResponseFilePath(file_path);
+ // Asynchronous IO is not supported, so file_task_runner is ignored.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ const size_t written_bytes = base::WriteFile(
+ file_path, fake_response_string_.c_str(), fake_response_string_.size());
+ DCHECK_EQ(written_bytes, fake_response_string_.size());
}
void TestURLFetcher::SaveResponseToTemporaryFile(
@@ -151,6 +159,33 @@ void TestURLFetcher::SaveResponseToTemporaryFile(
void TestURLFetcher::SaveResponseWithWriter(
scoped_ptr<URLFetcherResponseWriter> response_writer) {
+ // In class URLFetcherCore this method is called by all three:
+ // GetResponseAsString() / SaveResponseToFileAtPath() /
+ // SaveResponseToTemporaryFile(). But here (in TestURLFetcher), this method
+ // is never used by any of these three methods. So, file writing is expected
+ // to be done in SaveResponseToFileAtPath(), and this method supports only
+ // URLFetcherStringWriter (for testing of this method only).
+ if (fake_response_destination_ == STRING) {
+ response_writer_ = response_writer.Pass();
+ int response = response_writer_->Initialize(CompletionCallback());
+ // The TestURLFetcher doesn't handle asynchronous writes.
+ DCHECK_EQ(OK, response);
+
+ scoped_refptr<IOBuffer> buffer(new StringIOBuffer(fake_response_string_));
+ response = response_writer_->Write(buffer.get(),
+ fake_response_string_.size(),
+ CompletionCallback());
+ DCHECK_EQ(static_cast<int>(fake_response_string_.size()), response);
+ response = response_writer_->Finish(CompletionCallback());
+ DCHECK_EQ(OK, response);
+ } else if (fake_response_destination_ == TEMP_FILE) {
+ // SaveResponseToFileAtPath() should be called instead of this method to
+ // save file. Asynchronous file writing using URLFetcherFileWriter is not
+ // supported.
+ NOTIMPLEMENTED();
+ } else {
+ NOTREACHED();
+ }
}
HttpResponseHeaders* TestURLFetcher::GetResponseHeaders() const {
@@ -213,6 +248,11 @@ bool TestURLFetcher::GetResponseAsFilePath(
return true;
}
+void TestURLFetcher::GetExtraRequestHeaders(
+ HttpRequestHeaders* headers) const {
+ *headers = fake_extra_request_headers_;
+}
+
void TestURLFetcher::set_status(const URLRequestStatus& status) {
fake_status_ = status;
}
diff --git a/chromium/net/url_request/test_url_fetcher_factory.h b/chromium/net/url_request/test_url_fetcher_factory.h
index 75ffcade6ea..f94b8a9a972 100644
--- a/chromium/net/url_request/test_url_fetcher_factory.h
+++ b/chromium/net/url_request/test_url_fetcher_factory.h
@@ -104,11 +104,11 @@ class TestURLFetcher : public URLFetcher {
virtual void SetLoadFlags(int load_flags) OVERRIDE;
virtual int GetLoadFlags() const OVERRIDE;
virtual void SetReferrer(const std::string& referrer) OVERRIDE;
+ virtual void SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) OVERRIDE;
virtual void SetExtraRequestHeaders(
const std::string& extra_request_headers) OVERRIDE;
virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE;
- virtual void GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const OVERRIDE;
virtual void SetRequestContext(
URLRequestContextGetter* request_context_getter) OVERRIDE;
virtual void SetFirstPartyForCookies(
@@ -149,6 +149,8 @@ class TestURLFetcher : public URLFetcher {
virtual bool GetResponseAsFilePath(
bool take_ownership, base::FilePath* out_response_path) const OVERRIDE;
+ void GetExtraRequestHeaders(HttpRequestHeaders* headers) const;
+
// Sets owner of this class. Set it to a non-NULL value if you want
// to automatically unregister this fetcher from the owning factory
// upon destruction.
@@ -220,6 +222,7 @@ class TestURLFetcher : public URLFetcher {
HttpRequestHeaders fake_extra_request_headers_;
int fake_max_retries_;
base::TimeDelta fake_backoff_delay_;
+ scoped_ptr<URLFetcherResponseWriter> response_writer_;
DISALLOW_COPY_AND_ASSIGN(TestURLFetcher);
};
@@ -454,7 +457,6 @@ class URLFetcherImplFactory : public URLFetcherFactory {
const GURL& url,
URLFetcher::RequestType request_type,
URLFetcherDelegate* d) OVERRIDE;
-
};
} // namespace net
diff --git a/chromium/net/url_request/url_fetcher.h b/chromium/net/url_request/url_fetcher.h
index 4cc6f5fa62e..415d7e525cd 100644
--- a/chromium/net/url_request/url_fetcher.h
+++ b/chromium/net/url_request/url_fetcher.h
@@ -13,6 +13,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/supports_user_data.h"
#include "net/base/net_export.h"
+#include "net/url_request/url_request.h"
class GURL;
@@ -36,8 +37,9 @@ typedef std::vector<std::string> ResponseCookies;
// To use this class, create an instance with the desired URL and a pointer to
// the object to be notified when the URL has been loaded:
-// URLFetcher* fetcher = URLFetcher::Create("http://www.google.com",
-// URLFetcher::GET, this);
+// scoped_ptr<URLFetcher> fetcher(URLFetcher::Create("http://www.google.com",
+// URLFetcher::GET,
+// this));
//
// You must also set a request context getter:
//
@@ -50,6 +52,8 @@ typedef std::vector<std::string> ResponseCookies;
// Finally, start the request:
// fetcher->Start();
//
+// You may cancel the request by destroying the URLFetcher:
+// fetcher.reset();
//
// The object you supply as a delegate must inherit from
// URLFetcherDelegate; when the fetch is completed,
@@ -93,6 +97,7 @@ class NET_EXPORT URLFetcher {
// |url| is the URL to send the request to.
// |request_type| is the type of request to make.
// |d| the object that will receive the callback on fetch completion.
+ // Caller is responsible for destroying the returned URLFetcher.
static URLFetcher* Create(const GURL& url,
URLFetcher::RequestType request_type,
URLFetcherDelegate* d);
@@ -100,6 +105,7 @@ class NET_EXPORT URLFetcher {
// Like above, but if there's a URLFetcherFactory registered with the
// implementation it will be used. |id| may be used during testing to identify
// who is creating the URLFetcher.
+ // Caller is responsible for destroying the returned URLFetcher.
static URLFetcher* Create(int id,
const GURL& url,
URLFetcher::RequestType request_type,
@@ -170,6 +176,11 @@ class NET_EXPORT URLFetcher {
// started.
virtual void SetReferrer(const std::string& referrer) = 0;
+ // The referrer policy to apply when updating the referrer during redirects.
+ // The referrer policy may only be changed before Start() is called.
+ virtual void SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) = 0;
+
// Set extra headers on the request. Must be called before the request
// is started.
// This replaces the entire extra request headers.
@@ -181,9 +192,6 @@ class NET_EXPORT URLFetcher {
// This appends the header to the current extra request headers.
virtual void AddExtraRequestHeader(const std::string& header_line) = 0;
- virtual void GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const = 0;
-
// Set the URLRequestContext on the request. Must be called before the
// request is started.
virtual void SetRequestContext(
@@ -278,7 +286,7 @@ class NET_EXPORT URLFetcher {
// if an error prevented any response from being received.
virtual int GetResponseCode() const = 0;
- // Cookies recieved.
+ // Cookies received.
virtual const ResponseCookies& GetCookies() const = 0;
// Reports that the received content was malformed.
diff --git a/chromium/net/url_request/url_fetcher_core.cc b/chromium/net/url_request/url_fetcher_core.cc
index e2fcc450219..eb45ecf52e3 100644
--- a/chromium/net/url_request/url_fetcher_core.cc
+++ b/chromium/net/url_request/url_fetcher_core.cc
@@ -82,6 +82,8 @@ URLFetcherCore::URLFetcherCore(URLFetcher* fetcher,
upload_content_set_(false),
upload_range_offset_(0),
upload_range_length_(0),
+ referrer_policy_(
+ URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
is_chunked_upload_(false),
was_cancelled_(false),
stop_on_redirect_(false),
@@ -204,6 +206,11 @@ void URLFetcherCore::SetReferrer(const std::string& referrer) {
referrer_ = referrer;
}
+void URLFetcherCore::SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) {
+ referrer_policy_ = referrer_policy;
+}
+
void URLFetcherCore::SetExtraRequestHeaders(
const std::string& extra_request_headers) {
extra_request_headers_.Clear();
@@ -214,11 +221,6 @@ void URLFetcherCore::AddExtraRequestHeader(const std::string& header_line) {
extra_request_headers_.AddHeaderFromString(header_line);
}
-void URLFetcherCore::GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const {
- headers->CopyFrom(extra_request_headers_);
-}
-
void URLFetcherCore::SetRequestContext(
URLRequestContextGetter* request_context_getter) {
DCHECK(!request_context_getter_.get());
@@ -505,7 +507,7 @@ void URLFetcherCore::StartURLRequest() {
g_registry.Get().AddURLFetcherCore(this);
current_response_bytes_ = 0;
request_ = request_context_getter_->GetURLRequestContext()->CreateRequest(
- original_url_, DEFAULT_PRIORITY, this);
+ original_url_, DEFAULT_PRIORITY, this, NULL);
request_->set_stack_trace(stack_trace_);
int flags = request_->load_flags() | load_flags_;
if (!g_interception_enabled)
@@ -515,6 +517,7 @@ void URLFetcherCore::StartURLRequest() {
request_->EnableChunkedUpload();
request_->SetLoadFlags(flags);
request_->SetReferrer(referrer_);
+ request_->set_referrer_policy(referrer_policy_);
request_->set_first_party_for_cookies(first_party_for_cookies_.is_empty() ?
original_url_ : first_party_for_cookies_);
if (url_request_data_key_ && !url_request_create_data_callback_.is_null()) {
diff --git a/chromium/net/url_request/url_fetcher_core.h b/chromium/net/url_request/url_fetcher_core.h
index c8864d4a64c..abf42b5248c 100644
--- a/chromium/net/url_request/url_fetcher_core.h
+++ b/chromium/net/url_request/url_fetcher_core.h
@@ -77,9 +77,9 @@ class URLFetcherCore
void SetLoadFlags(int load_flags);
int GetLoadFlags() const;
void SetReferrer(const std::string& referrer);
+ void SetReferrerPolicy(URLRequest::ReferrerPolicy referrer_policy);
void SetExtraRequestHeaders(const std::string& extra_request_headers);
void AddExtraRequestHeader(const std::string& header_line);
- void GetExtraRequestHeaders(HttpRequestHeaders* headers) const;
void SetRequestContext(URLRequestContextGetter* request_context_getter);
// Set the URL that should be consulted for the third-party cookie
// blocking policy.
@@ -242,7 +242,8 @@ class URLFetcherCore
uint64 upload_range_length_; // The length of the part of file to be
// uploaded.
std::string upload_content_type_; // MIME type of POST payload
- std::string referrer_; // HTTP Referer header value
+ std::string referrer_; // HTTP Referer header value and policy
+ URLRequest::ReferrerPolicy referrer_policy_;
bool is_chunked_upload_; // True if using chunked transfer encoding
// Used to determine how long to wait before making a request or doing a
diff --git a/chromium/net/url_request/url_fetcher_impl.cc b/chromium/net/url_request/url_fetcher_impl.cc
index 32253c92145..fc44551c475 100644
--- a/chromium/net/url_request/url_fetcher_impl.cc
+++ b/chromium/net/url_request/url_fetcher_impl.cc
@@ -57,6 +57,11 @@ void URLFetcherImpl::SetReferrer(const std::string& referrer) {
core_->SetReferrer(referrer);
}
+void URLFetcherImpl::SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) {
+ core_->SetReferrerPolicy(referrer_policy);
+}
+
void URLFetcherImpl::SetLoadFlags(int load_flags) {
core_->SetLoadFlags(load_flags);
}
@@ -74,11 +79,6 @@ void URLFetcherImpl::AddExtraRequestHeader(const std::string& header_line) {
core_->AddExtraRequestHeader(header_line);
}
-void URLFetcherImpl::GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const {
- GetExtraRequestHeaders(headers);
-}
-
void URLFetcherImpl::SetRequestContext(
URLRequestContextGetter* request_context_getter) {
core_->SetRequestContext(request_context_getter);
diff --git a/chromium/net/url_request/url_fetcher_impl.h b/chromium/net/url_request/url_fetcher_impl.h
index fe0362b3aea..bc271b233d8 100644
--- a/chromium/net/url_request/url_fetcher_impl.h
+++ b/chromium/net/url_request/url_fetcher_impl.h
@@ -14,6 +14,8 @@
#ifndef NET_URL_REQUEST_URL_FETCHER_IMPL_H_
#define NET_URL_REQUEST_URL_FETCHER_IMPL_H_
+#include <string>
+
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "net/base/net_export.h"
@@ -50,11 +52,11 @@ class NET_EXPORT_PRIVATE URLFetcherImpl : public URLFetcher {
virtual void SetLoadFlags(int load_flags) OVERRIDE;
virtual int GetLoadFlags() const OVERRIDE;
virtual void SetReferrer(const std::string& referrer) OVERRIDE;
+ virtual void SetReferrerPolicy(
+ URLRequest::ReferrerPolicy referrer_policy) OVERRIDE;
virtual void SetExtraRequestHeaders(
const std::string& extra_request_headers) OVERRIDE;
virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE;
- virtual void GetExtraRequestHeaders(
- HttpRequestHeaders* headers) const OVERRIDE;
virtual void SetRequestContext(
URLRequestContextGetter* request_context_getter) OVERRIDE;
virtual void SetFirstPartyForCookies(
diff --git a/chromium/net/url_request/url_fetcher_impl_unittest.cc b/chromium/net/url_request/url_fetcher_impl_unittest.cc
index c96993f89a4..39e2bb286a9 100644
--- a/chromium/net/url_request/url_fetcher_impl_unittest.cc
+++ b/chromium/net/url_request/url_fetcher_impl_unittest.cc
@@ -1433,7 +1433,7 @@ TEST_F(URLFetcherFileTest, OverwriteExistingFile) {
std::string data(10000, '?'); // Meant to be larger than simple.html.
file_path_ = temp_dir.path().AppendASCII(kFileToFetch);
ASSERT_EQ(static_cast<int>(data.size()),
- file_util::WriteFile(file_path_, data.data(), data.size()));
+ base::WriteFile(file_path_, data.data(), data.size()));
ASSERT_TRUE(base::PathExists(file_path_));
expected_file_ = test_server.GetDocumentRoot().AppendASCII(kFileToFetch);
ASSERT_FALSE(base::ContentsEqual(file_path_, expected_file_));
diff --git a/chromium/net/url_request/url_fetcher_response_writer.cc b/chromium/net/url_request/url_fetcher_response_writer.cc
index 1a63d406c0a..6e3b61314ac 100644
--- a/chromium/net/url_request/url_fetcher_response_writer.cc
+++ b/chromium/net/url_request/url_fetcher_response_writer.cc
@@ -52,10 +52,10 @@ URLFetcherStringWriter* URLFetcherStringWriter::AsStringWriter() {
URLFetcherFileWriter::URLFetcherFileWriter(
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
const base::FilePath& file_path)
- : weak_factory_(this),
- file_task_runner_(file_task_runner),
+ : file_task_runner_(file_task_runner),
file_path_(file_path),
- owns_file_(false) {
+ owns_file_(false),
+ weak_factory_(this) {
DCHECK(file_task_runner_.get());
}
@@ -64,7 +64,7 @@ URLFetcherFileWriter::~URLFetcherFileWriter() {
}
int URLFetcherFileWriter::Initialize(const CompletionCallback& callback) {
- file_stream_.reset(new FileStream(NULL));
+ file_stream_.reset(new FileStream(file_task_runner_));
int result = ERR_IO_PENDING;
if (file_path_.empty()) {
@@ -80,8 +80,8 @@ int URLFetcherFileWriter::Initialize(const CompletionCallback& callback) {
} else {
result = file_stream_->Open(
file_path_,
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC |
- base::PLATFORM_FILE_CREATE_ALWAYS,
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC |
+ base::File::FLAG_CREATE_ALWAYS,
base::Bind(&URLFetcherFileWriter::DidOpenFile,
weak_factory_.GetWeakPtr(),
callback));
@@ -107,12 +107,16 @@ int URLFetcherFileWriter::Write(IOBuffer* buffer,
}
int URLFetcherFileWriter::Finish(const CompletionCallback& callback) {
- int result = file_stream_->Close(base::Bind(
- &URLFetcherFileWriter::CloseComplete,
- weak_factory_.GetWeakPtr(), callback));
- if (result != ERR_IO_PENDING)
- file_stream_.reset();
- return result;
+ // If the file_stream_ still exists at this point, close it.
+ if (file_stream_) {
+ int result = file_stream_->Close(base::Bind(
+ &URLFetcherFileWriter::CloseComplete,
+ weak_factory_.GetWeakPtr(), callback));
+ if (result != ERR_IO_PENDING)
+ file_stream_.reset();
+ return result;
+ }
+ return OK;
}
URLFetcherFileWriter* URLFetcherFileWriter::AsFileWriter() {
@@ -158,8 +162,8 @@ void URLFetcherFileWriter::DidCreateTempFile(const CompletionCallback& callback,
owns_file_ = true;
const int result = file_stream_->Open(
file_path_,
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC |
- base::PLATFORM_FILE_OPEN,
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC |
+ base::File::FLAG_OPEN,
base::Bind(&URLFetcherFileWriter::DidOpenFile,
weak_factory_.GetWeakPtr(),
callback));
diff --git a/chromium/net/url_request/url_fetcher_response_writer.h b/chromium/net/url_request/url_fetcher_response_writer.h
index 422a8a400ad..428f441cf29 100644
--- a/chromium/net/url_request/url_fetcher_response_writer.h
+++ b/chromium/net/url_request/url_fetcher_response_writer.h
@@ -5,6 +5,8 @@
#ifndef NET_URL_REQUEST_URL_FETCHER_RESPONSE_WRITER_H_
#define NET_URL_REQUEST_URL_FETCHER_RESPONSE_WRITER_H_
+#include <string>
+
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
@@ -119,9 +121,6 @@ class NET_EXPORT URLFetcherFileWriter : public URLFetcherResponseWriter {
// Callback which gets the result of closing a file.
void CloseComplete(const CompletionCallback& callback, int result);
- // Callbacks are created for use with base::FileUtilProxy.
- base::WeakPtrFactory<URLFetcherFileWriter> weak_factory_;
-
// Task runner on which file operations should happen.
scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
@@ -134,6 +133,9 @@ class NET_EXPORT URLFetcherFileWriter : public URLFetcherResponseWriter {
scoped_ptr<FileStream> file_stream_;
+ // Callbacks are created for use with base::FileUtilProxy.
+ base::WeakPtrFactory<URLFetcherFileWriter> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(URLFetcherFileWriter);
};
diff --git a/chromium/net/url_request/url_fetcher_response_writer_unittest.cc b/chromium/net/url_request/url_fetcher_response_writer_unittest.cc
new file mode 100644
index 00000000000..629e4bc3ce6
--- /dev/null
+++ b/chromium/net/url_request/url_fetcher_response_writer_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2014 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/url_request/url_fetcher_response_writer.h"
+
+#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"
+#include "testing/platform_test.h"
+
+namespace net {
+
+namespace {
+
+const char kData[] = "Hello!";
+
+} // namespace
+
+class URLFetcherStringWriterTest : public PlatformTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ writer_.reset(new URLFetcherStringWriter);
+ buf_ = new StringIOBuffer(kData);
+ }
+
+ scoped_ptr<URLFetcherStringWriter> writer_;
+ scoped_refptr<StringIOBuffer> buf_;
+};
+
+TEST_F(URLFetcherStringWriterTest, Basic) {
+ int rv = 0;
+ // Initialize(), Write() and Finish().
+ TestCompletionCallback callback;
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Write(buf_.get(), buf_->size(), callback.callback());
+ EXPECT_EQ(buf_->size(), callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Verify the result.
+ EXPECT_EQ(kData, writer_->data());
+
+ // Initialize() again to reset.
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ EXPECT_TRUE(writer_->data().empty());
+}
+
+class URLFetcherFileWriterTest : public PlatformTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ file_path_ = temp_dir_.path().AppendASCII("test.txt");
+ writer_.reset(new URLFetcherFileWriter(
+ base::MessageLoopProxy::current(), file_path_));
+ buf_ = new StringIOBuffer(kData);
+ }
+
+ base::ScopedTempDir temp_dir_;
+ base::FilePath file_path_;
+ scoped_ptr<URLFetcherFileWriter> writer_;
+ scoped_refptr<StringIOBuffer> buf_;
+};
+
+TEST_F(URLFetcherFileWriterTest, WriteToFile) {
+ int rv = 0;
+ // Initialize(), Write() and Finish().
+ TestCompletionCallback callback;
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Write(buf_.get(), buf_->size(), callback.callback());
+ EXPECT_EQ(buf_->size(), callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Verify the result.
+ EXPECT_EQ(file_path_.value(), writer_->file_path().value());
+ std::string file_contents;
+ EXPECT_TRUE(base::ReadFileToString(writer_->file_path(), &file_contents));
+ EXPECT_EQ(kData, file_contents);
+
+ // Destroy the writer. File should be deleted.
+ writer_.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(file_path_));
+}
+
+TEST_F(URLFetcherFileWriterTest, InitializeAgain) {
+ int rv = 0;
+ // Initialize(), Write() and Finish().
+ TestCompletionCallback callback;
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Write(buf_.get(), buf_->size(), callback.callback());
+ EXPECT_EQ(buf_->size(), callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Verify the result.
+ std::string file_contents;
+ EXPECT_TRUE(base::ReadFileToString(writer_->file_path(), &file_contents));
+ EXPECT_EQ(kData, file_contents);
+
+ // Initialize() again to reset. Write different data.
+ const std::string data2 = "Bye!";
+ scoped_refptr<StringIOBuffer> buf2(new StringIOBuffer(data2));
+
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Write(buf2.get(), buf2->size(), callback.callback());
+ EXPECT_EQ(buf2->size(), callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Verify the result.
+ file_contents.clear();
+ EXPECT_TRUE(base::ReadFileToString(writer_->file_path(), &file_contents));
+ EXPECT_EQ(data2, file_contents);
+}
+
+TEST_F(URLFetcherFileWriterTest, DisownFile) {
+ int rv = 0;
+ // Initialize() and Finish() to create a file.
+ TestCompletionCallback callback;
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Disown file.
+ writer_->DisownFile();
+
+ // File is not deleted even after the writer gets destroyed.
+ writer_.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(base::PathExists(file_path_));
+}
+
+class URLFetcherFileWriterTemporaryFileTest : public PlatformTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ writer_.reset(new URLFetcherFileWriter(
+ base::MessageLoopProxy::current(), base::FilePath()));
+ buf_ = new StringIOBuffer(kData);
+ }
+
+ scoped_ptr<URLFetcherFileWriter> writer_;
+ scoped_refptr<StringIOBuffer> buf_;
+};
+
+TEST_F(URLFetcherFileWriterTemporaryFileTest, WriteToTemporaryFile) {
+ int rv = 0;
+ // Initialize(), Write() and Finish().
+ TestCompletionCallback callback;
+ rv = writer_->Initialize(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+ rv = writer_->Write(buf_.get(), buf_->size(), callback.callback());
+ EXPECT_EQ(buf_->size(), callback.GetResult(rv));
+ rv = writer_->Finish(callback.callback());
+ EXPECT_EQ(OK, callback.GetResult(rv));
+
+ // Verify the result.
+ std::string file_contents;
+ EXPECT_TRUE(base::ReadFileToString(writer_->file_path(), &file_contents));
+ EXPECT_EQ(kData, file_contents);
+
+ // Destroy the writer. File should be deleted.
+ const base::FilePath file_path = writer_->file_path();
+ writer_.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+} // namespace net
diff --git a/chromium/net/url_request/url_range_request_job.cc b/chromium/net/url_request/url_range_request_job.cc
new file mode 100644
index 00000000000..cad7b83604d
--- /dev/null
+++ b/chromium/net/url_request/url_range_request_job.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 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/url_request/url_range_request_job.h"
+
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
+
+namespace net {
+
+URLRangeRequestJob::URLRangeRequestJob(URLRequest* request,
+ NetworkDelegate* delegate)
+ : URLRequestJob(request, delegate), range_parse_result_(OK) {
+}
+
+URLRangeRequestJob::~URLRangeRequestJob() {
+}
+
+void URLRangeRequestJob::SetExtraRequestHeaders(
+ const HttpRequestHeaders& headers) {
+ std::string range_header;
+ if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
+ if (!HttpUtil::ParseRangeHeader(range_header, &ranges_)) {
+ range_parse_result_ = ERR_REQUEST_RANGE_NOT_SATISFIABLE;
+ }
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/url_request/url_range_request_job.h b/chromium/net/url_request/url_range_request_job.h
new file mode 100644
index 00000000000..85f394c0e94
--- /dev/null
+++ b/chromium/net/url_request/url_range_request_job.h
@@ -0,0 +1,43 @@
+// Copyright 2014 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_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
+#define NET_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
+
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/http/http_byte_range.h"
+#include "net/url_request/url_request_job.h"
+
+namespace net {
+
+class HttpRequestHeaders;
+
+// URLRequestJob with support for parsing range requests.
+// It is up to subclasses to handle the response
+// and deal with an errors parsing the range request header.
+// This must be done after Start() has been called.
+class NET_EXPORT URLRangeRequestJob : public URLRequestJob {
+ public:
+ URLRangeRequestJob(URLRequest* request,
+ NetworkDelegate* delegate);
+
+ virtual void SetExtraRequestHeaders(
+ const HttpRequestHeaders& headers) OVERRIDE;
+
+ const std::vector<HttpByteRange>& ranges() const { return ranges_; }
+ int range_parse_result() const { return range_parse_result_; }
+
+ protected:
+ virtual ~URLRangeRequestJob();
+
+ private:
+ std::vector<HttpByteRange> ranges_;
+ int range_parse_result_;
+};
+
+} // namespace net
+
+#endif // NET_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
diff --git a/chromium/net/url_request/url_request.cc b/chromium/net/url_request/url_request.cc
index 6b0124730d5..7495b7e5d8d 100644
--- a/chromium/net/url_request/url_request.cc
+++ b/chromium/net/url_request/url_request.cc
@@ -14,6 +14,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/metrics/stats_counters.h"
+#include "base/metrics/user_metrics.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
@@ -81,9 +82,8 @@ bool g_default_can_use_cookies = true;
// at which each event occurred. The API requires the time which the request
// was blocked on each phase. This function handles the conversion.
//
-// In the case of reusing a SPDY session or HTTP pipeline, old proxy results may
-// have been reused, so proxy resolution times may be before the request was
-// started.
+// In the case of reusing a SPDY session, old proxy results may have been
+// reused, so proxy resolution times may be before the request was started.
//
// Due to preconnect and late binding, it is also possible for the connection
// attempt to start before a request has been started, or proxy resolution
@@ -141,12 +141,6 @@ void ConvertRealLoadTimesToBlockingTimes(
} // namespace
-URLRequest::ProtocolFactory*
-URLRequest::Deprecated::RegisterProtocolFactory(const std::string& scheme,
- ProtocolFactory* factory) {
- return URLRequest::RegisterProtocolFactory(scheme, factory);
-}
-
void URLRequest::Deprecated::RegisterRequestInterceptor(
Interceptor* interceptor) {
URLRequest::RegisterRequestInterceptor(interceptor);
@@ -197,6 +191,10 @@ void URLRequest::Delegate::OnSSLCertificateError(URLRequest* request,
request->Cancel();
}
+void URLRequest::Delegate::OnBeforeNetworkStart(URLRequest* request,
+ bool* defer) {
+}
+
///////////////////////////////////////////////////////////////////////////////
// URLRequest
@@ -204,37 +202,17 @@ URLRequest::URLRequest(const GURL& url,
RequestPriority priority,
Delegate* delegate,
const URLRequestContext* context)
- : context_(context),
- network_delegate_(context->network_delegate()),
- net_log_(BoundNetLog::Make(context->net_log(),
- NetLog::SOURCE_URL_REQUEST)),
- url_chain_(1, url),
- method_("GET"),
- referrer_policy_(CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
- load_flags_(LOAD_NORMAL),
- delegate_(delegate),
- is_pending_(false),
- is_redirecting_(false),
- redirect_limit_(kMaxRedirects),
- priority_(priority),
- identifier_(GenerateURLRequestIdentifier()),
- calling_delegate_(false),
- use_blocked_by_as_load_param_(false),
- before_request_callback_(base::Bind(&URLRequest::BeforeRequestComplete,
- base::Unretained(this))),
- has_notified_completion_(false),
- received_response_content_length_(0),
- creation_time_(base::TimeTicks::Now()) {
- SIMPLE_STATS_COUNTER("URLRequestCount");
-
- // Sanity check out environment.
- DCHECK(base::MessageLoop::current())
- << "The current base::MessageLoop must exist";
-
- CHECK(context);
- context->url_requests()->insert(this);
+ : identifier_(GenerateURLRequestIdentifier()) {
+ Init(url, priority, delegate, context, NULL);
+}
- net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE);
+URLRequest::URLRequest(const GURL& url,
+ RequestPriority priority,
+ Delegate* delegate,
+ const URLRequestContext* context,
+ CookieStore* cookie_store)
+ : identifier_(GenerateURLRequestIdentifier()) {
+ Init(url, priority, delegate, context, cookie_store);
}
URLRequest::~URLRequest() {
@@ -261,13 +239,6 @@ URLRequest::~URLRequest() {
}
// static
-URLRequest::ProtocolFactory* URLRequest::RegisterProtocolFactory(
- const string& scheme, ProtocolFactory* factory) {
- return URLRequestJobManager::GetInstance()->RegisterProtocolFactory(scheme,
- factory);
-}
-
-// static
void URLRequest::RegisterRequestInterceptor(Interceptor* interceptor) {
URLRequestJobManager::GetInstance()->RegisterRequestInterceptor(interceptor);
}
@@ -278,6 +249,47 @@ void URLRequest::UnregisterRequestInterceptor(Interceptor* interceptor) {
interceptor);
}
+void URLRequest::Init(const GURL& url,
+ RequestPriority priority,
+ Delegate* delegate,
+ const URLRequestContext* context,
+ CookieStore* cookie_store) {
+ context_ = context;
+ network_delegate_ = context->network_delegate();
+ net_log_ = BoundNetLog::Make(context->net_log(), NetLog::SOURCE_URL_REQUEST);
+ url_chain_.push_back(url);
+ method_ = "GET";
+ referrer_policy_ = CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+ load_flags_ = LOAD_NORMAL;
+ delegate_ = delegate;
+ is_pending_ = false;
+ is_redirecting_ = false;
+ redirect_limit_ = kMaxRedirects;
+ priority_ = priority;
+ calling_delegate_ = false;
+ use_blocked_by_as_load_param_ =false;
+ before_request_callback_ = base::Bind(&URLRequest::BeforeRequestComplete,
+ base::Unretained(this));
+ has_notified_completion_ = false;
+ received_response_content_length_ = 0;
+ creation_time_ = base::TimeTicks::Now();
+ notified_before_network_start_ = false;
+
+ SIMPLE_STATS_COUNTER("URLRequestCount");
+
+ // Sanity check out environment.
+ DCHECK(base::MessageLoop::current())
+ << "The current base::MessageLoop must exist";
+
+ CHECK(context);
+ context->url_requests()->insert(this);
+ cookie_store_ = cookie_store;
+ if (cookie_store_ == NULL)
+ cookie_store_ = context->cookie_store();
+
+ net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE);
+}
+
void URLRequest::EnableChunkedUpload() {
DCHECK(!upload_data_stream_ || upload_data_stream_->is_chunked());
if (!upload_data_stream_) {
@@ -346,13 +358,20 @@ bool URLRequest::GetFullRequestHeaders(HttpRequestHeaders* headers) const {
return job_->GetFullRequestHeaders(headers);
}
+int64 URLRequest::GetTotalReceivedBytes() const {
+ if (!job_.get())
+ return 0;
+
+ return job_->GetTotalReceivedBytes();
+}
+
LoadStateWithParam URLRequest::GetLoadState() const {
// The !blocked_by_.empty() check allows |this| to report it's blocked on a
// delegate before it has been started.
if (calling_delegate_ || !blocked_by_.empty()) {
return LoadStateWithParam(
LOAD_STATE_WAITING_FOR_DELEGATE,
- use_blocked_by_as_load_param_ ? UTF8ToUTF16(blocked_by_) :
+ use_blocked_by_as_load_param_ ? base::UTF8ToUTF16(blocked_by_) :
base::string16());
}
return LoadStateWithParam(job_.get() ? job_->GetLoadState() : LOAD_STATE_IDLE,
@@ -360,11 +379,11 @@ LoadStateWithParam URLRequest::GetLoadState() const {
}
base::Value* URLRequest::GetStateAsValue() const {
- DictionaryValue* dict = new DictionaryValue();
+ base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("url", original_url().possibly_invalid_spec());
if (url_chain_.size() > 1) {
- ListValue* list = new ListValue();
+ base::ListValue* list = new base::ListValue();
for (std::vector<GURL>::const_iterator url = url_chain_.begin();
url != url_chain_.end(); ++url) {
list->AppendString(url->possibly_invalid_spec());
@@ -530,7 +549,7 @@ void URLRequest::SetDefaultCookiePolicyToBlock() {
// static
bool URLRequest::IsHandledProtocol(const std::string& scheme) {
- return URLRequestJobManager::GetInstance()->SupportsScheme(scheme);
+ return URLRequestJobManager::SupportsScheme(scheme);
}
// static
@@ -575,20 +594,13 @@ std::string URLRequest::ComputeMethodForRedirect(
void URLRequest::SetReferrer(const std::string& referrer) {
DCHECK(!is_pending_);
- referrer_ = referrer;
- // Ensure that we do not send URL fragment, username and password
- // fields in the referrer.
GURL referrer_url(referrer);
UMA_HISTOGRAM_BOOLEAN("Net.URLRequest_SetReferrer_IsEmptyOrValid",
referrer_url.is_empty() || referrer_url.is_valid());
- if (referrer_url.is_valid() && (referrer_url.has_ref() ||
- referrer_url.has_username() || referrer_url.has_password())) {
- GURL::Replacements referrer_mods;
- referrer_mods.ClearRef();
- referrer_mods.ClearUsername();
- referrer_mods.ClearPassword();
- referrer_url = referrer_url.ReplaceComponents(referrer_mods);
- referrer_ = referrer_url.spec();
+ if (referrer_url.is_valid()) {
+ referrer_ = referrer_url.GetAsReferrer().spec();
+ } else {
+ referrer_ = referrer;
}
}
@@ -602,6 +614,9 @@ void URLRequest::set_delegate(Delegate* delegate) {
}
void URLRequest::Start() {
+ // Some values can be NULL, but the job factory must not be.
+ DCHECK(context_->job_factory());
+
DCHECK_EQ(network_delegate_, context_->network_delegate());
// Anything that sets |blocked_by_| before start should have cleaned up after
// itself.
@@ -654,7 +669,7 @@ void URLRequest::BeforeRequestComplete(int error) {
URLRequestRedirectJob* job = new URLRequestRedirectJob(
this, network_delegate_, new_url,
// Use status code 307 to preserve the method, so POST requests work.
- URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT);
+ URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "Delegate");
StartJob(job);
} else {
StartJob(URLRequestJobManager::GetInstance()->CreateJob(
@@ -684,6 +699,20 @@ void URLRequest::StartJob(URLRequestJob* job) {
response_info_.was_cached = false;
+ // If the referrer is secure, but the requested URL is not, the referrer
+ // policy should be something non-default. If you hit this, please file a
+ // bug.
+ if (referrer_policy_ ==
+ CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE &&
+ GURL(referrer_).SchemeIsSecure() && !url().SchemeIsSecure()) {
+#if !defined(OFFICIAL_BUILD)
+ LOG(FATAL) << "Trying to send secure referrer for insecure load";
+#endif
+ referrer_.clear();
+ base::RecordAction(
+ base::UserMetricsAction("Net.URLRequest_StartJob_InvalidReferrer"));
+ }
+
// Don't allow errors to be sent from within Start().
// TODO(brettw) this may cause NotifyDone to be sent synchronously,
// we probably don't want this: they should be sent asynchronously so
@@ -812,6 +841,24 @@ void URLRequest::NotifyReceivedRedirect(const GURL& location,
}
}
+void URLRequest::NotifyBeforeNetworkStart(bool* defer) {
+ if (delegate_ && !notified_before_network_start_) {
+ OnCallToDelegate();
+ delegate_->OnBeforeNetworkStart(this, defer);
+ if (!*defer)
+ OnCallToDelegateComplete();
+ notified_before_network_start_ = true;
+ }
+}
+
+void URLRequest::ResumeNetworkStart() {
+ DCHECK(job_);
+ DCHECK(notified_before_network_start_);
+
+ OnCallToDelegateComplete();
+ job_->ResumeNetworkStart();
+}
+
void URLRequest::NotifyResponseStarted() {
int net_error = OK;
if (!status_.is_success())
@@ -915,7 +962,7 @@ void URLRequest::OrphanJob() {
int URLRequest::Redirect(const GURL& location, int http_status_code) {
// Matches call in NotifyReceivedRedirect.
OnCallToDelegateComplete();
- if (net_log_.IsLoggingAllEvents()) {
+ if (net_log_.IsLogging()) {
net_log_.AddEvent(
NetLog::TYPE_URL_REQUEST_REDIRECTED,
NetLog::StringCallback("location", &location.possibly_invalid_spec()));
@@ -1007,17 +1054,14 @@ bool URLRequest::GetHSTSRedirect(GURL* redirect_url) const {
const GURL& url = this->url();
if (!url.SchemeIs("http"))
return false;
- TransportSecurityState::DomainState domain_state;
- if (context()->transport_security_state() &&
- context()->transport_security_state()->GetDomainState(
+ TransportSecurityState* state = context()->transport_security_state();
+ if (state &&
+ state->ShouldUpgradeToSSL(
url.host(),
- SSLConfigService::IsSNIAvailable(context()->ssl_config_service()),
- &domain_state) &&
- domain_state.ShouldUpgradeToSSL()) {
- url_canon::Replacements<char> replacements;
+ SSLConfigService::IsSNIAvailable(context()->ssl_config_service()))) {
+ url::Replacements<char> replacements;
const char kNewScheme[] = "https";
- replacements.SetScheme(kNewScheme,
- url_parse::Component(0, strlen(kNewScheme)));
+ replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme)));
*redirect_url = url.ReplaceComponents(replacements);
return true;
}
diff --git a/chromium/net/url_request/url_request.h b/chromium/net/url_request/url_request.h
index 84f50505658..fc9899b4418 100644
--- a/chromium/net/url_request/url_request.h
+++ b/chromium/net/url_request/url_request.h
@@ -25,17 +25,12 @@
#include "net/base/request_priority.h"
#include "net/base/upload_progress.h"
#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_store.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_info.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"
-// Temporary layering violation to allow existing users of a deprecated
-// interface.
-class ChildProcessSecurityPolicyTest;
-class TestAutomationProvider;
-class URLRequestAutomationJob;
-
namespace base {
class Value;
@@ -46,30 +41,8 @@ class StackTrace;
// Temporary layering violation to allow existing users of a deprecated
// interface.
-namespace appcache {
-class AppCacheInterceptor;
-class AppCacheRequestHandlerTest;
-class AppCacheURLRequestJobTest;
-}
-
-// Temporary layering violation to allow existing users of a deprecated
-// interface.
namespace content {
-class ResourceDispatcherHostTest;
-}
-
-// Temporary layering violation to allow existing users of a deprecated
-// interface.
-namespace fileapi {
-class FileSystemDirURLRequestJobTest;
-class FileSystemURLRequestJobTest;
-class FileWriterDelegateTest;
-}
-
-// Temporary layering violation to allow existing users of a deprecated
-// interface.
-namespace webkit_blob {
-class BlobURLRequestJobTest;
+class AppCacheInterceptor;
}
namespace net {
@@ -138,7 +111,7 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
// This class handles network interception. Use with
// (Un)RegisterRequestInterceptor.
class NET_EXPORT Interceptor {
- public:
+ public:
virtual ~Interceptor() {}
// Called for every request made. Should return a new job to handle the
@@ -176,23 +149,8 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
class NET_EXPORT Deprecated {
private:
// TODO(willchan): Kill off these friend declarations.
- friend class ::ChildProcessSecurityPolicyTest;
- friend class ::TestAutomationProvider;
- friend class ::URLRequestAutomationJob;
friend class TestInterceptor;
- friend class URLRequestFilter;
- friend class appcache::AppCacheInterceptor;
- friend class appcache::AppCacheRequestHandlerTest;
- friend class appcache::AppCacheURLRequestJobTest;
- friend class content::ResourceDispatcherHostTest;
- friend class fileapi::FileSystemDirURLRequestJobTest;
- friend class fileapi::FileSystemURLRequestJobTest;
- friend class fileapi::FileWriterDelegateTest;
- friend class webkit_blob::BlobURLRequestJobTest;
-
- // Use URLRequestJobFactory::ProtocolHandler instead.
- static ProtocolFactory* RegisterProtocolFactory(const std::string& scheme,
- ProtocolFactory* factory);
+ friend class content::AppCacheInterceptor;
// TODO(pauljensen): Remove this when AppCacheInterceptor is a
// ProtocolHandler, see crbug.com/161547.
@@ -278,6 +236,12 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
const SSLInfo& ssl_info,
bool fatal);
+ // Called to notify that the request must use the network to complete the
+ // request and is about to do so. This is called at most once per
+ // URLRequest, and by default does not defer. If deferred, call
+ // ResumeNetworkStart() to continue or Cancel() to cancel.
+ virtual void OnBeforeNetworkStart(URLRequest* request, bool* defer);
+
// After calling Start(), the delegate will receive an OnResponseStarted
// callback when the request has completed. If an error occurred, the
// request->status() will be set. On success, all redirects have been
@@ -299,11 +263,20 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
virtual ~Delegate() {}
};
+ // TODO(tburkard): we should get rid of this constructor, and have each
+ // creator of a URLRequest specifically list the cookie store to be used.
+ // For now, this constructor will use the cookie store in |context|.
URLRequest(const GURL& url,
RequestPriority priority,
Delegate* delegate,
const URLRequestContext* context);
+ URLRequest(const GURL& url,
+ RequestPriority priority,
+ Delegate* delegate,
+ const URLRequestContext* context,
+ CookieStore* cookie_store);
+
// If destroyed after Start() has been called but while IO is pending,
// then the request will be effectively canceled and the delegate
// will not have any more of its methods called.
@@ -438,6 +411,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
// 2. The OnResponseStarted callback is currently running or has run.
bool GetFullRequestHeaders(HttpRequestHeaders* headers) const;
+ // Gets the total amount of data received from network after SSL decoding and
+ // proxy handling.
+ int64 GetTotalReceivedBytes() const;
+
// Returns the current load state for the request. The returned value's
// |param| field is an optional parameter describing details related to the
// load state. Not all load states have a parameter.
@@ -504,6 +481,11 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
return response_info_.was_fetched_via_proxy;
}
+ // Returns true if the URLRequest was delivered over SPDY.
+ bool was_fetched_via_spdy() const {
+ return response_info_.was_fetched_via_spdy;
+ }
+
// Returns the host and port that the content was fetched from. See
// http_response_info.h for caveats relating to cached content.
HostPortPair GetSocketAddress() const;
@@ -626,6 +608,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
// response to an OnReceivedRedirect call.
void FollowDeferredRedirect();
+ // This method must be called to resume network communications that were
+ // deferred in response to an OnBeforeNetworkStart call.
+ void ResumeNetworkStart();
+
// One of the following two methods should be called in response to an
// OnAuthRequired() callback (and only then).
// SetAuth will reissue the request with the given credentials.
@@ -679,6 +665,12 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
return received_response_content_length_;
}
+ // Available at NetworkDelegate::NotifyHeadersReceived() time, which is before
+ // the more general response_info() is available, even though it is a subset.
+ const HostPortPair& proxy_server() const {
+ return proxy_server_;
+ }
+
protected:
// Allow the URLRequestJob class to control the is_pending() flag.
void set_is_pending(bool value) { is_pending_ = value; }
@@ -686,6 +678,8 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
// Allow the URLRequestJob class to set our status too
void set_status(const URLRequestStatus& value) { status_ = value; }
+ CookieStore* cookie_store() const { return cookie_store_; }
+
// Allow the URLRequestJob to redirect this request. Returns OK if
// successful, otherwise an error code is returned.
int Redirect(const GURL& location, int http_status_code);
@@ -693,6 +687,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
// Called by URLRequestJob to allow interception when a redirect occurs.
void NotifyReceivedRedirect(const GURL& location, bool* defer_redirect);
+ // Called by URLRequestHttpJob (note, only HTTP(S) jobs will call this) to
+ // allow deferral of network initialization.
+ void NotifyBeforeNetworkStart(bool* defer);
+
// Allow an interceptor's URLRequestJob to restart this request.
// Should only be called if the original job has not started a response.
void Restart();
@@ -700,27 +698,19 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
private:
friend class URLRequestJob;
- // Registers a new protocol handler for the given scheme. If the scheme is
- // already handled, this will overwrite the given factory. To delete the
- // protocol factory, use NULL for the factory BUT this WILL NOT put back
- // any previously registered protocol factory. It will have returned
- // the previously registered factory (or NULL if none is registered) when
- // the scheme was first registered so that the caller can manually put it
- // back if desired.
- //
- // The scheme must be all-lowercase ASCII. See the ProtocolFactory
- // declaration for its requirements.
- //
- // The registered protocol factory may return NULL, which will cause the
- // regular "built-in" protocol factory to be used.
- //
- static ProtocolFactory* RegisterProtocolFactory(const std::string& scheme,
- ProtocolFactory* factory);
-
// Registers or unregisters a network interception class.
static void RegisterRequestInterceptor(Interceptor* interceptor);
static void UnregisterRequestInterceptor(Interceptor* interceptor);
+ // Initializes the URLRequest. Code shared between the two constructors.
+ // TODO(tburkard): This can ultimately be folded into a single constructor
+ // again.
+ void Init(const GURL& url,
+ RequestPriority priotity,
+ Delegate* delegate,
+ const URLRequestContext* context,
+ CookieStore* cookie_store);
+
// Resumes or blocks a request paused by the NetworkDelegate::OnBeforeRequest
// handler. If |blocked| is true, the request is blocked and an error page is
// returned indicating so. This should only be called after Start is called
@@ -883,6 +873,15 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe),
scoped_ptr<const base::debug::StackTrace> stack_trace_;
+ // Keeps track of whether or not OnBeforeNetworkStart has been called yet.
+ bool notified_before_network_start_;
+
+ // The cookie store to be used for this request.
+ scoped_refptr<CookieStore> cookie_store_;
+
+ // The proxy server used for this request, if any.
+ HostPortPair proxy_server_;
+
DISALLOW_COPY_AND_ASSIGN(URLRequest);
};
diff --git a/chromium/net/url_request/url_request_context.cc b/chromium/net/url_request/url_request_context.cc
index ae531d10f4b..3a9f69de4fa 100644
--- a/chromium/net/url_request/url_request_context.cc
+++ b/chromium/net/url_request/url_request_context.cc
@@ -31,6 +31,7 @@ URLRequestContext::URLRequestContext()
http_transaction_factory_(NULL),
job_factory_(NULL),
throttler_manager_(NULL),
+ sdch_manager_(NULL),
url_requests_(new std::set<const URLRequest*>) {
}
@@ -56,6 +57,7 @@ void URLRequestContext::CopyFrom(const URLRequestContext* other) {
set_http_transaction_factory(other->http_transaction_factory_);
set_job_factory(other->job_factory_);
set_throttler_manager(other->throttler_manager_);
+ set_sdch_manager(other->sdch_manager_);
set_http_user_agent_settings(other->http_user_agent_settings_);
}
@@ -73,24 +75,16 @@ const HttpNetworkSession::Params* URLRequestContext::GetNetworkSessionParams(
scoped_ptr<URLRequest> URLRequestContext::CreateRequest(
const GURL& url,
RequestPriority priority,
- URLRequest::Delegate* delegate) const {
- return scoped_ptr<URLRequest>(new URLRequest(url, priority, delegate, this));
+ URLRequest::Delegate* delegate,
+ CookieStore* cookie_store) const {
+ return scoped_ptr<URLRequest>(
+ new URLRequest(url, priority, delegate, this, cookie_store));
}
void URLRequestContext::set_cookie_store(CookieStore* cookie_store) {
cookie_store_ = cookie_store;
}
-std::string URLRequestContext::GetAcceptLanguage() const {
- return http_user_agent_settings_ ?
- http_user_agent_settings_->GetAcceptLanguage() : std::string();
-}
-
-std::string URLRequestContext::GetUserAgent(const GURL& url) const {
- return http_user_agent_settings_ ?
- http_user_agent_settings_->GetUserAgent(url) : std::string();
-}
-
void URLRequestContext::AssertNoURLRequests() const {
int num_requests = url_requests_->size();
if (num_requests != 0) {
diff --git a/chromium/net/url_request/url_request_context.h b/chromium/net/url_request/url_request_context.h
index 2c8a3b850f9..4242d72e6d9 100644
--- a/chromium/net/url_request/url_request_context.h
+++ b/chromium/net/url_request/url_request_context.h
@@ -36,6 +36,7 @@ class HttpAuthHandlerFactory;
class HttpTransactionFactory;
class HttpUserAgentSettings;
class NetworkDelegate;
+class SdchManager;
class ServerBoundCertService;
class ProxyService;
class URLRequest;
@@ -58,9 +59,13 @@ class NET_EXPORT URLRequestContext
// May return NULL if this context doesn't have an associated network session.
const HttpNetworkSession::Params* GetNetworkSessionParams() const;
+ // Creates a URLRequest. |cookie_store| optionally specifies a cookie store
+ // to be used rather than the one represented by the context, or NULL
+ // otherwise.
scoped_ptr<URLRequest> CreateRequest(const GURL& url,
RequestPriority priority,
- URLRequest::Delegate* delegate) const;
+ URLRequest::Delegate* delegate,
+ CookieStore* cookie_store) const;
NetLog* net_log() const {
return net_log_;
@@ -167,17 +172,6 @@ class NET_EXPORT URLRequestContext
cert_transparency_verifier_ = verifier;
}
- // ---------------------------------------------------------------------------
- // Legacy accessors that delegate to http_user_agent_settings_.
- // TODO(pauljensen): Remove after all clients are updated to directly access
- // http_user_agent_settings_.
- // Gets the value of 'Accept-Language' header field.
- std::string GetAcceptLanguage() const;
- // Gets the UA string to use for the given URL. Pass an invalid URL (such as
- // GURL()) to get the default UA string.
- std::string GetUserAgent(const GURL& url) const;
- // ---------------------------------------------------------------------------
-
const URLRequestJobFactory* job_factory() const { return job_factory_; }
void set_job_factory(const URLRequestJobFactory* job_factory) {
job_factory_ = job_factory;
@@ -191,6 +185,14 @@ class NET_EXPORT URLRequestContext
throttler_manager_ = throttler_manager;
}
+ // May be NULL.
+ SdchManager* sdch_manager() const {
+ return sdch_manager_;
+ }
+ void set_sdch_manager(SdchManager* sdch_manager) {
+ sdch_manager_ = sdch_manager;
+ }
+
// Gets the URLRequest objects that hold a reference to this
// URLRequestContext.
std::set<const URLRequest*>* url_requests() const {
@@ -234,6 +236,7 @@ class NET_EXPORT URLRequestContext
HttpTransactionFactory* http_transaction_factory_;
const URLRequestJobFactory* job_factory_;
URLRequestThrottlerManager* throttler_manager_;
+ SdchManager* sdch_manager_;
// ---------------------------------------------------------------------------
// Important: When adding any new members below, consider whether they need to
diff --git a/chromium/net/url_request/url_request_context_builder.cc b/chromium/net/url_request/url_request_context_builder.cc
index 465aa87a1b7..93f5b1c0b74 100644
--- a/chromium/net/url_request/url_request_context_builder.cc
+++ b/chromium/net/url_request/url_request_context_builder.cc
@@ -28,13 +28,19 @@
#include "net/proxy/proxy_service.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/url_request/data_protocol_handler.h"
-#include "net/url_request/file_protocol_handler.h"
-#include "net/url_request/ftp_protocol_handler.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_storage.h"
#include "net/url_request/url_request_job_factory_impl.h"
+#if !defined(DISABLE_FILE_SUPPORT)
+#include "net/url_request/file_protocol_handler.h"
+#endif
+
+#if !defined(DISABLE_FTP_SUPPORT)
+#include "net/url_request/ftp_protocol_handler.h"
+#endif
+
namespace net {
namespace {
@@ -64,8 +70,8 @@ class BasicNetworkDelegate : public NetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers)
- OVERRIDE {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE {
return OK;
}
@@ -118,55 +124,42 @@ class BasicNetworkDelegate : public NetworkDelegate {
return OK;
}
- virtual void OnRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state) OVERRIDE {
- }
-
DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate);
};
class BasicURLRequestContext : public URLRequestContext {
public:
BasicURLRequestContext()
- : cache_thread_("Cache Thread"),
- file_thread_("File Thread"),
- storage_(this) {}
+ : storage_(this) {}
URLRequestContextStorage* storage() {
return &storage_;
}
- void StartCacheThread() {
- cache_thread_.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
- }
-
- scoped_refptr<base::MessageLoopProxy> cache_message_loop_proxy() {
- DCHECK(cache_thread_.IsRunning());
- return cache_thread_.message_loop_proxy();
- }
-
- void StartFileThread() {
- file_thread_.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_DEFAULT, 0));
- }
-
- base::MessageLoop* file_message_loop() {
- DCHECK(file_thread_.IsRunning());
- return file_thread_.message_loop();
+ base::Thread* GetCacheThread() {
+ if (!cache_thread_) {
+ cache_thread_.reset(new base::Thread("Cache Thread"));
+ cache_thread_->StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+ }
+ return cache_thread_.get();
}
- scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy() {
- DCHECK(file_thread_.IsRunning());
- return file_thread_.message_loop_proxy();
+ base::Thread* GetFileThread() {
+ if (!file_thread_) {
+ file_thread_.reset(new base::Thread("File Thread"));
+ file_thread_->StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_DEFAULT, 0));
+ }
+ return file_thread_.get();
}
protected:
virtual ~BasicURLRequestContext() {}
private:
- base::Thread cache_thread_;
- base::Thread file_thread_;
+ scoped_ptr<base::Thread> cache_thread_;
+ scoped_ptr<base::Thread> file_thread_;
URLRequestContextStorage storage_;
DISALLOW_COPY_AND_ASSIGN(BasicURLRequestContext);
};
@@ -181,17 +174,29 @@ URLRequestContextBuilder::HttpCacheParams::~HttpCacheParams() {}
URLRequestContextBuilder::HttpNetworkSessionParams::HttpNetworkSessionParams()
: ignore_certificate_errors(false),
host_mapping_rules(NULL),
- http_pipelining_enabled(false),
testing_fixed_http_port(0),
testing_fixed_https_port(0),
- trusted_spdy_proxy() {}
+ next_protos(NextProtosDefaults()),
+ use_alternate_protocols(true) {
+}
URLRequestContextBuilder::HttpNetworkSessionParams::~HttpNetworkSessionParams()
{}
+URLRequestContextBuilder::SchemeFactory::SchemeFactory(
+ const std::string& auth_scheme,
+ net::HttpAuthHandlerFactory* auth_handler_factory)
+ : scheme(auth_scheme), factory(auth_handler_factory) {
+}
+
+URLRequestContextBuilder::SchemeFactory::~SchemeFactory() {
+}
+
URLRequestContextBuilder::URLRequestContextBuilder()
: data_enabled_(false),
+#if !defined(DISABLE_FILE_SUPPORT)
file_enabled_(false),
+#endif
#if !defined(DISABLE_FTP_SUPPORT)
ftp_enabled_(false),
#endif
@@ -205,6 +210,12 @@ void URLRequestContextBuilder::set_proxy_config_service(
proxy_config_service_.reset(proxy_config_service);
}
+void URLRequestContextBuilder::SetSpdyAndQuicEnabled(bool spdy_enabled,
+ bool quic_enabled) {
+ http_network_session_params_.next_protos =
+ NextProtosWithSpdyAndQuic(spdy_enabled, quic_enabled);
+}
+
URLRequestContext* URLRequestContextBuilder::Build() {
BasicURLRequestContext* context = new BasicURLRequestContext;
URLRequestContextStorage* storage = context->storage();
@@ -221,7 +232,7 @@ URLRequestContext* URLRequestContextBuilder::Build() {
host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
storage->set_host_resolver(host_resolver_.Pass());
- context->StartFileThread();
+ storage->set_net_log(new net::NetLog);
// TODO(willchan): Switch to using this code when
// ProxyService::CreateSystemProxyConfigService()'s signature doesn't suck.
@@ -235,7 +246,7 @@ URLRequestContext* URLRequestContextBuilder::Build() {
proxy_config_service =
ProxyService::CreateSystemProxyConfigService(
base::ThreadTaskRunnerHandle::Get().get(),
- context->file_message_loop());
+ context->GetFileThread()->message_loop());
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
storage->set_proxy_service(
@@ -244,9 +255,15 @@ URLRequestContext* URLRequestContextBuilder::Build() {
4, // TODO(willchan): Find a better constant somewhere.
context->net_log()));
storage->set_ssl_config_service(new net::SSLConfigServiceDefaults);
- storage->set_http_auth_handler_factory(
+ HttpAuthHandlerRegistryFactory* http_auth_handler_registry_factory =
net::HttpAuthHandlerRegistryFactory::CreateDefault(
- context->host_resolver()));
+ context->host_resolver());
+ for (size_t i = 0; i < extra_http_auth_handlers_.size(); ++i) {
+ http_auth_handler_registry_factory->RegisterSchemeFactory(
+ extra_http_auth_handlers_[i].scheme,
+ extra_http_auth_handlers_[i].factory);
+ }
+ storage->set_http_auth_handler_factory(http_auth_handler_registry_factory);
storage->set_cookie_store(new CookieMonster(NULL, NULL));
storage->set_transport_security_state(new net::TransportSecurityState());
storage->set_http_server_properties(
@@ -268,18 +285,20 @@ URLRequestContext* URLRequestContextBuilder::Build() {
network_session_params.http_server_properties =
context->http_server_properties();
network_session_params.net_log = context->net_log();
+
network_session_params.ignore_certificate_errors =
http_network_session_params_.ignore_certificate_errors;
network_session_params.host_mapping_rules =
http_network_session_params_.host_mapping_rules;
- network_session_params.http_pipelining_enabled =
- http_network_session_params_.http_pipelining_enabled;
network_session_params.testing_fixed_http_port =
http_network_session_params_.testing_fixed_http_port;
network_session_params.testing_fixed_https_port =
http_network_session_params_.testing_fixed_https_port;
+ network_session_params.use_alternate_protocols =
+ http_network_session_params_.use_alternate_protocols;
network_session_params.trusted_spdy_proxy =
http_network_session_params_.trusted_spdy_proxy;
+ network_session_params.next_protos = http_network_session_params_.next_protos;
HttpTransactionFactory* http_transaction_factory = NULL;
if (http_cache_enabled_) {
@@ -287,13 +306,12 @@ URLRequestContext* URLRequestContextBuilder::Build() {
context->server_bound_cert_service();
HttpCache::BackendFactory* http_cache_backend = NULL;
if (http_cache_params_.type == HttpCacheParams::DISK) {
- context->StartCacheThread();
http_cache_backend = new HttpCache::DefaultBackend(
DISK_CACHE,
net::CACHE_BACKEND_DEFAULT,
http_cache_params_.path,
http_cache_params_.max_size,
- context->cache_message_loop_proxy().get());
+ context->GetCacheThread()->message_loop_proxy().get());
} else {
http_cache_backend =
HttpCache::DefaultBackend::InMemory(http_cache_params_.max_size);
@@ -312,9 +330,15 @@ URLRequestContext* URLRequestContextBuilder::Build() {
URLRequestJobFactoryImpl* job_factory = new URLRequestJobFactoryImpl;
if (data_enabled_)
job_factory->SetProtocolHandler("data", new DataProtocolHandler);
- if (file_enabled_)
+
+#if !defined(DISABLE_FILE_SUPPORT)
+ if (file_enabled_) {
job_factory->SetProtocolHandler(
- "file", new FileProtocolHandler(context->file_message_loop_proxy()));
+ "file",
+ new FileProtocolHandler(context->GetFileThread()->message_loop_proxy()));
+ }
+#endif // !defined(DISABLE_FILE_SUPPORT)
+
#if !defined(DISABLE_FTP_SUPPORT)
if (ftp_enabled_) {
ftp_transaction_factory_.reset(
@@ -322,7 +346,8 @@ URLRequestContext* URLRequestContextBuilder::Build() {
job_factory->SetProtocolHandler("ftp",
new FtpProtocolHandler(ftp_transaction_factory_.get()));
}
-#endif
+#endif // !defined(DISABLE_FTP_SUPPORT)
+
storage->set_job_factory(job_factory);
// TODO(willchan): Support sdch.
diff --git a/chromium/net/url_request/url_request_context_builder.h b/chromium/net/url_request/url_request_context_builder.h
index 899e74dff0e..cf7bba2c688 100644
--- a/chromium/net/url_request/url_request_context_builder.h
+++ b/chromium/net/url_request/url_request_context_builder.h
@@ -15,6 +15,7 @@
#define NET_URL_REQUEST_URL_REQUEST_CONTEXT_BUILDER_H_
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "base/files/file_path.h"
@@ -22,15 +23,17 @@
#include "base/memory/scoped_ptr.h"
#include "build/build_config.h"
#include "net/base/net_export.h"
+#include "net/base/network_delegate.h"
+#include "net/dns/host_resolver.h"
+#include "net/socket/next_proto.h"
namespace net {
class FtpTransactionFactory;
-class HostResolver;
class HostMappingRules;
+class HttpAuthHandlerFactory;
class ProxyConfigService;
class URLRequestContext;
-class NetworkDelegate;
class NET_EXPORT URLRequestContextBuilder {
public:
@@ -61,10 +64,11 @@ class NET_EXPORT URLRequestContextBuilder {
// These fields mirror those in net::HttpNetworkSession::Params;
bool ignore_certificate_errors;
HostMappingRules* host_mapping_rules;
- bool http_pipelining_enabled;
uint16 testing_fixed_http_port;
uint16 testing_fixed_https_port;
+ NextProtoVector next_protos;
std::string trusted_spdy_proxy;
+ bool use_alternate_protocols;
};
URLRequestContextBuilder();
@@ -87,10 +91,12 @@ class NET_EXPORT URLRequestContextBuilder {
data_enabled_ = enable;
}
+#if !defined(DISABLE_FILE_SUPPORT)
// Control support for file:// requests. By default it's disabled.
void set_file_enabled(bool enable) {
file_enabled_ = enable;
}
+#endif
#if !defined(DISABLE_FTP_SUPPORT)
// Control support for ftp:// requests. By default it's disabled.
@@ -111,12 +117,24 @@ class NET_EXPORT URLRequestContextBuilder {
network_delegate_.reset(delegate);
}
+
+ // Adds additional auth handler factories to be used in addition to what is
+ // provided in the default |HttpAuthHandlerRegistryFactory|. The auth |scheme|
+ // and |factory| are provided. The builder takes ownership of the factory and
+ // Build() must be called after this method.
+ void add_http_auth_handler_factory(const std::string& scheme,
+ net::HttpAuthHandlerFactory* factory) {
+ extra_http_auth_handlers_.push_back(SchemeFactory(scheme, factory));
+ }
+
// By default HttpCache is enabled with a default constructed HttpCacheParams.
void EnableHttpCache(const HttpCacheParams& params) {
+ http_cache_enabled_ = true;
http_cache_params_ = params;
}
void DisableHttpCache() {
+ http_cache_enabled_ = false;
http_cache_params_ = HttpCacheParams();
}
@@ -126,15 +144,30 @@ class NET_EXPORT URLRequestContextBuilder {
http_network_session_params_ = http_network_session_params;
}
+ // Adjust |http_network_session_params_.next_protos| to enable SPDY and QUIC.
+ void SetSpdyAndQuicEnabled(bool spdy_enabled,
+ bool quic_enabled);
+
URLRequestContext* Build();
private:
+ struct SchemeFactory {
+ SchemeFactory(const std::string& scheme,
+ net::HttpAuthHandlerFactory* factory);
+ ~SchemeFactory();
+
+ std::string scheme;
+ net::HttpAuthHandlerFactory* factory;
+ };
+
std::string accept_language_;
std::string user_agent_;
// Include support for data:// requests.
bool data_enabled_;
+#if !defined(DISABLE_FILE_SUPPORT)
// Include support for file:// requests.
bool file_enabled_;
+#endif
#if !defined(DISABLE_FTP_SUPPORT)
// Include support for ftp:// requests.
bool ftp_enabled_;
@@ -146,6 +179,7 @@ class NET_EXPORT URLRequestContextBuilder {
scoped_ptr<ProxyConfigService> proxy_config_service_;
scoped_ptr<NetworkDelegate> network_delegate_;
scoped_ptr<FtpTransactionFactory> ftp_transaction_factory_;
+ std::vector<SchemeFactory> extra_http_auth_handlers_;
DISALLOW_COPY_AND_ASSIGN(URLRequestContextBuilder);
};
diff --git a/chromium/net/url_request/url_request_context_builder_unittest.cc b/chromium/net/url_request/url_request_context_builder_unittest.cc
index e882253773f..a7a0fee75d6 100644
--- a/chromium/net/url_request/url_request_context_builder_unittest.cc
+++ b/chromium/net/url_request/url_request_context_builder_unittest.cc
@@ -6,6 +6,8 @@
#include "build/build_config.h"
#include "net/base/request_priority.h"
+#include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
@@ -36,6 +38,27 @@ class LocalHttpTestServer : public SpawnedTestServer {
base::FilePath()) {}
};
+class MockHttpAuthHandlerFactory : public HttpAuthHandlerFactory {
+ public:
+ explicit MockHttpAuthHandlerFactory(int return_code) :
+ return_code_(return_code) {}
+ virtual ~MockHttpAuthHandlerFactory() {}
+
+ virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
+ HttpAuth::Target target,
+ const GURL& origin,
+ CreateReason reason,
+ int nonce_count,
+ const BoundNetLog& net_log,
+ scoped_ptr<HttpAuthHandler>* handler) OVERRIDE {
+ handler->reset();
+ return return_code_;
+ }
+
+ private:
+ int return_code_;
+};
+
class URLRequestContextBuilderTest : public PlatformTest {
protected:
URLRequestContextBuilderTest()
@@ -83,6 +106,28 @@ TEST_F(URLRequestContextBuilderTest, UserAgent) {
EXPECT_EQ("Bar", delegate.data_received());
}
+TEST_F(URLRequestContextBuilderTest, ExtraHttpAuthHandlerFactory) {
+ GURL gurl("www.google.com");
+ const int kBasicReturnCode = net::OK;
+ MockHttpAuthHandlerFactory* mock_factory_basic =
+ new MockHttpAuthHandlerFactory(kBasicReturnCode);
+ scoped_ptr<HttpAuthHandler> handler;
+ builder_.add_http_auth_handler_factory("ExtraScheme", mock_factory_basic);
+ scoped_ptr<URLRequestContext> context(builder_.Build());
+ // Verify that a handler is returned for and added scheme.
+ EXPECT_EQ(kBasicReturnCode,
+ context->http_auth_handler_factory()->CreateAuthHandlerFromString(
+ "ExtraScheme",
+ HttpAuth::AUTH_SERVER,
+ gurl,
+ BoundNetLog(),
+ &handler));
+ // Verify that a handler isn't returned for a bogus scheme.
+ EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
+ context->http_auth_handler_factory()->CreateAuthHandlerFromString(
+ "Bogus", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/url_request/url_request_context_getter.cc b/chromium/net/url_request/url_request_context_getter.cc
index 88a6f3d6490..721a709fbd4 100644
--- a/chromium/net/url_request/url_request_context_getter.cc
+++ b/chromium/net/url_request/url_request_context_getter.cc
@@ -35,4 +35,22 @@ void URLRequestContextGetter::OnDestruct() const {
// This is also true if the IO thread is gone.
}
+TrivialURLRequestContextGetter::TrivialURLRequestContextGetter(
+ net::URLRequestContext* context,
+ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner)
+ : context_(context), main_task_runner_(main_task_runner) {}
+
+TrivialURLRequestContextGetter::~TrivialURLRequestContextGetter() {}
+
+net::URLRequestContext*
+TrivialURLRequestContextGetter::GetURLRequestContext() {
+ return context_;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+TrivialURLRequestContextGetter::GetNetworkTaskRunner() const {
+ return main_task_runner_;
+}
+
+
} // namespace net
diff --git a/chromium/net/url_request/url_request_context_getter.h b/chromium/net/url_request/url_request_context_getter.h
index 4c8e4af55c3..1f56f70750b 100644
--- a/chromium/net/url_request/url_request_context_getter.h
+++ b/chromium/net/url_request/url_request_context_getter.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_COMMON_NET_URL_REQUEST_CONTEXT_GETTER_H_
-#define CHROME_COMMON_NET_URL_REQUEST_CONTEXT_GETTER_H_
+#ifndef NET_URL_REQUEST_URL_REQUEST_CONTEXT_GETTER_H_
+#define NET_URL_REQUEST_URL_REQUEST_CONTEXT_GETTER_H_
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner_helpers.h"
@@ -53,6 +53,29 @@ struct URLRequestContextGetterTraits {
}
};
+// For use in shimming a URLRequestContext into a URLRequestContextGetter.
+class NET_EXPORT TrivialURLRequestContextGetter
+ : public URLRequestContextGetter {
+public:
+ TrivialURLRequestContextGetter(
+ net::URLRequestContext* context,
+ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner);
+
+ // net::URLRequestContextGetter implementation:
+ virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
+
+ virtual scoped_refptr<base::SingleThreadTaskRunner>
+ GetNetworkTaskRunner() const OVERRIDE;
+
+private:
+ virtual ~TrivialURLRequestContextGetter();
+
+ net::URLRequestContext* context_;
+ const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrivialURLRequestContextGetter);
+};
+
} // namespace net
-#endif // CHROME_COMMON_NET_URL_REQUEST_CONTEXT_GETTER_H_
+#endif // NET_URL_REQUEST_URL_REQUEST_CONTEXT_GETTER_H_
diff --git a/chromium/net/url_request/url_request_file_dir_job.cc b/chromium/net/url_request/url_request_file_dir_job.cc
index d98da02ae91..da586f3561b 100644
--- a/chromium/net/url_request/url_request_file_dir_job.cc
+++ b/chromium/net/url_request/url_request_file_dir_job.cc
@@ -108,7 +108,7 @@ void URLRequestFileDirJob::OnListFile(
// On Linux, the file system encoding is not defined, but we assume that
// SysNativeMBToWide takes care of it at least for now. We can try something
// more sophisticated if necessary later.
- const base::string16& title = WideToUTF16(
+ const base::string16& title = base::WideToUTF16(
base::SysNativeMBToWide(dir_path_.value()));
#endif
data_.append(GetDirectoryListingHeader(title));
diff --git a/chromium/net/url_request/url_request_file_job.cc b/chromium/net/url_request/url_request_file_job.cc
index 723e6a030b7..74e61315980 100644
--- a/chromium/net/url_request/url_request_file_job.cc
+++ b/chromium/net/url_request/url_request_file_job.cc
@@ -23,18 +23,18 @@
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/message_loop/message_loop.h"
-#include "base/platform_file.h"
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "base/task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/file_stream.h"
+#include "net/base/filename_util.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
-#include "net/base/net_util.h"
+#include "net/filter/filter.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_dir_job.h"
@@ -60,7 +60,7 @@ URLRequestFileJob::URLRequestFileJob(
const scoped_refptr<base::TaskRunner>& file_task_runner)
: URLRequestJob(request, network_delegate),
file_path_(file_path),
- stream_(new FileStream(NULL, file_task_runner)),
+ stream_(new FileStream(file_task_runner)),
file_task_runner_(file_task_runner),
remaining_bytes_(0),
weak_ptr_factory_(this) {}
@@ -83,8 +83,9 @@ void URLRequestFileJob::Kill() {
URLRequestJob::Kill();
}
-bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
- int *bytes_read) {
+bool URLRequestFileJob::ReadRawData(IOBuffer* dest,
+ int dest_size,
+ int* bytes_read) {
DCHECK_NE(dest_size, 0);
DCHECK(bytes_read);
DCHECK_GE(remaining_bytes_, 0);
@@ -99,9 +100,11 @@ bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
return true;
}
- int rv = stream_->Read(dest, dest_size,
+ int rv = stream_->Read(dest,
+ dest_size,
base::Bind(&URLRequestFileJob::DidRead,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(),
+ make_scoped_refptr(dest)));
if (rv >= 0) {
// Data is immediately available.
*bytes_read = rv;
@@ -192,16 +195,22 @@ void URLRequestFileJob::SetExtraRequestHeaders(
}
}
+void URLRequestFileJob::OnSeekComplete(int64 result) {
+}
+
+void URLRequestFileJob::OnReadComplete(net::IOBuffer* buf, int result) {
+}
+
URLRequestFileJob::~URLRequestFileJob() {
}
void URLRequestFileJob::FetchMetaInfo(const base::FilePath& file_path,
FileMetaInfo* meta_info) {
- base::PlatformFileInfo platform_info;
- meta_info->file_exists = base::GetFileInfo(file_path, &platform_info);
+ base::File::Info file_info;
+ meta_info->file_exists = base::GetFileInfo(file_path, &file_info);
if (meta_info->file_exists) {
- meta_info->file_size = platform_info.size;
- meta_info->is_directory = platform_info.is_directory;
+ meta_info->file_size = file_info.size;
+ meta_info->is_directory = file_info.is_directory;
}
// On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be
// done in WorkerPool.
@@ -229,9 +238,9 @@ void URLRequestFileJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) {
return;
}
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
+ int flags = base::File::FLAG_OPEN |
+ base::File::FLAG_READ |
+ base::File::FLAG_ASYNC;
int rv = stream_->Open(file_path_, flags,
base::Bind(&URLRequestFileJob::DidOpen,
weak_ptr_factory_.GetWeakPtr()));
@@ -273,6 +282,7 @@ void URLRequestFileJob::DidOpen(int result) {
}
void URLRequestFileJob::DidSeek(int64 result) {
+ OnSeekComplete(result);
if (result != byte_range_.first_byte_position()) {
NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
ERR_REQUEST_RANGE_NOT_SATISFIABLE));
@@ -283,18 +293,22 @@ void URLRequestFileJob::DidSeek(int64 result) {
NotifyHeadersComplete();
}
-void URLRequestFileJob::DidRead(int result) {
+void URLRequestFileJob::DidRead(scoped_refptr<net::IOBuffer> buf, int result) {
if (result > 0) {
SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
- } else if (result == 0) {
+ remaining_bytes_ -= result;
+ DCHECK_GE(remaining_bytes_, 0);
+ }
+
+ OnReadComplete(buf.get(), result);
+ buf = NULL;
+
+ if (result == 0) {
NotifyDone(URLRequestStatus());
- } else {
+ } else if (result < 0) {
NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
}
- remaining_bytes_ -= result;
- DCHECK_GE(remaining_bytes_, 0);
-
NotifyReadComplete(result);
}
diff --git a/chromium/net/url_request/url_request_file_job.h b/chromium/net/url_request/url_request_file_job.h
index 7cd7df0ba3b..15c1e4d9bc1 100644
--- a/chromium/net/url_request/url_request_file_job.h
+++ b/chromium/net/url_request/url_request_file_job.h
@@ -16,8 +16,7 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
-namespace base{
-struct PlatformFileInfo;
+namespace base {
class TaskRunner;
}
namespace file_util {
@@ -49,9 +48,15 @@ class NET_EXPORT URLRequestFileJob : public URLRequestJob {
virtual void SetExtraRequestHeaders(
const HttpRequestHeaders& headers) OVERRIDE;
+ // An interface for subclasses who wish to monitor read operations.
+ virtual void OnSeekComplete(int64 result);
+ virtual void OnReadComplete(net::IOBuffer* buf, int result);
+
protected:
virtual ~URLRequestFileJob();
+ int64 remaining_bytes() const { return remaining_bytes_; }
+
// The OS-specific full path name of the file
base::FilePath file_path_;
@@ -89,8 +94,8 @@ class NET_EXPORT URLRequestFileJob : public URLRequestJob {
// on a background thread.
void DidSeek(int64 result);
- // Callback after data is asynchronously read from the file.
- void DidRead(int result);
+ // Callback after data is asynchronously read from the file into |buf|.
+ void DidRead(scoped_refptr<net::IOBuffer> buf, int result);
scoped_ptr<FileStream> stream_;
FileMetaInfo meta_info_;
diff --git a/chromium/net/url_request/url_request_file_job_unittest.cc b/chromium/net/url_request/url_request_file_job_unittest.cc
new file mode 100644
index 00000000000..8e0cabb4bc3
--- /dev/null
+++ b/chromium/net/url_request/url_request_file_job_unittest.cc
@@ -0,0 +1,253 @@
+// Copyright 2014 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/url_request/url_request_file_job.h"
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "net/base/filename_util.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// A URLRequestFileJob for testing OnSeekComplete / OnReadComplete callbacks.
+class URLRequestFileJobWithCallbacks : public net::URLRequestFileJob {
+ public:
+ URLRequestFileJobWithCallbacks(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const base::FilePath& file_path,
+ const scoped_refptr<base::TaskRunner>& file_task_runner)
+ : URLRequestFileJob(request,
+ network_delegate,
+ file_path,
+ file_task_runner),
+ seek_position_(0) {
+ }
+
+ int64 seek_position() { return seek_position_; }
+ const std::vector<std::string>& data_chunks() { return data_chunks_; }
+
+ protected:
+ virtual ~URLRequestFileJobWithCallbacks() {}
+
+ virtual void OnSeekComplete(int64 result) OVERRIDE {
+ ASSERT_EQ(seek_position_, 0);
+ seek_position_ = result;
+ }
+
+ virtual void OnReadComplete(net::IOBuffer* buf, int result) OVERRIDE {
+ data_chunks_.push_back(std::string(buf->data(), result));
+ }
+
+ int64 seek_position_;
+ std::vector<std::string> data_chunks_;
+};
+
+// A URLRequestJobFactory that will return URLRequestFileJobWithCallbacks
+// instances for file:// scheme URLs.
+class CallbacksJobFactory : public net::URLRequestJobFactory {
+ public:
+ class JobObserver {
+ public:
+ virtual void OnJobCreated(URLRequestFileJobWithCallbacks* job) = 0;
+ };
+
+ CallbacksJobFactory(const base::FilePath& path, JobObserver* observer)
+ : path_(path), observer_(observer) {
+ }
+
+ virtual ~CallbacksJobFactory() {}
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ URLRequestFileJobWithCallbacks* job = new URLRequestFileJobWithCallbacks(
+ request,
+ network_delegate,
+ path_,
+ base::MessageLoop::current()->message_loop_proxy());
+ observer_->OnJobCreated(job);
+ return job;
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return scheme == "file";
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return IsHandledProtocol(url.scheme());
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ base::FilePath path_;
+ JobObserver* observer_;
+};
+
+// Helper function to create a file in |directory| filled with
+// |content|. Returns true on succes and fills in |path| with the full path to
+// the file.
+bool CreateTempFileWithContent(const std::string& content,
+ const base::ScopedTempDir& directory,
+ base::FilePath* path) {
+ if (!directory.IsValid())
+ return false;
+
+ if (!base::CreateTemporaryFileInDir(directory.path(), path))
+ return false;
+
+ return base::WriteFile(*path, content.c_str(), content.length());
+}
+
+class JobObserverImpl : public CallbacksJobFactory::JobObserver {
+ public:
+ virtual void OnJobCreated(URLRequestFileJobWithCallbacks* job) OVERRIDE {
+ jobs_.push_back(job);
+ }
+
+ typedef std::vector<scoped_refptr<URLRequestFileJobWithCallbacks> > JobList;
+
+ const JobList& jobs() { return jobs_; }
+
+ protected:
+ JobList jobs_;
+};
+
+// A simple holder for start/end used in http range requests.
+struct Range {
+ int start;
+ int end;
+
+ Range() {
+ start = 0;
+ end = 0;
+ }
+
+ Range(int start, int end) {
+ this->start = start;
+ this->end = end;
+ }
+};
+
+// A superclass for tests of the OnSeekComplete / OnReadComplete functions of
+// URLRequestFileJob.
+class URLRequestFileJobEventsTest : public testing::Test {
+ public:
+ URLRequestFileJobEventsTest();
+
+ protected:
+ // This creates a file with |content| as the contents, and then creates and
+ // runs a URLRequestFileJobWithCallbacks job to get the contents out of it,
+ // and makes sure that the callbacks observed the correct bytes. If a Range
+ // is provided, this function will add the appropriate Range http header to
+ // the request and verify that only the bytes in that range (inclusive) were
+ // observed.
+ void RunRequest(const std::string& content, const Range* range);
+
+ JobObserverImpl observer_;
+ net::TestURLRequestContext context_;
+ net::TestDelegate delegate_;
+};
+
+URLRequestFileJobEventsTest::URLRequestFileJobEventsTest() {}
+
+void URLRequestFileJobEventsTest::RunRequest(const std::string& content,
+ const Range* range) {
+ base::ScopedTempDir directory;
+ ASSERT_TRUE(directory.CreateUniqueTempDir());
+ base::FilePath path;
+ ASSERT_TRUE(CreateTempFileWithContent(content, directory, &path));
+ CallbacksJobFactory factory(path, &observer_);
+ context_.set_job_factory(&factory);
+
+ net::TestURLRequest request(net::FilePathToFileURL(path),
+ net::DEFAULT_PRIORITY,
+ &delegate_,
+ &context_);
+ if (range) {
+ ASSERT_GE(range->start, 0);
+ ASSERT_GE(range->end, 0);
+ ASSERT_LE(range->start, range->end);
+ ASSERT_LT(static_cast<unsigned int>(range->end), content.length());
+ std::string range_value =
+ base::StringPrintf("bytes=%d-%d", range->start, range->end);
+ request.SetExtraRequestHeaderByName(
+ net::HttpRequestHeaders::kRange, range_value, true /*overwrite*/);
+ }
+ request.Start();
+
+ base::RunLoop loop;
+ loop.Run();
+
+ EXPECT_FALSE(delegate_.request_failed());
+ int expected_length =
+ range ? (range->end - range->start + 1) : content.length();
+ EXPECT_EQ(delegate_.bytes_received(), expected_length);
+
+ std::string expected_content;
+ if (range) {
+ expected_content.insert(0, content, range->start, expected_length);
+ } else {
+ expected_content = content;
+ }
+ EXPECT_TRUE(delegate_.data_received() == expected_content);
+
+ ASSERT_EQ(observer_.jobs().size(), 1u);
+ ASSERT_EQ(observer_.jobs().at(0)->seek_position(), range ? range->start : 0);
+
+ std::string observed_content;
+ const std::vector<std::string>& chunks =
+ observer_.jobs().at(0)->data_chunks();
+ for (std::vector<std::string>::const_iterator i = chunks.begin();
+ i != chunks.end();
+ ++i) {
+ observed_content.append(*i);
+ }
+ EXPECT_EQ(expected_content, observed_content);
+ EXPECT_TRUE(expected_content == observed_content);
+}
+
+// Helper function to make a character array filled with |size| bytes of
+// test content.
+std::string MakeContentOfSize(int size) {
+ EXPECT_GE(size, 0);
+ std::string result;
+ result.reserve(size);
+ for (int i = 0; i < size; i++) {
+ result.append(1, static_cast<char>(i % 256));
+ }
+ return result;
+}
+
+} // namespace
+
+TEST_F(URLRequestFileJobEventsTest, TinyFile) {
+ RunRequest(std::string("hello world"), NULL);
+}
+
+TEST_F(URLRequestFileJobEventsTest, SmallFile) {
+ RunRequest(MakeContentOfSize(17 * 1024), NULL);
+}
+
+TEST_F(URLRequestFileJobEventsTest, BigFile) {
+ RunRequest(MakeContentOfSize(3 * 1024 * 1024), NULL);
+}
+
+TEST_F(URLRequestFileJobEventsTest, Range) {
+ // Use a 15KB content file and read a range chosen somewhat arbitrarily but
+ // not aligned on any likely page boundaries.
+ int size = 15 * 1024;
+ Range range(1701, (6 * 1024) + 3);
+ RunRequest(MakeContentOfSize(size), &range);
+}
diff --git a/chromium/net/url_request/url_request_filter.cc b/chromium/net/url_request/url_request_filter.cc
index 19a8747ea9b..842a54f0721 100644
--- a/chromium/net/url_request/url_request_filter.cc
+++ b/chromium/net/url_request/url_request_filter.cc
@@ -8,20 +8,20 @@
#include "base/logging.h"
#include "base/stl_util.h"
+#include "net/url_request/url_request_job_factory_impl.h"
namespace net {
namespace {
-class URLRequestFilterProtocolHandler
- : public URLRequestJobFactory::ProtocolHandler {
+class URLRequestFilterInterceptor : public URLRequestInterceptor {
public:
- explicit URLRequestFilterProtocolHandler(URLRequest::ProtocolFactory* factory)
+ explicit URLRequestFilterInterceptor(URLRequest::ProtocolFactory* factory)
: factory_(factory) {}
- virtual ~URLRequestFilterProtocolHandler() {}
+ virtual ~URLRequestFilterInterceptor() {}
- // URLRequestJobFactory::ProtocolHandler implementation
- virtual URLRequestJob* MaybeCreateJob(
+ // URLRequestInterceptor implementation.
+ virtual URLRequestJob* MaybeInterceptRequest(
URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
return factory_(request, network_delegate, request->url().scheme());
}
@@ -29,23 +29,13 @@ class URLRequestFilterProtocolHandler
private:
URLRequest::ProtocolFactory* factory_;
- DISALLOW_COPY_AND_ASSIGN(URLRequestFilterProtocolHandler);
+ DISALLOW_COPY_AND_ASSIGN(URLRequestFilterInterceptor);
};
} // namespace
URLRequestFilter* URLRequestFilter::shared_instance_ = NULL;
-URLRequestFilter::~URLRequestFilter() {}
-
-// static
-URLRequestJob* URLRequestFilter::Factory(URLRequest* request,
- NetworkDelegate* network_delegate,
- const std::string& scheme) {
- // Returning null here just means that the built-in handler will be used.
- return GetInstance()->FindRequestHandler(request, network_delegate, scheme);
-}
-
// static
URLRequestFilter* URLRequestFilter::GetInstance() {
if (!shared_instance_)
@@ -55,32 +45,28 @@ URLRequestFilter* URLRequestFilter::GetInstance() {
void URLRequestFilter::AddHostnameHandler(const std::string& scheme,
const std::string& hostname, URLRequest::ProtocolFactory* factory) {
- AddHostnameProtocolHandler(
+ AddHostnameInterceptor(
scheme, hostname,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler>(
- new URLRequestFilterProtocolHandler(factory)));
+ scoped_ptr<URLRequestInterceptor>(
+ new URLRequestFilterInterceptor(factory)));
}
-void URLRequestFilter::AddHostnameProtocolHandler(
+void URLRequestFilter::AddHostnameInterceptor(
const std::string& scheme,
const std::string& hostname,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler> protocol_handler) {
- DCHECK_EQ(0u, hostname_handler_map_.count(make_pair(scheme, hostname)));
- hostname_handler_map_[make_pair(scheme, hostname)] =
- protocol_handler.release();
-
- // Register with the ProtocolFactory.
- URLRequest::Deprecated::RegisterProtocolFactory(
- scheme, &URLRequestFilter::Factory);
+ scoped_ptr<URLRequestInterceptor> interceptor) {
+ DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(scheme, hostname)));
+ hostname_interceptor_map_[make_pair(scheme, hostname)] =
+ interceptor.release();
#ifndef NDEBUG
- // Check to see if we're masking URLs in the url_handler_map_.
- for (UrlHandlerMap::const_iterator i = url_handler_map_.begin();
- i != url_handler_map_.end(); ++i) {
- const GURL& url = GURL(i->first);
- HostnameHandlerMap::iterator host_it =
- hostname_handler_map_.find(make_pair(url.scheme(), url.host()));
- if (host_it != hostname_handler_map_.end())
+ // Check to see if we're masking URLs in the url_interceptor_map_.
+ for (URLInterceptorMap::const_iterator it = url_interceptor_map_.begin();
+ it != url_interceptor_map_.end(); ++it) {
+ const GURL& url = GURL(it->first);
+ HostnameInterceptorMap::const_iterator host_it =
+ hostname_interceptor_map_.find(make_pair(url.scheme(), url.host()));
+ if (host_it != hostname_interceptor_map_.end())
NOTREACHED();
}
#endif // !NDEBUG
@@ -88,12 +74,12 @@ void URLRequestFilter::AddHostnameProtocolHandler(
void URLRequestFilter::RemoveHostnameHandler(const std::string& scheme,
const std::string& hostname) {
- HostnameHandlerMap::iterator iter =
- hostname_handler_map_.find(make_pair(scheme, hostname));
- DCHECK(iter != hostname_handler_map_.end());
+ HostnameInterceptorMap::iterator it =
+ hostname_interceptor_map_.find(make_pair(scheme, hostname));
+ DCHECK(it != hostname_interceptor_map_.end());
- delete iter->second;
- hostname_handler_map_.erase(iter);
+ delete it->second;
+ hostname_interceptor_map_.erase(it);
// Note that we don't unregister from the URLRequest ProtocolFactory as
// this would leave no protocol factory for the remaining hostname and URL
// handlers.
@@ -102,86 +88,66 @@ void URLRequestFilter::RemoveHostnameHandler(const std::string& scheme,
bool URLRequestFilter::AddUrlHandler(
const GURL& url,
URLRequest::ProtocolFactory* factory) {
- return AddUrlProtocolHandler(
+ return AddUrlInterceptor(
url,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler>(
- new URLRequestFilterProtocolHandler(factory)));
+ scoped_ptr<URLRequestInterceptor>(
+ new URLRequestFilterInterceptor(factory)));
}
-
-bool URLRequestFilter::AddUrlProtocolHandler(
+bool URLRequestFilter::AddUrlInterceptor(
const GURL& url,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler> protocol_handler) {
+ scoped_ptr<URLRequestInterceptor> interceptor) {
if (!url.is_valid())
return false;
- DCHECK_EQ(0u, url_handler_map_.count(url.spec()));
- url_handler_map_[url.spec()] = protocol_handler.release();
+ DCHECK_EQ(0u, url_interceptor_map_.count(url.spec()));
+ url_interceptor_map_[url.spec()] = interceptor.release();
- // Register with the ProtocolFactory.
- URLRequest::Deprecated::RegisterProtocolFactory(url.scheme(),
- &URLRequestFilter::Factory);
// Check to see if this URL is masked by a hostname handler.
- DCHECK_EQ(0u, hostname_handler_map_.count(make_pair(url.scheme(),
- url.host())));
+ DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(url.scheme(),
+ url.host())));
return true;
}
void URLRequestFilter::RemoveUrlHandler(const GURL& url) {
- UrlHandlerMap::iterator iter = url_handler_map_.find(url.spec());
- DCHECK(iter != url_handler_map_.end());
+ URLInterceptorMap::iterator it = url_interceptor_map_.find(url.spec());
+ DCHECK(it != url_interceptor_map_.end());
- delete iter->second;
- url_handler_map_.erase(iter);
+ delete it->second;
+ url_interceptor_map_.erase(it);
// Note that we don't unregister from the URLRequest ProtocolFactory as
// this would leave no protocol factory for the remaining hostname and URL
// handlers.
}
void URLRequestFilter::ClearHandlers() {
- // Unregister with the ProtocolFactory.
- std::set<std::string> schemes;
- for (UrlHandlerMap::const_iterator i = url_handler_map_.begin();
- i != url_handler_map_.end(); ++i) {
- schemes.insert(GURL(i->first).scheme());
- }
- for (HostnameHandlerMap::const_iterator i = hostname_handler_map_.begin();
- i != hostname_handler_map_.end(); ++i) {
- schemes.insert(i->first.first);
- }
- for (std::set<std::string>::const_iterator scheme = schemes.begin();
- scheme != schemes.end(); ++scheme) {
- URLRequest::Deprecated::RegisterProtocolFactory(*scheme, NULL);
- }
-
- STLDeleteValues(&url_handler_map_);
- STLDeleteValues(&hostname_handler_map_);
+ STLDeleteValues(&url_interceptor_map_);
+ STLDeleteValues(&hostname_interceptor_map_);
hit_count_ = 0;
}
-URLRequestFilter::URLRequestFilter() : hit_count_(0) { }
-
-URLRequestJob* URLRequestFilter::FindRequestHandler(
+URLRequestJob* URLRequestFilter::MaybeInterceptRequest(
URLRequest* request,
- NetworkDelegate* network_delegate,
- const std::string& scheme) {
+ NetworkDelegate* network_delegate) const {
URLRequestJob* job = NULL;
- if (request->url().is_valid()) {
- // Check the hostname map first.
- const std::string& hostname = request->url().host();
-
- HostnameHandlerMap::iterator i =
- hostname_handler_map_.find(make_pair(scheme, hostname));
- if (i != hostname_handler_map_.end())
- job = i->second->MaybeCreateJob(request, network_delegate);
-
- if (!job) {
- // Not in the hostname map, check the url map.
- const std::string& url = request->url().spec();
- UrlHandlerMap::iterator i = url_handler_map_.find(url);
- if (i != url_handler_map_.end())
- job = i->second->MaybeCreateJob(request, network_delegate);
- }
+ if (!request->url().is_valid())
+ return NULL;
+
+ // Check the hostname map first.
+ const std::string hostname = request->url().host();
+ const std::string scheme = request->url().scheme();
+
+ HostnameInterceptorMap::const_iterator it =
+ hostname_interceptor_map_.find(make_pair(scheme, hostname));
+ if (it != hostname_interceptor_map_.end())
+ job = it->second->MaybeInterceptRequest(request, network_delegate);
+
+ if (!job) {
+ // Not in the hostname map, check the url map.
+ const std::string& url = request->url().spec();
+ URLInterceptorMap::const_iterator it = url_interceptor_map_.find(url);
+ if (it != url_interceptor_map_.end())
+ job = it->second->MaybeInterceptRequest(request, network_delegate);
}
if (job) {
DVLOG(1) << "URLRequestFilter hit for " << request->url().spec();
@@ -190,4 +156,12 @@ URLRequestJob* URLRequestFilter::FindRequestHandler(
return job;
}
+URLRequestFilter::URLRequestFilter() : hit_count_(0) {
+ URLRequestJobFactoryImpl::SetInterceptorForTesting(this);
+}
+
+URLRequestFilter::~URLRequestFilter() {
+ URLRequestJobFactoryImpl::SetInterceptorForTesting(NULL);
+}
+
} // namespace net
diff --git a/chromium/net/url_request/url_request_filter.h b/chromium/net/url_request/url_request_filter.h
index 418560df5c6..93baedba9ac 100644
--- a/chromium/net/url_request/url_request_filter.h
+++ b/chromium/net/url_request/url_request_filter.h
@@ -1,21 +1,6 @@
// Copyright (c) 2011 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.
-//
-// A class to help filter URLRequest jobs based on the URL of the request
-// rather than just the scheme. Example usage:
-//
-// // Use as an "http" handler.
-// URLRequest::RegisterProtocolFactory("http", &URLRequestFilter::Factory);
-// // Add special handling for the URL http://foo.com/
-// URLRequestFilter::GetInstance()->AddUrlHandler(
-// GURL("http://foo.com/"),
-// &URLRequestCustomJob::Factory);
-//
-// If URLRequestFilter::Factory can't find a handler for the request, it
-// returns null to URLRequestJobManager and lets a built-in protocol factory
-// handle the request.
-
#ifndef NET_URL_REQUEST_URL_REQUEST_FILTER_H_
#define NET_URL_REQUEST_URL_REQUEST_FILTER_H_
@@ -27,24 +12,29 @@
#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_job_factory.h"
+#include "net/url_request/url_request_interceptor.h"
class GURL;
namespace net {
class URLRequestJob;
+class URLRequestInterceptor;
-class NET_EXPORT URLRequestFilter {
+// A class to help filter URLRequest jobs based on the URL of the request
+// rather than just the scheme. Example usage:
+//
+// // Intercept "scheme://host/" requests.
+// URLRequestFilter::GetInstance()->AddHostnameInterceptor("scheme", "host",
+// interceptor.Pass());
+// // Add special handling for the URL http://foo.com/
+// URLRequestFilter::GetInstance()->AddUrlInterceptor(GURL("http://foo.com/"),
+// interceptor.Pass());
+//
+// If the URLRequestFilter::MaybeInterceptRequest can't find a handler for a
+// request, it returns NULL and lets the configured ProtocolHandler handle the
+// request.
+class NET_EXPORT URLRequestFilter : public URLRequestInterceptor {
public:
- // scheme,hostname -> ProtocolHandler
- typedef std::map<std::pair<std::string, std::string>,
- URLRequestJobFactory::ProtocolHandler* > HostnameHandlerMap;
- // URL -> ProtocolHandler
- typedef base::hash_map<std::string, URLRequestJobFactory::ProtocolHandler*>
- UrlHandlerMap;
-
- ~URLRequestFilter();
-
static URLRequest::ProtocolFactory Factory;
// Singleton instance for use.
@@ -53,10 +43,10 @@ class NET_EXPORT URLRequestFilter {
void AddHostnameHandler(const std::string& scheme,
const std::string& hostname,
URLRequest::ProtocolFactory* factory);
- void AddHostnameProtocolHandler(
+ void AddHostnameInterceptor(
const std::string& scheme,
const std::string& hostname,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler> protocol_handler);
+ scoped_ptr<URLRequestInterceptor> interceptor);
void RemoveHostnameHandler(const std::string& scheme,
const std::string& hostname);
@@ -64,9 +54,8 @@ class NET_EXPORT URLRequestFilter {
// old handlers for the URL if one existed.
bool AddUrlHandler(const GURL& url,
URLRequest::ProtocolFactory* factory);
- bool AddUrlProtocolHandler(
- const GURL& url,
- scoped_ptr<URLRequestJobFactory::ProtocolHandler> protocol_handler);
+ bool AddUrlInterceptor(const GURL& url,
+ scoped_ptr<URLRequestInterceptor> interceptor);
void RemoveUrlHandler(const GURL& url);
@@ -77,23 +66,29 @@ class NET_EXPORT URLRequestFilter {
// Returns the number of times a handler was used to service a request.
int hit_count() const { return hit_count_; }
- protected:
- URLRequestFilter();
+ // URLRequestInterceptor implementation:
+ virtual URLRequestJob* MaybeInterceptRequest(
+ URLRequest* request,
+ NetworkDelegate* network_delegate) const OVERRIDE;
- // Helper method that looks up the request in the url_handler_map_.
- URLRequestJob* FindRequestHandler(URLRequest* request,
- NetworkDelegate* network_delegate,
- const std::string& scheme);
+ private:
+ // scheme,hostname -> URLRequestInterceptor
+ typedef std::map<std::pair<std::string, std::string>,
+ URLRequestInterceptor* > HostnameInterceptorMap;
+ // URL -> URLRequestInterceptor
+ typedef base::hash_map<std::string, URLRequestInterceptor*> URLInterceptorMap;
- // Maps hostnames to factories. Hostnames take priority over URLs.
- HostnameHandlerMap hostname_handler_map_;
+ URLRequestFilter();
+ virtual ~URLRequestFilter();
- // Maps URLs to factories.
- UrlHandlerMap url_handler_map_;
+ // Maps hostnames to interceptors. Hostnames take priority over URLs.
+ HostnameInterceptorMap hostname_interceptor_map_;
- int hit_count_;
+ // Maps URLs to interceptors.
+ URLInterceptorMap url_interceptor_map_;
+
+ mutable int hit_count_;
- private:
// Singleton instance.
static URLRequestFilter* shared_instance_;
diff --git a/chromium/net/url_request/url_request_filter_unittest.cc b/chromium/net/url_request/url_request_filter_unittest.cc
index 2e024a285e6..48a3cef7962 100644
--- a/chromium/net/url_request/url_request_filter_unittest.cc
+++ b/chromium/net/url_request/url_request_filter_unittest.cc
@@ -8,8 +8,8 @@
#include "net/base/request_priority.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job.h"
-#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -38,11 +38,11 @@ URLRequestJob* FactoryB(URLRequest* request,
URLRequestTestJob* job_c;
-class TestProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
+class TestURLRequestInterceptor : public URLRequestInterceptor {
public:
- virtual ~TestProtocolHandler() {}
+ virtual ~TestURLRequestInterceptor() {}
- virtual URLRequestJob* MaybeCreateJob(
+ virtual URLRequestJob* MaybeInterceptRequest(
URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
job_c = new URLRequestTestJob(request, network_delegate);
return job_c;
@@ -52,6 +52,7 @@ class TestProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
TEST(URLRequestFilter, BasicMatching) {
TestDelegate delegate;
TestURLRequestContext request_context;
+ URLRequestFilter* filter = URLRequestFilter::GetInstance();
GURL url_1("http://foo.com/");
TestURLRequest request_1(
@@ -62,93 +63,82 @@ TEST(URLRequestFilter, BasicMatching) {
url_2, DEFAULT_PRIORITY, &delegate, &request_context);
// Check AddUrlHandler checks for invalid URLs.
- EXPECT_FALSE(URLRequestFilter::GetInstance()->AddUrlHandler(GURL(),
- &FactoryA));
+ EXPECT_FALSE(filter->AddUrlHandler(GURL(), &FactoryA));
// Check URL matching.
- URLRequestFilter::GetInstance()->ClearHandlers();
- EXPECT_TRUE(URLRequestFilter::GetInstance()->AddUrlHandler(url_1,
- &FactoryA));
+ filter->ClearHandlers();
+ EXPECT_TRUE(filter->AddUrlHandler(url_1, &FactoryA));
{
- scoped_refptr<URLRequestJob> found = URLRequestFilter::Factory(
- &request_1, NULL, url_1.scheme());
+ scoped_refptr<URLRequestJob> found =
+ filter->MaybeInterceptRequest(&request_1, NULL);
EXPECT_EQ(job_a, found);
EXPECT_TRUE(job_a != NULL);
job_a = NULL;
}
- EXPECT_EQ(URLRequestFilter::GetInstance()->hit_count(), 1);
+ EXPECT_EQ(filter->hit_count(), 1);
// Check we don't match other URLs.
- EXPECT_TRUE(URLRequestFilter::Factory(
- &request_2, NULL, url_2.scheme()) == NULL);
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ EXPECT_TRUE(filter->MaybeInterceptRequest(&request_2, NULL) == NULL);
+ EXPECT_EQ(1, filter->hit_count());
// Check we can remove URL matching.
- URLRequestFilter::GetInstance()->RemoveUrlHandler(url_1);
- EXPECT_TRUE(URLRequestFilter::Factory(
- &request_1, NULL, url_1.scheme()) == NULL);
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ filter->RemoveUrlHandler(url_1);
+ EXPECT_TRUE(filter->MaybeInterceptRequest(&request_1, NULL) == NULL);
+ EXPECT_EQ(1, filter->hit_count());
// Check hostname matching.
- URLRequestFilter::GetInstance()->ClearHandlers();
- EXPECT_EQ(0, URLRequestFilter::GetInstance()->hit_count());
- URLRequestFilter::GetInstance()->AddHostnameHandler(url_1.scheme(),
- url_1.host(),
- &FactoryB);
+ filter->ClearHandlers();
+ EXPECT_EQ(0, filter->hit_count());
+ filter->AddHostnameHandler(url_1.scheme(), url_1.host(), &FactoryB);
{
- scoped_refptr<URLRequestJob> found = URLRequestFilter::Factory(
- &request_1, NULL, url_1.scheme());
+ scoped_refptr<URLRequestJob> found =
+ filter->MaybeInterceptRequest(&request_1, NULL);
EXPECT_EQ(job_b, found);
EXPECT_TRUE(job_b != NULL);
job_b = NULL;
}
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ EXPECT_EQ(1, filter->hit_count());
// Check we don't match other hostnames.
- EXPECT_TRUE(URLRequestFilter::Factory(
- &request_2, NULL, url_2.scheme()) == NULL);
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ EXPECT_TRUE(filter->MaybeInterceptRequest(&request_2, NULL) == NULL);
+ EXPECT_EQ(1, filter->hit_count());
// Check we can remove hostname matching.
- URLRequestFilter::GetInstance()->RemoveHostnameHandler(url_1.scheme(),
- url_1.host());
- EXPECT_TRUE(URLRequestFilter::Factory(
- &request_1, NULL, url_1.scheme()) == NULL);
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
-
- // Check ProtocolHandler hostname matching.
- URLRequestFilter::GetInstance()->ClearHandlers();
- EXPECT_EQ(0, URLRequestFilter::GetInstance()->hit_count());
- URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
+ filter->RemoveHostnameHandler(url_1.scheme(), url_1.host());
+ EXPECT_TRUE(filter->MaybeInterceptRequest(&request_1, NULL) == NULL);
+ EXPECT_EQ(1, filter->hit_count());
+
+ // Check URLRequestInterceptor hostname matching.
+ filter->ClearHandlers();
+ EXPECT_EQ(0, filter->hit_count());
+ filter->AddHostnameInterceptor(
url_1.scheme(), url_1.host(),
- scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- new TestProtocolHandler()));
+ scoped_ptr<net::URLRequestInterceptor>(new TestURLRequestInterceptor()));
{
- scoped_refptr<URLRequestJob> found = URLRequestFilter::Factory(
- &request_1, NULL, url_1.scheme());
+ scoped_refptr<URLRequestJob> found =
+ filter->MaybeInterceptRequest(&request_1, NULL);
EXPECT_EQ(job_c, found);
EXPECT_TRUE(job_c != NULL);
job_c = NULL;
}
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ EXPECT_EQ(1, filter->hit_count());
- // Check ProtocolHandler URL matching.
- URLRequestFilter::GetInstance()->ClearHandlers();
- EXPECT_EQ(0, URLRequestFilter::GetInstance()->hit_count());
- URLRequestFilter::GetInstance()->AddUrlProtocolHandler(
+ // Check URLRequestInterceptor URL matching.
+ filter->ClearHandlers();
+ EXPECT_EQ(0, filter->hit_count());
+ filter->AddUrlInterceptor(
url_2,
- scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- new TestProtocolHandler()));
+ scoped_ptr<net::URLRequestInterceptor>(new TestURLRequestInterceptor()));
{
- scoped_refptr<URLRequestJob> found = URLRequestFilter::Factory(
- &request_2, NULL, url_2.scheme());
+ scoped_refptr<URLRequestJob> found =
+ filter->MaybeInterceptRequest(&request_2, NULL);
EXPECT_EQ(job_c, found);
EXPECT_TRUE(job_c != NULL);
job_c = NULL;
}
- EXPECT_EQ(1, URLRequestFilter::GetInstance()->hit_count());
+ EXPECT_EQ(1, filter->hit_count());
- URLRequestFilter::GetInstance()->ClearHandlers();
+ filter->ClearHandlers();
}
} // namespace
diff --git a/chromium/net/url_request/url_request_ftp_job.cc b/chromium/net/url_request/url_request_ftp_job.cc
index 6224141a8ee..caba5219a55 100644
--- a/chromium/net/url_request/url_request_ftp_job.cc
+++ b/chromium/net/url_request/url_request_ftp_job.cc
@@ -34,9 +34,9 @@ URLRequestFtpJob::URLRequestFtpJob(
pac_request_(NULL),
http_response_info_(NULL),
read_in_progress_(false),
- weak_factory_(this),
ftp_transaction_factory_(ftp_transaction_factory),
- ftp_auth_cache_(ftp_auth_cache) {
+ ftp_auth_cache_(ftp_auth_cache),
+ weak_factory_(this) {
DCHECK(proxy_service_);
DCHECK(ftp_transaction_factory);
DCHECK(ftp_auth_cache);
@@ -188,12 +188,12 @@ void URLRequestFtpJob::StartHttpTransaction() {
http_request_info_.load_flags = request_->load_flags();
int rv = request_->context()->http_transaction_factory()->CreateTransaction(
- priority_, &http_transaction_, NULL);
+ priority_, &http_transaction_);
if (rv == OK) {
rv = http_transaction_->Start(
&http_request_info_,
base::Bind(&URLRequestFtpJob::OnStartCompleted,
- base::Unretained(this)),
+ base::Unretained(this)),
request_->net_log());
if (rv == ERR_IO_PENDING)
return;
@@ -218,6 +218,7 @@ void URLRequestFtpJob::OnStartCompleted(int result) {
if (result == OK) {
if (http_transaction_) {
http_response_info_ = http_transaction_->GetResponseInfo();
+ SetProxyServer(http_response_info_->proxy_server);
if (http_response_info_->headers->response_code() == 401 ||
http_response_info_->headers->response_code() == 407) {
diff --git a/chromium/net/url_request/url_request_ftp_job.h b/chromium/net/url_request/url_request_ftp_job.h
index 4a064a01cba..6c40b65a8c5 100644
--- a/chromium/net/url_request/url_request_ftp_job.h
+++ b/chromium/net/url_request/url_request_ftp_job.h
@@ -94,11 +94,11 @@ class NET_EXPORT_PRIVATE URLRequestFtpJob : public URLRequestJob {
scoped_refptr<AuthData> auth_data_;
- base::WeakPtrFactory<URLRequestFtpJob> weak_factory_;
-
FtpTransactionFactory* ftp_transaction_factory_;
FtpAuthCache* ftp_auth_cache_;
+ base::WeakPtrFactory<URLRequestFtpJob> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(URLRequestFtpJob);
};
diff --git a/chromium/net/url_request/url_request_ftp_job_unittest.cc b/chromium/net/url_request/url_request_ftp_job_unittest.cc
index 4bc6675551d..8797a3584fb 100644
--- a/chromium/net/url_request/url_request_ftp_job_unittest.cc
+++ b/chromium/net/url_request/url_request_ftp_job_unittest.cc
@@ -7,9 +7,10 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/run_loop.h"
+#include "net/base/host_port_pair.h"
#include "net/base/request_priority.h"
#include "net/ftp/ftp_auth_cache.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/proxy/mock_proxy_resolver.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_config_service_fixed.h"
@@ -23,6 +24,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
+
namespace net {
class FtpTestURLRequestContext : public TestURLRequestContext {
@@ -274,6 +277,8 @@ TEST_F(URLRequestFtpJobTest, FtpProxyRequest) {
socket_data(0)->RunFor(4);
EXPECT_TRUE(url_request.status().is_success());
+ EXPECT_TRUE(url_request.proxy_server().Equals(
+ net::HostPortPair::FromString("localhost:80")));
EXPECT_EQ(1, network_delegate()->completed_requests());
EXPECT_EQ(0, network_delegate()->error_count());
EXPECT_FALSE(request_delegate.auth_required_called());
@@ -327,6 +332,8 @@ TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedProxyAuthNoCredentials) {
socket_data(0)->RunFor(5);
EXPECT_TRUE(url_request.status().is_success());
+ EXPECT_TRUE(url_request.proxy_server().Equals(
+ net::HostPortPair::FromString("localhost:80")));
EXPECT_EQ(1, network_delegate()->completed_requests());
EXPECT_EQ(0, network_delegate()->error_count());
EXPECT_TRUE(request_delegate.auth_required_called());
@@ -625,6 +632,8 @@ TEST_F(URLRequestFtpJobTest, FtpProxyRequestReuseSocket) {
socket_data(0)->RunFor(4);
EXPECT_TRUE(url_request1.status().is_success());
+ EXPECT_TRUE(url_request1.proxy_server().Equals(
+ net::HostPortPair::FromString("localhost:80")));
EXPECT_EQ(1, network_delegate()->completed_requests());
EXPECT_EQ(0, network_delegate()->error_count());
EXPECT_FALSE(request_delegate1.auth_required_called());
diff --git a/chromium/net/url_request/url_request_http_job.cc b/chromium/net/url_request/url_request_http_job.cc
index cac7e38aea8..4b7d4e0fae5 100644
--- a/chromium/net/url_request/url_request_http_job.cc
+++ b/chromium/net/url_request/url_request_http_job.cc
@@ -16,7 +16,6 @@
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "net/base/filter.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
@@ -25,14 +24,14 @@
#include "net/base/network_delegate.h"
#include "net/base/sdch_manager.h"
#include "net/cert/cert_status_flags.h"
-#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_store.h"
+#include "net/http/http_content_disposition.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_status_code.h"
#include "net/http/http_transaction.h"
-#include "net/http/http_transaction_delegate.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_cert_request_info.h"
@@ -60,12 +59,14 @@ class URLRequestHttpJob::HttpFilterContext : public FilterContext {
// FilterContext implementation.
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
virtual bool GetURL(GURL* gurl) const OVERRIDE;
+ virtual bool GetContentDisposition(std::string* disposition) const OVERRIDE;
virtual base::Time GetRequestTime() const OVERRIDE;
virtual bool IsCachedContent() const OVERRIDE;
virtual bool IsDownload() const OVERRIDE;
virtual bool IsSdchResponse() const OVERRIDE;
virtual int64 GetByteReadCount() const OVERRIDE;
virtual int GetResponseCode() const OVERRIDE;
+ virtual const URLRequestContext* GetURLRequestContext() const OVERRIDE;
virtual void RecordPacketStats(StatisticSelector statistic) const OVERRIDE;
// Method to allow us to reset filter context for a response that should have
@@ -78,84 +79,6 @@ class URLRequestHttpJob::HttpFilterContext : public FilterContext {
DISALLOW_COPY_AND_ASSIGN(HttpFilterContext);
};
-class URLRequestHttpJob::HttpTransactionDelegateImpl
- : public HttpTransactionDelegate {
- public:
- HttpTransactionDelegateImpl(URLRequest* request,
- NetworkDelegate* network_delegate)
- : request_(request),
- network_delegate_(network_delegate),
- state_(NONE_ACTIVE) {}
- virtual ~HttpTransactionDelegateImpl() { OnDetachRequest(); }
- void OnDetachRequest() {
- if (!IsRequestAndDelegateActive())
- return;
- NotifyStateChange(NetworkDelegate::REQUEST_WAIT_STATE_RESET);
- state_ = NONE_ACTIVE;
- request_ = NULL;
- }
- virtual void OnCacheActionStart() OVERRIDE {
- HandleStateChange(NONE_ACTIVE,
- CACHE_ACTIVE,
- NetworkDelegate::REQUEST_WAIT_STATE_CACHE_START);
- }
- virtual void OnCacheActionFinish() OVERRIDE {
- HandleStateChange(CACHE_ACTIVE,
- NONE_ACTIVE,
- NetworkDelegate::REQUEST_WAIT_STATE_CACHE_FINISH);
- }
- virtual void OnNetworkActionStart() OVERRIDE {
- HandleStateChange(NONE_ACTIVE,
- NETWORK_ACTIVE,
- NetworkDelegate::REQUEST_WAIT_STATE_NETWORK_START);
- }
- virtual void OnNetworkActionFinish() OVERRIDE {
- HandleStateChange(NETWORK_ACTIVE,
- NONE_ACTIVE,
- NetworkDelegate::REQUEST_WAIT_STATE_NETWORK_FINISH);
- }
-
- private:
- enum State {
- NONE_ACTIVE,
- CACHE_ACTIVE,
- NETWORK_ACTIVE
- };
-
- // Returns true if this object still has an active request and network
- // delegate.
- bool IsRequestAndDelegateActive() const {
- return request_ && network_delegate_;
- }
-
- // Notifies the |network_delegate_| object of a change in the state of the
- // |request_| to the state given by the |request_wait_state| argument.
- void NotifyStateChange(NetworkDelegate::RequestWaitState request_wait_state) {
- network_delegate_->NotifyRequestWaitStateChange(*request_,
- request_wait_state);
- }
-
- // Checks the request and delegate are still active, changes |state_| from
- // |expected_state| to |next_state|, and then notifies the network delegate of
- // the change to |request_wait_state|.
- void HandleStateChange(State expected_state,
- State next_state,
- NetworkDelegate::RequestWaitState request_wait_state) {
- if (!IsRequestAndDelegateActive())
- return;
- DCHECK_EQ(expected_state, state_);
- state_ = next_state;
- NotifyStateChange(request_wait_state);
- }
-
- URLRequest* request_;
- NetworkDelegate* network_delegate_;
- // Internal state tracking, for sanity checking.
- State state_;
-
- DISALLOW_COPY_AND_ASSIGN(HttpTransactionDelegateImpl);
-};
-
URLRequestHttpJob::HttpFilterContext::HttpFilterContext(URLRequestHttpJob* job)
: job_(job) {
DCHECK(job_);
@@ -176,6 +99,13 @@ bool URLRequestHttpJob::HttpFilterContext::GetURL(GURL* gurl) const {
return true;
}
+bool URLRequestHttpJob::HttpFilterContext::GetContentDisposition(
+ std::string* disposition) const {
+ HttpResponseHeaders* headers = job_->GetResponseHeaders();
+ void *iter = NULL;
+ return headers->EnumerateHeader(&iter, "Content-Disposition", disposition);
+}
+
base::Time URLRequestHttpJob::HttpFilterContext::GetRequestTime() const {
return job_->request() ? job_->request()->request_time() : base::Time();
}
@@ -205,6 +135,11 @@ int URLRequestHttpJob::HttpFilterContext::GetResponseCode() const {
return job_->GetResponseCode();
}
+const URLRequestContext*
+URLRequestHttpJob::HttpFilterContext::GetURLRequestContext() const {
+ return job_->request() ? job_->request()->context() : NULL;
+}
+
void URLRequestHttpJob::HttpFilterContext::RecordPacketStats(
StatisticSelector statistic) const {
job_->RecordPacketStats(statistic);
@@ -229,7 +164,7 @@ URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request,
return new URLRequestRedirectJob(
request, network_delegate, redirect_url,
// Use status code 307 to preserve the method, so POST requests work.
- URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT);
+ URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "HSTS");
}
return new URLRequestHttpJob(request,
network_delegate,
@@ -264,14 +199,12 @@ URLRequestHttpJob::URLRequestHttpJob(
request_time_snapshot_(),
final_packet_time_(),
filter_context_(new HttpFilterContext(this)),
- weak_factory_(this),
on_headers_received_callback_(
base::Bind(&URLRequestHttpJob::OnHeadersReceivedCallback,
base::Unretained(this))),
awaiting_callback_(false),
- http_transaction_delegate_(
- new HttpTransactionDelegateImpl(request, network_delegate)),
- http_user_agent_settings_(http_user_agent_settings) {
+ http_user_agent_settings_(http_user_agent_settings),
+ weak_factory_(this) {
URLRequestThrottlerManager* manager = request->context()->throttler_manager();
if (manager)
throttling_entry_ = manager->RegisterRequestUrl(request->url());
@@ -293,20 +226,6 @@ URLRequestHttpJob::~URLRequestHttpJob() {
// filter_context_ is still alive.
DestroyFilters();
- if (sdch_dictionary_url_.is_valid()) {
- // Prior to reaching the destructor, request_ has been set to a NULL
- // pointer, so request_->url() is no longer valid in the destructor, and we
- // use an alternate copy |request_info_.url|.
- SdchManager* manager = SdchManager::Global();
- // To be extra safe, since this is a "different time" from when we decided
- // to get the dictionary, we'll validate that an SdchManager is available.
- // At shutdown time, care is taken to be sure that we don't delete this
- // globally useful instance "too soon," so this check is just defensive
- // coding to assure that IF the system is shutting down, we don't have any
- // problem if the manager was deleted ahead of time.
- if (manager) // Defensive programming.
- manager->FetchDictionary(request_info_.url, sdch_dictionary_url_);
- }
DoneWithRequest(ABORTED);
}
@@ -335,7 +254,7 @@ void URLRequestHttpJob::Start() {
// Privacy mode could still be disabled in OnCookiesLoaded if we are going
// to send previously saved cookies.
request_info_.privacy_mode = enable_privacy_mode ?
- kPrivacyModeEnabled : kPrivacyModeDisabled;
+ PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED;
// Strip Referer from request_info_.extra_headers to prevent, e.g., plugins
// from overriding headers that are controlled using other means. Otherwise a
@@ -352,16 +271,13 @@ void URLRequestHttpJob::Start() {
request_info_.extra_headers.SetHeaderIfMissing(
HttpRequestHeaders::kUserAgent,
http_user_agent_settings_ ?
- http_user_agent_settings_->GetUserAgent(request_->url()) :
- std::string());
+ http_user_agent_settings_->GetUserAgent() : std::string());
AddExtraHeaders();
AddCookieHeaderAndStart();
}
void URLRequestHttpJob::Kill() {
- http_transaction_delegate_->OnDetachRequest();
-
if (!transaction_.get())
return;
@@ -389,8 +305,8 @@ void URLRequestHttpJob::NotifyHeadersComplete() {
ProcessStrictTransportSecurityHeader();
ProcessPublicKeyPinsHeader();
- if (SdchManager::Global() &&
- SdchManager::Global()->IsInSupportedDomain(request_->url())) {
+ SdchManager* sdch_manager(request()->context()->sdch_manager());
+ if (sdch_manager && sdch_manager->IsInSupportedDomain(request_->url())) {
const std::string name = "Get-Dictionary";
std::string url_text;
void* iter = NULL;
@@ -401,11 +317,11 @@ void URLRequestHttpJob::NotifyHeadersComplete() {
// Eventually we should wait until a dictionary is requested several times
// before we even download it (so that we don't waste memory or bandwidth).
if (GetResponseHeaders()->EnumerateHeader(&iter, name, &url_text)) {
- // request_->url() won't be valid in the destructor, so we use an
- // alternate copy.
- DCHECK_EQ(request_->url(), request_info_.url);
// Resolve suggested URL relative to request url.
- sdch_dictionary_url_ = request_info_.url.Resolve(url_text);
+ GURL sdch_dictionary_url = request_->url().Resolve(url_text);
+ if (sdch_dictionary_url.is_valid()) {
+ sdch_manager->FetchDictionary(request_->url(), sdch_dictionary_url);
+ }
}
}
@@ -495,7 +411,7 @@ void URLRequestHttpJob::StartTransactionInternal() {
DCHECK(request_->context()->http_transaction_factory());
rv = request_->context()->http_transaction_factory()->CreateTransaction(
- priority_, &transaction_, http_transaction_delegate_.get());
+ priority_, &transaction_);
if (rv == OK && request_info_.url.SchemeIsWSOrWSS()) {
// TODO(ricea): Implement WebSocket throttling semantics as defined in
@@ -511,6 +427,10 @@ void URLRequestHttpJob::StartTransactionInternal() {
}
if (rv == OK) {
+ transaction_->SetBeforeNetworkStartCallback(
+ base::Bind(&URLRequestHttpJob::NotifyBeforeNetworkStart,
+ base::Unretained(this)));
+
if (!throttling_entry_.get() ||
!throttling_entry_->ShouldRejectRequest(*request_)) {
rv = transaction_->Start(
@@ -535,6 +455,8 @@ void URLRequestHttpJob::StartTransactionInternal() {
}
void URLRequestHttpJob::AddExtraHeaders() {
+ SdchManager* sdch_manager = request()->context()->sdch_manager();
+
// Supply Accept-Encoding field only if it is not already provided.
// It should be provided IF the content is known to have restrictions on
// potential encoding, such as streaming multi-media.
@@ -544,19 +466,24 @@ void URLRequestHttpJob::AddExtraHeaders() {
// simple_data_source.
if (!request_info_.extra_headers.HasHeader(
HttpRequestHeaders::kAcceptEncoding)) {
- bool advertise_sdch = SdchManager::Global() &&
- SdchManager::Global()->IsInSupportedDomain(request_->url());
+ bool advertise_sdch = sdch_manager &&
+ // We don't support SDCH responses to POST as there is a possibility
+ // of having SDCH encoded responses returned (e.g. by the cache)
+ // which we cannot decode, and in those situations, we will need
+ // to retransmit the request without SDCH, which is illegal for a POST.
+ request()->method() != "POST" &&
+ sdch_manager->IsInSupportedDomain(request_->url());
std::string avail_dictionaries;
if (advertise_sdch) {
- SdchManager::Global()->GetAvailDictionaryList(request_->url(),
- &avail_dictionaries);
+ sdch_manager->GetAvailDictionaryList(request_->url(),
+ &avail_dictionaries);
// The AllowLatencyExperiment() is only true if we've successfully done a
// full SDCH compression recently in this browser session for this host.
// Note that for this path, there might be no applicable dictionaries,
// and hence we can't participate in the experiment.
if (!avail_dictionaries.empty() &&
- SdchManager::Global()->AllowLatencyExperiment(request_->url())) {
+ sdch_manager->AllowLatencyExperiment(request_->url())) {
// We are participating in the test (or control), and hence we'll
// eventually record statistics via either SDCH_EXPERIMENT_DECODE or
// SDCH_EXPERIMENT_HOLDBACK, and we'll need some packet timing data.
@@ -620,17 +547,12 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
if (!request_)
return;
- CookieStore* cookie_store = request_->context()->cookie_store();
+ CookieStore* cookie_store = GetCookieStore();
if (cookie_store && !(request_info_.load_flags & LOAD_DO_NOT_SEND_COOKIES)) {
- net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
- if (cookie_monster) {
- cookie_monster->GetAllCookiesForURLAsync(
- request_->url(),
- base::Bind(&URLRequestHttpJob::CheckCookiePolicyAndLoad,
- weak_factory_.GetWeakPtr()));
- } else {
- CheckCookiePolicyAndLoad(CookieList());
- }
+ cookie_store->GetAllCookiesForURLAsync(
+ request_->url(),
+ base::Bind(&URLRequestHttpJob::CheckCookiePolicyAndLoad,
+ weak_factory_.GetWeakPtr()));
} else {
DoStartTransaction();
}
@@ -639,7 +561,7 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
void URLRequestHttpJob::DoLoadCookies() {
CookieOptions options;
options.set_include_httponly();
- request_->context()->cookie_store()->GetCookiesWithOptionsAsync(
+ GetCookieStore()->GetCookiesWithOptionsAsync(
request_->url(), options,
base::Bind(&URLRequestHttpJob::OnCookiesLoaded,
weak_factory_.GetWeakPtr()));
@@ -658,7 +580,7 @@ void URLRequestHttpJob::OnCookiesLoaded(const std::string& cookie_line) {
request_info_.extra_headers.SetHeader(
HttpRequestHeaders::kCookie, cookie_line);
// Disable privacy mode as we are sending cookies anyway.
- request_info_.privacy_mode = kPrivacyModeDisabled;
+ request_info_.privacy_mode = PRIVACY_MODE_DISABLED;
}
DoStartTransaction();
}
@@ -719,8 +641,7 @@ void URLRequestHttpJob::SaveNextCookie() {
new SharedBoolean(true);
if (!(request_info_.load_flags & LOAD_DO_NOT_SAVE_COOKIES) &&
- request_->context()->cookie_store() &&
- response_cookies_.size() > 0) {
+ GetCookieStore() && response_cookies_.size() > 0) {
CookieOptions options;
options.set_include_httponly();
options.set_server_time(response_date_);
@@ -738,7 +659,7 @@ void URLRequestHttpJob::SaveNextCookie() {
if (CanSetCookie(
response_cookies_[response_cookies_save_index_], &options)) {
callback_pending->data = true;
- request_->context()->cookie_store()->SetCookieWithOptionsAsync(
+ GetCookieStore()->SetCookieWithOptionsAsync(
request_->url(), response_cookies_[response_cookies_save_index_],
options, callback);
}
@@ -852,9 +773,9 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
if (!request_)
return;
- // If the transaction was destroyed, then the job was cancelled, and
- // we can just ignore this notification.
- if (!transaction_.get())
+ // If the job is done (due to cancellation), can just ignore this
+ // notification.
+ if (done_)
return;
receive_headers_end_ = base::TimeTicks::Now();
@@ -879,17 +800,22 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
}
if (result == OK) {
+ if (transaction_ && transaction_->GetResponseInfo()) {
+ SetProxyServer(transaction_->GetResponseInfo()->proxy_server);
+ }
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
if (network_delegate()) {
// Note that |this| may not be deleted until
// |on_headers_received_callback_| or
// |NetworkDelegate::URLRequestDestroyed()| has been called.
OnCallToDelegate();
+ allowed_unsafe_redirect_url_ = GURL();
int error = network_delegate()->NotifyHeadersReceived(
request_,
on_headers_received_callback_,
headers.get(),
- &override_response_headers_);
+ &override_response_headers_,
+ &allowed_unsafe_redirect_url_);
if (error != net::OK) {
if (error == net::ERR_IO_PENDING) {
awaiting_callback_ = true;
@@ -917,14 +843,13 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
NotifySSLCertificateError(info, true);
} else {
// Maybe overridable, maybe not. Ask the delegate to decide.
- TransportSecurityState::DomainState domain_state;
const URLRequestContext* context = request_->context();
- const bool fatal = context->transport_security_state() &&
- context->transport_security_state()->GetDomainState(
+ TransportSecurityState* state = context->transport_security_state();
+ const bool fatal =
+ state &&
+ state->ShouldSSLErrorsBeFatal(
request_info_.url.host(),
- SSLConfigService::IsSNIAvailable(context->ssl_config_service()),
- &domain_state) &&
- domain_state.ShouldSSLErrorsBeFatal();
+ SSLConfigService::IsSNIAvailable(context->ssl_config_service()));
NotifySSLCertificateError(
transaction_->GetResponseInfo()->ssl_info, fatal);
}
@@ -932,6 +857,10 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
NotifyCertificateRequested(
transaction_->GetResponseInfo()->cert_request_info.get());
} else {
+ // Even on an error, there may be useful information in the response
+ // info (e.g. whether there's a cached copy).
+ if (transaction_.get())
+ response_info_ = transaction_->GetResponseInfo();
NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
}
}
@@ -1023,9 +952,10 @@ bool URLRequestHttpJob::GetCharset(std::string* charset) {
void URLRequestHttpJob::GetResponseInfo(HttpResponseInfo* info) {
DCHECK(request_);
- DCHECK(transaction_.get());
if (response_info_) {
+ DCHECK(transaction_.get());
+
*info = *response_info_;
if (override_response_headers_.get())
info->headers = override_response_headers_;
@@ -1105,6 +1035,16 @@ Filter* URLRequestHttpJob::SetupFilter() const {
? Filter::Factory(encoding_types, *filter_context_) : NULL;
}
+bool URLRequestHttpJob::CopyFragmentOnRedirect(const GURL& location) const {
+ // Allow modification of reference fragments by default, unless
+ // |allowed_unsafe_redirect_url_| is set and equal to the redirect URL.
+ // When this is the case, we assume that the network delegate has set the
+ // desired redirect URL (with or without fragment), so it must not be changed
+ // any more.
+ return !allowed_unsafe_redirect_url_.is_valid() ||
+ allowed_unsafe_redirect_url_ != location;
+}
+
bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
// HTTP is always safe.
// TODO(pauljensen): Remove once crbug.com/146591 is fixed.
@@ -1112,6 +1052,11 @@ bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
(location.scheme() == "http" || location.scheme() == "https")) {
return true;
}
+ // Delegates may mark a URL as safe for redirection.
+ if (allowed_unsafe_redirect_url_.is_valid() &&
+ allowed_unsafe_redirect_url_ == location) {
+ return true;
+ }
// Query URLRequestJobFactory as to whether |location| would be safe to
// redirect to.
return request_->context()->job_factory() &&
@@ -1250,6 +1195,11 @@ void URLRequestHttpJob::ContinueDespiteLastError() {
weak_factory_.GetWeakPtr(), rv));
}
+void URLRequestHttpJob::ResumeNetworkStart() {
+ DCHECK(transaction_.get());
+ transaction_->ResumeNetworkStart();
+}
+
bool URLRequestHttpJob::ShouldFixMismatchedContentLength(int rv) const {
// Some servers send the body compressed, but specify the content length as
// the uncompressed size. Although this violates the HTTP spec we want to
@@ -1316,9 +1266,35 @@ bool URLRequestHttpJob::GetFullRequestHeaders(
return transaction_->GetFullRequestHeaders(headers);
}
+int64 URLRequestHttpJob::GetTotalReceivedBytes() const {
+ if (!transaction_)
+ return 0;
+
+ return transaction_->GetTotalReceivedBytes();
+}
+
void URLRequestHttpJob::DoneReading() {
- if (transaction_.get())
+ if (transaction_) {
transaction_->DoneReading();
+ }
+ DoneWithRequest(FINISHED);
+}
+
+void URLRequestHttpJob::DoneReadingRedirectResponse() {
+ if (transaction_) {
+ if (transaction_->GetResponseInfo()->headers->IsRedirect(NULL)) {
+ // If the original headers indicate a redirect, go ahead and cache the
+ // response, even if the |override_response_headers_| are a redirect to
+ // another location.
+ transaction_->DoneReading();
+ } else {
+ // Otherwise, |override_response_headers_| must be non-NULL and contain
+ // bogus headers indicating a redirect.
+ DCHECK(override_response_headers_);
+ DCHECK(override_response_headers_->IsRedirect(NULL));
+ transaction_->StopCaching();
+ }
+ }
DoneWithRequest(FINISHED);
}
@@ -1535,8 +1511,4 @@ void URLRequestHttpJob::NotifyURLRequestDestroyed() {
awaiting_callback_ = false;
}
-void URLRequestHttpJob::OnDetachRequest() {
- http_transaction_delegate_->OnDetachRequest();
-}
-
} // namespace net
diff --git a/chromium/net/url_request/url_request_http_job.h b/chromium/net/url_request/url_request_http_job.h
index d5f46e0174b..d38b46281cc 100644
--- a/chromium/net/url_request/url_request_http_job.h
+++ b/chromium/net/url_request/url_request_http_job.h
@@ -16,6 +16,7 @@
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/cookies/cookie_store.h"
+#include "net/filter/filter.h"
#include "net/http/http_request_info.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_throttler_entry_interface.h"
@@ -106,6 +107,7 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
virtual bool GetResponseCookies(std::vector<std::string>* cookies) OVERRIDE;
virtual int GetResponseCode() const OVERRIDE;
virtual Filter* SetupFilter() const OVERRIDE;
+ virtual bool CopyFragmentOnRedirect(const GURL& location) const OVERRIDE;
virtual bool IsSafeRedirect(const GURL& location) OVERRIDE;
virtual bool NeedsAuth() OVERRIDE;
virtual void GetAuthChallengeInfo(scoped_refptr<AuthChallengeInfo>*) OVERRIDE;
@@ -113,12 +115,16 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
virtual void CancelAuth() OVERRIDE;
virtual void ContinueWithCertificate(X509Certificate* client_cert) OVERRIDE;
virtual void ContinueDespiteLastError() OVERRIDE;
+ virtual void ResumeNetworkStart() OVERRIDE;
virtual bool ReadRawData(IOBuffer* buf, int buf_size,
int* bytes_read) OVERRIDE;
virtual void StopCaching() OVERRIDE;
virtual bool GetFullRequestHeaders(
HttpRequestHeaders* headers) const OVERRIDE;
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
virtual void DoneReading() OVERRIDE;
+ virtual void DoneReadingRedirectResponse() OVERRIDE;
+
virtual HostPortPair GetSocketAddress() const OVERRIDE;
virtual void NotifyURLRequestDestroyed() OVERRIDE;
@@ -165,9 +171,6 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
// overridden by |override_response_headers_|.
HttpResponseHeaders* GetResponseHeaders() const;
- // Override of the private interface of URLRequestJob.
- virtual void OnDetachRequest() OVERRIDE;
-
RequestPriority priority_;
HttpRequestInfo request_info_;
@@ -187,9 +190,6 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
bool read_in_progress_;
- // An URL for an SDCH dictionary as suggested in a Get-Dictionary HTTP header.
- GURL sdch_dictionary_url_;
-
scoped_ptr<HttpTransaction> transaction_;
// This is used to supervise traffic and enforce exponential
@@ -245,7 +245,6 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
base::TimeTicks receive_headers_end_;
scoped_ptr<HttpFilterContext> filter_context_;
- base::WeakPtrFactory<URLRequestHttpJob> weak_factory_;
CompletionCallback on_headers_received_callback_;
@@ -254,6 +253,11 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
// layers of the network stack.
scoped_refptr<HttpResponseHeaders> override_response_headers_;
+ // The network delegate can mark a URL as safe for redirection.
+ // The reference fragment of the original URL is not appended to the redirect
+ // URL when the redirect URL is equal to |allowed_unsafe_redirect_url_|.
+ GURL allowed_unsafe_redirect_url_;
+
// Flag used to verify that |this| is not deleted while we are awaiting
// a callback from the NetworkDelegate. Used as a fail-fast mechanism.
// True if we are waiting a callback and
@@ -261,10 +265,10 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
// to inform the NetworkDelegate that it may not call back.
bool awaiting_callback_;
- scoped_ptr<HttpTransactionDelegateImpl> http_transaction_delegate_;
-
const HttpUserAgentSettings* http_user_agent_settings_;
+ base::WeakPtrFactory<URLRequestHttpJob> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(URLRequestHttpJob);
};
diff --git a/chromium/net/url_request/url_request_http_job_unittest.cc b/chromium/net/url_request/url_request_http_job_unittest.cc
index 9637917b9de..203d6d077da 100644
--- a/chromium/net/url_request/url_request_http_job_unittest.cc
+++ b/chromium/net/url_request/url_request_http_job_unittest.cc
@@ -12,7 +12,7 @@
#include "net/base/auth.h"
#include "net/base/request_priority.h"
#include "net/http/http_transaction_factory.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/socket/socket_test_util.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
@@ -213,10 +213,6 @@ class FakeWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
return ERR_IO_PENDING;
}
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
- return NULL;
- }
-
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE {
diff --git a/chromium/net/url_request/url_request_intercepting_job_factory.cc b/chromium/net/url_request/url_request_intercepting_job_factory.cc
new file mode 100644
index 00000000000..43789e1c27c
--- /dev/null
+++ b/chromium/net/url_request/url_request_intercepting_job_factory.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 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/url_request/url_request_intercepting_job_factory.h"
+
+#include "base/logging.h"
+#include "net/url_request/url_request_interceptor.h"
+
+namespace net {
+
+URLRequestInterceptingJobFactory::URLRequestInterceptingJobFactory(
+ scoped_ptr<URLRequestJobFactory> job_factory,
+ scoped_ptr<URLRequestInterceptor> interceptor)
+ : job_factory_(job_factory.Pass()),
+ interceptor_(interceptor.Pass()) {
+}
+
+URLRequestInterceptingJobFactory::~URLRequestInterceptingJobFactory() {}
+
+URLRequestJob* URLRequestInterceptingJobFactory::
+MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ URLRequest* request,
+ NetworkDelegate* network_delegate) const {
+ DCHECK(CalledOnValidThread());
+ URLRequestJob* job = interceptor_->MaybeInterceptRequest(request,
+ network_delegate);
+ if (job)
+ return job;
+ return job_factory_->MaybeCreateJobWithProtocolHandler(
+ scheme, request, network_delegate);
+}
+
+bool URLRequestInterceptingJobFactory::IsHandledProtocol(
+ const std::string& scheme) const {
+ return job_factory_->IsHandledProtocol(scheme);
+}
+
+bool URLRequestInterceptingJobFactory::IsHandledURL(const GURL& url) const {
+ return job_factory_->IsHandledURL(url);
+}
+
+bool URLRequestInterceptingJobFactory::IsSafeRedirectTarget(
+ const GURL& location) const {
+ return job_factory_->IsSafeRedirectTarget(location);
+}
+
+} // namespace net
diff --git a/chromium/net/url_request/url_request_intercepting_job_factory.h b/chromium/net/url_request/url_request_intercepting_job_factory.h
new file mode 100644
index 00000000000..b18af54726e
--- /dev/null
+++ b/chromium/net/url_request/url_request_intercepting_job_factory.h
@@ -0,0 +1,58 @@
+// Copyright 2014 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_URL_REQUEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_
+#define NET_URL_REQUEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/url_request/url_request_job_factory.h"
+
+class GURL;
+
+namespace net {
+
+class URLRequest;
+class URLRequestJob;
+class URLRequestInterceptor;
+
+// This class acts as a wrapper for URLRequestJobFactory. The
+// URLRequestInteceptor is given the option of creating a URLRequestJob for each
+// URLRequest. If the interceptor does not create a job, the URLRequest is
+// forwarded to the wrapped URLRequestJobFactory instead.
+//
+// This class is only intended for use in intercepting requests before they
+// are passed on to their default ProtocolHandler. Each supported scheme should
+// have its own ProtocolHandler.
+class NET_EXPORT URLRequestInterceptingJobFactory
+ : public URLRequestJobFactory {
+ public:
+ URLRequestInterceptingJobFactory(
+ scoped_ptr<URLRequestJobFactory> job_factory,
+ scoped_ptr<URLRequestInterceptor> interceptor);
+ virtual ~URLRequestInterceptingJobFactory();
+
+ // URLRequestJobFactory implementation
+ virtual URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ URLRequest* request,
+ NetworkDelegate* network_delegate) const OVERRIDE;
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE;
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE;
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE;
+
+ private:
+ scoped_ptr<URLRequestJobFactory> job_factory_;
+ scoped_ptr<URLRequestInterceptor> interceptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestInterceptingJobFactory);
+};
+
+} // namespace net
+
+#endif // NET_URL_REQUEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_
diff --git a/chromium/net/url_request/url_request_interceptor.cc b/chromium/net/url_request/url_request_interceptor.cc
new file mode 100644
index 00000000000..bcd0fd5ceee
--- /dev/null
+++ b/chromium/net/url_request/url_request_interceptor.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 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/url_request/url_request_interceptor.h"
+
+namespace net {
+
+URLRequestInterceptor::URLRequestInterceptor() {
+}
+
+URLRequestInterceptor::~URLRequestInterceptor() {
+}
+
+} // namespace net
diff --git a/chromium/net/url_request/url_request_interceptor.h b/chromium/net/url_request/url_request_interceptor.h
new file mode 100644
index 00000000000..682368d25a8
--- /dev/null
+++ b/chromium/net/url_request/url_request_interceptor.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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_URL_REQUEST_URL_REQUEST_INTERCEPTOR_H_
+#define NET_URL_REQUEST_URL_REQUEST_INTERCEPTOR_H_
+
+#include "base/macros.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class URLRequest;
+class URLRequestJob;
+class NetworkDelegate;
+
+// A URLRequestInterceptor is given a chance to create a URLRequestJob to
+// handle URLRequests before they're handed off to the ProtocolHandler for
+// the request's scheme.
+class NET_EXPORT URLRequestInterceptor {
+ public:
+ URLRequestInterceptor();
+ virtual ~URLRequestInterceptor();
+
+ // Returns a URLRequestJob to handle |request|, if the interceptor wants to
+ // take over the handling the request instead of the default ProtocolHandler.
+ // Otherwise, returns NULL.
+ virtual URLRequestJob* MaybeInterceptRequest(
+ URLRequest* request, NetworkDelegate* network_delegate) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(URLRequestInterceptor);
+};
+
+} // namespace net
+
+#endif // NET_URL_REQUEST_URL_REQUEST_INTERCEPTOR_H_
diff --git a/chromium/net/url_request/url_request_job.cc b/chromium/net/url_request/url_request_job.cc
index 1d6f0b49d19..a66a3f553f9 100644
--- a/chromium/net/url_request/url_request_job.cc
+++ b/chromium/net/url_request/url_request_job.cc
@@ -16,6 +16,7 @@
#include "net/base/load_states.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate.h"
+#include "net/filter/filter.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
@@ -59,7 +60,6 @@ void URLRequestJob::Kill() {
void URLRequestJob::DetachRequest() {
request_ = NULL;
- OnDetachRequest();
}
// This function calls ReadData to get stream data. If a filter exists, passes
@@ -111,6 +111,10 @@ bool URLRequestJob::GetFullRequestHeaders(HttpRequestHeaders* headers) const {
return false;
}
+int64 URLRequestJob::GetTotalReceivedBytes() const {
+ return 0;
+}
+
LoadState URLRequestJob::GetLoadState() const {
return LOAD_STATE_IDLE;
}
@@ -154,6 +158,10 @@ bool URLRequestJob::IsRedirectResponse(GURL* location,
return true;
}
+bool URLRequestJob::CopyFragmentOnRedirect(const GURL& location) const {
+ return true;
+}
+
bool URLRequestJob::IsSafeRedirect(const GURL& location) {
return true;
}
@@ -213,6 +221,12 @@ void URLRequestJob::FollowDeferredRedirect() {
FollowRedirect(redirect_url, redirect_status_code);
}
+void URLRequestJob::ResumeNetworkStart() {
+ // This should only be called for HTTP Jobs, and implemented in the derived
+ // class.
+ NOTREACHED();
+}
+
bool URLRequestJob::GetMimeType(std::string* mime_type) const {
return false;
}
@@ -276,6 +290,19 @@ bool URLRequestJob::CanEnablePrivacyMode() const {
return request_->CanEnablePrivacyMode();
}
+CookieStore* URLRequestJob::GetCookieStore() const {
+ DCHECK(request_);
+
+ return request_->cookie_store();
+}
+
+void URLRequestJob::NotifyBeforeNetworkStart(bool* defer) {
+ if (!request_)
+ return;
+
+ request_->NotifyBeforeNetworkStart(defer);
+}
+
void URLRequestJob::NotifyHeadersComplete() {
if (!request_ || !request_->has_delegate())
return; // The request was destroyed, so there is no more work to do.
@@ -304,11 +331,16 @@ void URLRequestJob::NotifyHeadersComplete() {
GURL new_location;
int http_status_code;
if (IsRedirectResponse(&new_location, &http_status_code)) {
+ // Redirect response bodies are not read. Notify the transaction
+ // so it does not treat being stopped as an error.
+ DoneReadingRedirectResponse();
+
const GURL& url = request_->url();
// Move the reference fragment of the old location to the new one if the
// new one has none. This duplicates mozilla's behavior.
- if (url.is_valid() && url.has_ref() && !new_location.has_ref()) {
+ if (url.is_valid() && url.has_ref() && !new_location.has_ref() &&
+ CopyFragmentOnRedirect(new_location)) {
GURL::Replacements replacements;
// Reference the |ref| directly out of the original URL to avoid a
// malloc.
@@ -317,10 +349,6 @@ 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);
@@ -371,8 +399,9 @@ void URLRequestJob::NotifyReadComplete(int bytes_read) {
// TODO(darin): Bug 1004233. Re-enable this test once all of the chrome
// unit_tests have been fixed to not trip this.
- //DCHECK(!request_->status().is_io_pending());
-
+#if 0
+ DCHECK(!request_->status().is_io_pending());
+#endif
// The headers should be complete before reads complete
DCHECK(has_handled_response_);
@@ -414,8 +443,13 @@ void URLRequestJob::NotifyStartError(const URLRequestStatus &status) {
DCHECK(!has_handled_response_);
has_handled_response_ = true;
if (request_) {
+ // There may be relevant information in the response info even in the
+ // error case.
+ GetResponseInfo(&request_->response_info_);
+
request_->set_status(status);
request_->NotifyResponseStarted();
+ // We may have been deleted.
}
}
@@ -504,139 +538,138 @@ void URLRequestJob::DoneReading() {
// Do nothing.
}
+void URLRequestJob::DoneReadingRedirectResponse() {
+}
+
void URLRequestJob::FilteredDataRead(int bytes_read) {
- DCHECK(filter_.get()); // don't add data if there is no filter
+ DCHECK(filter_);
filter_->FlushStreamBuffer(bytes_read);
}
bool URLRequestJob::ReadFilteredData(int* bytes_read) {
- DCHECK(filter_.get()); // don't add data if there is no filter
- DCHECK(filtered_read_buffer_.get() !=
- NULL); // we need to have a buffer to fill
- DCHECK_GT(filtered_read_buffer_len_, 0); // sanity check
- DCHECK_LT(filtered_read_buffer_len_, 1000000); // sanity check
- DCHECK(raw_read_buffer_.get() ==
- NULL); // there should be no raw read buffer yet
+ DCHECK(filter_);
+ DCHECK(filtered_read_buffer_);
+ DCHECK_GT(filtered_read_buffer_len_, 0);
+ DCHECK_LT(filtered_read_buffer_len_, 1000000); // Sanity check.
+ DCHECK(!raw_read_buffer_);
- bool rv = false;
*bytes_read = 0;
+ bool rv = false;
- if (is_done())
- return true;
-
- if (!filter_needs_more_output_space_ && !filter_->stream_data_len()) {
- // We don't have any raw data to work with, so
- // read from the socket.
- int filtered_data_read;
- if (ReadRawDataForFilter(&filtered_data_read)) {
- if (filtered_data_read > 0) {
- filter_->FlushStreamBuffer(filtered_data_read); // Give data to filter.
+ for (;;) {
+ if (is_done())
+ return true;
+
+ if (!filter_needs_more_output_space_ && !filter_->stream_data_len()) {
+ // We don't have any raw data to work with, so read from the transaction.
+ int filtered_data_read;
+ if (ReadRawDataForFilter(&filtered_data_read)) {
+ if (filtered_data_read > 0) {
+ // Give data to filter.
+ filter_->FlushStreamBuffer(filtered_data_read);
+ } else {
+ return true; // EOF.
+ }
} else {
- return true; // EOF
+ return false; // IO Pending (or error).
}
- } else {
- return false; // IO Pending (or error)
- }
- }
-
- if ((filter_->stream_data_len() || filter_needs_more_output_space_)
- && !is_done()) {
- // Get filtered data.
- int filtered_data_len = filtered_read_buffer_len_;
- Filter::FilterStatus status;
- int output_buffer_size = filtered_data_len;
- status = filter_->ReadData(filtered_read_buffer_->data(),
- &filtered_data_len);
-
- if (filter_needs_more_output_space_ && 0 == filtered_data_len) {
- // filter_needs_more_output_space_ was mistaken... there are no more bytes
- // and we should have at least tried to fill up the filter's input buffer.
- // Correct the state, and try again.
- filter_needs_more_output_space_ = false;
- return ReadFilteredData(bytes_read);
}
- switch (status) {
- case Filter::FILTER_DONE: {
+ if ((filter_->stream_data_len() || filter_needs_more_output_space_) &&
+ !is_done()) {
+ // Get filtered data.
+ int filtered_data_len = filtered_read_buffer_len_;
+ int output_buffer_size = filtered_data_len;
+ Filter::FilterStatus status =
+ filter_->ReadData(filtered_read_buffer_->data(), &filtered_data_len);
+
+ if (filter_needs_more_output_space_ && !filtered_data_len) {
+ // filter_needs_more_output_space_ was mistaken... there are no more
+ // bytes and we should have at least tried to fill up the filter's input
+ // buffer. Correct the state, and try again.
filter_needs_more_output_space_ = false;
- *bytes_read = filtered_data_len;
- postfilter_bytes_read_ += filtered_data_len;
- rv = true;
- break;
+ continue;
}
- case Filter::FILTER_NEED_MORE_DATA: {
- filter_needs_more_output_space_ =
- (filtered_data_len == output_buffer_size);
- // We have finished filtering all data currently in the buffer.
- // There might be some space left in the output buffer. One can
- // consider reading more data from the stream to feed the filter
- // and filling up the output buffer. This leads to more complicated
- // buffer management and data notification mechanisms.
- // We can revisit this issue if there is a real perf need.
- if (filtered_data_len > 0) {
+ filter_needs_more_output_space_ =
+ (filtered_data_len == output_buffer_size);
+
+ switch (status) {
+ case Filter::FILTER_DONE: {
+ filter_needs_more_output_space_ = false;
*bytes_read = filtered_data_len;
postfilter_bytes_read_ += filtered_data_len;
rv = true;
- } else {
- // Read again since we haven't received enough data yet (e.g., we may
- // not have a complete gzip header yet)
- rv = ReadFilteredData(bytes_read);
+ break;
+ }
+ case Filter::FILTER_NEED_MORE_DATA: {
+ // We have finished filtering all data currently in the buffer.
+ // There might be some space left in the output buffer. One can
+ // consider reading more data from the stream to feed the filter
+ // and filling up the output buffer. This leads to more complicated
+ // buffer management and data notification mechanisms.
+ // We can revisit this issue if there is a real perf need.
+ if (filtered_data_len > 0) {
+ *bytes_read = filtered_data_len;
+ postfilter_bytes_read_ += filtered_data_len;
+ rv = true;
+ } else {
+ // Read again since we haven't received enough data yet (e.g., we
+ // may not have a complete gzip header yet).
+ continue;
+ }
+ break;
+ }
+ case Filter::FILTER_OK: {
+ *bytes_read = filtered_data_len;
+ postfilter_bytes_read_ += filtered_data_len;
+ rv = true;
+ break;
+ }
+ case Filter::FILTER_ERROR: {
+ DVLOG(1) << __FUNCTION__ << "() "
+ << "\"" << (request_ ? request_->url().spec() : "???")
+ << "\"" << " Filter Error";
+ filter_needs_more_output_space_ = false;
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+ ERR_CONTENT_DECODING_FAILED));
+ rv = false;
+ break;
+ }
+ default: {
+ NOTREACHED();
+ filter_needs_more_output_space_ = false;
+ rv = false;
+ break;
}
- break;
- }
- case Filter::FILTER_OK: {
- filter_needs_more_output_space_ =
- (filtered_data_len == output_buffer_size);
- *bytes_read = filtered_data_len;
- postfilter_bytes_read_ += filtered_data_len;
- rv = true;
- break;
- }
- case Filter::FILTER_ERROR: {
- DVLOG(1) << __FUNCTION__ << "() "
- << "\"" << (request_ ? request_->url().spec() : "???") << "\""
- << " Filter Error";
- filter_needs_more_output_space_ = false;
- NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
- ERR_CONTENT_DECODING_FAILED));
- rv = false;
- break;
}
- default: {
- NOTREACHED();
- filter_needs_more_output_space_ = false;
- rv = false;
- break;
+
+ // If logging all bytes is enabled, log the filtered bytes read.
+ if (rv && request() && request()->net_log().IsLoggingBytes() &&
+ filtered_data_len > 0) {
+ request()->net_log().AddByteTransferEvent(
+ NetLog::TYPE_URL_REQUEST_JOB_FILTERED_BYTES_READ,
+ filtered_data_len, filtered_read_buffer_->data());
}
+ } else {
+ // we are done, or there is no data left.
+ rv = true;
}
- DVLOG(2) << __FUNCTION__ << "() "
- << "\"" << (request_ ? request_->url().spec() : "???") << "\""
- << " rv = " << rv
- << " post bytes read = " << filtered_data_len
- << " pre total = " << prefilter_bytes_read_
- << " post total = "
- << postfilter_bytes_read_;
- // If logging all bytes is enabled, log the filtered bytes read.
- if (rv && request() && request()->net_log().IsLoggingBytes() &&
- filtered_data_len > 0) {
- request()->net_log().AddByteTransferEvent(
- NetLog::TYPE_URL_REQUEST_JOB_FILTERED_BYTES_READ,
- filtered_data_len, filtered_read_buffer_->data());
- }
- } else {
- // we are done, or there is no data left.
- rv = true;
+ break;
}
if (rv) {
- // When we successfully finished a read, we no longer need to
- // save the caller's buffers. Release our reference.
+ // When we successfully finished a read, we no longer need to save the
+ // caller's buffers. Release our reference.
filtered_read_buffer_ = NULL;
filtered_read_buffer_len_ = 0;
}
return rv;
}
+void URLRequestJob::DestroyFilters() {
+ filter_.reset();
+}
+
const URLRequestStatus URLRequestJob::GetStatus() {
if (request_)
return request_->status();
@@ -650,6 +683,10 @@ void URLRequestJob::SetStatus(const URLRequestStatus &status) {
request_->set_status(status);
}
+void URLRequestJob::SetProxyServer(const HostPortPair& proxy_server) {
+ request_->proxy_server_ = proxy_server;
+}
+
bool URLRequestJob::ReadRawDataForFilter(int* bytes_read) {
bool rv = false;
@@ -681,15 +718,6 @@ bool URLRequestJob::ReadRawDataHelper(IOBuffer* buf, int buf_size,
bool rv = ReadRawData(buf, buf_size, bytes_read);
if (!request_->status().is_io_pending()) {
- // If |filter_| is NULL, and logging all bytes is enabled, log the raw
- // bytes read.
- if (!filter_.get() && request() && request()->net_log().IsLoggingBytes() &&
- *bytes_read > 0) {
- request()->net_log().AddByteTransferEvent(
- NetLog::TYPE_URL_REQUEST_JOB_BYTES_READ,
- *bytes_read, raw_read_buffer_->data());
- }
-
// If the read completes synchronously, either success or failure,
// invoke the OnRawReadComplete callback so we can account for the
// completed read.
@@ -706,6 +734,14 @@ void URLRequestJob::FollowRedirect(const GURL& location, int http_status_code) {
void URLRequestJob::OnRawReadComplete(int bytes_read) {
DCHECK(raw_read_buffer_.get());
+ // If |filter_| is non-NULL, bytes will be logged after it is applied instead.
+ if (!filter_.get() && request() && request()->net_log().IsLoggingBytes() &&
+ bytes_read > 0) {
+ request()->net_log().AddByteTransferEvent(
+ NetLog::TYPE_URL_REQUEST_JOB_BYTES_READ,
+ bytes_read, raw_read_buffer_->data());
+ }
+
if (bytes_read > 0) {
RecordBytesRead(bytes_read);
}
diff --git a/chromium/net/url_request/url_request_job.h b/chromium/net/url_request/url_request_job.h
index 9695f6a7aa9..2942e79d616 100644
--- a/chromium/net/url_request/url_request_job.h
+++ b/chromium/net/url_request/url_request_job.h
@@ -13,7 +13,6 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/power_monitor/power_observer.h"
-#include "net/base/filter.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_states.h"
#include "net/base/net_export.h"
@@ -27,6 +26,8 @@ namespace net {
class AuthChallengeInfo;
class AuthCredentials;
class CookieOptions;
+class CookieStore;
+class Filter;
class HttpRequestHeaders;
class HttpResponseInfo;
class IOBuffer;
@@ -107,6 +108,9 @@ class NET_EXPORT URLRequestJob
virtual bool GetFullRequestHeaders(HttpRequestHeaders* headers) const;
+ // Get the number of bytes received from network.
+ virtual int64 GetTotalReceivedBytes() const;
+
// Called to fetch the current load state for the job.
virtual LoadState GetLoadState() const;
@@ -153,6 +157,13 @@ class NET_EXPORT URLRequestJob
// The default implementation inspects the response_info_.
virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
+ // Called to determine if it is okay to copy the reference fragment from the
+ // original URL (if existent) to the redirection target when the redirection
+ // target has no reference fragment.
+ //
+ // The default implementation returns true.
+ virtual bool CopyFragmentOnRedirect(const GURL& location) const;
+
// Called to determine if it is okay to redirect this job to the specified
// location. This may be used to implement protocol-specific restrictions.
// If this function returns false, then the URLRequest will fail
@@ -179,6 +190,9 @@ class NET_EXPORT URLRequestJob
// Continue processing the request ignoring the last error.
virtual void ContinueDespiteLastError();
+ // Continue with the network request.
+ virtual void ResumeNetworkStart();
+
void FollowDeferredRedirect();
// Returns true if the Job is done producing response data and has called
@@ -232,6 +246,12 @@ class NET_EXPORT URLRequestJob
// Delegates to URLRequest::Delegate.
bool CanEnablePrivacyMode() const;
+ // Returns the cookie store to be used for the request.
+ CookieStore* GetCookieStore() const;
+
+ // Notifies the job that the network is about to be used.
+ void NotifyBeforeNetworkStart(bool* defer);
+
// Notifies the job that headers have been received.
void NotifyHeadersComplete();
@@ -257,7 +277,7 @@ class NET_EXPORT URLRequestJob
void NotifyCanceled();
// Notifies the job the request should be restarted.
- // Should only be called if the job has not started a resposne.
+ // Should only be called if the job has not started a response.
void NotifyRestartRequired();
// See corresponding functions in url_request.h.
@@ -280,6 +300,11 @@ class NET_EXPORT URLRequestJob
// the stream.
virtual void DoneReading();
+ // Called to tell the job that the body won't be read because it's a redirect.
+ // This is needed so that redirect headers can be cached even though their
+ // bodies are never read.
+ virtual void DoneReadingRedirectResponse();
+
// Informs the filter that data has been read into its buffer
void FilteredDataRead(int bytes_read);
@@ -297,7 +322,7 @@ class NET_EXPORT URLRequestJob
// be destroyed so that statistics can be gathered while the derived class is
// still present to assist in calculations. This is used by URLRequestHttpJob
// to get SDCH to emit stats.
- void DestroyFilters() { filter_.reset(); }
+ void DestroyFilters();
// Provides derived classes with access to the request's network delegate.
NetworkDelegate* network_delegate() { return network_delegate_; }
@@ -308,6 +333,9 @@ class NET_EXPORT URLRequestJob
// Set the status of the job.
void SetStatus(const URLRequestStatus& status);
+ // Set the proxy server that was used, if any.
+ void SetProxyServer(const HostPortPair& proxy_server);
+
// The number of bytes read before passing to the filter.
int prefilter_bytes_read() const { return prefilter_bytes_read_; }
@@ -356,9 +384,6 @@ class NET_EXPORT URLRequestJob
// The default implementation does nothing.
virtual void UpdatePacketReadTimes();
- // Custom handler for derived classes when the request is detached.
- virtual void OnDetachRequest() {}
-
// Indicates that the job is done producing data, either it has completed
// all the data or an error has been encountered. Set exclusively by
// NotifyDone so that it is kept in sync with the request.
diff --git a/chromium/net/url_request/url_request_job_factory_impl.cc b/chromium/net/url_request/url_request_job_factory_impl.cc
index f456da2798f..57e775b7f20 100644
--- a/chromium/net/url_request/url_request_job_factory_impl.cc
+++ b/chromium/net/url_request/url_request_job_factory_impl.cc
@@ -6,11 +6,18 @@
#include "base/stl_util.h"
#include "net/base/load_flags.h"
+#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job_manager.h"
#include "url/gurl.h"
namespace net {
+namespace {
+
+URLRequestInterceptor* g_interceptor_for_testing = NULL;
+
+} // namespace
+
URLRequestJobFactoryImpl::URLRequestJobFactoryImpl() {}
URLRequestJobFactoryImpl::~URLRequestJobFactoryImpl() {
@@ -43,6 +50,13 @@ URLRequestJob* URLRequestJobFactoryImpl::MaybeCreateJobWithProtocolHandler(
URLRequest* request,
NetworkDelegate* network_delegate) const {
DCHECK(CalledOnValidThread());
+ if (g_interceptor_for_testing) {
+ URLRequestJob* job = g_interceptor_for_testing->MaybeInterceptRequest(
+ request, network_delegate);
+ if (job)
+ return job;
+ }
+
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end())
return NULL;
@@ -80,4 +94,12 @@ bool URLRequestJobFactoryImpl::IsSafeRedirectTarget(
return it->second->IsSafeRedirectTarget(location);
}
+// static
+void URLRequestJobFactoryImpl::SetInterceptorForTesting(
+ URLRequestInterceptor* interceptor) {
+ DCHECK(!interceptor || !g_interceptor_for_testing);
+
+ g_interceptor_for_testing = interceptor;
+}
+
} // namespace net
diff --git a/chromium/net/url_request/url_request_job_factory_impl.h b/chromium/net/url_request/url_request_job_factory_impl.h
index 4f03fb073d4..8de514f4389 100644
--- a/chromium/net/url_request/url_request_job_factory_impl.h
+++ b/chromium/net/url_request/url_request_job_factory_impl.h
@@ -6,7 +6,7 @@
#define NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_IMPL_H_
#include <map>
-#include <vector>
+#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
@@ -15,6 +15,8 @@
namespace net {
+class URLRequestInterceptor;
+
class NET_EXPORT URLRequestJobFactoryImpl : public URLRequestJobFactory {
public:
URLRequestJobFactoryImpl();
@@ -36,8 +38,19 @@ class NET_EXPORT URLRequestJobFactoryImpl : public URLRequestJobFactory {
virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE;
private:
+ // For testing only.
+ friend class URLRequestFilter;
+
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
+ // Sets a global URLRequestInterceptor for testing purposes. The interceptor
+ // is given the chance to intercept any request before the corresponding
+ // ProtocolHandler, but after any other URLRequestJobFactories layered on top
+ // of the URLRequestJobFactoryImpl.
+ // If an interceptor is set, the old interceptor must be cleared before
+ // setting a new one.
+ static void SetInterceptorForTesting(URLRequestInterceptor* interceptor);
+
ProtocolHandlerMap protocol_handler_map_;
DISALLOW_COPY_AND_ASSIGN(URLRequestJobFactoryImpl);
diff --git a/chromium/net/url_request/url_request_job_manager.cc b/chromium/net/url_request/url_request_job_manager.cc
index 9ccf1d4e352..0fe557b1441 100644
--- a/chromium/net/url_request/url_request_job_manager.cc
+++ b/chromium/net/url_request/url_request_job_manager.cc
@@ -37,7 +37,6 @@ static const SchemeToFactory kBuiltinFactories[] = {
{ "ws", URLRequestHttpJob::Factory },
{ "wss", URLRequestHttpJob::Factory },
#endif // !defined(OS_IOS)
-
};
// static
@@ -58,12 +57,7 @@ URLRequestJob* URLRequestJobManager::CreateJob(
job_factory = request->context()->job_factory();
const std::string& scheme = request->url().scheme(); // already lowercase
- if (job_factory) {
- if (!job_factory->IsHandledProtocol(scheme)) {
- return new URLRequestErrorJob(
- request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
- }
- } else if (!SupportsScheme(scheme)) {
+ if (!job_factory->IsHandledProtocol(scheme)) {
return new URLRequestErrorJob(
request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
}
@@ -86,24 +80,10 @@ URLRequestJob* URLRequestJobManager::CreateJob(
}
}
- if (job_factory) {
- URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
- scheme, request, network_delegate);
- if (job)
- return job;
- }
-
- // TODO(willchan): Remove this in favor of
- // URLRequestJobFactory::ProtocolHandler.
- // See if the request should be handled by a registered protocol factory.
- // If the registered factory returns null, then we want to fall-back to the
- // built-in protocol factory.
- FactoryMap::const_iterator i = factories_.find(scheme);
- if (i != factories_.end()) {
- URLRequestJob* job = i->second(request, network_delegate, scheme);
- if (job)
- return job;
- }
+ URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
+ scheme, request, network_delegate);
+ if (job)
+ return job;
// See if the request should be handled by a built-in protocol factory.
for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
@@ -137,13 +117,8 @@ URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect(
job_factory = request->context()->job_factory();
const std::string& scheme = request->url().scheme(); // already lowercase
- if (job_factory) {
- if (!job_factory->IsHandledProtocol(scheme)) {
- return NULL;
- }
- } else if (!SupportsScheme(scheme)) {
+ if (!job_factory->IsHandledProtocol(scheme))
return NULL;
- }
InterceptorList::const_iterator i;
for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
@@ -169,13 +144,8 @@ URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
job_factory = request->context()->job_factory();
const std::string& scheme = request->url().scheme(); // already lowercase
- if (job_factory) {
- if (!job_factory->IsHandledProtocol(scheme)) {
- return NULL;
- }
- } else if (!SupportsScheme(scheme)) {
+ if (!job_factory->IsHandledProtocol(scheme))
return NULL;
- }
InterceptorList::const_iterator i;
for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
@@ -187,43 +157,16 @@ URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
return NULL;
}
-bool URLRequestJobManager::SupportsScheme(const std::string& scheme) const {
- // The set of registered factories may change on another thread.
- {
- base::AutoLock locked(lock_);
- if (factories_.find(scheme) != factories_.end())
- return true;
- }
-
- for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i)
+// static
+bool URLRequestJobManager::SupportsScheme(const std::string& scheme) {
+ for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
return true;
+ }
return false;
}
-URLRequest::ProtocolFactory* URLRequestJobManager::RegisterProtocolFactory(
- const std::string& scheme,
- URLRequest::ProtocolFactory* factory) {
- DCHECK(IsAllowedThread());
-
- base::AutoLock locked(lock_);
-
- URLRequest::ProtocolFactory* old_factory;
- FactoryMap::iterator i = factories_.find(scheme);
- if (i != factories_.end()) {
- old_factory = i->second;
- } else {
- old_factory = NULL;
- }
- if (factory) {
- factories_[scheme] = factory;
- } else if (i != factories_.end()) { // uninstall any old one
- factories_.erase(i);
- }
- return old_factory;
-}
-
void URLRequestJobManager::RegisterRequestInterceptor(
URLRequest::Interceptor* interceptor) {
DCHECK(IsAllowedThread());
diff --git a/chromium/net/url_request/url_request_job_manager.h b/chromium/net/url_request/url_request_job_manager.h
index ea441bea316..9877bed1891 100644
--- a/chromium/net/url_request/url_request_job_manager.h
+++ b/chromium/net/url_request/url_request_job_manager.h
@@ -5,7 +5,6 @@
#ifndef NET_URL_REQUEST_URL_REQUEST_JOB_MANAGER_H_
#define NET_URL_REQUEST_URL_REQUEST_JOB_MANAGER_H_
-#include <map>
#include <string>
#include <vector>
@@ -23,11 +22,8 @@ namespace net {
//
// MULTI-THREADING NOTICE:
// URLRequest is designed to have all consumers on a single thread, and
-// so no attempt is made to support ProtocolFactory or Interceptor instances
-// being registered/unregistered or in any way poked on multiple threads.
-// However, we do support checking for supported schemes FROM ANY THREAD
-// (i.e., it is safe to call SupportsScheme on any thread).
-//
+// so no attempt is made to support Interceptor instances being
+// registered/unregistered or in any way poked on multiple threads.
class URLRequestJobManager {
public:
// Returns the singleton instance.
@@ -52,23 +48,14 @@ class URLRequestJobManager {
URLRequestJob* MaybeInterceptResponse(
URLRequest* request, NetworkDelegate* network_delegate) const;
- // Returns true if there is a protocol factory registered for the given
- // scheme. Note: also returns true if there is a built-in handler for the
- // given scheme.
- bool SupportsScheme(const std::string& scheme) const;
-
- // Register a protocol factory associated with the given scheme. The factory
- // parameter may be null to clear any existing association. Returns the
- // previously registered protocol factory if any.
- URLRequest::ProtocolFactory* RegisterProtocolFactory(
- const std::string& scheme, URLRequest::ProtocolFactory* factory);
+ // Returns true if the manager has a built-in handler for |scheme|.
+ static bool SupportsScheme(const std::string& scheme);
// Register/unregister a request interceptor.
void RegisterRequestInterceptor(URLRequest::Interceptor* interceptor);
void UnregisterRequestInterceptor(URLRequest::Interceptor* interceptor);
private:
- typedef std::map<std::string, URLRequest::ProtocolFactory*> FactoryMap;
typedef std::vector<URLRequest::Interceptor*> InterceptorList;
friend struct DefaultSingletonTraits<URLRequestJobManager>;
@@ -105,7 +92,6 @@ class URLRequestJobManager {
#endif
mutable base::Lock lock_;
- FactoryMap factories_;
InterceptorList interceptors_;
DISALLOW_COPY_AND_ASSIGN(URLRequestJobManager);
diff --git a/chromium/net/url_request/url_request_job_unittest.cc b/chromium/net/url_request/url_request_job_unittest.cc
index 99f43141d98..bdce8bdcca9 100644
--- a/chromium/net/url_request/url_request_job_unittest.cc
+++ b/chromium/net/url_request/url_request_job_unittest.cc
@@ -4,8 +4,9 @@
#include "net/url_request/url_request_job.h"
+#include "base/run_loop.h"
#include "net/base/request_priority.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,13 +15,23 @@ namespace net {
namespace {
// This is a header that signals the end of the data.
-const char kGzipGata[] = "\x1f\x08b\x08\0\0\0\0\0\0\3\3\0\0\0\0\0\0\0\0";
+const char kGzipData[] = "\x1f\x08b\x08\0\0\0\0\0\0\3\3\0\0\0\0\0\0\0\0";
+const char kGzipDataWithName[] =
+ "\x1f\x08b\x08\x08\0\0\0\0\0\0name\0\3\0\0\0\0\0\0\0\0";
void GZipServer(const HttpRequestInfo* request,
std::string* response_status,
std::string* response_headers,
std::string* response_data) {
- response_data->assign(kGzipGata, sizeof(kGzipGata));
+ response_data->assign(kGzipData, sizeof(kGzipData));
+}
+
+void BigGZipServer(const HttpRequestInfo* request,
+ std::string* response_status,
+ std::string* response_headers,
+ std::string* response_data) {
+ response_data->assign(kGzipDataWithName, sizeof(kGzipDataWithName));
+ response_data->insert(10, 64 * 1024, 'a');
}
const MockTransaction kGZip_Transaction = {
@@ -96,7 +107,31 @@ TEST(URLRequestJob, SyncTransactionNotifiedWhenDone) {
req.set_method("GET");
req.Start();
- base::MessageLoop::current()->Run();
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(network_layer.done_reading_called());
+
+ RemoveMockTransaction(&transaction);
+}
+
+// Tests processing a large gzip header one byte at a time.
+TEST(URLRequestJob, SyncSlowTransaction) {
+ MockNetworkLayer network_layer;
+ TestURLRequestContext context;
+ context.set_http_transaction_factory(&network_layer);
+
+ TestDelegate d;
+ TestURLRequest req(
+ GURL(kGZip_Transaction.url), DEFAULT_PRIORITY, &d, &context);
+ MockTransaction transaction(kGZip_Transaction);
+ transaction.test_mode = TEST_MODE_SYNC_ALL | TEST_MODE_SLOW_READ;
+ transaction.handler = &BigGZipServer;
+ AddMockTransaction(&transaction);
+
+ req.set_method("GET");
+ req.Start();
+
+ base::RunLoop().Run();
EXPECT_TRUE(network_layer.done_reading_called());
@@ -116,11 +151,34 @@ TEST(URLRequestJob, RedirectTransactionNotifiedWhenDone) {
req.set_method("GET");
req.Start();
- base::MessageLoop::current()->Run();
+ base::RunLoop().Run();
EXPECT_TRUE(network_layer.done_reading_called());
RemoveMockTransaction(&kRedirect_Transaction);
}
+TEST(URLRequestJob, TransactionNotCachedWhenNetworkDelegateRedirects) {
+ MockNetworkLayer network_layer;
+ TestNetworkDelegate network_delegate;
+ network_delegate.set_redirect_on_headers_received_url(GURL("http://foo"));
+ TestURLRequestContext context;
+ context.set_http_transaction_factory(&network_layer);
+ context.set_network_delegate(&network_delegate);
+
+ TestDelegate d;
+ TestURLRequest req(GURL(kGZip_Transaction.url), DEFAULT_PRIORITY, &d,
+ &context);
+ AddMockTransaction(&kGZip_Transaction);
+
+ req.set_method("GET");
+ req.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(network_layer.stop_caching_called());
+
+ RemoveMockTransaction(&kGZip_Transaction);
+}
+
} // namespace net
diff --git a/chromium/net/url_request/url_request_redirect_job.cc b/chromium/net/url_request/url_request_redirect_job.cc
index 6454e36191c..818d4c397ec 100644
--- a/chromium/net/url_request/url_request_redirect_job.cc
+++ b/chromium/net/url_request/url_request_redirect_job.cc
@@ -6,21 +6,31 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "net/base/load_timing_info.h"
+#include "net/base/net_log.h"
+#include "net/url_request/url_request.h"
namespace net {
URLRequestRedirectJob::URLRequestRedirectJob(URLRequest* request,
NetworkDelegate* network_delegate,
const GURL& redirect_destination,
- StatusCode http_status_code)
+ StatusCode http_status_code,
+ const std::string& redirect_reason)
: URLRequestJob(request, network_delegate),
redirect_destination_(redirect_destination),
http_status_code_(http_status_code),
- weak_factory_(this) {}
+ redirect_reason_(redirect_reason),
+ weak_factory_(this) {
+ DCHECK(!redirect_reason_.empty());
+}
void URLRequestRedirectJob::Start() {
+ request()->net_log().AddEvent(
+ NetLog::TYPE_URL_REQUEST_REDIRECT_JOB,
+ NetLog::StringCallback("reason", &redirect_reason_));
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&URLRequestRedirectJob::StartAsync,
@@ -34,6 +44,12 @@ bool URLRequestRedirectJob::IsRedirectResponse(GURL* location,
return true;
}
+bool URLRequestRedirectJob::CopyFragmentOnRedirect(const GURL& location) const {
+ // The instantiators have full control over the desired redirection target,
+ // including the reference fragment part of the URL.
+ return false;
+}
+
URLRequestRedirectJob::~URLRequestRedirectJob() {}
void URLRequestRedirectJob::StartAsync() {
diff --git a/chromium/net/url_request/url_request_redirect_job.h b/chromium/net/url_request/url_request_redirect_job.h
index 1c18a7f65be..580fec2d109 100644
--- a/chromium/net/url_request/url_request_redirect_job.h
+++ b/chromium/net/url_request/url_request_redirect_job.h
@@ -5,6 +5,8 @@
#ifndef NET_URL_REQUEST_URL_REQUEST_REDIRECT_JOB_H_
#define NET_URL_REQUEST_URL_REQUEST_REDIRECT_JOB_H_
+#include <string>
+
#include "base/memory/weak_ptr.h"
#include "net/base/net_export.h"
#include "net/url_request/url_request_job.h"
@@ -27,15 +29,18 @@ class NET_EXPORT URLRequestRedirectJob : public URLRequestJob {
REDIRECT_307_TEMPORARY_REDIRECT = 307,
};
- // Constructs a job that redirects to the specified URL.
+ // Constructs a job that redirects to the specified URL. |redirect_reason| is
+ // logged for debugging purposes, and must not be an empty string.
URLRequestRedirectJob(URLRequest* request,
NetworkDelegate* network_delegate,
const GURL& redirect_destination,
- StatusCode http_status_code);
+ StatusCode http_status_code,
+ const std::string& redirect_reason);
virtual void Start() OVERRIDE;
virtual bool IsRedirectResponse(GURL* location,
int* http_status_code) OVERRIDE;
+ virtual bool CopyFragmentOnRedirect(const GURL& location) const OVERRIDE;
virtual void GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const OVERRIDE;
@@ -48,6 +53,7 @@ class NET_EXPORT URLRequestRedirectJob : public URLRequestJob {
const GURL redirect_destination_;
const int http_status_code_;
base::TimeTicks receive_headers_end_;
+ std::string redirect_reason_;
base::WeakPtrFactory<URLRequestRedirectJob> weak_factory_;
};
diff --git a/chromium/net/url_request/url_request_simple_job.cc b/chromium/net/url_request/url_request_simple_job.cc
index bd945557e3d..f266e3b505d 100644
--- a/chromium/net/url_request/url_request_simple_job.cc
+++ b/chromium/net/url_request/url_request_simple_job.cc
@@ -4,18 +4,22 @@
#include "net/url_request/url_request_simple_job.h"
+#include <vector>
+
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
#include "net/url_request/url_request_status.h"
namespace net {
URLRequestSimpleJob::URLRequestSimpleJob(
URLRequest* request, NetworkDelegate* network_delegate)
- : URLRequestJob(request, network_delegate),
+ : URLRangeRequestJob(request, network_delegate),
data_offset_(0),
weak_factory_(this) {}
@@ -42,7 +46,7 @@ URLRequestSimpleJob::~URLRequestSimpleJob() {}
bool URLRequestSimpleJob::ReadRawData(IOBuffer* buf, int buf_size,
int* bytes_read) {
DCHECK(bytes_read);
- int remaining = static_cast<int>(data_.size()) - data_offset_;
+ int remaining = byte_range_.last_byte_position() - data_offset_ + 1;
if (buf_size > remaining)
buf_size = remaining;
memcpy(buf->data(), data_.data() + data_offset_, buf_size);
@@ -55,6 +59,15 @@ void URLRequestSimpleJob::StartAsync() {
if (!request_)
return;
+ if (ranges().size() > 1) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+ ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+ return;
+ }
+
+ if (!ranges().empty() && range_parse_result() == OK)
+ byte_range_ = ranges().front();
+
int result = GetData(&mime_type_, &charset_, &data_,
base::Bind(&URLRequestSimpleJob::OnGetDataCompleted,
weak_factory_.GetWeakPtr()));
@@ -65,6 +78,16 @@ void URLRequestSimpleJob::StartAsync() {
void URLRequestSimpleJob::OnGetDataCompleted(int result) {
if (result == OK) {
// Notify that the headers are complete
+ if (!byte_range_.ComputeBounds(data_.size())) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+ ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+ return;
+ }
+
+ data_offset_ = byte_range_.first_byte_position();
+ int remaining_bytes = byte_range_.last_byte_position() -
+ byte_range_.first_byte_position() + 1;
+ set_expected_content_size(remaining_bytes);
NotifyHeadersComplete();
} else {
NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
diff --git a/chromium/net/url_request/url_request_simple_job.h b/chromium/net/url_request/url_request_simple_job.h
index 7577151debe..633a2e7af47 100644
--- a/chromium/net/url_request/url_request_simple_job.h
+++ b/chromium/net/url_request/url_request_simple_job.h
@@ -10,13 +10,13 @@
#include "base/memory/weak_ptr.h"
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
-#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_range_request_job.h"
namespace net {
class URLRequest;
-class NET_EXPORT URLRequestSimpleJob : public URLRequestJob {
+class NET_EXPORT URLRequestSimpleJob : public URLRangeRequestJob {
public:
URLRequestSimpleJob(URLRequest* request, NetworkDelegate* network_delegate);
@@ -51,6 +51,7 @@ class NET_EXPORT URLRequestSimpleJob : public URLRequestJob {
private:
void OnGetDataCompleted(int result);
+ HttpByteRange byte_range_;
std::string mime_type_;
std::string charset_;
std::string data_;
diff --git a/chromium/net/url_request/url_request_simple_job_unittest.cc b/chromium/net/url_request/url_request_simple_job_unittest.cc
new file mode 100644
index 00000000000..dfd985acd4c
--- /dev/null
+++ b/chromium/net/url_request/url_request_simple_job_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2014 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 "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/request_priority.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_simple_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const char kTestData[] = "Huge data array";
+const int kRangeFirstPosition = 5;
+const int kRangeLastPosition = 8;
+COMPILE_ASSERT(kRangeFirstPosition > 0 &&
+ kRangeFirstPosition < kRangeLastPosition &&
+ kRangeLastPosition < static_cast<int>(arraysize(kTestData) - 1),
+ invalid_range);
+
+class MockSimpleJob : public URLRequestSimpleJob {
+ public:
+ MockSimpleJob(URLRequest* request, NetworkDelegate* network_delegate)
+ : URLRequestSimpleJob(request, network_delegate) {
+ }
+
+ protected:
+ virtual int GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* data,
+ const CompletionCallback& callback) const OVERRIDE {
+ mime_type->assign("text/plain");
+ charset->assign("US-ASCII");
+ data->assign(kTestData);
+ return OK;
+ }
+
+ private:
+ virtual ~MockSimpleJob() {}
+
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockSimpleJob);
+};
+
+class SimpleJobProtocolHandler :
+ public URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual URLRequestJob* MaybeCreateJob(
+ URLRequest* request,
+ NetworkDelegate* network_delegate) const OVERRIDE {
+ return new MockSimpleJob(request, network_delegate);
+ }
+};
+
+class URLRequestSimpleJobTest : public ::testing::Test {
+ public:
+ URLRequestSimpleJobTest() : context_(true) {
+ job_factory_.SetProtocolHandler("data", new SimpleJobProtocolHandler());
+ context_.set_job_factory(&job_factory_);
+ context_.Init();
+
+ request_.reset(new URLRequest(
+ GURL("data:test"), DEFAULT_PRIORITY, &delegate_, &context_));
+ }
+
+ void StartRequest(const HttpRequestHeaders* headers) {
+ if (headers)
+ request_->SetExtraRequestHeaders(*headers);
+ request_->Start();
+
+ EXPECT_TRUE(request_->is_pending());
+ base::RunLoop().Run();
+ EXPECT_FALSE(request_->is_pending());
+ }
+
+ protected:
+ URLRequestJobFactoryImpl job_factory_;
+ TestURLRequestContext context_;
+ TestDelegate delegate_;
+ scoped_ptr<URLRequest> request_;
+};
+
+} // namespace
+
+TEST_F(URLRequestSimpleJobTest, SimpleRequest) {
+ StartRequest(NULL);
+ ASSERT_TRUE(request_->status().is_success());
+ EXPECT_EQ(kTestData, delegate_.data_received());
+}
+
+TEST_F(URLRequestSimpleJobTest, RangeRequest) {
+ const std::string kExpectedBody = std::string(
+ kTestData + kRangeFirstPosition, kTestData + kRangeLastPosition + 1);
+ HttpRequestHeaders headers;
+ headers.SetHeader(
+ HttpRequestHeaders::kRange,
+ HttpByteRange::Bounded(kRangeFirstPosition, kRangeLastPosition)
+ .GetHeaderValue());
+
+ StartRequest(&headers);
+
+ ASSERT_TRUE(request_->status().is_success());
+ EXPECT_EQ(kExpectedBody, delegate_.data_received());
+}
+
+TEST_F(URLRequestSimpleJobTest, MultipleRangeRequest) {
+ HttpRequestHeaders headers;
+ int middle_pos = (kRangeFirstPosition + kRangeLastPosition)/2;
+ std::string range = base::StringPrintf("bytes=%d-%d,%d-%d",
+ kRangeFirstPosition,
+ middle_pos,
+ middle_pos + 1,
+ kRangeLastPosition);
+ headers.SetHeader(HttpRequestHeaders::kRange, range);
+
+ StartRequest(&headers);
+
+ EXPECT_TRUE(delegate_.request_failed());
+ EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, request_->status().error());
+}
+
+TEST_F(URLRequestSimpleJobTest, InvalidRangeRequest) {
+ HttpRequestHeaders headers;
+ std::string range = base::StringPrintf(
+ "bytes=%d-%d", kRangeLastPosition, kRangeFirstPosition);
+ headers.SetHeader(HttpRequestHeaders::kRange, range);
+
+ StartRequest(&headers);
+
+ ASSERT_TRUE(request_->status().is_success());
+ EXPECT_EQ(kTestData, delegate_.data_received());
+}
+
+} // namespace net
diff --git a/chromium/net/url_request/url_request_test_job.cc b/chromium/net/url_request/url_request_test_job.cc
index 31a07feaa88..422e08030d7 100644
--- a/chromium/net/url_request/url_request_test_job.cc
+++ b/chromium/net/url_request/url_request_test_job.cc
@@ -24,6 +24,15 @@ typedef std::list<URLRequestTestJob*> URLRequestJobList;
base::LazyInstance<URLRequestJobList>::Leaky
g_pending_jobs = LAZY_INSTANCE_INITIALIZER;
+class TestJobProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
+ public:
+ // URLRequestJobFactory::ProtocolHandler implementation:
+ virtual URLRequestJob* MaybeCreateJob(
+ URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
+ return new URLRequestTestJob(request, network_delegate);
+ }
+};
+
} // namespace
// static getters for known URLs
@@ -98,10 +107,9 @@ std::string URLRequestTestJob::test_error_headers() {
}
// static
-URLRequestJob* URLRequestTestJob::Factory(URLRequest* request,
- NetworkDelegate* network_delegate,
- const std::string& scheme) {
- return new URLRequestTestJob(request, network_delegate);
+URLRequestJobFactory::ProtocolHandler*
+URLRequestTestJob::CreateProtocolHandler() {
+ return new TestJobProtocolHandler();
}
URLRequestTestJob::URLRequestTestJob(URLRequest* request,
diff --git a/chromium/net/url_request/url_request_test_job.h b/chromium/net/url_request/url_request_test_job.h
index 717cc0f2d88..1a85fb0f38b 100644
--- a/chromium/net/url_request/url_request_test_job.h
+++ b/chromium/net/url_request/url_request_test_job.h
@@ -11,6 +11,7 @@
#include "net/base/load_timing_info.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
namespace net {
@@ -103,8 +104,8 @@ class NET_EXPORT_PRIVATE URLRequestTestJob : public URLRequestJob {
RequestPriority priority() const { return priority_; }
- // Factory method for protocol factory registration if callers don't subclass
- static URLRequest::ProtocolFactory Factory;
+ // Create a protocol handler for callers that don't subclass.
+ static URLRequestJobFactory::ProtocolHandler* CreateProtocolHandler();
// Job functions
virtual void SetPriority(RequestPriority priority) OVERRIDE;
diff --git a/chromium/net/url_request/url_request_test_util.cc b/chromium/net/url_request/url_request_test_util.cc
index ea3c7ae6074..e240082fd9d 100644
--- a/chromium/net/url_request/url_request_test_util.cc
+++ b/chromium/net/url_request/url_request_test_util.cc
@@ -13,6 +13,7 @@
#include "net/cert/cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_network_session.h"
+#include "net/http/http_response_headers.h"
#include "net/http/http_server_properties_impl.h"
#include "net/http/transport_security_state.h"
#include "net/ssl/default_server_bound_cert_store.h"
@@ -94,6 +95,8 @@ void TestURLRequestContext::Init() {
EXPECT_FALSE(client_socket_factory_);
} else {
HttpNetworkSession::Params params;
+ if (http_network_session_params_)
+ params = *http_network_session_params_;
params.client_socket_factory = client_socket_factory();
params.host_resolver = host_resolver();
params.cert_verifier = cert_verifier();
@@ -168,10 +171,12 @@ TestDelegate::TestDelegate()
cancel_in_rd_pending_(false),
quit_on_complete_(true),
quit_on_redirect_(false),
+ quit_on_before_network_start_(false),
allow_certificate_errors_(false),
response_started_count_(0),
received_bytes_count_(0),
received_redirect_count_(0),
+ received_before_network_start_count_(0),
received_data_before_response_(false),
request_failed_(false),
have_certificate_errors_(false),
@@ -206,6 +211,15 @@ void TestDelegate::OnReceivedRedirect(URLRequest* request,
}
}
+void TestDelegate::OnBeforeNetworkStart(URLRequest* request, bool* defer) {
+ received_before_network_start_count_++;
+ if (quit_on_before_network_start_) {
+ *defer = true;
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::MessageLoop::QuitClosure());
+ }
+}
+
void TestDelegate::OnAuthRequired(URLRequest* request,
AuthChallengeInfo* auth_info) {
auth_required_ = true;
@@ -399,7 +413,8 @@ int TestNetworkDelegate::OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
int req_id = request->identifier();
event_order_[req_id] += "OnHeadersReceived\n";
InitRequestStatesIfNew(req_id);
@@ -415,6 +430,20 @@ int TestNetworkDelegate::OnHeadersReceived(
// layer before the URLRequest reports that a response has started.
next_states_[req_id] |= kStageBeforeSendHeaders;
+ if (!redirect_on_headers_received_url_.is_empty()) {
+ *override_response_headers =
+ new net::HttpResponseHeaders(original_response_headers->raw_headers());
+ (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
+ (*override_response_headers)->RemoveHeader("Location");
+ (*override_response_headers)->AddHeader(
+ "Location: " + redirect_on_headers_received_url_.spec());
+
+ redirect_on_headers_received_url_ = GURL();
+
+ if (!allowed_unsafe_redirect_url_.is_empty())
+ *allowed_unsafe_redirect_url = allowed_unsafe_redirect_url_;
+ }
+
return OK;
}
@@ -574,11 +603,6 @@ int TestNetworkDelegate::OnBeforeSocketStreamConnect(
return OK;
}
-void TestNetworkDelegate::OnRequestWaitStateChange(
- const URLRequest& request,
- RequestWaitState state) {
-}
-
// static
std::string ScopedCustomUrlRequestTestHttpHost::value_("127.0.0.1");
diff --git a/chromium/net/url_request/url_request_test_util.h b/chromium/net/url_request/url_request_test_util.h
index 0369baa2ebc..ebd02c4aca5 100644
--- a/chromium/net/url_request/url_request_test_util.h
+++ b/chromium/net/url_request/url_request_test_util.h
@@ -13,6 +13,7 @@
#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_proxy.h"
#include "base/path_service.h"
#include "base/strings/string16.h"
@@ -31,6 +32,7 @@
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
+#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/proxy/proxy_service.h"
#include "net/ssl/ssl_config_service_defaults.h"
@@ -66,9 +68,18 @@ class TestURLRequestContext : public URLRequestContext {
client_socket_factory_ = factory;
}
+ void set_http_network_session_params(
+ const HttpNetworkSession::Params& params) {
+ }
+
private:
bool initialized_;
+ // Optional parameters to override default values. Note that values that
+ // point to other objects the TestURLRequestContext creates will be
+ // overwritten.
+ scoped_ptr<HttpNetworkSession::Params> http_network_session_params_;
+
// Not owned:
ClientSocketFactory* client_socket_factory_;
@@ -130,6 +141,9 @@ class TestDelegate : public URLRequest::Delegate {
}
void set_quit_on_complete(bool val) { quit_on_complete_ = val; }
void set_quit_on_redirect(bool val) { quit_on_redirect_ = val; }
+ void set_quit_on_network_start(bool val) {
+ quit_on_before_network_start_ = val;
+ }
void set_allow_certificate_errors(bool val) {
allow_certificate_errors_ = val;
}
@@ -142,6 +156,9 @@ class TestDelegate : public URLRequest::Delegate {
int bytes_received() const { return static_cast<int>(data_received_.size()); }
int response_started_count() const { return response_started_count_; }
int received_redirect_count() const { return received_redirect_count_; }
+ int received_before_network_start_count() const {
+ return received_before_network_start_count_;
+ }
bool received_data_before_response() const {
return received_data_before_response_;
}
@@ -160,6 +177,7 @@ class TestDelegate : public URLRequest::Delegate {
// URLRequest::Delegate:
virtual void OnReceivedRedirect(URLRequest* request, const GURL& new_url,
bool* defer_redirect) OVERRIDE;
+ virtual void OnBeforeNetworkStart(URLRequest* request, bool* defer) OVERRIDE;
virtual void OnAuthRequired(URLRequest* request,
AuthChallengeInfo* auth_info) OVERRIDE;
// NOTE: |fatal| causes |certificate_errors_are_fatal_| to be set to true.
@@ -184,6 +202,7 @@ class TestDelegate : public URLRequest::Delegate {
bool cancel_in_rd_pending_;
bool quit_on_complete_;
bool quit_on_redirect_;
+ bool quit_on_before_network_start_;
bool allow_certificate_errors_;
AuthCredentials credentials_;
@@ -191,6 +210,7 @@ class TestDelegate : public URLRequest::Delegate {
int response_started_count_;
int received_bytes_count_;
int received_redirect_count_;
+ int received_before_network_start_count_;
bool received_data_before_response_;
bool request_failed_;
bool have_certificate_errors_;
@@ -225,6 +245,17 @@ class TestNetworkDelegate : public NetworkDelegate {
bool GetLoadTimingInfoBeforeAuth(
LoadTimingInfo* load_timing_info_before_auth) const;
+ // Will redirect once to the given URL when the next set of headers are
+ // received.
+ void set_redirect_on_headers_received_url(
+ GURL redirect_on_headers_received_url) {
+ redirect_on_headers_received_url_ = redirect_on_headers_received_url;
+ }
+
+ void set_allowed_unsafe_redirect_url(GURL allowed_unsafe_redirect_url) {
+ allowed_unsafe_redirect_url_ = allowed_unsafe_redirect_url;
+ }
+
void set_cookie_options(int o) {cookie_options_bit_mask_ = o; }
int last_error() const { return last_error_; }
@@ -257,7 +288,8 @@ class TestNetworkDelegate : public NetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE;
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
virtual void OnBeforeRedirect(URLRequest* request,
const GURL& new_location) OVERRIDE;
virtual void OnResponseStarted(URLRequest* request) OVERRIDE;
@@ -284,11 +316,13 @@ class TestNetworkDelegate : public NetworkDelegate {
virtual int OnBeforeSocketStreamConnect(
SocketStream* stream,
const CompletionCallback& callback) OVERRIDE;
- virtual void OnRequestWaitStateChange(const URLRequest& request,
- RequestWaitState state) OVERRIDE;
void InitRequestStatesIfNew(int request_id);
+ GURL redirect_on_headers_received_url_;
+ // URL marked as safe for redirection at the onHeadersReceived stage.
+ GURL allowed_unsafe_redirect_url_;
+
int last_error_;
int error_count_;
int created_requests_;
diff --git a/chromium/net/url_request/url_request_throttler_simulation_unittest.cc b/chromium/net/url_request/url_request_throttler_simulation_unittest.cc
index c68f59a4139..fc9dfde0711 100644
--- a/chromium/net/url_request/url_request_throttler_simulation_unittest.cc
+++ b/chromium/net/url_request/url_request_throttler_simulation_unittest.cc
@@ -332,7 +332,7 @@ class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry {
// Registry of results for a class of |Requester| objects (e.g. attackers vs.
// regular clients).
class RequesterResults {
-public:
+ public:
RequesterResults()
: num_attempts_(0), num_successful_(0), num_failed_(0), num_blocked_(0) {
}
diff --git a/chromium/net/url_request/url_request_throttler_unittest.cc b/chromium/net/url_request/url_request_throttler_unittest.cc
index b964b108551..32f94ff0fd5 100644
--- a/chromium/net/url_request/url_request_throttler_unittest.cc
+++ b/chromium/net/url_request/url_request_throttler_unittest.cc
@@ -5,13 +5,12 @@
#include "net/url_request/url_request_throttler_manager.h"
#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
#include "base/metrics/histogram_samples.h"
-#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "base/test/statistics_delta_reader.h"
#include "base/time/time.h"
#include "net/base/load_flags.h"
#include "net/base/request_priority.h"
@@ -29,10 +28,7 @@ namespace net {
namespace {
-using base::Histogram;
-using base::HistogramBase;
-using base::HistogramSamples;
-using base::StatisticsRecorder;
+const char kRequestThrottledHistogramName[] = "Throttling.RequestThrottled";
class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry {
public:
@@ -177,75 +173,21 @@ class URLRequestThrottlerEntryTest : public testing::Test {
: request_(GURL(), DEFAULT_PRIORITY, NULL, &context_) {}
virtual void SetUp();
- virtual void TearDown();
-
- // After calling this function, histogram snapshots in |samples_| contain
- // only the delta caused by the test case currently running.
- void CalculateHistogramDeltas();
TimeTicks now_;
MockURLRequestThrottlerManager manager_; // Dummy object, not used.
scoped_refptr<MockURLRequestThrottlerEntry> entry_;
- std::map<std::string, HistogramSamples*> original_samples_;
- std::map<std::string, HistogramSamples*> samples_;
-
TestURLRequestContext context_;
TestURLRequest request_;
};
-// List of all histograms we care about in these unit tests.
-const char* kHistogramNames[] = {
- "Throttling.FailureCountAtSuccess",
- "Throttling.PerceivedDowntime",
- "Throttling.RequestThrottled",
- "Throttling.SiteOptedOut",
-};
-
void URLRequestThrottlerEntryTest::SetUp() {
request_.SetLoadFlags(0);
now_ = TimeTicks::Now();
entry_ = new MockURLRequestThrottlerEntry(&manager_);
entry_->ResetToBlank(now_);
-
- for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
- // Must retrieve original samples for each histogram for comparison
- // as other tests may affect them.
- const char* name = kHistogramNames[i];
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (histogram) {
- original_samples_[name] = histogram->SnapshotSamples().release();
- } else {
- original_samples_[name] = NULL;
- }
- }
-}
-
-void URLRequestThrottlerEntryTest::TearDown() {
- STLDeleteValues(&original_samples_);
- STLDeleteValues(&samples_);
-}
-
-void URLRequestThrottlerEntryTest::CalculateHistogramDeltas() {
- for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
- const char* name = kHistogramNames[i];
- HistogramSamples* original = original_samples_[name];
-
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (histogram) {
- ASSERT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags());
-
- scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
- if (original)
- samples->Subtract(*original);
- samples_[name] = samples.release();
- }
- }
-
- // Ensure we don't accidentally use the originals in our tests.
- STLDeleteValues(&original_samples_);
- original_samples_.clear();
}
std::ostream& operator<<(std::ostream& out, const base::TimeTicks& time) {
@@ -265,6 +207,7 @@ TEST_F(URLRequestThrottlerEntryTest, CanThrottleRequest) {
}
TEST_F(URLRequestThrottlerEntryTest, InterfaceDuringExponentialBackoff) {
+ base::StatisticsDeltaReader statistics_delta_reader;
entry_->set_exponential_backoff_release_time(
entry_->fake_time_now_ + TimeDelta::FromMilliseconds(1));
EXPECT_TRUE(entry_->ShouldRejectRequest(request_));
@@ -273,21 +216,26 @@ TEST_F(URLRequestThrottlerEntryTest, InterfaceDuringExponentialBackoff) {
request_.SetLoadFlags(LOAD_MAYBE_USER_GESTURE);
EXPECT_FALSE(entry_->ShouldRejectRequest(request_));
- CalculateHistogramDeltas();
- ASSERT_EQ(1, samples_["Throttling.RequestThrottled"]->GetCount(0));
- ASSERT_EQ(1, samples_["Throttling.RequestThrottled"]->GetCount(1));
+ scoped_ptr<base::HistogramSamples> samples(
+ statistics_delta_reader.GetHistogramSamplesSinceCreation(
+ kRequestThrottledHistogramName));
+ ASSERT_EQ(1, samples->GetCount(0));
+ ASSERT_EQ(1, samples->GetCount(1));
}
TEST_F(URLRequestThrottlerEntryTest, InterfaceNotDuringExponentialBackoff) {
+ base::StatisticsDeltaReader statistics_delta_reader;
entry_->set_exponential_backoff_release_time(entry_->fake_time_now_);
EXPECT_FALSE(entry_->ShouldRejectRequest(request_));
entry_->set_exponential_backoff_release_time(
entry_->fake_time_now_ - TimeDelta::FromMilliseconds(1));
EXPECT_FALSE(entry_->ShouldRejectRequest(request_));
- CalculateHistogramDeltas();
- ASSERT_EQ(2, samples_["Throttling.RequestThrottled"]->GetCount(0));
- ASSERT_EQ(0, samples_["Throttling.RequestThrottled"]->GetCount(1));
+ scoped_ptr<base::HistogramSamples> samples(
+ statistics_delta_reader.GetHistogramSamplesSinceCreation(
+ kRequestThrottledHistogramName));
+ ASSERT_EQ(2, samples->GetCount(0));
+ ASSERT_EQ(0, samples->GetCount(1));
}
TEST_F(URLRequestThrottlerEntryTest, InterfaceUpdateFailure) {
diff --git a/chromium/net/url_request/url_request_unittest.cc b/chromium/net/url_request/url_request_unittest.cc
index fbc90d237f1..42a083505ef 100644
--- a/chromium/net/url_request/url_request_unittest.cc
+++ b/chromium/net/url_request/url_request_unittest.cc
@@ -57,6 +57,7 @@
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
#include "net/ocsp/nss_ocsp.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/ssl_client_socket.h"
@@ -64,11 +65,8 @@
#include "net/test/cert_test_util.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/url_request/data_protocol_handler.h"
-#include "net/url_request/file_protocol_handler.h"
-#include "net/url_request/ftp_protocol_handler.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_file_dir_job.h"
#include "net/url_request/url_request_http_job.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_redirect_job.h"
@@ -77,12 +75,23 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+#if !defined(DISABLE_FILE_SUPPORT)
+#include "net/base/filename_util.h"
+#include "net/url_request/file_protocol_handler.h"
+#include "net/url_request/url_request_file_dir_job.h"
+#endif
+
+#if !defined(DISABLE_FTP_SUPPORT)
+#include "net/url_request/ftp_protocol_handler.h"
+#endif
+
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#endif
+using base::ASCIIToUTF16;
using base::Time;
namespace net {
@@ -239,14 +248,6 @@ UploadDataStream* CreateSimpleUploadData(const char* data) {
// Verify that the SSLInfo of a successful SSL connection has valid values.
void CheckSSLInfo(const SSLInfo& ssl_info) {
- // Allow ChromeFrame fake SSLInfo to get through.
- if (ssl_info.cert.get() &&
- ssl_info.cert.get()->issuer().GetDisplayName() == "Chrome Internal") {
- // -1 means unknown.
- EXPECT_EQ(ssl_info.security_bits, -1);
- return;
- }
-
// -1 means unknown. 0 means no encryption.
EXPECT_GT(ssl_info.security_bits, 0);
@@ -374,7 +375,8 @@ class BlockingNetworkDelegate : public TestNetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE;
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
virtual NetworkDelegate::AuthRequiredResponse OnAuthRequired(
URLRequest* request,
@@ -397,7 +399,7 @@ class BlockingNetworkDelegate : public TestNetworkDelegate {
int retval_; // To be returned in non-auth stages.
AuthRequiredResponse auth_retval_;
- GURL redirect_url_; // Used if non-empty.
+ GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest.
int block_on_; // Bit mask: in which stages to block.
// |auth_credentials_| will be copied to |*target_auth_credential_| on
@@ -489,10 +491,13 @@ int BlockingNetworkDelegate::OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) {
- TestNetworkDelegate::OnHeadersReceived(
- request, callback, original_response_headers,
- override_response_headers);
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
+ TestNetworkDelegate::OnHeadersReceived(request,
+ callback,
+ original_response_headers,
+ override_response_headers,
+ allowed_unsafe_redirect_url);
return MaybeBlockStage(ON_HEADERS_RECEIVED, callback);
}
@@ -599,8 +604,10 @@ class URLRequestTest : public PlatformTest {
default_context_.set_network_delegate(&default_network_delegate_);
default_context_.set_net_log(&net_log_);
job_factory_.SetProtocolHandler("data", new DataProtocolHandler);
+#if !defined(DISABLE_FILE_SUPPORT)
job_factory_.SetProtocolHandler(
"file", new FileProtocolHandler(base::MessageLoopProxy::current()));
+#endif
default_context_.set_job_factory(&job_factory_);
default_context_.Init();
}
@@ -690,6 +697,7 @@ TEST_F(URLRequestTest, DataURLImageTest) {
}
}
+#if !defined(DISABLE_FILE_SUPPORT)
TEST_F(URLRequestTest, FileTest) {
base::FilePath app_path;
PathService::Get(base::FILE_EXE, &app_path);
@@ -745,7 +753,7 @@ TEST_F(URLRequestTest, FileTestFullSpecifiedRange) {
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
- EXPECT_TRUE(file_util::WriteFile(temp_path, buffer.get(), buffer_size));
+ EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size));
int64 file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
@@ -789,7 +797,7 @@ TEST_F(URLRequestTest, FileTestHalfSpecifiedRange) {
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
- EXPECT_TRUE(file_util::WriteFile(temp_path, buffer.get(), buffer_size));
+ EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size));
int64 file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
@@ -832,7 +840,7 @@ TEST_F(URLRequestTest, FileTestMultipleRanges) {
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
- EXPECT_TRUE(file_util::WriteFile(temp_path, buffer.get(), buffer_size));
+ EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size));
int64 file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
@@ -860,7 +868,7 @@ TEST_F(URLRequestTest, AllowFileURLs) {
base::FilePath test_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &test_file));
std::string test_data("monkey");
- file_util::WriteFile(test_file, test_data.data(), test_data.size());
+ base::WriteFile(test_file, test_data.data(), test_data.size());
GURL test_file_url = net::FilePathToFileURL(test_file);
{
@@ -888,80 +896,6 @@ TEST_F(URLRequestTest, AllowFileURLs) {
}
}
-TEST_F(URLRequestTest, InvalidUrlTest) {
- TestDelegate d;
- {
- URLRequest r(GURL("invalid url"), DEFAULT_PRIORITY, &d, &default_context_);
-
- r.Start();
- EXPECT_TRUE(r.is_pending());
-
- base::RunLoop().Run();
- EXPECT_TRUE(d.request_failed());
- }
-}
-
-#if defined(OS_WIN)
-TEST_F(URLRequestTest, ResolveShortcutTest) {
- base::FilePath app_path;
- PathService::Get(base::DIR_SOURCE_ROOT, &app_path);
- app_path = app_path.AppendASCII("net");
- app_path = app_path.AppendASCII("data");
- app_path = app_path.AppendASCII("url_request_unittest");
- app_path = app_path.AppendASCII("with-headers.html");
-
- std::wstring lnk_path = app_path.value() + L".lnk";
-
- base::win::ScopedCOMInitializer com_initializer;
-
- // Temporarily create a shortcut for test
- {
- base::win::ScopedComPtr<IShellLink> shell;
- ASSERT_TRUE(SUCCEEDED(shell.CreateInstance(CLSID_ShellLink, NULL,
- CLSCTX_INPROC_SERVER)));
- base::win::ScopedComPtr<IPersistFile> persist;
- ASSERT_TRUE(SUCCEEDED(shell.QueryInterface(persist.Receive())));
- EXPECT_TRUE(SUCCEEDED(shell->SetPath(app_path.value().c_str())));
- EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest")));
- EXPECT_TRUE(SUCCEEDED(persist->Save(lnk_path.c_str(), TRUE)));
- }
-
- TestDelegate d;
- {
- URLRequest r(FilePathToFileURL(base::FilePath(lnk_path)),
- DEFAULT_PRIORITY,
- &d,
- &default_context_);
-
- r.Start();
- EXPECT_TRUE(r.is_pending());
-
- base::RunLoop().Run();
-
- WIN32_FILE_ATTRIBUTE_DATA data;
- GetFileAttributesEx(app_path.value().c_str(),
- GetFileExInfoStandard, &data);
- HANDLE file = CreateFile(app_path.value().c_str(), GENERIC_READ,
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- EXPECT_NE(INVALID_HANDLE_VALUE, file);
- scoped_ptr<char[]> buffer(new char[data.nFileSizeLow]);
- DWORD read_size;
- BOOL result;
- result = ReadFile(file, buffer.get(), data.nFileSizeLow,
- &read_size, NULL);
- std::string content(buffer.get(), read_size);
- CloseHandle(file);
-
- EXPECT_TRUE(!r.is_pending());
- EXPECT_EQ(1, d.received_redirect_count());
- EXPECT_EQ(content, d.data_received());
- }
-
- // Clean the shortcut
- DeleteFile(lnk_path.c_str());
-}
-#endif // defined(OS_WIN)
TEST_F(URLRequestTest, FileDirCancelTest) {
// Put in mock resource provider.
@@ -1008,7 +942,7 @@ TEST_F(URLRequestTest, FileDirOutputSanity) {
// Generate entry for the sentinel file.
base::FilePath sentinel_path = path.AppendASCII(sentinel_name);
- base::PlatformFileInfo info;
+ base::File::Info info;
EXPECT_TRUE(base::GetFileInfo(sentinel_path, &info));
EXPECT_GT(info.size, 0);
std::string sentinel_output = GetDirectoryListingEntry(
@@ -1060,7 +994,84 @@ TEST_F(URLRequestTest, FileDirRedirectSingleSlash) {
ASSERT_EQ(1, d.received_redirect_count());
ASSERT_FALSE(req.status().is_success());
}
-#endif
+#endif // defined(OS_WIN)
+
+#endif // !defined(DISABLE_FILE_SUPPORT)
+
+TEST_F(URLRequestTest, InvalidUrlTest) {
+ TestDelegate d;
+ {
+ URLRequest r(GURL("invalid url"), DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ base::RunLoop().Run();
+ EXPECT_TRUE(d.request_failed());
+ }
+}
+
+#if defined(OS_WIN)
+TEST_F(URLRequestTest, ResolveShortcutTest) {
+ base::FilePath app_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &app_path);
+ app_path = app_path.AppendASCII("net");
+ app_path = app_path.AppendASCII("data");
+ app_path = app_path.AppendASCII("url_request_unittest");
+ app_path = app_path.AppendASCII("with-headers.html");
+
+ std::wstring lnk_path = app_path.value() + L".lnk";
+
+ base::win::ScopedCOMInitializer com_initializer;
+
+ // Temporarily create a shortcut for test
+ {
+ base::win::ScopedComPtr<IShellLink> shell;
+ ASSERT_TRUE(SUCCEEDED(shell.CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER)));
+ base::win::ScopedComPtr<IPersistFile> persist;
+ ASSERT_TRUE(SUCCEEDED(shell.QueryInterface(persist.Receive())));
+ EXPECT_TRUE(SUCCEEDED(shell->SetPath(app_path.value().c_str())));
+ EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest")));
+ EXPECT_TRUE(SUCCEEDED(persist->Save(lnk_path.c_str(), TRUE)));
+ }
+
+ TestDelegate d;
+ {
+ URLRequest r(FilePathToFileURL(base::FilePath(lnk_path)),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ base::RunLoop().Run();
+
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ GetFileAttributesEx(app_path.value().c_str(),
+ GetFileExInfoStandard, &data);
+ HANDLE file = CreateFile(app_path.value().c_str(), GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ EXPECT_NE(INVALID_HANDLE_VALUE, file);
+ scoped_ptr<char[]> buffer(new char[data.nFileSizeLow]);
+ DWORD read_size;
+ BOOL result;
+ result = ReadFile(file, buffer.get(), data.nFileSizeLow,
+ &read_size, NULL);
+ std::string content(buffer.get(), read_size);
+ CloseHandle(file);
+
+ EXPECT_TRUE(!r.is_pending());
+ EXPECT_EQ(1, d.received_redirect_count());
+ EXPECT_EQ(content, d.data_received());
+ }
+
+ // Clean the shortcut
+ DeleteFile(lnk_path.c_str());
+}
+#endif // defined(OS_WIN)
// Custom URLRequestJobs for use with interceptor tests
class RestartTestJob : public URLRequestTestJob {
@@ -1700,8 +1711,8 @@ TEST_F(URLRequestTest, InterceptLoadTimingProxy) {
// Make sure that URLRequest correctly adjusts proxy times when they're before
// |request_start|, due to already having a connected socket. This happens in
-// the case of reusing a SPDY session or HTTP pipeline. The connected socket is
-// not considered reused in this test (May be a preconnect).
+// the case of reusing a SPDY session. The connected socket is not considered
+// reused in this test (May be a preconnect).
//
// To mix things up from the test above, assumes DNS times but no SSL times.
TEST_F(URLRequestTest, InterceptLoadTimingEarlyProxyResolution) {
@@ -1852,6 +1863,8 @@ TEST_F(URLRequestTest, NetworkDelegateProxyError) {
// Check we see a failed request.
EXPECT_FALSE(req.status().is_success());
+ // The proxy server is not set before failure.
+ EXPECT_TRUE(req.proxy_server().IsEmpty());
EXPECT_EQ(URLRequestStatus::FAILED, req.status().status());
EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, req.status().error());
@@ -1964,6 +1977,7 @@ TEST_F(URLRequestTest, PriorityIgnoreLimits) {
// A subclass of SpawnedTestServer that uses a statically-configured hostname.
// This is to work around mysterious failures in chrome_frame_net_tests. See:
// http://crbug.com/114369
+// TODO(erikwright): remove or update as needed; see http://crbug.com/334634.
class LocalHttpTestServer : public SpawnedTestServer {
public:
explicit LocalHttpTestServer(const base::FilePath& document_root)
@@ -2422,8 +2436,8 @@ class FixedDateNetworkDelegate : public TestNetworkDelegate {
net::URLRequest* request,
const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
- scoped_refptr<net::HttpResponseHeaders>* override_response_headers)
- OVERRIDE;
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
private:
std::string fixed_date_;
@@ -2435,7 +2449,8 @@ int FixedDateNetworkDelegate::OnHeadersReceived(
net::URLRequest* request,
const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
- scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
net::HttpResponseHeaders* new_response_headers =
new net::HttpResponseHeaders(original_response_headers->raw_headers());
@@ -2446,7 +2461,8 @@ int FixedDateNetworkDelegate::OnHeadersReceived(
return TestNetworkDelegate::OnHeadersReceived(request,
callback,
original_response_headers,
- override_response_headers);
+ override_response_headers,
+ allowed_unsafe_redirect_url);
}
// Test that cookie expiration times are adjusted for server/client clock
@@ -2689,10 +2705,7 @@ class URLRequestTestHTTP : public URLRequestTest {
bool is_success = r.status().is_success();
if (!is_success) {
- // Requests handled by ChromeFrame send a less precise error message,
- // ERR_CONNECTION_ABORTED.
- EXPECT_TRUE(r.status().error() == ERR_RESPONSE_HEADERS_TOO_BIG ||
- r.status().error() == ERR_CONNECTION_ABORTED);
+ EXPECT_TRUE(r.status().error() == ERR_RESPONSE_HEADERS_TOO_BIG);
// The test server appears to be unable to handle subsequent requests
// after this error is triggered. Force it to restart.
EXPECT_TRUE(test_server_.Stop());
@@ -2726,6 +2739,8 @@ TEST_F(URLRequestTestHTTP, ProxyTunnelRedirectTest) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ // The proxy server is not set before failure.
+ EXPECT_TRUE(r.proxy_server().IsEmpty());
EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r.status().error());
EXPECT_EQ(1, d.response_started_count());
// We should not have followed the redirect.
@@ -2752,6 +2767,8 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateTunnelConnectionFailed) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ // The proxy server is not set before failure.
+ EXPECT_TRUE(r.proxy_server().IsEmpty());
EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r.status().error());
EXPECT_EQ(1, d.response_started_count());
// We should not have followed the redirect.
@@ -2826,6 +2843,8 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequest) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ // The proxy server is not set before cancellation.
+ EXPECT_TRUE(r.proxy_server().IsEmpty());
EXPECT_EQ(ERR_EMPTY_RESPONSE, r.status().error());
EXPECT_EQ(1, network_delegate.created_requests());
EXPECT_EQ(0, network_delegate.destroyed_requests());
@@ -2856,6 +2875,8 @@ void NetworkDelegateCancelRequest(BlockingNetworkDelegate::BlockMode block_mode,
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ // The proxy server is not set before cancellation.
+ EXPECT_TRUE(r.proxy_server().IsEmpty());
EXPECT_EQ(ERR_BLOCKED_BY_CLIENT, r.status().error());
EXPECT_EQ(1, network_delegate.created_requests());
EXPECT_EQ(0, network_delegate.destroyed_requests());
@@ -2932,6 +2953,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequest) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_TRUE(r.proxy_server().Equals(test_server_.host_port_pair()));
EXPECT_EQ(0, r.status().error());
EXPECT_EQ(redirect_url, r.url());
EXPECT_EQ(original_url, r.original_url());
@@ -2964,6 +2986,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestSynchronously) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_TRUE(r.proxy_server().Equals(test_server_.host_port_pair()));
EXPECT_EQ(0, r.status().error());
EXPECT_EQ(redirect_url, r.url());
EXPECT_EQ(original_url, r.original_url());
@@ -3016,6 +3039,40 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestPost) {
EXPECT_EQ(1, network_delegate.destroyed_requests());
}
+// Tests that the network delegate can block and redirect a request to a new
+// URL during OnHeadersReceived.
+TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ BlockingNetworkDelegate network_delegate(
+ BlockingNetworkDelegate::AUTO_CALLBACK);
+ network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED);
+ GURL redirect_url(test_server_.GetURL("simple.html"));
+ network_delegate.set_redirect_on_headers_received_url(redirect_url);
+
+ TestURLRequestContextWithProxy context(
+ test_server_.host_port_pair().ToString(), &network_delegate);
+
+ {
+ GURL original_url(test_server_.GetURL("empty.html"));
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &context);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_TRUE(r.proxy_server().Equals(test_server_.host_port_pair()));
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(redirect_url, r.url());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(2, network_delegate.created_requests());
+ EXPECT_EQ(0, network_delegate.destroyed_requests());
+ }
+ EXPECT_EQ(1, network_delegate.destroyed_requests());
+}
+
// Tests that the network delegate can synchronously complete OnAuthRequired
// by taking no action. This indicates that the NetworkDelegate does not want to
// handle the challenge, and is passing the buck along to the
@@ -3462,6 +3519,8 @@ TEST_F(URLRequestTestHTTP, UnexpectedServerAuthTest) {
base::RunLoop().Run();
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ // The proxy server is not set before failure.
+ EXPECT_TRUE(r.proxy_server().IsEmpty());
EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r.status().error());
}
}
@@ -3826,6 +3885,22 @@ class AsyncDelegateLogger : public base::RefCounted<AsyncDelegateLogger> {
return log_position + 1;
}
+ // Find delegate request begin and end messages for OnBeforeNetworkStart.
+ // Returns the position of the end message.
+ static size_t ExpectBeforeNetworkEvents(
+ const CapturingNetLog::CapturedEntryList& entries,
+ size_t log_position) {
+ log_position =
+ ExpectLogContainsSomewhereAfter(entries,
+ log_position,
+ NetLog::TYPE_URL_REQUEST_DELEGATE,
+ NetLog::PHASE_BEGIN);
+ EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE,
+ entries[log_position + 1].type);
+ EXPECT_EQ(NetLog::PHASE_END, entries[log_position + 1].phase);
+ return log_position + 1;
+ }
+
private:
friend class base::RefCounted<AsyncDelegateLogger>;
@@ -3872,7 +3947,7 @@ class AsyncDelegateLogger : public base::RefCounted<AsyncDelegateLogger> {
LoadStateWithParam load_state = url_request_->GetLoadState();
EXPECT_EQ(expected_third_load_state_, load_state.state);
if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE)
- EXPECT_EQ(string16(), load_state.param);
+ EXPECT_EQ(base::string16(), load_state.param);
callback_.Run();
}
@@ -3912,10 +3987,13 @@ class AsyncLoggingNetworkDelegate : public TestNetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE {
- TestNetworkDelegate::OnHeadersReceived(request, callback,
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE {
+ TestNetworkDelegate::OnHeadersReceived(request,
+ callback,
original_response_headers,
- override_response_headers);
+ override_response_headers,
+ allowed_unsafe_redirect_url);
return RunCallbackAsynchronously(request, callback);
}
@@ -4064,7 +4142,7 @@ TEST_F(URLRequestTestHTTP, DelegateInfoBeforeStart) {
&context);
LoadStateWithParam load_state = r.GetLoadState();
EXPECT_EQ(LOAD_STATE_IDLE, load_state.state);
- EXPECT_EQ(string16(), load_state.param);
+ EXPECT_EQ(base::string16(), load_state.param);
AsyncDelegateLogger::Run(
&r,
@@ -4112,7 +4190,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfo) {
&context);
LoadStateWithParam load_state = r.GetLoadState();
EXPECT_EQ(LOAD_STATE_IDLE, load_state.state);
- EXPECT_EQ(string16(), load_state.param);
+ EXPECT_EQ(base::string16(), load_state.param);
r.Start();
base::RunLoop().Run();
@@ -4140,6 +4218,11 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfo) {
ASSERT_LT(log_position, entries.size());
EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type);
EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase);
+
+ if (i == 1) {
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position + 1);
+ }
}
EXPECT_FALSE(LogContainsEntryWithTypeAfter(
@@ -4165,7 +4248,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfoRedirect) {
&context);
LoadStateWithParam load_state = r.GetLoadState();
EXPECT_EQ(LOAD_STATE_IDLE, load_state.state);
- EXPECT_EQ(string16(), load_state.param);
+ EXPECT_EQ(base::string16(), load_state.param);
r.Start();
base::RunLoop().Run();
@@ -4195,6 +4278,11 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfoRedirect) {
ASSERT_LT(log_position, entries.size());
EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type);
EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase);
+
+ if (i == 1) {
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position + 1);
+ }
}
// The URLRequest::Delegate then gets informed about the redirect.
@@ -4243,7 +4331,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfoAuth) {
&context);
LoadStateWithParam load_state = r.GetLoadState();
EXPECT_EQ(LOAD_STATE_IDLE, load_state.state);
- EXPECT_EQ(string16(), load_state.param);
+ EXPECT_EQ(base::string16(), load_state.param);
r.Start();
base::RunLoop().Run();
@@ -4274,6 +4362,11 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateInfoAuth) {
ASSERT_LT(log_position, entries.size());
EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type);
EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase);
+
+ if (i == 1) {
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position + 1);
+ }
}
EXPECT_FALSE(LogContainsEntryWithTypeAfter(
@@ -4295,7 +4388,7 @@ TEST_F(URLRequestTestHTTP, URLRequestDelegateInfo) {
// A chunked response with delays between chunks is used to make sure that
// attempts by the URLRequest delegate to log information while reading the
// body are ignored. Since they are ignored, this test is robust against
- // the possability of multiple reads being combined in the unlikely event
+ // the possibility of multiple reads being combined in the unlikely event
// that it occurs.
URLRequest r(test_server_.GetURL("chunked?waitBetweenChunks=20"),
DEFAULT_PRIORITY,
@@ -4312,14 +4405,18 @@ TEST_F(URLRequestTestHTTP, URLRequestDelegateInfo) {
CapturingNetLog::CapturedEntryList entries;
net_log_.GetEntries(&entries);
+ size_t log_position = 0;
+
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position);
+
// The delegate info should only have been logged on header complete. Other
// times it should silently be ignored.
-
- size_t log_position = ExpectLogContainsSomewhereAfter(
- entries,
- 0,
- NetLog::TYPE_URL_REQUEST_DELEGATE,
- NetLog::PHASE_BEGIN);
+ log_position =
+ ExpectLogContainsSomewhereAfter(entries,
+ log_position + 1,
+ NetLog::TYPE_URL_REQUEST_DELEGATE,
+ NetLog::PHASE_BEGIN);
log_position = AsyncDelegateLogger::CheckDelegateInfo(entries,
log_position + 1);
@@ -4366,6 +4463,11 @@ TEST_F(URLRequestTestHTTP, URLRequestDelegateInfoOnRedirect) {
// OnResponseStarted.
size_t log_position = 0;
for (int i = 0; i < 2; ++i) {
+ if (i == 0) {
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position) + 1;
+ }
+
log_position = ExpectLogContainsSomewhereAfter(
entries,
log_position,
@@ -4426,6 +4528,11 @@ TEST_F(URLRequestTestHTTP, URLRequestDelegateOnRedirectCancelled) {
// still currently supported in that call.
size_t log_position = 0;
for (int i = 0; i < 2; ++i) {
+ if (i == 0) {
+ log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents(
+ entries, log_position) + 1;
+ }
+
log_position = ExpectLogContainsSomewhereAfter(
entries,
log_position,
@@ -4699,7 +4806,7 @@ TEST_F(URLRequestTestHTTP, PostFileTest) {
base::FilePath dir;
PathService::Get(base::DIR_EXE, &dir);
- file_util::SetCurrentDirectory(dir);
+ base::SetCurrentDirectory(dir);
ScopedVector<UploadElementReader> element_readers;
@@ -4766,21 +4873,11 @@ TEST_F(URLRequestTestHTTP, PostUnreadableFileTest) {
base::RunLoop().Run();
- // TODO(tzik): Remove this #if after we stop supporting Chrome Frame.
- // http://crbug.com/317432
-#if defined(CHROME_FRAME_NET_TESTS)
- EXPECT_FALSE(d.request_failed());
- EXPECT_FALSE(d.received_data_before_response());
- EXPECT_EQ(0, d.bytes_received());
- EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
- EXPECT_EQ(OK, r.status().error());
-#else
EXPECT_TRUE(d.request_failed());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(0, d.bytes_received());
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
EXPECT_EQ(ERR_FILE_NOT_FOUND, r.status().error());
-#endif // defined(CHROME_FRAME_NET_TESTS)
}
}
@@ -4890,14 +4987,13 @@ TEST_F(URLRequestTestHTTP, ProcessSTS) {
TransportSecurityState* security_state =
default_context_.transport_security_state();
- bool sni_available = true;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(security_state->GetDomainState(
- SpawnedTestServer::kLocalhost, sni_available, &domain_state));
+ EXPECT_TRUE(security_state->GetDynamicDomainState(
+ SpawnedTestServer::kLocalhost, &domain_state));
EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS,
- domain_state.upgrade_mode);
- EXPECT_TRUE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ domain_state.sts.upgrade_mode);
+ EXPECT_TRUE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
#if defined(OS_ANDROID)
// Android's CertVerifyProc does not (yet) handle pins.
#else
@@ -4934,17 +5030,15 @@ TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKP) {
TransportSecurityState* security_state =
default_context_.transport_security_state();
- bool sni_available = true;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(security_state->GetDomainState(
- SpawnedTestServer::kLocalhost, sni_available, &domain_state));
+ EXPECT_TRUE(security_state->GetDynamicDomainState(
+ SpawnedTestServer::kLocalhost, &domain_state));
EXPECT_EQ(TransportSecurityState::DomainState::MODE_DEFAULT,
- domain_state.upgrade_mode);
- EXPECT_FALSE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ domain_state.sts.upgrade_mode);
+ EXPECT_FALSE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
EXPECT_TRUE(domain_state.HasPublicKeyPins());
- EXPECT_NE(domain_state.upgrade_expiry,
- domain_state.dynamic_spki_hashes_expiry);
+ EXPECT_NE(domain_state.sts.expiry, domain_state.pkp.expiry);
}
TEST_F(URLRequestTestHTTP, ProcessSTSOnce) {
@@ -4967,14 +5061,13 @@ TEST_F(URLRequestTestHTTP, ProcessSTSOnce) {
// We should have set parameters from the first header, not the second.
TransportSecurityState* security_state =
default_context_.transport_security_state();
- bool sni_available = true;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(security_state->GetDomainState(
- SpawnedTestServer::kLocalhost, sni_available, &domain_state));
+ EXPECT_TRUE(security_state->GetDynamicDomainState(
+ SpawnedTestServer::kLocalhost, &domain_state));
EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS,
- domain_state.upgrade_mode);
- EXPECT_FALSE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ domain_state.sts.upgrade_mode);
+ EXPECT_FALSE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
}
TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP) {
@@ -4997,25 +5090,23 @@ TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP) {
// We should have set parameters from the first header, not the second.
TransportSecurityState* security_state =
default_context_.transport_security_state();
- bool sni_available = true;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(security_state->GetDomainState(
- SpawnedTestServer::kLocalhost, sni_available, &domain_state));
+ EXPECT_TRUE(security_state->GetDynamicDomainState(
+ SpawnedTestServer::kLocalhost, &domain_state));
EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS,
- domain_state.upgrade_mode);
+ domain_state.sts.upgrade_mode);
#if defined(OS_ANDROID)
// Android's CertVerifyProc does not (yet) handle pins.
#else
EXPECT_TRUE(domain_state.HasPublicKeyPins());
#endif
- EXPECT_NE(domain_state.upgrade_expiry,
- domain_state.dynamic_spki_hashes_expiry);
+ EXPECT_NE(domain_state.sts.expiry, domain_state.pkp.expiry);
// Even though there is an HSTS header asserting includeSubdomains, it is
// the *second* such header, and we MUST process only the first.
- EXPECT_FALSE(domain_state.sts_include_subdomains);
+ EXPECT_FALSE(domain_state.sts.include_subdomains);
// includeSubdomains does not occur in the test HPKP header.
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
}
// Tests that when multiple HPKP headers are present, asserting different
@@ -5039,22 +5130,20 @@ TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP2) {
TransportSecurityState* security_state =
default_context_.transport_security_state();
- bool sni_available = true;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(security_state->GetDomainState(
- SpawnedTestServer::kLocalhost, sni_available, &domain_state));
+ EXPECT_TRUE(security_state->GetDynamicDomainState(
+ SpawnedTestServer::kLocalhost, &domain_state));
EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS,
- domain_state.upgrade_mode);
+ domain_state.sts.upgrade_mode);
#if defined(OS_ANDROID)
// Android's CertVerifyProc does not (yet) handle pins.
#else
EXPECT_TRUE(domain_state.HasPublicKeyPins());
#endif
- EXPECT_NE(domain_state.upgrade_expiry,
- domain_state.dynamic_spki_hashes_expiry);
+ EXPECT_NE(domain_state.sts.expiry, domain_state.pkp.expiry);
- EXPECT_TRUE(domain_state.sts_include_subdomains);
- EXPECT_FALSE(domain_state.pkp_include_subdomains);
+ EXPECT_TRUE(domain_state.sts.include_subdomains);
+ EXPECT_FALSE(domain_state.pkp.include_subdomains);
}
TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) {
@@ -5078,20 +5167,27 @@ TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) {
req.Cancel();
}
-TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictRedirects) {
+TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictDataRedirects) {
// Test URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget().
- GURL file_url("file:///foo.txt");
GURL data_url("data:,foo");
- FileProtocolHandler file_protocol_handler(base::MessageLoopProxy::current());
- EXPECT_FALSE(file_protocol_handler.IsSafeRedirectTarget(file_url));
DataProtocolHandler data_protocol_handler;
EXPECT_FALSE(data_protocol_handler.IsSafeRedirectTarget(data_url));
// Test URLRequestJobFactoryImpl::IsSafeRedirectTarget().
- EXPECT_FALSE(job_factory_.IsSafeRedirectTarget(file_url));
EXPECT_FALSE(job_factory_.IsSafeRedirectTarget(data_url));
}
+#if !defined(DISABLE_FILE_SUPPORT)
+TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictFileRedirects) {
+ // Test URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget().
+ GURL file_url("file:///foo.txt");
+ FileProtocolHandler file_protocol_handler(base::MessageLoopProxy::current());
+ EXPECT_FALSE(file_protocol_handler.IsSafeRedirectTarget(file_url));
+
+ // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget().
+ EXPECT_FALSE(job_factory_.IsSafeRedirectTarget(file_url));
+}
+
TEST_F(URLRequestTestHTTP, RestrictFileRedirects) {
ASSERT_TRUE(test_server_.Start());
@@ -5106,6 +5202,7 @@ TEST_F(URLRequestTestHTTP, RestrictFileRedirects) {
EXPECT_EQ(URLRequestStatus::FAILED, req.status().status());
EXPECT_EQ(ERR_UNSAFE_REDIRECT, req.status().error());
}
+#endif // !defined(DISABLE_FILE_SUPPORT)
TEST_F(URLRequestTestHTTP, RestrictDataRedirects) {
ASSERT_TRUE(test_server_.Start());
@@ -5137,6 +5234,238 @@ TEST_F(URLRequestTestHTTP, RedirectToInvalidURL) {
EXPECT_EQ(ERR_INVALID_URL, req.status().error());
}
+// Make sure redirects are cached, despite not reading their bodies.
+TEST_F(URLRequestTestHTTP, CacheRedirect) {
+ ASSERT_TRUE(test_server_.Start());
+ GURL redirect_url =
+ test_server_.GetURL("files/redirect302-to-echo-cacheable");
+
+ {
+ TestDelegate d;
+ URLRequest req(redirect_url, DEFAULT_PRIORITY, &d, &default_context_);
+ req.Start();
+ base::RunLoop().Run();
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ EXPECT_EQ(1, d.received_redirect_count());
+ EXPECT_EQ(test_server_.GetURL("echo"), req.url());
+ }
+
+ {
+ TestDelegate d;
+ d.set_quit_on_redirect(true);
+ URLRequest req(redirect_url, DEFAULT_PRIORITY, &d, &default_context_);
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.received_redirect_count());
+ EXPECT_EQ(0, d.response_started_count());
+ EXPECT_TRUE(req.was_cached());
+
+ req.FollowDeferredRedirect();
+ base::RunLoop().Run();
+ EXPECT_EQ(1, d.received_redirect_count());
+ EXPECT_EQ(1, d.response_started_count());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ EXPECT_EQ(test_server_.GetURL("echo"), req.url());
+ }
+}
+
+// Make sure a request isn't cached when a NetworkDelegate forces a redirect
+// when the headers are read, since the body won't have been read.
+TEST_F(URLRequestTestHTTP, NoCacheOnNetworkDelegateRedirect) {
+ ASSERT_TRUE(test_server_.Start());
+ // URL that is normally cached.
+ GURL initial_url = test_server_.GetURL("cachetime");
+
+ {
+ // Set up the TestNetworkDelegate tp force a redirect.
+ GURL redirect_to_url = test_server_.GetURL("echo");
+ default_network_delegate_.set_redirect_on_headers_received_url(
+ redirect_to_url);
+
+ TestDelegate d;
+ URLRequest req(initial_url, DEFAULT_PRIORITY, &d, &default_context_);
+ req.Start();
+ base::RunLoop().Run();
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ EXPECT_EQ(1, d.received_redirect_count());
+ EXPECT_EQ(redirect_to_url, req.url());
+ }
+
+ {
+ TestDelegate d;
+ URLRequest req(initial_url, DEFAULT_PRIORITY, &d, &default_context_);
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ EXPECT_FALSE(req.was_cached());
+ EXPECT_EQ(0, d.received_redirect_count());
+ EXPECT_EQ(initial_url, req.url());
+ }
+}
+
+// Tests that redirection to an unsafe URL is allowed when it has been marked as
+// safe.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectToWhitelistedUnsafeURL) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL unsafe_url("data:text/html,this-is-considered-an-unsafe-url");
+ default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
+ default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
+
+ TestDelegate d;
+ {
+ URLRequest r(test_server_.GetURL("whatever"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(unsafe_url, r.url());
+ EXPECT_EQ("this-is-considered-an-unsafe-url", d.data_received());
+ }
+}
+
+// Tests that a redirect to a different unsafe URL is blocked, even after adding
+// some other URL to the whitelist.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectToDifferentUnsafeURL) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL unsafe_url("data:text/html,something");
+ GURL different_unsafe_url("data:text/html,something-else");
+ default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
+ default_network_delegate_.set_allowed_unsafe_redirect_url(
+ different_unsafe_url);
+
+ TestDelegate d;
+ {
+ URLRequest r(test_server_.GetURL("whatever"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ EXPECT_EQ(ERR_UNSAFE_REDIRECT, r.status().error());
+ }
+}
+
+// Redirects from an URL with fragment to an unsafe URL with fragment should
+// be allowed, and the reference fragment of the target URL should be preserved.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectWithDifferentReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#fragment1"));
+ GURL unsafe_url("data:,url-marked-safe-and-used-in-redirect#fragment2");
+ GURL expected_url("data:,url-marked-safe-and-used-in-redirect#fragment2");
+
+ default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
+ default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
+
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(expected_url, r.url());
+ }
+}
+
+// When a delegate has specified a safe redirect URL, but it does not match the
+// redirect target, then do not prevent the reference fragment from being added.
+TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragmentAndUnrelatedUnsafeUrl) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#expected-fragment"));
+ GURL unsafe_url("data:text/html,this-url-does-not-match-redirect-url");
+ GURL redirect_url(test_server_.GetURL("target"));
+ GURL expected_redirect_url(test_server_.GetURL("target#expected-fragment"));
+
+ default_network_delegate_.set_redirect_on_headers_received_url(redirect_url);
+ default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
+
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(expected_redirect_url, r.url());
+ }
+}
+
+// When a delegate has specified a safe redirect URL, assume that the redirect
+// URL should not be changed. In particular, the reference fragment should not
+// be modified.
+TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#should-not-be-appended"));
+ GURL redirect_url("data:text/html,expect-no-reference-fragment");
+
+ default_network_delegate_.set_redirect_on_headers_received_url(redirect_url);
+ default_network_delegate_.set_allowed_unsafe_redirect_url(redirect_url);
+
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(redirect_url, r.url());
+ }
+}
+
+// When a URLRequestRedirectJob is created, the redirection must be followed and
+// the reference fragment of the target URL must not be modified.
+TEST_F(URLRequestTestHTTP, RedirectJobWithReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#should-not-be-appended"));
+ GURL redirect_url(test_server_.GetURL("echo"));
+
+ TestDelegate d;
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ URLRequestRedirectJob* job = new URLRequestRedirectJob(
+ &r, &default_network_delegate_, redirect_url,
+ URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason");
+ AddTestInterceptor()->set_main_intercept_job(job);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(redirect_url, r.url());
+}
+
TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) {
ASSERT_TRUE(test_server_.Start());
@@ -5183,6 +5512,95 @@ TEST_F(URLRequestTestHTTP, EmptyReferrerAfterValidReferrer) {
EXPECT_EQ(std::string("None"), d.data_received());
}
+// Defer network start and then resume, checking that the request was a success
+// and bytes were received.
+TEST_F(URLRequestTestHTTP, DeferredBeforeNetworkStart) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ {
+ d.set_quit_on_network_start(true);
+ GURL test_url(test_server_.GetURL("echo"));
+ URLRequest req(test_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.received_before_network_start_count());
+ EXPECT_EQ(0, d.response_started_count());
+
+ req.ResumeNetworkStart();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.response_started_count());
+ EXPECT_NE(0, d.bytes_received());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ }
+}
+
+// Check that OnBeforeNetworkStart is only called once even if there is a
+// redirect.
+TEST_F(URLRequestTestHTTP, BeforeNetworkStartCalledOnce) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ {
+ d.set_quit_on_redirect(true);
+ d.set_quit_on_network_start(true);
+ URLRequest req(test_server_.GetURL("server-redirect?echo"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.received_before_network_start_count());
+ EXPECT_EQ(0, d.response_started_count());
+ EXPECT_EQ(0, d.received_redirect_count());
+
+ req.ResumeNetworkStart();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.received_redirect_count());
+ req.FollowDeferredRedirect();
+ base::RunLoop().Run();
+
+ // Check that the redirect's new network transaction does not get propagated
+ // to a second OnBeforeNetworkStart() notification.
+ EXPECT_EQ(1, d.received_before_network_start_count());
+
+ EXPECT_EQ(1, d.response_started_count());
+ EXPECT_NE(0, d.bytes_received());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status());
+ }
+}
+
+// Cancel the request after learning that the request would use the network.
+TEST_F(URLRequestTestHTTP, CancelOnBeforeNetworkStart) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ {
+ d.set_quit_on_network_start(true);
+ GURL test_url(test_server_.GetURL("echo"));
+ URLRequest req(test_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.received_before_network_start_count());
+ EXPECT_EQ(0, d.response_started_count());
+
+ req.Cancel();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(1, d.response_started_count());
+ EXPECT_EQ(0, d.bytes_received());
+ EXPECT_EQ(URLRequestStatus::CANCELED, req.status().status());
+ }
+}
+
TEST_F(URLRequestTestHTTP, CancelRedirect) {
ASSERT_TRUE(test_server_.Start());
@@ -5627,6 +6045,57 @@ TEST_F(URLRequestTestHTTP, Redirect307Tests) {
HTTPRedirectMethodTest(url, "HEAD", "HEAD", false);
}
+TEST_F(URLRequestTestHTTP, Redirect308Tests) {
+ ASSERT_TRUE(test_server_.Start());
+
+ const GURL url = test_server_.GetURL("files/redirect308-to-echo");
+
+ HTTPRedirectMethodTest(url, "POST", "POST", true);
+ HTTPRedirectMethodTest(url, "PUT", "PUT", true);
+ HTTPRedirectMethodTest(url, "HEAD", "HEAD", false);
+}
+
+// Make sure that 308 responses without bodies are not treated as redirects.
+// Certain legacy apis that pre-date the response code expect this behavior
+// (Like Google Drive).
+TEST_F(URLRequestTestHTTP, NoRedirectOn308WithoutLocationHeader) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ const GURL url = test_server_.GetURL("files/308-without-location-header");
+
+ URLRequest request(url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ request.Start();
+ base::RunLoop().Run();
+ EXPECT_EQ(URLRequestStatus::SUCCESS, request.status().status());
+ EXPECT_EQ(OK, request.status().error());
+ EXPECT_EQ(0, d.received_redirect_count());
+ EXPECT_EQ(308, request.response_headers()->response_code());
+ EXPECT_EQ("This is not a redirect.", d.data_received());
+}
+
+TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("files/redirect302-to-echo#fragment"));
+ GURL expected_url(test_server_.GetURL("echo#fragment"));
+
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(net::OK, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(expected_url, r.url());
+ }
+}
+
TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) {
ASSERT_TRUE(test_server_.Start());
@@ -5646,7 +6115,7 @@ TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) {
URLRequestRedirectJob* job = new URLRequestRedirectJob(
&req, &default_network_delegate_, test_server_.GetURL("echo"),
- URLRequestRedirectJob::REDIRECT_302_FOUND);
+ URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason");
AddTestInterceptor()->set_main_intercept_job(job);
req.Start();
@@ -5673,7 +6142,8 @@ TEST_F(URLRequestTestHTTP, InterceptPost307RedirectPost) {
URLRequestRedirectJob* job = new URLRequestRedirectJob(
&req, &default_network_delegate_, test_server_.GetURL("echo"),
- URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT);
+ URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
+ "Very Good Reason");
AddTestInterceptor()->set_main_intercept_job(job);
req.Start();
@@ -5808,7 +6278,8 @@ TEST_F(URLRequestTestHTTP, DefaultUserAgent) {
&default_context_);
req.Start();
base::RunLoop().Run();
- EXPECT_EQ(req.context()->GetUserAgent(req.url()), d.data_received());
+ EXPECT_EQ(req.context()->http_user_agent_settings()->GetUserAgent(),
+ d.data_received());
}
// Check that if request overrides the User-Agent header,
@@ -5826,10 +6297,7 @@ TEST_F(URLRequestTestHTTP, OverrideUserAgent) {
req.SetExtraRequestHeaders(headers);
req.Start();
base::RunLoop().Run();
- // If the net tests are being run with ChromeFrame then we need to allow for
- // the 'chromeframe' suffix which is added to the user agent before the
- // closing parentheses.
- EXPECT_TRUE(StartsWithASCII(d.data_received(), "Lynx (textmode", true));
+ EXPECT_EQ(std::string("Lynx (textmode)"), d.data_received());
}
// Check that a NULL HttpUserAgentSettings causes the corresponding empty
@@ -5879,7 +6347,7 @@ TEST_F(URLRequestTestHTTP, SetSubsequentJobPriority) {
scoped_refptr<URLRequestRedirectJob> redirect_job =
new URLRequestRedirectJob(
&req, &default_network_delegate_, test_server_.GetURL("echo"),
- URLRequestRedirectJob::REDIRECT_302_FOUND);
+ URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason");
AddTestInterceptor()->set_main_intercept_job(redirect_job.get());
req.SetPriority(LOW);
@@ -5895,6 +6363,80 @@ TEST_F(URLRequestTestHTTP, SetSubsequentJobPriority) {
EXPECT_EQ(LOW, job->priority());
}
+// Check that creating a network request while entering/exiting suspend mode
+// fails as it should. This is the only case where an HttpTransactionFactory
+// does not return an HttpTransaction.
+TEST_F(URLRequestTestHTTP, NetworkSuspendTest) {
+ // Create a new HttpNetworkLayer that thinks it's suspended.
+ HttpNetworkSession::Params params;
+ params.host_resolver = default_context_.host_resolver();
+ params.cert_verifier = default_context_.cert_verifier();
+ params.transport_security_state = default_context_.transport_security_state();
+ params.proxy_service = default_context_.proxy_service();
+ params.ssl_config_service = default_context_.ssl_config_service();
+ params.http_auth_handler_factory =
+ default_context_.http_auth_handler_factory();
+ params.network_delegate = &default_network_delegate_;
+ params.http_server_properties = default_context_.http_server_properties();
+ scoped_ptr<HttpNetworkLayer> network_layer(
+ new HttpNetworkLayer(new HttpNetworkSession(params)));
+ network_layer->OnSuspend();
+
+ HttpCache http_cache(network_layer.release(), default_context_.net_log(),
+ HttpCache::DefaultBackend::InMemory(0));
+
+ TestURLRequestContext context(true);
+ context.set_http_transaction_factory(&http_cache);
+ context.Init();
+
+ TestDelegate d;
+ URLRequest req(GURL("http://127.0.0.1/"),
+ DEFAULT_PRIORITY,
+ &d,
+ &context);
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(d.request_failed());
+ EXPECT_EQ(URLRequestStatus::FAILED, req.status().status());
+ EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, req.status().error());
+}
+
+// Check that creating a network request while entering/exiting suspend mode
+// fails as it should in the case there is no cache. This is the only case
+// where an HttpTransactionFactory does not return an HttpTransaction.
+TEST_F(URLRequestTestHTTP, NetworkSuspendTestNoCache) {
+ // Create a new HttpNetworkLayer that thinks it's suspended.
+ HttpNetworkSession::Params params;
+ params.host_resolver = default_context_.host_resolver();
+ params.cert_verifier = default_context_.cert_verifier();
+ params.transport_security_state = default_context_.transport_security_state();
+ params.proxy_service = default_context_.proxy_service();
+ params.ssl_config_service = default_context_.ssl_config_service();
+ params.http_auth_handler_factory =
+ default_context_.http_auth_handler_factory();
+ params.network_delegate = &default_network_delegate_;
+ params.http_server_properties = default_context_.http_server_properties();
+ HttpNetworkLayer network_layer(new HttpNetworkSession(params));
+ network_layer.OnSuspend();
+
+ TestURLRequestContext context(true);
+ context.set_http_transaction_factory(&network_layer);
+ context.Init();
+
+ TestDelegate d;
+ URLRequest req(GURL("http://127.0.0.1/"),
+ DEFAULT_PRIORITY,
+ &d,
+ &context);
+ req.Start();
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(d.request_failed());
+ EXPECT_EQ(URLRequestStatus::FAILED, req.status().status());
+ EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, req.status().error());
+}
+
class HTTPSRequestTest : public testing::Test {
public:
HTTPSRequestTest() : default_context_(true) {
@@ -6016,12 +6558,11 @@ TEST_F(HTTPSRequestTest, HTTPSExpiredTest) {
// Tests TLSv1.1 -> TLSv1 fallback. Verifies that we don't fall back more
// than necessary.
TEST_F(HTTPSRequestTest, TLSv1Fallback) {
- uint16 default_version_max = SSLConfigService::default_version_max();
// The OpenSSL library in use may not support TLS 1.1.
#if !defined(USE_OPENSSL)
- EXPECT_GT(default_version_max, SSL_PROTOCOL_VERSION_TLS1);
+ EXPECT_GT(kDefaultSSLVersionMax, SSL_PROTOCOL_VERSION_TLS1);
#endif
- if (default_version_max <= SSL_PROTOCOL_VERSION_TLS1)
+ if (kDefaultSSLVersionMax <= SSL_PROTOCOL_VERSION_TLS1)
return;
SpawnedTestServer::SSLOptions ssl_options(
@@ -6148,11 +6689,10 @@ TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) {
base::FilePath(FILE_PATH_LITERAL("net/data/ssl")));
ASSERT_TRUE(test_server.Start());
- // We require that the URL be www.google.com in order to pick up the
- // preloaded and dynamic HSTS and public key pin entries in the
- // TransportSecurityState. This means that we have to use a
- // MockHostResolver in order to direct www.google.com to the testserver.
- // By default, MockHostResolver maps all hosts to 127.0.0.1.
+ // We require that the URL be www.google.com in order to pick up the static
+ // and dynamic STS and PKP entries in the TransportSecurityState. This means
+ // that we have to use a MockHostResolver in order to direct www.google.com to
+ // the testserver. By default, MockHostResolver maps all hosts to 127.0.0.1.
MockHostResolver host_resolver;
TestNetworkDelegate network_delegate; // Must outlive URLRequest.
@@ -6160,12 +6700,17 @@ TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) {
context.set_network_delegate(&network_delegate);
context.set_host_resolver(&host_resolver);
TransportSecurityState transport_security_state;
- TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(transport_security_state.GetDomainState("www.google.com", true,
- &domain_state));
+
+ TransportSecurityState::DomainState static_domain_state;
+ EXPECT_TRUE(transport_security_state.GetStaticDomainState(
+ "www.google.com", true, &static_domain_state));
context.set_transport_security_state(&transport_security_state);
context.Init();
+ TransportSecurityState::DomainState dynamic_domain_state;
+ EXPECT_FALSE(transport_security_state.GetDynamicDomainState(
+ "www.google.com", &dynamic_domain_state));
+
TestDelegate d;
URLRequest r(GURL(base::StringPrintf("https://www.google.com:%d",
test_server.host_port_pair().port())),
@@ -6183,21 +6728,24 @@ TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) {
EXPECT_TRUE(d.have_certificate_errors());
EXPECT_TRUE(d.certificate_errors_are_fatal());
- // Get a fresh copy of the state, and check that it hasn't been updated.
- TransportSecurityState::DomainState new_domain_state;
- EXPECT_TRUE(transport_security_state.GetDomainState("www.google.com", true,
- &new_domain_state));
- EXPECT_EQ(new_domain_state.upgrade_mode, domain_state.upgrade_mode);
- EXPECT_EQ(new_domain_state.sts_include_subdomains,
- domain_state.sts_include_subdomains);
- EXPECT_EQ(new_domain_state.pkp_include_subdomains,
- domain_state.pkp_include_subdomains);
- EXPECT_TRUE(FingerprintsEqual(new_domain_state.static_spki_hashes,
- domain_state.static_spki_hashes));
- EXPECT_TRUE(FingerprintsEqual(new_domain_state.dynamic_spki_hashes,
- domain_state.dynamic_spki_hashes));
- EXPECT_TRUE(FingerprintsEqual(new_domain_state.bad_static_spki_hashes,
- domain_state.bad_static_spki_hashes));
+ // Get a fresh copy of the states, and check that they haven't changed.
+ TransportSecurityState::DomainState new_static_domain_state;
+ EXPECT_TRUE(transport_security_state.GetStaticDomainState(
+ "www.google.com", true, &new_static_domain_state));
+ TransportSecurityState::DomainState new_dynamic_domain_state;
+ EXPECT_FALSE(transport_security_state.GetDynamicDomainState(
+ "www.google.com", &new_dynamic_domain_state));
+
+ EXPECT_EQ(new_static_domain_state.sts.upgrade_mode,
+ static_domain_state.sts.upgrade_mode);
+ EXPECT_EQ(new_static_domain_state.sts.include_subdomains,
+ static_domain_state.sts.include_subdomains);
+ EXPECT_EQ(new_static_domain_state.pkp.include_subdomains,
+ static_domain_state.pkp.include_subdomains);
+ EXPECT_TRUE(FingerprintsEqual(new_static_domain_state.pkp.spki_hashes,
+ static_domain_state.pkp.spki_hashes));
+ EXPECT_TRUE(FingerprintsEqual(new_static_domain_state.pkp.bad_spki_hashes,
+ static_domain_state.pkp.bad_spki_hashes));
}
// Make sure HSTS preserves a POST request's method and body.
@@ -6756,12 +7304,12 @@ static bool SystemSupportsHardFailRevocationChecking() {
// several tests are effected because our testing EV certificate won't be
// recognised as EV.
static bool SystemUsesChromiumEVMetadata() {
-#if defined(USE_OPENSSL)
+#if defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID)
// http://crbug.com/117478 - OpenSSL does not support EV validation.
return false;
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
- // On OS X, we use the system to tell us whether a certificate is EV or not
- // and the system won't recognise our testing root.
+#elif (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID)
+ // On OS X and Android, we use the system to tell us whether a certificate is
+ // EV or not and the system won't recognise our testing root.
return false;
#else
return true;
diff --git a/chromium/net/url_request/view_cache_helper_unittest.cc b/chromium/net/url_request/view_cache_helper_unittest.cc
index 0fdd1938a16..70bf201a507 100644
--- a/chromium/net/url_request/view_cache_helper_unittest.cc
+++ b/chromium/net/url_request/view_cache_helper_unittest.cc
@@ -9,6 +9,7 @@
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/url_request/url_request_context.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,7 +30,7 @@ class TestURLRequestContext : public URLRequestContext {
};
TestURLRequestContext::TestURLRequestContext()
- : cache_(reinterpret_cast<HttpTransactionFactory*>(NULL), NULL,
+ : cache_(new MockNetworkLayer(), NULL,
HttpCache::DefaultBackend::InMemory(0)) {
set_http_transaction_factory(&cache_);
}
diff --git a/chromium/net/websockets/OWNERS b/chromium/net/websockets/OWNERS
index 8ef489a002e..6c81c618dad 100644
--- a/chromium/net/websockets/OWNERS
+++ b/chromium/net/websockets/OWNERS
@@ -1,8 +1,3 @@
tyoshino@chromium.org
-
-# Have been inactive for a while.
-yutak@chromium.org
-toyoshim@chromium.org
-
-# On leave
-bashi@chromium.org
+ricea@chromium.org
+yhirano@chromium.org
diff --git a/chromium/net/websockets/README b/chromium/net/websockets/README
index fab4c203bc8..c428efbb851 100644
--- a/chromium/net/websockets/README
+++ b/chromium/net/websockets/README
@@ -64,6 +64,10 @@ websocket_handshake_stream_base.h
websocket_handshake_stream_create_helper.cc
websocket_handshake_stream_create_helper.h
websocket_handshake_stream_create_helper_test.cc
+websocket_handshake_request_info.cc
+websocket_handshake_request_info.h
+websocket_handshake_response_info.cc
+websocket_handshake_response_info.h
websocket_inflater.cc
websocket_inflater.h
websocket_inflater_test.cc
diff --git a/chromium/net/websockets/websocket_basic_handshake_stream.cc b/chromium/net/websockets/websocket_basic_handshake_stream.cc
index 73a10453be0..810ac75a6cb 100644
--- a/chromium/net/websockets/websocket_basic_handshake_stream.cc
+++ b/chromium/net/websockets/websocket_basic_handshake_stream.cc
@@ -6,13 +6,23 @@
#include <algorithm>
#include <iterator>
+#include <set>
+#include <string>
+#include <vector>
#include "base/base64.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
#include "crypto/random.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
@@ -22,13 +32,51 @@
#include "net/http/http_stream_parser.h"
#include "net/socket/client_socket_handle.h"
#include "net/websockets/websocket_basic_stream.h"
+#include "net/websockets/websocket_deflate_predictor.h"
+#include "net/websockets/websocket_deflate_predictor_impl.h"
+#include "net/websockets/websocket_deflate_stream.h"
+#include "net/websockets/websocket_deflater.h"
+#include "net/websockets/websocket_extension_parser.h"
#include "net/websockets/websocket_handshake_constants.h"
#include "net/websockets/websocket_handshake_handler.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
#include "net/websockets/websocket_stream.h"
namespace net {
+
+// TODO(ricea): If more extensions are added, replace this with a more general
+// mechanism.
+struct WebSocketExtensionParams {
+ WebSocketExtensionParams()
+ : deflate_enabled(false),
+ client_window_bits(15),
+ deflate_mode(WebSocketDeflater::TAKE_OVER_CONTEXT) {}
+
+ bool deflate_enabled;
+ int client_window_bits;
+ WebSocketDeflater::ContextTakeOverMode deflate_mode;
+};
+
namespace {
+enum GetHeaderResult {
+ GET_HEADER_OK,
+ GET_HEADER_MISSING,
+ GET_HEADER_MULTIPLE,
+};
+
+std::string MissingHeaderMessage(const std::string& header_name) {
+ return std::string("'") + header_name + "' header is missing";
+}
+
+std::string MultipleHeaderValuesMessage(const std::string& header_name) {
+ return
+ std::string("'") +
+ header_name +
+ "' header must not appear more than once in a response";
+}
+
std::string GenerateHandshakeChallenge() {
std::string raw_challenge(websockets::kRawChallengeLength, '\0');
crypto::RandBytes(string_as_array(&raw_challenge), raw_challenge.length());
@@ -45,59 +93,248 @@ void AddVectorHeaderIfNonEmpty(const char* name,
headers->SetHeader(name, JoinString(value, ", "));
}
-// If |case_sensitive| is false, then |value| must be in lower-case.
-bool ValidateSingleTokenHeader(
- const scoped_refptr<HttpResponseHeaders>& headers,
- const base::StringPiece& name,
- const std::string& value,
- bool case_sensitive) {
+GetHeaderResult GetSingleHeaderValue(const HttpResponseHeaders* headers,
+ const base::StringPiece& name,
+ std::string* value) {
void* state = NULL;
- std::string token;
- int tokens = 0;
- bool has_value = false;
- while (headers->EnumerateHeader(&state, name, &token)) {
- if (++tokens > 1)
- return false;
- has_value = case_sensitive ? value == token
- : LowerCaseEqualsASCII(token, value.c_str());
+ size_t num_values = 0;
+ std::string temp_value;
+ while (headers->EnumerateHeader(&state, name, &temp_value)) {
+ if (++num_values > 1)
+ return GET_HEADER_MULTIPLE;
+ *value = temp_value;
+ }
+ return num_values > 0 ? GET_HEADER_OK : GET_HEADER_MISSING;
+}
+
+bool ValidateHeaderHasSingleValue(GetHeaderResult result,
+ const std::string& header_name,
+ std::string* failure_message) {
+ if (result == GET_HEADER_MISSING) {
+ *failure_message = MissingHeaderMessage(header_name);
+ return false;
+ }
+ if (result == GET_HEADER_MULTIPLE) {
+ *failure_message = MultipleHeaderValuesMessage(header_name);
+ return false;
+ }
+ DCHECK_EQ(result, GET_HEADER_OK);
+ return true;
+}
+
+bool ValidateUpgrade(const HttpResponseHeaders* headers,
+ std::string* failure_message) {
+ std::string value;
+ GetHeaderResult result =
+ GetSingleHeaderValue(headers, websockets::kUpgrade, &value);
+ if (!ValidateHeaderHasSingleValue(result,
+ websockets::kUpgrade,
+ failure_message)) {
+ return false;
}
- return has_value;
+
+ if (!LowerCaseEqualsASCII(value, websockets::kWebSocketLowercase)) {
+ *failure_message =
+ "'Upgrade' header value is not 'WebSocket': " + value;
+ return false;
+ }
+ return true;
+}
+
+bool ValidateSecWebSocketAccept(const HttpResponseHeaders* headers,
+ const std::string& expected,
+ std::string* failure_message) {
+ std::string actual;
+ GetHeaderResult result =
+ GetSingleHeaderValue(headers, websockets::kSecWebSocketAccept, &actual);
+ if (!ValidateHeaderHasSingleValue(result,
+ websockets::kSecWebSocketAccept,
+ failure_message)) {
+ return false;
+ }
+
+ if (expected != actual) {
+ *failure_message = "Incorrect 'Sec-WebSocket-Accept' header value";
+ return false;
+ }
+ return true;
+}
+
+bool ValidateConnection(const HttpResponseHeaders* headers,
+ std::string* failure_message) {
+ // Connection header is permitted to contain other tokens.
+ if (!headers->HasHeader(HttpRequestHeaders::kConnection)) {
+ *failure_message = MissingHeaderMessage(HttpRequestHeaders::kConnection);
+ return false;
+ }
+ if (!headers->HasHeaderValue(HttpRequestHeaders::kConnection,
+ websockets::kUpgrade)) {
+ *failure_message = "'Connection' header value must contain 'Upgrade'";
+ return false;
+ }
+ return true;
}
bool ValidateSubProtocol(
- const scoped_refptr<HttpResponseHeaders>& headers,
+ const HttpResponseHeaders* headers,
const std::vector<std::string>& requested_sub_protocols,
- std::string* sub_protocol) {
+ std::string* sub_protocol,
+ std::string* failure_message) {
void* state = NULL;
- std::string token;
+ std::string value;
base::hash_set<std::string> requested_set(requested_sub_protocols.begin(),
requested_sub_protocols.end());
- int accepted = 0;
- while (headers->EnumerateHeader(
- &state, websockets::kSecWebSocketProtocol, &token)) {
- if (requested_set.count(token) == 0)
- return false;
+ int count = 0;
+ bool has_multiple_protocols = false;
+ bool has_invalid_protocol = false;
+
+ while (!has_invalid_protocol || !has_multiple_protocols) {
+ std::string temp_value;
+ if (!headers->EnumerateHeader(
+ &state, websockets::kSecWebSocketProtocol, &temp_value))
+ break;
+ value = temp_value;
+ if (requested_set.count(value) == 0)
+ has_invalid_protocol = true;
+ if (++count > 1)
+ has_multiple_protocols = true;
+ }
- *sub_protocol = token;
- // The server is only allowed to accept one protocol.
- if (++accepted > 1)
- return false;
+ if (has_multiple_protocols) {
+ *failure_message =
+ MultipleHeaderValuesMessage(websockets::kSecWebSocketProtocol);
+ return false;
+ } else if (count > 0 && requested_sub_protocols.size() == 0) {
+ *failure_message =
+ std::string("Response must not include 'Sec-WebSocket-Protocol' "
+ "header if not present in request: ")
+ + value;
+ return false;
+ } else if (has_invalid_protocol) {
+ *failure_message =
+ "'Sec-WebSocket-Protocol' header value '" +
+ value +
+ "' in response does not match any of sent values";
+ return false;
+ } else if (requested_sub_protocols.size() > 0 && count == 0) {
+ *failure_message =
+ "Sent non-empty 'Sec-WebSocket-Protocol' header "
+ "but no response was received";
+ return false;
+ }
+ *sub_protocol = value;
+ return true;
+}
+
+bool DeflateError(std::string* message, const base::StringPiece& piece) {
+ *message = "Error in permessage-deflate: ";
+ piece.AppendToString(message);
+ return false;
+}
+
+bool ValidatePerMessageDeflateExtension(const WebSocketExtension& extension,
+ std::string* failure_message,
+ WebSocketExtensionParams* params) {
+ static const char kClientPrefix[] = "client_";
+ static const char kServerPrefix[] = "server_";
+ static const char kNoContextTakeover[] = "no_context_takeover";
+ static const char kMaxWindowBits[] = "max_window_bits";
+ const size_t kPrefixLen = arraysize(kClientPrefix) - 1;
+ COMPILE_ASSERT(kPrefixLen == arraysize(kServerPrefix) - 1,
+ the_strings_server_and_client_must_be_the_same_length);
+ typedef std::vector<WebSocketExtension::Parameter> ParameterVector;
+
+ DCHECK_EQ("permessage-deflate", extension.name());
+ const ParameterVector& parameters = extension.parameters();
+ std::set<std::string> seen_names;
+ for (ParameterVector::const_iterator it = parameters.begin();
+ it != parameters.end(); ++it) {
+ const std::string& name = it->name();
+ if (seen_names.count(name) != 0) {
+ return DeflateError(
+ failure_message,
+ "Received duplicate permessage-deflate extension parameter " + name);
+ }
+ seen_names.insert(name);
+ const std::string client_or_server(name, 0, kPrefixLen);
+ const bool is_client = (client_or_server == kClientPrefix);
+ if (!is_client && client_or_server != kServerPrefix) {
+ return DeflateError(
+ failure_message,
+ "Received an unexpected permessage-deflate extension parameter");
+ }
+ const std::string rest(name, kPrefixLen);
+ if (rest == kNoContextTakeover) {
+ if (it->HasValue()) {
+ return DeflateError(failure_message,
+ "Received invalid " + name + " parameter");
+ }
+ if (is_client)
+ params->deflate_mode = WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT;
+ } else if (rest == kMaxWindowBits) {
+ if (!it->HasValue())
+ return DeflateError(failure_message, name + " must have value");
+ int bits = 0;
+ if (!base::StringToInt(it->value(), &bits) || bits < 8 || bits > 15 ||
+ it->value()[0] == '0' ||
+ it->value().find_first_not_of("0123456789") != std::string::npos) {
+ return DeflateError(failure_message,
+ "Received invalid " + name + " parameter");
+ }
+ if (is_client)
+ params->client_window_bits = bits;
+ } else {
+ return DeflateError(
+ failure_message,
+ "Received an unexpected permessage-deflate extension parameter");
+ }
}
- // If the browser requested > 0 protocols, the server is required to accept
- // one.
- return requested_set.empty() || accepted == 1;
+ params->deflate_enabled = true;
+ return true;
}
-bool ValidateExtensions(const scoped_refptr<HttpResponseHeaders>& headers,
+bool ValidateExtensions(const HttpResponseHeaders* headers,
const std::vector<std::string>& requested_extensions,
- std::string* extensions) {
+ std::string* extensions,
+ std::string* failure_message,
+ WebSocketExtensionParams* params) {
void* state = NULL;
- std::string token;
+ std::string value;
+ std::vector<std::string> accepted_extensions;
+ // TODO(ricea): If adding support for additional extensions, generalise this
+ // code.
+ bool seen_permessage_deflate = false;
while (headers->EnumerateHeader(
- &state, websockets::kSecWebSocketExtensions, &token)) {
- // TODO(ricea): Accept permessage-deflate with valid parameters.
- return false;
+ &state, websockets::kSecWebSocketExtensions, &value)) {
+ WebSocketExtensionParser parser;
+ parser.Parse(value);
+ if (parser.has_error()) {
+ // TODO(yhirano) Set appropriate failure message.
+ *failure_message =
+ "'Sec-WebSocket-Extensions' header value is "
+ "rejected by the parser: " +
+ value;
+ return false;
+ }
+ if (parser.extension().name() == "permessage-deflate") {
+ if (seen_permessage_deflate) {
+ *failure_message = "Received duplicate permessage-deflate response";
+ return false;
+ }
+ seen_permessage_deflate = true;
+ if (!ValidatePerMessageDeflateExtension(
+ parser.extension(), failure_message, params))
+ return false;
+ } else {
+ *failure_message =
+ "Found an unsupported extension '" +
+ parser.extension().name() +
+ "' in 'Sec-WebSocket-Extensions' header";
+ return false;
+ }
+ accepted_extensions.push_back(value);
}
+ *extensions = JoinString(accepted_extensions, ", ");
return true;
}
@@ -105,13 +342,20 @@ bool ValidateExtensions(const scoped_refptr<HttpResponseHeaders>& headers,
WebSocketBasicHandshakeStream::WebSocketBasicHandshakeStream(
scoped_ptr<ClientSocketHandle> connection,
+ WebSocketStream::ConnectDelegate* connect_delegate,
bool using_proxy,
std::vector<std::string> requested_sub_protocols,
- std::vector<std::string> requested_extensions)
+ std::vector<std::string> requested_extensions,
+ std::string* failure_message)
: state_(connection.release(), using_proxy),
+ connect_delegate_(connect_delegate),
http_response_info_(NULL),
requested_sub_protocols_(requested_sub_protocols),
- requested_extensions_(requested_extensions) {}
+ requested_extensions_(requested_extensions),
+ failure_message_(failure_message) {
+ DCHECK(connect_delegate);
+ DCHECK(failure_message);
+}
WebSocketBasicHandshakeStream::~WebSocketBasicHandshakeStream() {}
@@ -120,6 +364,7 @@ int WebSocketBasicHandshakeStream::InitializeStream(
RequestPriority priority,
const BoundNetLog& net_log,
const CompletionCallback& callback) {
+ url_ = request_info->url;
state_.Initialize(request_info, priority, net_log, callback);
return OK;
}
@@ -152,16 +397,22 @@ int WebSocketBasicHandshakeStream::SendRequest(
}
enriched_headers.SetHeader(websockets::kSecWebSocketKey, handshake_challenge);
- AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol,
- requested_sub_protocols_,
- &enriched_headers);
AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketExtensions,
requested_extensions_,
&enriched_headers);
+ AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol,
+ requested_sub_protocols_,
+ &enriched_headers);
ComputeSecWebSocketAccept(handshake_challenge,
&handshake_challenge_response_);
+ DCHECK(connect_delegate_);
+ scoped_ptr<WebSocketHandshakeRequestInfo> request(
+ new WebSocketHandshakeRequestInfo(url_, base::Time::Now()));
+ request->headers.CopyFrom(enriched_headers);
+ connect_delegate_->OnStartOpeningHandshake(request.Pass());
+
return parser()->SendRequest(
state_.GenerateRequestLine(), enriched_headers, response, callback);
}
@@ -176,11 +427,9 @@ int WebSocketBasicHandshakeStream::ReadResponseHeaders(
base::Bind(&WebSocketBasicHandshakeStream::ReadResponseHeadersCallback,
base::Unretained(this),
callback));
- return rv == OK ? ValidateResponse() : rv;
-}
-
-const HttpResponseInfo* WebSocketBasicHandshakeStream::GetResponseInfo() const {
- return parser()->GetResponseInfo();
+ if (rv == ERR_IO_PENDING)
+ return rv;
+ return ValidateResponse(rv);
}
int WebSocketBasicHandshakeStream::ReadResponseBody(
@@ -250,16 +499,30 @@ void WebSocketBasicHandshakeStream::SetPriority(RequestPriority priority) {
}
scoped_ptr<WebSocketStream> WebSocketBasicHandshakeStream::Upgrade() {
- // TODO(ricea): Add deflate support.
-
// The HttpStreamParser object has a pointer to our ClientSocketHandle. Make
// sure it does not touch it again before it is destroyed.
state_.DeleteParser();
- return scoped_ptr<WebSocketStream>(
+ scoped_ptr<WebSocketStream> basic_stream(
new WebSocketBasicStream(state_.ReleaseConnection(),
state_.read_buf(),
sub_protocol_,
extensions_));
+ DCHECK(extension_params_.get());
+ if (extension_params_->deflate_enabled) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.WebSocket.DeflateMode",
+ extension_params_->deflate_mode,
+ WebSocketDeflater::NUM_CONTEXT_TAKEOVER_MODE_TYPES);
+
+ return scoped_ptr<WebSocketStream>(
+ new WebSocketDeflateStream(basic_stream.Pass(),
+ extension_params_->deflate_mode,
+ extension_params_->client_window_bits,
+ scoped_ptr<WebSocketDeflatePredictor>(
+ new WebSocketDeflatePredictorImpl)));
+ } else {
+ return basic_stream.Pass();
+ }
}
void WebSocketBasicHandshakeStream::SetWebSocketKeyForTesting(
@@ -270,49 +533,102 @@ void WebSocketBasicHandshakeStream::SetWebSocketKeyForTesting(
void WebSocketBasicHandshakeStream::ReadResponseHeadersCallback(
const CompletionCallback& callback,
int result) {
- if (result == OK)
- result = ValidateResponse();
- callback.Run(result);
+ callback.Run(ValidateResponse(result));
}
-int WebSocketBasicHandshakeStream::ValidateResponse() {
+void WebSocketBasicHandshakeStream::OnFinishOpeningHandshake() {
+ DCHECK(connect_delegate_);
DCHECK(http_response_info_);
- const scoped_refptr<HttpResponseHeaders>& headers =
- http_response_info_->headers;
-
- switch (headers->response_code()) {
- case HTTP_SWITCHING_PROTOCOLS:
- return ValidateUpgradeResponse(headers);
-
- // We need to pass these through for authentication to work.
- case HTTP_UNAUTHORIZED:
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- return OK;
-
- // Other status codes are potentially risky (see the warnings in the
- // WHATWG WebSocket API spec) and so are dropped by default.
- default:
- return ERR_INVALID_RESPONSE;
+ scoped_refptr<HttpResponseHeaders> headers = http_response_info_->headers;
+ // If the headers are too large, HttpStreamParser will just not parse them at
+ // all.
+ if (headers) {
+ scoped_ptr<WebSocketHandshakeResponseInfo> response(
+ new WebSocketHandshakeResponseInfo(url_,
+ headers->response_code(),
+ headers->GetStatusText(),
+ headers,
+ http_response_info_->response_time));
+ connect_delegate_->OnFinishOpeningHandshake(response.Pass());
+ }
+}
+
+int WebSocketBasicHandshakeStream::ValidateResponse(int rv) {
+ DCHECK(http_response_info_);
+ // Most net errors happen during connection, so they are not seen by this
+ // method. The histogram for error codes is created in
+ // Delegate::OnResponseStarted in websocket_stream.cc instead.
+ if (rv >= 0) {
+ const HttpResponseHeaders* headers = http_response_info_->headers.get();
+ const int response_code = headers->response_code();
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.WebSocket.ResponseCode", response_code);
+ switch (response_code) {
+ case HTTP_SWITCHING_PROTOCOLS:
+ OnFinishOpeningHandshake();
+ return ValidateUpgradeResponse(headers);
+
+ // We need to pass these through for authentication to work.
+ case HTTP_UNAUTHORIZED:
+ case HTTP_PROXY_AUTHENTICATION_REQUIRED:
+ return OK;
+
+ // Other status codes are potentially risky (see the warnings in the
+ // WHATWG WebSocket API spec) and so are dropped by default.
+ default:
+ // A WebSocket server cannot be using HTTP/0.9, so if we see version
+ // 0.9, it means the response was garbage.
+ // Reporting "Unexpected response code: 200" in this case is not
+ // helpful, so use a different error message.
+ if (headers->GetHttpVersion() == HttpVersion(0, 9)) {
+ set_failure_message(
+ "Error during WebSocket handshake: Invalid status line");
+ } else {
+ set_failure_message(base::StringPrintf(
+ "Error during WebSocket handshake: Unexpected response code: %d",
+ headers->response_code()));
+ }
+ OnFinishOpeningHandshake();
+ return ERR_INVALID_RESPONSE;
+ }
+ } else {
+ if (rv == ERR_EMPTY_RESPONSE) {
+ set_failure_message(
+ "Connection closed before receiving a handshake response");
+ return rv;
+ }
+ set_failure_message(std::string("Error during WebSocket handshake: ") +
+ ErrorToString(rv));
+ OnFinishOpeningHandshake();
+ return rv;
}
}
int WebSocketBasicHandshakeStream::ValidateUpgradeResponse(
- const scoped_refptr<HttpResponseHeaders>& headers) {
- if (ValidateSingleTokenHeader(headers,
- websockets::kUpgrade,
- websockets::kWebSocketLowercase,
- false) &&
- ValidateSingleTokenHeader(headers,
- websockets::kSecWebSocketAccept,
- handshake_challenge_response_,
- true) &&
- headers->HasHeaderValue(HttpRequestHeaders::kConnection,
- websockets::kUpgrade) &&
- ValidateSubProtocol(headers, requested_sub_protocols_, &sub_protocol_) &&
- ValidateExtensions(headers, requested_extensions_, &extensions_)) {
+ const HttpResponseHeaders* headers) {
+ extension_params_.reset(new WebSocketExtensionParams);
+ std::string failure_message;
+ if (ValidateUpgrade(headers, &failure_message) &&
+ ValidateSecWebSocketAccept(
+ headers, handshake_challenge_response_, &failure_message) &&
+ ValidateConnection(headers, &failure_message) &&
+ ValidateSubProtocol(headers,
+ requested_sub_protocols_,
+ &sub_protocol_,
+ &failure_message) &&
+ ValidateExtensions(headers,
+ requested_extensions_,
+ &extensions_,
+ &failure_message,
+ extension_params_.get())) {
return OK;
}
+ set_failure_message("Error during WebSocket handshake: " + failure_message);
return ERR_INVALID_RESPONSE;
}
+void WebSocketBasicHandshakeStream::set_failure_message(
+ const std::string& failure_message) {
+ *failure_message_ = failure_message;
+}
+
} // namespace net
diff --git a/chromium/net/websockets/websocket_basic_handshake_stream.h b/chromium/net/websockets/websocket_basic_handshake_stream.h
index 2e5b628cde6..51f0c4db1c5 100644
--- a/chromium/net/websockets/websocket_basic_handshake_stream.h
+++ b/chromium/net/websockets/websocket_basic_handshake_stream.h
@@ -13,6 +13,7 @@
#include "net/base/net_export.h"
#include "net/http/http_basic_state.h"
#include "net/websockets/websocket_handshake_stream_base.h"
+#include "url/gurl.h"
namespace net {
@@ -21,14 +22,19 @@ class HttpResponseHeaders;
class HttpResponseInfo;
class HttpStreamParser;
+struct WebSocketExtensionParams;
+
class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream
: public WebSocketHandshakeStreamBase {
public:
+ // |connect_delegate| and |failure_message| must out-live this object.
WebSocketBasicHandshakeStream(
scoped_ptr<ClientSocketHandle> connection,
+ WebSocketStream::ConnectDelegate* connect_delegate,
bool using_proxy,
std::vector<std::string> requested_sub_protocols,
- std::vector<std::string> requested_extensions);
+ std::vector<std::string> requested_extensions,
+ std::string* failure_message);
virtual ~WebSocketBasicHandshakeStream();
@@ -41,7 +47,6 @@ class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream
HttpResponseInfo* response,
const CompletionCallback& callback) OVERRIDE;
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
@@ -78,20 +83,29 @@ class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream
void ReadResponseHeadersCallback(const CompletionCallback& callback,
int result);
- // Validates the response from the server and returns OK or
- // ERR_INVALID_RESPONSE.
- int ValidateResponse();
+ void OnFinishOpeningHandshake();
+
+ // Validates the response and sends the finished handshake event.
+ int ValidateResponse(int rv);
// Check that the headers are well-formed for a 101 response, and returns
// OK if they are, otherwise returns ERR_INVALID_RESPONSE.
- int ValidateUpgradeResponse(
- const scoped_refptr<HttpResponseHeaders>& headers);
+ int ValidateUpgradeResponse(const HttpResponseHeaders* headers);
HttpStreamParser* parser() const { return state_.parser(); }
+ void set_failure_message(const std::string& failure_message);
+
+ // The request URL.
+ GURL url_;
+
// HttpBasicState holds most of the handshake-related state.
HttpBasicState state_;
+ // Owned by another object.
+ // |connect_delegate| will live during the lifetime of this object.
+ WebSocketStream::ConnectDelegate* connect_delegate_;
+
// This is stored in SendRequest() for use by ReadResponseHeaders().
HttpResponseInfo* http_response_info_;
@@ -114,6 +128,12 @@ class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream
// The extension(s) selected by the server.
std::string extensions_;
+ // The extension parameters. The class is defined in the implementation file
+ // to avoid including extension-related header files here.
+ scoped_ptr<WebSocketExtensionParams> extension_params_;
+
+ std::string* failure_message_;
+
DISALLOW_COPY_AND_ASSIGN(WebSocketBasicHandshakeStream);
};
diff --git a/chromium/net/websockets/websocket_basic_stream.cc b/chromium/net/websockets/websocket_basic_stream.cc
index 12472b48d1d..fd2766bcb94 100644
--- a/chromium/net/websockets/websocket_basic_stream.cc
+++ b/chromium/net/websockets/websocket_basic_stream.cc
@@ -12,7 +12,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/logging.h"
-#include "base/safe_numerics.h"
+#include "base/numerics/safe_conversions.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/socket/client_socket_handle.h"
@@ -157,13 +157,15 @@ int WebSocketBasicStream::WriteFrames(ScopedVector<WebSocketFrame>* frames,
dest += result;
remaining_size -= result;
- const char* const frame_data = frame->data->data();
const int frame_size = frame->header.payload_length;
- CHECK_GE(remaining_size, frame_size);
- std::copy(frame_data, frame_data + frame_size, dest);
- MaskWebSocketFramePayload(mask, 0, dest, frame_size);
- dest += frame_size;
- remaining_size -= frame_size;
+ if (frame_size > 0) {
+ CHECK_GE(remaining_size, frame_size);
+ const char* const frame_data = frame->data->data();
+ std::copy(frame_data, frame_data + frame_size, dest);
+ MaskWebSocketFramePayload(mask, 0, dest, frame_size);
+ dest += frame_size;
+ remaining_size -= frame_size;
+ }
}
DCHECK_EQ(0, remaining_size) << "Buffer size calculation was wrong; "
<< remaining_size << " bytes left over.";
@@ -347,10 +349,10 @@ int WebSocketBasicStream::ConvertChunkToFrame(
// header. A check for exact equality can only be used when the whole frame
// arrives in one chunk.
DCHECK_GE(current_frame_header_->payload_length,
- base::checked_numeric_cast<uint64>(chunk_size));
+ base::checked_cast<uint64>(chunk_size));
DCHECK(!is_first_chunk || !is_final_chunk ||
current_frame_header_->payload_length ==
- base::checked_numeric_cast<uint64>(chunk_size));
+ base::checked_cast<uint64>(chunk_size));
// Convert the chunk to a complete frame.
*frame = CreateFrame(is_final_chunk, data_buffer);
@@ -376,9 +378,16 @@ scoped_ptr<WebSocketFrame> WebSocketBasicStream::CreateFrame(
result_frame->header.payload_length = data_size;
result_frame->data = data;
// Ensure that opcodes Text and Binary are only used for the first frame in
- // the message.
- if (WebSocketFrameHeader::IsKnownDataOpCode(opcode))
+ // the message. Also clear the reserved bits.
+ // TODO(ricea): If a future extension requires the reserved bits to be
+ // retained on continuation frames, make this behaviour conditional on a
+ // flag set at construction time.
+ if (!is_final_chunk && WebSocketFrameHeader::IsKnownDataOpCode(opcode)) {
current_frame_header_->opcode = WebSocketFrameHeader::kOpCodeContinuation;
+ current_frame_header_->reserved1 = false;
+ current_frame_header_->reserved2 = false;
+ current_frame_header_->reserved3 = false;
+ }
}
// Make sure that a frame header is not applied to any chunks that do not
// belong to it.
diff --git a/chromium/net/websockets/websocket_basic_stream_test.cc b/chromium/net/websockets/websocket_basic_stream_test.cc
index cb936a5f01e..71af0797818 100644
--- a/chromium/net/websockets/websocket_basic_stream_test.cc
+++ b/chromium/net/websockets/websocket_basic_stream_test.cc
@@ -13,8 +13,8 @@
#include <string>
#include "base/basictypes.h"
+#include "base/big_endian.h"
#include "base/port.h"
-#include "net/base/big_endian.h"
#include "net/base/capturing_net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/socket_test_util.h"
@@ -57,6 +57,8 @@ WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(CloseFrame,
"\x88\x09\x03\xe8occludo");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(WriteFrame,
"\x81\x85\x00\x00\x00\x00Write");
+WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MaskedEmptyPong,
+ "\x8A\x80\x00\x00\x00\x00");
const WebSocketMaskingKey kNulMaskingKey = {{'\0', '\0', '\0', '\0'}};
const WebSocketMaskingKey kNonNulMaskingKey = {
{'\x0d', '\x1b', '\x06', '\x17'}};
@@ -801,7 +803,7 @@ TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OneMegFrame) {
(kWireSize + kReadBufferSize - 1) / kReadBufferSize;
scoped_ptr<char[]> big_frame(new char[kWireSize]);
memcpy(big_frame.get(), "\x81\x7F", 2);
- WriteBigEndian(big_frame.get() + 2, kPayloadSize);
+ base::WriteBigEndian(big_frame.get() + 2, kPayloadSize);
memset(big_frame.get() + kLargeFrameHeaderSize, 'A', kPayloadSize);
CreateChunkedRead(ASYNC,
@@ -826,6 +828,33 @@ TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OneMegFrame) {
}
}
+// A frame with reserved flag(s) set that arrives in chunks should only have the
+// reserved flag(s) set on the first chunk when split.
+TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ReservedFlagCleared) {
+ static const char kReservedFlagFrame[] = "\x41\x05Hello";
+ const size_t kReservedFlagFrameSize = arraysize(kReservedFlagFrame) - 1;
+ const size_t kChunkSize = 5;
+
+ CreateChunkedRead(ASYNC,
+ kReservedFlagFrame,
+ kReservedFlagFrameSize,
+ kChunkSize,
+ 2,
+ LAST_FRAME_BIG);
+
+ TestCompletionCallback cb[2];
+ ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
+ EXPECT_EQ(OK, cb[0].WaitForResult());
+ ASSERT_EQ(1U, frames_.size());
+ EXPECT_TRUE(frames_[0]->header.reserved1);
+
+ frames_.clear();
+ ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
+ EXPECT_EQ(OK, cb[1].WaitForResult());
+ ASSERT_EQ(1U, frames_.size());
+ EXPECT_FALSE(frames_[0]->header.reserved1);
+}
+
// Check that writing a frame all at once works.
TEST_F(WebSocketBasicStreamSocketWriteTest, WriteAtOnce) {
MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, kWriteFrameSize)};
@@ -856,6 +885,23 @@ TEST_F(WebSocketBasicStreamSocketWriteTest, WriteInBits) {
EXPECT_EQ(OK, cb_.WaitForResult());
}
+// Check that writing a Pong frame with a NULL body works.
+TEST_F(WebSocketBasicStreamSocketWriteTest, WriteNullPong) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, kMaskedEmptyPong, kMaskedEmptyPongSize)};
+ CreateWriteOnly(writes);
+
+ scoped_ptr<WebSocketFrame> frame(
+ new WebSocketFrame(WebSocketFrameHeader::kOpCodePong));
+ WebSocketFrameHeader& header = frame->header;
+ header.final = true;
+ header.masked = true;
+ header.payload_length = 0;
+ ScopedVector<WebSocketFrame> frames;
+ frames.push_back(frame.release());
+ EXPECT_EQ(OK, stream_->WriteFrames(&frames, cb_.callback()));
+}
+
// Check that writing with a non-NULL mask works correctly.
TEST_F(WebSocketBasicStreamSocketTest, WriteNonNulMask) {
std::string masked_frame = std::string("\x81\x88");
diff --git a/chromium/net/websockets/websocket_channel.cc b/chromium/net/websockets/websocket_channel.cc
index 61a47b71eb8..12f152f5248 100644
--- a/chromium/net/websockets/websocket_channel.cc
+++ b/chromium/net/websockets/websocket_channel.cc
@@ -4,28 +4,43 @@
#include "net/websockets/websocket_channel.h"
+#include <limits.h> // for INT_MAX
+
#include <algorithm>
+#include <deque>
#include "base/basictypes.h" // for size_t
+#include "base/big_endian.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
-#include "base/safe_numerics.h"
-#include "base/strings/string_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-#include "net/base/big_endian.h"
#include "net/base/io_buffer.h"
#include "net/base/net_log.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/websockets/websocket_errors.h"
#include "net/websockets/websocket_event_interface.h"
#include "net/websockets/websocket_frame.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
#include "net/websockets/websocket_mux.h"
#include "net/websockets/websocket_stream.h"
+#include "url/origin.h"
namespace net {
namespace {
+using base::StreamingUtf8Validator;
+
const int kDefaultSendQuotaLowWaterMark = 1 << 16;
const int kDefaultSendQuotaHighWaterMark = 1 << 17;
const size_t kWebSocketCloseCodeLength = 2;
@@ -46,12 +61,14 @@ const size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength;
// used for close codes received from a renderer that we are intending to send
// out over the network. See ParseClose() for the restrictions on incoming close
// codes. The |code| parameter is type int for convenience of implementation;
-// the real type is uint16.
+// the real type is uint16. Code 1005 is treated specially; it cannot be set
+// explicitly by Javascript but the renderer uses it to indicate we should send
+// a Close frame with no payload.
bool IsStrictlyValidCloseStatusCode(int code) {
static const int kInvalidRanges[] = {
// [BAD, OK)
0, 1000, // 1000 is the first valid code
- 1005, 1007, // 1005 and 1006 MUST NOT be set.
+ 1006, 1007, // 1006 MUST NOT be set.
1014, 3000, // 1014 unassigned; 1015 up to 2999 are reserved.
5000, 65536, // Codes above 5000 are invalid.
};
@@ -71,6 +88,38 @@ bool IsStrictlyValidCloseStatusCode(int code) {
// This function avoids a bunch of boilerplate code.
void AllowUnused(ChannelState ALLOW_UNUSED unused) {}
+// Sets |name| to the name of the frame type for the given |opcode|. Note that
+// for all of Text, Binary and Continuation opcode, this method returns
+// "Data frame".
+void GetFrameTypeForOpcode(WebSocketFrameHeader::OpCode opcode,
+ std::string* name) {
+ switch (opcode) {
+ case WebSocketFrameHeader::kOpCodeText: // fall-thru
+ case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
+ case WebSocketFrameHeader::kOpCodeContinuation:
+ *name = "Data frame";
+ break;
+
+ case WebSocketFrameHeader::kOpCodePing:
+ *name = "Ping";
+ break;
+
+ case WebSocketFrameHeader::kOpCodePong:
+ *name = "Pong";
+ break;
+
+ case WebSocketFrameHeader::kOpCodeClose:
+ *name = "Close";
+ break;
+
+ default:
+ *name = "Unknown frame type";
+ break;
+ }
+
+ return;
+}
+
} // namespace
// A class to encapsulate a set of frames and information about the size of
@@ -112,11 +161,30 @@ class WebSocketChannel::ConnectDelegate
// |this| may have been deleted.
}
- virtual void OnFailure(uint16 websocket_error) OVERRIDE {
- creator_->OnConnectFailure(websocket_error);
+ virtual void OnFailure(const std::string& message) OVERRIDE {
+ creator_->OnConnectFailure(message);
// |this| has been deleted.
}
+ virtual void OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
+ creator_->OnStartOpeningHandshake(request.Pass());
+ }
+
+ virtual void OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
+ creator_->OnFinishOpeningHandshake(response.Pass());
+ }
+
+ virtual void OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
+ ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ creator_->OnSSLCertificateError(
+ ssl_error_callbacks.Pass(), ssl_info, fatal);
+ }
+
private:
// A pointer to the WebSocketChannel that created this object. There is no
// danger of this pointer being stale, because deleting the WebSocketChannel
@@ -127,6 +195,101 @@ class WebSocketChannel::ConnectDelegate
DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
};
+class WebSocketChannel::HandshakeNotificationSender
+ : public base::SupportsWeakPtr<HandshakeNotificationSender> {
+ public:
+ explicit HandshakeNotificationSender(WebSocketChannel* channel);
+ ~HandshakeNotificationSender();
+
+ static void Send(base::WeakPtr<HandshakeNotificationSender> sender);
+
+ ChannelState SendImmediately(WebSocketEventInterface* event_interface);
+
+ const WebSocketHandshakeRequestInfo* handshake_request_info() const {
+ return handshake_request_info_.get();
+ }
+
+ void set_handshake_request_info(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request_info) {
+ handshake_request_info_ = request_info.Pass();
+ }
+
+ const WebSocketHandshakeResponseInfo* handshake_response_info() const {
+ return handshake_response_info_.get();
+ }
+
+ void set_handshake_response_info(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response_info) {
+ handshake_response_info_ = response_info.Pass();
+ }
+
+ private:
+ WebSocketChannel* owner_;
+ scoped_ptr<WebSocketHandshakeRequestInfo> handshake_request_info_;
+ scoped_ptr<WebSocketHandshakeResponseInfo> handshake_response_info_;
+};
+
+WebSocketChannel::HandshakeNotificationSender::HandshakeNotificationSender(
+ WebSocketChannel* channel)
+ : owner_(channel) {}
+
+WebSocketChannel::HandshakeNotificationSender::~HandshakeNotificationSender() {}
+
+void WebSocketChannel::HandshakeNotificationSender::Send(
+ base::WeakPtr<HandshakeNotificationSender> sender) {
+ // Do nothing if |sender| is already destructed.
+ if (sender) {
+ WebSocketChannel* channel = sender->owner_;
+ AllowUnused(sender->SendImmediately(channel->event_interface_.get()));
+ }
+}
+
+ChannelState WebSocketChannel::HandshakeNotificationSender::SendImmediately(
+ WebSocketEventInterface* event_interface) {
+
+ if (handshake_request_info_.get()) {
+ if (CHANNEL_DELETED == event_interface->OnStartOpeningHandshake(
+ handshake_request_info_.Pass()))
+ return CHANNEL_DELETED;
+ }
+
+ if (handshake_response_info_.get()) {
+ if (CHANNEL_DELETED == event_interface->OnFinishOpeningHandshake(
+ handshake_response_info_.Pass()))
+ return CHANNEL_DELETED;
+
+ // TODO(yhirano): We can release |this| to save memory because
+ // there will be no more opening handshake notification.
+ }
+
+ return CHANNEL_ALIVE;
+}
+
+WebSocketChannel::PendingReceivedFrame::PendingReceivedFrame(
+ bool final,
+ WebSocketFrameHeader::OpCode opcode,
+ const scoped_refptr<IOBuffer>& data,
+ size_t offset,
+ size_t size)
+ : final_(final),
+ opcode_(opcode),
+ data_(data),
+ offset_(offset),
+ size_(size) {}
+
+WebSocketChannel::PendingReceivedFrame::~PendingReceivedFrame() {}
+
+void WebSocketChannel::PendingReceivedFrame::ResetOpcode() {
+ DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(opcode_));
+ opcode_ = WebSocketFrameHeader::kOpCodeContinuation;
+}
+
+void WebSocketChannel::PendingReceivedFrame::DidConsume(size_t bytes) {
+ DCHECK_LE(offset_, size_);
+ DCHECK_LE(bytes, size_ - offset_);
+ offset_ += bytes;
+}
+
WebSocketChannel::WebSocketChannel(
scoped_ptr<WebSocketEventInterface> event_interface,
URLRequestContext* url_request_context)
@@ -135,9 +298,15 @@ WebSocketChannel::WebSocketChannel(
send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
current_send_quota_(0),
+ current_receive_quota_(0),
timeout_(base::TimeDelta::FromSeconds(kClosingHandshakeTimeoutSeconds)),
- closing_code_(0),
- state_(FRESHLY_CONSTRUCTED) {}
+ received_close_code_(0),
+ state_(FRESHLY_CONSTRUCTED),
+ notification_sender_(new HandshakeNotificationSender(this)),
+ sending_text_message_(false),
+ receiving_text_message_(false),
+ expecting_to_handle_continuation_(false),
+ initial_frame_forwarded_(false) {}
WebSocketChannel::~WebSocketChannel() {
// The stream may hold a pointer to read_frames_, and so it needs to be
@@ -151,7 +320,7 @@ WebSocketChannel::~WebSocketChannel() {
void WebSocketChannel::SendAddChannelRequest(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin) {
+ const url::Origin& origin) {
// Delegate to the tested version.
SendAddChannelRequestWithSuppliedCreator(
socket_url,
@@ -160,6 +329,19 @@ void WebSocketChannel::SendAddChannelRequest(
base::Bind(&WebSocketStream::CreateAndConnectStream));
}
+void WebSocketChannel::SetState(State new_state) {
+ DCHECK_NE(state_, new_state);
+
+ if (new_state == CONNECTED)
+ established_on_ = base::TimeTicks::Now();
+ if (state_ == CONNECTED && !established_on_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES(
+ "Net.WebSocket.Duration", base::TimeTicks::Now() - established_on_);
+ }
+
+ state_ = new_state;
+}
+
bool WebSocketChannel::InClosingState() const {
// The state RECV_CLOSED is not supported here, because it is only used in one
// code path and should not leak into the code in general.
@@ -182,18 +364,18 @@ void WebSocketChannel::SendFrame(bool fin,
return;
}
if (InClosingState()) {
- VLOG(1) << "SendFrame called in state " << state_
- << ". This may be a bug, or a harmless race.";
+ DVLOG(1) << "SendFrame called in state " << state_
+ << ". This may be a bug, or a harmless race.";
return;
}
if (state_ != CONNECTED) {
NOTREACHED() << "SendFrame() called in state " << state_;
return;
}
- if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) {
- AllowUnused(FailChannel(SEND_GOING_AWAY,
- kWebSocketMuxErrorSendQuotaViolation,
- "Send quota exceeded"));
+ if (data.size() > base::checked_cast<size_t>(current_send_quota_)) {
+ // TODO(ricea): Kill renderer.
+ AllowUnused(
+ FailChannel("Send quota exceeded", kWebSocketErrorGoingAway, ""));
// |this| has been deleted.
return;
}
@@ -203,30 +385,95 @@ void WebSocketChannel::SendFrame(bool fin,
<< " data.size()=" << data.size();
return;
}
+ if (op_code == WebSocketFrameHeader::kOpCodeText ||
+ (op_code == WebSocketFrameHeader::kOpCodeContinuation &&
+ sending_text_message_)) {
+ StreamingUtf8Validator::State state =
+ outgoing_utf8_validator_.AddBytes(vector_as_array(&data), data.size());
+ if (state == StreamingUtf8Validator::INVALID ||
+ (state == StreamingUtf8Validator::VALID_MIDPOINT && fin)) {
+ // TODO(ricea): Kill renderer.
+ AllowUnused(
+ FailChannel("Browser sent a text frame containing invalid UTF-8",
+ kWebSocketErrorGoingAway,
+ ""));
+ // |this| has been deleted.
+ return;
+ }
+ sending_text_message_ = !fin;
+ DCHECK(!fin || state == StreamingUtf8Validator::VALID_ENDPOINT);
+ }
current_send_quota_ -= data.size();
// TODO(ricea): If current_send_quota_ has dropped below
// send_quota_low_water_mark_, it might be good to increase the "low
// water mark" and "high water mark", but only if the link to the WebSocket
// server is not saturated.
- // TODO(ricea): For kOpCodeText, do UTF-8 validation?
scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size()));
std::copy(data.begin(), data.end(), buffer->data());
- AllowUnused(SendIOBuffer(fin, op_code, buffer, data.size()));
+ AllowUnused(SendFrameFromIOBuffer(fin, op_code, buffer, data.size()));
// |this| may have been deleted.
}
void WebSocketChannel::SendFlowControl(int64 quota) {
DCHECK(state_ == CONNECTING || state_ == CONNECTED || state_ == SEND_CLOSED ||
state_ == CLOSE_WAIT);
- // TODO(ricea): Add interface to WebSocketStream and implement.
- // stream_->SendFlowControl(quota);
+ // TODO(ricea): Kill the renderer if it tries to send us a negative quota
+ // value or > INT_MAX.
+ DCHECK_GE(quota, 0);
+ DCHECK_LE(quota, INT_MAX);
+ if (!pending_received_frames_.empty()) {
+ DCHECK_EQ(0, current_receive_quota_);
+ }
+ while (!pending_received_frames_.empty() && quota > 0) {
+ PendingReceivedFrame& front = pending_received_frames_.front();
+ const size_t data_size = front.size() - front.offset();
+ const size_t bytes_to_send =
+ std::min(base::checked_cast<size_t>(quota), data_size);
+ const bool final = front.final() && data_size == bytes_to_send;
+ const char* data = front.data() ?
+ front.data()->data() + front.offset() : NULL;
+ DCHECK(!bytes_to_send || data) << "Non empty data should not be null.";
+ const std::vector<char> data_vector(data, data + bytes_to_send);
+ DVLOG(3) << "Sending frame previously split due to quota to the "
+ << "renderer: quota=" << quota << " data_size=" << data_size
+ << " bytes_to_send=" << bytes_to_send;
+ if (event_interface_->OnDataFrame(final, front.opcode(), data_vector) ==
+ CHANNEL_DELETED)
+ return;
+ if (bytes_to_send < data_size) {
+ front.DidConsume(bytes_to_send);
+ front.ResetOpcode();
+ return;
+ }
+ const int64 signed_bytes_to_send = base::checked_cast<int64>(bytes_to_send);
+ DCHECK_GE(quota, signed_bytes_to_send);
+ quota -= signed_bytes_to_send;
+
+ pending_received_frames_.pop();
+ }
+ // If current_receive_quota_ == 0 then there is no pending ReadFrames()
+ // operation.
+ const bool start_read =
+ current_receive_quota_ == 0 && quota > 0 &&
+ (state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT);
+ current_receive_quota_ += base::checked_cast<int>(quota);
+ if (start_read)
+ AllowUnused(ReadFrames());
+ // |this| may have been deleted.
}
void WebSocketChannel::StartClosingHandshake(uint16 code,
const std::string& reason) {
if (InClosingState()) {
- VLOG(1) << "StartClosingHandshake called in state " << state_
- << ". This may be a bug, or a harmless race.";
+ DVLOG(1) << "StartClosingHandshake called in state " << state_
+ << ". This may be a bug, or a harmless race.";
+ return;
+ }
+ if (state_ == CONNECTING) {
+ // Abort the in-progress handshake and drop the connection immediately.
+ stream_request_.reset();
+ SetState(CLOSED);
+ AllowUnused(DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""));
return;
}
if (state_ != CONNECTED) {
@@ -242,19 +489,25 @@ void WebSocketChannel::StartClosingHandshake(uint16 code,
// errata 3227 to RFC6455. If the renderer is sending us an invalid code or
// reason it must be malfunctioning in some way, and based on that we
// interpret this as an internal error.
- AllowUnused(
- SendClose(kWebSocketErrorInternalServerError, "Internal Error"));
- // |this| may have been deleted.
+ if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED) {
+ DCHECK_EQ(CONNECTED, state_);
+ SetState(SEND_CLOSED);
+ }
return;
}
- AllowUnused(SendClose(code, IsStringUTF8(reason) ? reason : std::string()));
- // |this| may have been deleted.
+ if (SendClose(
+ code,
+ StreamingUtf8Validator::Validate(reason) ? reason : std::string()) ==
+ CHANNEL_DELETED)
+ return;
+ DCHECK_EQ(CONNECTED, state_);
+ SetState(SEND_CLOSED);
}
void WebSocketChannel::SendAddChannelRequestForTesting(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin,
+ const url::Origin& origin,
const WebSocketStreamCreator& creator) {
SendAddChannelRequestWithSuppliedCreator(
socket_url, requested_subprotocols, origin, creator);
@@ -268,13 +521,13 @@ void WebSocketChannel::SetClosingHandshakeTimeoutForTesting(
void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin,
+ const url::Origin& origin,
const WebSocketStreamCreator& creator) {
DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
if (!socket_url.SchemeIsWSOrWSS()) {
// TODO(ricea): Kill the renderer (this error should have been caught by
// Javascript).
- AllowUnused(event_interface_->OnAddChannelResponse(true, ""));
+ AllowUnused(event_interface_->OnAddChannelResponse(true, "", ""));
// |this| is deleted here.
return;
}
@@ -287,16 +540,20 @@ void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator(
url_request_context_,
BoundNetLog(),
connect_delegate.Pass());
- state_ = CONNECTING;
+ SetState(CONNECTING);
}
void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
DCHECK(stream);
DCHECK_EQ(CONNECTING, state_);
+
stream_ = stream.Pass();
- state_ = CONNECTED;
+
+ SetState(CONNECTED);
+
if (event_interface_->OnAddChannelResponse(
- false, stream_->GetSubProtocol()) == CHANNEL_DELETED)
+ false, stream_->GetSubProtocol(), stream_->GetExtensions()) ==
+ CHANNEL_DELETED)
return;
// TODO(ricea): Get flow control information from the WebSocketStream once we
@@ -308,18 +565,64 @@ void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
// |stream_request_| is not used once the connection has succeeded.
stream_request_.reset();
+
AllowUnused(ReadFrames());
// |this| may have been deleted.
}
-void WebSocketChannel::OnConnectFailure(uint16 websocket_error) {
+void WebSocketChannel::OnConnectFailure(const std::string& message) {
DCHECK_EQ(CONNECTING, state_);
- state_ = CLOSED;
+
+ // Copy the message before we delete its owner.
+ std::string message_copy = message;
+
+ SetState(CLOSED);
stream_request_.reset();
- AllowUnused(event_interface_->OnAddChannelResponse(true, ""));
+
+ if (CHANNEL_DELETED ==
+ notification_sender_->SendImmediately(event_interface_.get())) {
+ // |this| has been deleted.
+ return;
+ }
+ AllowUnused(event_interface_->OnFailChannel(message_copy));
// |this| has been deleted.
}
+void WebSocketChannel::OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal) {
+ AllowUnused(event_interface_->OnSSLCertificateError(
+ ssl_error_callbacks.Pass(), socket_url_, ssl_info, fatal));
+}
+
+void WebSocketChannel::OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) {
+ DCHECK(!notification_sender_->handshake_request_info());
+
+ // Because it is hard to handle an IPC error synchronously is difficult,
+ // we asynchronously notify the information.
+ notification_sender_->set_handshake_request_info(request.Pass());
+ ScheduleOpeningHandshakeNotification();
+}
+
+void WebSocketChannel::OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) {
+ DCHECK(!notification_sender_->handshake_response_info());
+
+ // Because it is hard to handle an IPC error synchronously is difficult,
+ // we asynchronously notify the information.
+ notification_sender_->set_handshake_response_info(response.Pass());
+ ScheduleOpeningHandshakeNotification();
+}
+
+void WebSocketChannel::ScheduleOpeningHandshakeNotification() {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(HandshakeNotificationSender::Send,
+ notification_sender_->AsWeakPtr()));
+}
+
ChannelState WebSocketChannel::WriteFrames() {
int result = OK;
do {
@@ -333,6 +636,8 @@ ChannelState WebSocketChannel::WriteFrames() {
if (result != ERR_IO_PENDING) {
if (OnWriteDone(true, result) == CHANNEL_DELETED)
return CHANNEL_DELETED;
+ // OnWriteDone() returns CHANNEL_DELETED on error. Here |state_| is
+ // guaranteed to be the same as before OnWriteDone() call.
}
} while (result == OK && data_being_sent_);
return CHANNEL_ALIVE;
@@ -373,17 +678,16 @@ ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) {
default:
DCHECK_LT(result, 0)
<< "WriteFrames() should only return OK or ERR_ codes";
+
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
- return event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
- "Abnormal Closure");
+ SetState(CLOSED);
+ return DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
}
}
ChannelState WebSocketChannel::ReadFrames() {
int result = OK;
- do {
+ while (result == OK && current_receive_quota_ > 0) {
// This use of base::Unretained is safe because this object owns the
// WebSocketStream, and any pending reads will be cancelled when it is
// destroyed.
@@ -397,7 +701,7 @@ ChannelState WebSocketChannel::ReadFrames() {
return CHANNEL_DELETED;
}
DCHECK_NE(CLOSED, state_);
- } while (result == OK);
+ }
return CHANNEL_ALIVE;
}
@@ -414,7 +718,7 @@ ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) {
for (size_t i = 0; i < read_frames_.size(); ++i) {
scoped_ptr<WebSocketFrame> frame(read_frames_[i]);
read_frames_[i] = NULL;
- if (ProcessFrame(frame.Pass()) == CHANNEL_DELETED)
+ if (HandleFrame(frame.Pass()) == CHANNEL_DELETED)
return CHANNEL_DELETED;
}
read_frames_.clear();
@@ -426,48 +730,64 @@ ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) {
return CHANNEL_ALIVE;
case ERR_WS_PROTOCOL_ERROR:
- return FailChannel(SEND_REAL_ERROR,
+ // This could be kWebSocketErrorProtocolError (specifically, non-minimal
+ // encoding of payload length) or kWebSocketErrorMessageTooBig, or an
+ // extension-specific error.
+ return FailChannel("Invalid frame header",
kWebSocketErrorProtocolError,
"WebSocket Protocol Error");
default:
DCHECK_LT(result, 0)
<< "ReadFrames() should only return OK or ERR_ codes";
+
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
+ SetState(CLOSED);
+
uint16 code = kWebSocketErrorAbnormalClosure;
- std::string reason = "Abnormal Closure";
- if (closing_code_ != 0) {
- code = closing_code_;
- reason = closing_reason_;
+ std::string reason = "";
+ bool was_clean = false;
+ if (received_close_code_ != 0) {
+ code = received_close_code_;
+ reason = received_close_reason_;
+ was_clean = (result == ERR_CONNECTION_CLOSED);
}
- return event_interface_->OnDropChannel(code, reason);
+
+ return DoDropChannel(was_clean, code, reason);
}
}
-ChannelState WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) {
+ChannelState WebSocketChannel::HandleFrame(scoped_ptr<WebSocketFrame> frame) {
if (frame->header.masked) {
// RFC6455 Section 5.1 "A client MUST close a connection if it detects a
// masked frame."
- return FailChannel(SEND_REAL_ERROR,
- kWebSocketErrorProtocolError,
- "Masked frame from server");
+ return FailChannel(
+ "A server must not mask any frames that it sends to the "
+ "client.",
+ kWebSocketErrorProtocolError,
+ "Masked frame from server");
}
const WebSocketFrameHeader::OpCode opcode = frame->header.opcode;
- if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) &&
- !frame->header.final) {
- return FailChannel(SEND_REAL_ERROR,
+ DCHECK(!WebSocketFrameHeader::IsKnownControlOpCode(opcode) ||
+ frame->header.final);
+ if (frame->header.reserved1 || frame->header.reserved2 ||
+ frame->header.reserved3) {
+ return FailChannel(base::StringPrintf(
+ "One or more reserved bits are on: reserved1 = %d, "
+ "reserved2 = %d, reserved3 = %d",
+ static_cast<int>(frame->header.reserved1),
+ static_cast<int>(frame->header.reserved2),
+ static_cast<int>(frame->header.reserved3)),
kWebSocketErrorProtocolError,
- "Control message with FIN bit unset received");
+ "Invalid reserved bit");
}
// Respond to the frame appropriately to its type.
- return HandleFrame(
+ return HandleFrameByState(
opcode, frame->header.final, frame->data, frame->header.payload_length);
}
-ChannelState WebSocketChannel::HandleFrame(
+ChannelState WebSocketChannel::HandleFrameByState(
const WebSocketFrameHeader::OpCode opcode,
bool final,
const scoped_refptr<IOBuffer>& data_buffer,
@@ -478,96 +798,66 @@ ChannelState WebSocketChannel::HandleFrame(
DCHECK_NE(CLOSED, state_);
if (state_ == CLOSE_WAIT) {
std::string frame_name;
- switch (opcode) {
- case WebSocketFrameHeader::kOpCodeText: // fall-thru
- case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
- case WebSocketFrameHeader::kOpCodeContinuation:
- frame_name = "Data frame";
- break;
-
- case WebSocketFrameHeader::kOpCodePing:
- frame_name = "Ping";
- break;
-
- case WebSocketFrameHeader::kOpCodePong:
- frame_name = "Pong";
- break;
-
- case WebSocketFrameHeader::kOpCodeClose:
- frame_name = "Close";
- break;
-
- default:
- frame_name = "Unknown frame type";
- break;
- }
- // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send
- // another Close frame.
- return FailChannel(SEND_REAL_ERROR,
- kWebSocketErrorProtocolError,
- frame_name + " received after close");
+ GetFrameTypeForOpcode(opcode, &frame_name);
+
+ // FailChannel() won't send another Close frame.
+ return FailChannel(
+ frame_name + " received after close", kWebSocketErrorProtocolError, "");
}
switch (opcode) {
- case WebSocketFrameHeader::kOpCodeText: // fall-thru
- case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
+ case WebSocketFrameHeader::kOpCodeText: // fall-thru
+ case WebSocketFrameHeader::kOpCodeBinary:
case WebSocketFrameHeader::kOpCodeContinuation:
- if (state_ == CONNECTED) {
- // TODO(ricea): Need to fail the connection if UTF-8 is invalid
- // post-reassembly. Requires a streaming UTF-8 validator.
- // TODO(ricea): Can this copy be eliminated?
- const char* const data_begin = size ? data_buffer->data() : NULL;
- const char* const data_end = data_begin + size;
- const std::vector<char> data(data_begin, data_end);
- // TODO(ricea): Handle the case when ReadFrames returns far
- // more data at once than should be sent in a single IPC. This needs to
- // be handled carefully, as an overloaded IO thread is one possible
- // cause of receiving very large chunks.
-
- // Sends the received frame to the renderer process.
- return event_interface_->OnDataFrame(final, opcode, data);
- }
- VLOG(3) << "Ignored data packet received in state " << state_;
- return CHANNEL_ALIVE;
+ return HandleDataFrame(opcode, final, data_buffer, size);
case WebSocketFrameHeader::kOpCodePing:
- VLOG(1) << "Got Ping of size " << size;
+ DVLOG(1) << "Got Ping of size " << size;
if (state_ == CONNECTED)
- return SendIOBuffer(
+ return SendFrameFromIOBuffer(
true, WebSocketFrameHeader::kOpCodePong, data_buffer, size);
- VLOG(3) << "Ignored ping in state " << state_;
+ DVLOG(3) << "Ignored ping in state " << state_;
return CHANNEL_ALIVE;
case WebSocketFrameHeader::kOpCodePong:
- VLOG(1) << "Got Pong of size " << size;
+ DVLOG(1) << "Got Pong of size " << size;
// There is no need to do anything with pong messages.
return CHANNEL_ALIVE;
case WebSocketFrameHeader::kOpCodeClose: {
+ // TODO(ricea): If there is a message which is queued for transmission to
+ // the renderer, then the renderer should not receive an
+ // OnClosingHandshake or OnDropChannel IPC until the queued message has
+ // been completedly transmitted.
uint16 code = kWebSocketNormalClosure;
std::string reason;
- ParseClose(data_buffer, size, &code, &reason);
+ std::string message;
+ if (!ParseClose(data_buffer, size, &code, &reason, &message)) {
+ return FailChannel(message, code, reason);
+ }
// TODO(ricea): Find a way to safely log the message from the close
// message (escape control codes and so on).
- VLOG(1) << "Got Close with code " << code;
+ DVLOG(1) << "Got Close with code " << code;
switch (state_) {
case CONNECTED:
- state_ = RECV_CLOSED;
- if (SendClose(code, reason) == // Sets state_ to CLOSE_WAIT
- CHANNEL_DELETED)
+ SetState(RECV_CLOSED);
+ if (SendClose(code, reason) == CHANNEL_DELETED)
return CHANNEL_DELETED;
+ DCHECK_EQ(RECV_CLOSED, state_);
+ SetState(CLOSE_WAIT);
+
if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED)
return CHANNEL_DELETED;
- closing_code_ = code;
- closing_reason_ = reason;
+ received_close_code_ = code;
+ received_close_reason_ = reason;
break;
case SEND_CLOSED:
- state_ = CLOSE_WAIT;
+ SetState(CLOSE_WAIT);
// From RFC6455 section 7.1.5: "Each endpoint
// will see the status code sent by the other end as _The WebSocket
// Connection Close Code_."
- closing_code_ = code;
- closing_reason_ = reason;
+ received_close_code_ = code;
+ received_close_reason_ = reason;
break;
default:
@@ -579,23 +869,105 @@ ChannelState WebSocketChannel::HandleFrame(
default:
return FailChannel(
- SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode");
+ base::StringPrintf("Unrecognized frame opcode: %d", opcode),
+ kWebSocketErrorProtocolError,
+ "Unknown opcode");
}
}
-ChannelState WebSocketChannel::SendIOBuffer(
+ChannelState WebSocketChannel::HandleDataFrame(
+ WebSocketFrameHeader::OpCode opcode,
+ bool final,
+ const scoped_refptr<IOBuffer>& data_buffer,
+ size_t size) {
+ if (state_ != CONNECTED) {
+ DVLOG(3) << "Ignored data packet received in state " << state_;
+ return CHANNEL_ALIVE;
+ }
+ DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation ||
+ opcode == WebSocketFrameHeader::kOpCodeText ||
+ opcode == WebSocketFrameHeader::kOpCodeBinary);
+ const bool got_continuation =
+ (opcode == WebSocketFrameHeader::kOpCodeContinuation);
+ if (got_continuation != expecting_to_handle_continuation_) {
+ const std::string console_log = got_continuation
+ ? "Received unexpected continuation frame."
+ : "Received start of new message but previous message is unfinished.";
+ const std::string reason = got_continuation
+ ? "Unexpected continuation"
+ : "Previous data frame unfinished";
+ return FailChannel(console_log, kWebSocketErrorProtocolError, reason);
+ }
+ expecting_to_handle_continuation_ = !final;
+ WebSocketFrameHeader::OpCode opcode_to_send = opcode;
+ if (!initial_frame_forwarded_ &&
+ opcode == WebSocketFrameHeader::kOpCodeContinuation) {
+ opcode_to_send = receiving_text_message_
+ ? WebSocketFrameHeader::kOpCodeText
+ : WebSocketFrameHeader::kOpCodeBinary;
+ }
+ if (opcode == WebSocketFrameHeader::kOpCodeText ||
+ (opcode == WebSocketFrameHeader::kOpCodeContinuation &&
+ receiving_text_message_)) {
+ // This call is not redundant when size == 0 because it tells us what
+ // the current state is.
+ StreamingUtf8Validator::State state = incoming_utf8_validator_.AddBytes(
+ size ? data_buffer->data() : NULL, size);
+ if (state == StreamingUtf8Validator::INVALID ||
+ (state == StreamingUtf8Validator::VALID_MIDPOINT && final)) {
+ return FailChannel("Could not decode a text frame as UTF-8.",
+ kWebSocketErrorProtocolError,
+ "Invalid UTF-8 in text frame");
+ }
+ receiving_text_message_ = !final;
+ DCHECK(!final || state == StreamingUtf8Validator::VALID_ENDPOINT);
+ }
+ if (size == 0U && !final)
+ return CHANNEL_ALIVE;
+
+ initial_frame_forwarded_ = !final;
+ if (size > base::checked_cast<size_t>(current_receive_quota_) ||
+ !pending_received_frames_.empty()) {
+ const bool no_quota = (current_receive_quota_ == 0);
+ DCHECK(no_quota || pending_received_frames_.empty());
+ DVLOG(3) << "Queueing frame to renderer due to quota. quota="
+ << current_receive_quota_ << " size=" << size;
+ WebSocketFrameHeader::OpCode opcode_to_queue =
+ no_quota ? opcode_to_send : WebSocketFrameHeader::kOpCodeContinuation;
+ pending_received_frames_.push(PendingReceivedFrame(
+ final, opcode_to_queue, data_buffer, current_receive_quota_, size));
+ if (no_quota)
+ return CHANNEL_ALIVE;
+ size = current_receive_quota_;
+ final = false;
+ }
+
+ // TODO(ricea): Can this copy be eliminated?
+ const char* const data_begin = size ? data_buffer->data() : NULL;
+ const char* const data_end = data_begin + size;
+ const std::vector<char> data(data_begin, data_end);
+ current_receive_quota_ -= size;
+ DCHECK_GE(current_receive_quota_, 0);
+
+ // Sends the received frame to the renderer process.
+ return event_interface_->OnDataFrame(final, opcode_to_send, data);
+}
+
+ChannelState WebSocketChannel::SendFrameFromIOBuffer(
bool fin,
WebSocketFrameHeader::OpCode op_code,
const scoped_refptr<IOBuffer>& buffer,
size_t size) {
DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
DCHECK(stream_);
+
scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code));
WebSocketFrameHeader& header = frame->header;
header.final = fin;
header.masked = true;
header.payload_length = size;
frame->data = buffer;
+
if (data_being_sent_) {
// Either the link to the WebSocket server is saturated, or several messages
// are being sent in a batch.
@@ -606,36 +978,31 @@ ChannelState WebSocketChannel::SendIOBuffer(
data_to_send_next_->AddFrame(frame.Pass());
return CHANNEL_ALIVE;
}
+
data_being_sent_.reset(new SendBuffer);
data_being_sent_->AddFrame(frame.Pass());
return WriteFrames();
}
-ChannelState WebSocketChannel::FailChannel(ExposeError expose,
+ChannelState WebSocketChannel::FailChannel(const std::string& message,
uint16 code,
const std::string& reason) {
DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
DCHECK_NE(CONNECTING, state_);
DCHECK_NE(CLOSED, state_);
+
// TODO(ricea): Logging.
if (state_ == CONNECTED) {
- uint16 send_code = kWebSocketErrorGoingAway;
- std::string send_reason = "Internal Error";
- if (expose == SEND_REAL_ERROR) {
- send_code = code;
- send_reason = reason;
- }
- if (SendClose(send_code, send_reason) == // Sets state_ to SEND_CLOSED
- CHANNEL_DELETED)
+ if (SendClose(code, reason) == CHANNEL_DELETED)
return CHANNEL_DELETED;
}
+
// Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser
// should close the connection itself without waiting for the closing
// handshake.
stream_->Close();
- state_ = CLOSED;
-
- return event_interface_->OnDropChannel(code, reason);
+ SetState(CLOSED);
+ return event_interface_->OnFailChannel(message);
}
ChannelState WebSocketChannel::SendClose(uint16 code,
@@ -647,12 +1014,13 @@ ChannelState WebSocketChannel::SendClose(uint16 code,
if (code == kWebSocketErrorNoStatusReceived) {
// Special case: translate kWebSocketErrorNoStatusReceived into a Close
// frame with no payload.
+ DCHECK(reason.empty());
body = new IOBuffer(0);
} else {
const size_t payload_length = kWebSocketCloseCodeLength + reason.length();
body = new IOBuffer(payload_length);
size = payload_length;
- WriteBigEndian(body->data(), code);
+ base::WriteBigEndian(body->data(), code);
COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
they_should_both_be_two);
std::copy(
@@ -664,57 +1032,82 @@ ChannelState WebSocketChannel::SendClose(uint16 code,
FROM_HERE,
timeout_,
base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
- if (SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size) ==
+ if (SendFrameFromIOBuffer(
+ true, WebSocketFrameHeader::kOpCodeClose, body, size) ==
CHANNEL_DELETED)
return CHANNEL_DELETED;
- // SendIOBuffer() checks |state_|, so it is best not to change it until after
- // SendIOBuffer() returns.
- state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT;
return CHANNEL_ALIVE;
}
-void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
+bool WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
size_t size,
uint16* code,
- std::string* reason) {
+ std::string* reason,
+ std::string* message) {
reason->clear();
if (size < kWebSocketCloseCodeLength) {
- *code = kWebSocketErrorNoStatusReceived;
- if (size != 0) {
- VLOG(1) << "Close frame with payload size " << size << " received "
- << "(the first byte is " << std::hex
- << static_cast<int>(buffer->data()[0]) << ")";
+ if (size == 0U) {
+ *code = kWebSocketErrorNoStatusReceived;
+ return true;
}
- return;
+
+ DVLOG(1) << "Close frame with payload size " << size << " received "
+ << "(the first byte is " << std::hex
+ << static_cast<int>(buffer->data()[0]) << ")";
+ *code = kWebSocketErrorProtocolError;
+ *message =
+ "Received a broken close frame containing an invalid size body.";
+ return false;
}
+
const char* data = buffer->data();
uint16 unchecked_code = 0;
- ReadBigEndian(data, &unchecked_code);
+ base::ReadBigEndian(data, &unchecked_code);
COMPILE_ASSERT(sizeof(unchecked_code) == kWebSocketCloseCodeLength,
they_should_both_be_two_bytes);
- if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) &&
- unchecked_code <=
- static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) {
- *code = unchecked_code;
- } else {
- VLOG(1) << "Close frame contained code outside of the valid range: "
- << unchecked_code;
- *code = kWebSocketErrorAbnormalClosure;
+
+ switch (unchecked_code) {
+ case kWebSocketErrorNoStatusReceived:
+ case kWebSocketErrorAbnormalClosure:
+ case kWebSocketErrorTlsHandshake:
+ *code = kWebSocketErrorProtocolError;
+ *message =
+ "Received a broken close frame containing a reserved status code.";
+ return false;
+
+ default:
+ *code = unchecked_code;
+ break;
}
+
std::string text(data + kWebSocketCloseCodeLength, data + size);
- // IsStringUTF8() blocks surrogate pairs and non-characters, so it is strictly
- // stronger than required by RFC3629.
- if (IsStringUTF8(text)) {
+ if (StreamingUtf8Validator::Validate(text)) {
reason->swap(text);
+ return true;
}
+
+ *code = kWebSocketErrorProtocolError;
+ *reason = "Invalid UTF-8 in Close frame";
+ *message = "Received a broken close frame containing invalid UTF-8.";
+ return false;
+}
+
+ChannelState WebSocketChannel::DoDropChannel(bool was_clean,
+ uint16 code,
+ const std::string& reason) {
+ if (CHANNEL_DELETED ==
+ notification_sender_->SendImmediately(event_interface_.get()))
+ return CHANNEL_DELETED;
+ ChannelState result =
+ event_interface_->OnDropChannel(was_clean, code, reason);
+ DCHECK_EQ(CHANNEL_DELETED, result);
+ return result;
}
void WebSocketChannel::CloseTimeout() {
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
- AllowUnused(event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
- "Abnormal Closure"));
+ SetState(CLOSED);
+ AllowUnused(DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""));
// |this| has been deleted.
}
diff --git a/chromium/net/websockets/websocket_channel.h b/chromium/net/websockets/websocket_channel.h
index 61f191af978..6d5640e8be0 100644
--- a/chromium/net/websockets/websocket_channel.h
+++ b/chromium/net/websockets/websocket_channel.h
@@ -5,12 +5,15 @@
#ifndef NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_
#define NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_
+#include <queue>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h" // for WARN_UNUSED_RESULT
+#include "base/i18n/streaming_utf8_validator.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
@@ -21,11 +24,17 @@
#include "net/websockets/websocket_stream.h"
#include "url/gurl.h"
+namespace url {
+class Origin;
+} // namespace url
+
namespace net {
class BoundNetLog;
class IOBuffer;
class URLRequestContext;
+struct WebSocketHandshakeRequestInfo;
+struct WebSocketHandshakeResponseInfo;
// Transport-independent implementation of WebSockets. Implements protocol
// semantics that do not depend on the underlying transport. Provides the
@@ -39,7 +48,7 @@ class NET_EXPORT WebSocketChannel {
typedef base::Callback<scoped_ptr<WebSocketStreamRequest>(
const GURL&,
const std::vector<std::string>&,
- const GURL&,
+ const url::Origin&,
URLRequestContext*,
const BoundNetLog&,
scoped_ptr<WebSocketStream::ConnectDelegate>)> WebSocketStreamCreator;
@@ -55,7 +64,7 @@ class NET_EXPORT WebSocketChannel {
void SendAddChannelRequest(
const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
- const GURL& origin);
+ const url::Origin& origin);
// Sends a data frame to the remote side. The frame should usually be no
// larger than 32KB to prevent the time required to copy the buffers from from
@@ -93,7 +102,7 @@ class NET_EXPORT WebSocketChannel {
void SendAddChannelRequestForTesting(
const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
- const GURL& origin,
+ const url::Origin& origin,
const WebSocketStreamCreator& creator);
// The default timout for the closing handshake is a sensible value (see
@@ -101,7 +110,55 @@ class NET_EXPORT WebSocketChannel {
// set it to a very small value for testing purposes.
void SetClosingHandshakeTimeoutForTesting(base::TimeDelta delay);
+ // Called when the stream starts the WebSocket Opening Handshake.
+ // This method is public for testing.
+ void OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request);
+
+ // Called when the stream ends the WebSocket Opening Handshake.
+ // This method is public for testing.
+ void OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response);
+
private:
+ class HandshakeNotificationSender;
+
+ // The Windows implementation of std::queue requires that this declaration be
+ // visible in the header.
+ class PendingReceivedFrame {
+ public:
+ PendingReceivedFrame(bool final,
+ WebSocketFrameHeader::OpCode opcode,
+ const scoped_refptr<IOBuffer>& data,
+ size_t offset,
+ size_t size);
+ ~PendingReceivedFrame();
+
+ bool final() const { return final_; }
+ WebSocketFrameHeader::OpCode opcode() const { return opcode_; }
+ // ResetOpcode() to Continuation.
+ void ResetOpcode();
+ const scoped_refptr<IOBuffer>& data() const { return data_; }
+ size_t offset() const { return offset_; }
+ size_t size() const { return size_; }
+ // Increase |offset_| by |bytes|.
+ void DidConsume(size_t bytes);
+
+ // This object needs to be copyable and assignable, since it will be placed
+ // in a std::queue. The compiler-generated copy constructor and assignment
+ // operator will do the right thing.
+
+ private:
+ bool final_;
+ WebSocketFrameHeader::OpCode opcode_;
+ scoped_refptr<IOBuffer> data_;
+ // Where to start reading from data_. Everything prior to offset_ has
+ // already been sent to the browser.
+ size_t offset_;
+ // The size of data_.
+ size_t size_;
+ };
+
// Methods which return a value of type ChannelState may delete |this|. If the
// return value is CHANNEL_DELETED, then the caller must return without making
// any further access to member variables or methods.
@@ -124,14 +181,6 @@ class NET_EXPORT WebSocketChannel {
// has been closed; or the connection is failed.
};
- // When failing a channel, sometimes it is inappropriate to expose the real
- // reason for failing to the remote server. This enum is used by FailChannel()
- // to select between sending the real status or a "Going Away" status.
- enum ExposeError {
- SEND_REAL_ERROR,
- SEND_GOING_AWAY,
- };
-
// Implementation of WebSocketStream::ConnectDelegate for
// WebSocketChannel. WebSocketChannel does not inherit from
// WebSocketStream::ConnectDelegate directly to avoid cluttering the public
@@ -144,7 +193,7 @@ class NET_EXPORT WebSocketChannel {
void SendAddChannelRequestWithSuppliedCreator(
const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
- const GURL& origin,
+ const url::Origin& origin,
const WebSocketStreamCreator& creator);
// Success callback from WebSocketStream::CreateAndConnectStream(). Reports
@@ -153,7 +202,23 @@ class NET_EXPORT WebSocketChannel {
// Failure callback from WebSocketStream::CreateAndConnectStream(). Reports
// failure to the event interface. May delete |this|.
- void OnConnectFailure(uint16 websocket_error);
+ void OnConnectFailure(const std::string& message);
+
+ // SSL certificate error callback from
+ // WebSocketStream::CreateAndConnectStream(). Forwards the request to the
+ // event interface.
+ void OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
+ ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal);
+
+ // Posts a task that sends pending notifications relating WebSocket Opening
+ // Handshake to the renderer.
+ void ScheduleOpeningHandshakeNotification();
+
+ // Sets |state_| to |new_state| and updates UMA if necessary.
+ void SetState(State new_state);
// Returns true if state_ is SEND_CLOSED, CLOSE_WAIT or CLOSED.
bool InClosingState() const;
@@ -168,7 +233,8 @@ class NET_EXPORT WebSocketChannel {
// WriteFrames() itself.
ChannelState OnWriteDone(bool synchronous, int result) WARN_UNUSED_RESULT;
- // Calls WebSocketStream::ReadFrames() with the appropriate arguments.
+ // Calls WebSocketStream::ReadFrames() with the appropriate arguments. Stops
+ // calling ReadFrames if current_receive_quota_ is 0.
ChannelState ReadFrames() WARN_UNUSED_RESULT;
// Callback from WebSocketStream::ReadFrames. Handles any errors and processes
@@ -177,37 +243,52 @@ class NET_EXPORT WebSocketChannel {
// within the ReadFrames() loop and does not need to call ReadFrames() itself.
ChannelState OnReadDone(bool synchronous, int result) WARN_UNUSED_RESULT;
- // Processes a single frame that has been read from the stream.
- ChannelState ProcessFrame(
+ // Handles a single frame that the object has received enough of to process.
+ // May call |event_interface_| methods, send responses to the server, and
+ // change the value of |state_|.
+ //
+ // This method performs sanity checks on the frame that are needed regardless
+ // of the current state. Then, calls the HandleFrameByState() method below
+ // which performs the appropriate action(s) depending on the current state.
+ ChannelState HandleFrame(
scoped_ptr<WebSocketFrame> frame) WARN_UNUSED_RESULT;
- // Handles a frame that the object has received enough of to process. May call
- // |event_interface_| methods, send responses to the server, and change the
- // value of |state_|.
- ChannelState HandleFrame(const WebSocketFrameHeader::OpCode opcode,
- bool final,
- const scoped_refptr<IOBuffer>& data_buffer,
- size_t size) WARN_UNUSED_RESULT;
+ // Handles a single frame depending on the current state. It's used by the
+ // HandleFrame() method.
+ ChannelState HandleFrameByState(
+ const WebSocketFrameHeader::OpCode opcode,
+ bool final,
+ const scoped_refptr<IOBuffer>& data_buffer,
+ size_t size) WARN_UNUSED_RESULT;
+
+ // Forward a received data frame to the renderer, if connected. If
+ // |expecting_continuation| is not equal to |expecting_to_read_continuation_|,
+ // will fail the channel. Also checks the UTF-8 validity of text frames.
+ ChannelState HandleDataFrame(WebSocketFrameHeader::OpCode opcode,
+ bool final,
+ const scoped_refptr<IOBuffer>& data_buffer,
+ size_t size) WARN_UNUSED_RESULT;
// Low-level method to send a single frame. Used for both data and control
// frames. Either sends the frame immediately or buffers it to be scheduled
// when the current write finishes. |fin| and |op_code| are defined as for
// SendFrame() above, except that |op_code| may also be a control frame
// opcode.
- ChannelState SendIOBuffer(bool fin,
- WebSocketFrameHeader::OpCode op_code,
- const scoped_refptr<IOBuffer>& buffer,
- size_t size) WARN_UNUSED_RESULT;
+ ChannelState SendFrameFromIOBuffer(bool fin,
+ WebSocketFrameHeader::OpCode op_code,
+ const scoped_refptr<IOBuffer>& buffer,
+ size_t size) WARN_UNUSED_RESULT;
// Performs the "Fail the WebSocket Connection" operation as defined in
- // RFC6455. The supplied code and reason are sent back to the renderer in an
- // OnDropChannel message. If state_ is CONNECTED then a Close message is sent
- // to the remote host. If |expose| is SEND_REAL_ERROR then the remote host is
- // given the same status code passed to the renderer; otherwise it is sent a
- // fixed "Going Away" code. Closes the stream_ and sets state_ to CLOSED.
- // FailChannel() always returns CHANNEL_DELETED. It is not valid to access any
- // member variables or methods after calling FailChannel().
- ChannelState FailChannel(ExposeError expose,
+ // RFC6455. A NotifyFailure message is sent to the renderer with |message|.
+ // The renderer will log the message to the console but not expose it to
+ // Javascript. Javascript will see a Close code of AbnormalClosure (1006) with
+ // an empty reason string. If state_ is CONNECTED then a Close message is sent
+ // to the remote host containing the supplied |code| and |reason|. If the
+ // stream is open, closes it and sets state_ to CLOSED. FailChannel() always
+ // returns CHANNEL_DELETED. It is not valid to access any member variables or
+ // methods after calling FailChannel().
+ ChannelState FailChannel(const std::string& message,
uint16 code,
const std::string& reason) WARN_UNUSED_RESULT;
@@ -218,15 +299,26 @@ class NET_EXPORT WebSocketChannel {
ChannelState SendClose(uint16 code,
const std::string& reason) WARN_UNUSED_RESULT;
- // Parses a Close frame. If no status code is supplied, then |code| is set to
- // 1005 (No status code) with empty |reason|. If the supplied code is
- // outside the valid range, then 1002 (Protocol error) is set instead. If the
- // reason text is not valid UTF-8, then |reason| is set to an empty string
- // instead.
- void ParseClose(const scoped_refptr<IOBuffer>& buffer,
+ // Parses a Close frame payload. If no status code is supplied, then |code| is
+ // set to 1005 (No status code) with empty |reason|. If the reason text is not
+ // valid UTF-8, then |reason| is set to an empty string. If the payload size
+ // is 1, or the supplied code is not permitted to be sent over the network,
+ // then false is returned and |message| is set to an appropriate console
+ // message.
+ bool ParseClose(const scoped_refptr<IOBuffer>& buffer,
size_t size,
uint16* code,
- std::string* reason);
+ std::string* reason,
+ std::string* message);
+
+ // Drop this channel.
+ // If there are pending opening handshake notifications, notify them
+ // before dropping.
+ //
+ // Always returns CHANNEL_DELETED.
+ ChannelState DoDropChannel(bool was_clean,
+ uint16 code,
+ const std::string& reason);
// Called if the closing handshake times out. Closes the connection and
// informs the |event_interface_| if appropriate.
@@ -256,6 +348,10 @@ class NET_EXPORT WebSocketChannel {
// Destination for the current call to WebSocketStream::ReadFrames
ScopedVector<WebSocketFrame> read_frames_;
+ // Frames that have been read but not yet forwarded to the renderer due to
+ // lack of quota.
+ std::queue<PendingReceivedFrame> pending_received_frames_;
+
// Handle to an in-progress WebSocketStream creation request. Only non-NULL
// during the connection process.
scoped_ptr<WebSocketStreamRequest> stream_request_;
@@ -270,6 +366,9 @@ class NET_EXPORT WebSocketChannel {
// The current amount of quota that the renderer has available for sending
// on this logical channel (quota units).
int current_send_quota_;
+ // The remaining amount of quota that the renderer will allow us to send on
+ // this logical channel (quota units).
+ int current_receive_quota_;
// Timer for the closing handshake.
base::OneShotTimer<WebSocketChannel> timer_;
@@ -280,13 +379,35 @@ class NET_EXPORT WebSocketChannel {
// Storage for the status code and reason from the time the Close frame
// arrives until the connection is closed and they are passed to
// OnDropChannel().
- uint16 closing_code_;
- std::string closing_reason_;
+ uint16 received_close_code_;
+ std::string received_close_reason_;
// The current state of the channel. Mainly used for sanity checking, but also
// used to track the close state.
State state_;
+ // |notification_sender_| is owned by this object.
+ scoped_ptr<HandshakeNotificationSender> notification_sender_;
+
+ // UTF-8 validator for outgoing Text messages.
+ base::StreamingUtf8Validator outgoing_utf8_validator_;
+ bool sending_text_message_;
+
+ // UTF-8 validator for incoming Text messages.
+ base::StreamingUtf8Validator incoming_utf8_validator_;
+ bool receiving_text_message_;
+
+ // True if we are in the middle of receiving a message.
+ bool expecting_to_handle_continuation_;
+
+ // True if we have already sent the type (Text or Binary) of the current
+ // message to the renderer. This can be false if the message is empty so far.
+ bool initial_frame_forwarded_;
+
+ // For UMA. The time when OnConnectSuccess() method was called and |stream_|
+ // was set.
+ base::TimeTicks established_on_;
+
DISALLOW_COPY_AND_ASSIGN(WebSocketChannel);
};
diff --git a/chromium/net/websockets/websocket_channel_test.cc b/chromium/net/websockets/websocket_channel_test.cc
index 1bd75db2eb1..4a8f119a65b 100644
--- a/chromium/net/websockets/websocket_channel_test.cc
+++ b/chromium/net/websockets/websocket_channel_test.cc
@@ -4,6 +4,7 @@
#include "net/websockets/websocket_channel.h"
+#include <limits.h>
#include <string.h>
#include <iostream>
@@ -18,17 +19,20 @@
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/safe_numerics.h"
#include "base/strings/string_piece.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
+#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_context.h"
#include "net/websockets/websocket_errors.h"
#include "net/websockets/websocket_event_interface.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
#include "net/websockets/websocket_mux.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+#include "url/origin.h"
// Hacky macros to construct the body of a Close message from a code and a
// string, while ensuring the result is a compile-time constant string.
@@ -37,6 +41,7 @@
#define WEBSOCKET_CLOSE_CODE_AS_STRING_NORMAL_CLOSURE "\x03\xe8"
#define WEBSOCKET_CLOSE_CODE_AS_STRING_GOING_AWAY "\x03\xe9"
#define WEBSOCKET_CLOSE_CODE_AS_STRING_PROTOCOL_ERROR "\x03\xea"
+#define WEBSOCKET_CLOSE_CODE_AS_STRING_ABNORMAL_CLOSURE "\x03\xee"
#define WEBSOCKET_CLOSE_CODE_AS_STRING_SERVER_ERROR "\x03\xf3"
namespace net {
@@ -92,6 +97,7 @@ using ::testing::AnyNumber;
using ::testing::DefaultValue;
using ::testing::InSequence;
using ::testing::MockFunction;
+using ::testing::NotNull;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
@@ -125,27 +131,62 @@ const size_t kDefaultQuotaRefreshTrigger = (1 << 16) + 1;
// in that time! I would like my tests to run a bit quicker.
const int kVeryTinyTimeoutMillis = 1;
+// Enough quota to pass any test.
+const int64 kPlentyOfQuota = INT_MAX;
+
typedef WebSocketEventInterface::ChannelState ChannelState;
const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE;
const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED;
// This typedef mainly exists to avoid having to repeat the "NOLINT" incantation
// all over the place.
-typedef MockFunction<void(int)> Checkpoint; // NOLINT
+typedef StrictMock< MockFunction<void(int)> > Checkpoint; // NOLINT
// This mock is for testing expectations about how the EventInterface is used.
class MockWebSocketEventInterface : public WebSocketEventInterface {
public:
- MOCK_METHOD2(OnAddChannelResponse,
- ChannelState(bool, const std::string&)); // NOLINT
+ MockWebSocketEventInterface() {}
+
+ MOCK_METHOD3(OnAddChannelResponse,
+ ChannelState(bool,
+ const std::string&,
+ const std::string&)); // NOLINT
MOCK_METHOD3(OnDataFrame,
ChannelState(bool,
WebSocketMessageType,
const std::vector<char>&)); // NOLINT
- MOCK_METHOD1(OnFlowControl, ChannelState(int64)); // NOLINT
+ MOCK_METHOD1(OnFlowControl, ChannelState(int64)); // NOLINT
MOCK_METHOD0(OnClosingHandshake, ChannelState(void)); // NOLINT
- MOCK_METHOD2(OnDropChannel,
- ChannelState(uint16, const std::string&)); // NOLINT
+ MOCK_METHOD1(OnFailChannel, ChannelState(const std::string&)); // NOLINT
+ MOCK_METHOD3(OnDropChannel,
+ ChannelState(bool, uint16, const std::string&)); // NOLINT
+
+ // We can't use GMock with scoped_ptr.
+ ChannelState OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo>) OVERRIDE {
+ OnStartOpeningHandshakeCalled();
+ return CHANNEL_ALIVE;
+ }
+ ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo>) OVERRIDE {
+ OnFinishOpeningHandshakeCalled();
+ return CHANNEL_ALIVE;
+ }
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
+ const GURL& url,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ OnSSLCertificateErrorCalled(
+ ssl_error_callbacks.get(), url, ssl_info, fatal);
+ return CHANNEL_ALIVE;
+ }
+
+ MOCK_METHOD0(OnStartOpeningHandshakeCalled, void()); // NOLINT
+ MOCK_METHOD0(OnFinishOpeningHandshakeCalled, void()); // NOLINT
+ MOCK_METHOD4(
+ OnSSLCertificateErrorCalled,
+ void(SSLErrorCallbacks*, const GURL&, const SSLInfo&, bool)); // NOLINT
};
// This fake EventInterface is for tests which need a WebSocketEventInterface
@@ -153,7 +194,8 @@ class MockWebSocketEventInterface : public WebSocketEventInterface {
class FakeWebSocketEventInterface : public WebSocketEventInterface {
virtual ChannelState OnAddChannelResponse(
bool fail,
- const std::string& selected_protocol) OVERRIDE {
+ const std::string& selected_protocol,
+ const std::string& extensions) OVERRIDE {
return fail ? CHANNEL_DELETED : CHANNEL_ALIVE;
}
virtual ChannelState OnDataFrame(bool fin,
@@ -165,10 +207,29 @@ class FakeWebSocketEventInterface : public WebSocketEventInterface {
return CHANNEL_ALIVE;
}
virtual ChannelState OnClosingHandshake() OVERRIDE { return CHANNEL_ALIVE; }
- virtual ChannelState OnDropChannel(uint16 code,
+ virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE {
+ return CHANNEL_DELETED;
+ }
+ virtual ChannelState OnDropChannel(bool was_clean,
+ uint16 code,
const std::string& reason) OVERRIDE {
return CHANNEL_DELETED;
}
+ virtual ChannelState OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
+ return CHANNEL_ALIVE;
+ }
+ virtual ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
+ return CHANNEL_ALIVE;
+ }
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
+ const GURL& url,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ return CHANNEL_ALIVE;
+ }
};
// This fake WebSocketStream is for tests that require a WebSocketStream but are
@@ -212,15 +273,9 @@ class FakeWebSocketStream : public WebSocketStream {
// To make the static initialisers easier to read, we use enums rather than
// bools.
-enum IsFinal {
- NOT_FINAL_FRAME,
- FINAL_FRAME
-};
+enum IsFinal { NOT_FINAL_FRAME, FINAL_FRAME };
-enum IsMasked {
- NOT_MASKED,
- MASKED
-};
+enum IsMasked { NOT_MASKED, MASKED };
// This is used to initialise a WebSocketFrame but is statically initialisable.
struct InitFrame {
@@ -394,10 +449,7 @@ ACTION_P(InvokeClosureReturnDeleted, closure) {
// A FakeWebSocketStream whose ReadFrames() function returns data.
class ReadableFakeWebSocketStream : public FakeWebSocketStream {
public:
- enum IsSync {
- SYNC,
- ASYNC
- };
+ enum IsSync { SYNC, ASYNC };
// After constructing the object, call PrepareReadFrames() once for each
// time you wish it to return from the test.
@@ -653,7 +705,7 @@ struct ArgumentCopyingWebSocketStreamCreator {
scoped_ptr<WebSocketStreamRequest> Create(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin,
+ const url::Origin& origin,
URLRequestContext* url_request_context,
const BoundNetLog& net_log,
scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate) {
@@ -667,7 +719,7 @@ struct ArgumentCopyingWebSocketStreamCreator {
}
GURL socket_url;
- GURL origin;
+ url::Origin origin;
std::vector<std::string> requested_subprotocols;
URLRequestContext* url_request_context;
BoundNetLog net_log;
@@ -681,6 +733,13 @@ std::vector<char> AsVector(const std::string& s) {
return std::vector<char>(s.begin(), s.end());
}
+class FakeSSLErrorCallbacks
+ : public WebSocketEventInterface::SSLErrorCallbacks {
+ public:
+ virtual void CancelSSLRequest(int error, const SSLInfo* ssl_info) OVERRIDE {}
+ virtual void ContinueSSLRequest() OVERRIDE {}
+};
+
// Base class for all test fixtures.
class WebSocketChannelTest : public ::testing::Test {
protected:
@@ -703,6 +762,9 @@ class WebSocketChannelTest : public ::testing::Test {
// well. This method is virtual so that subclasses can also set the stream.
virtual void CreateChannelAndConnectSuccessfully() {
CreateChannelAndConnect();
+ // Most tests aren't concerned with flow control from the renderer, so allow
+ // MAX_INT quota units.
+ channel_->SendFlowControl(kPlentyOfQuota);
connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
}
@@ -727,10 +789,7 @@ class WebSocketChannelTest : public ::testing::Test {
// A struct containing the data that will be used to connect the channel.
// Grouped for readability.
struct ConnectData {
- ConnectData() :
- socket_url("ws://ws/"),
- origin("http://ws/")
- {}
+ ConnectData() : socket_url("ws://ws/"), origin("http://ws") {}
// URLRequestContext object.
URLRequestContext url_request_context;
@@ -740,7 +799,7 @@ class WebSocketChannelTest : public ::testing::Test {
// Requested protocols for the request.
std::vector<std::string> requested_subprotocols;
// Origin of the request
- GURL origin;
+ url::Origin origin;
// A fake WebSocketStreamCreator that just records its arguments.
ArgumentCopyingWebSocketStreamCreator creator;
@@ -761,7 +820,11 @@ enum EventInterfaceCall {
EVENT_ON_DATA_FRAME = 0x2,
EVENT_ON_FLOW_CONTROL = 0x4,
EVENT_ON_CLOSING_HANDSHAKE = 0x8,
- EVENT_ON_DROP_CHANNEL = 0x10,
+ EVENT_ON_FAIL_CHANNEL = 0x10,
+ EVENT_ON_DROP_CHANNEL = 0x20,
+ EVENT_ON_START_OPENING_HANDSHAKE = 0x40,
+ EVENT_ON_FINISH_OPENING_HANDSHAKE = 0x80,
+ EVENT_ON_SSL_CERTIFICATE_ERROR = 0x100,
};
class WebSocketChannelDeletingTest : public WebSocketChannelTest {
@@ -780,7 +843,11 @@ class WebSocketChannelDeletingTest : public WebSocketChannelTest {
: deleting_(EVENT_ON_ADD_CHANNEL_RESPONSE | EVENT_ON_DATA_FRAME |
EVENT_ON_FLOW_CONTROL |
EVENT_ON_CLOSING_HANDSHAKE |
- EVENT_ON_DROP_CHANNEL) {}
+ EVENT_ON_FAIL_CHANNEL |
+ EVENT_ON_DROP_CHANNEL |
+ EVENT_ON_START_OPENING_HANDSHAKE |
+ EVENT_ON_FINISH_OPENING_HANDSHAKE |
+ EVENT_ON_SSL_CERTIFICATE_ERROR) {}
// Create a ChannelDeletingFakeWebSocketEventInterface. Defined out-of-line to
// avoid circular dependency.
virtual scoped_ptr<WebSocketEventInterface> CreateEventInterface() OVERRIDE;
@@ -802,7 +869,8 @@ class ChannelDeletingFakeWebSocketEventInterface
virtual ChannelState OnAddChannelResponse(
bool fail,
- const std::string& selected_protocol) OVERRIDE {
+ const std::string& selected_protocol,
+ const std::string& extensions) OVERRIDE {
return fixture_->DeleteIfDeleting(EVENT_ON_ADD_CHANNEL_RESPONSE);
}
@@ -820,11 +888,32 @@ class ChannelDeletingFakeWebSocketEventInterface
return fixture_->DeleteIfDeleting(EVENT_ON_CLOSING_HANDSHAKE);
}
- virtual ChannelState OnDropChannel(uint16 code,
+ virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE {
+ return fixture_->DeleteIfDeleting(EVENT_ON_FAIL_CHANNEL);
+ }
+
+ virtual ChannelState OnDropChannel(bool was_clean,
+ uint16 code,
const std::string& reason) OVERRIDE {
return fixture_->DeleteIfDeleting(EVENT_ON_DROP_CHANNEL);
}
+ virtual ChannelState OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
+ return fixture_->DeleteIfDeleting(EVENT_ON_START_OPENING_HANDSHAKE);
+ }
+ virtual ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
+ return fixture_->DeleteIfDeleting(EVENT_ON_FINISH_OPENING_HANDSHAKE);
+ }
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
+ const GURL& url,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ return fixture_->DeleteIfDeleting(EVENT_ON_SSL_CERTIFICATE_ERROR);
+ }
+
private:
// A pointer to the test fixture. Owned by the test harness; this object will
// be deleted before it is.
@@ -844,9 +933,11 @@ class WebSocketChannelEventInterfaceTest : public WebSocketChannelTest {
WebSocketChannelEventInterfaceTest()
: event_interface_(new StrictMock<MockWebSocketEventInterface>) {
DefaultValue<ChannelState>::Set(CHANNEL_ALIVE);
- ON_CALL(*event_interface_, OnAddChannelResponse(true, _))
+ ON_CALL(*event_interface_, OnAddChannelResponse(true, _, _))
+ .WillByDefault(Return(CHANNEL_DELETED));
+ ON_CALL(*event_interface_, OnDropChannel(_, _, _))
.WillByDefault(Return(CHANNEL_DELETED));
- ON_CALL(*event_interface_, OnDropChannel(_, _))
+ ON_CALL(*event_interface_, OnFailChannel(_))
.WillByDefault(Return(CHANNEL_DELETED));
}
@@ -880,11 +971,54 @@ class WebSocketChannelStreamTest : public WebSocketChannelTest {
scoped_ptr<MockWebSocketStream> mock_stream_;
};
+// Fixture for tests which test UTF-8 validation of sent Text frames via the
+// EventInterface.
+class WebSocketChannelSendUtf8Test
+ : public WebSocketChannelEventInterfaceTest {
+ public:
+ virtual void SetUp() {
+ set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream));
+ // For the purpose of the tests using this fixture, it doesn't matter
+ // whether these methods are called or not.
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*event_interface_, OnFlowControl(_))
+ .Times(AnyNumber());
+ }
+};
+
+// Fixture for tests which test use of receive quota from the renderer.
+class WebSocketChannelFlowControlTest
+ : public WebSocketChannelEventInterfaceTest {
+ protected:
+ // Tests using this fixture should use CreateChannelAndConnectWithQuota()
+ // instead of CreateChannelAndConnectSuccessfully().
+ void CreateChannelAndConnectWithQuota(int64 quota) {
+ CreateChannelAndConnect();
+ channel_->SendFlowControl(quota);
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+ }
+
+ virtual void CreateChannelAndConnectSuccesfully() { NOTREACHED(); }
+};
+
+// Fixture for tests which test UTF-8 validation of received Text frames using a
+// mock WebSocketStream.
+class WebSocketChannelReceiveUtf8Test : public WebSocketChannelStreamTest {
+ public:
+ virtual void SetUp() {
+ // For the purpose of the tests using this fixture, it doesn't matter
+ // whether these methods are called or not.
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ }
+};
+
// Simple test that everything that should be passed to the creator function is
// passed to the creator function.
TEST_F(WebSocketChannelTest, EverythingIsPassedToTheCreatorFunction) {
connect_data_.socket_url = GURL("ws://example.com/test");
- connect_data_.origin = GURL("http://example.com/test");
+ connect_data_.origin = url::Origin("http://example.com");
connect_data_.requested_subprotocols.push_back("Sinbad");
CreateChannelAndConnect();
@@ -896,7 +1030,7 @@ TEST_F(WebSocketChannelTest, EverythingIsPassedToTheCreatorFunction) {
EXPECT_EQ(connect_data_.socket_url, actual.socket_url);
EXPECT_EQ(connect_data_.requested_subprotocols,
actual.requested_subprotocols);
- EXPECT_EQ(connect_data_.origin, actual.origin);
+ EXPECT_EQ(connect_data_.origin.string(), actual.origin.string());
}
// Verify that calling SendFlowControl before the connection is established does
@@ -915,8 +1049,7 @@ TEST_F(WebSocketChannelTest, SendFlowControlDuringHandshakeOkay) {
TEST_F(WebSocketChannelDeletingTest, OnAddChannelResponseFail) {
CreateChannelAndConnect();
EXPECT_TRUE(channel_);
- connect_data_.creator.connect_delegate->OnFailure(
- kWebSocketErrorNoStatusReceived);
+ connect_data_.creator.connect_delegate->OnFailure("bye");
EXPECT_EQ(NULL, channel_.get());
}
@@ -964,7 +1097,7 @@ TEST_F(WebSocketChannelDeletingTest, OnFlowControlAfterConnect) {
TEST_F(WebSocketChannelDeletingTest, OnFlowControlAfterSend) {
set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream));
// Avoid deleting the channel yet.
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL | EVENT_ON_DROP_CHANNEL;
CreateChannelAndConnectSuccessfully();
ASSERT_TRUE(channel_);
deleting_ = EVENT_ON_FLOW_CONTROL;
@@ -1025,9 +1158,50 @@ TEST_F(WebSocketChannelDeletingTest, OnDropChannelReadError) {
EXPECT_EQ(NULL, channel_.get());
}
+TEST_F(WebSocketChannelDeletingTest, OnNotifyStartOpeningHandshakeError) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
+ set_stream(stream.Pass());
+ deleting_ = EVENT_ON_START_OPENING_HANDSHAKE;
+
+ CreateChannelAndConnectSuccessfully();
+ ASSERT_TRUE(channel_);
+ channel_->OnStartOpeningHandshake(scoped_ptr<WebSocketHandshakeRequestInfo>(
+ new WebSocketHandshakeRequestInfo(GURL("http://www.example.com/"),
+ base::Time())));
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(NULL, channel_.get());
+}
+
+TEST_F(WebSocketChannelDeletingTest, OnNotifyFinishOpeningHandshakeError) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
+ set_stream(stream.Pass());
+ deleting_ = EVENT_ON_FINISH_OPENING_HANDSHAKE;
+
+ CreateChannelAndConnectSuccessfully();
+ ASSERT_TRUE(channel_);
+ scoped_refptr<HttpResponseHeaders> response_headers(
+ new HttpResponseHeaders(""));
+ channel_->OnFinishOpeningHandshake(scoped_ptr<WebSocketHandshakeResponseInfo>(
+ new WebSocketHandshakeResponseInfo(GURL("http://www.example.com/"),
+ 200,
+ "OK",
+ response_headers,
+ base::Time())));
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(NULL, channel_.get());
+}
+
TEST_F(WebSocketChannelDeletingTest, FailChannelInSendFrame) {
set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream));
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
ASSERT_TRUE(channel_);
channel_->SendFrame(true,
@@ -1042,7 +1216,7 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelInOnReadDone) {
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC,
ERR_WS_PROTOCOL_ERROR);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
ASSERT_TRUE(channel_);
base::MessageLoop::current()->RunUntilIdle();
@@ -1056,7 +1230,7 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToMaskedFrame) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "HELLO"}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1066,10 +1240,10 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToBadControlFrame) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
new ReadableFakeWebSocketStream);
static const InitFrame frames[] = {
- {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, ""}};
+ {FINAL_FRAME, 0xF, NOT_MASKED, ""}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1080,10 +1254,10 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToBadControlFrameNull) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
new ReadableFakeWebSocketStream);
static const InitFrame frames[] = {
- {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}};
+ {FINAL_FRAME, 0xF, NOT_MASKED, NULL}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1093,12 +1267,12 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToPongAfterClose) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
new ReadableFakeWebSocketStream);
static const InitFrame frames[] = {
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED,
- CLOSE_DATA(NORMAL_CLOSURE, "Success")},
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, ""}};
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED,
+ CLOSE_DATA(NORMAL_CLOSURE, "Success")},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, ""}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1108,12 +1282,12 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToPongAfterCloseNull) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
new ReadableFakeWebSocketStream);
static const InitFrame frames[] = {
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED,
- CLOSE_DATA(NORMAL_CLOSURE, "Success")},
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}};
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED,
+ CLOSE_DATA(NORMAL_CLOSURE, "Success")},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1125,7 +1299,7 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToUnknownOpCode) {
static const InitFrame frames[] = {{FINAL_FRAME, 0x7, NOT_MASKED, ""}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1137,7 +1311,21 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToUnknownOpCodeNull) {
static const InitFrame frames[] = {{FINAL_FRAME, 0x7, NOT_MASKED, NULL}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- deleting_ = EVENT_ON_DROP_CHANNEL;
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
+
+ CreateChannelAndConnectSuccessfully();
+ EXPECT_EQ(NULL, channel_.get());
+}
+
+TEST_F(WebSocketChannelDeletingTest, FailChannelDueInvalidCloseReason) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
+ NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ deleting_ = EVENT_ON_FAIL_CHANNEL;
CreateChannelAndConnectSuccessfully();
EXPECT_EQ(NULL, channel_.get());
@@ -1145,7 +1333,7 @@ TEST_F(WebSocketChannelDeletingTest, FailChannelDueToUnknownOpCodeNull) {
TEST_F(WebSocketChannelEventInterfaceTest, ConnectSuccessReported) {
// false means success.
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, ""));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "", ""));
// OnFlowControl is always called immediately after connect to provide initial
// quota to the renderer.
EXPECT_CALL(*event_interface_, OnFlowControl(_));
@@ -1156,23 +1344,21 @@ TEST_F(WebSocketChannelEventInterfaceTest, ConnectSuccessReported) {
}
TEST_F(WebSocketChannelEventInterfaceTest, ConnectFailureReported) {
- // true means failure.
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(true, ""));
+ EXPECT_CALL(*event_interface_, OnFailChannel("hello"));
CreateChannelAndConnect();
- connect_data_.creator.connect_delegate->OnFailure(
- kWebSocketErrorNoStatusReceived);
+ connect_data_.creator.connect_delegate->OnFailure("hello");
}
TEST_F(WebSocketChannelEventInterfaceTest, NonWebSocketSchemeRejected) {
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(true, ""));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(true, "", ""));
connect_data_.socket_url = GURL("http://www.google.com/");
CreateChannelAndConnect();
}
TEST_F(WebSocketChannelEventInterfaceTest, ProtocolPassed) {
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "Bob"));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "Bob", ""));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
CreateChannelAndConnect();
@@ -1181,6 +1367,17 @@ TEST_F(WebSocketChannelEventInterfaceTest, ProtocolPassed) {
scoped_ptr<WebSocketStream>(new FakeWebSocketStream("Bob", "")));
}
+TEST_F(WebSocketChannelEventInterfaceTest, ExtensionsPassed) {
+ EXPECT_CALL(*event_interface_,
+ OnAddChannelResponse(false, "", "extension1, extension2"));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+
+ CreateChannelAndConnect();
+
+ connect_data_.creator.connect_delegate->OnSuccess(scoped_ptr<WebSocketStream>(
+ new FakeWebSocketStream("", "extension1, extension2")));
+}
+
// The first frames from the server can arrive together with the handshake, in
// which case they will be available as soon as ReadFrames() is called the first
// time.
@@ -1193,7 +1390,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, DataLeftFromHandshake) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(
*event_interface_,
@@ -1218,12 +1415,13 @@ TEST_F(WebSocketChannelEventInterfaceTest, CloseAfterHandshake) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_, OnClosingHandshake());
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorInternalServerError,
- "Internal Server Error"));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDropChannel(
+ true, kWebSocketErrorInternalServerError, "Internal Server Error"));
}
CreateChannelAndConnectSuccessfully();
@@ -1239,10 +1437,10 @@ TEST_F(WebSocketChannelEventInterfaceTest, ConnectionCloseAfterHandshake) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _));
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
}
CreateChannelAndConnectSuccessfully();
@@ -1260,7 +1458,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, NormalAsyncRead) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(
@@ -1290,7 +1488,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, AsyncThenSyncRead) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(
*event_interface_,
@@ -1332,7 +1530,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, FragmentedMessage) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(
*event_interface_,
@@ -1368,7 +1566,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, NullMessage) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, NULL}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(
*event_interface_,
@@ -1376,29 +1574,6 @@ TEST_F(WebSocketChannelEventInterfaceTest, NullMessage) {
CreateChannelAndConnectSuccessfully();
}
-// A control frame is not permitted to be split into multiple frames. RFC6455
-// 5.5 "All control frames ... MUST NOT be fragmented."
-TEST_F(WebSocketChannelEventInterfaceTest, MultiFrameControlMessageIsRejected) {
- scoped_ptr<ReadableFakeWebSocketStream> stream(
- new ReadableFakeWebSocketStream);
- static const InitFrame frames[] = {
- {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, "Pi"},
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
- NOT_MASKED, "ng"}};
- stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
- set_stream(stream.Pass());
- {
- InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
- EXPECT_CALL(*event_interface_, OnFlowControl(_));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
- }
-
- CreateChannelAndConnectSuccessfully();
- base::MessageLoop::current()->RunUntilIdle();
-}
-
// Connection closed by the remote host without a closing handshake.
TEST_F(WebSocketChannelEventInterfaceTest, AsyncAbnormalClosure) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
@@ -1408,10 +1583,10 @@ TEST_F(WebSocketChannelEventInterfaceTest, AsyncAbnormalClosure) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _));
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
}
CreateChannelAndConnectSuccessfully();
@@ -1427,10 +1602,10 @@ TEST_F(WebSocketChannelEventInterfaceTest, ConnectionReset) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _));
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
}
CreateChannelAndConnectSuccessfully();
@@ -1448,10 +1623,12 @@ TEST_F(WebSocketChannelEventInterfaceTest, MaskedFramesAreRejected) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "A server must not mask any frames that it sends to the client."));
}
CreateChannelAndConnectSuccessfully();
@@ -1469,10 +1646,10 @@ TEST_F(WebSocketChannelEventInterfaceTest, UnknownOpCodeIsRejected) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
+ OnFailChannel("Unrecognized frame opcode: 4"));
}
CreateChannelAndConnectSuccessfully();
@@ -1500,7 +1677,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, ControlFrameInDataMessage) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(
*event_interface_,
@@ -1525,7 +1702,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, PongWithNullData) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
CreateChannelAndConnectSuccessfully();
@@ -1545,10 +1722,12 @@ TEST_F(WebSocketChannelEventInterfaceTest, FrameAfterInvalidFrame) {
set_stream(stream.Pass());
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "A server must not mask any frames that it sends to the client."));
}
CreateChannelAndConnectSuccessfully();
@@ -1561,7 +1740,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, SmallWriteDoesntUpdateQuota) {
set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream));
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
}
@@ -1578,7 +1757,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, LargeWriteUpdatesQuota) {
Checkpoint checkpoint;
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
@@ -1599,7 +1778,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, QuotaReallyIsRefreshed) {
Checkpoint checkpoint;
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
@@ -1629,10 +1808,9 @@ TEST_F(WebSocketChannelEventInterfaceTest, WriteOverQuotaIsRejected) {
set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream));
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketMuxErrorSendQuotaViolation, _));
+ EXPECT_CALL(*event_interface_, OnFailChannel("Send quota exceeded"));
}
CreateChannelAndConnectSuccessfully();
@@ -1647,11 +1825,11 @@ TEST_F(WebSocketChannelEventInterfaceTest, FailedWrite) {
Checkpoint checkpoint;
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _));
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _));
EXPECT_CALL(checkpoint, Call(2));
}
@@ -1667,10 +1845,10 @@ TEST_F(WebSocketChannelEventInterfaceTest, SendCloseDropsChannel) {
set_stream(make_scoped_ptr(new EchoeyFakeWebSocketStream));
{
InSequence s;
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketNormalClosure, "Fred"));
+ OnDropChannel(true, kWebSocketNormalClosure, "Fred"));
}
CreateChannelAndConnectSuccessfully();
@@ -1679,15 +1857,25 @@ TEST_F(WebSocketChannelEventInterfaceTest, SendCloseDropsChannel) {
base::MessageLoop::current()->RunUntilIdle();
}
+// StartClosingHandshake() also works before connection completes, and calls
+// OnDropChannel.
+TEST_F(WebSocketChannelEventInterfaceTest, CloseDuringConnection) {
+ EXPECT_CALL(*event_interface_,
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, ""));
+
+ CreateChannelAndConnect();
+ channel_->StartClosingHandshake(kWebSocketNormalClosure, "Joe");
+}
+
// OnDropChannel() is only called once when a write() on the socket triggers a
// connection reset.
TEST_F(WebSocketChannelEventInterfaceTest, OnDropChannelCalledOnce) {
set_stream(make_scoped_ptr(new ResetOnWriteFakeWebSocketStream));
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, "Abnormal Closure"))
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, ""))
.Times(1);
CreateChannelAndConnectSuccessfully();
@@ -1707,11 +1895,11 @@ TEST_F(WebSocketChannelEventInterfaceTest, CloseWithNoPayloadGivesStatus1005) {
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC,
ERR_CONNECTION_CLOSED);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_, OnClosingHandshake());
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorNoStatusReceived, _));
+ OnDropChannel(true, kWebSocketErrorNoStatusReceived, _));
CreateChannelAndConnectSuccessfully();
}
@@ -1727,28 +1915,27 @@ TEST_F(WebSocketChannelEventInterfaceTest,
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC,
ERR_CONNECTION_CLOSED);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_, OnClosingHandshake());
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorNoStatusReceived, _));
+ OnDropChannel(true, kWebSocketErrorNoStatusReceived, _));
CreateChannelAndConnectSuccessfully();
}
-// If ReadFrames() returns ERR_WS_PROTOCOL_ERROR, then
-// kWebSocketErrorProtocolError must be sent to the renderer.
+// If ReadFrames() returns ERR_WS_PROTOCOL_ERROR, then the connection must be
+// failed.
TEST_F(WebSocketChannelEventInterfaceTest, SyncProtocolErrorGivesStatus1002) {
scoped_ptr<ReadableFakeWebSocketStream> stream(
new ReadableFakeWebSocketStream);
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC,
ERR_WS_PROTOCOL_ERROR);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
+ EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header"));
CreateChannelAndConnectSuccessfully();
}
@@ -1760,16 +1947,195 @@ TEST_F(WebSocketChannelEventInterfaceTest, AsyncProtocolErrorGivesStatus1002) {
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC,
ERR_WS_PROTOCOL_ERROR);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
- EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorProtocolError, _));
+ EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header"));
+
+ CreateChannelAndConnectSuccessfully();
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+TEST_F(WebSocketChannelEventInterfaceTest, StartHandshakeRequest) {
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(*event_interface_, OnStartOpeningHandshakeCalled());
+ }
+
+ CreateChannelAndConnectSuccessfully();
+
+ scoped_ptr<WebSocketHandshakeRequestInfo> request_info(
+ new WebSocketHandshakeRequestInfo(GURL("ws://www.example.com/"),
+ base::Time()));
+ connect_data_.creator.connect_delegate->OnStartOpeningHandshake(
+ request_info.Pass());
+
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+TEST_F(WebSocketChannelEventInterfaceTest, FinishHandshakeRequest) {
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(*event_interface_, OnFinishOpeningHandshakeCalled());
+ }
CreateChannelAndConnectSuccessfully();
+
+ scoped_refptr<HttpResponseHeaders> response_headers(
+ new HttpResponseHeaders(""));
+ scoped_ptr<WebSocketHandshakeResponseInfo> response_info(
+ new WebSocketHandshakeResponseInfo(GURL("ws://www.example.com/"),
+ 200,
+ "OK",
+ response_headers,
+ base::Time()));
+ connect_data_.creator.connect_delegate->OnFinishOpeningHandshake(
+ response_info.Pass());
base::MessageLoop::current()->RunUntilIdle();
}
+TEST_F(WebSocketChannelEventInterfaceTest, FailJustAfterHandshake) {
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnStartOpeningHandshakeCalled());
+ EXPECT_CALL(*event_interface_, OnFinishOpeningHandshakeCalled());
+ EXPECT_CALL(*event_interface_, OnFailChannel("bye"));
+ }
+
+ CreateChannelAndConnect();
+
+ WebSocketStream::ConnectDelegate* connect_delegate =
+ connect_data_.creator.connect_delegate.get();
+ GURL url("ws://www.example.com/");
+ scoped_ptr<WebSocketHandshakeRequestInfo> request_info(
+ new WebSocketHandshakeRequestInfo(url, base::Time()));
+ scoped_refptr<HttpResponseHeaders> response_headers(
+ new HttpResponseHeaders(""));
+ scoped_ptr<WebSocketHandshakeResponseInfo> response_info(
+ new WebSocketHandshakeResponseInfo(url,
+ 200,
+ "OK",
+ response_headers,
+ base::Time()));
+ connect_delegate->OnStartOpeningHandshake(request_info.Pass());
+ connect_delegate->OnFinishOpeningHandshake(response_info.Pass());
+
+ connect_delegate->OnFailure("bye");
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+// Any frame after close is invalid. This test uses a Text frame. See also
+// test "PingAfterCloseIfRejected".
+TEST_F(WebSocketChannelEventInterfaceTest, DataAfterCloseIsRejected) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED,
+ CLOSE_DATA(NORMAL_CLOSURE, "OK")},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "Payload"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnClosingHandshake());
+ EXPECT_CALL(*event_interface_,
+ OnFailChannel("Data frame received after close"));
+ }
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A Close frame with a one-byte payload elicits a specific console error
+// message.
+TEST_F(WebSocketChannelEventInterfaceTest, OneByteClosePayloadMessage) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, "\x03"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "Received a broken close frame containing an invalid size body."));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A Close frame with a reserved status code also elicits a specific console
+// error message.
+TEST_F(WebSocketChannelEventInterfaceTest, ClosePayloadReservedStatusMessage) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
+ NOT_MASKED, CLOSE_DATA(ABNORMAL_CLOSURE, "Not valid on wire")}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "Received a broken close frame containing a reserved status code."));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A Close frame with invalid UTF-8 also elicits a specific console error
+// message.
+TEST_F(WebSocketChannelEventInterfaceTest, ClosePayloadInvalidReason) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
+ NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "Received a broken close frame containing invalid UTF-8."));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// The reserved bits must all be clear on received frames. Extensions should
+// clear the bits when they are set correctly before passing on the frame.
+TEST_F(WebSocketChannelEventInterfaceTest, ReservedBitsMustNotBeSet) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, "sakana"}};
+ // It is not worth adding support for reserved bits to InitFrame just for this
+ // one test, so set the bit manually.
+ ScopedVector<WebSocketFrame> raw_frames = CreateFrameVector(frames);
+ raw_frames[0]->header.reserved1 = true;
+ stream->PrepareRawReadFrames(
+ ReadableFakeWebSocketStream::SYNC, OK, raw_frames.Pass());
+ set_stream(stream.Pass());
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(*event_interface_,
+ OnFailChannel(
+ "One or more reserved bits are on: reserved1 = 1, "
+ "reserved2 = 0, reserved3 = 0"));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
// The closing handshake times out and sends an OnDropChannel event if no
// response to the client Close message is received.
TEST_F(WebSocketChannelEventInterfaceTest,
@@ -1779,7 +2145,7 @@ TEST_F(WebSocketChannelEventInterfaceTest,
stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC,
ERR_IO_PENDING);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
// This checkpoint object verifies that the OnDropChannel message comes after
// the timeout.
@@ -1789,7 +2155,7 @@ TEST_F(WebSocketChannelEventInterfaceTest,
InSequence s;
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _))
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _))
.WillOnce(InvokeClosureReturnDeleted(completion.closure()));
}
CreateChannelAndConnectSuccessfully();
@@ -1814,7 +2180,7 @@ TEST_F(WebSocketChannelEventInterfaceTest,
NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}};
stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
Checkpoint checkpoint;
TestClosure completion;
@@ -1823,7 +2189,7 @@ TEST_F(WebSocketChannelEventInterfaceTest,
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(*event_interface_, OnClosingHandshake());
EXPECT_CALL(*event_interface_,
- OnDropChannel(kWebSocketErrorAbnormalClosure, _))
+ OnDropChannel(false, kWebSocketErrorAbnormalClosure, _))
.WillOnce(InvokeClosureReturnDeleted(completion.closure()));
}
CreateChannelAndConnectSuccessfully();
@@ -1833,6 +2199,280 @@ TEST_F(WebSocketChannelEventInterfaceTest,
completion.WaitForResult();
}
+// The renderer should provide us with some quota immediately, and then
+// WebSocketChannel calls ReadFrames as soon as the stream is available.
+TEST_F(WebSocketChannelStreamTest, FlowControlEarly) {
+ Checkpoint checkpoint;
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ {
+ InSequence s;
+ EXPECT_CALL(checkpoint, Call(1));
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(Return(ERR_IO_PENDING));
+ EXPECT_CALL(checkpoint, Call(2));
+ }
+
+ set_stream(mock_stream_.Pass());
+ CreateChannelAndConnect();
+ channel_->SendFlowControl(kPlentyOfQuota);
+ checkpoint.Call(1);
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+ checkpoint.Call(2);
+}
+
+// If for some reason the connect succeeds before the renderer sends us quota,
+// we shouldn't call ReadFrames() immediately.
+// TODO(ricea): Actually we should call ReadFrames() with a small limit so we
+// can still handle control frames. This should be done once we have any API to
+// expose quota to the lower levels.
+TEST_F(WebSocketChannelStreamTest, FlowControlLate) {
+ Checkpoint checkpoint;
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ {
+ InSequence s;
+ EXPECT_CALL(checkpoint, Call(1));
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(Return(ERR_IO_PENDING));
+ EXPECT_CALL(checkpoint, Call(2));
+ }
+
+ set_stream(mock_stream_.Pass());
+ CreateChannelAndConnect();
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+ checkpoint.Call(1);
+ channel_->SendFlowControl(kPlentyOfQuota);
+ checkpoint.Call(2);
+}
+
+// We should stop calling ReadFrames() when all quota is used.
+TEST_F(WebSocketChannelStreamTest, FlowControlStopsReadFrames) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}};
+
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames));
+
+ set_stream(mock_stream_.Pass());
+ CreateChannelAndConnect();
+ channel_->SendFlowControl(4);
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+}
+
+// Providing extra quota causes ReadFrames() to be called again.
+TEST_F(WebSocketChannelStreamTest, FlowControlStartsWithMoreQuota) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}};
+ Checkpoint checkpoint;
+
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames));
+ EXPECT_CALL(checkpoint, Call(1));
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(Return(ERR_IO_PENDING));
+ }
+
+ set_stream(mock_stream_.Pass());
+ CreateChannelAndConnect();
+ channel_->SendFlowControl(4);
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+ checkpoint.Call(1);
+ channel_->SendFlowControl(4);
+}
+
+// ReadFrames() isn't called again until all pending data has been passed to
+// the renderer.
+TEST_F(WebSocketChannelStreamTest, ReadFramesNotCalledUntilQuotaAvailable) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}};
+ Checkpoint checkpoint;
+
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames));
+ EXPECT_CALL(checkpoint, Call(1));
+ EXPECT_CALL(checkpoint, Call(2));
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(Return(ERR_IO_PENDING));
+ }
+
+ set_stream(mock_stream_.Pass());
+ CreateChannelAndConnect();
+ channel_->SendFlowControl(2);
+ connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
+ checkpoint.Call(1);
+ channel_->SendFlowControl(2);
+ checkpoint.Call(2);
+ channel_->SendFlowControl(2);
+}
+
+// A message that needs to be split into frames to fit within quota should
+// maintain correct semantics.
+TEST_F(WebSocketChannelFlowControlTest, SingleFrameMessageSplitSync) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FO")));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(
+ false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("U")));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(
+ true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("R")));
+ }
+
+ CreateChannelAndConnectWithQuota(2);
+ channel_->SendFlowControl(1);
+ channel_->SendFlowControl(1);
+}
+
+// The code path for async messages is slightly different, so test it
+// separately.
+TEST_F(WebSocketChannelFlowControlTest, SingleFrameMessageSplitAsync) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames);
+ set_stream(stream.Pass());
+ Checkpoint checkpoint;
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(checkpoint, Call(1));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FO")));
+ EXPECT_CALL(checkpoint, Call(2));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(
+ false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("U")));
+ EXPECT_CALL(checkpoint, Call(3));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(
+ true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("R")));
+ }
+
+ CreateChannelAndConnectWithQuota(2);
+ checkpoint.Call(1);
+ base::MessageLoop::current()->RunUntilIdle();
+ checkpoint.Call(2);
+ channel_->SendFlowControl(1);
+ checkpoint.Call(3);
+ channel_->SendFlowControl(1);
+}
+
+// A message split into multiple frames which is further split due to quota
+// restrictions should stil be correct.
+// TODO(ricea): The message ends up split into more frames than are strictly
+// necessary. The complexity/performance tradeoffs here need further
+// examination.
+TEST_F(WebSocketChannelFlowControlTest, MultipleFrameSplit) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, "FIRST FRAME IS 25 BYTES. "},
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "SECOND FRAME IS 26 BYTES. "},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "FINAL FRAME IS 24 BYTES."}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(false,
+ WebSocketFrameHeader::kOpCodeText,
+ AsVector("FIRST FRAME IS")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(false,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector(" 25 BYTES. ")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(false,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("SECOND FRAME IS 26 BYTES. ")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(false,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("FINAL ")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(true,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("FRAME IS 24 BYTES.")));
+ }
+ CreateChannelAndConnectWithQuota(14);
+ channel_->SendFlowControl(43);
+ channel_->SendFlowControl(32);
+}
+
+// An empty message handled when we are out of quota must not be delivered
+// out-of-order with respect to other messages.
+TEST_F(WebSocketChannelFlowControlTest, EmptyMessageNoQuota) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, "FIRST MESSAGE"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, NULL},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, "THIRD MESSAGE"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+ {
+ InSequence s;
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(_));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(false,
+ WebSocketFrameHeader::kOpCodeText,
+ AsVector("FIRST ")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(true,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("MESSAGE")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(true,
+ WebSocketFrameHeader::kOpCodeText,
+ AsVector("")));
+ EXPECT_CALL(*event_interface_,
+ OnDataFrame(true,
+ WebSocketFrameHeader::kOpCodeText,
+ AsVector("THIRD MESSAGE")));
+ }
+
+ CreateChannelAndConnectWithQuota(6);
+ channel_->SendFlowControl(128);
+}
+
// RFC6455 5.1 "a client MUST mask all frames that it sends to the server".
// WebSocketChannel actually only sets the mask bit in the header, it doesn't
// perform masking itself (not all transports actually use masking).
@@ -1841,6 +2481,7 @@ TEST_F(WebSocketChannelStreamTest, SentFramesAreMasked) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
MASKED, "NEEDS MASKING"}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
.WillOnce(Return(OK));
@@ -1857,6 +2498,7 @@ TEST_F(WebSocketChannelStreamTest, NothingIsSentAfterClose) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
.WillOnce(Return(OK));
@@ -1877,6 +2519,7 @@ TEST_F(WebSocketChannelStreamTest, CloseIsEchoedBack) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Close")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -1901,11 +2544,14 @@ TEST_F(WebSocketChannelStreamTest, CloseOnlySentOnce) {
CompletionCallback read_callback;
ScopedVector<WebSocketFrame>* frames = NULL;
+ // These are not interesting.
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+
// Use a checkpoint to make the ordering of events clearer.
Checkpoint checkpoint;
{
InSequence s;
- EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(DoAll(SaveArg<0>(&frames),
SaveArg<1>(&read_callback),
@@ -1935,9 +2581,10 @@ TEST_F(WebSocketChannelStreamTest, CloseOnlySentOnce) {
TEST_F(WebSocketChannelStreamTest, InvalidCloseStatusCodeNotSent) {
static const InitFrame expected[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
- MASKED, CLOSE_DATA(SERVER_ERROR, "Internal Error")}};
+ MASKED, CLOSE_DATA(SERVER_ERROR, "")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(Return(ERR_IO_PENDING));
@@ -1952,9 +2599,10 @@ TEST_F(WebSocketChannelStreamTest, InvalidCloseStatusCodeNotSent) {
TEST_F(WebSocketChannelStreamTest, LongCloseReasonNotSent) {
static const InitFrame expected[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
- MASKED, CLOSE_DATA(SERVER_ERROR, "Internal Error")}};
+ MASKED, CLOSE_DATA(SERVER_ERROR, "")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(Return(ERR_IO_PENDING));
@@ -1975,6 +2623,7 @@ TEST_F(WebSocketChannelStreamTest, Code1005IsNotEchoed) {
static const InitFrame expected[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, ""}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -1990,6 +2639,7 @@ TEST_F(WebSocketChannelStreamTest, Code1005IsNotEchoedNull) {
static const InitFrame expected[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, ""}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -1999,6 +2649,28 @@ TEST_F(WebSocketChannelStreamTest, Code1005IsNotEchoedNull) {
CreateChannelAndConnectSuccessfully();
}
+// Receiving an invalid UTF-8 payload in a Close frame causes us to fail the
+// connection.
+TEST_F(WebSocketChannelStreamTest, CloseFrameInvalidUtf8) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
+ NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
+ MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in Close frame")}};
+
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close());
+
+ CreateChannelAndConnectSuccessfully();
+}
+
// RFC6455 5.5.2 "Upon receipt of a Ping frame, an endpoint MUST send a Pong
// frame in response"
// 5.5.3 "A Pong frame sent in response to a Ping frame must have identical
@@ -2012,6 +2684,7 @@ TEST_F(WebSocketChannelStreamTest, PingRepliedWithPong) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodePong,
MASKED, "Application data"}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -2021,14 +2694,15 @@ TEST_F(WebSocketChannelStreamTest, PingRepliedWithPong) {
CreateChannelAndConnectSuccessfully();
}
-// A ping with a NULL payload should be responded to with a Pong with an empty
+// A ping with a NULL payload should be responded to with a Pong with a NULL
// payload.
-TEST_F(WebSocketChannelStreamTest, NullPingRepliedWithEmptyPong) {
+TEST_F(WebSocketChannelStreamTest, NullPingRepliedWithNullPong) {
static const InitFrame frames[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, NULL}};
static const InitFrame expected[] = {
- {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, MASKED, ""}};
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, MASKED, NULL}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -2053,6 +2727,7 @@ TEST_F(WebSocketChannelStreamTest, PongInTheMiddleOfDataMessage) {
ScopedVector<WebSocketFrame>* read_frames;
CompletionCallback read_callback;
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(DoAll(SaveArg<0>(&read_frames),
SaveArg<1>(&read_callback),
@@ -2089,6 +2764,7 @@ TEST_F(WebSocketChannelStreamTest, WriteFramesOneAtATime) {
Checkpoint checkpoint;
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
{
InSequence s;
@@ -2127,6 +2803,7 @@ TEST_F(WebSocketChannelStreamTest, WaitingMessagesAreBatched) {
CompletionCallback write_callback;
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
{
InSequence s;
@@ -2145,17 +2822,14 @@ TEST_F(WebSocketChannelStreamTest, WaitingMessagesAreBatched) {
write_callback.Run(OK);
}
-// When the renderer sends more on a channel than it has quota for, then we send
-// a kWebSocketMuxErrorSendQuotaViolation status code (from the draft websocket
-// mux specification) back to the renderer. This should not be sent to the
-// remote server, which may not even implement the mux specification, and could
-// even be using a different extension which uses that code to mean something
-// else.
-TEST_F(WebSocketChannelStreamTest, MuxErrorIsNotSentToStream) {
+// When the renderer sends more on a channel than it has quota for, we send the
+// remote server a kWebSocketErrorGoingAway error code.
+TEST_F(WebSocketChannelStreamTest, SendGoingAwayOnRendererQuotaExceeded) {
static const InitFrame expected[] = {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
- MASKED, CLOSE_DATA(GOING_AWAY, "Internal Error")}};
+ MASKED, CLOSE_DATA(GOING_AWAY, "")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
.WillOnce(Return(OK));
@@ -2174,6 +2848,7 @@ TEST_F(WebSocketChannelStreamTest, WrittenBinaryFramesAre8BitClean) {
ScopedVector<WebSocketFrame>* frames = NULL;
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING));
EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
.WillOnce(DoAll(SaveArg<0>(&frames), Return(ERR_IO_PENDING)));
@@ -2207,7 +2882,7 @@ TEST_F(WebSocketChannelEventInterfaceTest, ReadBinaryFramesAre8BitClean) {
stream->PrepareRawReadFrames(
ReadableFakeWebSocketStream::SYNC, OK, frames.Pass());
set_stream(stream.Pass());
- EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _));
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
EXPECT_CALL(*event_interface_, OnFlowControl(_));
EXPECT_CALL(*event_interface_,
OnDataFrame(true,
@@ -2218,6 +2893,376 @@ TEST_F(WebSocketChannelEventInterfaceTest, ReadBinaryFramesAre8BitClean) {
CreateChannelAndConnectSuccessfully();
}
+// Invalid UTF-8 is not permitted in Text frames.
+TEST_F(WebSocketChannelSendUtf8Test, InvalidUtf8Rejected) {
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel("Browser sent a text frame containing invalid UTF-8"));
+
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ true, WebSocketFrameHeader::kOpCodeText, AsVector("\xff"));
+}
+
+// A Text message cannot end with a partial UTF-8 character.
+TEST_F(WebSocketChannelSendUtf8Test, IncompleteCharacterInFinalFrame) {
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel("Browser sent a text frame containing invalid UTF-8"));
+
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ true, WebSocketFrameHeader::kOpCodeText, AsVector("\xc2"));
+}
+
+// A non-final Text frame may end with a partial UTF-8 character (compare to
+// previous test).
+TEST_F(WebSocketChannelSendUtf8Test, IncompleteCharacterInNonFinalFrame) {
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeText, AsVector("\xc2"));
+}
+
+// UTF-8 parsing context must be retained between frames.
+TEST_F(WebSocketChannelSendUtf8Test, ValidCharacterSplitBetweenFrames) {
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeText, AsVector("\xf1"));
+ channel_->SendFrame(true,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("\x80\xa0\xbf"));
+}
+
+// Similarly, an invalid character should be detected even if split.
+TEST_F(WebSocketChannelSendUtf8Test, InvalidCharacterSplit) {
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel("Browser sent a text frame containing invalid UTF-8"));
+
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeText, AsVector("\xe1"));
+ channel_->SendFrame(true,
+ WebSocketFrameHeader::kOpCodeContinuation,
+ AsVector("\x80\xa0\xbf"));
+}
+
+// An invalid character must be detected in continuation frames.
+TEST_F(WebSocketChannelSendUtf8Test, InvalidByteInContinuation) {
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel("Browser sent a text frame containing invalid UTF-8"));
+
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeText, AsVector("foo"));
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("bar"));
+ channel_->SendFrame(
+ true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\xff"));
+}
+
+// However, continuation frames of a Binary frame will not be tested for UTF-8
+// validity.
+TEST_F(WebSocketChannelSendUtf8Test, BinaryContinuationNotChecked) {
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeBinary, AsVector("foo"));
+ channel_->SendFrame(
+ false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("bar"));
+ channel_->SendFrame(
+ true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\xff"));
+}
+
+// Multiple text messages can be validated without the validation state getting
+// confused.
+TEST_F(WebSocketChannelSendUtf8Test, ValidateMultipleTextMessages) {
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("foo"));
+ channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("bar"));
+}
+
+// UTF-8 validation is enforced on received Text frames.
+TEST_F(WebSocketChannelEventInterfaceTest, ReceivedInvalidUtf8) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xff"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+ EXPECT_CALL(*event_interface_,
+ OnFailChannel("Could not decode a text frame as UTF-8."));
+
+ CreateChannelAndConnectSuccessfully();
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+// Invalid UTF-8 is not sent over the network.
+TEST_F(WebSocketChannelStreamTest, InvalidUtf8TextFrameNotSent) {
+ static const InitFrame expected[] = {{FINAL_FRAME,
+ WebSocketFrameHeader::kOpCodeClose,
+ MASKED, CLOSE_DATA(GOING_AWAY, "")}};
+ EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+
+ CreateChannelAndConnectSuccessfully();
+
+ channel_->SendFrame(
+ true, WebSocketFrameHeader::kOpCodeText, AsVector("\xff"));
+}
+
+// The rest of the tests for receiving invalid UTF-8 test the communication with
+// the server. Since there is only one code path, it would be redundant to
+// perform the same tests on the EventInterface as well.
+
+// If invalid UTF-8 is received in a Text frame, the connection is failed.
+TEST_F(WebSocketChannelReceiveUtf8Test, InvalidTextFrameRejected) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xff"}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED,
+ CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}};
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+ }
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A received Text message is not permitted to end with a partial UTF-8
+// character.
+TEST_F(WebSocketChannelReceiveUtf8Test, IncompleteCharacterReceived) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED,
+ CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// However, a non-final Text frame may end with a partial UTF-8 character.
+TEST_F(WebSocketChannelReceiveUtf8Test, IncompleteCharacterIncompleteMessage) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// However, it will become an error if it is followed by an empty final frame.
+TEST_F(WebSocketChannelReceiveUtf8Test, TricksyIncompleteCharacter) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, ""}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED,
+ CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// UTF-8 parsing context must be retained between received frames of the same
+// message.
+TEST_F(WebSocketChannelReceiveUtf8Test, ReceivedParsingContextRetained) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xf1"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "\x80\xa0\xbf"}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// An invalid character must be detected even if split between frames.
+TEST_F(WebSocketChannelReceiveUtf8Test, SplitInvalidCharacterReceived) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xe1"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "\x80\xa0\xbf"}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED,
+ CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// An invalid character received in a continuation frame must be detected.
+TEST_F(WebSocketChannelReceiveUtf8Test, InvalidReceivedIncontinuation) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "foo"},
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "bar"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "\xff"}};
+ static const InitFrame expected[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED,
+ CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+ EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
+ .WillOnce(Return(OK));
+ EXPECT_CALL(*mock_stream_, Close()).Times(1);
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// Continuations of binary frames must not be tested for UTF-8 validity.
+TEST_F(WebSocketChannelReceiveUtf8Test, ReceivedBinaryNotUtf8Tested) {
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeBinary, NOT_MASKED, "foo"},
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "bar"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "\xff"}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// Multiple Text messages can be validated.
+TEST_F(WebSocketChannelReceiveUtf8Test, ValidateMultipleReceived) {
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "foo"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "bar"}};
+ EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
+ .WillOnce(ReturnFrames(&frames))
+ .WillRepeatedly(Return(ERR_IO_PENDING));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A new data message cannot start in the middle of another data message.
+TEST_F(WebSocketChannelEventInterfaceTest, BogusContinuation) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeBinary,
+ NOT_MASKED, "frame1"},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText,
+ NOT_MASKED, "frame2"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(
+ false, WebSocketFrameHeader::kOpCodeBinary, AsVector("frame1")));
+ EXPECT_CALL(
+ *event_interface_,
+ OnFailChannel(
+ "Received start of new message but previous message is unfinished."));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A new message cannot start with a Continuation frame.
+TEST_F(WebSocketChannelEventInterfaceTest, MessageStartingWithContinuation) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, "continuation"}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+ EXPECT_CALL(*event_interface_,
+ OnFailChannel("Received unexpected continuation frame."));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// A frame passed to the renderer must be either non-empty or have the final bit
+// set.
+TEST_F(WebSocketChannelEventInterfaceTest, DataFramesNonEmptyOrFinal) {
+ scoped_ptr<ReadableFakeWebSocketStream> stream(
+ new ReadableFakeWebSocketStream);
+ static const InitFrame frames[] = {
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, ""},
+ {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation,
+ NOT_MASKED, ""},
+ {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, ""}};
+ stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames);
+ set_stream(stream.Pass());
+
+ EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _));
+ EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota));
+ EXPECT_CALL(
+ *event_interface_,
+ OnDataFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("")));
+
+ CreateChannelAndConnectSuccessfully();
+}
+
+// Calls to OnSSLCertificateError() must be passed through to the event
+// interface with the correct URL attached.
+TEST_F(WebSocketChannelEventInterfaceTest, OnSSLCertificateErrorCalled) {
+ const GURL wss_url("wss://example.com/sslerror");
+ connect_data_.socket_url = wss_url;
+ const SSLInfo ssl_info;
+ const bool fatal = true;
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> fake_callbacks(
+ new FakeSSLErrorCallbacks);
+
+ EXPECT_CALL(*event_interface_,
+ OnSSLCertificateErrorCalled(NotNull(), wss_url, _, fatal));
+
+ CreateChannelAndConnect();
+ connect_data_.creator.connect_delegate->OnSSLCertificateError(
+ fake_callbacks.Pass(), ssl_info, fatal);
+}
+
// If we receive another frame after Close, it is not valid. It is not
// completely clear what behaviour is required from the standard in this case,
// but the current implementation fails the connection. Since a Close has
@@ -2232,6 +3277,7 @@ TEST_F(WebSocketChannelStreamTest, PingAfterCloseIsRejected) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -2256,6 +3302,7 @@ TEST_F(WebSocketChannelStreamTest, ProtocolError) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(PROTOCOL_ERROR, "WebSocket Protocol Error")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(Return(ERR_WS_PROTOCOL_ERROR));
EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _))
@@ -2273,6 +3320,7 @@ class WebSocketChannelStreamTimeoutTest : public WebSocketChannelStreamTest {
virtual void CreateChannelAndConnectSuccessfully() OVERRIDE {
set_stream(mock_stream_.Pass());
CreateChannelAndConnect();
+ channel_->SendFlowControl(kPlentyOfQuota);
channel_->SetClosingHandshakeTimeoutForTesting(
TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis));
connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass());
@@ -2292,6 +3340,7 @@ TEST_F(WebSocketChannelStreamTimeoutTest, ServerInitiatedCloseTimesOut) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillOnce(ReturnFrames(&frames))
.WillRepeatedly(Return(ERR_IO_PENDING));
@@ -2320,6 +3369,7 @@ TEST_F(WebSocketChannelStreamTimeoutTest, ClientInitiatedCloseTimesOut) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
EXPECT_CALL(*mock_stream_, ReadFrames(_, _))
.WillRepeatedly(Return(ERR_IO_PENDING));
TestClosure completion;
@@ -2348,6 +3398,7 @@ TEST_F(WebSocketChannelStreamTimeoutTest, ConnectionCloseTimesOut) {
{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose,
NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}};
EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber());
+ EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber());
TestClosure completion;
ScopedVector<WebSocketFrame>* read_frames = NULL;
CompletionCallback read_callback;
diff --git a/chromium/net/websockets/websocket_deflate_stream.cc b/chromium/net/websockets/websocket_deflate_stream.cc
index 601670d373b..38de5fa2eca 100644
--- a/chromium/net/websockets/websocket_deflate_stream.cc
+++ b/chromium/net/websockets/websocket_deflate_stream.cc
@@ -36,6 +36,7 @@ const size_t kChunkSize = 4 * 1024;
WebSocketDeflateStream::WebSocketDeflateStream(
scoped_ptr<WebSocketStream> stream,
WebSocketDeflater::ContextTakeOverMode mode,
+ int client_window_bits,
scoped_ptr<WebSocketDeflatePredictor> predictor)
: stream_(stream.Pass()),
deflater_(mode),
@@ -46,7 +47,9 @@ WebSocketDeflateStream::WebSocketDeflateStream(
current_writing_opcode_(WebSocketFrameHeader::kOpCodeText),
predictor_(predictor.Pass()) {
DCHECK(stream_);
- deflater_.Initialize(kWindowBits);
+ DCHECK_GE(client_window_bits, 8);
+ DCHECK_LE(client_window_bits, 15);
+ deflater_.Initialize(client_window_bits);
inflater_.Initialize(kWindowBits);
}
@@ -54,16 +57,18 @@ WebSocketDeflateStream::~WebSocketDeflateStream() {}
int WebSocketDeflateStream::ReadFrames(ScopedVector<WebSocketFrame>* frames,
const CompletionCallback& callback) {
- CompletionCallback callback_to_pass =
+ int result = stream_->ReadFrames(
+ frames,
base::Bind(&WebSocketDeflateStream::OnReadComplete,
base::Unretained(this),
base::Unretained(frames),
- callback);
- int result = stream_->ReadFrames(frames, callback_to_pass);
+ callback));
if (result < 0)
return result;
DCHECK_EQ(OK, result);
- return InflateAndReadIfNecessary(frames, callback_to_pass);
+ DCHECK(!frames->empty());
+
+ return InflateAndReadIfNecessary(frames, callback);
}
int WebSocketDeflateStream::WriteFrames(ScopedVector<WebSocketFrame>* frames,
@@ -274,6 +279,11 @@ int WebSocketDeflateStream::Inflate(ScopedVector<WebSocketFrame>* frames) {
for (size_t i = 0; i < frames_passed.size(); ++i) {
scoped_ptr<WebSocketFrame> frame(frames_passed[i]);
frames_passed[i] = NULL;
+ DVLOG(3) << "Input frame: opcode=" << frame->header.opcode
+ << " final=" << frame->header.final
+ << " reserved1=" << frame->header.reserved1
+ << " payload_length=" << frame->header.payload_length;
+
if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) {
frames_to_output.push_back(frame.release());
continue;
@@ -323,9 +333,7 @@ int WebSocketDeflateStream::Inflate(ScopedVector<WebSocketFrame>* frames) {
scoped_ptr<WebSocketFrame> inflated(
new WebSocketFrame(WebSocketFrameHeader::kOpCodeText));
scoped_refptr<IOBufferWithSize> data = inflater_.GetOutput(size);
- bool is_final = !inflater_.CurrentOutputSize();
- // |is_final| can't be true if |frame->header.final| is false.
- DCHECK(!(is_final && !frame->header.final));
+ bool is_final = !inflater_.CurrentOutputSize() && frame->header.final;
if (!data) {
DVLOG(1) << "WebSocket protocol error. "
<< "inflater_.GetOutput() returns an error.";
@@ -337,7 +345,10 @@ int WebSocketDeflateStream::Inflate(ScopedVector<WebSocketFrame>* frames) {
inflated->header.reserved1 = false;
inflated->data = data;
inflated->header.payload_length = data->size();
-
+ DVLOG(3) << "Inflated frame: opcode=" << inflated->header.opcode
+ << " final=" << inflated->header.final
+ << " reserved1=" << inflated->header.reserved1
+ << " payload_length=" << inflated->header.payload_length;
frames_to_output.push_back(inflated.release());
current_reading_opcode_ = WebSocketFrameHeader::kOpCodeContinuation;
if (is_final)
@@ -357,11 +368,18 @@ int WebSocketDeflateStream::InflateAndReadIfNecessary(
int result = Inflate(frames);
while (result == ERR_IO_PENDING) {
DCHECK(frames->empty());
- result = stream_->ReadFrames(frames, callback);
+
+ result = stream_->ReadFrames(
+ frames,
+ base::Bind(&WebSocketDeflateStream::OnReadComplete,
+ base::Unretained(this),
+ base::Unretained(frames),
+ callback));
if (result < 0)
break;
DCHECK_EQ(OK, result);
DCHECK(!frames->empty());
+
result = Inflate(frames);
}
if (result < 0)
diff --git a/chromium/net/websockets/websocket_deflate_stream.h b/chromium/net/websockets/websocket_deflate_stream.h
index a7859446f6a..39ac2dfa256 100644
--- a/chromium/net/websockets/websocket_deflate_stream.h
+++ b/chromium/net/websockets/websocket_deflate_stream.h
@@ -41,6 +41,7 @@ class NET_EXPORT_PRIVATE WebSocketDeflateStream : public WebSocketStream {
public:
WebSocketDeflateStream(scoped_ptr<WebSocketStream> stream,
WebSocketDeflater::ContextTakeOverMode mode,
+ int client_window_bits,
scoped_ptr<WebSocketDeflatePredictor> predictor);
virtual ~WebSocketDeflateStream();
@@ -67,6 +68,7 @@ class NET_EXPORT_PRIVATE WebSocketDeflateStream : public WebSocketStream {
NOT_WRITING,
};
+ // Handles asynchronous completion of ReadFrames() call on |stream_|.
void OnReadComplete(ScopedVector<WebSocketFrame>* frames,
const CompletionCallback& callback,
int result);
diff --git a/chromium/net/websockets/websocket_deflate_stream_test.cc b/chromium/net/websockets/websocket_deflate_stream_test.cc
index 1775962dce1..a8b4e59c32b 100644
--- a/chromium/net/websockets/websocket_deflate_stream_test.cc
+++ b/chromium/net/websockets/websocket_deflate_stream_test.cc
@@ -209,17 +209,27 @@ class WebSocketDeflatePredictorMock : public WebSocketDeflatePredictor {
class WebSocketDeflateStreamTest : public ::testing::Test {
public:
WebSocketDeflateStreamTest()
- : mock_stream_(NULL) {
+ : mock_stream_(NULL),
+ predictor_(NULL) {}
+ virtual ~WebSocketDeflateStreamTest() {}
+
+ virtual void SetUp() {
+ Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, kWindowBits);
+ }
+
+ protected:
+ // Initialize deflate_stream_ with the given parameters.
+ void Initialize(WebSocketDeflater::ContextTakeOverMode mode,
+ int window_bits) {
mock_stream_ = new testing::StrictMock<MockWebSocketStream>;
predictor_ = new WebSocketDeflatePredictorMock;
deflate_stream_.reset(new WebSocketDeflateStream(
scoped_ptr<WebSocketStream>(mock_stream_),
- WebSocketDeflater::TAKE_OVER_CONTEXT,
+ mode,
+ window_bits,
scoped_ptr<WebSocketDeflatePredictor>(predictor_)));
}
- virtual ~WebSocketDeflateStreamTest() {}
- protected:
scoped_ptr<WebSocketDeflateStream> deflate_stream_;
// Owned by |deflate_stream_|.
MockWebSocketStream* mock_stream_;
@@ -231,25 +241,41 @@ class WebSocketDeflateStreamTest : public ::testing::Test {
// websocket_deflater_test.cc, we have only a few tests for this configuration
// here.
class WebSocketDeflateStreamWithDoNotTakeOverContextTest
- : public ::testing::Test {
+ : public WebSocketDeflateStreamTest {
public:
- WebSocketDeflateStreamWithDoNotTakeOverContextTest()
- : mock_stream_(NULL) {
- mock_stream_ = new testing::StrictMock<MockWebSocketStream>;
- predictor_ = new WebSocketDeflatePredictorMock;
- deflate_stream_.reset(new WebSocketDeflateStream(
- scoped_ptr<WebSocketStream>(mock_stream_),
- WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT,
- scoped_ptr<WebSocketDeflatePredictor>(predictor_)));
- }
+ WebSocketDeflateStreamWithDoNotTakeOverContextTest() {}
virtual ~WebSocketDeflateStreamWithDoNotTakeOverContextTest() {}
+ virtual void SetUp() {
+ Initialize(WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT, kWindowBits);
+ }
+};
+
+class WebSocketDeflateStreamWithClientWindowBitsTest
+ : public WebSocketDeflateStreamTest {
+ public:
+ WebSocketDeflateStreamWithClientWindowBitsTest() {}
+ virtual ~WebSocketDeflateStreamWithClientWindowBitsTest() {}
+
+ // Overridden to postpone the call to Initialize().
+ virtual void SetUp() {}
+
+ // This needs to be called explicitly from the tests.
+ void SetUpWithWindowBits(int window_bits) {
+ Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, window_bits);
+ }
+
+ // Add a frame which will be compressed to a smaller size if the window
+ // size is large enough.
+ void AddCompressibleFrameString() {
+ const std::string word = "Chromium";
+ const std::string payload = word + std::string(256, 'a') + word;
+ AppendTo(&frames_, WebSocketFrameHeader::kOpCodeText, kFinal, payload);
+ predictor_->AddFramesToBeInput(frames_);
+ }
+
protected:
- scoped_ptr<WebSocketDeflateStream> deflate_stream_;
- // |mock_stream_| will be deleted when |deflate_stream_| is destroyed.
- MockWebSocketStream* mock_stream_;
- // |predictor_| will be deleted when |deflate_stream_| is destroyed.
- WebSocketDeflatePredictorMock* predictor_;
+ ScopedVector<WebSocketFrame> frames_;
};
// ReadFrameStub is a stub for WebSocketStream::ReadFrames.
@@ -707,6 +733,47 @@ TEST_F(WebSocketDeflateStreamTest, SplitToMultipleFramesInReadFrames) {
ToString(frames[0]) + ToString(frames[1]) + ToString(frames[2]));
}
+TEST_F(WebSocketDeflateStreamTest, InflaterInternalDataCanBeEmpty) {
+ WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
+ deflater.Initialize(kWindowBits);
+ const std::string original_data(kChunkSize, 'a');
+ deflater.AddBytes(original_data.data(), original_data.size());
+ deflater.Finish();
+
+ ScopedVector<WebSocketFrame> frames_to_output;
+ AppendTo(&frames_to_output,
+ WebSocketFrameHeader::kOpCodeBinary,
+ kReserved1,
+ ToString(deflater.GetOutput(deflater.CurrentOutputSize())));
+ AppendTo(&frames_to_output,
+ WebSocketFrameHeader::kOpCodeBinary,
+ kFinal,
+ "");
+
+ ReadFramesStub stub(OK, &frames_to_output);
+ CompletionCallback callback;
+ ScopedVector<WebSocketFrame> frames;
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
+ .WillOnce(Invoke(&stub, &ReadFramesStub::Call));
+ }
+
+ ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback));
+ ASSERT_EQ(2u, frames.size());
+ EXPECT_EQ(WebSocketFrameHeader::kOpCodeBinary, frames[0]->header.opcode);
+ EXPECT_FALSE(frames[0]->header.final);
+ EXPECT_FALSE(frames[0]->header.reserved1);
+ EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[0]->header.payload_length));
+
+ EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
+ frames[1]->header.opcode);
+ EXPECT_TRUE(frames[1]->header.final);
+ EXPECT_FALSE(frames[1]->header.reserved1);
+ EXPECT_EQ(0u, static_cast<size_t>(frames[1]->header.payload_length));
+ EXPECT_EQ(original_data, ToString(frames[0]) + ToString(frames[1]));
+}
+
TEST_F(WebSocketDeflateStreamTest,
Reserved1TurnsOnDuringReadingCompressedContinuationFrame) {
const std::string data1("\xf2\x48\xcd", 3);
@@ -886,6 +953,43 @@ TEST_F(WebSocketDeflateStreamTest,
EXPECT_EQ("compressed", ToString(frames[1]));
}
+// This is a regression test for crbug.com/343506.
+TEST_F(WebSocketDeflateStreamTest, ReadEmptyAsyncFrame) {
+ ScopedVector<ReadFramesStub> stub_vector;
+ stub_vector.push_back(new ReadFramesStub(ERR_IO_PENDING));
+ stub_vector.push_back(new ReadFramesStub(ERR_IO_PENDING));
+ MockCallback mock_callback;
+ CompletionCallback callback =
+ base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
+ ScopedVector<WebSocketFrame> frames;
+
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
+ .WillOnce(Invoke(stub_vector[0], &ReadFramesStub::Call));
+
+ EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
+ .WillOnce(Invoke(stub_vector[1], &ReadFramesStub::Call));
+
+ EXPECT_CALL(mock_callback, Call(OK));
+ }
+
+ ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
+ AppendTo(stub_vector[0]->frames_passed(),
+ WebSocketFrameHeader::kOpCodeText,
+ kReserved1,
+ std::string());
+ stub_vector[0]->callback().Run(OK);
+ AppendTo(stub_vector[1]->frames_passed(),
+ WebSocketFrameHeader::kOpCodeContinuation,
+ kFinal,
+ std::string("\x02\x00"));
+ stub_vector[1]->callback().Run(OK);
+ ASSERT_EQ(1u, frames.size());
+ EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
+ EXPECT_EQ("", ToString(frames[0]));
+}
+
TEST_F(WebSocketDeflateStreamTest, WriteEmpty) {
ScopedVector<WebSocketFrame> frames;
CompletionCallback callback;
@@ -1006,7 +1110,7 @@ TEST_F(WebSocketDeflateStreamTest, WriteEmptyMessage) {
EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode);
EXPECT_TRUE(frames_passed[0]->header.final);
EXPECT_TRUE(frames_passed[0]->header.reserved1);
- EXPECT_EQ(std::string("\x02\x00", 2), ToString(frames_passed[0]));
+ EXPECT_EQ(std::string("\x00", 1), ToString(frames_passed[0]));
}
TEST_F(WebSocketDeflateStreamTest, WriteUncompressedMessage) {
@@ -1201,6 +1305,44 @@ TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest,
EXPECT_EQ("YY", ToString(frames_passed[4]));
}
+// This is based on the similar test from websocket_deflater_test.cc
+TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits8) {
+ SetUpWithWindowBits(8);
+ CompletionCallback callback;
+ AddCompressibleFrameString();
+ WriteFramesStub stub(predictor_, OK);
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
+ .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
+ }
+ ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames_, callback));
+ const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames();
+ ASSERT_EQ(1u, frames_passed.size());
+ EXPECT_EQ(std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x39\xa3"
+ "(?7\xb3\x34\x17\x00", 21),
+ ToString(frames_passed[0]));
+}
+
+// The same input with window_bits=10 returns smaller output.
+TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits10) {
+ SetUpWithWindowBits(10);
+ CompletionCallback callback;
+ AddCompressibleFrameString();
+ WriteFramesStub stub(predictor_, OK);
+ {
+ InSequence s;
+ EXPECT_CALL(*mock_stream_, WriteFrames(_, _))
+ .WillOnce(Invoke(&stub, &WriteFramesStub::Call));
+ }
+ ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames_, callback));
+ const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames();
+ ASSERT_EQ(1u, frames_passed.size());
+ EXPECT_EQ(
+ std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x19\x1a\x0e\0\0", 17),
+ ToString(frames_passed[0]));
+}
+
} // namespace
} // namespace net
diff --git a/chromium/net/websockets/websocket_deflater.cc b/chromium/net/websockets/websocket_deflater.cc
index 41d13e86870..a4c56bccb19 100644
--- a/chromium/net/websockets/websocket_deflater.cc
+++ b/chromium/net/websockets/websocket_deflater.cc
@@ -66,7 +66,6 @@ bool WebSocketDeflater::Finish() {
// Since consecutive calls of deflate with Z_SYNC_FLUSH and no input
// lead to an error, we create and return the output for the empty input
// manually.
- buffer_.push_back('\x02');
buffer_.push_back('\x00');
ResetContext();
return true;
diff --git a/chromium/net/websockets/websocket_deflater.h b/chromium/net/websockets/websocket_deflater.h
index da85bfec912..1b631e2663e 100644
--- a/chromium/net/websockets/websocket_deflater.h
+++ b/chromium/net/websockets/websocket_deflater.h
@@ -21,9 +21,12 @@ class IOBufferWithSize;
class NET_EXPORT_PRIVATE WebSocketDeflater {
public:
+ // Do not reorder or remove entries of this enum. The values of them are used
+ // in UMA.
enum ContextTakeOverMode {
DO_NOT_TAKE_OVER_CONTEXT,
TAKE_OVER_CONTEXT,
+ NUM_CONTEXT_TAKEOVER_MODE_TYPES,
};
explicit WebSocketDeflater(ContextTakeOverMode mode);
diff --git a/chromium/net/websockets/websocket_deflater_test.cc b/chromium/net/websockets/websocket_deflater_test.cc
index 03b8a3d7c52..ae0133c6424 100644
--- a/chromium/net/websockets/websocket_deflater_test.cc
+++ b/chromium/net/websockets/websocket_deflater_test.cc
@@ -25,7 +25,7 @@ TEST(WebSocketDeflaterTest, Construct) {
ASSERT_TRUE(deflater.Finish());
scoped_refptr<IOBufferWithSize> actual =
deflater.GetOutput(deflater.CurrentOutputSize());
- EXPECT_EQ(std::string("\x02\00", 2), ToString(actual.get()));
+ EXPECT_EQ(std::string("\00", 1), ToString(actual.get()));
ASSERT_EQ(0u, deflater.CurrentOutputSize());
}
@@ -93,8 +93,8 @@ TEST(WebSocketDeflaterTest, GetMultipleDeflatedOutput) {
actual = deflater.GetOutput(deflater.CurrentOutputSize());
EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00\x00\x00\xff\xff"
- "\x02\x00\x00\x00\xff\xff"
- "\xf2\x00\x11\x00\x00", 22),
+ "\x00\x00\x00\xff\xff"
+ "\xf2\x00\x11\x00\x00", 21),
ToString(actual.get()));
ASSERT_EQ(0u, deflater.CurrentOutputSize());
}
diff --git a/chromium/net/websockets/websocket_event_interface.h b/chromium/net/websockets/websocket_event_interface.h
index baba88ce012..d32a7c131cf 100644
--- a/chromium/net/websockets/websocket_event_interface.h
+++ b/chromium/net/websockets/websocket_event_interface.h
@@ -12,8 +12,14 @@
#include "base/compiler_specific.h" // for WARN_UNUSED_RESULT
#include "net/base/net_export.h"
+class GURL;
+
namespace net {
+class SSLInfo;
+struct WebSocketHandshakeRequestInfo;
+struct WebSocketHandshakeResponseInfo;
+
// Interface for events sent from the network layer to the content layer. These
// events will generally be sent as-is to the renderer process.
class NET_EXPORT WebSocketEventInterface {
@@ -29,13 +35,15 @@ class NET_EXPORT WebSocketEventInterface {
};
virtual ~WebSocketEventInterface() {}
+
// Called in response to an AddChannelRequest. This generally means that a
// response has been received from the remote server, but the response might
// have been generated internally. If |fail| is true, the channel cannot be
// used and should be deleted, returning CHANNEL_DELETED.
virtual ChannelState OnAddChannelResponse(
bool fail,
- const std::string& selected_subprotocol) WARN_UNUSED_RESULT = 0;
+ const std::string& selected_subprotocol,
+ const std::string& extensions) WARN_UNUSED_RESULT = 0;
// Called when a data frame has been received from the remote host and needs
// to be forwarded to the renderer process.
@@ -63,14 +71,63 @@ class NET_EXPORT WebSocketEventInterface {
// callers must take care not to provide details that could be useful to
// attackers attempting to use WebSockets to probe networks.
//
+ // |was_clean| should be true if the closing handshake completed successfully.
+ //
// The channel should not be used again after OnDropChannel() has been
// called.
//
// This method returns a ChannelState for consistency, but all implementations
// must delete the Channel and return CHANNEL_DELETED.
- virtual ChannelState OnDropChannel(uint16 code, const std::string& reason)
+ virtual ChannelState OnDropChannel(bool was_clean,
+ uint16 code,
+ const std::string& reason)
WARN_UNUSED_RESULT = 0;
+ // Called when the browser fails the channel, as specified in the spec.
+ //
+ // The channel should not be used again after OnFailChannel() has been
+ // called.
+ //
+ // This method returns a ChannelState for consistency, but all implementations
+ // must delete the Channel and return CHANNEL_DELETED.
+ virtual ChannelState OnFailChannel(const std::string& message)
+ WARN_UNUSED_RESULT = 0;
+
+ // Called when the browser starts the WebSocket Opening Handshake.
+ virtual ChannelState OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) WARN_UNUSED_RESULT = 0;
+
+ // Called when the browser finishes the WebSocket Opening Handshake.
+ virtual ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response)
+ WARN_UNUSED_RESULT = 0;
+
+ // Callbacks to be used in response to a call to OnSSLCertificateError. Very
+ // similar to content::SSLErrorHandler::Delegate (which we can't use directly
+ // due to layering constraints).
+ class NET_EXPORT SSLErrorCallbacks {
+ public:
+ virtual ~SSLErrorCallbacks() {}
+
+ // Cancels the SSL response in response to the error.
+ virtual void CancelSSLRequest(int error, const SSLInfo* ssl_info) = 0;
+
+ // Continue with the SSL connection despite the error.
+ virtual void ContinueSSLRequest() = 0;
+ };
+
+ // Called on SSL Certificate Error during the SSL handshake. Should result in
+ // a call to either ssl_error_callbacks->ContinueSSLRequest() or
+ // ssl_error_callbacks->CancelSSLRequest(). Normally the implementation of
+ // this method will delegate to content::SSLManager::OnSSLCertificateError to
+ // make the actual decision. The callbacks must not be called after the
+ // WebSocketChannel has been destroyed.
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
+ const GURL& url,
+ const SSLInfo& ssl_info,
+ bool fatal) WARN_UNUSED_RESULT = 0;
+
protected:
WebSocketEventInterface() {}
diff --git a/chromium/net/websockets/websocket_frame.cc b/chromium/net/websockets/websocket_frame.cc
index 763712a6f57..6fe972ba4a8 100644
--- a/chromium/net/websockets/websocket_frame.cc
+++ b/chromium/net/websockets/websocket_frame.cc
@@ -7,9 +7,9 @@
#include <algorithm>
#include "base/basictypes.h"
+#include "base/big_endian.h"
#include "base/logging.h"
#include "base/rand_util.h"
-#include "net/base/big_endian.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -131,10 +131,10 @@ int WriteWebSocketFrameHeader(const WebSocketFrameHeader& header,
// Writes "extended payload length" field.
if (extended_length_size == 2) {
uint16 payload_length_16 = static_cast<uint16>(header.payload_length);
- WriteBigEndian(buffer + buffer_index, payload_length_16);
+ base::WriteBigEndian(buffer + buffer_index, payload_length_16);
buffer_index += sizeof(payload_length_16);
} else if (extended_length_size == 8) {
- WriteBigEndian(buffer + buffer_index, header.payload_length);
+ base::WriteBigEndian(buffer + buffer_index, header.payload_length);
buffer_index += sizeof(header.payload_length);
}
diff --git a/chromium/net/websockets/websocket_frame_parser.cc b/chromium/net/websockets/websocket_frame_parser.cc
index 3b199128b42..2e4c58fe302 100644
--- a/chromium/net/websockets/websocket_frame_parser.cc
+++ b/chromium/net/websockets/websocket_frame_parser.cc
@@ -8,11 +8,11 @@
#include <limits>
#include "base/basictypes.h"
+#include "base/big_endian.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
-#include "net/base/big_endian.h"
#include "net/base/io_buffer.h"
#include "net/websockets/websocket_frame.h"
@@ -124,7 +124,7 @@ void WebSocketFrameParser::DecodeFrameHeader() {
if (end - current < 2)
return;
uint16 payload_length_16;
- ReadBigEndian(current, &payload_length_16);
+ base::ReadBigEndian(current, &payload_length_16);
current += 2;
payload_length = payload_length_16;
if (payload_length <= kMaxPayloadLengthWithoutExtendedLengthField)
@@ -132,7 +132,7 @@ void WebSocketFrameParser::DecodeFrameHeader() {
} else if (payload_length == kPayloadLengthWithEightByteExtendedLengthField) {
if (end - current < 8)
return;
- ReadBigEndian(current, &payload_length);
+ base::ReadBigEndian(current, &payload_length);
current += 8;
if (payload_length <= kuint16max ||
payload_length > static_cast<uint64>(kint64max)) {
diff --git a/chromium/net/websockets/websocket_frame_test.cc b/chromium/net/websockets/websocket_frame_test.cc
index 97fac03e12e..b37dbb33001 100644
--- a/chromium/net/websockets/websocket_frame_test.cc
+++ b/chromium/net/websockets/websocket_frame_test.cc
@@ -308,7 +308,7 @@ TEST(WebSocketFrameTest, MaskPayloadAlignment) {
};
COMPILE_ASSERT(arraysize(kTestInput) == arraysize(kTestOutput),
output_and_input_arrays_have_the_same_length);
- scoped_ptr_malloc<char, base::ScopedPtrAlignedFree> scratch(
+ scoped_ptr<char, base::AlignedFreeDeleter> scratch(
static_cast<char*>(
base::AlignedAlloc(kScratchBufferSize, kMaxVectorAlignment)));
WebSocketMaskingKey masking_key;
@@ -348,7 +348,7 @@ class WebSocketFrameTestMaskBenchmark : public testing::Test {
virtual void SetUp() {
std::string iterations(
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kBenchmarkIterations));
int benchmark_iterations = 0;
if (!iterations.empty() &&
diff --git a/chromium/net/websockets/websocket_handshake_handler_spdy_test.cc b/chromium/net/websockets/websocket_handshake_handler_spdy_test.cc
index a825dcdfede..064bdcfb360 100644
--- a/chromium/net/websockets/websocket_handshake_handler_spdy_test.cc
+++ b/chromium/net/websockets/websocket_handshake_handler_spdy_test.cc
@@ -29,8 +29,7 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
WebSocketHandshakeHandlerSpdyTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(WebSocketHandshakeHandlerSpdyTest, RequestResponse) {
WebSocketHandshakeRequestHandler request_handler;
diff --git a/chromium/net/websockets/websocket_handshake_request_info.cc b/chromium/net/websockets/websocket_handshake_request_info.cc
new file mode 100644
index 00000000000..7acd4d0433c
--- /dev/null
+++ b/chromium/net/websockets/websocket_handshake_request_info.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 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_handshake_request_info.h"
+
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace net {
+
+WebSocketHandshakeRequestInfo::WebSocketHandshakeRequestInfo(
+ const GURL& url,
+ base::Time request_time)
+ : url(url), request_time(request_time) {}
+
+WebSocketHandshakeRequestInfo::~WebSocketHandshakeRequestInfo() {}
+
+} // namespace net
diff --git a/chromium/net/websockets/websocket_handshake_request_info.h b/chromium/net/websockets/websocket_handshake_request_info.h
new file mode 100644
index 00000000000..e5ef3369b2a
--- /dev/null
+++ b/chromium/net/websockets/websocket_handshake_request_info.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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_HANDSHAKE_REQUEST_INFO_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_REQUEST_INFO_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/http/http_request_headers.h"
+#include "url/gurl.h"
+
+namespace net {
+
+struct NET_EXPORT WebSocketHandshakeRequestInfo {
+ WebSocketHandshakeRequestInfo(const GURL& url, base::Time request_time);
+ ~WebSocketHandshakeRequestInfo();
+ // The request URL
+ GURL url;
+ // HTTP request headers
+ HttpRequestHeaders headers;
+ // The time that this request is sent
+ base::Time request_time;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeRequestInfo);
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_REQUEST_INFO_H_
diff --git a/chromium/net/websockets/websocket_handshake_response_info.cc b/chromium/net/websockets/websocket_handshake_response_info.cc
new file mode 100644
index 00000000000..b9588b63e3f
--- /dev/null
+++ b/chromium/net/websockets/websocket_handshake_response_info.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 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_handshake_response_info.h"
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "net/http/http_response_headers.h"
+#include "url/gurl.h"
+
+namespace net {
+
+WebSocketHandshakeResponseInfo::WebSocketHandshakeResponseInfo(
+ const GURL& url,
+ int status_code,
+ const std::string& status_text,
+ scoped_refptr<HttpResponseHeaders> headers,
+ base::Time response_time)
+ : url(url),
+ status_code(status_code),
+ status_text(status_text),
+ headers(headers),
+ response_time(response_time) {}
+
+WebSocketHandshakeResponseInfo::~WebSocketHandshakeResponseInfo() {}
+
+} // namespace net
diff --git a/chromium/net/websockets/websocket_handshake_response_info.h b/chromium/net/websockets/websocket_handshake_response_info.h
new file mode 100644
index 00000000000..66aff4417b7
--- /dev/null
+++ b/chromium/net/websockets/websocket_handshake_response_info.h
@@ -0,0 +1,43 @@
+// Copyright 2014 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_HANDSHAKE_RESPONSE_INFO_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_RESPONSE_INFO_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "url/gurl.h"
+
+namespace net {
+
+class HttpResponseHeaders;
+
+struct NET_EXPORT WebSocketHandshakeResponseInfo {
+ WebSocketHandshakeResponseInfo(const GURL& url,
+ int status_code,
+ const std::string& status_text,
+ scoped_refptr<HttpResponseHeaders> headers,
+ base::Time response_time);
+ ~WebSocketHandshakeResponseInfo();
+ // The request URL
+ GURL url;
+ // HTTP status code
+ int status_code;
+ // HTTP status text
+ std::string status_text;
+ // HTTP response headers
+ scoped_refptr<HttpResponseHeaders> headers;
+ // The time that this response arrived
+ base::Time response_time;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeResponseInfo);
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_RESPONSE_INFO_H_
diff --git a/chromium/net/websockets/websocket_handshake_stream_base.h b/chromium/net/websockets/websocket_handshake_stream_base.h
index 71d8321824f..8208c0e742f 100644
--- a/chromium/net/websockets/websocket_handshake_stream_base.h
+++ b/chromium/net/websockets/websocket_handshake_stream_base.h
@@ -9,6 +9,8 @@
// Since net/http can be built without linking net/websockets code,
// this file must not introduce any link-time dependencies on websockets.
+#include <string>
+
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
diff --git a/chromium/net/websockets/websocket_handshake_stream_create_helper.cc b/chromium/net/websockets/websocket_handshake_stream_create_helper.cc
index 8f1060c4868..e68052ef102 100644
--- a/chromium/net/websockets/websocket_handshake_stream_create_helper.cc
+++ b/chromium/net/websockets/websocket_handshake_stream_create_helper.cc
@@ -14,9 +14,14 @@
namespace net {
WebSocketHandshakeStreamCreateHelper::WebSocketHandshakeStreamCreateHelper(
+ WebSocketStream::ConnectDelegate* connect_delegate,
const std::vector<std::string>& requested_subprotocols)
: requested_subprotocols_(requested_subprotocols),
- stream_(NULL) {}
+ stream_(NULL),
+ connect_delegate_(connect_delegate),
+ failure_message_(NULL) {
+ DCHECK(connect_delegate_);
+}
WebSocketHandshakeStreamCreateHelper::~WebSocketHandshakeStreamCreateHelper() {}
@@ -24,11 +29,18 @@ WebSocketHandshakeStreamBase*
WebSocketHandshakeStreamCreateHelper::CreateBasicStream(
scoped_ptr<ClientSocketHandle> connection,
bool using_proxy) {
- return stream_ =
- new WebSocketBasicHandshakeStream(connection.Pass(),
- using_proxy,
- requested_subprotocols_,
- std::vector<std::string>());
+ DCHECK(failure_message_) << "set_failure_message() must be called";
+ // The list of supported extensions and parameters is hard-coded.
+ // TODO(ricea): If more extensions are added, consider a more flexible
+ // method.
+ std::vector<std::string> extensions(
+ 1, "permessage-deflate; client_max_window_bits");
+ return stream_ = new WebSocketBasicHandshakeStream(connection.Pass(),
+ connect_delegate_,
+ using_proxy,
+ requested_subprotocols_,
+ extensions,
+ failure_message_);
}
// TODO(ricea): Create a WebSocketSpdyHandshakeStream. crbug.com/323852
diff --git a/chromium/net/websockets/websocket_handshake_stream_create_helper.h b/chromium/net/websockets/websocket_handshake_stream_create_helper.h
index 31be2313ff7..648f8fd23fe 100644
--- a/chromium/net/websockets/websocket_handshake_stream_create_helper.h
+++ b/chromium/net/websockets/websocket_handshake_stream_create_helper.h
@@ -10,6 +10,7 @@
#include "net/base/net_export.h"
#include "net/websockets/websocket_handshake_stream_base.h"
+#include "net/websockets/websocket_stream.h"
namespace net {
@@ -22,7 +23,9 @@ namespace net {
class NET_EXPORT_PRIVATE WebSocketHandshakeStreamCreateHelper
: public WebSocketHandshakeStreamBase::CreateHelper {
public:
+ // |connect_delegate| must out-live this object.
explicit WebSocketHandshakeStreamCreateHelper(
+ WebSocketStream::ConnectDelegate* connect_delegate,
const std::vector<std::string>& requested_subprotocols);
virtual ~WebSocketHandshakeStreamCreateHelper();
@@ -42,17 +45,30 @@ class NET_EXPORT_PRIVATE WebSocketHandshakeStreamCreateHelper
// Return the WebSocketHandshakeStreamBase object that we created. In the case
// where CreateBasicStream() was called more than once, returns the most
// recent stream, which will be the one on which the handshake succeeded.
+ // It is not safe to call this if the handshake failed.
WebSocketHandshakeStreamBase* stream() { return stream_; }
+ // Set a pointer to the std::string into which to write any failure messages
+ // that are encountered. This method must be called before CreateBasicStream()
+ // or CreateSpdyStream(). The |failure_message| pointer must remain valid as
+ // long as this object exists.
+ void set_failure_message(std::string* failure_message) {
+ failure_message_ = failure_message;
+ }
+
private:
const std::vector<std::string> requested_subprotocols_;
// This is owned by the caller of CreateBaseStream() or
// CreateSpdyStream(). Both the stream and this object will be destroyed
// during the destruction of the URLRequest object associated with the
- // handshake.
+ // handshake. This is only guaranteed to be a valid pointer if the handshake
+ // succeeded.
WebSocketHandshakeStreamBase* stream_;
+ WebSocketStream::ConnectDelegate* connect_delegate_;
+ std::string* failure_message_;
+
DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeStreamCreateHelper);
};
diff --git a/chromium/net/websockets/websocket_handshake_stream_create_helper_test.cc b/chromium/net/websockets/websocket_handshake_stream_create_helper_test.cc
index 7566edf6174..644679410b5 100644
--- a/chromium/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/chromium/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -4,6 +4,9 @@
#include "net/websockets/websocket_handshake_stream_create_helper.h"
+#include <string>
+#include <vector>
+
#include "net/base/completion_callback.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
@@ -54,6 +57,23 @@ class MockClientSocketHandleFactory {
DISALLOW_COPY_AND_ASSIGN(MockClientSocketHandleFactory);
};
+class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
+ public:
+ virtual ~TestConnectDelegate() {}
+
+ virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {}
+ virtual void OnFailure(const std::string& failure_message) OVERRIDE {}
+ virtual void OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {}
+ virtual void OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {}
+ virtual void OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
+ ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {}
+};
+
class WebSocketHandshakeStreamCreateHelperTest : public ::testing::Test {
protected:
scoped_ptr<WebSocketStream> CreateAndInitializeStream(
@@ -63,7 +83,9 @@ class WebSocketHandshakeStreamCreateHelperTest : public ::testing::Test {
const std::string& origin,
const std::string& extra_request_headers,
const std::string& extra_response_headers) {
- WebSocketHandshakeStreamCreateHelper create_helper(sub_protocols);
+ WebSocketHandshakeStreamCreateHelper create_helper(&connect_delegate_,
+ sub_protocols);
+ create_helper.set_failure_message(&failure_message_);
scoped_ptr<ClientSocketHandle> socket_handle =
socket_handle_factory_.CreateClientSocketHandle(
@@ -91,6 +113,8 @@ class WebSocketHandshakeStreamCreateHelperTest : public ::testing::Test {
HttpRequestHeaders headers;
headers.SetHeader("Host", "localhost");
headers.SetHeader("Connection", "Upgrade");
+ headers.SetHeader("Pragma", "no-cache");
+ headers.SetHeader("Cache-Control", "no-cache");
headers.SetHeader("Upgrade", "websocket");
headers.SetHeader("Origin", origin);
headers.SetHeader("Sec-WebSocket-Version", "13");
@@ -114,6 +138,8 @@ class WebSocketHandshakeStreamCreateHelperTest : public ::testing::Test {
}
MockClientSocketHandleFactory socket_handle_factory_;
+ TestConnectDelegate connect_delegate_;
+ std::string failure_message_;
};
// Confirm that the basic case works as expected.
@@ -132,14 +158,47 @@ TEST_F(WebSocketHandshakeStreamCreateHelperTest, SubProtocols) {
sub_protocols.push_back("chat");
sub_protocols.push_back("superchat");
scoped_ptr<WebSocketStream> stream =
- CreateAndInitializeStream("ws://localhost/", "/",
- sub_protocols, "http://localhost/",
+ CreateAndInitializeStream("ws://localhost/",
+ "/",
+ sub_protocols,
+ "http://localhost/",
"Sec-WebSocket-Protocol: chat, superchat\r\n",
"Sec-WebSocket-Protocol: superchat\r\n");
EXPECT_EQ("superchat", stream->GetSubProtocol());
}
-// TODO(ricea): Test extensions once they are implemented.
+// Verify that extension name is available. Bad extension names are tested in
+// websocket_stream_test.cc.
+TEST_F(WebSocketHandshakeStreamCreateHelperTest, Extensions) {
+ scoped_ptr<WebSocketStream> stream = CreateAndInitializeStream(
+ "ws://localhost/",
+ "/",
+ std::vector<std::string>(),
+ "http://localhost/",
+ "",
+ "Sec-WebSocket-Extensions: permessage-deflate\r\n");
+ EXPECT_EQ("permessage-deflate", stream->GetExtensions());
+}
+
+// Verify that extension parameters are available. Bad parameters are tested in
+// websocket_stream_test.cc.
+TEST_F(WebSocketHandshakeStreamCreateHelperTest, ExtensionParameters) {
+ scoped_ptr<WebSocketStream> stream = CreateAndInitializeStream(
+ "ws://localhost/",
+ "/",
+ std::vector<std::string>(),
+ "http://localhost/",
+ "",
+ "Sec-WebSocket-Extensions: permessage-deflate;"
+ " client_max_window_bits=14; server_max_window_bits=14;"
+ " server_no_context_takeover; client_no_context_takeover\r\n");
+
+ EXPECT_EQ(
+ "permessage-deflate;"
+ " client_max_window_bits=14; server_max_window_bits=14;"
+ " server_no_context_takeover; client_no_context_takeover",
+ stream->GetExtensions());
+}
} // namespace
} // namespace net
diff --git a/chromium/net/websockets/websocket_job.cc b/chromium/net/websockets/websocket_job.cc
index b0f5be8bf3f..eb653fa5a97 100644
--- a/chromium/net/websockets/websocket_job.cc
+++ b/chromium/net/websockets/websocket_job.cc
@@ -36,9 +36,10 @@ const char* const kSetCookieHeaders[] = {
};
net::SocketStreamJob* WebSocketJobFactory(
- const GURL& url, net::SocketStream::Delegate* delegate) {
+ const GURL& url, net::SocketStream::Delegate* delegate,
+ net::URLRequestContext* context, net::CookieStore* cookie_store) {
net::WebSocketJob* job = new net::WebSocketJob(delegate);
- job->InitSocketStream(new net::SocketStream(url, job));
+ job->InitSocketStream(new net::SocketStream(url, job, context, cookie_store));
return job;
}
@@ -58,18 +59,11 @@ static base::LazyInstance<WebSocketJobInitSingleton> g_websocket_job_init =
namespace net {
-bool WebSocketJob::websocket_over_spdy_enabled_ = false;
-
// static
void WebSocketJob::EnsureInit() {
g_websocket_job_init.Get();
}
-// static
-void WebSocketJob::set_websocket_over_spdy_enabled(bool enabled) {
- websocket_over_spdy_enabled_ = enabled;
-}
-
WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
: delegate_(delegate),
state_(INITIALIZED),
@@ -303,9 +297,10 @@ void WebSocketJob::OnSentSpdyHeaders() {
DCHECK_NE(INITIALIZED, state_);
if (state_ != CONNECTING)
return;
- if (delegate_)
- delegate_->OnSentData(socket_.get(), handshake_request_->original_length());
+ size_t original_length = handshake_request_->original_length();
handshake_request_.reset();
+ if (delegate_)
+ delegate_->OnSentData(socket_.get(), original_length);
}
void WebSocketJob::OnSpdyResponseHeadersUpdated(
@@ -370,11 +365,11 @@ void WebSocketJob::AddCookieHeaderAndSend() {
if (socket_.get() && delegate_ && state_ == CONNECTING) {
handshake_request_->RemoveHeaders(kCookieHeaders,
arraysize(kCookieHeaders));
- if (allow && socket_->context()->cookie_store()) {
+ if (allow && socket_->cookie_store()) {
// Add cookies, including HttpOnly cookies.
CookieOptions cookie_options;
cookie_options.set_include_httponly();
- socket_->context()->cookie_store()->GetCookiesWithOptionsAsync(
+ socket_->cookie_store()->GetCookiesWithOptionsAsync(
GetURLForCookies(), cookie_options,
base::Bind(&WebSocketJob::LoadCookieCallback,
weak_ptr_factory_.GetWeakPtr()));
@@ -387,7 +382,7 @@ void WebSocketJob::AddCookieHeaderAndSend() {
void WebSocketJob::LoadCookieCallback(const std::string& cookie) {
if (!cookie.empty())
// TODO(tyoshino): Sending cookie means that connection doesn't need
- // kPrivacyModeEnabled as cookies may be server-bound and channel id
+ // PRIVACY_MODE_ENABLED as cookies may be server-bound and channel id
// wouldn't negatively affect privacy anyway. Need to restart connection
// or refactor to determine cookie status prior to connecting.
handshake_request_->AppendHeaderIfMissing("Cookie", cookie);
@@ -422,11 +417,12 @@ void WebSocketJob::OnSentHandshakeRequest(
if (handshake_request_sent_ >= handshake_request_->raw_length()) {
// handshake request has been sent.
// notify original size of handshake request to delegate.
- if (delegate_)
- delegate_->OnSentData(
- socket,
- handshake_request_->original_length());
+ // Reset the handshake_request_ first in case this object is deleted by the
+ // delegate.
+ size_t original_length = handshake_request_->original_length();
handshake_request_.reset();
+ if (delegate_)
+ delegate_->OnSentData(socket, original_length);
}
}
@@ -505,7 +501,7 @@ void WebSocketJob::SaveNextCookie() {
callback_pending_ = false;
save_next_cookie_running_ = true;
- if (socket_->context()->cookie_store()) {
+ if (socket_->cookie_store()) {
GURL url_for_cookies = GetURLForCookies();
CookieOptions options;
@@ -526,7 +522,7 @@ void WebSocketJob::SaveNextCookie() {
continue;
callback_pending_ = true;
- socket_->context()->cookie_store()->SetCookieWithOptionsAsync(
+ socket_->cookie_store()->SetCookieWithOptionsAsync(
url_for_cookies, cookie, options,
base::Bind(&WebSocketJob::OnCookieSaved,
weak_ptr_factory_.GetWeakPtr()));
@@ -563,9 +559,8 @@ void WebSocketJob::OnCookieSaved(bool cookie_status) {
GURL WebSocketJob::GetURLForCookies() const {
GURL url = socket_->url();
std::string scheme = socket_->is_secure() ? "https" : "http";
- url_canon::Replacements<char> replacements;
- replacements.SetScheme(scheme.c_str(),
- url_parse::Component(0, scheme.length()));
+ url::Replacements<char> replacements;
+ replacements.SetScheme(scheme.c_str(), url::Component(0, scheme.length()));
return url.ReplaceComponents(replacements);
}
@@ -577,16 +572,13 @@ int WebSocketJob::TrySpdyStream() {
if (!socket_.get())
return ERR_FAILED;
- if (!websocket_over_spdy_enabled_)
- return OK;
-
// Check if we have a SPDY session available.
HttpTransactionFactory* factory =
socket_->context()->http_transaction_factory();
if (!factory)
return OK;
scoped_refptr<HttpNetworkSession> session = factory->GetSession();
- if (!session.get())
+ if (!session.get() || !session->params().enable_websocket_over_spdy)
return OK;
SpdySessionPool* spdy_pool = session->spdy_session_pool();
PrivacyMode privacy_mode = socket_->privacy_mode();
diff --git a/chromium/net/websockets/websocket_job.h b/chromium/net/websockets/websocket_job.h
index 119c4dcfaa9..2e90a24d16c 100644
--- a/chromium/net/websockets/websocket_job.h
+++ b/chromium/net/websockets/websocket_job.h
@@ -49,10 +49,6 @@ class NET_EXPORT WebSocketJob
static void EnsureInit();
- // Enable or Disable WebSocket over SPDY feature.
- // This function is intended to be called before I/O thread starts.
- static void set_websocket_over_spdy_enabled(bool enabled);
-
State state() const { return state_; }
virtual void Connect() OVERRIDE;
virtual bool SendData(const char* data, int len) OVERRIDE;
@@ -124,8 +120,6 @@ class NET_EXPORT WebSocketJob
void CloseInternal();
void SendPending();
- static bool websocket_over_spdy_enabled_;
-
SocketStream::Delegate* delegate_;
State state_;
bool waiting_;
diff --git a/chromium/net/websockets/websocket_job_test.cc b/chromium/net/websockets/websocket_job_test.cc
index bdbae709eb9..7b87a870d8d 100644
--- a/chromium/net/websockets/websocket_job_test.cc
+++ b/chromium/net/websockets/websocket_job_test.cc
@@ -41,8 +41,9 @@ namespace {
class MockSocketStream : public SocketStream {
public:
- MockSocketStream(const GURL& url, SocketStream::Delegate* delegate)
- : SocketStream(url, delegate) {}
+ MockSocketStream(const GURL& url, SocketStream::Delegate* delegate,
+ URLRequestContext* context, CookieStore* cookie_store)
+ : SocketStream(url, delegate, context, cookie_store) {}
virtual void Connect() OVERRIDE {}
virtual bool SendData(const char* data, int len) OVERRIDE {
@@ -203,6 +204,12 @@ class MockCookieStore : public CookieStore {
callback.Run(GetCookiesWithOptions(url, options));
}
+ virtual void GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) OVERRIDE {
+ ADD_FAILURE();
+ }
+
virtual void DeleteCookieAsync(const GURL& url,
const std::string& cookie_name,
const base::Closure& callback) OVERRIDE {
@@ -216,6 +223,14 @@ class MockCookieStore : public CookieStore {
ADD_FAILURE();
}
+ virtual void DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) OVERRIDE {
+ ADD_FAILURE();
+ }
+
virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE {
ADD_FAILURE();
}
@@ -259,11 +274,14 @@ class MockURLRequestContext : public URLRequestContext {
class MockHttpTransactionFactory : public HttpTransactionFactory {
public:
- MockHttpTransactionFactory(NextProto next_proto, OrderedSocketData* data) {
+ MockHttpTransactionFactory(NextProto next_proto,
+ OrderedSocketData* data,
+ bool enable_websocket_over_spdy) {
data_ = data;
MockConnect connect_data(SYNCHRONOUS, OK);
data_->set_connect_data(connect_data);
session_deps_.reset(new SpdySessionDependencies(next_proto));
+ session_deps_->enable_websocket_over_spdy = enable_websocket_over_spdy;
session_deps_->socket_factory->AddSocketDataProvider(data_);
http_session_ =
SpdySessionDependencies::SpdyCreateSession(session_deps_.get());
@@ -271,15 +289,14 @@ class MockHttpTransactionFactory : public HttpTransactionFactory {
host_port_pair_.set_port(80);
spdy_session_key_ = SpdySessionKey(host_port_pair_,
ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
session_ = CreateInsecureSpdySession(
http_session_, spdy_session_key_, BoundNetLog());
}
virtual int CreateTransaction(
RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) OVERRIDE {
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE {
NOTREACHED();
return ERR_UNEXPECTED;
}
@@ -302,12 +319,82 @@ class MockHttpTransactionFactory : public HttpTransactionFactory {
SpdySessionKey spdy_session_key_;
};
+class DeletingSocketStreamDelegate : public SocketStream::Delegate {
+ public:
+ DeletingSocketStreamDelegate()
+ : delete_next_(false) {}
+
+ // Since this class needs to be able to delete |job_|, it must be the only
+ // reference holder (except for temporary references). Provide access to the
+ // pointer for tests to use.
+ WebSocketJob* job() { return job_.get(); }
+
+ void set_job(WebSocketJob* job) { job_ = job; }
+
+ // After calling this, the next call to a method on this delegate will delete
+ // the WebSocketJob object.
+ void set_delete_next(bool delete_next) { delete_next_ = delete_next; }
+
+ void DeleteJobMaybe() {
+ if (delete_next_) {
+ job_->DetachContext();
+ job_->DetachDelegate();
+ job_ = NULL;
+ }
+ }
+
+ // SocketStream::Delegate implementation
+
+ // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost
+
+ virtual void OnConnected(SocketStream* socket,
+ int max_pending_send_allowed) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ virtual void OnSentData(SocketStream* socket, int amount_sent) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ virtual void OnReceivedData(SocketStream* socket,
+ const char* data,
+ int len) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ virtual void OnClose(SocketStream* socket) OVERRIDE { DeleteJobMaybe(); }
+
+ virtual void OnAuthRequired(SocketStream* socket,
+ AuthChallengeInfo* auth_info) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ virtual void OnSSLCertificateError(SocketStream* socket,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ virtual void OnError(const SocketStream* socket, int error) OVERRIDE {
+ DeleteJobMaybe();
+ }
+
+ // CanGetCookies() and CanSetCookies() do not appear to be able to delete the
+ // WebSocketJob object.
+
+ private:
+ scoped_refptr<WebSocketJob> job_;
+ bool delete_next_;
+};
+
} // namespace
class WebSocketJobTest : public PlatformTest,
public ::testing::WithParamInterface<NextProto> {
public:
- WebSocketJobTest() : spdy_util_(GetParam()) {}
+ WebSocketJobTest()
+ : spdy_util_(GetParam()),
+ enable_websocket_over_spdy_(false) {}
virtual void SetUp() OVERRIDE {
stream_type_ = STREAM_INVALID;
@@ -334,6 +421,7 @@ class WebSocketJobTest : public PlatformTest,
int WaitForResult() {
return sync_test_callback_.WaitForResult();
}
+
protected:
enum StreamType {
STREAM_INVALID,
@@ -357,12 +445,13 @@ class WebSocketJobTest : public PlatformTest,
websocket_ = new WebSocketJob(delegate);
if (stream_type == STREAM_MOCK_SOCKET)
- socket_ = new MockSocketStream(url, websocket_.get());
+ socket_ = new MockSocketStream(url, websocket_.get(), context_.get(),
+ NULL);
if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) {
if (stream_type == STREAM_SPDY_WEBSOCKET) {
- http_factory_.reset(
- new MockHttpTransactionFactory(GetParam(), data_.get()));
+ http_factory_.reset(new MockHttpTransactionFactory(
+ GetParam(), data_.get(), enable_websocket_over_spdy_));
context_->set_http_transaction_factory(http_factory_.get());
}
@@ -373,7 +462,7 @@ class WebSocketJobTest : public PlatformTest,
host_resolver_.reset(new MockHostResolver);
context_->set_host_resolver(host_resolver_.get());
- socket_ = new SocketStream(url, websocket_.get());
+ socket_ = new SocketStream(url, websocket_.get(), context_.get(), NULL);
socket_factory_.reset(new MockClientSocketFactory);
DCHECK(data_.get());
socket_factory_->AddSocketDataProvider(data_.get());
@@ -381,7 +470,6 @@ class WebSocketJobTest : public PlatformTest,
}
websocket_->InitSocketStream(socket_.get());
- websocket_->set_context(context_.get());
// MockHostResolver resolves all hosts to 127.0.0.1; however, when we create
// a WebSocketJob purely to block another one in a throttling test, we don't
// perform a real connect. In that case, the following address is used
@@ -448,6 +536,9 @@ class WebSocketJobTest : public PlatformTest,
scoped_ptr<MockHostResolver> host_resolver_;
scoped_ptr<MockHttpTransactionFactory> http_factory_;
+ // Must be set before call to enable_websocket_over_spdy, defaults to false.
+ bool enable_websocket_over_spdy_;
+
static const char kHandshakeRequestWithoutCookie[];
static const char kHandshakeRequestWithCookie[];
static const char kHandshakeRequestWithFilteredCookie[];
@@ -466,6 +557,34 @@ class WebSocketJobTest : public PlatformTest,
static const size_t kDataWorldLength;
};
+// Tests using this fixture verify that the WebSocketJob can handle being
+// deleted while calling back to the delegate correctly. These tests need to be
+// run under AddressSanitizer or other systems for detecting use-after-free
+// errors in order to find problems.
+class WebSocketJobDeleteTest : public ::testing::Test {
+ protected:
+ WebSocketJobDeleteTest()
+ : delegate_(new DeletingSocketStreamDelegate),
+ cookie_store_(new MockCookieStore),
+ context_(new MockURLRequestContext(cookie_store_.get())) {
+ WebSocketJob* websocket = new WebSocketJob(delegate_.get());
+ delegate_->set_job(websocket);
+
+ socket_ = new MockSocketStream(
+ GURL("ws://127.0.0.1/"), websocket, context_.get(), NULL);
+
+ websocket->InitSocketStream(socket_.get());
+ }
+
+ void SetDeleteNext() { return delegate_->set_delete_next(true); }
+ WebSocketJob* job() { return delegate_->job(); }
+
+ scoped_ptr<DeletingSocketStreamDelegate> delegate_;
+ scoped_refptr<MockCookieStore> cookie_store_;
+ scoped_ptr<MockURLRequestContext> context_;
+ scoped_refptr<SocketStream> socket_;
+};
+
const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] =
"GET /demo HTTP/1.1\r\n"
"Host: example.com\r\n"
@@ -598,11 +717,10 @@ INSTANTIATE_TEST_CASE_P(
NextProto,
WebSocketJobTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
TEST_P(WebSocketJobTest, DelayedCookies) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
GURL url("ws://example.com/demo");
GURL cookieUrl("http://example.com/demo");
CookieOptions cookie_options;
@@ -731,14 +849,14 @@ void WebSocketJobTest::TestHSTSUpgrade() {
scoped_refptr<SocketStreamJob> job =
SocketStreamJob::CreateSocketStreamJob(
url, &delegate, context_->transport_security_state(),
- context_->ssl_config_service());
+ context_->ssl_config_service(), NULL, NULL);
EXPECT_TRUE(GetSocket(job.get())->is_secure());
job->DetachDelegate();
url = GURL("ws://donotupgrademe.com/");
job = SocketStreamJob::CreateSocketStreamJob(
url, &delegate, context_->transport_security_state(),
- context_->ssl_config_service());
+ context_->ssl_config_service(), NULL, NULL);
EXPECT_FALSE(GetSocket(job.get())->is_secure());
job->DetachDelegate();
}
@@ -1005,87 +1123,79 @@ void WebSocketJobTest::TestThrottlingLimit() {
// Execute tests in both spdy-disabled mode and spdy-enabled mode.
TEST_P(WebSocketJobTest, SimpleHandshake) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestSimpleHandshake();
}
TEST_P(WebSocketJobTest, SlowHandshake) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestSlowHandshake();
}
TEST_P(WebSocketJobTest, HandshakeWithCookie) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestHandshakeWithCookie();
}
TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestHandshakeWithCookieButNotAllowed();
}
TEST_P(WebSocketJobTest, HSTSUpgrade) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestHSTSUpgrade();
}
TEST_P(WebSocketJobTest, InvalidSendData) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestInvalidSendData();
}
TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestSimpleHandshake();
}
TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestSlowHandshake();
}
TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestHandshakeWithCookie();
}
TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestHandshakeWithCookieButNotAllowed();
}
TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestHSTSUpgrade();
}
TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestInvalidSendData();
}
TEST_P(WebSocketJobTest, ConnectByWebSocket) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
+ enable_websocket_over_spdy_ = true;
TestConnectByWebSocket(THROTTLING_OFF);
}
TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestConnectByWebSocket(THROTTLING_OFF);
}
TEST_P(WebSocketJobTest, ConnectBySpdy) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF);
}
TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestConnectBySpdy(SPDY_ON, THROTTLING_OFF);
}
TEST_P(WebSocketJobTest, ThrottlingWebSocket) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestConnectByWebSocket(THROTTLING_ON);
}
@@ -1094,20 +1204,89 @@ TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) {
}
TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestConnectByWebSocket(THROTTLING_ON);
}
TEST_P(WebSocketJobTest, ThrottlingSpdy) {
- WebSocketJob::set_websocket_over_spdy_enabled(false);
TestConnectBySpdy(SPDY_OFF, THROTTLING_ON);
}
TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) {
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ enable_websocket_over_spdy_ = true;
TestConnectBySpdy(SPDY_ON, THROTTLING_ON);
}
+TEST_F(WebSocketJobDeleteTest, OnClose) {
+ SetDeleteNext();
+ job()->OnClose(socket_.get());
+ // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so
+ // socket_->delegate is still set at this point. Clear it to avoid hitting
+ // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish()
+ // is the only caller of this method in real code, and it also sets delegate_
+ // to NULL.
+ socket_->DetachDelegate();
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, OnAuthRequired) {
+ SetDeleteNext();
+ job()->OnAuthRequired(socket_.get(), NULL);
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) {
+ SSLInfo ssl_info;
+ SetDeleteNext();
+ job()->OnSSLCertificateError(socket_.get(), ssl_info, true);
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, OnError) {
+ SetDeleteNext();
+ job()->OnError(socket_.get(), ERR_CONNECTION_RESET);
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) {
+ job()->Connect();
+ SetDeleteNext();
+ job()->OnSentSpdyHeaders();
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) {
+ static const char kMinimalRequest[] =
+ "GET /demo HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+ "Origin: http://example.com\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "\r\n";
+ const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1;
+ job()->Connect();
+ job()->SendData(kMinimalRequest, kMinimalRequestSize);
+ SetDeleteNext();
+ job()->OnSentData(socket_.get(), kMinimalRequestSize);
+ EXPECT_FALSE(job());
+}
+
+TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) {
+ static const char kMinimalResponse[] =
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
+ "\r\n";
+ job()->Connect();
+ SetDeleteNext();
+ job()->OnReceivedData(
+ socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1);
+ EXPECT_FALSE(job());
+}
+
// TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation.
// TODO(toyoshim,yutak): Add tests to verify closing handshake.
} // namespace net
diff --git a/chromium/net/websockets/websocket_net_log_params_test.cc b/chromium/net/websockets/websocket_net_log_params_test.cc
index 4690fd66964..d6d2a0d3ff5 100644
--- a/chromium/net/websockets/websocket_net_log_params_test.cc
+++ b/chromium/net/websockets/websocket_net_log_params_test.cc
@@ -44,7 +44,7 @@ TEST(NetLogWebSocketHandshakeParameterTest, ToValue) {
scoped_ptr<base::Value> actual(
net::NetLogWebSocketHandshakeCallback(&testInput,
- net::NetLog::LOG_BASIC));
+ net::NetLog::LOG_ALL));
EXPECT_TRUE(expected.Equals(actual.get()));
}
diff --git a/chromium/net/websockets/websocket_stream.cc b/chromium/net/websockets/websocket_stream.cc
index e81c24e706e..8ddce8d9b72 100644
--- a/chromium/net/websockets/websocket_stream.cc
+++ b/chromium/net/websockets/websocket_stream.cc
@@ -6,16 +6,21 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/websockets/websocket_errors.h"
+#include "net/websockets/websocket_event_interface.h"
#include "net/websockets/websocket_handshake_constants.h"
#include "net/websockets/websocket_handshake_stream_base.h"
#include "net/websockets/websocket_handshake_stream_create_helper.h"
#include "net/websockets/websocket_test_util.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace net {
namespace {
@@ -24,10 +29,32 @@ class StreamRequestImpl;
class Delegate : public URLRequest::Delegate {
public:
- explicit Delegate(StreamRequestImpl* owner) : owner_(owner) {}
- virtual ~Delegate() {}
+ enum HandshakeResult {
+ INCOMPLETE,
+ CONNECTED,
+ FAILED,
+ NUM_HANDSHAKE_RESULT_TYPES,
+ };
+
+ explicit Delegate(StreamRequestImpl* owner)
+ : owner_(owner), result_(INCOMPLETE) {}
+ virtual ~Delegate() {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.WebSocket.HandshakeResult", result_, NUM_HANDSHAKE_RESULT_TYPES);
+ }
// Implementation of URLRequest::Delegate methods.
+ virtual void OnReceivedRedirect(URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE {
+ // HTTP status codes returned by HttpStreamParser are filtered by
+ // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted
+ // back up the stack to HttpNetworkTransaction. In particular, redirect
+ // codes are never allowed, and so URLRequest never sees a redirect on a
+ // WebSocket request.
+ NOTREACHED();
+ }
+
virtual void OnResponseStarted(URLRequest* request) OVERRIDE;
virtual void OnAuthRequired(URLRequest* request,
@@ -45,6 +72,7 @@ class Delegate : public URLRequest::Delegate {
private:
StreamRequestImpl* owner_;
+ HandshakeResult result_;
};
class StreamRequestImpl : public WebSocketStreamRequest {
@@ -52,25 +80,64 @@ class StreamRequestImpl : public WebSocketStreamRequest {
StreamRequestImpl(
const GURL& url,
const URLRequestContext* context,
+ const url::Origin& origin,
scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate,
- WebSocketHandshakeStreamCreateHelper* create_helper)
+ scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper)
: delegate_(new Delegate(this)),
url_request_(url, DEFAULT_PRIORITY, delegate_.get(), context),
connect_delegate_(connect_delegate.Pass()),
- create_helper_(create_helper) {}
+ create_helper_(create_helper.release()) {
+ create_helper_->set_failure_message(&failure_message_);
+ HttpRequestHeaders headers;
+ headers.SetHeader(websockets::kUpgrade, websockets::kWebSocketLowercase);
+ headers.SetHeader(HttpRequestHeaders::kConnection, websockets::kUpgrade);
+ headers.SetHeader(HttpRequestHeaders::kOrigin, origin.string());
+ headers.SetHeader(websockets::kSecWebSocketVersion,
+ websockets::kSupportedVersion);
+ url_request_.SetExtraRequestHeaders(headers);
+
+ // This passes the ownership of |create_helper_| to |url_request_|.
+ url_request_.SetUserData(
+ WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
+ create_helper_);
+ url_request_.SetLoadFlags(LOAD_DISABLE_CACHE |
+ LOAD_BYPASS_CACHE |
+ LOAD_DO_NOT_PROMPT_FOR_LOGIN);
+ }
// Destroying this object destroys the URLRequest, which cancels the request
// and so terminates the handshake if it is incomplete.
virtual ~StreamRequestImpl() {}
- URLRequest* url_request() { return &url_request_; }
+ void Start() {
+ url_request_.Start();
+ }
void PerformUpgrade() {
connect_delegate_->OnSuccess(create_helper_->stream()->Upgrade());
}
void ReportFailure() {
- connect_delegate_->OnFailure(kWebSocketErrorAbnormalClosure);
+ if (failure_message_.empty()) {
+ switch (url_request_.status().status()) {
+ case URLRequestStatus::SUCCESS:
+ case URLRequestStatus::IO_PENDING:
+ break;
+ case URLRequestStatus::CANCELED:
+ failure_message_ = "WebSocket opening handshake was canceled";
+ break;
+ case URLRequestStatus::FAILED:
+ failure_message_ =
+ std::string("Error in connection establishment: ") +
+ ErrorToString(url_request_.status().error());
+ break;
+ }
+ }
+ connect_delegate_->OnFailure(failure_message_);
+ }
+
+ WebSocketStream::ConnectDelegate* connect_delegate() const {
+ return connect_delegate_.get();
}
private:
@@ -86,11 +153,47 @@ class StreamRequestImpl : public WebSocketStreamRequest {
// Owned by the URLRequest.
WebSocketHandshakeStreamCreateHelper* create_helper_;
+
+ // The failure message supplied by WebSocketBasicHandshakeStream, if any.
+ std::string failure_message_;
+};
+
+class SSLErrorCallbacks : public WebSocketEventInterface::SSLErrorCallbacks {
+ public:
+ explicit SSLErrorCallbacks(URLRequest* url_request)
+ : url_request_(url_request) {}
+
+ virtual void CancelSSLRequest(int error, const SSLInfo* ssl_info) OVERRIDE {
+ if (ssl_info) {
+ url_request_->CancelWithSSLError(error, *ssl_info);
+ } else {
+ url_request_->CancelWithError(error);
+ }
+ }
+
+ virtual void ContinueSSLRequest() OVERRIDE {
+ url_request_->ContinueDespiteLastError();
+ }
+
+ private:
+ URLRequest* url_request_;
};
void Delegate::OnResponseStarted(URLRequest* request) {
- switch (request->GetResponseCode()) {
+ // All error codes, including OK and ABORTED, as with
+ // Net.ErrorCodesForMainFrame3
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.WebSocket.ErrorCodes",
+ -request->status().error());
+ if (!request->status().is_success()) {
+ DVLOG(3) << "OnResponseStarted (request failed)";
+ owner_->ReportFailure();
+ return;
+ }
+ const int response_code = request->GetResponseCode();
+ DVLOG(3) << "OnResponseStarted (response code " << response_code << ")";
+ switch (response_code) {
case HTTP_SWITCHING_PROTOCOLS:
+ result_ = CONNECTED;
owner_->PerformUpgrade();
return;
@@ -99,61 +202,42 @@ void Delegate::OnResponseStarted(URLRequest* request) {
return;
default:
+ result_ = FAILED;
owner_->ReportFailure();
}
}
void Delegate::OnAuthRequired(URLRequest* request,
AuthChallengeInfo* auth_info) {
+ // This should only be called if credentials are not already stored.
request->CancelAuth();
}
void Delegate::OnCertificateRequested(URLRequest* request,
SSLCertRequestInfo* cert_request_info) {
- request->ContinueWithCertificate(NULL);
+ // This method is called when a client certificate is requested, and the
+ // request context does not already contain a client certificate selection for
+ // the endpoint. In this case, a main frame resource request would pop-up UI
+ // to permit selection of a client certificate, but since WebSockets are
+ // sub-resources they should not pop-up UI and so there is nothing more we can
+ // do.
+ request->Cancel();
}
void Delegate::OnSSLCertificateError(URLRequest* request,
const SSLInfo& ssl_info,
bool fatal) {
- request->Cancel();
+ owner_->connect_delegate()->OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>(
+ new SSLErrorCallbacks(request)),
+ ssl_info,
+ fatal);
}
void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) {
NOTREACHED();
}
-// Internal implementation of CreateAndConnectStream and
-// CreateAndConnectStreamForTesting.
-scoped_ptr<WebSocketStreamRequest> CreateAndConnectStreamWithCreateHelper(
- const GURL& socket_url,
- scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
- const GURL& origin,
- URLRequestContext* url_request_context,
- const BoundNetLog& net_log,
- scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate) {
- scoped_ptr<StreamRequestImpl> request(
- new StreamRequestImpl(socket_url,
- url_request_context,
- connect_delegate.Pass(),
- create_helper.get()));
- HttpRequestHeaders headers;
- headers.SetHeader(websockets::kUpgrade, websockets::kWebSocketLowercase);
- headers.SetHeader(HttpRequestHeaders::kConnection, websockets::kUpgrade);
- headers.SetHeader(HttpRequestHeaders::kOrigin, origin.spec());
- // TODO(ricea): Move the version number to websocket_handshake_constants.h
- headers.SetHeader(websockets::kSecWebSocketVersion,
- websockets::kSupportedVersion);
- request->url_request()->SetExtraRequestHeaders(headers);
- request->url_request()->SetUserData(
- WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
- create_helper.release());
- request->url_request()->SetLoadFlags(LOAD_DISABLE_CACHE |
- LOAD_DO_NOT_PROMPT_FOR_LOGIN);
- request->url_request()->Start();
- return request.PassAs<WebSocketStreamRequest>();
-}
-
} // namespace
WebSocketStreamRequest::~WebSocketStreamRequest() {}
@@ -166,34 +250,39 @@ WebSocketStream::ConnectDelegate::~ConnectDelegate() {}
scoped_ptr<WebSocketStreamRequest> WebSocketStream::CreateAndConnectStream(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin,
+ const url::Origin& origin,
URLRequestContext* url_request_context,
const BoundNetLog& net_log,
scoped_ptr<ConnectDelegate> connect_delegate) {
scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper(
- new WebSocketHandshakeStreamCreateHelper(requested_subprotocols));
- return CreateAndConnectStreamWithCreateHelper(socket_url,
- create_helper.Pass(),
- origin,
- url_request_context,
- net_log,
- connect_delegate.Pass());
+ new WebSocketHandshakeStreamCreateHelper(connect_delegate.get(),
+ requested_subprotocols));
+ scoped_ptr<StreamRequestImpl> request(
+ new StreamRequestImpl(socket_url,
+ url_request_context,
+ origin,
+ connect_delegate.Pass(),
+ create_helper.Pass()));
+ request->Start();
+ return request.PassAs<WebSocketStreamRequest>();
}
// This is declared in websocket_test_util.h.
scoped_ptr<WebSocketStreamRequest> CreateAndConnectStreamForTesting(
- const GURL& socket_url,
- scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
- const GURL& origin,
- URLRequestContext* url_request_context,
- const BoundNetLog& net_log,
- scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate) {
- return CreateAndConnectStreamWithCreateHelper(socket_url,
- create_helper.Pass(),
- origin,
- url_request_context,
- net_log,
- connect_delegate.Pass());
+ const GURL& socket_url,
+ scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
+ const url::Origin& origin,
+ URLRequestContext* url_request_context,
+ const BoundNetLog& net_log,
+ scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate) {
+ scoped_ptr<StreamRequestImpl> request(
+ new StreamRequestImpl(socket_url,
+ url_request_context,
+ origin,
+ connect_delegate.Pass(),
+ create_helper.Pass()));
+ request->Start();
+ return request.PassAs<WebSocketStreamRequest>();
}
} // namespace net
diff --git a/chromium/net/websockets/websocket_stream.h b/chromium/net/websockets/websocket_stream.h
index c08f8dc39b7..09f11b22f1a 100644
--- a/chromium/net/websockets/websocket_stream.h
+++ b/chromium/net/websockets/websocket_stream.h
@@ -14,9 +14,16 @@
#include "base/memory/scoped_vector.h"
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
+#include "net/websockets/websocket_event_interface.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
class GURL;
+namespace url {
+class Origin;
+} // namespace url
+
namespace net {
class BoundNetLog;
@@ -57,10 +64,26 @@ class NET_EXPORT_PRIVATE WebSocketStream {
// WebSocketStream.
virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) = 0;
- // Called on failure to connect. The parameter is either one of the values
- // defined in net::WebSocketError, or an error defined by some WebSocket
- // extension protocol that we implement.
- virtual void OnFailure(unsigned short websocket_error) = 0;
+ // Called on failure to connect.
+ // |message| contains defails of the failure.
+ virtual void OnFailure(const std::string& message) = 0;
+
+ // Called when the WebSocket Opening Handshake starts.
+ virtual void OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) = 0;
+
+ // Called when the WebSocket Opening Handshake ends.
+ virtual void OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) = 0;
+
+ // Called when there is an SSL certificate error. Should call
+ // ssl_error_callbacks->ContinueSSLRequest() or
+ // ssl_error_callbacks->CancelSSLRequest().
+ virtual void OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
+ ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal) = 0;
};
// Create and connect a WebSocketStream of an appropriate type. The actual
@@ -78,7 +101,7 @@ class NET_EXPORT_PRIVATE WebSocketStream {
static scoped_ptr<WebSocketStreamRequest> CreateAndConnectStream(
const GURL& socket_url,
const std::vector<std::string>& requested_subprotocols,
- const GURL& origin,
+ const url::Origin& origin,
URLRequestContext* url_request_context,
const BoundNetLog& net_log,
scoped_ptr<ConnectDelegate> connect_delegate);
@@ -125,6 +148,10 @@ class NET_EXPORT_PRIVATE WebSocketStream {
// calling callback.Run() (and any calling methods in the same object) must
// return immediately without any further method calls or access to member
// variables. Implementors should write test(s) for this case.
+ //
+ // Extensions which use reserved header bits should clear them when they are
+ // set correctly. If the reserved header bits are set incorrectly, it is okay
+ // to leave it to the caller to report the error.
virtual int ReadFrames(ScopedVector<WebSocketFrame>* frames,
const CompletionCallback& callback) = 0;
diff --git a/chromium/net/websockets/websocket_stream_test.cc b/chromium/net/websockets/websocket_stream_test.cc
index 3e11a95ac1c..0a6b99be4d6 100644
--- a/chromium/net/websockets/websocket_stream_test.cc
+++ b/chromium/net/websockets/websocket_stream_test.cc
@@ -4,31 +4,67 @@
#include "net/websockets/websocket_stream.h"
+#include <algorithm>
#include <string>
+#include <utility>
#include <vector>
+#include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/socket_test_util.h"
+#include "net/test/cert_test_util.h"
#include "net/url_request/url_request_test_util.h"
#include "net/websockets/websocket_basic_handshake_stream.h"
+#include "net/websockets/websocket_frame.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
#include "net/websockets/websocket_handshake_stream_create_helper.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace net {
namespace {
+typedef std::pair<std::string, std::string> HeaderKeyValuePair;
+
+std::vector<HeaderKeyValuePair> ToVector(const HttpRequestHeaders& headers) {
+ HttpRequestHeaders::Iterator it(headers);
+ std::vector<HeaderKeyValuePair> result;
+ while (it.GetNext())
+ result.push_back(HeaderKeyValuePair(it.name(), it.value()));
+ return result;
+}
+
+std::vector<HeaderKeyValuePair> ToVector(const HttpResponseHeaders& headers) {
+ void* iter = NULL;
+ std::string name, value;
+ std::vector<HeaderKeyValuePair> result;
+ while (headers.EnumerateHeaderLines(&iter, &name, &value))
+ result.push_back(HeaderKeyValuePair(name, value));
+ return result;
+}
+
// A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
// deterministic key to use in the WebSocket handshake.
class DeterministicKeyWebSocketHandshakeStreamCreateHelper
: public WebSocketHandshakeStreamCreateHelper {
public:
DeterministicKeyWebSocketHandshakeStreamCreateHelper(
+ WebSocketStream::ConnectDelegate* connect_delegate,
const std::vector<std::string>& requested_subprotocols)
- : WebSocketHandshakeStreamCreateHelper(requested_subprotocols) {}
+ : WebSocketHandshakeStreamCreateHelper(connect_delegate,
+ requested_subprotocols) {}
virtual WebSocketHandshakeStreamBase* CreateBasicStream(
scoped_ptr<ClientSocketHandle> connection,
@@ -44,8 +80,8 @@ class DeterministicKeyWebSocketHandshakeStreamCreateHelper
};
class WebSocketStreamCreateTest : public ::testing::Test {
- protected:
- WebSocketStreamCreateTest() : websocket_error_(0) {}
+ public:
+ WebSocketStreamCreateTest() : has_failed_(false), ssl_fatal_(false) {}
void CreateAndConnectCustomResponse(
const std::string& socket_url,
@@ -82,7 +118,7 @@ class WebSocketStreamCreateTest : public ::testing::Test {
const std::vector<std::string>& sub_protocols,
const std::string& origin,
scoped_ptr<DeterministicSocketData> socket_data) {
- url_request_context_host_.SetRawExpectations(socket_data.Pass());
+ url_request_context_host_.AddRawExpectations(socket_data.Pass());
CreateAndConnectStream(socket_url, sub_protocols, origin);
}
@@ -91,16 +127,25 @@ class WebSocketStreamCreateTest : public ::testing::Test {
void CreateAndConnectStream(const std::string& socket_url,
const std::vector<std::string>& sub_protocols,
const std::string& origin) {
+ for (size_t i = 0; i < ssl_data_.size(); ++i) {
+ scoped_ptr<SSLSocketDataProvider> ssl_data(ssl_data_[i]);
+ ssl_data_[i] = NULL;
+ url_request_context_host_.AddSSLSocketDataProvider(ssl_data.Pass());
+ }
+ ssl_data_.clear();
+ scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
+ new TestConnectDelegate(this));
+ WebSocketStream::ConnectDelegate* delegate = connect_delegate.get();
+ scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper(
+ new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
+ delegate, sub_protocols));
stream_request_ = ::net::CreateAndConnectStreamForTesting(
GURL(socket_url),
- scoped_ptr<WebSocketHandshakeStreamCreateHelper>(
- new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
- sub_protocols)),
- GURL(origin),
+ create_helper.Pass(),
+ url::Origin(origin),
url_request_context_host_.GetURLRequestContext(),
BoundNetLog(),
- scoped_ptr<WebSocketStream::ConnectDelegate>(
- new TestConnectDelegate(this)));
+ connect_delegate.Pass());
}
static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
@@ -110,18 +155,43 @@ class WebSocketStreamCreateTest : public ::testing::Test {
return std::vector<std::string>();
}
- uint16 error() const { return websocket_error_; }
+ const std::string& failure_message() const { return failure_message_; }
+ bool has_failed() const { return has_failed_; }
class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
public:
- TestConnectDelegate(WebSocketStreamCreateTest* owner) : owner_(owner) {}
+ explicit TestConnectDelegate(WebSocketStreamCreateTest* owner)
+ : owner_(owner) {}
virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
stream.swap(owner_->stream_);
}
- virtual void OnFailure(uint16 websocket_error) OVERRIDE {
- owner_->websocket_error_ = websocket_error;
+ virtual void OnFailure(const std::string& message) OVERRIDE {
+ owner_->has_failed_ = true;
+ owner_->failure_message_ = message;
+ }
+
+ virtual void OnStartOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
+ if (owner_->request_info_)
+ ADD_FAILURE();
+ owner_->request_info_ = request.Pass();
+ }
+ virtual void OnFinishOpeningHandshake(
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
+ if (owner_->response_info_)
+ ADD_FAILURE();
+ owner_->response_info_ = response.Pass();
+ }
+ virtual void OnSSLCertificateError(
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
+ ssl_error_callbacks,
+ const SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ owner_->ssl_error_callbacks_ = ssl_error_callbacks.Pass();
+ owner_->ssl_info_ = ssl_info;
+ owner_->ssl_fatal_ = fatal;
}
private:
@@ -132,22 +202,144 @@ class WebSocketStreamCreateTest : public ::testing::Test {
scoped_ptr<WebSocketStreamRequest> stream_request_;
// Only set if the connection succeeded.
scoped_ptr<WebSocketStream> stream_;
- // Only set if the connection failed. 0 otherwise.
- uint16 websocket_error_;
+ // Only set if the connection failed.
+ std::string failure_message_;
+ bool has_failed_;
+ scoped_ptr<WebSocketHandshakeRequestInfo> request_info_;
+ scoped_ptr<WebSocketHandshakeResponseInfo> response_info_;
+ scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks_;
+ SSLInfo ssl_info_;
+ bool ssl_fatal_;
+ ScopedVector<SSLSocketDataProvider> ssl_data_;
+};
+
+// There are enough tests of the Sec-WebSocket-Extensions header that they
+// deserve their own test fixture.
+class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
+ public:
+ // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
+ // header in the response set to |extensions_header_value|. Runs the event
+ // loop to allow the connect to complete.
+ void CreateAndConnectWithExtensions(
+ const std::string& extensions_header_value) {
+ CreateAndConnectStandard(
+ "ws://localhost/testing_path",
+ "/testing_path",
+ NoSubProtocols(),
+ "http://localhost",
+ "",
+ "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
+ RunUntilIdle();
+ }
+};
+
+class WebSocketStreamCreateUMATest : public ::testing::Test {
+ public:
+ // This enum should match with the enum in Delegate in websocket_stream.cc.
+ enum HandshakeResult {
+ INCOMPLETE,
+ CONNECTED,
+ FAILED,
+ NUM_HANDSHAKE_RESULT_TYPES,
+ };
+
+ class StreamCreation : public WebSocketStreamCreateTest {
+ virtual void TestBody() OVERRIDE {}
+ };
+
+ scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
+ base::HistogramBase* histogram =
+ base::StatisticsRecorder::FindHistogram(name);
+ return histogram ? histogram->SnapshotSamples()
+ : scoped_ptr<base::HistogramSamples>();
+ }
};
// Confirm that the basic case works as expected.
TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
CreateAndConnectStandard(
- "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
+ "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
+ EXPECT_FALSE(request_info_);
+ EXPECT_FALSE(response_info_);
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_TRUE(stream_);
+ EXPECT_TRUE(request_info_);
+ EXPECT_TRUE(response_info_);
+}
+
+TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
+ static const char kResponse[] =
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
+ "foo: bar, baz\r\n"
+ "hoge: fuga\r\n"
+ "hoge: piyo\r\n"
+ "\r\n";
+
+ CreateAndConnectCustomResponse(
+ "ws://localhost/",
+ "/",
+ NoSubProtocols(),
+ "http://localhost",
+ "",
+ kResponse);
+ EXPECT_FALSE(request_info_);
+ EXPECT_FALSE(response_info_);
+ RunUntilIdle();
+ EXPECT_TRUE(stream_);
+ ASSERT_TRUE(request_info_);
+ ASSERT_TRUE(response_info_);
+ std::vector<HeaderKeyValuePair> request_headers =
+ ToVector(request_info_->headers);
+ // We examine the contents of request_info_ and response_info_
+ // mainly only in this test case.
+ EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
+ EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
+ EXPECT_EQ(101, response_info_->status_code);
+ EXPECT_EQ("Switching Protocols", response_info_->status_text);
+ ASSERT_EQ(12u, request_headers.size());
+ EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
+ EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
+ EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
+ EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
+ request_headers[3]);
+ EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
+ EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
+ request_headers[5]);
+ EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
+ request_headers[6]);
+ EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
+ EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip,deflate"),
+ request_headers[8]);
+ EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
+ request_headers[9]);
+ EXPECT_EQ("Sec-WebSocket-Key", request_headers[10].first);
+ EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
+ "permessage-deflate; client_max_window_bits"),
+ request_headers[11]);
+
+ std::vector<HeaderKeyValuePair> response_headers =
+ ToVector(*response_info_->headers);
+ ASSERT_EQ(6u, response_headers.size());
+ // Sort the headers for ease of verification.
+ std::sort(response_headers.begin(), response_headers.end());
+
+ EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
+ EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
+ EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
+ EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
+ EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
+ EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
}
// Confirm that the stream isn't established until the message loop runs.
TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
CreateAndConnectStandard(
- "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
+ "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
+ EXPECT_FALSE(has_failed());
EXPECT_FALSE(stream_);
}
@@ -156,10 +348,11 @@ TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
"");
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_TRUE(stream_);
}
@@ -168,10 +361,11 @@ TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
NoSubProtocols(),
- "http://google.com/",
+ "http://google.com",
"",
"");
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_TRUE(stream_);
}
@@ -183,12 +377,13 @@ TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
sub_protocols,
- "http://google.com/",
+ "http://google.com",
"Sec-WebSocket-Protocol: chatv11.chromium.org, "
"chatv20.chromium.org\r\n",
"Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
RunUntilIdle();
EXPECT_TRUE(stream_);
+ EXPECT_FALSE(has_failed());
EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
}
@@ -197,25 +392,35 @@ TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
NoSubProtocols(),
- "http://google.com/",
+ "http://google.com",
"",
"Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
RunUntilIdle();
EXPECT_FALSE(stream_);
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "Response must not include 'Sec-WebSocket-Protocol' header "
+ "if not present in request: chatv20.chromium.org",
+ failure_message());
}
// Missing sub-protocol response is rejected.
TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
+ std::vector<std::string> sub_protocols;
+ sub_protocols.push_back("chat.example.com");
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
- std::vector<std::string>(1, "chat.example.com"),
- "http://localhost/",
+ sub_protocols,
+ "http://localhost",
"Sec-WebSocket-Protocol: chat.example.com\r\n",
"");
RunUntilIdle();
EXPECT_FALSE(stream_);
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "Sent non-empty 'Sec-WebSocket-Protocol' header "
+ "but no response was received",
+ failure_message());
}
// Only one sub-protocol can be accepted.
@@ -226,41 +431,261 @@ TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
sub_protocols,
- "http://google.com/",
+ "http://google.com",
"Sec-WebSocket-Protocol: chatv11.chromium.org, "
"chatv20.chromium.org\r\n",
"Sec-WebSocket-Protocol: chatv11.chromium.org, "
"chatv20.chromium.org\r\n");
RunUntilIdle();
EXPECT_FALSE(stream_);
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Sec-WebSocket-Protocol' header must not appear "
+ "more than once in a response",
+ failure_message());
}
-// Unknown extension in the response is rejected
-TEST_F(WebSocketStreamCreateTest, UnknownExtension) {
+// Unmatched sub-protocol should be rejected.
+TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
+ std::vector<std::string> sub_protocols;
+ sub_protocols.push_back("chatv11.chromium.org");
+ sub_protocols.push_back("chatv20.chromium.org");
CreateAndConnectStandard("ws://localhost/testing_path",
"/testing_path",
- NoSubProtocols(),
- "http://localhost/",
- "",
- "Sec-WebSocket-Extensions: x-unknown-extension\r\n");
+ sub_protocols,
+ "http://google.com",
+ "Sec-WebSocket-Protocol: chatv11.chromium.org, "
+ "chatv20.chromium.org\r\n",
+ "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
+ RunUntilIdle();
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
+ "in response does not match any of sent values",
+ failure_message());
+}
+
+// permessage-deflate extension basic success case.
+TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
+ CreateAndConnectWithExtensions("permessage-deflate");
+ EXPECT_TRUE(stream_);
+ EXPECT_FALSE(has_failed());
+}
+
+// permessage-deflate extensions success with all parameters.
+TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_no_context_takeover; "
+ "server_max_window_bits=11; client_max_window_bits=13; "
+ "server_no_context_takeover");
+ EXPECT_TRUE(stream_);
+ EXPECT_FALSE(has_failed());
+}
+
+// Verify that incoming messages are actually decompressed with
+// permessage-deflate enabled.
+TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
+ CreateAndConnectCustomResponse(
+ "ws://localhost/testing_path",
+ "/testing_path",
+ NoSubProtocols(),
+ "http://localhost",
+ "",
+ WebSocketStandardResponse(
+ "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
+ std::string(
+ "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
+ "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
+ 9));
RunUntilIdle();
+
+ ASSERT_TRUE(stream_);
+ ScopedVector<WebSocketFrame> frames;
+ CompletionCallback callback;
+ ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
+ ASSERT_EQ(1U, frames.size());
+ ASSERT_EQ(5U, frames[0]->header.payload_length);
+ EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
+}
+
+// Unknown extension in the response is rejected
+TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
+ CreateAndConnectWithExtensions("x-unknown-extension");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "Found an unsupported extension 'x-unknown-extension' "
+ "in 'Sec-WebSocket-Extensions' header",
+ failure_message());
+}
+
+// Malformed extensions are rejected (this file does not cover all possible
+// parse failures, as the parser is covered thoroughly by its own unit tests).
+TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
+ CreateAndConnectWithExtensions(";");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
+ "value is rejected by the parser: ;",
+ failure_message());
+}
+
+// The permessage-deflate extension may only be specified once.
+TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate, permessage-deflate; client_max_window_bits=10");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: "
+ "Received duplicate permessage-deflate response",
+ failure_message());
+}
+
+// permessage-deflate parameters may not be duplicated.
+TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_no_context_takeover; "
+ "client_no_context_takeover");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received duplicate permessage-deflate extension parameter "
+ "client_no_context_takeover",
+ failure_message());
+}
+
+// permessage-deflate parameters must start with "client_" or "server_"
+TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; absurd_no_context_takeover");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received an unexpected permessage-deflate extension parameter",
+ failure_message());
+}
+
+// permessage-deflate parameters must be either *_no_context_takeover or
+// *_max_window_bits
+TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_max_content_bits=5");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received an unexpected permessage-deflate extension parameter",
+ failure_message());
+}
+
+// *_no_context_takeover parameters must not have an argument
+TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_no_context_takeover=true");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid client_no_context_takeover parameter",
+ failure_message());
+}
+
+// *_max_window_bits must have an argument
+TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
+ CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "client_max_window_bits must have value",
+ failure_message());
+}
+
+// *_max_window_bits must be an integer
+TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; server_max_window_bits=banana");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid server_max_window_bits parameter",
+ failure_message());
+}
+
+// *_max_window_bits must be >= 8
+TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; server_max_window_bits=7");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid server_max_window_bits parameter",
+ failure_message());
+}
+
+// *_max_window_bits must be <= 15
+TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_max_window_bits=16");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid client_max_window_bits parameter",
+ failure_message());
+}
+
+// *_max_window_bits must not start with 0
+TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; client_max_window_bits=08");
+ EXPECT_FALSE(stream_);
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid client_max_window_bits parameter",
+ failure_message());
+}
+
+// *_max_window_bits must not start with +
+TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
+ CreateAndConnectWithExtensions(
+ "permessage-deflate; server_max_window_bits=+9");
EXPECT_FALSE(stream_);
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ(
+ "Error during WebSocket handshake: Error in permessage-deflate: "
+ "Received invalid server_max_window_bits parameter",
+ failure_message());
}
+// TODO(ricea): Check that WebSocketDeflateStream is initialised with the
+// arguments from the server. This is difficult because the data written to the
+// socket is randomly masked.
+
// Additional Sec-WebSocket-Accept headers should be rejected.
TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
CreateAndConnectStandard(
"ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
RunUntilIdle();
EXPECT_FALSE(stream_);
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Sec-WebSocket-Accept' header must not appear "
+ "more than once in a response",
+ failure_message());
}
// Response code 200 must be rejected.
@@ -274,11 +699,13 @@ TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kInvalidStatusCodeResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
+ failure_message());
}
// Redirects are not followed (according to the WHATWG WebSocket API, which
@@ -295,11 +722,13 @@ TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kRedirectResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
+ failure_message());
}
// Malformed responses should be rejected. HttpStreamParser will accept just
@@ -317,11 +746,13 @@ TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kMalformedResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
+ failure_message());
}
// Upgrade header must be present.
@@ -334,11 +765,13 @@ TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kMissingUpgradeResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
+ failure_message());
}
// There must only be one upgrade header.
@@ -347,10 +780,34 @@ TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
"ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"", "Upgrade: HTTP/2.0\r\n");
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Upgrade' header must not appear more than once in a response",
+ failure_message());
+}
+
+// There must only be one correct upgrade header.
+TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
+ static const char kMissingUpgradeResponse[] =
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
+ "Upgrade: hogefuga\r\n"
+ "\r\n";
+ CreateAndConnectCustomResponse("ws://localhost/",
+ "/",
+ NoSubProtocols(),
+ "http://localhost",
+ "",
+ kMissingUpgradeResponse);
+ RunUntilIdle();
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Upgrade' header value is not 'WebSocket': hogefuga",
+ failure_message());
}
// Connection header must be present.
@@ -363,11 +820,35 @@ TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
+ "",
+ kMissingConnectionResponse);
+ RunUntilIdle();
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Connection' header is missing",
+ failure_message());
+}
+
+// Connection header must contain "Upgrade".
+TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
+ static const char kMissingConnectionResponse[] =
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
+ "Connection: hogefuga\r\n"
+ "\r\n";
+ CreateAndConnectCustomResponse("ws://localhost/",
+ "/",
+ NoSubProtocols(),
+ "http://localhost",
"",
kMissingConnectionResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Connection' header value must contain 'Upgrade'",
+ failure_message());
}
// Connection header is permitted to contain other tokens.
@@ -381,10 +862,11 @@ TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kAdditionalConnectionTokenResponse);
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_TRUE(stream_);
}
@@ -398,11 +880,14 @@ TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kMissingAcceptResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "'Sec-WebSocket-Accept' header is missing",
+ failure_message());
}
// Sec-WebSocket-Accept header must match the key that was sent.
@@ -416,20 +901,26 @@ TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
CreateAndConnectCustomResponse("ws://localhost/",
"/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
"",
kIncorrectAcceptResponse);
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error during WebSocket handshake: "
+ "Incorrect 'Sec-WebSocket-Accept' header value",
+ failure_message());
}
// Cancellation works.
TEST_F(WebSocketStreamCreateTest, Cancellation) {
CreateAndConnectStandard(
- "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
+ "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
stream_request_.reset();
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_FALSE(stream_);
+ EXPECT_FALSE(request_info_);
+ EXPECT_FALSE(response_info_);
}
// Connect failure must look just like negotiation failure.
@@ -439,9 +930,13 @@ TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
socket_data->set_connect_data(
MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
- "http://localhost/", socket_data.Pass());
+ "http://localhost", socket_data.Pass());
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
+ failure_message());
+ EXPECT_FALSE(request_info_);
+ EXPECT_FALSE(response_info_);
}
// Connect timeout must look just like any other failure.
@@ -451,9 +946,11 @@ TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
socket_data->set_connect_data(
MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
- "http://localhost/", socket_data.Pass());
+ "http://localhost", socket_data.Pass());
RunUntilIdle();
- EXPECT_EQ(1006, error());
+ EXPECT_TRUE(has_failed());
+ EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
+ failure_message());
}
// Cancellation during connect works.
@@ -463,10 +960,11 @@ TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
CreateAndConnectRawExpectations("ws://localhost/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
socket_data.Pass());
stream_request_.reset();
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_FALSE(stream_);
}
@@ -482,17 +980,20 @@ TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
socket_data->SetStop(1);
CreateAndConnectRawExpectations("ws://localhost/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
make_scoped_ptr(socket_data));
socket_data->Run();
stream_request_.reset();
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_FALSE(stream_);
+ EXPECT_TRUE(request_info_);
+ EXPECT_FALSE(response_info_);
}
// Cancellation during read of the response headers works.
TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
- std::string request = WebSocketStandardRequest("/", "http://localhost/", "");
+ std::string request = WebSocketStandardRequest("/", "http://localhost", "");
MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
MockRead reads[] = {
MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
@@ -503,12 +1004,176 @@ TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
socket_data->SetStop(1);
CreateAndConnectRawExpectations("ws://localhost/",
NoSubProtocols(),
- "http://localhost/",
+ "http://localhost",
make_scoped_ptr(socket_data));
socket_data->Run();
stream_request_.reset();
RunUntilIdle();
+ EXPECT_FALSE(has_failed());
EXPECT_FALSE(stream_);
+ EXPECT_TRUE(request_info_);
+ EXPECT_FALSE(response_info_);
+}
+
+// Over-size response headers (> 256KB) should not cause a crash. This is a
+// regression test for crbug.com/339456. It is based on the layout test
+// "cookie-flood.html".
+TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
+ std::string set_cookie_headers;
+ set_cookie_headers.reserve(45 * 10000);
+ for (int i = 0; i < 10000; ++i) {
+ set_cookie_headers +=
+ base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
+ }
+ CreateAndConnectStandard("ws://localhost/", "/", NoSubProtocols(),
+ "http://localhost", "", set_cookie_headers);
+ RunUntilIdle();
+ EXPECT_TRUE(has_failed());
+ EXPECT_FALSE(response_info_);
+}
+
+// If the remote host closes the connection without sending headers, we should
+// log the console message "Connection closed before receiving a handshake
+// response".
+TEST_F(WebSocketStreamCreateTest, NoResponse) {
+ std::string request = WebSocketStandardRequest("/", "http://localhost", "");
+ MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
+ MockRead reads[] = {MockRead(ASYNC, 0, 1)};
+ DeterministicSocketData* socket_data(new DeterministicSocketData(
+ reads, arraysize(reads), writes, arraysize(writes)));
+ socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ CreateAndConnectRawExpectations("ws://localhost/",
+ NoSubProtocols(),
+ "http://localhost",
+ make_scoped_ptr(socket_data));
+ socket_data->RunFor(2);
+ EXPECT_TRUE(has_failed());
+ EXPECT_FALSE(stream_);
+ EXPECT_FALSE(response_info_);
+ EXPECT_EQ("Connection closed before receiving a handshake response",
+ failure_message());
+}
+
+TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
+ ssl_data_.push_back(
+ new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
+ ssl_data_[0]->cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
+ ASSERT_TRUE(ssl_data_[0]->cert);
+ scoped_ptr<DeterministicSocketData> raw_socket_data(
+ new DeterministicSocketData(NULL, 0, NULL, 0));
+ CreateAndConnectRawExpectations("wss://localhost/",
+ NoSubProtocols(),
+ "http://localhost",
+ raw_socket_data.Pass());
+ RunUntilIdle();
+ EXPECT_FALSE(has_failed());
+ ASSERT_TRUE(ssl_error_callbacks_);
+ ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
+ &ssl_info_);
+ RunUntilIdle();
+ EXPECT_TRUE(has_failed());
+}
+
+TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
+ scoped_ptr<SSLSocketDataProvider> ssl_data(
+ new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
+ ssl_data->cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
+ ASSERT_TRUE(ssl_data->cert);
+ ssl_data_.push_back(ssl_data.release());
+ ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
+ ssl_data_.push_back(ssl_data.release());
+ url_request_context_host_.AddRawExpectations(
+ make_scoped_ptr(new DeterministicSocketData(NULL, 0, NULL, 0)));
+ CreateAndConnectStandard(
+ "wss://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
+ RunUntilIdle();
+ ASSERT_TRUE(ssl_error_callbacks_);
+ ssl_error_callbacks_->ContinueSSLRequest();
+ RunUntilIdle();
+ EXPECT_FALSE(has_failed());
+ EXPECT_TRUE(stream_);
+}
+
+TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
+ const std::string name("Net.WebSocket.HandshakeResult");
+ scoped_ptr<base::HistogramSamples> original(GetSamples(name));
+
+ {
+ StreamCreation creation;
+ creation.CreateAndConnectStandard("ws://localhost/",
+ "/",
+ creation.NoSubProtocols(),
+ "http://localhost",
+ "",
+ "");
+ }
+
+ scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
+ ASSERT_TRUE(samples);
+ if (original) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
+ EXPECT_EQ(0, samples->GetCount(CONNECTED));
+ EXPECT_EQ(0, samples->GetCount(FAILED));
+}
+
+TEST_F(WebSocketStreamCreateUMATest, Connected) {
+ const std::string name("Net.WebSocket.HandshakeResult");
+ scoped_ptr<base::HistogramSamples> original(GetSamples(name));
+
+ {
+ StreamCreation creation;
+ creation.CreateAndConnectStandard("ws://localhost/",
+ "/",
+ creation.NoSubProtocols(),
+ "http://localhost",
+ "",
+ "");
+ creation.RunUntilIdle();
+ }
+
+ scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
+ ASSERT_TRUE(samples);
+ if (original) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
+ EXPECT_EQ(1, samples->GetCount(CONNECTED));
+ EXPECT_EQ(0, samples->GetCount(FAILED));
+}
+
+TEST_F(WebSocketStreamCreateUMATest, Failed) {
+ const std::string name("Net.WebSocket.HandshakeResult");
+ scoped_ptr<base::HistogramSamples> original(GetSamples(name));
+
+ {
+ StreamCreation creation;
+ static const char kInvalidStatusCodeResponse[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
+ "\r\n";
+ creation.CreateAndConnectCustomResponse("ws://localhost/",
+ "/",
+ creation.NoSubProtocols(),
+ "http://localhost",
+ "",
+ kInvalidStatusCodeResponse);
+ creation.RunUntilIdle();
+ }
+
+ scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
+ ASSERT_TRUE(samples);
+ if (original) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
+ EXPECT_EQ(0, samples->GetCount(CONNECTED));
+ EXPECT_EQ(0, samples->GetCount(FAILED));
}
} // namespace
diff --git a/chromium/net/websockets/websocket_test_util.cc b/chromium/net/websockets/websocket_test_util.cc
index 55113c6f15c..bfa89803447 100644
--- a/chromium/net/websockets/websocket_test_util.cc
+++ b/chromium/net/websockets/websocket_test_util.cc
@@ -4,7 +4,12 @@
#include "net/websockets/websocket_test_util.h"
+#include <algorithm>
+#include <vector>
+
#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "net/socket/socket_test_util.h"
@@ -37,6 +42,8 @@ std::string WebSocketStandardRequest(const std::string& path,
"GET %s HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: Upgrade\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache\r\n"
"Upgrade: websocket\r\n"
"Origin: %s\r\n"
"Sec-WebSocket-Version: 13\r\n"
@@ -44,6 +51,7 @@ std::string WebSocketStandardRequest(const std::string& path,
"Accept-Encoding: gzip,deflate\r\n"
"Accept-Language: en-us,fr\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+ "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"
"%s\r\n",
path.c_str(),
origin.c_str(),
@@ -63,9 +71,10 @@ std::string WebSocketStandardResponse(const std::string& extra_headers) {
struct WebSocketDeterministicMockClientSocketFactoryMaker::Detail {
std::string expect_written;
std::string return_to_read;
- MockRead read;
+ std::vector<MockRead> reads;
MockWrite write;
- scoped_ptr<DeterministicSocketData> data;
+ ScopedVector<DeterministicSocketData> socket_data_vector;
+ ScopedVector<SSLSocketDataProvider> ssl_socket_data_vector;
DeterministicMockClientSocketFactory factory;
};
@@ -84,22 +93,46 @@ WebSocketDeterministicMockClientSocketFactoryMaker::factory() {
void WebSocketDeterministicMockClientSocketFactoryMaker::SetExpectations(
const std::string& expect_written,
const std::string& return_to_read) {
+ const size_t kHttpStreamParserBufferSize = 4096;
// We need to extend the lifetime of these strings.
detail_->expect_written = expect_written;
detail_->return_to_read = return_to_read;
- detail_->write = MockWrite(SYNCHRONOUS, 0, detail_->expect_written.c_str());
- detail_->read = MockRead(SYNCHRONOUS, 1, detail_->return_to_read.c_str());
+ int sequence = 0;
+ detail_->write = MockWrite(SYNCHRONOUS,
+ detail_->expect_written.data(),
+ detail_->expect_written.size(),
+ sequence++);
+ // HttpStreamParser reads 4KB at a time. We need to take this implementation
+ // detail into account if |return_to_read| is big enough.
+ for (size_t place = 0; place < detail_->return_to_read.size();
+ place += kHttpStreamParserBufferSize) {
+ detail_->reads.push_back(
+ MockRead(SYNCHRONOUS, detail_->return_to_read.data() + place,
+ std::min(detail_->return_to_read.size() - place,
+ kHttpStreamParserBufferSize),
+ sequence++));
+ }
scoped_ptr<DeterministicSocketData> socket_data(
- new DeterministicSocketData(&detail_->read, 1, &detail_->write, 1));
+ new DeterministicSocketData(vector_as_array(&detail_->reads),
+ detail_->reads.size(),
+ &detail_->write,
+ 1));
socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
- socket_data->SetStop(2);
- SetRawExpectations(socket_data.Pass());
+ socket_data->SetStop(sequence);
+ AddRawExpectations(socket_data.Pass());
}
-void WebSocketDeterministicMockClientSocketFactoryMaker::SetRawExpectations(
+void WebSocketDeterministicMockClientSocketFactoryMaker::AddRawExpectations(
scoped_ptr<DeterministicSocketData> socket_data) {
- detail_->data = socket_data.Pass();
- detail_->factory.AddSocketDataProvider(detail_->data.get());
+ detail_->factory.AddSocketDataProvider(socket_data.get());
+ detail_->socket_data_vector.push_back(socket_data.release());
+}
+
+void
+WebSocketDeterministicMockClientSocketFactoryMaker::AddSSLSocketDataProvider(
+ scoped_ptr<SSLSocketDataProvider> ssl_socket_data) {
+ detail_->factory.AddSSLSocketDataProvider(ssl_socket_data.get());
+ detail_->ssl_socket_data_vector.push_back(ssl_socket_data.release());
}
WebSocketTestURLRequestContextHost::WebSocketTestURLRequestContextHost()
@@ -109,9 +142,14 @@ WebSocketTestURLRequestContextHost::WebSocketTestURLRequestContextHost()
WebSocketTestURLRequestContextHost::~WebSocketTestURLRequestContextHost() {}
-void WebSocketTestURLRequestContextHost::SetRawExpectations(
+void WebSocketTestURLRequestContextHost::AddRawExpectations(
scoped_ptr<DeterministicSocketData> socket_data) {
- maker_.SetRawExpectations(socket_data.Pass());
+ maker_.AddRawExpectations(socket_data.Pass());
+}
+
+void WebSocketTestURLRequestContextHost::AddSSLSocketDataProvider(
+ scoped_ptr<SSLSocketDataProvider> ssl_socket_data) {
+ maker_.AddSSLSocketDataProvider(ssl_socket_data.Pass());
}
TestURLRequestContext*
diff --git a/chromium/net/websockets/websocket_test_util.h b/chromium/net/websockets/websocket_test_util.h
index 71b2ce668c8..2ad86c08fe0 100644
--- a/chromium/net/websockets/websocket_test_util.h
+++ b/chromium/net/websockets/websocket_test_util.h
@@ -14,13 +14,18 @@
class GURL;
+namespace url {
+class Origin;
+} // namespace url
+
namespace net {
class BoundNetLog;
+class DeterministicMockClientSocketFactory;
class DeterministicSocketData;
class URLRequestContext;
class WebSocketHandshakeStreamCreateHelper;
-class DeterministicMockClientSocketFactory;
+struct SSLSocketDataProvider;
class LinearCongruentialGenerator {
public:
@@ -38,7 +43,7 @@ NET_EXPORT_PRIVATE extern scoped_ptr<WebSocketStreamRequest>
CreateAndConnectStreamForTesting(
const GURL& socket_url,
scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
- const GURL& origin,
+ const url::Origin& origin,
URLRequestContext* url_request_context,
const BoundNetLog& net_log,
scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate);
@@ -61,15 +66,26 @@ class WebSocketDeterministicMockClientSocketFactoryMaker {
WebSocketDeterministicMockClientSocketFactoryMaker();
~WebSocketDeterministicMockClientSocketFactoryMaker();
- // The socket created by the factory will expect |expect_written| to be
- // written to the socket, and will respond with |return_to_read|. The test
- // will fail if the expected text is not written, or all the bytes are not
- // read.
+ // Tell the factory to create a socket which expects |expect_written| to be
+ // written, and responds with |return_to_read|. The test will fail if the
+ // expected text is not written, or all the bytes are not read. This adds data
+ // for a new mock-socket using AddRawExpections(), and so can be called
+ // multiple times to queue up multiple mock sockets, but usually in those
+ // cases the lower-level AddRawExpections() interface is more appropriate.
void SetExpectations(const std::string& expect_written,
const std::string& return_to_read);
- // A low-level interface to permit arbitrary expectations to be set.
- void SetRawExpectations(scoped_ptr<DeterministicSocketData> socket_data);
+ // A low-level interface to permit arbitrary expectations to be added. The
+ // mock sockets will be created in the same order that they were added.
+ void AddRawExpectations(scoped_ptr<DeterministicSocketData> socket_data);
+
+ // Allow an SSL socket data provider to be added. You must also supply a mock
+ // transport socket for it to use. If the mock SSL handshake fails then the
+ // mock transport socket will connect but have nothing read or written. If the
+ // mock handshake succeeds then the data from the underlying transport socket
+ // will be passed through unchanged (without encryption).
+ void AddSSLSocketDataProvider(
+ scoped_ptr<SSLSocketDataProvider> ssl_socket_data);
// Call to get a pointer to the factory, which remains owned by this object.
DeterministicMockClientSocketFactory* factory();
@@ -94,9 +110,13 @@ struct WebSocketTestURLRequestContextHost {
maker_.SetExpectations(expect_written, return_to_read);
}
- void SetRawExpectations(scoped_ptr<DeterministicSocketData> socket_data);
+ void AddRawExpectations(scoped_ptr<DeterministicSocketData> socket_data);
+
+ // Allow an SSL socket data provider to be added.
+ void AddSSLSocketDataProvider(
+ scoped_ptr<SSLSocketDataProvider> ssl_socket_data);
- // Call after calling one of SetExpections() or SetRawExpectations(). The
+ // Call after calling one of SetExpections() or AddRawExpectations(). The
// returned pointer remains owned by this object. This should only be called
// once.
TestURLRequestContext* GetURLRequestContext();
diff --git a/chromium/net/websockets/websocket_throttle_test.cc b/chromium/net/websockets/websocket_throttle_test.cc
index 14237b9b265..4d9300191bb 100644
--- a/chromium/net/websockets/websocket_throttle_test.cc
+++ b/chromium/net/websockets/websocket_throttle_test.cc
@@ -16,20 +16,34 @@
#include "testing/platform_test.h"
#include "url/gurl.h"
-class DummySocketStreamDelegate : public net::SocketStream::Delegate {
+namespace net {
+
+namespace {
+
+class DummySocketStreamDelegate : public SocketStream::Delegate {
public:
DummySocketStreamDelegate() {}
virtual ~DummySocketStreamDelegate() {}
virtual void OnConnected(
- net::SocketStream* socket, int max_pending_send_allowed) OVERRIDE {}
- virtual void OnSentData(net::SocketStream* socket,
+ SocketStream* socket, int max_pending_send_allowed) OVERRIDE {}
+ virtual void OnSentData(SocketStream* socket,
int amount_sent) OVERRIDE {}
- virtual void OnReceivedData(net::SocketStream* socket,
+ virtual void OnReceivedData(SocketStream* socket,
const char* data, int len) OVERRIDE {}
- virtual void OnClose(net::SocketStream* socket) OVERRIDE {}
+ virtual void OnClose(SocketStream* socket) OVERRIDE {}
};
-namespace net {
+class WebSocketThrottleTestContext : public TestURLRequestContext {
+ public:
+ explicit WebSocketThrottleTestContext(bool enable_websocket_over_spdy)
+ : TestURLRequestContext(true) {
+ HttpNetworkSession::Params params;
+ params.enable_websocket_over_spdy = enable_websocket_over_spdy;
+ Init();
+ }
+};
+
+} // namespace
class WebSocketThrottleTest : public PlatformTest {
protected:
@@ -60,11 +74,10 @@ class WebSocketThrottleTest : public PlatformTest {
};
TEST_F(WebSocketThrottleTest, Throttle) {
- TestURLRequestContext context;
- DummySocketStreamDelegate delegate;
// TODO(toyoshim): We need to consider both spdy-enabled and spdy-disabled
// configuration.
- WebSocketJob::set_websocket_over_spdy_enabled(true);
+ WebSocketThrottleTestContext context(true);
+ DummySocketStreamDelegate delegate;
// For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
AddressList addr;
@@ -73,8 +86,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 6));
scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://host1/"), w1.get()));
- s1->set_context(&context);
+ new SocketStream(GURL("ws://host1/"), w1.get(), &context, NULL));
w1->InitSocketStream(s1.get());
WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
@@ -94,8 +106,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 4));
scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s2(
- new SocketStream(GURL("ws://host2/"), w2.get()));
- s2->set_context(&context);
+ new SocketStream(GURL("ws://host2/"), w2.get(), &context, NULL));
w2->InitSocketStream(s2.get());
WebSocketThrottleTest::MockSocketStreamConnect(s2.get(), addr);
@@ -115,8 +126,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 5));
scoped_refptr<WebSocketJob> w3(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s3(
- new SocketStream(GURL("ws://host3/"), w3.get()));
- s3->set_context(&context);
+ new SocketStream(GURL("ws://host3/"), w3.get(), &context, NULL));
w3->InitSocketStream(s3.get());
WebSocketThrottleTest::MockSocketStreamConnect(s3.get(), addr);
@@ -136,8 +146,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 6));
scoped_refptr<WebSocketJob> w4(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s4(
- new SocketStream(GURL("ws://host4/"), w4.get()));
- s4->set_context(&context);
+ new SocketStream(GURL("ws://host4/"), w4.get(), &context, NULL));
w4->InitSocketStream(s4.get());
WebSocketThrottleTest::MockSocketStreamConnect(s4.get(), addr);
@@ -156,8 +165,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 6));
scoped_refptr<WebSocketJob> w5(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s5(
- new SocketStream(GURL("ws://host5/"), w5.get()));
- s5->set_context(&context);
+ new SocketStream(GURL("ws://host5/"), w5.get(), &context, NULL));
w5->InitSocketStream(s5.get());
WebSocketThrottleTest::MockSocketStreamConnect(s5.get(), addr);
@@ -176,8 +184,7 @@ TEST_F(WebSocketThrottleTest, Throttle) {
addr.push_back(MakeAddr(1, 2, 3, 6));
scoped_refptr<WebSocketJob> w6(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s6(
- new SocketStream(GURL("ws://host6/"), w6.get()));
- s6->set_context(&context);
+ new SocketStream(GURL("ws://host6/"), w6.get(), &context, NULL));
w6->InitSocketStream(s6.get());
WebSocketThrottleTest::MockSocketStreamConnect(s6.get(), addr);
@@ -279,9 +286,8 @@ TEST_F(WebSocketThrottleTest, Throttle) {
}
TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
- TestURLRequestContext context;
+ WebSocketThrottleTestContext context(true);
DummySocketStreamDelegate delegate;
- WebSocketJob::set_websocket_over_spdy_enabled(true);
// For localhost: 127.0.0.1, 127.0.0.1
AddressList addr;
@@ -289,8 +295,7 @@ TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
addr.push_back(MakeAddr(127, 0, 0, 1));
scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://localhost/"), w1.get()));
- s1->set_context(&context);
+ new SocketStream(GURL("ws://localhost/"), w1.get(), &context, NULL));
w1->InitSocketStream(s1.get());
WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
@@ -309,17 +314,15 @@ TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
// A connection should not be blocked by another connection to the same IP
// with a different port.
TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) {
- TestURLRequestContext context;
+ WebSocketThrottleTestContext context(false);
DummySocketStreamDelegate delegate;
IPAddressNumber localhost;
ParseIPLiteralToNumber("127.0.0.1", &localhost);
- WebSocketJob::set_websocket_over_spdy_enabled(false);
// socket1: 127.0.0.1:80
scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s1(
- new SocketStream(GURL("ws://localhost:80/"), w1.get()));
- s1->set_context(&context);
+ new SocketStream(GURL("ws://localhost:80/"), w1.get(), &context, NULL));
w1->InitSocketStream(s1.get());
MockSocketStreamConnect(s1.get(),
AddressList::CreateFromIPAddress(localhost, 80));
@@ -332,8 +335,7 @@ TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) {
// socket2: 127.0.0.1:81
scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
scoped_refptr<SocketStream> s2(
- new SocketStream(GURL("ws://localhost:81/"), w2.get()));
- s2->set_context(&context);
+ new SocketStream(GURL("ws://localhost:81/"), w2.get(), &context, NULL));
w2->InitSocketStream(s2.get());
MockSocketStreamConnect(s2.get(),
AddressList::CreateFromIPAddress(localhost, 81));
@@ -354,4 +356,4 @@ TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) {
base::MessageLoopForIO::current()->RunUntilIdle();
}
-}
+} // namespace net